This commit is contained in:
David Bomba 2015-10-27 10:01:13 +11:00
commit 7cdbb56284
134 changed files with 14945 additions and 12783 deletions

View File

@ -20,6 +20,6 @@ MAIL_FROM_ADDRESS
MAIL_FROM_NAME MAIL_FROM_NAME
MAIL_PASSWORD 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 LOG=single

2
.gitignore vendored
View File

@ -14,7 +14,7 @@
/vendor /vendor
/node_modules /node_modules
/.DS_Store /.DS_Store
/Thumbs.db Thumbs.db
/.env /.env
/.env.development.php /.env.development.php
/.env.php /.env.php

View File

@ -130,6 +130,16 @@ module.exports = function(grunt) {
options: { options: {
process: false process: false
} }
},
js_pdf: {
src: [
'public/js/pdf_viewer.js',
'public/js/compatibility.js',
'public/js/pdfmake.min.js',
'public/js/vfs_fonts.js',
],
dest: 'public/js/pdf.built.js',
nonull: true
} }
} }
}); });

View File

@ -235,7 +235,7 @@ class CheckData extends Command {
'updated_at' => new Carbon, 'updated_at' => new Carbon,
'account_id' => $client->account_id, 'account_id' => $client->account_id,
'client_id' => $client->id, 'client_id' => $client->id,
'message' => 'Recovered update to invoice [<a href="https://github.com/hillelcoren/invoice-ninja/releases/tag/v1.7.1" target="_blank">details</a>]', 'message' => 'Corrected client balance',
'adjustment' => $client->actual_balance - $activity->balance, 'adjustment' => $client->actual_balance - $activity->balance,
'balance' => $client->actual_balance, 'balance' => $client->actual_balance,
]); ]);

View File

@ -59,7 +59,7 @@ class CreateRandomData extends Command {
} }
$invoice = Invoice::createNew($user); $invoice = Invoice::createNew($user);
$invoice->invoice_number = $user->account->getNextInvoiceNumber(); $invoice->invoice_number = $user->account->getNextInvoiceNumber($invoice);
$invoice->amount = $invoice->balance = $price; $invoice->amount = $invoice->balance = $price;
$invoice->created_at = date('Y-m-d', strtotime(date("Y-m-d") . ' - ' . rand(1, 100) . ' days')); $invoice->created_at = date('Y-m-d', strtotime(date("Y-m-d") . ' - ' . rand(1, 100) . ' days'));
$client->invoices()->save($invoice); $client->invoices()->save($invoice);

View File

@ -33,15 +33,22 @@ class SendRecurringInvoices extends Command
$today = new DateTime(); $today = new DateTime();
$invoices = Invoice::with('account.timezone', 'invoice_items', 'client', 'user') $invoices = Invoice::with('account.timezone', 'invoice_items', 'client', 'user')
->whereRaw('is_deleted IS FALSE AND deleted_at IS NULL AND is_recurring IS TRUE AND start_date <= ? AND (end_date IS NULL OR end_date >= ?)', array($today, $today))->get(); ->whereRaw('is_deleted IS FALSE AND deleted_at IS NULL AND is_recurring IS TRUE AND frequency_id > 0 AND start_date <= ? AND (end_date IS NULL OR end_date >= ?)', array($today, $today))
->orderBy('id', 'asc')
->get();
$this->info(count($invoices).' recurring invoice(s) found'); $this->info(count($invoices).' recurring invoice(s) found');
foreach ($invoices as $recurInvoice) { foreach ($invoices as $recurInvoice) {
if (!$recurInvoice->user->confirmed) {
continue;
}
$recurInvoice->account->loadLocalizationSettings($recurInvoice->client);
$this->info('Processing Invoice '.$recurInvoice->id.' - Should send '.($recurInvoice->shouldSendToday() ? 'YES' : 'NO')); $this->info('Processing Invoice '.$recurInvoice->id.' - Should send '.($recurInvoice->shouldSendToday() ? 'YES' : 'NO'));
$invoice = $this->invoiceRepo->createRecurringInvoice($recurInvoice); $invoice = $this->invoiceRepo->createRecurringInvoice($recurInvoice);
if ($invoice && !$invoice->isPaid()) { if ($invoice && !$invoice->isPaid()) {
$recurInvoice->account->loadLocalizationSettings($invoice->client); $this->info('Sending Invoice');
$this->mailer->sendInvoice($invoice); $this->mailer->sendInvoice($invoice);
} }
} }

View File

@ -15,7 +15,6 @@ use Cache;
use Response; use Response;
use parseCSV; use parseCSV;
use Request; use Request;
use App\Models\Affiliate; use App\Models\Affiliate;
use App\Models\License; use App\Models\License;
use App\Models\User; use App\Models\User;
@ -37,6 +36,7 @@ use App\Models\Gateway;
use App\Models\Timezone; use App\Models\Timezone;
use App\Models\Industry; use App\Models\Industry;
use App\Models\InvoiceDesign; use App\Models\InvoiceDesign;
use App\Models\TaxRate;
use App\Ninja\Repositories\AccountRepository; use App\Ninja\Repositories\AccountRepository;
use App\Ninja\Mailers\UserMailer; use App\Ninja\Mailers\UserMailer;
use App\Ninja\Mailers\ContactMailer; use App\Ninja\Mailers\ContactMailer;
@ -129,9 +129,9 @@ class AccountController extends BaseController
Session::put("show_trash:{$entityType}", $visible == 'true'); Session::put("show_trash:{$entityType}", $visible == 'true');
if ($entityType == 'user') { if ($entityType == 'user') {
return Redirect::to('company/'.ACCOUNT_ADVANCED_SETTINGS.'/'.ACCOUNT_USER_MANAGEMENT); return Redirect::to('settings/'.ACCOUNT_USER_MANAGEMENT);
} elseif ($entityType == 'token') { } elseif ($entityType == 'token') {
return Redirect::to('company/'.ACCOUNT_ADVANCED_SETTINGS.'/'.ACCOUNT_TOKEN_MANAGEMENT); return Redirect::to('settings/'.ACCOUNT_API_TOKENS);
} else { } else {
return Redirect::to("{$entityType}s"); return Redirect::to("{$entityType}s");
} }
@ -143,10 +143,80 @@ class AccountController extends BaseController
return Response::json($data); return Response::json($data);
} }
public function showSection($section = ACCOUNT_DETAILS, $subSection = false) public function showSection($section = false)
{ {
if ($section == ACCOUNT_DETAILS) { if (!$section) {
return Redirect::to('/settings/' . ACCOUNT_COMPANY_DETAILS, 301);
}
if ($section == ACCOUNT_COMPANY_DETAILS) {
return self::showCompanyDetails();
} elseif ($section == ACCOUNT_USER_DETAILS) {
return self::showUserDetails();
} elseif ($section == ACCOUNT_LOCALIZATION) {
return self::showLocalization();
} elseif ($section == ACCOUNT_PAYMENTS) {
return self::showOnlinePayments();
} elseif ($section == ACCOUNT_INVOICE_SETTINGS) {
return self::showInvoiceSettings();
} elseif ($section == ACCOUNT_IMPORT_EXPORT) {
return View::make('accounts.import_export', ['title' => trans('texts.import_export')]);
} elseif ($section == ACCOUNT_INVOICE_DESIGN || $section == ACCOUNT_CUSTOMIZE_DESIGN) {
return self::showInvoiceDesign($section);
} elseif ($section === ACCOUNT_TEMPLATES_AND_REMINDERS) {
return self::showTemplates();
} elseif ($section === ACCOUNT_PRODUCTS) {
return self::showProducts();
} elseif ($section === ACCOUNT_TAX_RATES) {
return self::showTaxRates();
} else {
$data = [
'account' => Account::with('users')->findOrFail(Auth::user()->account_id),
'title' => trans("texts.{$section}"),
'section' => $section
];
return View::make("accounts.{$section}", $data);
}
}
private function showInvoiceSettings()
{
$account = Auth::user()->account;
$recurringHours = [];
for ($i=0; $i<24; $i++) {
if ($account->military_time) {
$format = 'H:i';
} else {
$format = 'g:i a';
}
$recurringHours[$i] = date($format, strtotime("{$i}:00"));
}
$data = [
'account' => Account::with('users')->findOrFail(Auth::user()->account_id),
'title' => trans("texts.invoice_settings"),
'section' => ACCOUNT_INVOICE_SETTINGS,
'recurringHours' => $recurringHours
];
return View::make("accounts.invoice_settings", $data);
}
private function showCompanyDetails()
{
$data = [
'account' => Account::with('users')->findOrFail(Auth::user()->account_id),
'countries' => Cache::get('countries'),
'sizes' => Cache::get('sizes'),
'industries' => Cache::get('industries'),
'title' => trans('texts.company_details'),
];
return View::make('accounts.details', $data);
}
private function showUserDetails()
{
$oauthLoginUrls = []; $oauthLoginUrls = [];
foreach (AuthService::$providers as $provider) { foreach (AuthService::$providers as $provider) {
$oauthLoginUrls[] = ['label' => $provider, 'url' => '/auth/' . strtolower($provider)]; $oauthLoginUrls[] = ['label' => $provider, 'url' => '/auth/' . strtolower($provider)];
@ -154,23 +224,32 @@ class AccountController extends BaseController
$data = [ $data = [
'account' => Account::with('users')->findOrFail(Auth::user()->account_id), 'account' => Account::with('users')->findOrFail(Auth::user()->account_id),
'countries' => Cache::get('countries'), 'title' => trans('texts.user_details'),
'sizes' => Cache::get('sizes'),
'industries' => Cache::get('industries'),
'timezones' => Cache::get('timezones'),
'dateFormats' => Cache::get('dateFormats'),
'datetimeFormats' => Cache::get('datetimeFormats'),
'currencies' => Cache::get('currencies'),
'languages' => Cache::get('languages'),
'title' => trans('texts.company_details'),
'user' => Auth::user(), 'user' => Auth::user(),
'oauthProviderName' => AuthService::getProviderName(Auth::user()->oauth_provider_id), 'oauthProviderName' => AuthService::getProviderName(Auth::user()->oauth_provider_id),
'oauthLoginUrls' => $oauthLoginUrls, 'oauthLoginUrls' => $oauthLoginUrls,
]; ];
return View::make('accounts.details', $data); return View::make('accounts.user_details', $data);
} elseif ($section == ACCOUNT_PAYMENTS) { }
private function showLocalization()
{
$data = [
'account' => Account::with('users')->findOrFail(Auth::user()->account_id),
'timezones' => Cache::get('timezones'),
'dateFormats' => Cache::get('dateFormats'),
'datetimeFormats' => Cache::get('datetimeFormats'),
'currencies' => Cache::get('currencies'),
'languages' => Cache::get('languages'),
'title' => trans('texts.localization'),
];
return View::make('accounts.localization', $data);
}
private function showOnlinePayments()
{
$account = Auth::user()->account; $account = Auth::user()->account;
$account->load('account_gateways'); $account->load('account_gateways');
$count = count($account->account_gateways); $count = count($account->account_gateways);
@ -183,25 +262,39 @@ class AccountController extends BaseController
'title' => trans('texts.online_payments') 'title' => trans('texts.online_payments')
]); ]);
} }
} elseif ($section == ACCOUNT_NOTIFICATIONS) { }
private function showProducts()
{
$columns = ['product', 'description', 'unit_cost'];
if (Auth::user()->account->invoice_item_taxes) {
$columns[] = 'tax_rate';
}
$columns[] = 'action';
$data = [ $data = [
'account' => Account::with('users')->findOrFail(Auth::user()->account_id), 'account' => Auth::user()->account,
'title' => trans('texts.notifications'), 'title' => trans('texts.product_library'),
'columns' => Utils::trans($columns),
]; ];
return View::make('accounts.notifications', $data); return View::make('accounts.products', $data);
} elseif ($section == ACCOUNT_IMPORT_EXPORT) { }
return View::make('accounts.import_export', ['title' => trans('texts.import_export')]);
} elseif ($section == ACCOUNT_ADVANCED_SETTINGS) { private function showTaxRates()
{
$data = [
'account' => Auth::user()->account,
'title' => trans('texts.tax_rates'),
'taxRates' => TaxRate::scope()->get(['id', 'name', 'rate']),
];
return View::make('accounts.tax_rates', $data);
}
private function showInvoiceDesign($section)
{
$account = Auth::user()->account->load('country'); $account = Auth::user()->account->load('country');
$data = [
'account' => $account,
'feature' => $subSection,
'title' => trans('texts.invoice_settings'),
];
if ($subSection == ACCOUNT_INVOICE_DESIGN
|| $subSection == ACCOUNT_CUSTOMIZE_DESIGN) {
$invoice = new stdClass(); $invoice = new stdClass();
$client = new stdClass(); $client = new stdClass();
$contact = new stdClass(); $contact = new stdClass();
@ -215,7 +308,7 @@ class AccountController extends BaseController
$client->work_phone = ''; $client->work_phone = '';
$client->work_email = ''; $client->work_email = '';
$invoice->invoice_number = $account->getNextInvoiceNumber(); $invoice->invoice_number = '0000';
$invoice->invoice_date = Utils::fromSqlDate(date('Y-m-d')); $invoice->invoice_date = Utils::fromSqlDate(date('Y-m-d'));
$invoice->account = json_decode($account->toJson()); $invoice->account = json_decode($account->toJson());
$invoice->amount = $invoice->balance = 100; $invoice->amount = $invoice->balance = 100;
@ -239,6 +332,7 @@ class AccountController extends BaseController
$data['invoiceLabels'] = json_decode($account->invoice_labels) ?: []; $data['invoiceLabels'] = json_decode($account->invoice_labels) ?: [];
$data['title'] = trans('texts.invoice_design'); $data['title'] = trans('texts.invoice_design');
$data['invoiceDesigns'] = InvoiceDesign::getDesigns(); $data['invoiceDesigns'] = InvoiceDesign::getDesigns();
$data['section'] = $section;
$design = false; $design = false;
foreach ($data['invoiceDesigns'] as $item) { foreach ($data['invoiceDesigns'] as $item) {
@ -248,10 +342,16 @@ class AccountController extends BaseController
} }
} }
if ($subSection == ACCOUNT_CUSTOMIZE_DESIGN) { if ($section == ACCOUNT_CUSTOMIZE_DESIGN) {
$data['customDesign'] = ($account->custom_design && !$design) ? $account->custom_design : $design; $data['customDesign'] = ($account->custom_design && !$design) ? $account->custom_design : $design;
} }
} else if ($subSection == ACCOUNT_TEMPLATES_AND_REMINDERS) { return View::make("accounts.{$section}", $data);
}
private function showTemplates()
{
$account = Auth::user()->account->load('country');
$data['account'] = $account;
$data['templates'] = []; $data['templates'] = [];
$data['defaultTemplates'] = []; $data['defaultTemplates'] = [];
foreach ([ENTITY_INVOICE, ENTITY_QUOTE, ENTITY_PAYMENT, REMINDER1, REMINDER2, REMINDER3] as $type) { foreach ([ENTITY_INVOICE, ENTITY_QUOTE, ENTITY_PAYMENT, REMINDER1, REMINDER2, REMINDER3] as $type) {
@ -266,45 +366,37 @@ class AccountController extends BaseController
} }
$data['emailFooter'] = $account->getEmailFooter(); $data['emailFooter'] = $account->getEmailFooter();
$data['title'] = trans('texts.email_templates'); $data['title'] = trans('texts.email_templates');
} else if ($subSection == ACCOUNT_USER_MANAGEMENT) { return View::make('accounts.templates_and_reminders', $data);
$data['title'] = trans('texts.users_and_tokens');
} }
return View::make("accounts.{$subSection}", $data); public function doSection($section = ACCOUNT_COMPANY_DETAILS)
} elseif ($section == ACCOUNT_PRODUCTS) {
$data = [
'account' => Auth::user()->account,
'title' => trans('texts.product_library'),
];
return View::make('accounts.products', $data);
}
}
public function doSection($section = ACCOUNT_DETAILS, $subSection = false)
{ {
if ($section == ACCOUNT_DETAILS) { if ($section === ACCOUNT_COMPANY_DETAILS) {
return AccountController::saveDetails(); return AccountController::saveDetails();
} elseif ($section == ACCOUNT_IMPORT_EXPORT) { } elseif ($section === ACCOUNT_USER_DETAILS) {
return AccountController::saveUserDetails();
} elseif ($section === ACCOUNT_LOCALIZATION) {
return AccountController::saveLocalization();
} elseif ($section === ACCOUNT_IMPORT_EXPORT) {
return AccountController::importFile(); return AccountController::importFile();
} elseif ($section == ACCOUNT_MAP) { } elseif ($section === ACCOUNT_MAP) {
return AccountController::mapFile(); return AccountController::mapFile();
} elseif ($section == ACCOUNT_NOTIFICATIONS) { } elseif ($section === ACCOUNT_NOTIFICATIONS) {
return AccountController::saveNotifications(); return AccountController::saveNotifications();
} elseif ($section == ACCOUNT_EXPORT) { } elseif ($section === ACCOUNT_EXPORT) {
return AccountController::export(); return AccountController::export();
} elseif ($section == ACCOUNT_ADVANCED_SETTINGS) { } elseif ($section === ACCOUNT_INVOICE_SETTINGS) {
if ($subSection == ACCOUNT_INVOICE_SETTINGS) {
return AccountController::saveInvoiceSettings(); return AccountController::saveInvoiceSettings();
} elseif ($subSection == ACCOUNT_INVOICE_DESIGN) { } elseif ($section === ACCOUNT_INVOICE_DESIGN) {
return AccountController::saveInvoiceDesign(); return AccountController::saveInvoiceDesign();
} elseif ($subSection == ACCOUNT_CUSTOMIZE_DESIGN) { } elseif ($section === ACCOUNT_CUSTOMIZE_DESIGN) {
return AccountController::saveCustomizeDesign(); return AccountController::saveCustomizeDesign();
} elseif ($subSection == ACCOUNT_TEMPLATES_AND_REMINDERS) { } elseif ($section === ACCOUNT_TEMPLATES_AND_REMINDERS) {
return AccountController::saveEmailTemplates(); return AccountController::saveEmailTemplates();
} } elseif ($section === ACCOUNT_PRODUCTS) {
} elseif ($section == ACCOUNT_PRODUCTS) {
return AccountController::saveProducts(); return AccountController::saveProducts();
} elseif ($section === ACCOUNT_TAX_RATES) {
return AccountController::saveTaxRates();
} }
} }
@ -318,7 +410,7 @@ class AccountController extends BaseController
Session::flash('message', trans('texts.updated_settings')); Session::flash('message', trans('texts.updated_settings'));
} }
return Redirect::to('company/advanced_settings/customize_design'); return Redirect::to('settings/' . ACCOUNT_CUSTOMIZE_DESIGN);
} }
private function saveEmailTemplates() private function saveEmailTemplates()
@ -351,7 +443,21 @@ class AccountController extends BaseController
Session::flash('message', trans('texts.updated_settings')); Session::flash('message', trans('texts.updated_settings'));
} }
return Redirect::to('company/advanced_settings/templates_and_reminders'); return Redirect::to('settings/' . ACCOUNT_TEMPLATES_AND_REMINDERS);
}
private function saveTaxRates()
{
$account = Auth::user()->account;
$account->invoice_taxes = Input::get('invoice_taxes') ? true : false;
$account->invoice_item_taxes = Input::get('invoice_item_taxes') ? true : false;
$account->show_item_taxes = Input::get('show_item_taxes') ? true : false;
$account->default_tax_rate_id = Input::get('default_tax_rate_id');
$account->save();
Session::flash('message', trans('texts.updated_settings'));
return Redirect::to('settings/' . ACCOUNT_TAX_RATES);
} }
private function saveProducts() private function saveProducts()
@ -363,14 +469,18 @@ class AccountController extends BaseController
$account->save(); $account->save();
Session::flash('message', trans('texts.updated_settings')); Session::flash('message', trans('texts.updated_settings'));
return Redirect::to('company/products'); return Redirect::to('settings/' . ACCOUNT_PRODUCTS);
} }
private function saveInvoiceSettings() private function saveInvoiceSettings()
{ {
if (Auth::user()->account->isPro()) { if (Auth::user()->account->isPro()) {
$rules = []; $rules = [
'invoice_number_pattern' => 'has_counter',
'quote_number_pattern' => 'has_counter',
];
$user = Auth::user(); $user = Auth::user();
$iframeURL = preg_replace('/[^a-zA-Z0-9_\-\:\/\.]/', '', substr(strtolower(Input::get('iframe_url')), 0, MAX_IFRAME_URL_LENGTH)); $iframeURL = preg_replace('/[^a-zA-Z0-9_\-\:\/\.]/', '', substr(strtolower(Input::get('iframe_url')), 0, MAX_IFRAME_URL_LENGTH));
$iframeURL = rtrim($iframeURL, "/"); $iframeURL = rtrim($iframeURL, "/");
@ -386,7 +496,7 @@ class AccountController extends BaseController
$validator = Validator::make(Input::all(), $rules); $validator = Validator::make(Input::all(), $rules);
if ($validator->fails()) { if ($validator->fails()) {
return Redirect::to('company/details') return Redirect::to('settings/' . ACCOUNT_INVOICE_SETTINGS)
->withErrors($validator) ->withErrors($validator)
->withInput(); ->withInput();
} else { } else {
@ -406,22 +516,39 @@ class AccountController extends BaseController
$account->custom_invoice_text_label1 = trim(Input::get('custom_invoice_text_label1')); $account->custom_invoice_text_label1 = trim(Input::get('custom_invoice_text_label1'));
$account->custom_invoice_text_label2 = trim(Input::get('custom_invoice_text_label2')); $account->custom_invoice_text_label2 = trim(Input::get('custom_invoice_text_label2'));
$account->invoice_number_prefix = Input::get('invoice_number_prefix');
$account->invoice_number_counter = Input::get('invoice_number_counter'); $account->invoice_number_counter = Input::get('invoice_number_counter');
$account->quote_number_prefix = Input::get('quote_number_prefix'); $account->quote_number_prefix = Input::get('quote_number_prefix');
$account->share_counter = Input::get('share_counter') ? true : false; $account->share_counter = Input::get('share_counter') ? true : false;
$account->pdf_email_attachment = Input::get('pdf_email_attachment') ? true : false; $account->pdf_email_attachment = Input::get('pdf_email_attachment') ? true : false;
$account->auto_wrap = Input::get('auto_wrap') ? true : false;
if (Input::has('recurring_hour')) {
$account->recurring_hour = Input::get('recurring_hour');
}
if (!$account->share_counter) { if (!$account->share_counter) {
$account->quote_number_counter = Input::get('quote_number_counter'); $account->quote_number_counter = Input::get('quote_number_counter');
} }
if (Input::get('invoice_number_type') == 'prefix') {
$account->invoice_number_prefix = trim(Input::get('invoice_number_prefix'));
$account->invoice_number_pattern = null;
} else {
$account->invoice_number_pattern = trim(Input::get('invoice_number_pattern'));
$account->invoice_number_prefix = null;
}
if (Input::get('quote_number_type') == 'prefix') {
$account->quote_number_prefix = trim(Input::get('quote_number_prefix'));
$account->quote_number_pattern = null;
} else {
$account->quote_number_pattern = trim(Input::get('quote_number_pattern'));
$account->quote_number_prefix = null;
}
if (!$account->share_counter && $account->invoice_number_prefix == $account->quote_number_prefix) { if (!$account->share_counter && $account->invoice_number_prefix == $account->quote_number_prefix) {
Session::flash('error', trans('texts.invalid_counter')); Session::flash('error', trans('texts.invalid_counter'));
return Redirect::to('settings/' . ACCOUNT_INVOICE_SETTINGS)->withInput();
return Redirect::to('company/advanced_settings/invoice_settings')->withInput();
} else { } else {
$account->save(); $account->save();
Session::flash('message', trans('texts.updated_settings')); Session::flash('message', trans('texts.updated_settings'));
@ -429,7 +556,7 @@ class AccountController extends BaseController
} }
} }
return Redirect::to('company/advanced_settings/invoice_settings'); return Redirect::to('settings/' . ACCOUNT_INVOICE_SETTINGS);
} }
private function saveInvoiceDesign() private function saveInvoiceDesign()
@ -447,7 +574,7 @@ class AccountController extends BaseController
} }
$labels = []; $labels = [];
foreach (['item', 'description', 'unit_cost', 'quantity'] as $field) { foreach (['item', 'description', 'unit_cost', 'quantity', 'line_total'] as $field) {
$labels[$field] = trim(Input::get("labels_{$field}")); $labels[$field] = trim(Input::get("labels_{$field}"));
} }
$account->invoice_labels = json_encode($labels); $account->invoice_labels = json_encode($labels);
@ -457,7 +584,7 @@ class AccountController extends BaseController
Session::flash('message', trans('texts.updated_settings')); Session::flash('message', trans('texts.updated_settings'));
} }
return Redirect::to('company/advanced_settings/invoice_design'); return Redirect::to('settings/' . ACCOUNT_INVOICE_DESIGN);
} }
private function export() private function export()
@ -568,7 +695,7 @@ class AccountController extends BaseController
if ($file == null) { if ($file == null) {
Session::flash('error', trans('texts.select_file')); Session::flash('error', trans('texts.select_file'));
return Redirect::to('company/import_export'); return Redirect::to('settings/' . ACCOUNT_IMPORT_EXPORT);
} }
$name = $file->getRealPath(); $name = $file->getRealPath();
@ -582,7 +709,7 @@ class AccountController extends BaseController
$message = trans('texts.limit_clients', ['count' => Auth::user()->getMaxNumClients()]); $message = trans('texts.limit_clients', ['count' => Auth::user()->getMaxNumClients()]);
Session::flash('error', $message); Session::flash('error', $message);
return Redirect::to('company/import_export'); return Redirect::to('settings/' . ACCOUNT_IMPORT_EXPORT);
} }
Session::put('data', $csv->data); Session::put('data', $csv->data);
@ -680,26 +807,20 @@ class AccountController extends BaseController
Session::flash('message', trans('texts.updated_settings')); Session::flash('message', trans('texts.updated_settings'));
return Redirect::to('company/notifications'); return Redirect::to('settings/' . ACCOUNT_NOTIFICATIONS);
} }
private function saveDetails() private function saveDetails()
{ {
$rules = array( $rules = array(
'name' => 'required', 'name' => 'required',
'logo' => 'sometimes|max:1024|mimes:jpeg,gif,png', 'logo' => 'sometimes|max:512|mimes:jpeg,gif,png',
); );
$user = Auth::user()->account->users()->orderBy('id')->first();
if (Auth::user()->id === $user->id) {
$rules['email'] = 'email|required|unique:users,email,'.$user->id.',id';
}
$validator = Validator::make(Input::all(), $rules); $validator = Validator::make(Input::all(), $rules);
if ($validator->fails()) { if ($validator->fails()) {
return Redirect::to('company/details') return Redirect::to('settings/' . ACCOUNT_COMPANY_DETAILS)
->withErrors($validator) ->withErrors($validator)
->withInput(); ->withInput();
} else { } else {
@ -717,30 +838,8 @@ class AccountController extends BaseController
$account->country_id = Input::get('country_id') ? Input::get('country_id') : null; $account->country_id = Input::get('country_id') ? Input::get('country_id') : null;
$account->size_id = Input::get('size_id') ? Input::get('size_id') : null; $account->size_id = Input::get('size_id') ? Input::get('size_id') : null;
$account->industry_id = Input::get('industry_id') ? Input::get('industry_id') : null; $account->industry_id = Input::get('industry_id') ? Input::get('industry_id') : null;
$account->timezone_id = Input::get('timezone_id') ? Input::get('timezone_id') : null;
$account->date_format_id = Input::get('date_format_id') ? Input::get('date_format_id') : null;
$account->datetime_format_id = Input::get('datetime_format_id') ? Input::get('datetime_format_id') : null;
$account->currency_id = Input::get('currency_id') ? Input::get('currency_id') : 1; // US Dollar
$account->language_id = Input::get('language_id') ? Input::get('language_id') : 1; // English
$account->military_time = Input::get('military_time') ? true : false;
$account->save(); $account->save();
$user = Auth::user();
$user->first_name = trim(Input::get('first_name'));
$user->last_name = trim(Input::get('last_name'));
$user->username = trim(Input::get('email'));
$user->email = trim(strtolower(Input::get('email')));
$user->phone = trim(Input::get('phone'));
if (Utils::isNinja()) {
if (Input::get('referral_code') && !$user->referral_code) {
$user->referral_code = $this->accountRepo->getReferralCode();
}
}
if (Utils::isNinjaDev()) {
$user->dark_mode = Input::get('dark_mode') ? true : false;
}
$user->save();
/* Logo image file */ /* Logo image file */
if ($file = Input::file('logo')) { if ($file = Input::file('logo')) {
$path = Input::file('logo')->getRealPath(); $path = Input::file('logo')->getRealPath();
@ -770,10 +869,61 @@ class AccountController extends BaseController
Event::fire(new UserSettingsChanged()); Event::fire(new UserSettingsChanged());
Session::flash('message', trans('texts.updated_settings')); Session::flash('message', trans('texts.updated_settings'));
return Redirect::to('company/details'); return Redirect::to('settings/' . ACCOUNT_COMPANY_DETAILS);
} }
} }
private function saveUserDetails()
{
$user = Auth::user();
$rules = ['email' => 'email|required|unique:users,email,'.$user->id.',id'];
$validator = Validator::make(Input::all(), $rules);
if ($validator->fails()) {
return Redirect::to('settings/' . ACCOUNT_USER_DETAILS)
->withErrors($validator)
->withInput();
} else {
$user->first_name = trim(Input::get('first_name'));
$user->last_name = trim(Input::get('last_name'));
$user->username = trim(Input::get('email'));
$user->email = trim(strtolower(Input::get('email')));
$user->phone = trim(Input::get('phone'));
if (Utils::isNinja()) {
if (Input::get('referral_code') && !$user->referral_code) {
$user->referral_code = $this->accountRepo->getReferralCode();
}
}
if (Utils::isNinjaDev()) {
$user->dark_mode = Input::get('dark_mode') ? true : false;
}
$user->save();
Event::fire(new UserSettingsChanged());
Session::flash('message', trans('texts.updated_settings'));
return Redirect::to('settings/' . ACCOUNT_USER_DETAILS);
}
}
private function saveLocalization()
{
$account = Auth::user()->account;
$account->timezone_id = Input::get('timezone_id') ? Input::get('timezone_id') : null;
$account->date_format_id = Input::get('date_format_id') ? Input::get('date_format_id') : null;
$account->datetime_format_id = Input::get('datetime_format_id') ? Input::get('datetime_format_id') : null;
$account->currency_id = Input::get('currency_id') ? Input::get('currency_id') : 1; // US Dollar
$account->language_id = Input::get('language_id') ? Input::get('language_id') : 1; // English
$account->military_time = Input::get('military_time') ? true : false;
$account->save();
Event::fire(new UserSettingsChanged());
Session::flash('message', trans('texts.updated_settings'));
return Redirect::to('settings/' . ACCOUNT_LOCALIZATION);
}
public function removeLogo() public function removeLogo()
{ {
File::delete('logo/'.Auth::user()->account->account_key.'.jpg'); File::delete('logo/'.Auth::user()->account->account_key.'.jpg');
@ -781,7 +931,7 @@ class AccountController extends BaseController
Session::flash('message', trans('texts.removed_logo')); Session::flash('message', trans('texts.removed_logo'));
return Redirect::to('company/details'); return Redirect::to('settings/' . ACCOUNT_COMPANY_DETAILS);
} }
public function checkEmail() public function checkEmail()
@ -864,7 +1014,10 @@ class AccountController extends BaseController
$this->userMailer->sendTo(CONTACT_EMAIL, $email, $name, 'Invoice Ninja Feedback [Canceled Account]', 'contact', $data); $this->userMailer->sendTo(CONTACT_EMAIL, $email, $name, 'Invoice Ninja Feedback [Canceled Account]', 'contact', $data);
} }
$user = Auth::user();
$account = Auth::user()->account; $account = Auth::user()->account;
\Log::info("Canceled Account: {$account->name} - {$user->email}");
$this->accountRepo->unlinkAccount($account); $this->accountRepo->unlinkAccount($account);
$account->forceDelete(); $account->forceDelete();
@ -879,6 +1032,26 @@ class AccountController extends BaseController
$user = Auth::user(); $user = Auth::user();
$this->userMailer->sendConfirmation($user); $this->userMailer->sendConfirmation($user);
return Redirect::to('/company/details')->with('message', trans('texts.confirmation_resent')); return Redirect::to('/settings/' . ACCOUNT_COMPANY_DETAILS)->with('message', trans('texts.confirmation_resent'));
}
public function redirectLegacy($section, $subSection = false)
{
if ($section === 'details') {
$section = ACCOUNT_COMPANY_DETAILS;
} elseif ($section === 'payments') {
$section = ACCOUNT_PAYMENTS;
} elseif ($section === 'advanced_settings') {
$section = $subSection;
if ($section === 'token_management') {
$section = ACCOUNT_API_TOKENS;
}
}
if (!in_array($section, array_merge(Account::$basicSettings, Account::$advancedSettings))) {
$section = ACCOUNT_COMPANY_DETAILS;
}
return Redirect::to("/settings/$section/", 301);
} }
} }

View File

@ -19,6 +19,11 @@ use App\Ninja\Repositories\AccountRepository;
class AccountGatewayController extends BaseController class AccountGatewayController extends BaseController
{ {
public function index()
{
return Redirect::to('settings/' . ACCOUNT_PAYMENTS);
}
public function getDatatable() public function getDatatable()
{ {
$query = DB::table('account_gateways') $query = DB::table('account_gateways')
@ -159,7 +164,6 @@ class AccountGatewayController extends BaseController
'gateways' => $gateways, 'gateways' => $gateways,
'creditCardTypes' => $creditCards, 'creditCardTypes' => $creditCards,
'tokenBillingOptions' => $tokenBillingOptions, 'tokenBillingOptions' => $tokenBillingOptions,
'showBreadcrumbs' => false,
'countGateways' => count($currentGateways) 'countGateways' => count($currentGateways)
]; ];
} }
@ -173,7 +177,7 @@ class AccountGatewayController extends BaseController
Session::flash('message', trans('texts.deleted_gateway')); Session::flash('message', trans('texts.deleted_gateway'));
return Redirect::to('company/payments'); return Redirect::to('settings/' . ACCOUNT_PAYMENTS);
} }
/** /**

View File

@ -56,7 +56,7 @@ class AppController extends BaseController
$test = Input::get('test'); $test = Input::get('test');
$app = Input::get('app'); $app = Input::get('app');
$app['key'] = str_random(RANDOM_KEY_LENGTH); $app['key'] = env('APP_KEY') ?: str_random(RANDOM_KEY_LENGTH);
$database = Input::get('database'); $database = Input::get('database');
$dbType = $database['default']; $dbType = $database['default'];
@ -94,7 +94,7 @@ class AppController extends BaseController
"MAIL_USERNAME={$mail['username']}\n". "MAIL_USERNAME={$mail['username']}\n".
"MAIL_FROM_NAME={$mail['from']['name']}\n". "MAIL_FROM_NAME={$mail['from']['name']}\n".
"MAIL_PASSWORD={$mail['password']}\n\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 // Write Config Settings
$fp = fopen(base_path()."/.env", 'w'); $fp = fopen(base_path()."/.env", 'w');

View File

@ -61,7 +61,7 @@ class AuthController extends Controller {
$this->accountRepo->unlinkUserFromOauth(Auth::user()); $this->accountRepo->unlinkUserFromOauth(Auth::user());
Session::flash('message', trans('texts.updated_settings')); Session::flash('message', trans('texts.updated_settings'));
return redirect()->to('/company/details'); return redirect()->to('/settings/' . ACCOUNT_USER_DETAILS);
} }
public function getLoginWrapper() public function getLoginWrapper()
@ -92,7 +92,7 @@ class AuthController extends Controller {
// we're linking a new account // we're linking a new account
if ($userId && Auth::user()->id != $userId) { if ($userId && Auth::user()->id != $userId) {
$users = $this->accountRepo->associateAccounts($userId, Auth::user()->id); $users = $this->accountRepo->associateAccounts($userId, Auth::user()->id);
Session::flash('message', trans('texts.associated_accounts')); Session::flash('warning', trans('texts.associated_accounts'));
// check if other accounts are linked // check if other accounts are linked
} else { } else {
$users = $this->accountRepo->loadAccounts(Auth::user()->id); $users = $this->accountRepo->loadAccounts(Auth::user()->id);

View File

@ -258,6 +258,13 @@ class ClientController extends BaseController
$client->payment_terms = Input::get('payment_terms') ?: 0; $client->payment_terms = Input::get('payment_terms') ?: 0;
$client->website = trim(Input::get('website')); $client->website = trim(Input::get('website'));
if (Input::has('invoice_number_counter')) {
$client->invoice_number_counter = (int) Input::get('invoice_number_counter');
}
if (Input::has('quote_number_counter')) {
$client->invoice_number_counter = (int) Input::get('quote_number_counter');
}
$client->save(); $client->save();
$data = json_decode(Input::get('data')); $data = json_decode(Input::get('data'));

View File

@ -59,16 +59,6 @@ class InvoiceApiController extends Controller
$data = Input::all(); $data = Input::all();
$error = null; $error = null;
// check if the invoice number is set and unique
if (!isset($data['invoice_number']) && !isset($data['id'])) {
$data['invoice_number'] = Auth::user()->account->getNextInvoiceNumber();
} else if (isset($data['invoice_number'])) {
$invoice = Invoice::scope()->where('invoice_number', '=', $data['invoice_number'])->first();
if ($invoice) {
$error = trans('validation.unique', ['attribute' => 'texts.invoice_number']);
}
}
if (isset($data['email'])) { if (isset($data['email'])) {
$client = Client::scope()->whereHas('contacts', function($query) use ($data) { $client = Client::scope()->whereHas('contacts', function($query) use ($data) {
$query->where('email', '=', $data['email']); $query->where('email', '=', $data['email']);
@ -95,6 +85,16 @@ class InvoiceApiController extends Controller
$client = Client::scope($data['client_id'])->first(); $client = Client::scope($data['client_id'])->first();
} }
// check if the invoice number is set and unique
if (!isset($data['invoice_number']) && !isset($data['id'])) {
// do nothing... invoice number will be set automatically
} else if (isset($data['invoice_number'])) {
$invoice = Invoice::scope()->where('invoice_number', '=', $data['invoice_number'])->first();
if ($invoice) {
$error = trans('validation.unique', ['attribute' => 'texts.invoice_number']);
}
}
if (!$error) { if (!$error) {
if (!isset($data['client_id']) && !isset($data['email'])) { if (!isset($data['client_id']) && !isset($data['email'])) {
$error = trans('validation.', ['attribute' => 'client_id or email']); $error = trans('validation.', ['attribute' => 'client_id or email']);

View File

@ -31,7 +31,6 @@ use App\Models\Gateway;
use App\Ninja\Mailers\ContactMailer as Mailer; use App\Ninja\Mailers\ContactMailer as Mailer;
use App\Ninja\Repositories\InvoiceRepository; use App\Ninja\Repositories\InvoiceRepository;
use App\Ninja\Repositories\ClientRepository; use App\Ninja\Repositories\ClientRepository;
use App\Ninja\Repositories\TaxRateRepository;
use App\Events\InvoiceViewed; use App\Events\InvoiceViewed;
class InvoiceController extends BaseController class InvoiceController extends BaseController
@ -39,16 +38,14 @@ class InvoiceController extends BaseController
protected $mailer; protected $mailer;
protected $invoiceRepo; protected $invoiceRepo;
protected $clientRepo; protected $clientRepo;
protected $taxRateRepo;
public function __construct(Mailer $mailer, InvoiceRepository $invoiceRepo, ClientRepository $clientRepo, TaxRateRepository $taxRateRepo) public function __construct(Mailer $mailer, InvoiceRepository $invoiceRepo, ClientRepository $clientRepo)
{ {
parent::__construct(); parent::__construct();
$this->mailer = $mailer; $this->mailer = $mailer;
$this->invoiceRepo = $invoiceRepo; $this->invoiceRepo = $invoiceRepo;
$this->clientRepo = $clientRepo; $this->clientRepo = $clientRepo;
$this->taxRateRepo = $taxRateRepo;
} }
public function index() public function index()
@ -131,13 +128,22 @@ class InvoiceController extends BaseController
public function view($invitationKey) public function view($invitationKey)
{ {
$invitation = $this->invoiceRepo->findInvoiceByInvitation($invitationKey); if (!$invitation = $this->invoiceRepo->findInvoiceByInvitation($invitationKey)) {
return response()->view('error', [
'error' => trans('texts.invoice_not_found'),
'hideHeader' => true,
]);
}
$invoice = $invitation->invoice; $invoice = $invitation->invoice;
$client = $invoice->client; $client = $invoice->client;
$account = $invoice->account; $account = $invoice->account;
if (!$account->checkSubdomain(Request::server('HTTP_HOST'))) { if (!$account->checkSubdomain(Request::server('HTTP_HOST'))) {
app()->abort(404, trans('texts.invoice_not_found')); return response()->view('error', [
'error' => trans('texts.invoice_not_found'),
'hideHeader' => true,
]);
} }
if (!Input::has('phantomjs') && !Session::has($invitationKey) && (!Auth::check() || Auth::user()->account_id != $invoice->account_id)) { if (!Input::has('phantomjs') && !Session::has($invitationKey) && (!Auth::check() || Auth::user()->account_id != $invoice->account_id)) {
@ -159,9 +165,7 @@ class InvoiceController extends BaseController
} else { } else {
$invoice->invoice_design->javascript = $invoice->invoice_design->pdfmake; $invoice->invoice_design->javascript = $invoice->invoice_design->pdfmake;
} }
$contact = $invitation->contact; $contact->setVisible([
$contact = $invitation->contact;
$contact->setVisible([
'first_name', 'first_name',
'last_name', 'last_name',
'email', 'email',
@ -229,6 +233,7 @@ class InvoiceController extends BaseController
public function edit($publicId, $clone = false) public function edit($publicId, $clone = false)
{ {
$account = Auth::user()->account;
$invoice = Invoice::scope($publicId)->withTrashed()->with('invitations', 'account.country', 'client.contacts', 'client.country', 'invoice_items')->firstOrFail(); $invoice = Invoice::scope($publicId)->withTrashed()->with('invitations', 'account.country', 'client.contacts', 'client.country', 'invoice_items')->firstOrFail();
$entityType = $invoice->getEntityType(); $entityType = $invoice->getEntityType();
@ -241,7 +246,7 @@ class InvoiceController extends BaseController
if ($clone) { if ($clone) {
$invoice->id = null; $invoice->id = null;
$invoice->invoice_number = Auth::user()->account->getNextInvoiceNumber($invoice->is_quote); $invoice->invoice_number = $account->getNextInvoiceNumber($invoice);
$invoice->balance = $invoice->amount; $invoice->balance = $invoice->amount;
$invoice->invoice_status_id = 0; $invoice->invoice_status_id = 0;
$invoice->invoice_date = date_create()->format('Y-m-d'); $invoice->invoice_date = date_create()->format('Y-m-d');
@ -299,7 +304,6 @@ class InvoiceController extends BaseController
'entityType' => $entityType, 'entityType' => $entityType,
'showBreadcrumbs' => $clone, 'showBreadcrumbs' => $clone,
'invoice' => $invoice, 'invoice' => $invoice,
'data' => false,
'method' => $method, 'method' => $method,
'invitationContactIds' => $contactIds, 'invitationContactIds' => $contactIds,
'url' => $url, 'url' => $url,
@ -327,7 +331,7 @@ class InvoiceController extends BaseController
if ($invitation->contact_id == $contact->id) { if ($invitation->contact_id == $contact->id) {
$contact->email_error = $invitation->email_error; $contact->email_error = $invitation->email_error;
$contact->invitation_link = $invitation->getLink(); $contact->invitation_link = $invitation->getLink();
$contact->invitation_viewed = $invitation->viewed_date; $contact->invitation_viewed = $invitation->viewed_date && $invitation->viewed_date != '0000-00-00 00:00:00' ? $invitation->viewed_date : false;
$contact->invitation_status = $contact->email_error ? false : $invitation->getStatus(); $contact->invitation_status = $contact->email_error ? false : $invitation->getStatus();
} }
} }
@ -342,23 +346,21 @@ class InvoiceController extends BaseController
public function create($clientPublicId = 0, $isRecurring = false) public function create($clientPublicId = 0, $isRecurring = false)
{ {
$client = null; $account = Auth::user()->account;
$invoiceNumber = $isRecurring ? microtime(true) : Auth::user()->account->getNextInvoiceNumber(); $clientId = null;
if ($clientPublicId) { if ($clientPublicId) {
$client = Client::scope($clientPublicId)->firstOrFail(); $clientId = Client::getPrivateId($clientPublicId);
} }
$entityType = $isRecurring ? ENTITY_RECURRING_INVOICE : ENTITY_INVOICE;
$invoice = $account->createInvoice($entityType, $clientId);
$data = array( $data = [
'entityType' => ENTITY_INVOICE, 'entityType' => $invoice->getEntityType(),
'invoice' => null, 'invoice' => $invoice,
'data' => Input::old('data'),
'invoiceNumber' => $invoiceNumber,
'method' => 'POST', 'method' => 'POST',
'url' => 'invoices', 'url' => 'invoices',
'title' => trans('texts.new_invoice'), 'title' => trans('texts.new_invoice'),
'isRecurring' => $isRecurring, ];
'client' => $client);
$data = array_merge($data, self::getViewModel()); $data = array_merge($data, self::getViewModel());
return View::make('invoices.edit', $data); return View::make('invoices.edit', $data);
@ -383,8 +385,9 @@ class InvoiceController extends BaseController
} }
return [ return [
'data' => Input::old('data'),
'account' => Auth::user()->account->load('country'), 'account' => Auth::user()->account->load('country'),
'products' => Product::scope()->orderBy('id')->get(array('product_key', 'notes', 'cost', 'qty')), 'products' => Product::scope()->with('default_tax_rate')->orderBy('id')->get(),
'countries' => Cache::get('countries'), 'countries' => Cache::get('countries'),
'clients' => Client::scope()->with('contacts', 'country')->orderBy('name')->get(), 'clients' => Client::scope()->with('contacts', 'country')->orderBy('name')->get(),
'taxRates' => TaxRate::scope()->orderBy('name')->get(), 'taxRates' => TaxRate::scope()->orderBy('name')->get(),
@ -432,9 +435,8 @@ class InvoiceController extends BaseController
if ($errors = $this->invoiceRepo->getErrors($input->invoice)) { if ($errors = $this->invoiceRepo->getErrors($input->invoice)) {
Session::flash('error', trans('texts.invoice_error')); Session::flash('error', trans('texts.invoice_error'));
$url = "{$entityType}s/" . ($publicId ?: 'create');
return Redirect::to("{$entityType}s/create") return Redirect::to($url)->withInput()->withErrors($errors);
->withInput()->withErrors($errors);
} else { } else {
$invoice = $this->saveInvoice($publicId, $input, $entityType); $invoice = $this->saveInvoice($publicId, $input, $entityType);
$url = "{$entityType}s/".$invoice->public_id.'/edit'; $url = "{$entityType}s/".$invoice->public_id.'/edit';
@ -443,8 +445,8 @@ class InvoiceController extends BaseController
// check if we created a new client with the invoice // check if we created a new client with the invoice
if ($input->invoice->client->public_id == '-1') { if ($input->invoice->client->public_id == '-1') {
$message = $message.' '.trans('texts.and_created_client'); $message = $message.' '.trans('texts.and_created_client');
$url = URL::to('clients/'.$input->invoice->client->public_id); $trackUrl = URL::to('clients/'.$invoice->client->public_id);
Utils::trackViewed($client->getDisplayName(), ENTITY_CLIENT, $url); Utils::trackViewed($invoice->client->getDisplayName(), ENTITY_CLIENT, $trackUrl);
} }
if ($action == 'clone') { if ($action == 'clone') {
@ -468,8 +470,7 @@ class InvoiceController extends BaseController
if (!Auth::user()->confirmed) { if (!Auth::user()->confirmed) {
$errorMessage = trans(Auth::user()->registered ? 'texts.confirmation_required' : 'texts.registration_required'); $errorMessage = trans(Auth::user()->registered ? 'texts.confirmation_required' : 'texts.registration_required');
Session::flash('error', $errorMessage); Session::flash('error', $errorMessage);
Session::flash('message', $message); return Redirect::to('invoices/'.$invoice->public_id.'/edit');
return Redirect::to($url);
} }
if ($invoice->is_recurring) { if ($invoice->is_recurring) {
@ -491,7 +492,13 @@ class InvoiceController extends BaseController
private function emailRecurringInvoice(&$invoice) private function emailRecurringInvoice(&$invoice)
{ {
if (!$invoice->shouldSendToday()) { if (!$invoice->shouldSendToday()) {
return trans('texts.recurring_too_soon'); if ($date = $invoice->getNextSendDate()) {
$date = $invoice->account->formatDate($date);
$date .= ' ' . DEFAULT_SEND_RECURRING_HOUR . ':00 am ' . $invoice->account->getTimezone();
return trans('texts.recurring_too_soon', ['date' => $date]);
} else {
return trans('texts.no_longer_running');
}
} }
// switch from the recurring invoice to the generated invoice // switch from the recurring invoice to the generated invoice
@ -509,8 +516,6 @@ class InvoiceController extends BaseController
{ {
$invoice = $input->invoice; $invoice = $input->invoice;
$this->taxRateRepo->save($input->tax_rates);
$clientData = (array) $invoice->client; $clientData = (array) $invoice->client;
$client = $this->clientRepo->save($invoice->client->public_id, $clientData); $client = $this->clientRepo->save($invoice->client->public_id, $clientData);
@ -518,18 +523,6 @@ class InvoiceController extends BaseController
$invoiceData['client_id'] = $client->id; $invoiceData['client_id'] = $client->id;
$invoice = $this->invoiceRepo->save($publicId, $invoiceData, $entityType); $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'); $client->load('contacts');
$sendInvoiceIds = []; $sendInvoiceIds = [];

View File

@ -174,7 +174,8 @@ class PaymentController extends BaseController
$acceptedCreditCardTypes = $accountGateway->getCreditcardTypes(); $acceptedCreditCardTypes = $accountGateway->getCreditcardTypes();
// Handle offsite payments // Handle offsite payments
if ($useToken || $paymentType != PAYMENT_TYPE_CREDIT_CARD || $gateway->id == GATEWAY_EWAY) { if ($useToken || $paymentType != PAYMENT_TYPE_CREDIT_CARD
|| $gateway->id == GATEWAY_EWAY || $gateway->id == GATEWAY_TWO_CHECKOUT) {
if (Session::has('error')) { if (Session::has('error')) {
Session::reflash(); Session::reflash();
return Redirect::to('view/'.$invitationKey); return Redirect::to('view/'.$invitationKey);
@ -418,7 +419,7 @@ class PaymentController extends BaseController
} }
$gateway = $this->paymentService->createGateway($accountGateway); $gateway = $this->paymentService->createGateway($accountGateway);
$details = $this->paymentService->getPaymentDetails($invitation, $data); $details = $this->paymentService->getPaymentDetails($invitation, $accountGateway, $data);
// check if we're creating/using a billing token // check if we're creating/using a billing token
if ($accountGateway->gateway_id == GATEWAY_STRIPE) { if ($accountGateway->gateway_id == GATEWAY_STRIPE) {
@ -439,7 +440,8 @@ class PaymentController extends BaseController
if ($accountGateway->gateway_id == GATEWAY_EWAY) { if ($accountGateway->gateway_id == GATEWAY_EWAY) {
$ref = $response->getData()['AccessCode']; $ref = $response->getData()['AccessCode'];
$token = $response->getCardReference(); } elseif ($accountGateway->gateway_id == GATEWAY_TWO_CHECKOUT) {
$ref = $response->getData()['cart_order_id'];
} else { } else {
$ref = $response->getTransactionReference(); $ref = $response->getTransactionReference();
} }
@ -467,7 +469,6 @@ class PaymentController extends BaseController
} elseif ($response->isRedirect()) { } elseif ($response->isRedirect()) {
$invitation->transaction_reference = $ref; $invitation->transaction_reference = $ref;
$invitation->save(); $invitation->save();
Session::put('transaction_reference', $ref); Session::put('transaction_reference', $ref);
Session::save(); Session::save();
$response->redirect(); $response->redirect();
@ -513,8 +514,8 @@ class PaymentController extends BaseController
} }
try { try {
if (method_exists($gateway, 'completePurchase')) { if (method_exists($gateway, 'completePurchase') && !$accountGateway->isGateway(GATEWAY_TWO_CHECKOUT)) {
$details = $this->paymentService->getPaymentDetails($invitation); $details = $this->paymentService->getPaymentDetails($invitation, $accountGateway);
$response = $gateway->completePurchase($details)->send(); $response = $gateway->completePurchase($details)->send();
$ref = $response->getTransactionReference(); $ref = $response->getTransactionReference();

View File

@ -12,21 +12,38 @@ use Session;
use Redirect; use Redirect;
use App\Models\Product; use App\Models\Product;
use App\Models\TaxRate;
class ProductController extends BaseController class ProductController extends BaseController
{ {
public function index()
{
return Redirect::to('settings/' . ACCOUNT_PRODUCTS);
}
public function getDatatable() public function getDatatable()
{ {
$account = Auth::user()->account;
$query = DB::table('products') $query = DB::table('products')
->leftJoin('tax_rates', function($join){
$join->on('tax_rates.id', '=', 'products.default_tax_rate_id')
->whereNull('tax_rates.deleted_at');
})
->where('products.account_id', '=', Auth::user()->account_id) ->where('products.account_id', '=', Auth::user()->account_id)
->where('products.deleted_at', '=', null) ->where('products.deleted_at', '=', null)
->select('products.public_id', 'products.product_key', 'products.notes', 'products.cost'); ->select('products.public_id', 'products.product_key', 'products.notes', 'products.cost', 'tax_rates.name as tax_name', 'tax_rates.rate as tax_rate');
return Datatable::query($query) $datatable = Datatable::query($query)
->addColumn('product_key', function ($model) { return link_to('products/'.$model->public_id.'/edit', $model->product_key); }) ->addColumn('product_key', function ($model) { return link_to('products/'.$model->public_id.'/edit', $model->product_key); })
->addColumn('notes', function ($model) { return nl2br(Str::limit($model->notes, 100)); }) ->addColumn('notes', function ($model) { return nl2br(Str::limit($model->notes, 100)); })
->addColumn('cost', function ($model) { return Utils::formatMoney($model->cost); }) ->addColumn('cost', function ($model) { return Utils::formatMoney($model->cost); });
->addColumn('dropdown', function ($model) {
if ($account->invoice_item_taxes) {
$datatable->addColumn('tax_rate', function ($model) { return $model->tax_rate ? ($model->tax_name . ' ' . $model->tax_rate . '%') : ''; });
}
return $datatable->addColumn('dropdown', function ($model) {
return '<div class="btn-group tr-action" style="visibility:hidden;"> return '<div class="btn-group tr-action" style="visibility:hidden;">
<button type="button" class="btn btn-xs btn-default dropdown-toggle" data-toggle="dropdown"> <button type="button" class="btn btn-xs btn-default dropdown-toggle" data-toggle="dropdown">
'.trans('texts.select').' <span class="caret"></span> '.trans('texts.select').' <span class="caret"></span>
@ -44,8 +61,11 @@ class ProductController extends BaseController
public function edit($publicId) public function edit($publicId)
{ {
$account = Auth::user()->account;
$data = [ $data = [
'showBreadcrumbs' => false, 'account' => $account,
'taxRates' => $account->invoice_item_taxes ? TaxRate::scope()->get(['id', 'name', 'rate']) : null,
'product' => Product::scope($publicId)->firstOrFail(), 'product' => Product::scope($publicId)->firstOrFail(),
'method' => 'PUT', 'method' => 'PUT',
'url' => 'products/'.$publicId, 'url' => 'products/'.$publicId,
@ -57,8 +77,11 @@ class ProductController extends BaseController
public function create() public function create()
{ {
$account = Auth::user()->account;
$data = [ $data = [
'showBreadcrumbs' => false, 'account' => $account,
'taxRates' => $account->invoice_item_taxes ? TaxRate::scope()->get(['id', 'name', 'rate']) : null,
'product' => null, 'product' => null,
'method' => 'POST', 'method' => 'POST',
'url' => 'products', 'url' => 'products',
@ -89,12 +112,14 @@ class ProductController extends BaseController
$product->product_key = trim(Input::get('product_key')); $product->product_key = trim(Input::get('product_key'));
$product->notes = trim(Input::get('notes')); $product->notes = trim(Input::get('notes'));
$product->cost = trim(Input::get('cost')); $product->cost = trim(Input::get('cost'));
$product->default_tax_rate_id = Input::get('default_tax_rate_id');
$product->save(); $product->save();
$message = $productPublicId ? trans('texts.updated_product') : trans('texts.created_product'); $message = $productPublicId ? trans('texts.updated_product') : trans('texts.created_product');
Session::flash('message', $message); Session::flash('message', $message);
return Redirect::to('company/products'); return Redirect::to('settings/' . ACCOUNT_PRODUCTS);
} }
public function archive($publicId) public function archive($publicId)
@ -104,6 +129,6 @@ class ProductController extends BaseController
Session::flash('message', trans('texts.archived_product')); Session::flash('message', trans('texts.archived_product'));
return Redirect::to('company/products'); return Redirect::to('settings/' . ACCOUNT_PRODUCTS);
} }
} }

View File

@ -22,7 +22,9 @@ class PublicClientController extends BaseController
public function dashboard() public function dashboard()
{ {
$invitation = $this->getInvitation(); if (!$invitation = $this->getInvitation()) {
return $this->returnError();
}
$account = $invitation->account; $account = $invitation->account;
$invoice = $invitation->invoice; $invoice = $invitation->invoice;
$client = $invoice->client; $client = $invoice->client;
@ -32,6 +34,7 @@ class PublicClientController extends BaseController
'color' => $color, 'color' => $color,
'account' => $account, 'account' => $account,
'client' => $client, 'client' => $client,
'hideLogo' => $account->isWhiteLabel(),
]; ];
return response()->view('invited.dashboard', $data); return response()->view('invited.dashboard', $data);
@ -39,7 +42,9 @@ class PublicClientController extends BaseController
public function activityDatatable() public function activityDatatable()
{ {
$invitation = $this->getInvitation(); if (!$invitation = $this->getInvitation()) {
return false;
}
$invoice = $invitation->invoice; $invoice = $invitation->invoice;
$query = DB::table('activities') $query = DB::table('activities')
@ -58,7 +63,9 @@ class PublicClientController extends BaseController
public function invoiceIndex() public function invoiceIndex()
{ {
$invitation = $this->getInvitation(); if (!$invitation = $this->getInvitation()) {
return $this->returnError();
}
$account = $invitation->account; $account = $invitation->account;
$color = $account->primary_color ? $account->primary_color : '#0b4d78'; $color = $account->primary_color ? $account->primary_color : '#0b4d78';
@ -75,7 +82,9 @@ class PublicClientController extends BaseController
public function invoiceDatatable() public function invoiceDatatable()
{ {
$invitation = $this->getInvitation(); if (!$invitation = $this->getInvitation()) {
return false;
}
return $this->invoiceRepo->getClientDatatable($invitation->contact_id, ENTITY_INVOICE, Input::get('sSearch')); return $this->invoiceRepo->getClientDatatable($invitation->contact_id, ENTITY_INVOICE, Input::get('sSearch'));
} }
@ -83,7 +92,9 @@ class PublicClientController extends BaseController
public function paymentIndex() public function paymentIndex()
{ {
$invitation = $this->getInvitation(); if (!$invitation = $this->getInvitation()) {
return $this->returnError();
}
$account = $invitation->account; $account = $invitation->account;
$color = $account->primary_color ? $account->primary_color : '#0b4d78'; $color = $account->primary_color ? $account->primary_color : '#0b4d78';
@ -100,7 +111,9 @@ class PublicClientController extends BaseController
public function paymentDatatable() public function paymentDatatable()
{ {
$invitation = $this->getInvitation(); if (!$invitation = $this->getInvitation()) {
return false;
}
$payments = $this->paymentRepo->findForContact($invitation->contact->id, Input::get('sSearch')); $payments = $this->paymentRepo->findForContact($invitation->contact->id, Input::get('sSearch'));
return Datatable::query($payments) return Datatable::query($payments)
@ -114,7 +127,9 @@ class PublicClientController extends BaseController
public function quoteIndex() public function quoteIndex()
{ {
$invitation = $this->getInvitation(); if (!$invitation = $this->getInvitation()) {
return $this->returnError();
}
$account = $invitation->account; $account = $invitation->account;
$color = $account->primary_color ? $account->primary_color : '#0b4d78'; $color = $account->primary_color ? $account->primary_color : '#0b4d78';
@ -132,29 +147,39 @@ class PublicClientController extends BaseController
public function quoteDatatable() public function quoteDatatable()
{ {
$invitation = $this->getInvitation(); if (!$invitation = $this->getInvitation()) {
return false;
}
return $this->invoiceRepo->getClientDatatable($invitation->contact_id, ENTITY_QUOTE, Input::get('sSearch')); return $this->invoiceRepo->getClientDatatable($invitation->contact_id, ENTITY_QUOTE, Input::get('sSearch'));
} }
private function returnError()
{
return response()->view('error', [
'error' => trans('texts.invoice_not_found'),
'hideHeader' => true,
]);
}
private function getInvitation() private function getInvitation()
{ {
$invitationKey = session('invitation_key'); $invitationKey = session('invitation_key');
if (!$invitationKey) { if (!$invitationKey) {
app()->abort(404); return false;
} }
$invitation = Invitation::where('invitation_key', '=', $invitationKey)->first(); $invitation = Invitation::where('invitation_key', '=', $invitationKey)->first();
if (!$invitation || $invitation->is_deleted) { if (!$invitation || $invitation->is_deleted) {
app()->abort(404); return false;
} }
$invoice = $invitation->invoice; $invoice = $invitation->invoice;
if (!$invoice || $invoice->is_deleted) { if (!$invoice || $invoice->is_deleted) {
app()->abort(404); return false;
} }
return $invitation; return $invitation;

View File

@ -81,23 +81,21 @@ class QuoteController extends BaseController
return Redirect::to('/invoices/create'); return Redirect::to('/invoices/create');
} }
$client = null; $account = Auth::user()->account;
$invoiceNumber = Auth::user()->account->getNextInvoiceNumber(true); $clientId = null;
$account = Account::with('country')->findOrFail(Auth::user()->account_id);
if ($clientPublicId) { if ($clientPublicId) {
$client = Client::scope($clientPublicId)->firstOrFail(); $clientId = Client::getPrivateId($clientPublicId);
} }
$invoice = $account->createInvoice(ENTITY_QUOTE, $clientId);
$data = array( $data = [
'account' => $account, 'entityType' => $invoice->getEntityType(),
'invoice' => null, 'invoice' => $invoice,
'data' => Input::old('data'), 'data' => Input::old('data'),
'invoiceNumber' => $invoiceNumber,
'method' => 'POST', 'method' => 'POST',
'url' => 'invoices', 'url' => 'invoices',
'title' => trans('texts.new_quote'), 'title' => trans('texts.new_quote'),
'client' => $client, ); ];
$data = array_merge($data, self::getViewModel()); $data = array_merge($data, self::getViewModel());
return View::make('invoices.edit', $data); return View::make('invoices.edit', $data);

View File

@ -33,7 +33,6 @@ class ReportController extends BaseController
} }
$data = [ $data = [
'feature' => ACCOUNT_DATA_VISUALIZATIONS,
'clients' => $clients, 'clients' => $clients,
'message' => $message, 'message' => $message,
]; ];
@ -276,7 +275,6 @@ class ReportController extends BaseController
'startDate' => $startDate->format(Session::get(SESSION_DATE_FORMAT)), 'startDate' => $startDate->format(Session::get(SESSION_DATE_FORMAT)),
'endDate' => $endDate->format(Session::get(SESSION_DATE_FORMAT)), 'endDate' => $endDate->format(Session::get(SESSION_DATE_FORMAT)),
'groupBy' => $groupBy, 'groupBy' => $groupBy,
'feature' => ACCOUNT_CHART_BUILDER,
'displayData' => $displayData, 'displayData' => $displayData,
'columns' => $columns, 'columns' => $columns,
'reportTotals' => $reportTotals, 'reportTotals' => $reportTotals,

View File

@ -0,0 +1,110 @@
<?php namespace App\Http\Controllers;
use Auth;
use Str;
use DB;
use Datatable;
use Utils;
use URL;
use View;
use Input;
use Session;
use Redirect;
use App\Models\TaxRate;
class TaxRateController extends BaseController
{
public function index()
{
return Redirect::to('settings/' . ACCOUNT_TAX_RATES);
}
public function getDatatable()
{
$query = DB::table('tax_rates')
->where('tax_rates.account_id', '=', Auth::user()->account_id)
->where('tax_rates.deleted_at', '=', null)
->select('tax_rates.public_id', 'tax_rates.name', 'tax_rates.rate');
return Datatable::query($query)
->addColumn('name', function ($model) { return link_to('tax_rates/'.$model->public_id.'/edit', $model->name); })
->addColumn('rate', function ($model) { return $model->rate . '%'; })
->addColumn('dropdown', function ($model) {
return '<div class="btn-group tr-action" style="visibility:hidden;">
<button type="button" class="btn btn-xs btn-default dropdown-toggle" data-toggle="dropdown">
'.trans('texts.select').' <span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu">
<li><a href="'.URL::to('tax_rates/'.$model->public_id).'/edit">'.uctrans('texts.edit_tax_rate').'</a></li>
<li class="divider"></li>
<li><a href="'.URL::to('tax_rates/'.$model->public_id).'/archive">'.uctrans('texts.archive_tax_rate').'</a></li>
</ul>
</div>';
})
->orderColumns(['name', 'rate'])
->make();
}
public function edit($publicId)
{
$data = [
'taxRate' => TaxRate::scope($publicId)->firstOrFail(),
'method' => 'PUT',
'url' => 'tax_rates/'.$publicId,
'title' => trans('texts.edit_tax_rate'),
];
return View::make('accounts.tax_rate', $data);
}
public function create()
{
$data = [
'taxRate' => null,
'method' => 'POST',
'url' => 'tax_rates',
'title' => trans('texts.create_tax_rate'),
];
return View::make('accounts.tax_rate', $data);
}
public function store()
{
return $this->save();
}
public function update($publicId)
{
return $this->save($publicId);
}
private function save($publicId = false)
{
if ($publicId) {
$taxRate = TaxRate::scope($publicId)->firstOrFail();
} else {
$taxRate = TaxRate::createNew();
}
$taxRate->name = trim(Input::get('name'));
$taxRate->rate = Utils::parseFloat(Input::get('rate'));
$taxRate->save();
$message = $publicId ? trans('texts.updated_tax_rate') : trans('texts.created_tax_rate');
Session::flash('message', $message);
return Redirect::to('settings/' . ACCOUNT_TAX_RATES);
}
public function archive($publicId)
{
$tax_rate = TaxRate::scope($publicId)->firstOrFail();
$tax_rate->delete();
Session::flash('message', trans('texts.archived_tax_rate'));
return Redirect::to('settings/' . ACCOUNT_TAX_RATES);
}
}

View File

@ -25,6 +25,11 @@ use App\Ninja\Repositories\AccountRepository;
class TokenController extends BaseController class TokenController extends BaseController
{ {
public function index()
{
return Redirect::to('settings/' . ACCOUNT_API_TOKENS);
}
public function getDatatable() public function getDatatable()
{ {
$query = DB::table('account_tokens') $query = DB::table('account_tokens')
@ -67,7 +72,6 @@ class TokenController extends BaseController
->where('public_id', '=', $publicId)->firstOrFail(); ->where('public_id', '=', $publicId)->firstOrFail();
$data = [ $data = [
'showBreadcrumbs' => false,
'token' => $token, 'token' => $token,
'method' => 'PUT', 'method' => 'PUT',
'url' => 'tokens/'.$publicId, 'url' => 'tokens/'.$publicId,
@ -94,12 +98,10 @@ class TokenController extends BaseController
public function create() public function create()
{ {
$data = [ $data = [
'showBreadcrumbs' => false,
'token' => null, 'token' => null,
'method' => 'POST', 'method' => 'POST',
'url' => 'tokens', 'url' => 'tokens',
'title' => trans('texts.add_token'), 'title' => trans('texts.add_token'),
'feature' => 'tokens',
]; ];
return View::make('accounts.token', $data); return View::make('accounts.token', $data);
@ -115,7 +117,7 @@ class TokenController extends BaseController
Session::flash('message', trans('texts.deleted_token')); Session::flash('message', trans('texts.deleted_token'));
return Redirect::to('company/advanced_settings/token_management'); return Redirect::to('settings/' . ACCOUNT_API_TOKENS);
} }
/** /**
@ -163,7 +165,7 @@ class TokenController extends BaseController
Session::flash('message', $message); Session::flash('message', $message);
} }
return Redirect::to('company/advanced_settings/token_management'); return Redirect::to('settings/' . ACCOUNT_API_TOKENS);
} }
} }

View File

@ -35,6 +35,11 @@ class UserController extends BaseController
$this->userMailer = $userMailer; $this->userMailer = $userMailer;
} }
public function index()
{
return Redirect::to('settings/' . ACCOUNT_USER_MANAGEMENT);
}
public function getDatatable() public function getDatatable()
{ {
$query = DB::table('users') $query = DB::table('users')
@ -106,7 +111,6 @@ class UserController extends BaseController
->where('public_id', '=', $publicId)->firstOrFail(); ->where('public_id', '=', $publicId)->firstOrFail();
$data = [ $data = [
'showBreadcrumbs' => false,
'user' => $user, 'user' => $user,
'method' => 'PUT', 'method' => 'PUT',
'url' => 'users/'.$publicId, 'url' => 'users/'.$publicId,
@ -134,24 +138,22 @@ class UserController extends BaseController
{ {
if (!Auth::user()->registered) { if (!Auth::user()->registered) {
Session::flash('error', trans('texts.register_to_add_user')); Session::flash('error', trans('texts.register_to_add_user'));
return Redirect::to('company/advanced_settings/user_management'); return Redirect::to('settings/' . ACCOUNT_USER_MANAGEMENT);
} }
if (!Auth::user()->confirmed) { if (!Auth::user()->confirmed) {
Session::flash('error', trans('texts.confirmation_required')); Session::flash('error', trans('texts.confirmation_required'));
return Redirect::to('company/advanced_settings/user_management'); return Redirect::to('settings/' . ACCOUNT_USER_MANAGEMENT);
} }
if (Utils::isNinja()) { if (Utils::isNinja()) {
$count = User::where('account_id', '=', Auth::user()->account_id)->count(); $count = User::where('account_id', '=', Auth::user()->account_id)->count();
if ($count >= MAX_NUM_USERS) { if ($count >= MAX_NUM_USERS) {
Session::flash('error', trans('texts.limit_users')); Session::flash('error', trans('texts.limit_users'));
return Redirect::to('settings/' . ACCOUNT_USER_MANAGEMENT);
return Redirect::to('company/advanced_settings/user_management');
} }
} }
$data = [ $data = [
'showBreadcrumbs' => false,
'user' => null, 'user' => null,
'method' => 'POST', 'method' => 'POST',
'url' => 'users', 'url' => 'users',
@ -171,7 +173,7 @@ class UserController extends BaseController
Session::flash('message', trans('texts.deleted_user')); Session::flash('message', trans('texts.deleted_user'));
return Redirect::to('company/advanced_settings/user_management'); return Redirect::to('settings/' . ACCOUNT_USER_MANAGEMENT);
} }
public function restoreUser($userPublicId) public function restoreUser($userPublicId)
@ -184,7 +186,7 @@ class UserController extends BaseController
Session::flash('message', trans('texts.restored_user')); Session::flash('message', trans('texts.restored_user'));
return Redirect::to('company/advanced_settings/user_management'); return Redirect::to('settings/' . ACCOUNT_USER_MANAGEMENT);
} }
/** /**
@ -247,7 +249,7 @@ class UserController extends BaseController
Session::flash('message', $message); Session::flash('message', $message);
} }
return Redirect::to('company/advanced_settings/user_management'); return Redirect::to('settings/' . ACCOUNT_USER_MANAGEMENT);
} }
public function sendConfirmation($userPublicId) public function sendConfirmation($userPublicId)
@ -258,7 +260,7 @@ class UserController extends BaseController
$this->userMailer->sendConfirmation($user, Auth::user()); $this->userMailer->sendConfirmation($user, Auth::user());
Session::flash('message', trans('texts.sent_invite')); Session::flash('message', trans('texts.sent_invite'));
return Redirect::to('company/advanced_settings/user_management'); return Redirect::to('settings/' . ACCOUNT_USER_MANAGEMENT);
} }

View File

@ -27,11 +27,11 @@ class StartupCheck
{ {
// Set up trusted X-Forwarded-Proto proxies // Set up trusted X-Forwarded-Proto proxies
// TRUSTED_PROXIES accepts a comma delimited list of subnets // TRUSTED_PROXIES accepts a comma delimited list of subnets
// // ie, TRUSTED_PROXIES='10.0.0.0/8,172.16.0.0/12,192.168.0.0/16'
// TRUSTED_PROXIES='10.0.0.0/8,172.16.0.0/12,192.168.0.0/16'
if (isset($_ENV['TRUSTED_PROXIES'])) { if (isset($_ENV['TRUSTED_PROXIES'])) {
Request::setTrustedProxies(array_map('trim',explode(",",env('TRUSTED_PROXIES')))); Request::setTrustedProxies(array_map('trim', explode(',', env('TRUSTED_PROXIES'))));
} }
// Ensure all request are over HTTPS in production // Ensure all request are over HTTPS in production
if (App::environment() == ENV_PRODUCTION && !Request::secure()) { if (App::environment() == ENV_PRODUCTION && !Request::secure()) {
return Redirect::secure(Request::getRequestUri()); return Redirect::secure(Request::getRequestUri());

View File

@ -21,10 +21,10 @@
//Log::error('test'); //Log::error('test');
// Application setup // Application setup
Route::get('setup', 'AppController@showSetup'); Route::get('/setup', 'AppController@showSetup');
Route::post('setup', 'AppController@doSetup'); Route::post('/setup', 'AppController@doSetup');
Route::get('install', 'AppController@install'); Route::get('/install', 'AppController@install');
Route::get('update', 'AppController@update'); Route::get('/update', 'AppController@update');
/* /*
// Codeception code coverage // Codeception code coverage
@ -35,11 +35,11 @@ Route::get('/c3.php', function () {
// Public pages // Public pages
Route::get('/', 'HomeController@showIndex'); Route::get('/', 'HomeController@showIndex');
Route::get('terms', 'HomeController@showTerms'); Route::get('/terms', 'HomeController@showTerms');
Route::get('log_error', 'HomeController@logError'); Route::get('/log_error', 'HomeController@logError');
Route::get('invoice_now', 'HomeController@invoiceNow'); Route::get('/invoice_now', 'HomeController@invoiceNow');
Route::get('keep_alive', 'HomeController@keepAlive'); Route::get('/keep_alive', 'HomeController@keepAlive');
Route::post('get_started', 'AccountController@getStarted'); Route::post('/get_started', 'AccountController@getStarted');
// Client visible pages // Client visible pages
Route::get('view/{invitation_key}', 'InvoiceController@view'); Route::get('view/{invitation_key}', 'InvoiceController@view');
@ -64,21 +64,14 @@ Route::get('claim_license', 'PaymentController@claim_license');
Route::post('signup/validate', 'AccountController@checkEmail'); Route::post('signup/validate', 'AccountController@checkEmail');
Route::post('signup/submit', 'AccountController@submitSignup'); Route::post('signup/submit', 'AccountController@submitSignup');
Route::get('auth/{provider}', 'Auth\AuthController@authLogin'); Route::get('/auth/{provider}', 'Auth\AuthController@authLogin');
Route::get('auth_unlink', 'Auth\AuthController@authUnlink'); Route::get('/auth_unlink', 'Auth\AuthController@authUnlink');
Route::post('hook/email_bounced', 'AppController@emailBounced'); Route::post('/hook/email_bounced', 'AppController@emailBounced');
Route::post('hook/email_opened', 'AppController@emailOpened'); Route::post('/hook/email_opened', 'AppController@emailOpened');
// Laravel auth routes // Laravel auth routes
/*
Route::controllers([
'auth' => 'Auth\AuthController',
'password' => 'Auth\PasswordController',
]);
*/
get('/signup', array('as' => 'signup', 'uses' => 'Auth\AuthController@getRegister')); get('/signup', array('as' => 'signup', 'uses' => 'Auth\AuthController@getRegister'));
post('/signup', array('as' => 'signup', 'uses' => 'Auth\AuthController@postRegister')); post('/signup', array('as' => 'signup', 'uses' => 'Auth\AuthController@postRegister'));
get('/login', array('as' => 'login', 'uses' => 'Auth\AuthController@getLoginWrapper')); get('/login', array('as' => 'login', 'uses' => 'Auth\AuthController@getLoginWrapper'));
@ -121,14 +114,20 @@ Route::group(['middleware' => 'auth'], function() {
Route::resource('products', 'ProductController'); Route::resource('products', 'ProductController');
Route::get('products/{product_id}/archive', 'ProductController@archive'); Route::get('products/{product_id}/archive', 'ProductController@archive');
Route::get('company/advanced_settings/data_visualizations', 'ReportController@d3'); Route::get('api/tax_rates', array('as'=>'api.tax_rates', 'uses'=>'TaxRateController@getDatatable'));
Route::get('company/advanced_settings/charts_and_reports', 'ReportController@showReports'); Route::resource('tax_rates', 'TaxRateController');
Route::post('company/advanced_settings/charts_and_reports', 'ReportController@showReports'); Route::get('tax_rates/{tax_rates_id}/archive', 'TaxRateController@archive');
Route::get('company/{section}/{subSection?}', 'AccountController@redirectLegacy');
Route::get('settings/data_visualizations', 'ReportController@d3');
Route::get('settings/charts_and_reports', 'ReportController@showReports');
Route::post('settings/charts_and_reports', 'ReportController@showReports');
Route::post('settings/cancel_account', 'AccountController@cancelAccount');
Route::get('settings/{section?}', 'AccountController@showSection');
Route::post('settings/{section?}', 'AccountController@doSection');
Route::post('company/cancel_account', 'AccountController@cancelAccount');
Route::get('account/getSearchData', array('as' => 'getSearchData', 'uses' => 'AccountController@getSearchData')); Route::get('account/getSearchData', array('as' => 'getSearchData', 'uses' => 'AccountController@getSearchData'));
Route::get('company/{section?}/{sub_section?}', 'AccountController@showSection');
Route::post('company/{section?}/{sub_section?}', 'AccountController@doSection');
Route::post('user/setTheme', 'UserController@setTheme'); Route::post('user/setTheme', 'UserController@setTheme');
Route::post('remove_logo', 'AccountController@removeLogo'); Route::post('remove_logo', 'AccountController@removeLogo');
Route::post('account/go_pro', 'AccountController@enableProPlan'); Route::post('account/go_pro', 'AccountController@enableProPlan');
@ -183,7 +182,6 @@ Route::group(['middleware' => 'auth'], function() {
Route::post('credits/bulk', 'CreditController@bulk'); Route::post('credits/bulk', 'CreditController@bulk');
get('/resend_confirmation', 'AccountController@resendConfirmation'); get('/resend_confirmation', 'AccountController@resendConfirmation');
//Route::resource('timesheets', 'TimesheetController');
}); });
// Route group for API // Route group for API
@ -253,21 +251,27 @@ if (!defined('CONTACT_EMAIL')) {
define('PERSON_CONTACT', 'contact'); define('PERSON_CONTACT', 'contact');
define('PERSON_USER', 'user'); define('PERSON_USER', 'user');
define('ACCOUNT_DETAILS', 'details'); define('BASIC_SETTINGS', 'basic_settings');
define('ADVANCED_SETTINGS', 'advanced_settings');
define('ACCOUNT_COMPANY_DETAILS', 'company_details');
define('ACCOUNT_USER_DETAILS', 'user_details');
define('ACCOUNT_LOCALIZATION', 'localization');
define('ACCOUNT_NOTIFICATIONS', 'notifications'); define('ACCOUNT_NOTIFICATIONS', 'notifications');
define('ACCOUNT_IMPORT_EXPORT', 'import_export'); define('ACCOUNT_IMPORT_EXPORT', 'import_export');
define('ACCOUNT_PAYMENTS', 'payments'); define('ACCOUNT_PAYMENTS', 'online_payments');
define('ACCOUNT_MAP', 'import_map'); define('ACCOUNT_MAP', 'import_map');
define('ACCOUNT_EXPORT', 'export'); define('ACCOUNT_EXPORT', 'export');
define('ACCOUNT_TAX_RATES', 'tax_rates');
define('ACCOUNT_PRODUCTS', 'products'); define('ACCOUNT_PRODUCTS', 'products');
define('ACCOUNT_ADVANCED_SETTINGS', 'advanced_settings'); define('ACCOUNT_ADVANCED_SETTINGS', 'advanced_settings');
define('ACCOUNT_INVOICE_SETTINGS', 'invoice_settings'); define('ACCOUNT_INVOICE_SETTINGS', 'invoice_settings');
define('ACCOUNT_INVOICE_DESIGN', 'invoice_design'); define('ACCOUNT_INVOICE_DESIGN', 'invoice_design');
define('ACCOUNT_CHART_BUILDER', 'chart_builder'); define('ACCOUNT_CHARTS_AND_REPORTS', 'charts_and_reports');
define('ACCOUNT_USER_MANAGEMENT', 'user_management'); define('ACCOUNT_USER_MANAGEMENT', 'user_management');
define('ACCOUNT_DATA_VISUALIZATIONS', 'data_visualizations'); define('ACCOUNT_DATA_VISUALIZATIONS', 'data_visualizations');
define('ACCOUNT_TEMPLATES_AND_REMINDERS', 'templates_and_reminders'); define('ACCOUNT_TEMPLATES_AND_REMINDERS', 'templates_and_reminders');
define('ACCOUNT_TOKEN_MANAGEMENT', 'token_management'); define('ACCOUNT_API_TOKENS', 'api_tokens');
define('ACCOUNT_CUSTOMIZE_DESIGN', 'customize_design'); define('ACCOUNT_CUSTOMIZE_DESIGN', 'customize_design');
@ -310,12 +314,16 @@ if (!defined('CONTACT_EMAIL')) {
define('RECENTLY_VIEWED_LIMIT', 8); define('RECENTLY_VIEWED_LIMIT', 8);
define('LOGGED_ERROR_LIMIT', 100); define('LOGGED_ERROR_LIMIT', 100);
define('RANDOM_KEY_LENGTH', 32); define('RANDOM_KEY_LENGTH', 32);
define('MAX_NUM_CLIENTS', 500);
define('MAX_NUM_CLIENTS_PRO', 20000);
define('MAX_NUM_USERS', 20); define('MAX_NUM_USERS', 20);
define('MAX_SUBDOMAIN_LENGTH', 30); define('MAX_SUBDOMAIN_LENGTH', 30);
define('MAX_IFRAME_URL_LENGTH', 250); define('MAX_IFRAME_URL_LENGTH', 250);
define('DEFAULT_FONT_SIZE', 9); define('DEFAULT_FONT_SIZE', 9);
define('DEFAULT_SEND_RECURRING_HOUR', 8);
define('MAX_NUM_CLIENTS', 100);
define('MAX_NUM_CLIENTS_PRO', 20000);
define('MAX_NUM_CLIENTS_LEGACY', 500);
define('LEGACY_CUTOFF', 57800);
define('INVOICE_STATUS_DRAFT', 1); define('INVOICE_STATUS_DRAFT', 1);
define('INVOICE_STATUS_SENT', 2); define('INVOICE_STATUS_SENT', 2);
@ -391,16 +399,16 @@ if (!defined('CONTACT_EMAIL')) {
define('NINJA_GATEWAY_CONFIG', 'NINJA_GATEWAY_CONFIG'); define('NINJA_GATEWAY_CONFIG', 'NINJA_GATEWAY_CONFIG');
define('NINJA_WEB_URL', 'https://www.invoiceninja.com'); define('NINJA_WEB_URL', 'https://www.invoiceninja.com');
define('NINJA_APP_URL', 'https://app.invoiceninja.com'); define('NINJA_APP_URL', 'https://app.invoiceninja.com');
define('NINJA_VERSION', '2.4.2'); define('NINJA_VERSION', '2.4.3');
define('NINJA_DATE', '2000-01-01'); define('NINJA_DATE', '2000-01-01');
define('NINJA_FROM_EMAIL', 'maildelivery@invoiceninja.com'); define('NINJA_FROM_EMAIL', 'maildelivery@invoiceninja.com');
define('RELEASES_URL', 'https://github.com/hillelcoren/invoice-ninja/releases/'); define('RELEASES_URL', 'https://trello.com/b/63BbiVVe/invoice-ninja');
define('ZAPIER_URL', 'https://zapier.com/zapbook/invoice-ninja'); define('ZAPIER_URL', 'https://zapier.com/zapbook/invoice-ninja');
define('OUTDATE_BROWSER_URL', 'http://browsehappy.com/'); define('OUTDATE_BROWSER_URL', 'http://browsehappy.com/');
define('PDFMAKE_DOCS', 'http://pdfmake.org/playground.html'); define('PDFMAKE_DOCS', 'http://pdfmake.org/playground.html');
define('PHANTOMJS_CLOUD', 'http://api.phantomjscloud.com/single/browser/v1/'); define('PHANTOMJS_CLOUD', 'http://api.phantomjscloud.com/single/browser/v1/');
define('GITHUB_RELEASES', 'https://github.com/hillelcoren/invoice-ninja/releases'); define('PHP_DATE_FORMATS', 'http://php.net/manual/en/function.date.php');
define('REFERRAL_PROGRAM_URL', false); define('REFERRAL_PROGRAM_URL', false);
define('COUNT_FREE_DESIGNS', 4); define('COUNT_FREE_DESIGNS', 4);

View File

@ -259,6 +259,10 @@ class Utils
public static function dateToString($date) public static function dateToString($date)
{ {
if (!$date) {
return false;
}
$dateTime = new DateTime($date); $dateTime = new DateTime($date);
$timestamp = $dateTime->getTimestamp(); $timestamp = $dateTime->getTimestamp();
$format = Session::get(SESSION_DATE_FORMAT, DEFAULT_DATE_FORMAT); $format = Session::get(SESSION_DATE_FORMAT, DEFAULT_DATE_FORMAT);

View File

@ -15,6 +15,27 @@ class Account extends Eloquent
protected $dates = ['deleted_at']; protected $dates = ['deleted_at'];
protected $hidden = ['ip']; protected $hidden = ['ip'];
public static $basicSettings = [
ACCOUNT_COMPANY_DETAILS,
ACCOUNT_USER_DETAILS,
ACCOUNT_LOCALIZATION,
ACCOUNT_PAYMENTS,
ACCOUNT_TAX_RATES,
ACCOUNT_PRODUCTS,
ACCOUNT_NOTIFICATIONS,
ACCOUNT_IMPORT_EXPORT,
];
public static $advancedSettings = [
ACCOUNT_INVOICE_SETTINGS,
ACCOUNT_INVOICE_DESIGN,
ACCOUNT_TEMPLATES_AND_REMINDERS,
ACCOUNT_CHARTS_AND_REPORTS,
ACCOUNT_DATA_VISUALIZATIONS,
ACCOUNT_USER_MANAGEMENT,
ACCOUNT_API_TOKENS,
];
/* /*
protected $casts = [ protected $casts = [
'invoice_settings' => 'object', 'invoice_settings' => 'object',
@ -86,6 +107,11 @@ class Account extends Eloquent
return $this->belongsTo('App\Models\Industry'); return $this->belongsTo('App\Models\Industry');
} }
public function default_tax_rate()
{
return $this->belongsTo('App\Models\TaxRate');
}
public function isGatewayConfigured($gatewayId = 0) public function isGatewayConfigured($gatewayId = 0)
{ {
$this->load('account_gateways'); $this->load('account_gateways');
@ -140,6 +166,25 @@ class Account extends Eloquent
} }
} }
public function getDateTime($date = 'now')
{
return new \DateTime($date, new \DateTimeZone($this->getTimezone()));
}
public function getCustomDateFormat()
{
return $this->date_format ? $this->date_format->format : DEFAULT_DATE_FORMAT;
}
public function formatDate($date)
{
if (!$date) {
return null;
}
return $date->format($this->getCustomDateFormat());
}
public function getGatewayByType($type = PAYMENT_TYPE_ANY) public function getGatewayByType($type = PAYMENT_TYPE_ANY)
{ {
foreach ($this->account_gateways as $gateway) { foreach ($this->account_gateways as $gateway) {
@ -198,10 +243,131 @@ class Account extends Eloquent
return $height; return $height;
} }
public function getNextInvoiceNumber($isQuote = false, $prefix = '') public function createInvoice($entityType, $clientId = null)
{ {
$counter = $isQuote && !$this->share_counter ? $this->quote_number_counter : $this->invoice_number_counter; $invoice = Invoice::createNew();
$prefix .= $isQuote ? $this->quote_number_prefix : $this->invoice_number_prefix;
$invoice->invoice_date = Utils::today();
$invoice->start_date = Utils::today();
$invoice->invoice_design_id = $this->invoice_design_id;
$invoice->client_id = $clientId;
if ($entityType === ENTITY_RECURRING_INVOICE) {
$invoice->invoice_number = microtime(true);
$invoice->is_recurring = true;
} else {
if ($entityType == ENTITY_QUOTE) {
$invoice->is_quote = true;
}
if ($this->hasClientNumberPattern($invoice) && !$clientId) {
// do nothing, we don't yet know the value
} else {
$invoice->invoice_number = $this->getNextInvoiceNumber($invoice);
}
}
if (!$clientId) {
$invoice->client = Client::createNew();
$invoice->client->public_id = 0;
}
return $invoice;
}
public function hasNumberPattern($isQuote)
{
return $isQuote ? ($this->quote_number_pattern ? true : false) : ($this->invoice_number_pattern ? true : false);
}
public function hasClientNumberPattern($invoice)
{
$pattern = $invoice->is_quote ? $this->quote_number_pattern : $this->invoice_number_pattern;
return strstr($pattern, '$custom');
}
public function getNumberPattern($invoice)
{
$pattern = $invoice->is_quote ? $this->quote_number_pattern : $this->invoice_number_pattern;
if (!$pattern) {
return false;
}
$search = ['{$year}'];
$replace = [date('Y')];
$search[] = '{$counter}';
$replace[] = str_pad($this->getCounter($invoice->is_quote), 4, '0', STR_PAD_LEFT);
if (strstr($pattern, '{$userId}')) {
$search[] = '{$userId}';
$replace[] = str_pad($invoice->user->public_id, 2, '0', STR_PAD_LEFT);
}
$matches = false;
preg_match('/{\$date:(.*?)}/', $pattern, $matches);
if (count($matches) > 1) {
$format = $matches[1];
$search[] = $matches[0];
$replace[] = str_replace($format, date($format), $matches[1]);
}
$pattern = str_replace($search, $replace, $pattern);
if ($invoice->client->id) {
$pattern = $this->getClientInvoiceNumber($pattern, $invoice);
}
return $pattern;
}
private function getClientInvoiceNumber($pattern, $invoice)
{
if (!$invoice->client) {
return $pattern;
}
$search = [
//'{$clientId}',
'{$custom1}',
'{$custom2}',
];
$replace = [
//str_pad($client->public_id, 3, '0', STR_PAD_LEFT),
$invoice->client->custom_value1,
$invoice->client->custom_value2,
];
return str_replace($search, $replace, $pattern);
}
// if we're using a pattern we don't know the next number until a client
// is selected, to support this the default value is blank
public function getDefaultInvoiceNumber($invoice = false)
{
if ($this->hasClientNumberPattern($invoice)) {
return false;
}
return $this->getNextInvoiceNumber($invoice);
}
public function getCounter($isQuote)
{
return $isQuote && !$this->share_counter ? $this->quote_number_counter : $this->invoice_number_counter;
}
public function getNextInvoiceNumber($invoice)
{
if ($this->hasNumberPattern($invoice->is_quote)) {
return $this->getNumberPattern($invoice);
}
$counter = $this->getCounter($invoice->is_quote);
$prefix = $invoice->is_quote ? $this->quote_number_prefix : $this->invoice_number_prefix;
$counterOffset = 0; $counterOffset = 0;
// confirm the invoice number isn't already taken // confirm the invoice number isn't already taken
@ -214,7 +380,7 @@ class Account extends Eloquent
// update the invoice counter to be caught up // update the invoice counter to be caught up
if ($counterOffset > 1) { if ($counterOffset > 1) {
if ($isQuote && !$this->share_counter) { if ($invoice->is_quote && !$this->share_counter) {
$this->quote_number_counter += $counterOffset - 1; $this->quote_number_counter += $counterOffset - 1;
} else { } else {
$this->invoice_number_counter += $counterOffset - 1; $this->invoice_number_counter += $counterOffset - 1;
@ -226,9 +392,9 @@ class Account extends Eloquent
return $number; return $number;
} }
public function incrementCounter($isQuote = false) public function incrementCounter($invoice)
{ {
if ($isQuote && !$this->share_counter) { if ($invoice->is_quote && !$this->share_counter) {
$this->quote_number_counter += 1; $this->quote_number_counter += 1;
} else { } else {
$this->invoice_number_counter += 1; $this->invoice_number_counter += 1;
@ -237,18 +403,13 @@ class Account extends Eloquent
$this->save(); $this->save();
} }
public function getLocale()
{
$language = Language::where('id', '=', $this->account->language_id)->first();
return $language->locale;
}
public function loadLocalizationSettings($client = false) public function loadLocalizationSettings($client = false)
{ {
$this->load('timezone', 'date_format', 'datetime_format', 'language'); $this->load('timezone', 'date_format', 'datetime_format', 'language');
Session::put(SESSION_TIMEZONE, $this->timezone ? $this->timezone->name : DEFAULT_TIMEZONE); $timezone = $this->timezone ? $this->timezone->name : DEFAULT_TIMEZONE;
Session::put(SESSION_TIMEZONE, $timezone);
Session::put(SESSION_DATE_FORMAT, $this->date_format ? $this->date_format->format : DEFAULT_DATE_FORMAT); Session::put(SESSION_DATE_FORMAT, $this->date_format ? $this->date_format->format : DEFAULT_DATE_FORMAT);
Session::put(SESSION_DATE_PICKER_FORMAT, $this->date_format ? $this->date_format->picker_format : DEFAULT_DATE_PICKER_FORMAT); Session::put(SESSION_DATE_PICKER_FORMAT, $this->date_format ? $this->date_format->picker_format : DEFAULT_DATE_PICKER_FORMAT);

View File

@ -25,6 +25,11 @@ class Client extends EntityModel
return $this->belongsTo('App\Models\Account'); return $this->belongsTo('App\Models\Account');
} }
public function user()
{
return $this->belongsTo('App\Models\User');
}
public function invoices() public function invoices()
{ {
return $this->hasMany('App\Models\Invoice'); return $this->hasMany('App\Models\Invoice');
@ -168,6 +173,11 @@ class Client extends EntityModel
return $this->account->currency_id ?: DEFAULT_CURRENCY; return $this->account->currency_id ?: DEFAULT_CURRENCY;
} }
public function getCounter($isQuote)
{
return $isQuote ? $this->quote_number_counter : $this->invoice_number_counter;
}
} }
/* /*

View File

@ -9,14 +9,14 @@ class EntityModel extends Eloquent
public $timestamps = true; public $timestamps = true;
protected $hidden = ['id']; protected $hidden = ['id'];
public static function createNew($parent = false) public static function createNew($context = null)
{ {
$className = get_called_class(); $className = get_called_class();
$entity = new $className(); $entity = new $className();
if ($parent) { if ($context) {
$entity->user_id = $parent instanceof User ? $parent->id : $parent->user_id; $entity->user_id = $context instanceof User ? $context->id : $context->user_id;
$entity->account_id = $parent->account_id; $entity->account_id = $context->account_id;
} elseif (Auth::check()) { } elseif (Auth::check()) {
$entity->user_id = Auth::user()->id; $entity->user_id = Auth::user()->id;
$entity->account_id = Auth::user()->account_id; $entity->account_id = Auth::user()->account_id;

View File

@ -57,7 +57,7 @@ class Invitation extends EntityModel
foreach ($statuses as $status) { foreach ($statuses as $status) {
$field = "{$status}_date"; $field = "{$status}_date";
$date = ''; $date = '';
if ($this->$field) { if ($this->$field && $this->field != '0000-00-00 00:00:00') {
$date = Utils::dateToString($this->$field); $date = Utils::dateToString($this->$field);
$hasValue = true; $hasValue = true;
} }

View File

@ -6,7 +6,10 @@ use Illuminate\Database\Eloquent\SoftDeletes;
class Invoice extends EntityModel class Invoice extends EntityModel
{ {
use SoftDeletes; use SoftDeletes {
SoftDeletes::trashed as parentTrashed;
}
protected $dates = ['deleted_at']; protected $dates = ['deleted_at'];
protected $casts = [ protected $casts = [
@ -15,6 +18,24 @@ class Invoice extends EntityModel
'auto_bill' => 'boolean', 'auto_bill' => 'boolean',
]; ];
public static $patternFields = [
'counter',
'custom1',
'custom2',
'userId',
'year',
'date:',
];
public function trashed()
{
if ($this->client && $this->client->trashed()) {
return true;
}
return self::parentTrashed();
}
public function account() public function account()
{ {
return $this->belongsTo('App\Models\Account'); return $this->belongsTo('App\Models\Account');
@ -216,6 +237,117 @@ class Invoice extends EntityModel
return $this; return $this;
} }
public function getSchedule()
{
if (!$this->start_date || !$this->is_recurring || !$this->frequency_id) {
return false;
}
$startDate = $this->getOriginal('last_sent_date') ?: $this->getOriginal('start_date');
$startDate .= ' ' . $this->account->recurring_hour . ':00:00';
$startDate = $this->account->getDateTime($startDate);
$endDate = $this->end_date ? $this->account->getDateTime($this->getOriginal('end_date')) : null;
$timezone = $this->account->getTimezone();
$rule = $this->getRecurrenceRule();
$rule = new \Recurr\Rule("{$rule}", $startDate, $endDate, $timezone);
// Fix for months with less than 31 days
$transformerConfig = new \Recurr\Transformer\ArrayTransformerConfig();
$transformerConfig->enableLastDayOfMonthFix();
$transformer = new \Recurr\Transformer\ArrayTransformer();
$transformer->setConfig($transformerConfig);
$dates = $transformer->transform($rule);
if (count($dates) < 2) {
return false;
}
return $dates;
}
public function getNextSendDate()
{
if ($this->start_date && !$this->last_sent_date) {
$startDate = $this->getOriginal('start_date') . ' ' . $this->account->recurring_hour . ':00:00';
return $this->account->getDateTime($startDate);
}
if (!$schedule = $this->getSchedule()) {
return null;
}
if (count($schedule) < 2) {
return null;
}
return $schedule[1]->getStart();
}
public function getPrettySchedule($min = 1, $max = 10)
{
if (!$schedule = $this->getSchedule($max)) {
return null;
}
$dates = [];
for ($i=$min; $i<min($max, count($schedule)); $i++) {
$date = $schedule[$i];
$date = $this->account->formatDate($date->getStart());
$dates[] = $date;
}
return implode('<br/>', $dates);
}
private function getRecurrenceRule()
{
$rule = '';
switch ($this->frequency_id) {
case FREQUENCY_WEEKLY:
$rule = 'FREQ=WEEKLY;';
break;
case FREQUENCY_TWO_WEEKS:
$rule = 'FREQ=WEEKLY;INTERVAL=2;';
break;
case FREQUENCY_FOUR_WEEKS:
$rule = 'FREQ=WEEKLY;INTERVAL=4;';
break;
case FREQUENCY_MONTHLY:
$rule = 'FREQ=MONTHLY;';
break;
case FREQUENCY_THREE_MONTHS:
$rule = 'FREQ=MONTHLY;INTERVAL=3;';
break;
case FREQUENCY_SIX_MONTHS:
$rule = 'FREQ=MONTHLY;INTERVAL=6;';
break;
case FREQUENCY_ANNUALLY:
$rule = 'FREQ=YEARLY;';
break;
}
if ($this->end_date) {
$rule .= 'UNTIL=' . $this->end_date;
}
return $rule;
}
/*
public function shouldSendToday()
{
if (!$nextSendDate = $this->getNextSendDate()) {
return false;
}
return $this->account->getDateTime() >= $nextSendDate;
}
*/
public function shouldSendToday() public function shouldSendToday()
{ {
if (!$this->start_date || strtotime($this->start_date) > strtotime('now')) { if (!$this->start_date || strtotime($this->start_date) > strtotime('now')) {
@ -268,7 +400,8 @@ class Invoice extends EntityModel
return false; return false;
} }
public function getReminder() { public function getReminder()
{
for ($i=1; $i<=3; $i++) { for ($i=1; $i<=3; $i++) {
$field = "enable_reminder{$i}"; $field = "enable_reminder{$i}";
if (!$this->account->$field) { if (!$this->account->$field) {
@ -320,7 +453,7 @@ class Invoice extends EntityModel
Invoice::creating(function ($invoice) { Invoice::creating(function ($invoice) {
if (!$invoice->is_recurring) { if (!$invoice->is_recurring) {
$invoice->account->incrementCounter($invoice->is_quote); $invoice->account->incrementCounter($invoice);
} }
}); });

View File

@ -11,4 +11,9 @@ class Product extends EntityModel
{ {
return Product::scope()->where('product_key', '=', $key)->first(); return Product::scope()->where('product_key', '=', $key)->first();
} }
public function default_tax_rate()
{
return $this->belongsTo('App\Models\TaxRate');
}
} }

View File

@ -142,7 +142,15 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
public function getMaxNumClients() public function getMaxNumClients()
{ {
return $this->isPro() ? MAX_NUM_CLIENTS_PRO : MAX_NUM_CLIENTS; if ($this->isPro()) {
return MAX_NUM_CLIENTS_PRO;
}
if ($this->id < LEGACY_CUTOFF) {
return MAX_NUM_CLIENTS_LEGACY;
}
return MAX_NUM_CLIENTS;
} }
public function getRememberToken() public function getRememberToken()
@ -195,7 +203,8 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
{ {
if (!$user->getOriginal('email') if (!$user->getOriginal('email')
|| $user->getOriginal('email') == TEST_USERNAME || $user->getOriginal('email') == TEST_USERNAME
|| $user->getOriginal('username') == TEST_USERNAME) { || $user->getOriginal('username') == TEST_USERNAME
|| $user->getOriginal('email') == 'tests@bitrock.com') {
event(new UserSignedUp()); event(new UserSignedUp());
} }

View File

@ -145,7 +145,6 @@ class ContactMailer extends Mailer
$subject = $this->processVariables($emailSubject, $variables); $subject = $this->processVariables($emailSubject, $variables);
$data['invoice_id'] = $payment->invoice->id; $data['invoice_id'] = $payment->invoice->id;
$invoice->updateCachedPDF();
if ($user->email && $contact->email) { if ($user->email && $contact->email) {
$this->sendTo($contact->email, $user->email, $accountName, $subject, $view, $data); $this->sendTo($contact->email, $user->email, $accountName, $subject, $view, $data);

View File

@ -9,6 +9,10 @@ class Mailer
{ {
public function sendTo($toEmail, $fromEmail, $fromName, $subject, $view, $data = []) public function sendTo($toEmail, $fromEmail, $fromName, $subject, $view, $data = [])
{ {
if (stristr($toEmail, '@example.com')) {
return true;
}
if (isset($_ENV['POSTMARK_API_TOKEN'])) { if (isset($_ENV['POSTMARK_API_TOKEN'])) {
$views = 'emails.'.$view.'_html'; $views = 'emails.'.$view.'_html';
} else { } else {
@ -63,7 +67,7 @@ class Mailer
private function handleFailure($exception) private function handleFailure($exception)
{ {
if (isset($_ENV['POSTMARK_API_TOKEN'])) { if (isset($_ENV['POSTMARK_API_TOKEN']) && $exception->getResponse()) {
$response = $exception->getResponse()->getBody()->getContents(); $response = $exception->getResponse()->getBody()->getContents();
$response = json_decode($response); $response = json_decode($response);
$emailError = nl2br($response->Message); $emailError = nl2br($response->Message);

View File

@ -139,7 +139,7 @@ class AccountRepository
$invoice->user_id = $account->users()->first()->id; $invoice->user_id = $account->users()->first()->id;
$invoice->public_id = $publicId; $invoice->public_id = $publicId;
$invoice->client_id = $client->id; $invoice->client_id = $client->id;
$invoice->invoice_number = $account->getNextInvoiceNumber(); $invoice->invoice_number = $account->getNextInvoiceNumber($invoice);
$invoice->invoice_date = date_create()->format('Y-m-d'); $invoice->invoice_date = date_create()->format('Y-m-d');
$invoice->amount = PRO_PLAN_PRICE; $invoice->amount = PRO_PLAN_PRICE;
$invoice->balance = PRO_PLAN_PRICE; $invoice->balance = PRO_PLAN_PRICE;

View File

@ -250,18 +250,17 @@ class InvoiceRepository
public function save($publicId, $data, $entityType) public function save($publicId, $data, $entityType)
{ {
$account = \Auth::user()->account;
if ($publicId) { if ($publicId) {
$invoice = Invoice::scope($publicId)->firstOrFail(); $invoice = Invoice::scope($publicId)->firstOrFail();
} else { } else {
$invoice = Invoice::createNew(); if ($data['is_recurring']) {
$entityType = ENTITY_RECURRING_INVOICE;
if ($entityType == ENTITY_QUOTE) {
$invoice->is_quote = true;
} }
$invoice = $account->createInvoice($entityType, $data['client_id']);
} }
$account = \Auth::user()->account;
if ((isset($data['set_default_terms']) && $data['set_default_terms']) if ((isset($data['set_default_terms']) && $data['set_default_terms'])
|| (isset($data['set_default_footer']) && $data['set_default_footer'])) { || (isset($data['set_default_footer']) && $data['set_default_footer'])) {
if (isset($data['set_default_terms']) && $data['set_default_terms']) { if (isset($data['set_default_terms']) && $data['set_default_terms']) {
@ -273,7 +272,7 @@ class InvoiceRepository
$account->save(); $account->save();
} }
if (isset($data['invoice_number'])) { if (isset($data['invoice_number']) && !$invoice->is_recurring) {
$invoice->invoice_number = trim($data['invoice_number']); $invoice->invoice_number = trim($data['invoice_number']);
} }
@ -283,11 +282,6 @@ class InvoiceRepository
$invoice->invoice_date = isset($data['invoice_date_sql']) ? $data['invoice_date_sql'] : Utils::toSqlDate($data['invoice_date']); $invoice->invoice_date = isset($data['invoice_date_sql']) ? $data['invoice_date_sql'] : Utils::toSqlDate($data['invoice_date']);
$invoice->has_tasks = isset($data['has_tasks']) ? $data['has_tasks'] : false; $invoice->has_tasks = isset($data['has_tasks']) ? $data['has_tasks'] : false;
if (!$publicId) {
$invoice->client_id = $data['client_id'];
$invoice->is_recurring = $data['is_recurring'] ? true : false;
}
if ($invoice->is_recurring) { if ($invoice->is_recurring) {
if ($invoice->start_date && $invoice->start_date != Utils::toSqlDate($data['start_date'])) { if ($invoice->start_date && $invoice->start_date != Utils::toSqlDate($data['start_date'])) {
$invoice->last_sent_date = null; $invoice->last_sent_date = null;
@ -478,7 +472,7 @@ class InvoiceRepository
} }
$clone->invoice_number = $account->invoice_number_prefix.$invoiceNumber; $clone->invoice_number = $account->invoice_number_prefix.$invoiceNumber;
} else { } else {
$clone->invoice_number = $account->getNextInvoiceNumber(); $clone->invoice_number = $account->getNextInvoiceNumber($invoice);
} }
foreach ([ foreach ([
@ -579,20 +573,21 @@ class InvoiceRepository
public function findInvoiceByInvitation($invitationKey) public function findInvoiceByInvitation($invitationKey)
{ {
$invitation = Invitation::where('invitation_key', '=', $invitationKey)->first(); $invitation = Invitation::where('invitation_key', '=', $invitationKey)->first();
if (!$invitation) { if (!$invitation) {
app()->abort(404, trans('texts.invoice_not_found')); return false;
} }
$invoice = $invitation->invoice; $invoice = $invitation->invoice;
if (!$invoice || $invoice->is_deleted) { if (!$invoice || $invoice->is_deleted) {
app()->abort(404, trans('texts.invoice_not_found')); return false;
} }
$invoice->load('user', 'invoice_items', 'invoice_design', 'account.country', 'client.contacts', 'client.country'); $invoice->load('user', 'invoice_items', 'invoice_design', 'account.country', 'client.contacts', 'client.country');
$client = $invoice->client; $client = $invoice->client;
if (!$client || $client->is_deleted) { if (!$client || $client->is_deleted) {
app()->abort(404, trans('texts.invoice_not_found')); return false;
} }
return $invitation; return $invitation;
@ -630,7 +625,7 @@ class InvoiceRepository
$invoice = Invoice::createNew($recurInvoice); $invoice = Invoice::createNew($recurInvoice);
$invoice->client_id = $recurInvoice->client_id; $invoice->client_id = $recurInvoice->client_id;
$invoice->recurring_invoice_id = $recurInvoice->id; $invoice->recurring_invoice_id = $recurInvoice->id;
$invoice->invoice_number = $recurInvoice->account->getNextInvoiceNumber(false, 'R'); $invoice->invoice_number = 'R' . $recurInvoice->account->getNextInvoiceNumber($recurInvoice);
$invoice->amount = $recurInvoice->amount; $invoice->amount = $recurInvoice->amount;
$invoice->balance = $recurInvoice->amount; $invoice->balance = $recurInvoice->amount;
$invoice->invoice_date = date_create()->format('Y-m-d'); $invoice->invoice_date = date_create()->format('Y-m-d');

View File

@ -5,6 +5,7 @@ use Utils;
class TaxRateRepository class TaxRateRepository
{ {
/*
public function save($taxRates) public function save($taxRates)
{ {
$taxRateIds = []; $taxRateIds = [];
@ -39,4 +40,5 @@ class TaxRateRepository
} }
} }
} }
*/
} }

View File

@ -144,6 +144,10 @@ class AppServiceProvider extends ServiceProvider {
Validator::replacer('less_than', function($message, $attribute, $rule, $parameters) { Validator::replacer('less_than', function($message, $attribute, $rule, $parameters) {
return str_replace(':value', $parameters[0], $message); return str_replace(':value', $parameters[0], $message);
}); });
Validator::extend('has_counter', function($attribute, $value, $parameters) {
return !$value || strstr($value, '{$counter}');
});
} }
/** /**

View File

@ -46,10 +46,10 @@ class AuthService
if ($result === true) { if ($result === true) {
if (!$isRegistered) { if (!$isRegistered) {
event(new UserSignedUp()); event(new UserSignedUp());
Session::flash('warning', trans('texts.success_message')); Session::flash('message', trans('texts.success_message'));
} else { } else {
Session::flash('message', trans('texts.updated_settings')); Session::flash('message', trans('texts.updated_settings'));
return redirect()->to('/company/details'); return redirect()->to('/settings/' . ACCOUNT_USER_DETAILS);
} }
} else { } else {
Session::flash('error', $result); Session::flash('error', $result);

View File

@ -36,7 +36,7 @@ class PaymentService {
$gateway->$function($val); $gateway->$function($val);
} }
if ($accountGateway->gateway->id == GATEWAY_DWOLLA) { if ($accountGateway->isGateway(GATEWAY_DWOLLA)) {
if ($gateway->getSandbox() && isset($_ENV['DWOLLA_SANDBOX_KEY']) && isset($_ENV['DWOLLA_SANSBOX_SECRET'])) { if ($gateway->getSandbox() && isset($_ENV['DWOLLA_SANDBOX_KEY']) && isset($_ENV['DWOLLA_SANSBOX_SECRET'])) {
$gateway->setKey($_ENV['DWOLLA_SANDBOX_KEY']); $gateway->setKey($_ENV['DWOLLA_SANDBOX_KEY']);
$gateway->setSecret($_ENV['DWOLLA_SANSBOX_SECRET']); $gateway->setSecret($_ENV['DWOLLA_SANSBOX_SECRET']);
@ -49,7 +49,7 @@ class PaymentService {
return $gateway; return $gateway;
} }
public function getPaymentDetails($invitation, $input = null) public function getPaymentDetails($invitation, $accountGateway, $input = null)
{ {
$invoice = $invitation->invoice; $invoice = $invitation->invoice;
$account = $invoice->account; $account = $invoice->account;
@ -66,8 +66,7 @@ class PaymentService {
} }
$card = new CreditCard($data); $card = new CreditCard($data);
$data = [
return [
'amount' => $invoice->getRequestedAmount(), 'amount' => $invoice->getRequestedAmount(),
'card' => $card, 'card' => $card,
'currency' => $currencyCode, 'currency' => $currencyCode,
@ -77,6 +76,12 @@ class PaymentService {
'transactionId' => $invoice->invoice_number, 'transactionId' => $invoice->invoice_number,
'transactionType' => 'Purchase', 'transactionType' => 'Purchase',
]; ];
if ($accountGateway->isGateway(GATEWAY_PAYPAL_EXPRESS) || $accountGateway->isGateway(GATEWAY_PAYPAL_PRO)) {
$data['ButtonSource'] = 'InvoiceNinja_SP';
};
return $data;
} }
public function convertInputForOmnipay($input) public function convertInputForOmnipay($input)
@ -222,7 +227,7 @@ class PaymentService {
// setup the gateway/payment info // setup the gateway/payment info
$gateway = $this->createGateway($accountGateway); $gateway = $this->createGateway($accountGateway);
$details = $this->getPaymentDetails($invitation); $details = $this->getPaymentDetails($invitation, $accountGateway);
$details['cardReference'] = $token; $details['cardReference'] = $token;
// submit purchase/get response // submit purchase/get response

View File

@ -29,7 +29,7 @@
"coatesap/omnipay-datacash": "~2.0", "coatesap/omnipay-datacash": "~2.0",
"alfaproject/omnipay-neteller": "1.0.*@dev", "alfaproject/omnipay-neteller": "1.0.*@dev",
"mfauveau/omnipay-pacnet": "~2.0", "mfauveau/omnipay-pacnet": "~2.0",
"coatesap/omnipay-paymentsense": "~2.0", "coatesap/omnipay-paymentsense": "2.0.0",
"coatesap/omnipay-realex": "~2.0", "coatesap/omnipay-realex": "~2.0",
"fruitcakestudio/omnipay-sisow": "~2.0", "fruitcakestudio/omnipay-sisow": "~2.0",
"alfaproject/omnipay-skrill": "dev-master", "alfaproject/omnipay-skrill": "dev-master",
@ -38,7 +38,8 @@
"laravelcollective/html": "~5.0", "laravelcollective/html": "~5.0",
"wildbit/laravel-postmark-provider": "dev-master", "wildbit/laravel-postmark-provider": "dev-master",
"Dwolla/omnipay-dwolla": "dev-master", "Dwolla/omnipay-dwolla": "dev-master",
"laravel/socialite": "~2.0" "laravel/socialite": "~2.0",
"simshaun/recurr": "dev-master"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "~4.0", "phpunit/phpunit": "~4.0",

7171
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -39,25 +39,25 @@ return [
'github' => [ 'github' => [
'client_id' => env('GITHUB_CLIENT_ID'), 'client_id' => env('GITHUB_CLIENT_ID'),
'client_secret' => env('GITHUB_CLIENT_SECRET'), 'client_secret' => env('GITHUB_CLIENT_SECRET'),
'redirect' => 'http://ninja.dev/auth/github' 'redirect' => env('GITHUB_OAUTH_REDIRECT'),
], ],
'google' => [ 'google' => [
'client_id' => '640903115046-dd09j2q24lcc3ilrrv5f2ft2i3n0sreg.apps.googleusercontent.com', 'client_id' => env('GOOGLE_CLIENT_ID'),
'client_secret' => 'Vsfhldq7mRxsCXQTQI8U_4Ua', 'client_secret' => env('GOOGLE_CLIENT_SECRET'),
'redirect' => 'http://ninja.dev/auth/google', 'redirect' => env('GOOGLE_OAUTH_REDIRECT'),
], ],
'facebook' => [ 'facebook' => [
'client_id' => '635126583203143', 'client_id' => env('FACEBOOK_CLIENT_ID'),
'client_secret' => '7aa7c391019f2ece3c6aa90f4c9b1485', 'client_secret' => env('FACEBOOK_CLIENT_SECRET'),
'redirect' => 'http://ninja.dev/auth/facebook', 'redirect' => env('FACEBOOK_OAUTH_REDIRECT'),
], ],
'linkedin' => [ 'linkedin' => [
'client_id' => '778is2j21w25xj', 'client_id' => env('LINKEDIN_CLIENT_ID'),
'client_secret' => 'DvDExxfBLXUtxc81', 'client_secret' => env('LINKEDIN_CLIENT_SECRET'),
'redirect' => 'http://ninja.dev/auth/linkedin', 'redirect' => env('LINKEDIN_OAUTH_REDIRECT'),
], ],
]; ];

View File

@ -14,10 +14,10 @@ class AddInvoiceNumberSettings extends Migration {
{ {
Schema::table('accounts', function($table) Schema::table('accounts', function($table)
{ {
$table->text('invoice_number_prefix')->nullable(); $table->string('invoice_number_prefix')->nullable();
$table->integer('invoice_number_counter')->default(1)->nullable(); $table->integer('invoice_number_counter')->default(1)->nullable();
$table->text('quote_number_prefix')->nullable(); $table->string('quote_number_prefix')->nullable();
$table->integer('quote_number_counter')->default(1)->nullable(); $table->integer('quote_number_counter')->default(1)->nullable();
$table->boolean('share_counter')->default(true); $table->boolean('share_counter')->default(true);

View File

@ -0,0 +1,40 @@
<?php
use Illuminate\Database\Migrations\Migration;
class AddDefaultTaxRates extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('accounts', function ($table) {
$table->unsignedInteger('default_tax_rate_id')->nullable();
$table->smallInteger('recurring_hour')->default(DEFAULT_SEND_RECURRING_HOUR);
});
Schema::table('products', function ($table) {
$table->unsignedInteger('default_tax_rate_id')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('accounts', function ($table) {
$table->dropColumn('default_tax_rate_id');
$table->dropColumn('recurring_hour');
});
Schema::table('products', function ($table) {
$table->dropColumn('default_tax_rate_id');
});
}
}

View File

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
class AddInvoiceNumberPattern extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('accounts', function ($table) {
$table->string('invoice_number_pattern')->nullable();
$table->string('quote_number_pattern')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('accounts', function ($table) {
$table->dropColumn('invoice_number_pattern');
$table->dropColumn('quote_number_pattern');
});
}
}

View File

@ -95,6 +95,8 @@ class PaymentLibrariesSeeder extends Seeder
['name' => 'United Arab Emirates Dirham', 'code' => 'AED', 'symbol' => 'DH ', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'], ['name' => 'United Arab Emirates Dirham', 'code' => 'AED', 'symbol' => 'DH ', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['name' => 'Hong Kong Dollar', 'code' => 'HKD', 'symbol' => '$', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'], ['name' => 'Hong Kong Dollar', 'code' => 'HKD', 'symbol' => '$', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['name' => 'Indonesian Rupiah', 'code' => 'IDR', 'symbol' => 'Rp', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'], ['name' => 'Indonesian Rupiah', 'code' => 'IDR', 'symbol' => 'Rp', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['name' => 'Mexican Peso', 'code' => 'MXN', 'symbol' => '$', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['name' => 'Egyptian Pound', 'code' => 'EGP', 'symbol' => '£', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
]; ];
foreach ($currencies as $currency) { foreach ($currencies as $currency) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

50
public/css/built.css vendored
View File

@ -2352,6 +2352,9 @@ body { background: #f8f8f8 !important;
font-family: 'Roboto', sans-serif; font-family: 'Roboto', sans-serif;
font-size: 15px; font-size: 15px;
} }
html {
overflow-y: scroll;
}
.bold { font-weight: 700; } .bold { font-weight: 700; }
a {color:#0b4d78;} a {color:#0b4d78;}
/*a:hover { text-decoration: none; color: #0a3857;}*/ /*a:hover { text-decoration: none; color: #0a3857;}*/
@ -3105,23 +3108,45 @@ box-shadow: 0px 0px 15px 0px rgba(0, 5, 5, 0.2);
text-decoration: line-through; text-decoration: line-through;
} }
@media screen and (min-width: 992px) {
.hide-desktop {display: none;} /* Custom, iPhone Retina */
@media only screen and (min-width : 320px) {
} }
@media (max-width: 992px) { /* Extra Small Devices, Phones */
.hide-phone { @media only screen and (min-width : 480px) {
display: none !important;
}
/* Small Devices, Tablets */
@media only screen and (min-width : 768px) {
.form-padding-right {
padding-right: 40px;
} }
} }
@media screen and (min-width: 992px) { /* Medium Devices, Desktops */
@media only screen and (min-width : 992px) {
.form-padding-right {
padding-right: 100px;
}
.medium-dialog { .medium-dialog {
width: 760px; width: 760px;
} }
.large-dialog { .large-dialog {
width: 960px; width: 960px;
} }
.hide-desktop
{
display: none;
}
}
@media (max-width: 992px) {
.hide-phone {
display: none !important;
}
} }
@media (max-width: 767px) { @media (max-width: 767px) {
@ -3337,7 +3362,18 @@ ul.user-accounts a:hover div.remove {
visibility: visible; visibility: visible;
} }
.tooltip-inner { .invoice-contact .tooltip-inner {
text-align:left; text-align:left;
width: 350px; width: 350px;
} }
/* Show selected section in settings nav */
.list-group-item.selected:before {
position: absolute;
top: 0;
left: 0;
bottom: 0;
width: 2px;
content: "";
background-color: #e37329;
}

View File

@ -785,7 +785,9 @@ body {
font-size: 14px; font-size: 14px;
background-color: #f8f8f8; background-color: #f8f8f8;
} }
html {
overflow-y: scroll;
}
@media screen and (min-width: 700px) { @media screen and (min-width: 700px) {
.navbar-header { .navbar-header {
@ -837,7 +839,7 @@ body {
display: inline-block; display: inline-block;
width: 9px; width: 9px;
height: 15px; height: 15px;
background: url({{ asset('images/social/facebook.svg') }}) no-repeat; background: url('../images/social/facebook.svg') no-repeat;
margin: 0 6px 0 0; margin: 0 6px 0 0;
position: relative; position: relative;
top: 3px; top: 3px;
@ -848,7 +850,7 @@ body {
display: inline-block; display: inline-block;
width: 19px; width: 19px;
height: 16px; height: 16px;
background: url({{ asset('images/social/twitter.svg') }}) no-repeat; background: url('../images/social/twitter.svg') no-repeat;
margin: 0 6px 0 0; margin: 0 6px 0 0;
position: relative; position: relative;
top: 3px; top: 3px;
@ -859,18 +861,18 @@ body {
display: inline-block; display: inline-block;
width: 19px; width: 19px;
height: 16px; height: 16px;
background: url({{ asset('images/social/github.png') }}) no-repeat; background: url('../images/social/github.png') no-repeat;
margin: 0 6px 0 0; margin: 0 6px 0 0;
position: relative; position: relative;
top: 3px; top: 3px;
} }
/* Hide bootstrap sort header icons */ /* Hide bootstrap sort header icons */
table.data-table thead .sorting:after { content: '' !important } table.table thead .sorting:after { content: '' !important }
table.data-table thead .sorting_asc:after { content: '' !important } table.table thead .sorting_asc:after { content: '' !important }
table.data-table thead .sorting_desc:after { content: '' !important} table.table thead .sorting_desc:after { content: '' !important }
table.data-table thead .sorting_asc_disabled:after { content: '' !important } table.table thead .sorting_asc_disabled:after { content: '' !important }
table.data-table thead .sorting_desc_disabled:after { content: '' !important } table.table thead .sorting_desc_disabled:after { content: '' !important }
.dataTables_length { .dataTables_length {
padding-left: 20px; padding-left: 20px;

View File

@ -3,7 +3,9 @@ body {
font-size: 14px; font-size: 14px;
background-color: #f8f8f8; background-color: #f8f8f8;
} }
html {
overflow-y: scroll;
}
@media screen and (min-width: 700px) { @media screen and (min-width: 700px) {
.navbar-header { .navbar-header {
@ -55,7 +57,7 @@ body {
display: inline-block; display: inline-block;
width: 9px; width: 9px;
height: 15px; height: 15px;
background: url({{ asset('images/social/facebook.svg') }}) no-repeat; background: url('../images/social/facebook.svg') no-repeat;
margin: 0 6px 0 0; margin: 0 6px 0 0;
position: relative; position: relative;
top: 3px; top: 3px;
@ -66,7 +68,7 @@ body {
display: inline-block; display: inline-block;
width: 19px; width: 19px;
height: 16px; height: 16px;
background: url({{ asset('images/social/twitter.svg') }}) no-repeat; background: url('../images/social/twitter.svg') no-repeat;
margin: 0 6px 0 0; margin: 0 6px 0 0;
position: relative; position: relative;
top: 3px; top: 3px;
@ -77,7 +79,7 @@ body {
display: inline-block; display: inline-block;
width: 19px; width: 19px;
height: 16px; height: 16px;
background: url({{ asset('images/social/github.png') }}) no-repeat; background: url('../images/social/github.png') no-repeat;
margin: 0 6px 0 0; margin: 0 6px 0 0;
position: relative; position: relative;
top: 3px; top: 3px;

50
public/css/style.css vendored
View File

@ -2,6 +2,9 @@ body { background: #f8f8f8 !important;
font-family: 'Roboto', sans-serif; font-family: 'Roboto', sans-serif;
font-size: 15px; font-size: 15px;
} }
html {
overflow-y: scroll;
}
.bold { font-weight: 700; } .bold { font-weight: 700; }
a {color:#0b4d78;} a {color:#0b4d78;}
/*a:hover { text-decoration: none; color: #0a3857;}*/ /*a:hover { text-decoration: none; color: #0a3857;}*/
@ -755,23 +758,45 @@ box-shadow: 0px 0px 15px 0px rgba(0, 5, 5, 0.2);
text-decoration: line-through; text-decoration: line-through;
} }
@media screen and (min-width: 992px) {
.hide-desktop {display: none;} /* Custom, iPhone Retina */
@media only screen and (min-width : 320px) {
} }
@media (max-width: 992px) { /* Extra Small Devices, Phones */
.hide-phone { @media only screen and (min-width : 480px) {
display: none !important;
}
/* Small Devices, Tablets */
@media only screen and (min-width : 768px) {
.form-padding-right {
padding-right: 40px;
} }
} }
@media screen and (min-width: 992px) { /* Medium Devices, Desktops */
@media only screen and (min-width : 992px) {
.form-padding-right {
padding-right: 100px;
}
.medium-dialog { .medium-dialog {
width: 760px; width: 760px;
} }
.large-dialog { .large-dialog {
width: 960px; width: 960px;
} }
.hide-desktop
{
display: none;
}
}
@media (max-width: 992px) {
.hide-phone {
display: none !important;
}
} }
@media (max-width: 767px) { @media (max-width: 767px) {
@ -987,7 +1012,18 @@ ul.user-accounts a:hover div.remove {
visibility: visible; visibility: visible;
} }
.tooltip-inner { .invoice-contact .tooltip-inner {
text-align:left; text-align:left;
width: 350px; width: 350px;
} }
/* Show selected section in settings nav */
.list-group-item.selected:before {
position: absolute;
top: 0;
left: 0;
bottom: 0;
width: 2px;
content: "";
background-color: #e37329;
}

View File

@ -32060,6 +32060,7 @@ NINJA.clientDetails = function(invoice) {
if (!client) { if (!client) {
return; return;
} }
var account = invoice.account;
var contact = client.contacts[0]; var contact = client.contacts[0];
var clientName = client.name || (contact.first_name || contact.last_name ? (contact.first_name + ' ' + contact.last_name) : contact.email); var clientName = client.name || (contact.first_name || contact.last_name ? (contact.first_name + ' ' + contact.last_name) : contact.email);
var clientEmail = client.contacts[0].email == clientName ? '' : client.contacts[0].email; var clientEmail = client.contacts[0].email == clientName ? '' : client.contacts[0].email;
@ -32070,6 +32071,11 @@ NINJA.clientDetails = function(invoice) {
cityStatePostal = formatAddress(client.city, client.state, client.postal_code, swap); cityStatePostal = formatAddress(client.city, client.state, client.postal_code, swap);
} }
// if a custom field is used in the invoice/quote number then we'll hide it from the PDF
var pattern = invoice.is_quote ? account.quote_number_pattern : account.invoice_number_pattern;
var custom1InPattern = (pattern && pattern.indexOf('{$custom1}') >= 0);
var custom2InPattern = (pattern && pattern.indexOf('{$custom2}') >= 0);
data = [ data = [
{text:clientName || ' ', style: ['clientName']}, {text:clientName || ' ', style: ['clientName']},
{text:client.id_number}, {text:client.id_number},
@ -32079,8 +32085,8 @@ NINJA.clientDetails = function(invoice) {
{text:cityStatePostal}, {text:cityStatePostal},
{text:client.country ? client.country.name : ''}, {text:client.country ? client.country.name : ''},
{text:clientEmail}, {text:clientEmail},
{text: invoice.client.custom_value1 ? invoice.account.custom_client_label1 + ' ' + invoice.client.custom_value1 : false}, {text: client.custom_value1 && !custom1InPattern ? account.custom_client_label1 + ' ' + client.custom_value1 : false},
{text: invoice.client.custom_value2 ? invoice.account.custom_client_label2 + ' ' + invoice.client.custom_value2 : false} {text: client.custom_value2 && !custom2InPattern ? account.custom_client_label2 + ' ' + client.custom_value2 : false}
]; ];
return NINJA.prepareDataList(data, 'clientDetails'); return NINJA.prepareDataList(data, 'clientDetails');

7928
public/js/pdf.built.js Normal file

File diff suppressed because one or more lines are too long

View File

@ -487,6 +487,7 @@ NINJA.clientDetails = function(invoice) {
if (!client) { if (!client) {
return; return;
} }
var account = invoice.account;
var contact = client.contacts[0]; var contact = client.contacts[0];
var clientName = client.name || (contact.first_name || contact.last_name ? (contact.first_name + ' ' + contact.last_name) : contact.email); var clientName = client.name || (contact.first_name || contact.last_name ? (contact.first_name + ' ' + contact.last_name) : contact.email);
var clientEmail = client.contacts[0].email == clientName ? '' : client.contacts[0].email; var clientEmail = client.contacts[0].email == clientName ? '' : client.contacts[0].email;
@ -497,6 +498,11 @@ NINJA.clientDetails = function(invoice) {
cityStatePostal = formatAddress(client.city, client.state, client.postal_code, swap); cityStatePostal = formatAddress(client.city, client.state, client.postal_code, swap);
} }
// if a custom field is used in the invoice/quote number then we'll hide it from the PDF
var pattern = invoice.is_quote ? account.quote_number_pattern : account.invoice_number_pattern;
var custom1InPattern = (pattern && pattern.indexOf('{$custom1}') >= 0);
var custom2InPattern = (pattern && pattern.indexOf('{$custom2}') >= 0);
data = [ data = [
{text:clientName || ' ', style: ['clientName']}, {text:clientName || ' ', style: ['clientName']},
{text:client.id_number}, {text:client.id_number},
@ -506,8 +512,8 @@ NINJA.clientDetails = function(invoice) {
{text:cityStatePostal}, {text:cityStatePostal},
{text:client.country ? client.country.name : ''}, {text:client.country ? client.country.name : ''},
{text:clientEmail}, {text:clientEmail},
{text: invoice.client.custom_value1 ? invoice.account.custom_client_label1 + ' ' + invoice.client.custom_value1 : false}, {text: client.custom_value1 && !custom1InPattern ? account.custom_client_label1 + ' ' + client.custom_value1 : false},
{text: invoice.client.custom_value2 ? invoice.account.custom_client_label2 + ' ' + invoice.client.custom_value2 : false} {text: client.custom_value2 && !custom2InPattern ? account.custom_client_label2 + ' ' + client.custom_value2 : false}
]; ];
return NINJA.prepareDataList(data, 'clientDetails'); return NINJA.prepareDataList(data, 'clientDetails');

View File

@ -7,14 +7,13 @@
[![Join the chat at https://gitter.im/hillelcoren/invoice-ninja](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/hillelcoren/invoice-ninja?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Join the chat at https://gitter.im/hillelcoren/invoice-ninja](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/hillelcoren/invoice-ninja?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
Please help our project by voting for us on [Product Hunt](http://www.producthunt.com/tech/invoice-ninja).
If you'd like to use our code to sell your own invoicing app email us for details about our affiliate program. If you'd like to use our code to sell your own invoicing app email us for details about our affiliate program.
### Installation Options ### Installation Options
* [Zip - Free](https://www.invoiceninja.com/knowledgebase/self-host/) * [Self-Host Zip](https://www.invoiceninja.com/knowledgebase/self-host/) - Free
* [Bitnami - Free](https://bitnami.com/stack/invoice-ninja) * [Docker File](https://github.com/rollbrettler/Dockerfiles/blob/master/invoice-ninja/Dockerfile) - Free
* [Softaculous - $30](https://www.softaculous.com/apps/ecommerce/Invoice_Ninja) * [Bitnami](https://bitnami.com/stack/invoice-ninja) - Free
* [Softaculous](https://www.softaculous.com/apps/ecommerce/Invoice_Ninja) - $30
### Features ### Features
* Built using Laravel 5 * Built using Laravel 5
@ -26,6 +25,7 @@ If you'd like to use our code to sell your own invoicing app email us for detail
* Tax rates and payment terms * Tax rates and payment terms
* Reminder emails * Reminder emails
* Partial payments * Partial payments
* Client portal
* Custom email templates * Custom email templates
* [Zapier](https://zapier.com/) integration * [Zapier](https://zapier.com/) integration
* [D3.js](http://d3js.org/) visualizations * [D3.js](http://d3js.org/) visualizations
@ -78,3 +78,4 @@ If you'd like to use our code to sell your own invoicing app email us for detail
* [bgrins/spectrum](https://github.com/bgrins/spectrum) - The No Hassle JavaScript Colorpicker * [bgrins/spectrum](https://github.com/bgrins/spectrum) - The No Hassle JavaScript Colorpicker
* [lokesh/lightbox2](https://github.com/lokesh/lightbox2/) - The original lightbox script * [lokesh/lightbox2](https://github.com/lokesh/lightbox2/) - The original lightbox script
* [josdejong/jsoneditor](https://github.com/josdejong/jsoneditor/) - A web-based tool to view, edit and format JSON * [josdejong/jsoneditor](https://github.com/josdejong/jsoneditor/) - A web-based tool to view, edit and format JSON
* [simshaun/recurr](https://github.com/simshaun/recurr) - PHP library for working with recurrence rules

View File

@ -262,7 +262,7 @@
'email_salutation' => 'Kære :name,', 'email_salutation' => 'Kære :name,',
'email_signature' => 'Med venlig hilsen,', 'email_signature' => 'Med venlig hilsen,',
'email_from' => 'Invoice Ninja Teamet', 'email_from' => 'Invoice Ninja Teamet',
'user_email_footer' => 'For at justere varslings indstillingene besøg venligst'.SITE_URL.'/company/notifications', 'user_email_footer' => 'For at justere varslings indstillingene besøg venligst'.SITE_URL.'/settings/notifications',
'invoice_link_message' => 'Hvis du vil se din klient faktura klik på linket under:', 'invoice_link_message' => 'Hvis du vil se din klient faktura klik på linket under:',
'notification_invoice_paid_subject' => 'Faktura :invoice betalt af :client', 'notification_invoice_paid_subject' => 'Faktura :invoice betalt af :client',
'notification_invoice_sent_subject' => 'Faktura :invoice sendt til :client', 'notification_invoice_sent_subject' => 'Faktura :invoice sendt til :client',
@ -273,7 +273,6 @@
'reset_password' => 'Du kan nulstille din adgangskode ved at besøge følgende link:', 'reset_password' => 'Du kan nulstille din adgangskode ved at besøge følgende link:',
'reset_password_footer' => 'Hvis du ikke bad om at få nulstillet din adgangskode kontakt venligst kundeservice: '.CONTACT_EMAIL, 'reset_password_footer' => 'Hvis du ikke bad om at få nulstillet din adgangskode kontakt venligst kundeservice: '.CONTACT_EMAIL,
// Payment page // Payment page
'secure_payment' => 'Sikker betaling', 'secure_payment' => 'Sikker betaling',
'card_number' => 'Kortnummer', 'card_number' => 'Kortnummer',
@ -746,7 +745,6 @@
'current_user' => 'Nuværende bruger', 'current_user' => 'Nuværende bruger',
'new_recurring_invoice' => 'Ny gentaget fakture', 'new_recurring_invoice' => 'Ny gentaget fakture',
'recurring_invoice' => 'Gentaget faktura', 'recurring_invoice' => 'Gentaget faktura',
'recurring_too_soon' => 'Det er for tidligt at generere den næste faktura',
'created_by_invoice' => 'Oprettet fra :invoice', 'created_by_invoice' => 'Oprettet fra :invoice',
'primary_user' => 'Primær bruger', 'primary_user' => 'Primær bruger',
'help' => 'Hjælp', 'help' => 'Hjælp',
@ -818,6 +816,15 @@
'custom_invoice_link' => 'Custom Invoice Link', 'custom_invoice_link' => 'Custom Invoice Link',
'total_invoiced' => 'Total Invoiced', 'total_invoiced' => 'Total Invoiced',
'open_balance' => 'Open Balance', 'open_balance' => 'Open Balance',
'verify_email' => 'Please visit the link in the account confirmation email to verify your email address.',
'basic_settings' => 'Basic Settings',
'pro' => 'Pro',
'gateways' => 'Payment Gateways',
'recurring_too_soon' => 'Det er for tidligt at generere den næste faktura, it\'s scheduled for :date',
'next_send_on' => 'Send Next: :date',
'no_longer_running' => 'This invoice is not scheduled to run',
'general_settings' => 'General Settings',
'customize' => 'Customize',
); );

View File

@ -77,6 +77,7 @@ return array(
"has_credit" => "The client does not have enough credit.", "has_credit" => "The client does not have enough credit.",
"notmasked" => "The values are masked", "notmasked" => "The values are masked",
"less_than" => 'The :attribute must be less than :value', "less_than" => 'The :attribute must be less than :value',
"has_counter" => 'The value must contain {$counter}',
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

View File

@ -0,0 +1,22 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Password Reminder Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are the default lines which match reasons
| that are given by the password broker for a password update attempt
| has failed, such as for an invalid token or invalid new password.
|
*/
"password" => "Beide Passwörter müssen übereinstimmen und mindestens 6 Zeichen lang sein.",
"user" => "Wir können keinen Nutzer mit dieser E-Mail Adresse finden.",
"token" => "Der Code zum Zurücksetzen des Passworts ist ungültig oder abgelaufen.",
"sent" => "Es wurde soeben eine E-Mail verschickt, die einen Link zum Zurücksetzen des Passworts enthält!",
"reset" => "Das Passwort wurde zurückgesetzt!",
];

View File

@ -46,7 +46,7 @@ return array(
'line_total' => 'Summe', 'line_total' => 'Summe',
'subtotal' => 'Zwischensumme', 'subtotal' => 'Zwischensumme',
'paid_to_date' => 'Bereits gezahlt', 'paid_to_date' => 'Bereits gezahlt',
'balance_due' => 'Geschuldeter Betrag', 'balance_due' => 'Offener Betrag',
'invoice_design_id' => 'Design', 'invoice_design_id' => 'Design',
'terms' => 'Bedingungen', 'terms' => 'Bedingungen',
'your_invoice' => 'Ihre Rechnung', 'your_invoice' => 'Ihre Rechnung',
@ -262,7 +262,7 @@ return array(
'email_salutation' => 'Sehr geehrte/r :name,', 'email_salutation' => 'Sehr geehrte/r :name,',
'email_signature' => 'Mit freundlichen Grüßen,', 'email_signature' => 'Mit freundlichen Grüßen,',
'email_from' => 'Das InvoiceNinja Team', 'email_from' => 'Das InvoiceNinja Team',
'user_email_footer' => 'Um deine E-Mail-Benachrichtigungen anzupassen besuche bitte '.SITE_URL.'/company/notifications', 'user_email_footer' => 'Um deine E-Mail-Benachrichtigungen anzupassen besuche bitte '.SITE_URL.'/settings/notifications',
'invoice_link_message' => 'Um deine Kundenrechnung anzuschauen, klicke auf den folgenden Link:', 'invoice_link_message' => 'Um deine Kundenrechnung anzuschauen, klicke auf den folgenden Link:',
'notification_invoice_paid_subject' => 'Die Rechnung :invoice wurde von :client bezahlt.', 'notification_invoice_paid_subject' => 'Die Rechnung :invoice wurde von :client bezahlt.',
'notification_invoice_sent_subject' => 'Die Rechnung :invoice wurde an :client versendet.', 'notification_invoice_sent_subject' => 'Die Rechnung :invoice wurde an :client versendet.',
@ -308,7 +308,6 @@ return array(
'success_message' => 'Du hast dich erfolgreich registriert. Bitte besuche den Link in deiner Bestätigungsmail um deine E-Mail-Adresse zu verifizieren.', 'success_message' => 'Du hast dich erfolgreich registriert. Bitte besuche den Link in deiner Bestätigungsmail um deine E-Mail-Adresse zu verifizieren.',
'erase_data' => 'Diese Aktion wird deine Daten dauerhaft löschen.', 'erase_data' => 'Diese Aktion wird deine Daten dauerhaft löschen.',
'password' => 'Passwort', 'password' => 'Passwort',
'close' => 'Schließen',
'pro_plan_product' => 'Pro Plan', 'pro_plan_product' => 'Pro Plan',
'pro_plan_description' => 'Jahresmitgliedschaft beim Invoice Ninja Pro Plan.', 'pro_plan_description' => 'Jahresmitgliedschaft beim Invoice Ninja Pro Plan.',
@ -574,7 +573,8 @@ return array(
'recover_password' => 'Passwort wiederherstellen', 'recover_password' => 'Passwort wiederherstellen',
'forgot_password' => 'Passwort vergessen?', 'forgot_password' => 'Passwort vergessen?',
'email_address' => 'E-Mail-Adresse', 'email_address' => 'E-Mail-Adresse',
'lets_go' => "Auf geht's", 'lets_go' => 'Auf geht\'s',
//'lets_go' => 'Login',
'password_recovery' => 'Passwort-Wiederherstellung', 'password_recovery' => 'Passwort-Wiederherstellung',
'send_email' => 'E-Mail verschicken', 'send_email' => 'E-Mail verschicken',
'set_password' => 'Passwort festlegen', 'set_password' => 'Passwort festlegen',
@ -745,14 +745,14 @@ return array(
'current_user' => 'Aktueller Benutzer', 'current_user' => 'Aktueller Benutzer',
'new_recurring_invoice' => 'Neue wiederkehrende Rechnung', 'new_recurring_invoice' => 'Neue wiederkehrende Rechnung',
'recurring_invoice' => 'Wiederkehrende Rechnung', 'recurring_invoice' => 'Wiederkehrende Rechnung',
'recurring_too_soon' => 'Es ist zu früh, um die nächste wiederkehrende Rechnung zu erstellen', 'recurring_too_soon' => 'Es ist zu früh die nächste wiederkehrende Rechnung zu erstellen, die nächste ist am :date geplant',
'created_by_invoice' => 'Erstellt durch :invoice', 'created_by_invoice' => 'Erstellt durch :invoice',
'primary_user' => 'Primärer Benutzer', 'primary_user' => 'Primärer Benutzer',
'help' => 'Hilfe', 'help' => 'Hilfe',
'customize_help' => '<p>We use <a href="http://pdfmake.org/" target="_blank">pdfmake</a> to define the invoice designs declaratively. The pdfmake <a href="http://pdfmake.org/playground.html" target="_blank">playground</a> provide\'s a great way to see the library in action.</p> 'customize_help' => '<p>Wir benutzen zur deklarativen Definition der Rechnungsdesigns <a href="http://pdfmake.org/" target="_blank">pdfmake</a>. Der pdfmake <a href="http://pdfmake.org/playground.html" target="_blank">playground</a> bietet Gelegenheit die Bibliothek in Aktion zu sehen.</p>
<p>You can access any invoice field by adding <code>Value</code> to the end. For example <code>$invoiceNumberValue</code> displays the invoice number.</p> <p>Man kann jedes Rechnungsfeld nutzen, in dem man <code>Value</code> hinten anhängt. Zum Beispiel zeigt <code>$invoiceNumberValue</code> die Rechnungsnummer.</p>
<p>To access a child property using dot notation. For example to show the client name you could use <code>$client.nameValue</code>.</p> <p>Mit der <i>dot notation</i> kann auf Kind-Eigenschaften zugegriffen werden. Für den Kundennamen kann man zum Beispiel <code>$client.nameValue</code> benutzen.</p>
<p>If you need help figuring something out post a question to our <a href="https://www.invoiceninja.com/forums/forum/support/" target="_blank">support forum</a>.</p>', <p>Wenn du Hilfe brauchst schreibe uns gern im <a href="https://www.invoiceninja.com/forums/forum/support/" target="_blank">Support Forum</a> (Englisch).</p>',
'invoice_due_date' => 'Fällig am', 'invoice_due_date' => 'Fällig am',
'quote_due_date' => 'Gültig bis', 'quote_due_date' => 'Gültig bis',
@ -767,11 +767,11 @@ return array(
'status_paid' => 'Bezahlt', 'status_paid' => 'Bezahlt',
'show_line_item_tax' => '<b>Steuern für Belegpositionen</b> in der jeweiligen Zeile anzeigen', 'show_line_item_tax' => '<b>Steuern für Belegpositionen</b> in der jeweiligen Zeile anzeigen',
'iframe_url' => 'Website', 'iframe_url' => 'Webseite',
'iframe_url_help1' => 'Kopiere den folgenden Code in eine Seite auf deiner Website.', 'iframe_url_help1' => 'Kopiere den folgenden Code in eine Seite auf deiner Website.',
'iframe_url_help2' => 'You can test the feature by clicking \'View as recipient\' for an invoice.', 'iframe_url_help2' => 'Du kannst diese Funktion testen, in dem du für eine Rechnung \'Als Empfänger betrachten\'. anklickst.',
'auto_bill' => 'Auto Bill', 'auto_bill' => 'Auto-Rechnung',
'military_time' => '24-Stunden-Zeit', 'military_time' => '24-Stunden-Zeit',
'last_sent' => 'Zuletzt gesendet', 'last_sent' => 'Zuletzt gesendet',
@ -792,31 +792,53 @@ return array(
'last_sent_on' => 'Zuletzt versendet am :date', 'last_sent_on' => 'Zuletzt versendet am :date',
'page_expire' => 'Diese Seite wird bald ablaufen, :click_here um weiter zu arbeiten', 'page_expire' => 'Diese Seite wird bald ablaufen, :click_here um weiter zu arbeiten',
'upcoming_quotes' => 'Upcoming Quotes', 'upcoming_quotes' => 'Kommende Angebote',
'expired_quotes' => 'Expired Quotes', 'expired_quotes' => 'Abgelaufene Angebote',
'sign_up_using' => 'Sign up using', 'sign_up_using' => 'Anmelden mit',
'invalid_credentials' => 'These credentials do not match our records', 'invalid_credentials' => 'Diese Zugangsdaten können wir nicht finden.',
'show_all_options' => 'Show all options', 'show_all_options' => 'Zeige alle Optionen',
'user_details' => 'User Details', 'user_details' => 'Nutzer Details',
'oneclick_login' => 'One-Click Login', 'oneclick_login' => 'One-Click Login',
'disable' => 'Disable', 'disable' => 'Deaktivieren',
'invoice_quote_number' => 'Invoice and Quote Numbers', 'invoice_quote_number' => 'Rechnungs- und Angebotsnummern',
'invoice_charges' => 'Invoice Charges', 'invoice_charges' => 'Rechnungs-Gebühren',
'invitation_status' => [ 'invitation_status' => [
'sent' => 'Email Sent', 'sent' => 'E-Mail versandt',
'opened' => 'Email Openend', 'opened' => 'E-Mail geöffnet',
'viewed' => 'Invoice Viewed', 'viewed' => 'Rechnung angesehen',
], ],
'notification_invoice_bounced' => 'We were unable to deliver Invoice :invoice to :contact.', 'notification_invoice_bounced' => 'Die Rechnung :invoice an :contact konnte nicht zugestellt werden.',
'notification_invoice_bounced_subject' => 'Unable to deliver Invoice :invoice', 'notification_invoice_bounced_subject' => 'Rechnung :invoice nicht zugestellt.',
'notification_quote_bounced' => 'We were unable to deliver Quote :invoice to :contact.', 'notification_quote_bounced' => 'Das Angebot :invoice an :contact konnte nicht zugestellt werden.',
'notification_quote_bounced_subject' => 'Unable to deliver Quote :invoice', 'notification_quote_bounced_subject' => 'Angebot :invoice wurde nicht zugestellt.',
'custom_invoice_link' => 'Custom Invoice Link', 'custom_invoice_link' => 'Manueller Rechnungs-Link',
'total_invoiced' => 'Total Invoiced', 'total_invoiced' => 'Rechnungs-Betrag',
'open_balance' => 'Open Balance', 'open_balance' => 'Offener Betrag',
'verify_email' => 'Bitte klicken sie auf den Link in der Bestätigungsmail, um die E-Mail-Adresse zu verifizieren.',
'basic_settings' => 'Allgemeine Einstellungen',
'pro' => 'Pro',
'gateways' => 'Payment Gateways',
'next_send_on' => 'Nächster Versand: :date',
'no_longer_running' => 'Diese Rechnung wird momentan nicht automatisch erstellt.',
'general_settings' => 'Allgemeine Einstellungen',
'customize' => 'Anpassen',
'oneclick_login_help' => 'Connect an account to login without a password',
'referral_code_help' => 'Earn money by sharing our app online',
'enable_with_stripe' => 'Enable | Requires Stripe',
'tax_settings' => 'Steuer Einstellungen',
'create_tax_rate' => 'Neuer Steuersatz',
'updated_tax_rate' => 'Steuersatz aktualisiert',
'created_tax_rate' => 'Steuersatz erstellt',
'edit_tax_rate' => 'Steuersatz bearbeiten',
'archive_tax_rate' => 'Steuersatz archivieren',
'archived_tax_rate' => 'Steuersatz archiviert',
'default_tax_rate_id' => 'Standard Steuersatz',
'tax_rate' => 'Steuersatz',
); );

View File

@ -75,6 +75,7 @@ return array(
"has_credit" => "Der Kunde hat nicht genug Guthaben.", "has_credit" => "Der Kunde hat nicht genug Guthaben.",
"notmasked" => "The values are masked", "notmasked" => "The values are masked",
"less_than" => 'The :attribute must be less than :value', "less_than" => 'The :attribute must be less than :value',
"has_counter" => 'The value must contain {$counter}',
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

View File

@ -262,7 +262,7 @@ return array(
'email_salutation' => 'Dear :name,', 'email_salutation' => 'Dear :name,',
'email_signature' => 'Regards,', 'email_signature' => 'Regards,',
'email_from' => 'The Invoice Ninja Team', 'email_from' => 'The Invoice Ninja Team',
'user_email_footer' => 'To adjust your email notification settings please visit '.SITE_URL.'/company/notifications', 'user_email_footer' => 'To adjust your email notification settings please visit '.SITE_URL.'/settings/notifications',
'invoice_link_message' => 'To view your client invoice click the link below:', 'invoice_link_message' => 'To view your client invoice click the link below:',
'notification_invoice_paid_subject' => 'Invoice :invoice was paid by :client', 'notification_invoice_paid_subject' => 'Invoice :invoice was paid by :client',
'notification_invoice_sent_subject' => 'Invoice :invoice was sent to :client', 'notification_invoice_sent_subject' => 'Invoice :invoice was sent to :client',
@ -337,7 +337,7 @@ return array(
'fill_products_help' => 'Selecting a product will automatically <b>fill in the description and cost</b>', 'fill_products_help' => 'Selecting a product will automatically <b>fill in the description and cost</b>',
'update_products' => 'Auto-update products', 'update_products' => 'Auto-update products',
'update_products_help' => 'Updating an invoice will automatically <b>update the product library</b>', 'update_products_help' => 'Updating an invoice will automatically <b>update the product library</b>',
'create_product' => 'Create Product', 'create_product' => 'Add Product',
'edit_product' => 'Edit Product', 'edit_product' => 'Edit Product',
'archive_product' => 'Archive Product', 'archive_product' => 'Archive Product',
'updated_product' => 'Successfully updated product', 'updated_product' => 'Successfully updated product',
@ -557,7 +557,7 @@ return array(
'created_gateway' => 'Successfully created gateway', 'created_gateway' => 'Successfully created gateway',
'deleted_gateway' => 'Successfully deleted gateway', 'deleted_gateway' => 'Successfully deleted gateway',
'pay_with_paypal' => 'PayPal', 'pay_with_paypal' => 'PayPal',
'pay_with_card' => 'Credit card', 'pay_with_card' => 'Credit Card',
'change_password' => 'Change password', 'change_password' => 'Change password',
'current_password' => 'Current password', 'current_password' => 'Current password',
@ -587,7 +587,7 @@ return array(
'confirmation_resent' => 'The confirmation email was resent', 'confirmation_resent' => 'The confirmation email was resent',
'gateway_help_42' => ':link to sign up for BitPay.<br/>Note: use a Legacy API Key, not an API token.', 'gateway_help_42' => ':link to sign up for BitPay.<br/>Note: use a Legacy API Key, not an API token.',
'payment_type_credit_card' => 'Credit card', 'payment_type_credit_card' => 'Credit Card',
'payment_type_paypal' => 'PayPal', 'payment_type_paypal' => 'PayPal',
'payment_type_bitcoin' => 'Bitcoin', 'payment_type_bitcoin' => 'Bitcoin',
'knowledge_base' => 'Knowledge Base', 'knowledge_base' => 'Knowledge Base',
@ -745,7 +745,7 @@ return array(
'current_user' => 'Current User', 'current_user' => 'Current User',
'new_recurring_invoice' => 'New Recurring Invoice', 'new_recurring_invoice' => 'New Recurring Invoice',
'recurring_invoice' => 'Recurring Invoice', 'recurring_invoice' => 'Recurring Invoice',
'recurring_too_soon' => 'It\'s too soon to create the next recurring invoice', 'recurring_too_soon' => 'It\'s too soon to create the next recurring invoice, it\'s scheduled for :date',
'created_by_invoice' => 'Created by :invoice', 'created_by_invoice' => 'Created by :invoice',
'primary_user' => 'Primary User', 'primary_user' => 'Primary User',
'help' => 'Help', 'help' => 'Help',
@ -789,7 +789,7 @@ return array(
'referral_program' => 'Referral Program', 'referral_program' => 'Referral Program',
'referral_code' => 'Referral Code', 'referral_code' => 'Referral Code',
'last_sent_on' => 'Last sent on :date', 'last_sent_on' => 'Sent Last: :date',
'page_expire' => 'This page will expire soon, :click_here to keep working', 'page_expire' => 'This page will expire soon, :click_here to keep working',
'upcoming_quotes' => 'Upcoming Quotes', 'upcoming_quotes' => 'Upcoming Quotes',
@ -809,6 +809,7 @@ return array(
'opened' => 'Email Openend', 'opened' => 'Email Openend',
'viewed' => 'Invoice Viewed', 'viewed' => 'Invoice Viewed',
], ],
'notification_invoice_bounced' => 'We were unable to deliver Invoice :invoice to :contact.', 'notification_invoice_bounced' => 'We were unable to deliver Invoice :invoice to :contact.',
'notification_invoice_bounced_subject' => 'Unable to deliver Invoice :invoice', 'notification_invoice_bounced_subject' => 'Unable to deliver Invoice :invoice',
'notification_quote_bounced' => 'We were unable to deliver Quote :invoice to :contact.', 'notification_quote_bounced' => 'We were unable to deliver Quote :invoice to :contact.',
@ -818,6 +819,37 @@ return array(
'total_invoiced' => 'Total Invoiced', 'total_invoiced' => 'Total Invoiced',
'open_balance' => 'Open Balance', 'open_balance' => 'Open Balance',
'verify_email' => 'Please visit the link in the account confirmation email to verify your email address.', 'verify_email' => 'Please visit the link in the account confirmation email to verify your email address.',
'basic_settings' => 'Basic Settings',
'pro' => 'Pro',
'gateways' => 'Payment Gateways',
'next_send_on' => 'Send Next: :date',
'no_longer_running' => 'This invoice is not scheduled to run',
'general_settings' => 'General Settings',
'customize' => 'Customize',
'oneclick_login_help' => 'Connect an account to login without a password',
'referral_code_help' => 'Earn money by sharing our app online',
'enable_with_stripe' => 'Enable | Requires Stripe',
'tax_settings' => 'Tax Settings',
'create_tax_rate' => 'Add Tax Rate',
'updated_tax_rate' => 'Successfully updated tax rate',
'created_tax_rate' => 'Successfully created tax rate',
'edit_tax_rate' => 'Edit tax rate',
'archive_tax_rate' => 'Archive tax rate',
'archived_tax_rate' => 'Successfully archived the tax rate',
'default_tax_rate_id' => 'Default Tax Rate',
'tax_rate' => 'Tax Rate',
'recurring_hour' => 'Recurring Hour',
'pattern' => 'Pattern',
'pattern_help_title' => 'Pattern Help',
'pattern_help_1' => 'Create custom invoice and quote numbers by specifying a pattern',
'pattern_help_2' => 'Available variables:',
'pattern_help_3' => 'For example, :example would be converted to :value',
'see_options' => 'See options',
'invoice_counter' => 'Invoice Counter',
'quote_counter' => 'Quote Counter',
'type' => 'Type',
); );

View File

@ -73,6 +73,7 @@ return array(
"has_credit" => "The client does not have enough credit.", "has_credit" => "The client does not have enough credit.",
"notmasked" => "The values are masked", "notmasked" => "The values are masked",
"less_than" => 'The :attribute must be less than :value', "less_than" => 'The :attribute must be less than :value',
"has_counter" => 'The value must contain {$counter}',
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

View File

@ -256,7 +256,7 @@ return array(
'email_salutation' => 'Estimado :name,', 'email_salutation' => 'Estimado :name,',
'email_signature' => 'Un saludo cordial,', 'email_signature' => 'Un saludo cordial,',
'email_from' => 'El equipo de Invoice Ninja ', 'email_from' => 'El equipo de Invoice Ninja ',
'user_email_footer' => 'Para ajustar la configuración de las notificaciones de tu correo, visita '.SITE_URL.'/company/notifications', 'user_email_footer' => 'Para ajustar la configuración de las notificaciones de tu correo, visita '.SITE_URL.'/settings/notifications',
'invoice_link_message' => 'Para visualizar la factura de cliente, haz clic en el enlace abajo:', 'invoice_link_message' => 'Para visualizar la factura de cliente, haz clic en el enlace abajo:',
'notification_invoice_paid_subject' => 'La factura :invoice ha sido pagada por el cliente :client', 'notification_invoice_paid_subject' => 'La factura :invoice ha sido pagada por el cliente :client',
'notification_invoice_sent_subject' => 'La factura :invoice ha sido enviada a el cliente :client', 'notification_invoice_sent_subject' => 'La factura :invoice ha sido enviada a el cliente :client',
@ -723,7 +723,6 @@ return array(
'current_user' => 'Usuario Actual', 'current_user' => 'Usuario Actual',
'new_recurring_invoice' => 'Nueva Factura Recurrente', 'new_recurring_invoice' => 'Nueva Factura Recurrente',
'recurring_invoice' => 'Factura Recurrente', 'recurring_invoice' => 'Factura Recurrente',
'recurring_too_soon' => 'Es my pronto para crear la siguiente factura recurrente',
'created_by_invoice' => 'Creado por :invoice', 'created_by_invoice' => 'Creado por :invoice',
'primary_user' => 'Usuario Primario', 'primary_user' => 'Usuario Primario',
'help' => 'Ayuda', 'help' => 'Ayuda',
@ -795,6 +794,16 @@ return array(
'custom_invoice_link' => 'Custom Invoice Link', 'custom_invoice_link' => 'Custom Invoice Link',
'total_invoiced' => 'Total Invoiced', 'total_invoiced' => 'Total Invoiced',
'open_balance' => 'Open Balance', 'open_balance' => 'Open Balance',
'verify_email' => 'Please visit the link in the account confirmation email to verify your email address.',
'basic_settings' => 'Basic Settings',
'pro' => 'Pro',
'gateways' => 'Payment Gateways',
'recurring_too_soon' => 'Es my pronto para crear la siguiente factura recurrente, it\'s scheduled for :date',
'next_send_on' => 'Send Next: :date',
'no_longer_running' => 'This invoice is not scheduled to run',
'general_settings' => 'General Settings',
'customize' => 'Customize',
); );

View File

@ -74,6 +74,7 @@ return array(
"has_credit" => "el cliente no tiene crédito suficiente.", "has_credit" => "el cliente no tiene crédito suficiente.",
"notmasked" => "The values are masked", "notmasked" => "The values are masked",
"less_than" => 'The :attribute must be less than :value', "less_than" => 'The :attribute must be less than :value',
"has_counter" => 'The value must contain {$counter}',
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

View File

@ -1,4 +1,4 @@
<?php <?php
return array( return array(
@ -271,7 +271,7 @@ return array(
'email_salutation' => 'Estimado :name,', 'email_salutation' => 'Estimado :name,',
'email_signature' => 'Un cordial saludo,', 'email_signature' => 'Un cordial saludo,',
'email_from' => 'El equipo de Invoice Ninja ', 'email_from' => 'El equipo de Invoice Ninja ',
'user_email_footer' => 'Para ajustar la configuración de las notificaciones de tu email, visita '.SITE_URL.'/company/notifications', 'user_email_footer' => 'Para ajustar la configuración de las notificaciones de tu email, visita '.SITE_URL.'/settings/notifications',
'invoice_link_message' => 'Para visualizar la factura de cliente, haz clic en el enlace de abajo:', 'invoice_link_message' => 'Para visualizar la factura de cliente, haz clic en el enlace de abajo:',
'notification_invoice_paid_subject' => 'La factura :invoice ha sido pagada por el cliente :client', 'notification_invoice_paid_subject' => 'La factura :invoice ha sido pagada por el cliente :client',
'notification_invoice_sent_subject' => 'La factura :invoice ha sido enviada a el cliente :client', 'notification_invoice_sent_subject' => 'La factura :invoice ha sido enviada a el cliente :client',
@ -435,7 +435,6 @@ return array(
'invalid_counter' => 'Para evitar posibles conflictos, por favor crea un prefijo de facturación y de presupuesto.', 'invalid_counter' => 'Para evitar posibles conflictos, por favor crea un prefijo de facturación y de presupuesto.',
'mark_sent' => 'Marcar como enviado', 'mark_sent' => 'Marcar como enviado',
'gateway_help_1' => ':link para registrarse en Authorize.net.', 'gateway_help_1' => ':link para registrarse en Authorize.net.',
'gateway_help_2' => ':link para registrarse en Authorize.net.', 'gateway_help_2' => ':link para registrarse en Authorize.net.',
'gateway_help_17' => ':link para obtener su firma API de PayPal.', 'gateway_help_17' => ':link para obtener su firma API de PayPal.',
@ -745,7 +744,7 @@ return array(
'current_user' => 'Current User', 'current_user' => 'Current User',
'new_recurring_invoice' => 'New Recurring Invoice', 'new_recurring_invoice' => 'New Recurring Invoice',
'recurring_invoice' => 'Recurring Invoice', 'recurring_invoice' => 'Recurring Invoice',
'recurring_too_soon' => 'It\'s too soon to create the next recurring invoice', 'recurring_too_soon' => 'It\'s too soon to create the next recurring invoice, it\'s scheduled for :date',
'created_by_invoice' => 'Created by :invoice', 'created_by_invoice' => 'Created by :invoice',
'primary_user' => 'Primary User', 'primary_user' => 'Primary User',
'help' => 'Help', 'help' => 'Help',
@ -817,6 +816,14 @@ return array(
'custom_invoice_link' => 'Custom Invoice Link', 'custom_invoice_link' => 'Custom Invoice Link',
'total_invoiced' => 'Total Invoiced', 'total_invoiced' => 'Total Invoiced',
'open_balance' => 'Open Balance', 'open_balance' => 'Open Balance',
'verify_email' => 'Please visit the link in the account confirmation email to verify your email address.',
'basic_settings' => 'Basic Settings',
'pro' => 'Pro',
'gateways' => 'Payment Gateways',
'next_send_on' => 'Send Next: :date',
'no_longer_running' => 'This invoice is not scheduled to run',
'general_settings' => 'General Settings',
'customize' => 'Customize',
); );

View File

@ -74,6 +74,7 @@ return array(
"has_credit" => "el cliente no tiene crédito suficiente.", "has_credit" => "el cliente no tiene crédito suficiente.",
"notmasked" => "The values are masked", "notmasked" => "The values are masked",
"less_than" => 'The :attribute must be less than :value', "less_than" => 'The :attribute must be less than :value',
"has_counter" => 'The value must contain {$counter}',
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

View File

@ -262,7 +262,7 @@ return array(
'email_salutation' => 'Cher :name,', 'email_salutation' => 'Cher :name,',
'email_signature' => 'Cordialement,', 'email_signature' => 'Cordialement,',
'email_from' => 'L\'équipe Invoice Ninja', 'email_from' => 'L\'équipe Invoice Ninja',
'user_email_footer' => 'Pour modifier vos paramètres de notification par courriel, veuillez visiter '.SITE_URL.'/company/notifications', 'user_email_footer' => 'Pour modifier vos paramètres de notification par courriel, veuillez visiter '.SITE_URL.'/settings/notifications',
'invoice_link_message' => 'Pour voir la facture de votre client cliquez sur le lien ci-après :', 'invoice_link_message' => 'Pour voir la facture de votre client cliquez sur le lien ci-après :',
'notification_invoice_paid_subject' => 'La facture :invoice a été payée par le client :client', 'notification_invoice_paid_subject' => 'La facture :invoice a été payée par le client :client',
'notification_invoice_sent_subject' => 'La facture :invoice a été envoyée au client :client', 'notification_invoice_sent_subject' => 'La facture :invoice a été envoyée au client :client',
@ -532,16 +532,16 @@ return array(
'invoice_footer' => 'Pied de facture', 'invoice_footer' => 'Pied de facture',
'save_as_default_footer' => 'Définir comme pied de facture par défatu', 'save_as_default_footer' => 'Définir comme pied de facture par défatu',
'token_management' => 'Token Management', 'token_management' => 'Gestion des jetons',
'tokens' => 'Tokens', 'tokens' => 'Jetons',
'add_token' => 'Add Token', 'add_token' => 'Ajouter jeton',
'show_deleted_tokens' => 'Show deleted tokens', 'show_deleted_tokens' => 'Voir les jetons supprimés',
'deleted_token' => 'Successfully deleted token', 'deleted_token' => 'Jeton supprimé avec succès',
'created_token' => 'Successfully created token', 'created_token' => 'Jeton crée avec succès',
'updated_token' => 'Successfully updated token', 'updated_token' => 'Jeton mis à jour avec succès',
'edit_token' => 'Edit Token', 'edit_token' => 'Editer jeton',
'delete_token' => 'Delete Token', 'delete_token' => 'Supprimer jeton',
'token' => 'Token', 'token' => 'Jeton',
'add_gateway' => 'Ajouter passerelle', 'add_gateway' => 'Ajouter passerelle',
'delete_gateway' => 'Supprimer passerelle', 'delete_gateway' => 'Supprimer passerelle',
@ -560,17 +560,17 @@ return array(
'password_error_invalid' => 'Le nouveau mot de passe est invalide', 'password_error_invalid' => 'Le nouveau mot de passe est invalide',
'updated_password' => 'Mot de passe mis à jour avec succès', 'updated_password' => 'Mot de passe mis à jour avec succès',
'api_tokens' => 'API Tokens', 'api_tokens' => 'Jetons d\'API',
'users_and_tokens' => 'Users & Tokens', 'users_and_tokens' => 'Utilisateurs & jetons',
'account_login' => 'Account Login', 'account_login' => 'Connexion à votre compte',
'recover_password' => 'Recover your password', 'recover_password' => 'Recover your password',
'forgot_password' => 'Mot de passe oublié ?', 'forgot_password' => 'Mot de passe oublié ?',
'email_address' => 'Adresse email', 'email_address' => 'Adresse email',
'lets_go' => 'Allons-y !', 'lets_go' => 'Allons-y !',
'password_recovery' => 'Password Recovery', 'password_recovery' => 'Récupération du mot de passe',
'send_email' => 'Send email', 'send_email' => 'Envoyer email',
'set_password' => 'Set Password', 'set_password' => 'Ajouter mot de passe',
'converted' => 'Converted', 'converted' => 'Converti',
'email_approved' => 'Email me when a quote is <b>approved</b>', 'email_approved' => 'Email me when a quote is <b>approved</b>',
'notification_quote_approved_subject' => 'Quote :invoice was approved by :client', 'notification_quote_approved_subject' => 'Quote :invoice was approved by :client',
@ -730,14 +730,13 @@ return array(
'invoice_to' => 'Facture pour', 'invoice_to' => 'Facture pour',
'invoice_no' => 'Facture n°', 'invoice_no' => 'Facture n°',
'recent_payments' => 'Paiements récents', 'recent_payments' => 'Paiements récents',
'outstanding' => 'Extraordinaire', 'outstanding' => 'Impayé',
'manage_companies' => 'Gérer les sociétés', 'manage_companies' => 'Gérer les sociétés',
'total_revenue' => 'Revenu total', 'total_revenue' => 'Revenu total',
'current_user' => 'Utilisateur actuel', 'current_user' => 'Utilisateur actuel',
'new_recurring_invoice' => 'Nouvelle facture récurrente', 'new_recurring_invoice' => 'Nouvelle facture récurrente',
'recurring_invoice' => 'Facture récurrente', 'recurring_invoice' => 'Facture récurrente',
'recurring_too_soon' => 'Il est trop tôt pour créer la prochaine facture récurrente',
'created_by_invoice' => 'Créé par :invoice', 'created_by_invoice' => 'Créé par :invoice',
'primary_user' => 'Utilisateur principal', 'primary_user' => 'Utilisateur principal',
'help' => 'Aide', 'help' => 'Aide',
@ -763,17 +762,17 @@ return array(
'iframe_url_help1' => 'Copy the following code to a page on your site.', 'iframe_url_help1' => 'Copy the following code to a page on your site.',
'iframe_url_help2' => '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', 'auto_bill' => 'Facturation automatique',
'military_time' => '24 Hour Time', 'military_time' => '24 Hour Time',
'last_sent' => 'Last Sent', 'last_sent' => 'Dernier envoi',
'reminder_emails' => 'Reminder Emails', 'reminder_emails' => 'Emails de rappel',
'templates_and_reminders' => 'Templates & Reminders', 'templates_and_reminders' => 'Templates & Reminders',
'subject' => 'Subject', 'subject' => 'Sujet',
'body' => 'Body', 'body' => 'Corps',
'first_reminder' => 'First Reminder', 'first_reminder' => 'Premier rappel',
'second_reminder' => 'Second Reminder', 'second_reminder' => 'Second rappel',
'third_reminder' => 'Third Reminder', 'third_reminder' => 'Troisième rappel',
'num_days_reminder' => 'Days after due date', 'num_days_reminder' => 'Days after due date',
'reminder_subject' => 'Reminder: Invoice :invoice from :account', 'reminder_subject' => 'Reminder: Invoice :invoice from :account',
'reset' => 'Reset', 'reset' => 'Reset',
@ -784,8 +783,8 @@ return array(
'last_sent_on' => 'Last sent on :date', 'last_sent_on' => 'Last sent on :date',
'page_expire' => 'This page will expire soon, :click_here to keep working', 'page_expire' => 'This page will expire soon, :click_here to keep working',
'upcoming_quotes' => 'Upcoming Quotes', 'upcoming_quotes' => 'Devis à venir',
'expired_quotes' => 'Expired Quotes', 'expired_quotes' => 'Devis expirés',
'sign_up_using' => 'Sign up using', 'sign_up_using' => 'Sign up using',
'invalid_credentials' => 'These credentials do not match our records', 'invalid_credentials' => 'These credentials do not match our records',
@ -797,9 +796,9 @@ return array(
'invoice_charges' => 'Invoice Charges', 'invoice_charges' => 'Invoice Charges',
'invitation_status' => [ 'invitation_status' => [
'sent' => 'Email Sent', 'sent' => 'Email envoyé',
'opened' => 'Email Openend', 'opened' => 'Email ouvert',
'viewed' => 'Invoice Viewed', 'viewed' => 'Facture vue',
], ],
'notification_invoice_bounced' => 'We were unable to deliver Invoice :invoice to :contact.', 'notification_invoice_bounced' => 'We were unable to deliver Invoice :invoice to :contact.',
'notification_invoice_bounced_subject' => 'Unable to deliver Invoice :invoice', 'notification_invoice_bounced_subject' => 'Unable to deliver Invoice :invoice',
@ -809,6 +808,16 @@ return array(
'custom_invoice_link' => 'Custom Invoice Link', 'custom_invoice_link' => 'Custom Invoice Link',
'total_invoiced' => 'Total Invoiced', 'total_invoiced' => 'Total Invoiced',
'open_balance' => 'Open Balance', 'open_balance' => 'Open Balance',
'verify_email' => 'Please visit the link in the account confirmation email to verify your email address.',
'basic_settings' => 'Basic Settings',
'pro' => 'Pro',
'gateways' => 'Passerelles de paiement',
'recurring_too_soon' => 'Il est trop tôt pour créer la prochaine facture récurrente, it\'s scheduled for :date',
'next_send_on' => 'Send Next: :date',
'no_longer_running' => 'This invoice is not scheduled to run',
'general_settings' => 'General Settings',
'customize' => 'Customize',
); );

View File

@ -75,6 +75,7 @@ return array(
"has_credit" => "The client does not have enough credit.", "has_credit" => "The client does not have enough credit.",
"notmasked" => "The values are masked", "notmasked" => "The values are masked",
"less_than" => 'The :attribute must be less than :value', "less_than" => 'The :attribute must be less than :value',
"has_counter" => 'The value must contain {$counter}',
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

View File

@ -262,7 +262,7 @@ return array(
'email_salutation' => 'Cher :name,', 'email_salutation' => 'Cher :name,',
'email_signature' => 'Cordialement,', 'email_signature' => 'Cordialement,',
'email_from' => 'L\'équipe InvoiceNinja', 'email_from' => 'L\'équipe InvoiceNinja',
'user_email_footer' => 'Pour modifier vos paramètres de notification par courriel, veuillez visiter '.SITE_URL.'/company/notifications', 'user_email_footer' => 'Pour modifier vos paramètres de notification par courriel, veuillez visiter '.SITE_URL.'/settings/notifications',
'invoice_link_message' => 'Pour voir la facture de votre client cliquez sur le lien ci-après :', 'invoice_link_message' => 'Pour voir la facture de votre client cliquez sur le lien ci-après :',
'notification_invoice_paid_subject' => 'La facture :invoice a été payée par le client :client', 'notification_invoice_paid_subject' => 'La facture :invoice a été payée par le client :client',
'notification_invoice_sent_subject' => 'La facture :invoice a été envoyée au client :client', 'notification_invoice_sent_subject' => 'La facture :invoice a été envoyée au client :client',
@ -738,7 +738,7 @@ return array(
'current_user' => 'Current User', 'current_user' => 'Current User',
'new_recurring_invoice' => 'New Recurring Invoice', 'new_recurring_invoice' => 'New Recurring Invoice',
'recurring_invoice' => 'Recurring Invoice', 'recurring_invoice' => 'Recurring Invoice',
'recurring_too_soon' => 'It\'s too soon to create the next recurring invoice', 'recurring_too_soon' => 'It\'s too soon to create the next recurring invoice, it\'s scheduled for :date',
'created_by_invoice' => 'Created by :invoice', 'created_by_invoice' => 'Created by :invoice',
'primary_user' => 'Primary User', 'primary_user' => 'Primary User',
'help' => 'Help', 'help' => 'Help',
@ -810,6 +810,15 @@ return array(
'custom_invoice_link' => 'Custom Invoice Link', 'custom_invoice_link' => 'Custom Invoice Link',
'total_invoiced' => 'Total Invoiced', 'total_invoiced' => 'Total Invoiced',
'open_balance' => 'Open Balance', 'open_balance' => 'Open Balance',
'verify_email' => 'Please visit the link in the account confirmation email to verify your email address.',
'basic_settings' => 'Basic Settings',
'pro' => 'Pro',
'gateways' => 'Payment Gateways',
'next_send_on' => 'Send Next: :date',
'no_longer_running' => 'This invoice is not scheduled to run',
'general_settings' => 'General Settings',
'customize' => 'Customize',
); );

View File

@ -75,6 +75,7 @@ return array(
"has_credit" => "Le client n'a pas un crédit suffisant.", "has_credit" => "Le client n'a pas un crédit suffisant.",
"notmasked" => "Les valeurs sont masquées", "notmasked" => "Les valeurs sont masquées",
"less_than" => 'The :attribute must be less than :value', "less_than" => 'The :attribute must be less than :value',
"has_counter" => 'The value must contain {$counter}',
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

View File

@ -262,7 +262,7 @@ return array(
'email_salutation' => 'Caro :name,', 'email_salutation' => 'Caro :name,',
'email_signature' => 'Distinti saluti,', 'email_signature' => 'Distinti saluti,',
'email_from' => 'Il Team di InvoiceNinja', 'email_from' => 'Il Team di InvoiceNinja',
'user_email_footer' => 'Per modificare le impostazioni di notifiche via email per favore accedi a: '.SITE_URL.'/company/notifications', 'user_email_footer' => 'Per modificare le impostazioni di notifiche via email per favore accedi a: '.SITE_URL.'/settings/notifications',
'invoice_link_message' => 'Per visualizzare la tua fattura del cliente clicca sul link qui sotto:', 'invoice_link_message' => 'Per visualizzare la tua fattura del cliente clicca sul link qui sotto:',
'notification_invoice_paid_subject' => 'La fattura :invoice è stata pagata da :client', 'notification_invoice_paid_subject' => 'La fattura :invoice è stata pagata da :client',
'notification_invoice_sent_subject' => 'La fattura :invoice è stata inviata a :client', 'notification_invoice_sent_subject' => 'La fattura :invoice è stata inviata a :client',
@ -740,7 +740,7 @@ return array(
'current_user' => 'Current User', 'current_user' => 'Current User',
'new_recurring_invoice' => 'New Recurring Invoice', 'new_recurring_invoice' => 'New Recurring Invoice',
'recurring_invoice' => 'Recurring Invoice', 'recurring_invoice' => 'Recurring Invoice',
'recurring_too_soon' => 'It\'s too soon to create the next recurring invoice', 'recurring_too_soon' => 'It\'s too soon to create the next recurring invoice, it\'s scheduled for :date',
'created_by_invoice' => 'Created by :invoice', 'created_by_invoice' => 'Created by :invoice',
'primary_user' => 'Primary User', 'primary_user' => 'Primary User',
'help' => 'Help', 'help' => 'Help',
@ -812,5 +812,14 @@ return array(
'custom_invoice_link' => 'Custom Invoice Link', 'custom_invoice_link' => 'Custom Invoice Link',
'total_invoiced' => 'Total Invoiced', 'total_invoiced' => 'Total Invoiced',
'open_balance' => 'Open Balance', 'open_balance' => 'Open Balance',
'verify_email' => 'Please visit the link in the account confirmation email to verify your email address.',
'basic_settings' => 'Basic Settings',
'pro' => 'Pro',
'gateways' => 'Payment Gateways',
'next_send_on' => 'Send Next: :date',
'no_longer_running' => 'This invoice is not scheduled to run',
'general_settings' => 'General Settings',
'customize' => 'Customize',
); );

View File

@ -74,6 +74,7 @@ return array(
"has_credit" => "The client does not have enough credit.", "has_credit" => "The client does not have enough credit.",
"notmasked" => "The values are masked", "notmasked" => "The values are masked",
"less_than" => 'The :attribute must be less than :value', "less_than" => 'The :attribute must be less than :value',
"has_counter" => 'The value must contain {$counter}',
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

View File

@ -262,7 +262,7 @@ return array(
'email_salutation' => 'Dear :name,', 'email_salutation' => 'Dear :name,',
'email_signature' => 'Regards,', 'email_signature' => 'Regards,',
'email_from' => 'The Invoice Ninja Team', 'email_from' => 'The Invoice Ninja Team',
'user_email_footer' => 'To adjust your email notification settings please visit '.SITE_URL.'/company/notifications', 'user_email_footer' => 'To adjust your email notification settings please visit '.SITE_URL.'/settings/notifications',
'invoice_link_message' => 'To view your client invoice click the link below:', 'invoice_link_message' => 'To view your client invoice click the link below:',
'notification_invoice_paid_subject' => 'Invoice :invoice was paid by :client', 'notification_invoice_paid_subject' => 'Invoice :invoice was paid by :client',
'notification_invoice_sent_subject' => 'Invoice :invoice was sent to :client', 'notification_invoice_sent_subject' => 'Invoice :invoice was sent to :client',
@ -337,7 +337,7 @@ return array(
'fill_products_help' => 'Selecting a product will automatically <b>fill in the description and cost</b>', 'fill_products_help' => 'Selecting a product will automatically <b>fill in the description and cost</b>',
'update_products' => 'Auto-update products', 'update_products' => 'Auto-update products',
'update_products_help' => 'Updating an invoice will automatically <b>update the product library</b>', 'update_products_help' => 'Updating an invoice will automatically <b>update the product library</b>',
'create_product' => 'Create Product', 'create_product' => 'Add Product',
'edit_product' => 'Edit Product', 'edit_product' => 'Edit Product',
'archive_product' => 'Archive Product', 'archive_product' => 'Archive Product',
'updated_product' => 'Successfully updated product', 'updated_product' => 'Successfully updated product',
@ -747,7 +747,7 @@ return array(
'current_user' => 'Current User', 'current_user' => 'Current User',
'new_recurring_invoice' => 'New Recurring Invoice', 'new_recurring_invoice' => 'New Recurring Invoice',
'recurring_invoice' => 'Recurring Invoice', 'recurring_invoice' => 'Recurring Invoice',
'recurring_too_soon' => 'It\'s too soon to create the next recurring invoice', 'recurring_too_soon' => 'It\'s too soon to create the next recurring invoice, it\'s scheduled for :date',
'created_by_invoice' => 'Created by :invoice', 'created_by_invoice' => 'Created by :invoice',
'primary_user' => 'Primary User', 'primary_user' => 'Primary User',
'help' => 'Help', 'help' => 'Help',
@ -819,6 +819,15 @@ return array(
'custom_invoice_link' => 'Custom Invoice Link', 'custom_invoice_link' => 'Custom Invoice Link',
'total_invoiced' => 'Total Invoiced', 'total_invoiced' => 'Total Invoiced',
'open_balance' => 'Open Balance', 'open_balance' => 'Open Balance',
'verify_email' => 'Please visit the link in the account confirmation email to verify your email address.',
'basic_settings' => 'Basic Settings',
'pro' => 'Pro',
'gateways' => 'Payment Gateways',
'next_send_on' => 'Send Next: :date',
'no_longer_running' => 'This invoice is not scheduled to run',
'general_settings' => 'General Settings',
'customize' => 'Customize',
); );

View File

@ -262,7 +262,7 @@ return array(
'email_salutation' => 'Kj&#230;re :name,', 'email_salutation' => 'Kj&#230;re :name,',
'email_signature' => 'Med vennlig hilsen,', 'email_signature' => 'Med vennlig hilsen,',
'email_from' => 'The Invoice Ninja Team', 'email_from' => 'The Invoice Ninja Team',
'user_email_footer' => 'For &#229; justere varslingsinnstillingene vennligst bes&#248;k '.SITE_URL.'/company/notifications', 'user_email_footer' => 'For &#229; justere varslingsinnstillingene vennligst bes&#248;k '.SITE_URL.'/settings/notifications',
'invoice_link_message' => 'Hvis du vil se din klientfaktura klikk p&#229; linken under:', 'invoice_link_message' => 'Hvis du vil se din klientfaktura klikk p&#229; linken under:',
'notification_invoice_paid_subject' => 'Faktura :invoice betalt av :client', 'notification_invoice_paid_subject' => 'Faktura :invoice betalt av :client',
'notification_invoice_sent_subject' => 'Faktura :invoice sendt til :client', 'notification_invoice_sent_subject' => 'Faktura :invoice sendt til :client',
@ -745,7 +745,7 @@ return array(
'current_user' => 'Current User', 'current_user' => 'Current User',
'new_recurring_invoice' => 'New Recurring Invoice', 'new_recurring_invoice' => 'New Recurring Invoice',
'recurring_invoice' => 'Recurring Invoice', 'recurring_invoice' => 'Recurring Invoice',
'recurring_too_soon' => 'It\'s too soon to create the next recurring invoice', 'recurring_too_soon' => 'It\'s too soon to create the next recurring invoice, it\'s scheduled for :date',
'created_by_invoice' => 'Created by :invoice', 'created_by_invoice' => 'Created by :invoice',
'primary_user' => 'Primary User', 'primary_user' => 'Primary User',
'help' => 'Help', 'help' => 'Help',
@ -817,5 +817,14 @@ return array(
'custom_invoice_link' => 'Custom Invoice Link', 'custom_invoice_link' => 'Custom Invoice Link',
'total_invoiced' => 'Total Invoiced', 'total_invoiced' => 'Total Invoiced',
'open_balance' => 'Open Balance', 'open_balance' => 'Open Balance',
'verify_email' => 'Please visit the link in the account confirmation email to verify your email address.',
'basic_settings' => 'Basic Settings',
'pro' => 'Pro',
'gateways' => 'Payment Gateways',
'next_send_on' => 'Send Next: :date',
'no_longer_running' => 'This invoice is not scheduled to run',
'general_settings' => 'General Settings',
'customize' => 'Customize',
); );

View File

@ -73,6 +73,7 @@ return array(
"has_credit" => "Klienten har ikke h&#248;y nok kreditt.", "has_credit" => "Klienten har ikke h&#248;y nok kreditt.",
"notmasked" => "Verdiene er skjult", "notmasked" => "Verdiene er skjult",
"less_than" => 'The :attribute must be less than :value', "less_than" => 'The :attribute must be less than :value',
"has_counter" => 'The value must contain {$counter}',
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

View File

@ -260,7 +260,7 @@ return array(
'email_salutation' => 'Beste :name,', 'email_salutation' => 'Beste :name,',
'email_signature' => 'Met vriendelijke groeten,', 'email_signature' => 'Met vriendelijke groeten,',
'email_from' => 'Het InvoiceNinja Team', 'email_from' => 'Het InvoiceNinja Team',
'user_email_footer' => 'Ga alstublieft naar '.SITE_URL.'/company/notifications om je e-mail notificatie instellingen aan te passen ', 'user_email_footer' => 'Ga alstublieft naar '.SITE_URL.'/settings/notifications om je e-mail notificatie instellingen aan te passen ',
'invoice_link_message' => 'Klik op volgende link om de Factuur van je klant te bekijken:', 'invoice_link_message' => 'Klik op volgende link om de Factuur van je klant te bekijken:',
'notification_invoice_paid_subject' => 'Factuur :invoice is betaald door :client', 'notification_invoice_paid_subject' => 'Factuur :invoice is betaald door :client',
'notification_invoice_sent_subject' => 'Factuur :invoice is gezonden door :client', 'notification_invoice_sent_subject' => 'Factuur :invoice is gezonden door :client',
@ -740,7 +740,6 @@ return array(
'current_user' => 'Huidige gebruiker', 'current_user' => 'Huidige gebruiker',
'new_recurring_invoice' => 'Nieuwe wederkerende factuur', 'new_recurring_invoice' => 'Nieuwe wederkerende factuur',
'recurring_invoice' => 'Wederkerende factuur', 'recurring_invoice' => 'Wederkerende factuur',
'recurring_too_soon' => 'Het is te vroeg om de volgende wederkerende factuur aan te maken',
'created_by_invoice' => 'Aangemaakt door :invoice', 'created_by_invoice' => 'Aangemaakt door :invoice',
'primary_user' => 'Primaire gebruiker', 'primary_user' => 'Primaire gebruiker',
'help' => 'Help', 'help' => 'Help',
@ -812,5 +811,15 @@ return array(
'custom_invoice_link' => 'Custom Invoice Link', 'custom_invoice_link' => 'Custom Invoice Link',
'total_invoiced' => 'Total Invoiced', 'total_invoiced' => 'Total Invoiced',
'open_balance' => 'Open Balance', 'open_balance' => 'Open Balance',
'verify_email' => 'Please visit the link in the account confirmation email to verify your email address.',
'basic_settings' => 'Basic Settings',
'pro' => 'Pro',
'gateways' => 'Payment Gateways',
'recurring_too_soon' => 'Het is te vroeg om de volgende wederkerende factuur aan te maken, it\'s scheduled for :date',
'next_send_on' => 'Send Next: :date',
'no_longer_running' => 'This invoice is not scheduled to run',
'general_settings' => 'General Settings',
'customize' => 'Customize',
); );

View File

@ -75,6 +75,7 @@ return array(
"has_credit" => "De klant heeft niet voldoende krediet.", "has_credit" => "De klant heeft niet voldoende krediet.",
"notmasked" => "De waarden zijn verborgen", "notmasked" => "De waarden zijn verborgen",
"less_than" => 'Het :attribute moet minder zijn dan :value', "less_than" => 'Het :attribute moet minder zijn dan :value',
"has_counter" => 'The value must contain {$counter}',
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

View File

@ -15,10 +15,10 @@ return array(
"password" => "Senhas devem possuir no mínimo seis caracteres e devem ser iguais.", "password" => "Senhas devem possuir no mínimo seis caracteres e devem ser iguais.",
"user" => "Não achamos um usuário com o endereço de e-mail informado.", "user" => "Não foi encontrado um usuário com o endereço de e-mail informado.",
"token" => "Este token de redefinição de senha é inválido.", "token" => "Este token de redefinição de senha é inválido.",
"sent" => "Lmebrete de senha enviado!", "sent" => "Lembrete de senha enviado!",
); );

File diff suppressed because it is too large Load Diff

View File

@ -17,17 +17,17 @@ return array(
"active_url" => ":attribute não é uma URL válida.", "active_url" => ":attribute não é uma URL válida.",
"after" => ":attribute deve ser uma data maior que :date.", "after" => ":attribute deve ser uma data maior que :date.",
"alpha" => ":attribute deve conter apenas letras.", "alpha" => ":attribute deve conter apenas letras.",
"alpha_dash" => ":attribute pode conter apenas letras, número e traços", "alpha_dash" => ":attribute pode conter apenas letras, número e hífens",
"alpha_num" => ":attribute pode conter apenas letras e números.", "alpha_num" => ":attribute pode conter apenas letras e números.",
"array" => ":attribute deve ser um array.", "array" => ":attribute deve ser uma lista.",
"before" => ":attribute deve ser uma data anterior a :date.", "before" => ":attribute deve ser uma data anterior a :date.",
"between" => array( "between" => array(
"numeric" => ":attribute deve ser entre :min - :max.", "numeric" => ":attribute deve estar entre :min - :max.",
"file" => ":attribute deve ser entre :min - :max kilobytes.", "file" => ":attribute deve estar entre :min - :max kilobytes.",
"string" => ":attribute deve ser entre :min - :max caracteres.", "string" => ":attribute deve estar entre :min - :max caracteres.",
"array" => ":attribute deve conter entre :min - :max itens.", "array" => ":attribute deve conter entre :min - :max itens.",
), ),
"confirmed" => ":attribute confirmação não correponde.", "confirmed" => ":attribute confirmação não corresponde.",
"date" => ":attribute não é uma data válida.", "date" => ":attribute não é uma data válida.",
"date_format" => ":attribute não satisfaz o formato :format.", "date_format" => ":attribute não satisfaz o formato :format.",
"different" => ":attribute e :other devem ser diferentes.", "different" => ":attribute e :other devem ser diferentes.",
@ -71,8 +71,9 @@ return array(
"positive" => ":attribute deve ser maior que zero.", "positive" => ":attribute deve ser maior que zero.",
"has_credit" => "O cliente não possui crédito suficiente.", "has_credit" => "O cliente não possui crédito suficiente.",
"notmasked" => "The values are masked", "notmasked" => "Os valores são mascarados",
"less_than" => 'The :attribute must be less than :value', "less_than" => ':attribute deve ser menor que :value',
"has_counter" => 'O valor deve conter {$counter}',
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

View File

@ -262,7 +262,7 @@ return array(
'email_salutation' => 'Hej :name,', 'email_salutation' => 'Hej :name,',
'email_signature' => 'Vänliga hälsningar,', 'email_signature' => 'Vänliga hälsningar,',
'email_from' => 'Invoice Ninja teamet', 'email_from' => 'Invoice Ninja teamet',
'user_email_footer' => 'För att anpassa dina e-post notifieringar gå till '.SITE_URL.'/company/notifications', 'user_email_footer' => 'För att anpassa dina e-post notifieringar gå till '.SITE_URL.'/settings/notifications',
'invoice_link_message' => 'För att se din kundfaktura klicka på länken nedan:', 'invoice_link_message' => 'För att se din kundfaktura klicka på länken nedan:',
'notification_invoice_paid_subject' => 'Faktura :invoice är betald av :client', 'notification_invoice_paid_subject' => 'Faktura :invoice är betald av :client',
'notification_invoice_sent_subject' => 'Faktura :invoice är skickad till :client', 'notification_invoice_sent_subject' => 'Faktura :invoice är skickad till :client',
@ -743,7 +743,7 @@ return array(
'current_user' => 'Current User', 'current_user' => 'Current User',
'new_recurring_invoice' => 'New Recurring Invoice', 'new_recurring_invoice' => 'New Recurring Invoice',
'recurring_invoice' => 'Recurring Invoice', 'recurring_invoice' => 'Recurring Invoice',
'recurring_too_soon' => 'It\'s too soon to create the next recurring invoice', 'recurring_too_soon' => 'It\'s too soon to create the next recurring invoice, it\'s scheduled for :date',
'created_by_invoice' => 'Created by :invoice', 'created_by_invoice' => 'Created by :invoice',
'primary_user' => 'Primary User', 'primary_user' => 'Primary User',
'help' => 'Help', 'help' => 'Help',
@ -815,5 +815,14 @@ return array(
'custom_invoice_link' => 'Custom Invoice Link', 'custom_invoice_link' => 'Custom Invoice Link',
'total_invoiced' => 'Total Invoiced', 'total_invoiced' => 'Total Invoiced',
'open_balance' => 'Open Balance', 'open_balance' => 'Open Balance',
'verify_email' => 'Please visit the link in the account confirmation email to verify your email address.',
'basic_settings' => 'Basic Settings',
'pro' => 'Pro',
'gateways' => 'Payment Gateways',
'next_send_on' => 'Send Next: :date',
'no_longer_running' => 'This invoice is not scheduled to run',
'general_settings' => 'General Settings',
'customize' => 'Customize',
); );

View File

@ -77,6 +77,7 @@ return [
"has_credit" => "The client does not have enough credit.", "has_credit" => "The client does not have enough credit.",
"notmasked" => "The values are masked", "notmasked" => "The values are masked",
"less_than" => 'The :attribute must be less than :value', "less_than" => 'The :attribute must be less than :value',
"has_counter" => 'The value must contain {$counter}',
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

View File

@ -1,9 +1,11 @@
@extends('accounts.nav') @extends('header')
@section('content') @section('content')
@parent @parent
{!! Former::open($url)->method($method)->rule()->addClass('col-md-8 col-md-offset-2 warn-on-exit') !!} @include('accounts.nav', ['selected' => ACCOUNT_PAYMENTS])
{!! Former::open($url)->method($method)->rule()->addClass('warn-on-exit') !!}
{!! Former::populate($account) !!} {!! Former::populate($account) !!}
@ -11,7 +13,7 @@
<div class="panel-heading"> <div class="panel-heading">
<h3 class="panel-title">{!! trans($title) !!}</h3> <h3 class="panel-title">{!! trans($title) !!}</h3>
</div> </div>
<div class="panel-body"> <div class="panel-body form-padding-right">
@if ($accountGateway) @if ($accountGateway)
{!! Former::populateField('gateway_id', $accountGateway->gateway_id) !!} {!! Former::populateField('gateway_id', $accountGateway->gateway_id) !!}
@ -106,7 +108,7 @@
<p/>&nbsp;<p/> <p/>&nbsp;<p/>
{!! Former::actions( {!! Former::actions(
$countGateways > 0 ? Button::normal(trans('texts.cancel'))->large()->asLinkTo(URL::to('/company/payments'))->appendIcon(Icon::create('remove-circle')) : false, $countGateways > 0 ? Button::normal(trans('texts.cancel'))->large()->asLinkTo(URL::to('/settings/online_payments'))->appendIcon(Icon::create('remove-circle')) : false,
Button::success(trans('texts.save'))->submit()->large()->appendIcon(Icon::create('floppy-disk'))) !!} Button::success(trans('texts.save'))->submit()->large()->appendIcon(Icon::create('floppy-disk'))) !!}
{!! Former::close() !!} {!! Former::close() !!}

View File

@ -1,8 +1,8 @@
@extends('accounts.nav') @extends('header')
@section('content') @section('content')
@parent @parent
@include('accounts.nav_advanced') @include('accounts.nav', ['selected' => ACCOUNT_API_TOKENS, 'advanced' => true])
{!! Former::open('tokens/delete')->addClass('user-form') !!} {!! Former::open('tokens/delete')->addClass('user-form') !!}

View File

@ -1,16 +1,13 @@
@extends('accounts.nav') @extends('header')
@section('head') @section('head')
@parent @parent
<script src="{{ asset('js/pdf_viewer.js') }}" type="text/javascript"></script>
<script src="{{ asset('js/compatibility.js') }}" type="text/javascript"></script>
<link href="{{ asset('css/jsoneditor.min.css') }}" rel="stylesheet" type="text/css"> <link href="{{ asset('css/jsoneditor.min.css') }}" rel="stylesheet" type="text/css">
<script src="{{ asset('js/jsoneditor.min.js') }}" type="text/javascript"></script> <script src="{{ asset('js/jsoneditor.min.js') }}" type="text/javascript"></script>
<script src="{{ asset('js/pdfmake.min.js') }}" type="text/javascript"></script>
<script src="{{ asset('js/vfs_fonts.js') }}" type="text/javascript"></script> <script src="{{ asset('js/pdf.built.js') }}" type="text/javascript"></script>
<style type="text/css"> <style type="text/css">
@ -28,9 +25,6 @@
@section('content') @section('content')
@parent @parent
@include('accounts.nav_advanced')
<script> <script>
var invoiceDesigns = {!! $invoiceDesigns !!}; var invoiceDesigns = {!! $invoiceDesigns !!};
@ -155,17 +149,15 @@
{!! Former::select('invoice_design_id')->style('display:inline;width:120px')->fromQuery($invoiceDesigns, 'name', 'id')->onchange('onSelectChange()')->raw() !!} {!! Former::select('invoice_design_id')->style('display:inline;width:120px')->fromQuery($invoiceDesigns, 'name', 'id')->onchange('onSelectChange()')->raw() !!}
<div class="pull-right"> <div class="pull-right">
{!! Button::normal(trans('texts.help'))->withAttributes(['onclick' => 'showHelp()'])->appendIcon(Icon::create('question-sign')) !!} {!! Button::normal(trans('texts.help'))->withAttributes(['onclick' => 'showHelp()'])->appendIcon(Icon::create('question-sign')) !!}
{!! Button::normal(trans('texts.cancel'))->asLinkTo(URL::to('/company/advanced_settings/invoice_design'))->appendIcon(Icon::create('remove-circle')) !!} {!! Button::normal(trans('texts.cancel'))->asLinkTo(URL::to('/settings/invoice_design'))->appendIcon(Icon::create('remove-circle')) !!}
@if (Auth::user()->isPro()) {!! Button::success(trans('texts.save'))->withAttributes(['onclick' => 'submitForm()'])->appendIcon(Icon::create('floppy-disk'))->withAttributes(['class' => 'save-button']) !!}
{!! Button::success(trans('texts.save'))->withAttributes(['onclick' => 'submitForm()'])->appendIcon(Icon::create('floppy-disk')) !!}
@endif
</div> </div>
</div> </div>
<script> <script>
@if (!Auth::user()->isPro()) @if (!Auth::user()->isPro())
$(function() { $(function() {
$('form.warn-on-exit input').prop('disabled', true); $('form.warn-on-exit input, .save-button').prop('disabled', true);
}); });
@endif @endif

View File

@ -1,4 +1,4 @@
@extends('accounts.nav') @extends('header')
@section('content') @section('content')
@parent @parent
@ -13,27 +13,20 @@
{!! Former::open_for_files()->addClass('warn-on-exit')->rules(array( {!! Former::open_for_files()->addClass('warn-on-exit')->rules(array(
'name' => 'required', 'name' => 'required',
'email' => 'email|required'
)) !!} )) !!}
{{ Former::populate($account) }} {{ Former::populate($account) }}
{{ Former::populateField('military_time', intval($account->military_time)) }}
{{ Former::populateField('first_name', $user->first_name) }} @include('accounts.nav', ['selected' => ACCOUNT_COMPANY_DETAILS])
{{ Former::populateField('last_name', $user->last_name) }}
{{ Former::populateField('email', $user->email) }}
{{ Former::populateField('phone', $user->phone) }}
@if (Utils::isNinjaDev())
{{ Former::populateField('dark_mode', intval($user->dark_mode)) }}
@endif
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-12">
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<h3 class="panel-title">{!! trans('texts.details') !!}</h3> <h3 class="panel-title">{!! trans('texts.details') !!}</h3>
</div> </div>
<div class="panel-body"> <div class="panel-body form-padding-right">
{!! Former::text('name') !!} {!! Former::text('name') !!}
{!! Former::text('id_number') !!} {!! Former::text('id_number') !!}
@ -58,7 +51,7 @@
<div class="panel-heading"> <div class="panel-heading">
<h3 class="panel-title">{!! trans('texts.address') !!}</h3> <h3 class="panel-title">{!! trans('texts.address') !!}</h3>
</div> </div>
<div class="panel-body"> <div class="panel-body form-padding-right">
{!! Former::text('address1') !!} {!! Former::text('address1') !!}
{!! Former::text('address2') !!} {!! Former::text('address2') !!}
@ -72,133 +65,13 @@
</div> </div>
</div> </div>
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{!! trans('texts.user_details') !!}</h3>
</div>
<div class="panel-body">
{!! Former::text('first_name') !!}
{!! Former::text('last_name') !!}
{!! Former::text('email') !!}
{!! Former::text('phone') !!}
@if (Utils::isNinja())
{!! Former::plaintext('oneclick_login')->value(
$user->oauth_provider_id ?
$oauthProviderName . ' - ' . link_to('#', trans('texts.disable'), ['onclick' => 'disableSocialLogin()']) :
DropdownButton::primary(trans('texts.enable'))->withContents($oauthLoginUrls)->small()
) !!}
@endif
@if (Utils::isNinja() && $user->confirmed)
@if ($user->referral_code)
{!! Former::plaintext('referral_code')
->value($user->referral_code . ' <a href="'.REFERRAL_PROGRAM_URL.'" target="_blank" title="'.trans('texts.learn_more').'">' . Icon::create('question-sign') . '</a>') !!}
@else
{!! Former::checkbox('referral_code')
->text(trans('texts.enable') . ' <a href="'.REFERRAL_PROGRAM_URL.'" target="_blank" title="'.trans('texts.learn_more').'">' . Icon::create('question-sign') . '</a>') !!}
@endif
@endif
@if (false && Utils::isNinjaDev())
{!! Former::checkbox('dark_mode')->text(trans('texts.dark_mode_help')) !!}
@endif
@if (Utils::isNinja())
<br/>
@if (Auth::user()->confirmed)
{!! Former::actions( Button::primary(trans('texts.change_password'))->small()->withAttributes(['onclick'=>'showChangePassword()'])) !!}
@elseif (Auth::user()->registered)
{!! Former::actions( Button::primary(trans('texts.resend_confirmation'))->asLinkTo(URL::to('/resend_confirmation'))->small() ) !!}
@endif
@endif
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{!! trans('texts.localization') !!}</h3>
</div>
<div class="panel-body">
{!! Former::select('currency_id')->addOption('','')
->fromQuery($currencies, 'name', 'id') !!}
{!! Former::select('language_id')->addOption('','')
->fromQuery($languages, 'name', 'id') !!}
{!! Former::select('timezone_id')->addOption('','')
->fromQuery($timezones, 'location', 'id') !!}
{!! Former::select('date_format_id')->addOption('','')
->fromQuery($dateFormats, 'label', 'id') !!}
{!! Former::select('datetime_format_id')->addOption('','')
->fromQuery($datetimeFormats, 'label', 'id') !!}
{!! Former::checkbox('military_time')->text(trans('texts.enable')) !!}
</div>
</div>
</div>
</div> </div>
<center> <center>
{!! Button::success(trans('texts.save'))->submit()->large()->appendIcon(Icon::create('floppy-disk')) !!} {!! Button::success(trans('texts.save'))->submit()->large()->appendIcon(Icon::create('floppy-disk')) !!}
</center> </center>
<div class="modal fade" id="passwordModal" tabindex="-1" role="dialog" aria-labelledby="passwordModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" id="passwordModalLabel">{{ trans('texts.change_password') }}</h4>
</div>
<div style="background-color: #fff" id="changePasswordDiv" onkeyup="validateChangePassword()" onclick="validateChangePassword()" onkeydown="checkForEnter(event)">
&nbsp;
{!! Former::password('current_password')->style('width:300px') !!}
{!! Former::password('newer_password')->style('width:300px')->label(trans('texts.new_password')) !!}
{!! Former::password('confirm_password')->style('width:300px') !!}
&nbsp;
<br/>
<center>
<div id="changePasswordError"></div>
</center>
<br/>
</div>
<div style="padding-left:40px;padding-right:40px;display:none;min-height:130px" id="working">
<h3>{{ trans('texts.working') }}...</h3>
<div class="progress progress-striped active">
<div class="progress-bar" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 100%"></div>
</div>
</div>
<div style="background-color: #fff; padding-right:20px;padding-left:20px; display:none" id="successDiv">
<br/>
<h3>{{ trans('texts.success') }}</h3>
{{ trans('texts.updated_password') }}
<br/>
&nbsp;
<br/>
</div>
<div class="modal-footer" style="margin-top: 0px" id="changePasswordFooter">
<button type="button" class="btn btn-default" id="cancelChangePasswordButton" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-success" onclick="submitChangePassword()" id="changePasswordButton" disabled>
{{ trans('texts.save') }}
<i class="glyphicon glyphicon-floppy-disk"></i>
</button>
</div>
</div>
</div>
</div>
{!! Former::close() !!} {!! Former::close() !!}
{!! Form::open(['url' => 'remove_logo', 'class' => 'removeLogoForm']) !!} {!! Form::open(['url' => 'remove_logo', 'class' => 'removeLogoForm']) !!}
@ -209,21 +82,6 @@
$(function() { $(function() {
$('#country_id').combobox(); $('#country_id').combobox();
$('#passwordModal').on('hidden.bs.modal', function () {
$(['current_password', 'newer_password', 'confirm_password']).each(function(i, field) {
var $input = $('form #'+field);
$input.val('');
$input.closest('div.form-group').removeClass('has-success');
});
$('#changePasswordButton').prop('disabled', true);
})
$('#passwordModal').on('shown.bs.modal', function () {
$('#current_password').focus();
})
localStorage.setItem('auth_provider', '{{ strtolower($oauthProviderName) }}');
}); });
function deleteLogo() { function deleteLogo() {
@ -232,84 +90,6 @@
} }
} }
function showChangePassword() {
$('#passwordModal').modal('show');
}
function checkForEnter(event)
{
if (event.keyCode === 13){
event.preventDefault();
return false;
}
}
function validateChangePassword(showError)
{
var isFormValid = true;
$(['current_password', 'newer_password', 'confirm_password']).each(function(i, field) {
var $input = $('form #'+field),
val = $.trim($input.val());
var isValid = val && val.length >= 6;
if (isValid && field == 'confirm_password') {
isValid = val == $.trim($('#newer_password').val());
}
if (isValid) {
$input.closest('div.form-group').removeClass('has-error').addClass('has-success');
} else {
isFormValid = false;
$input.closest('div.form-group').removeClass('has-success');
if (showError) {
$input.closest('div.form-group').addClass('has-error');
}
}
});
$('#changePasswordButton').prop('disabled', !isFormValid);
return isFormValid;
}
function submitChangePassword()
{
if (!validateChangePassword(true)) {
return;
}
$('#changePasswordDiv, #changePasswordFooter').hide();
$('#working').show();
$.ajax({
type: 'POST',
url: '{{ URL::to('/users/change_password') }}',
data: 'current_password=' + encodeURIComponent($('form #current_password').val()) +
'&new_password=' + encodeURIComponent($('form #newer_password').val()) +
'&confirm_password=' + encodeURIComponent($('form #confirm_password').val()),
success: function(result) {
if (result == 'success') {
NINJA.formIsChanged = false;
$('#changePasswordButton').hide();
$('#successDiv').show();
$('#cancelChangePasswordButton').html('{{ trans('texts.close') }}');
} else {
$('#changePasswordError').html(result);
$('#changePasswordDiv').show();
}
$('#changePasswordFooter').show();
$('#working').hide();
}
});
}
function disableSocialLogin() {
if (!confirm("{!! trans('texts.are_you_sure') !!}")) {
return;
}
window.location = '{{ URL::to('/auth_unlink') }}';
}
</script> </script>
@stop @stop

View File

@ -1,7 +1,8 @@
@extends('accounts.nav') @extends('header')
@section('content') @section('content')
@parent @parent
@include('accounts.nav', ['selected' => ACCOUNT_IMPORT_EXPORT])
{{ Former::open()->addClass('col-md-9 col-md-offset-1') }} {{ Former::open()->addClass('col-md-9 col-md-offset-1') }}
{{ Former::legend('Export Client Data') }} {{ Former::legend('Export Client Data') }}

View File

@ -1,9 +1,11 @@
@extends('accounts.nav') @extends('header')
@section('content') @section('content')
@parent @parent
{!! Former::open_for_files('company/import_map')->addClass('col-md-8 col-md-offset-2') !!} @include('accounts.nav', ['selected' => ACCOUNT_IMPORT_EXPORT])
{!! Former::open_for_files('settings/' . ACCOUNT_MAP) !!}
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<h3 class="panel-title">{!! trans('texts.import_clients') !!}</h3> <h3 class="panel-title">{!! trans('texts.import_clients') !!}</h3>
@ -16,7 +18,7 @@
{!! Former::close() !!} {!! Former::close() !!}
{!! Former::open('company/export')->addClass('col-md-8 col-md-offset-2') !!} {!! Former::open('settings/' . ACCOUNT_EXPORT) !!}
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<h3 class="panel-title">{!! trans('texts.export_clients') !!}</h3> <h3 class="panel-title">{!! trans('texts.export_clients') !!}</h3>
@ -28,7 +30,7 @@
{!! Former::close() !!} {!! Former::close() !!}
{!! Former::open('company/cancel_account')->addClass('col-md-8 col-md-offset-2 cancel-account') !!} {!! Former::open('settings/cancel_account')->addClass('cancel-account') !!}
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<h3 class="panel-title">{!! trans('texts.cancel_account') !!}</h3> <h3 class="panel-title">{!! trans('texts.cancel_account') !!}</h3>

View File

@ -1,9 +1,11 @@
@extends('accounts.nav') @extends('header')
@section('content') @section('content')
@parent @parent
{!! Former::open('company/import_export')->addClass('col-md-8 col-md-offset-2 warn-on-exit') !!} @include('accounts.nav', ['selected' => ACCOUNT_IMPORT_EXPORT])
{!! Former::open('settings/' . ACCOUNT_IMPORT_EXPORT)->addClass('warn-on-exit') !!}
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
@ -46,7 +48,7 @@
{!! Former::actions( {!! Former::actions(
Button::normal(trans('texts.cancel'))->large()->asLinkTo(URL::to('/company/import_export'))->appendIcon(Icon::create('remove-circle')), Button::normal(trans('texts.cancel'))->large()->asLinkTo(URL::to('/settings/import_export'))->appendIcon(Icon::create('remove-circle')),
Button::success(trans('texts.import'))->submit()->large()->appendIcon(Icon::create('floppy-disk'))) !!} Button::success(trans('texts.import'))->submit()->large()->appendIcon(Icon::create('floppy-disk'))) !!}
{!! Former::close() !!} {!! Former::close() !!}

View File

@ -1,18 +1,15 @@
@extends('accounts.nav') @extends('header')
@section('head') @section('head')
@parent @parent
<script src="{{ asset('js/pdf_viewer.js') }}" type="text/javascript"></script> <script src="{{ asset('js/pdf.built.js') }}" type="text/javascript"></script>
<script src="{{ asset('js/compatibility.js') }}" type="text/javascript"></script>
<script src="{{ asset('js/pdfmake.min.js') }}" type="text/javascript"></script>
<script src="{{ asset('js/vfs_fonts.js') }}" type="text/javascript"></script>
@stop @stop
@section('content') @section('content')
@parent @parent
@include('accounts.nav_advanced') @include('accounts.nav', ['selected' => ACCOUNT_INVOICE_DESIGN, 'advanced' => true])
<script> <script>
var invoiceDesigns = {!! $invoiceDesigns !!}; var invoiceDesigns = {!! $invoiceDesigns !!};
@ -40,7 +37,7 @@
NINJA.secondaryColor = $('#secondary_color').val(); NINJA.secondaryColor = $('#secondary_color').val();
NINJA.fontSize = parseInt($('#font_size').val()); NINJA.fontSize = parseInt($('#font_size').val());
var fields = ['item', 'description', 'unit_cost', 'quantity']; var fields = ['item', 'description', 'unit_cost', 'quantity', 'line_total'];
invoiceLabels.old = {}; invoiceLabels.old = {};
for (var i=0; i<fields.length; i++) { for (var i=0; i<fields.length; i++) {
var field = fields[i]; var field = fields[i];
@ -76,7 +73,7 @@
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-12">
{!! Former::open()->addClass('warn-on-exit')->onchange('refreshPDF()') !!} {!! Former::open()->addClass('warn-on-exit')->onchange('refreshPDF()') !!}
{!! Former::populate($account) !!} {!! Former::populate($account) !!}
@ -91,58 +88,74 @@
<div class="panel-heading"> <div class="panel-heading">
<h3 class="panel-title">{!! trans('texts.invoice_design') !!}</h3> <h3 class="panel-title">{!! trans('texts.invoice_design') !!}</h3>
</div> </div>
<div class="panel-body form-padding-right">
<div role="tabpanel">
<ul class="nav nav-tabs" role="tablist" style="border: none">
<li role="presentation" class="active"><a href="#generalSettings" aria-controls="generalSettings" role="tab" data-toggle="tab">{{ trans('texts.general_settings') }}</a></li>
<li role="presentation"><a href="#invoiceLabels" aria-controls="invoiceLabels" role="tab" data-toggle="tab">{{ trans('texts.invoice_labels') }}</a></li>
<li role="presentation"><a href="#invoiceOptions" aria-controls="invoiceOptions" role="tab" data-toggle="tab">{{ trans('texts.invoice_options') }}</a></li>
</ul>
</div>
<div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="generalSettings">
<div class="panel-body"> <div class="panel-body">
@if (!Utils::isPro() || \App\Models\InvoiceDesign::count() == COUNT_FREE_DESIGNS_SELF_HOST) @if (!Utils::isPro() || \App\Models\InvoiceDesign::count() == COUNT_FREE_DESIGNS_SELF_HOST)
{!! Former::select('invoice_design_id')->style('display:inline;width:120px')->fromQuery($invoiceDesigns, 'name', 'id')->addOption(trans('texts.more_designs') . '...', '-1') !!} {!! Former::select('invoice_design_id')->style('display:inline')->fromQuery($invoiceDesigns, 'name', 'id')->addOption(trans('texts.more_designs') . '...', '-1') !!}
@else @else
{!! Former::select('invoice_design_id')->style('display:inline;width:120px')->fromQuery($invoiceDesigns, 'name', 'id') !!} {!! Former::select('invoice_design_id')->style('display:inline')->fromQuery($invoiceDesigns, 'name', 'id') !!}
@endif @endif
{!! Former::text('font_size')->type('number')->min('0')->step('1')->style('width:120px') !!} {!! Former::text('font_size')->type('number')->min('0')->step('1') !!}
{!! Former::text('primary_color') !!} {!! Former::text('primary_color') !!}
{!! Former::text('secondary_color') !!} {!! Former::text('secondary_color') !!}
{!! Former::actions(
Button::primary(trans('texts.customize_design'))->small()->asLinkTo(URL::to('/company/advanced_settings/customize_design'))
) !!}
</div> </div>
</div> </div>
<div role="tabpanel" class="tab-pane" id="invoiceLabels">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{!! trans('texts.invoice_labels') !!}</h3>
</div>
<div class="panel-body"> <div class="panel-body">
{!! Former::text('labels_item')->label(trans('texts.item')) !!} {!! Former::text('labels_item')->label(trans('texts.item')) !!}
{!! Former::text('labels_description')->label(trans('texts.description')) !!} {!! Former::text('labels_description')->label(trans('texts.description')) !!}
{!! Former::text('labels_unit_cost')->label(trans('texts.unit_cost')) !!} {!! Former::text('labels_unit_cost')->label(trans('texts.unit_cost')) !!}
{!! Former::text('labels_quantity')->label(trans('texts.quantity')) !!} {!! Former::text('labels_quantity')->label(trans('texts.quantity')) !!}
{!! Former::text('labels_line_total')->label(trans('texts.line_total')) !!}
</div> </div>
</div> </div>
<div role="tabpanel" class="tab-pane" id="invoiceOptions">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{!! trans('texts.invoice_options') !!}</h3>
</div>
<div class="panel-body"> <div class="panel-body">
{!! Former::checkbox('hide_quantity')->text(trans('texts.hide_quantity_help')) !!} {!! Former::checkbox('hide_quantity')->text(trans('texts.hide_quantity_help')) !!}
{!! Former::checkbox('hide_paid_to_date')->text(trans('texts.hide_paid_to_date_help')) !!} {!! Former::checkbox('hide_paid_to_date')->text(trans('texts.hide_paid_to_date_help')) !!}
</div>
</div>
</div>
</div> </div>
</div> </div>
@if (Auth::user()->isPro()) <br/>
{!! Former::actions( Button::success(trans('texts.save'))->submit()->large()->appendIcon(Icon::create('floppy-disk'))) !!} {!! Former::actions(
@else Button::primary(trans('texts.customize'))
->appendIcon(Icon::create('edit'))
->asLinkTo(URL::to('/settings/customize_design'))
->large(),
Button::success(trans('texts.save'))
->submit()->large()
->appendIcon(Icon::create('floppy-disk'))
->withAttributes(['class' => 'save-button'])
) !!}
<br/>
@if (!Auth::user()->isPro())
<script> <script>
$(function() { $(function() {
$('form.warn-on-exit input').prop('disabled', true); $('form.warn-on-exit input, .save-button').prop('disabled', true);
}); });
</script> </script>
@endif @endif
@ -150,11 +163,10 @@
{!! Former::close() !!} {!! Former::close() !!}
</div> </div>
<div class="col-md-6"> </div>
@include('invoices.pdf', ['account' => Auth::user()->account, 'pdfHeight' => 800]) @include('invoices.pdf', ['account' => Auth::user()->account, 'pdfHeight' => 800])
</div>
</div>
@stop @stop

View File

@ -1,4 +1,4 @@
@extends('accounts.nav') @extends('header')
@section('head') @section('head')
@parent @parent
@ -10,7 +10,7 @@
.input-group-addon div.checkbox { .input-group-addon div.checkbox {
display: inline; display: inline;
} }
span.input-group-addon { .tab-content .pad-checkbox span.input-group-addon {
padding-right: 30px; padding-right: 30px;
} }
</style> </style>
@ -18,9 +18,9 @@
@section('content') @section('content')
@parent @parent
@include('accounts.nav_advanced') @include('accounts.nav', ['selected' => ACCOUNT_INVOICE_SETTINGS, 'advanced' => true])
{!! Former::open()->addClass('col-md-8 col-md-offset-2 warn-on-exit') !!} {!! Former::open()->rules(['iframe_url' => 'url'])->addClass('warn-on-exit') !!}
{{ Former::populate($account) }} {{ Former::populate($account) }}
{{ Former::populateField('custom_invoice_taxes1', intval($account->custom_invoice_taxes1)) }} {{ Former::populateField('custom_invoice_taxes1', intval($account->custom_invoice_taxes1)) }}
{{ Former::populateField('custom_invoice_taxes2', intval($account->custom_invoice_taxes2)) }} {{ Former::populateField('custom_invoice_taxes2', intval($account->custom_invoice_taxes2)) }}
@ -32,9 +32,11 @@
<div class="panel-heading"> <div class="panel-heading">
<h3 class="panel-title">{!! trans('texts.email_settings') !!}</h3> <h3 class="panel-title">{!! trans('texts.email_settings') !!}</h3>
</div> </div>
<div class="panel-body"> <div class="panel-body form-padding-right">
{!! Former::checkbox('pdf_email_attachment')->text(trans('texts.enable')) !!} {!! Former::checkbox('pdf_email_attachment')->text(trans('texts.enable')) !!}
@if (Utils::isNinja())
{{-- Former::select('recurring_hour')->options($recurringHours) --}}
{!! Former::inline_radios('custom_invoice_link') {!! Former::inline_radios('custom_invoice_link')
->onchange('onCustomLinkChange()') ->onchange('onCustomLinkChange()')
->radios([ ->radios([
@ -42,17 +44,18 @@
trans('texts.website') => ['value' => 'website', 'name' => 'custom_link'], trans('texts.website') => ['value' => 'website', 'name' => 'custom_link'],
])->check($account->iframe_url ? 'website' : 'subdomain') !!} ])->check($account->iframe_url ? 'website' : 'subdomain') !!}
{{ Former::setOption('capitalize_translations', false) }} {{ Former::setOption('capitalize_translations', false) }}
{!! Former::text('subdomain') {!! Former::text('subdomain')
->placeholder(trans('texts.www')) ->placeholder(trans('texts.www'))
->onchange('onSubdomainChange()') ->onchange('onSubdomainChange()')
->addGroupClass('subdomain') ->addGroupClass('subdomain')
->label(' ') !!} ->label(' ') !!}
{!! Former::text('iframe_url') {!! Former::text('iframe_url')
->placeholder('http://www.example.com/invoice') ->placeholder('http://www.example.com/invoice')
->appendIcon('question-sign') ->appendIcon('question-sign')
->addGroupClass('iframe_url') ->addGroupClass('iframe_url')
->label(' ') !!} ->label(' ') !!}
@endif
</div> </div>
</div> </div>
@ -60,7 +63,8 @@
<div class="panel-heading"> <div class="panel-heading">
<h3 class="panel-title">{!! trans('texts.invoice_quote_number') !!}</h3> <h3 class="panel-title">{!! trans('texts.invoice_quote_number') !!}</h3>
</div> </div>
<div class="panel-body"> <div class="panel-body form-padding-right">
<div role="tabpanel"> <div role="tabpanel">
<ul class="nav nav-tabs" role="tablist" style="border: none"> <ul class="nav nav-tabs" role="tablist" style="border: none">
<li role="presentation" class="active"><a href="#invoiceNumber" aria-controls="invoiceNumber" role="tab" data-toggle="tab">{{ trans('texts.invoice_number') }}</a></li> <li role="presentation" class="active"><a href="#invoiceNumber" aria-controls="invoiceNumber" role="tab" data-toggle="tab">{{ trans('texts.invoice_number') }}</a></li>
@ -70,18 +74,56 @@
<div class="tab-content"> <div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="invoiceNumber"> <div role="tabpanel" class="tab-pane active" id="invoiceNumber">
<div class="panel-body"> <div class="panel-body">
{!! Former::text('invoice_number_prefix')->label(trans('texts.prefix')) !!} {!! Former::inline_radios('invoice_number_type')
{!! Former::text('invoice_number_counter')->label(trans('texts.counter')) !!} ->onchange('onInvoiceNumberTypeChange()')
->label(trans('texts.type'))
->radios([
trans('texts.prefix') => ['value' => 'prefix', 'name' => 'invoice_number_type'],
trans('texts.pattern') => ['value' => 'pattern', 'name' => 'invoice_number_type'],
])->check($account->invoice_number_pattern ? 'pattern' : 'prefix') !!}
{!! Former::text('invoice_number_prefix')
->addGroupClass('invoice-prefix')
->label(' ') !!}
{!! Former::text('invoice_number_pattern')
->appendIcon('question-sign')
->addGroupClass('invoice-pattern')
->label(' ')
->addGroupClass('number-pattern') !!}
{!! Former::text('invoice_number_counter')
->label(trans('texts.counter')) !!}
</div> </div>
</div> </div>
<div role="tabpanel" class="tab-pane" id="quoteNumber"> <div role="tabpanel" class="tab-pane" id="quoteNumber">
<div class="panel-body"> <div class="panel-body">
{!! Former::text('quote_number_prefix')->label(trans('texts.prefix')) !!} {!! Former::inline_radios('quote_number_type')
{!! Former::text('quote_number_counter')->label(trans('texts.counter')) ->onchange('onQuoteNumberTypeChange()')
->append(Former::checkbox('share_counter')->raw()->onclick('setQuoteNumberEnabled()') . ' ' . trans('texts.share_invoice_counter')) !!} ->label(trans('texts.type'))
->radios([
trans('texts.prefix') => ['value' => 'prefix', 'name' => 'quote_number_type'],
trans('texts.pattern') => ['value' => 'pattern', 'name' => 'quote_number_type'],
])->check($account->quote_number_pattern ? 'pattern' : 'prefix') !!}
{!! Former::text('quote_number_prefix')
->addGroupClass('quote-prefix')
->label(' ') !!}
{!! Former::text('quote_number_pattern')
->appendIcon('question-sign')
->addGroupClass('quote-pattern')
->addGroupClass('number-pattern')
->label(' ') !!}
{!! Former::text('quote_number_counter')
->label(trans('texts.counter'))
->addGroupClass('pad-checkbox')
->append(Former::checkbox('share_counter')->raw()
->onclick('setQuoteNumberEnabled()') . ' ' . trans('texts.share_invoice_counter')) !!}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
@ -90,7 +132,7 @@
<div class="panel-heading"> <div class="panel-heading">
<h3 class="panel-title">{!! trans('texts.custom_fields') !!}</h3> <h3 class="panel-title">{!! trans('texts.custom_fields') !!}</h3>
</div> </div>
<div class="panel-body"> <div class="panel-body form-padding-right">
<div role="tabpanel"> <div role="tabpanel">
<ul class="nav nav-tabs" role="tablist" style="border: none"> <ul class="nav nav-tabs" role="tablist" style="border: none">
@ -131,10 +173,16 @@
<div role="tabpanel" class="tab-pane" id="invoiceCharges"> <div role="tabpanel" class="tab-pane" id="invoiceCharges">
<div class="panel-body"> <div class="panel-body">
{!! Former::text('custom_invoice_label1')->label(trans('texts.field_label')) {!! Former::text('custom_invoice_label1')
->append(Former::checkbox('custom_invoice_taxes1')->raw() . trans('texts.charge_taxes')) !!} ->label(trans('texts.field_label'))
{!! Former::text('custom_invoice_label2')->label(trans('texts.field_label')) ->addGroupClass('pad-checkbox')
->append(Former::checkbox('custom_invoice_taxes2')->raw() . trans('texts.charge_taxes')) !!} ->append(Former::checkbox('custom_invoice_taxes1')
->raw() . trans('texts.charge_taxes')) !!}
{!! Former::text('custom_invoice_label2')
->label(trans('texts.field_label'))
->addGroupClass('pad-checkbox')
->append(Former::checkbox('custom_invoice_taxes2')
->raw() . trans('texts.charge_taxes')) !!}
</div> </div>
</div> </div>
@ -147,12 +195,6 @@
<center> <center>
{!! Button::success(trans('texts.save'))->large()->submit()->appendIcon(Icon::create('floppy-disk')) !!} {!! Button::success(trans('texts.save'))->large()->submit()->appendIcon(Icon::create('floppy-disk')) !!}
</center> </center>
@else
<script>
$(function() {
$('form.warn-on-exit input').prop('disabled', true);
});
</script>
@endif @endif
@ -185,6 +227,40 @@
</div> </div>
</div> </div>
<div class="modal fade" id="patternHelpModal" tabindex="-1" role="dialog" aria-labelledby="patternHelpModalLabel" aria-hidden="true">
<div class="modal-dialog" style="min-width:150px">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" id="patternHelpModalLabel">{{ trans('texts.pattern_help_title') }}</h4>
</div>
<div class="modal-body">
<p>{{ trans('texts.pattern_help_1') }}</p>
<p>{{ trans('texts.pattern_help_2') }}</p>
<ul>
@foreach (\App\Models\Invoice::$patternFields as $field)
@if ($field == 'date:')
<li>{$date:format} - {!! link_to(PHP_DATE_FORMATS, trans('texts.see_options'), ['target' => '_blank']) !!}</li>
@else
<li>{${{ $field }}}</li>
@endif
@endforeach
</ul>
<p>{{ trans('texts.pattern_help_3', [
'example' => '{$year}-{$counter}',
'value' => date('Y') . '-0001'
]) }}</p>
</div>
<div class="modal-footer" style="margin-top: 0px">
<button type="button" class="btn btn-primary" data-dismiss="modal">{{ trans('texts.close') }}</button>
</div>
</div>
</div>
</div>
{!! Former::close() !!} {!! Former::close() !!}
@ -216,13 +292,41 @@
} }
} }
function onInvoiceNumberTypeChange() {
var val = $('input[name=invoice_number_type]:checked').val()
if (val == 'prefix') {
$('.invoice-prefix').show();
$('.invoice-pattern').hide();
} else {
$('.invoice-prefix').hide();
$('.invoice-pattern').show();
}
}
function onQuoteNumberTypeChange() {
var val = $('input[name=quote_number_type]:checked').val()
if (val == 'prefix') {
$('.quote-prefix').show();
$('.quote-pattern').hide();
} else {
$('.quote-prefix').hide();
$('.quote-pattern').show();
}
}
$('.iframe_url .input-group-addon').click(function() { $('.iframe_url .input-group-addon').click(function() {
$('#iframeHelpModal').modal('show'); $('#iframeHelpModal').modal('show');
}); });
$('.number-pattern .input-group-addon').click(function() {
$('#patternHelpModal').modal('show');
});
$(function() { $(function() {
setQuoteNumberEnabled(); setQuoteNumberEnabled();
onCustomLinkChange(); onCustomLinkChange();
onInvoiceNumberTypeChange();
onQuoteNumberTypeChange();
$('#subdomain').change(function() { $('#subdomain').change(function() {
$('#iframe_url').val(''); $('#iframe_url').val('');

View File

@ -0,0 +1,46 @@
@extends('header')
@section('content')
@parent
{!! Former::open_for_files()->addClass('warn-on-exit') !!}
{{ Former::populate($account) }}
{{ Former::populateField('military_time', intval($account->military_time)) }}
@include('accounts.nav', ['selected' => ACCOUNT_LOCALIZATION])
<div class="row">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{!! trans('texts.localization') !!}</h3>
</div>
<div class="panel-body form-padding-right">
{!! Former::select('currency_id')->addOption('','')
->fromQuery($currencies, 'name', 'id') !!}
{!! Former::select('language_id')->addOption('','')
->fromQuery($languages, 'name', 'id') !!}
{!! Former::select('timezone_id')->addOption('','')
->fromQuery($timezones, 'location', 'id') !!}
{!! Former::select('date_format_id')->addOption('','')
->fromQuery($dateFormats, 'label', 'id') !!}
{!! Former::select('datetime_format_id')->addOption('','')
->fromQuery($datetimeFormats, 'label', 'id') !!}
{!! Former::checkbox('military_time')->text(trans('texts.enable')) !!}
</div>
</div>
</div>
<center>
{!! Button::success(trans('texts.save'))->submit()->large()->appendIcon(Icon::create('floppy-disk')) !!}
</center>
{!! Former::close() !!}
@stop
@section('onReady')
$('#currency_id').focus();
@stop

View File

@ -1,16 +1,33 @@
@extends('header') @if (!Utils::isPro() && isset($advanced) && $advanced)
<div class="alert alert-warning" style="font-size:larger;">
<center>
{!! trans('texts.pro_plan_advanced_settings', ['link'=>'<a href="#" onclick="showProPlan(\''.$selected.'\')">'.trans('texts.pro_plan.remove_logo_link').'</a>']) !!}
</center>
</div>
@endif
@section('content') <div class="row">
<ul class="nav nav-tabs nav nav-justified"> <div class="col-md-3">
{!! HTML::nav_link('company/details', 'company_details') !!} @foreach([
{!! HTML::nav_link('company/payments', 'online_payments', 'gateways') !!} BASIC_SETTINGS => \App\Models\Account::$basicSettings,
{!! HTML::nav_link('company/products', 'product_library') !!} ADVANCED_SETTINGS => \App\Models\Account::$advancedSettings,
{!! HTML::nav_link('company/notifications', 'notifications') !!} ] as $type => $settings)
{!! HTML::nav_link('company/import_export', 'import_export', 'company/import_map') !!} <div class="panel panel-default">
{!! HTML::nav_link('company/advanced_settings/invoice_design', 'advanced_settings', '*/advanced_settings/*') !!} <div class="panel-heading" style="color:white">
</ul> {{ trans("texts.{$type}") }}
@if ($type == ADVANCED_SETTINGS && !Utils::isPro())
<sup>{{ strtoupper(trans('texts.pro')) }}</sup>
@endif
</div>
<div class="list-group">
@foreach ($settings as $section)
<a href="{{ URL::to("settings/{$section}") }}" class="list-group-item {{ $selected === $section ? 'selected' : '' }}"
style="width:100%;text-align:left">{{ trans("texts.{$section}") }}</a>
@endforeach
</div>
</div>
@endforeach
</div>
<br/> <div class="col-md-9">
@stop

View File

@ -1,9 +1,11 @@
@extends('accounts.nav') @extends('header')
@section('content') @section('content')
@parent @parent
{!! Former::open()->addClass('col-md-8 col-md-offset-2 warn-on-exit') !!} @include('accounts.nav', ['selected' => ACCOUNT_NOTIFICATIONS])
{!! Former::open()->addClass('warn-on-exit') !!}
{{ Former::populate($account) }} {{ Former::populate($account) }}
{{ Former::populateField('notify_sent', intval(Auth::user()->notify_sent)) }} {{ Former::populateField('notify_sent', intval(Auth::user()->notify_sent)) }}
{{ Former::populateField('notify_viewed', intval(Auth::user()->notify_viewed)) }} {{ Former::populateField('notify_viewed', intval(Auth::user()->notify_viewed)) }}
@ -41,13 +43,12 @@
<div class="fb-follow" data-href="https://www.facebook.com/invoiceninja" data-colorscheme="light" data-layout="button" data-show-faces="false"></div>&nbsp;&nbsp; <div class="fb-follow" data-href="https://www.facebook.com/invoiceninja" data-colorscheme="light" data-layout="button" data-show-faces="false"></div>&nbsp;&nbsp;
<a href="https://twitter.com/invoiceninja" class="twitter-follow-button" data-show-count="false" data-related="hillelcoren" data-size="medium">Follow @invoiceninja</a> <a href="https://twitter.com/invoiceninja" class="twitter-follow-button" data-show-count="false" data-related="hillelcoren" data-size="large">Follow @invoiceninja</a>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script> <script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script>
</div>
</div></div> </div>
--> -->
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<h3 class="panel-title">{!! trans('texts.custom_messages') !!}</h3> <h3 class="panel-title">{!! trans('texts.custom_messages') !!}</h3>

View File

@ -1,7 +1,8 @@
@extends('accounts.nav') @extends('header')
@section('content') @section('content')
@parent @parent
@include('accounts.nav', ['selected' => ACCOUNT_PAYMENTS])
{!! Former::open('gateways/delete')->addClass('user-form') !!} {!! Former::open('gateways/delete')->addClass('user-form') !!}

View File

@ -1,18 +1,20 @@
@extends('accounts.nav') @extends('header')
@section('content') @section('content')
@parent @parent
@include('accounts.nav', ['selected' => ACCOUNT_PRODUCTS])
{!! Former::open($url)->method($method) {!! Former::open($url)->method($method)
->rules(['product_key' => 'required|max:255']) ->rules(['product_key' => 'required|max:255'])
->addClass('col-md-8 col-md-offset-2 warn-on-exit') !!} ->addClass('warn-on-exit') !!}
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<h3 class="panel-title">{!! $title !!}</h3> <h3 class="panel-title">{!! $title !!}</h3>
</div> </div>
<div class="panel-body"> <div class="panel-body form-padding-right">
@if ($product) @if ($product)
{{ Former::populate($product) }} {{ Former::populate($product) }}
@ -23,11 +25,18 @@
{!! Former::textarea('notes') !!} {!! Former::textarea('notes') !!}
{!! Former::text('cost') !!} {!! Former::text('cost') !!}
@if ($account->invoice_item_taxes)
{!! Former::select('default_tax_rate_id')
->addOption('', '')
->label(trans('texts.tax_rate'))
->fromQuery($taxRates, function($model) { return $model->name . ' ' . $model->rate . '%'; }, 'id') !!}
@endif
</div> </div>
</div> </div>
{!! Former::actions( {!! Former::actions(
Button::normal(trans('texts.cancel'))->large()->asLinkTo(URL::to('/company/products'))->appendIcon(Icon::create('remove-circle')), Button::normal(trans('texts.cancel'))->large()->asLinkTo(URL::to('/settings/products'))->appendIcon(Icon::create('remove-circle')),
Button::success(trans('texts.save'))->submit()->large()->appendIcon(Icon::create('floppy-disk')) Button::success(trans('texts.save'))->submit()->large()->appendIcon(Icon::create('floppy-disk'))
) !!} ) !!}

View File

@ -1,8 +1,10 @@
@extends('accounts.nav') @extends('header')
@section('content') @section('content')
@parent @parent
@include('accounts.nav', ['selected' => ACCOUNT_PRODUCTS])
{!! Former::open()->addClass('warn-on-exit') !!} {!! Former::open()->addClass('warn-on-exit') !!}
{{ Former::populateField('fill_products', intval($account->fill_products)) }} {{ Former::populateField('fill_products', intval($account->fill_products)) }}
{{ Former::populateField('update_products', intval($account->update_products)) }} {{ Former::populateField('update_products', intval($account->update_products)) }}
@ -17,7 +19,7 @@
{!! Former::checkbox('fill_products')->text(trans('texts.fill_products_help')) !!} {!! Former::checkbox('fill_products')->text(trans('texts.fill_products_help')) !!}
{!! Former::checkbox('update_products')->text(trans('texts.update_products_help')) !!} {!! Former::checkbox('update_products')->text(trans('texts.update_products_help')) !!}
&nbsp; &nbsp;
{!! Former::actions( Button::success(trans('texts.save'))->submit()->large()->appendIcon(Icon::create('floppy-disk')) ) !!} {!! Former::actions( Button::success(trans('texts.save'))->submit()->appendIcon(Icon::create('floppy-disk')) ) !!}
{!! Former::close() !!} {!! Former::close() !!}
</div> </div>
</div> </div>
@ -28,16 +30,12 @@
->appendIcon(Icon::create('plus-sign')) !!} ->appendIcon(Icon::create('plus-sign')) !!}
{!! Datatable::table() {!! Datatable::table()
->addColumn( ->addColumn($columns)
trans('texts.product'),
trans('texts.description'),
trans('texts.unit_cost'),
trans('texts.action'))
->setUrl(url('api/products/')) ->setUrl(url('api/products/'))
->setOptions('sPaginationType', 'bootstrap') ->setOptions('sPaginationType', 'bootstrap')
->setOptions('bFilter', false) ->setOptions('bFilter', false)
->setOptions('bAutoWidth', false) ->setOptions('bAutoWidth', false)
->setOptions('aoColumns', [[ "sWidth"=> "20%" ], [ "sWidth"=> "45%" ], ["sWidth"=> "20%"], ["sWidth"=> "15%" ]]) //->setOptions('aoColumns', [[ "sWidth"=> "15%" ], [ "sWidth"=> "35%" ]])
->setOptions('aoColumnDefs', [['bSortable'=>false, 'aTargets'=>[3]]]) ->setOptions('aoColumnDefs', [['bSortable'=>false, 'aTargets'=>[3]]])
->render('datatable') !!} ->render('datatable') !!}

View File

@ -0,0 +1,47 @@
@extends('header')
@section('content')
@parent
@include('accounts.nav', ['selected' => ACCOUNT_TAX_RATES])
{!! Former::open($url)->method($method)
->rules([
'name' => 'required',
'rate' => 'required'
])
->addClass('warn-on-exit') !!}
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{!! $title !!}</h3>
</div>
<div class="panel-body form-padding-right">
@if ($taxRate)
{{ Former::populate($taxRate) }}
@endif
{!! Former::text('name')->label('texts.name') !!}
{!! Former::text('rate')->label('texts.rate')->append('%') !!}
</div>
</div>
{!! Former::actions(
Button::normal(trans('texts.cancel'))->large()->asLinkTo(URL::to('/settings/tax_rates'))->appendIcon(Icon::create('remove-circle')),
Button::success(trans('texts.save'))->submit()->large()->appendIcon(Icon::create('floppy-disk'))
) !!}
{!! Former::close() !!}
<script type="text/javascript">
$(function() {
$('#name').focus();
});
</script>
@stop

View File

@ -0,0 +1,79 @@
@extends('header')
@section('content')
@parent
@include('accounts.nav', ['selected' => ACCOUNT_TAX_RATES])
{!! Former::open()->addClass('warn-on-exit') !!}
{{ Former::populate($account) }}
{{ Former::populateField('invoice_taxes', intval($account->invoice_taxes)) }}
{{ Former::populateField('invoice_item_taxes', intval($account->invoice_item_taxes)) }}
{{ Former::populateField('show_item_taxes', intval($account->show_item_taxes)) }}
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{!! trans('texts.tax_settings') !!}</h3>
</div>
<div class="panel-body">
{!! Former::checkbox('invoice_taxes')
->text(trans('texts.enable_invoice_tax'))
->label('&nbsp;') !!}
{!! Former::checkbox('invoice_item_taxes')
->text(trans('texts.enable_line_item_tax'))
->label('&nbsp;') !!}
{!! Former::checkbox('show_item_taxes')
->text(trans('texts.show_line_item_tax'))
->label('&nbsp;') !!}
&nbsp;
{!! Former::select('default_tax_rate_id')
->style('max-width: 250px')
->addOption('', '')
->fromQuery($taxRates, function($model) { return $model->name . ': ' . $model->rate . '%'; }, 'id') !!}
&nbsp;
{!! Former::actions( Button::success(trans('texts.save'))->submit()->appendIcon(Icon::create('floppy-disk')) ) !!}
{!! Former::close() !!}
</div>
</div>
{!! Button::primary(trans('texts.create_tax_rate'))
->asLinkTo(URL::to('/tax_rates/create'))
->withAttributes(['class' => 'pull-right'])
->appendIcon(Icon::create('plus-sign')) !!}
{!! Datatable::table()
->addColumn(
trans('texts.name'),
trans('texts.rate'),
trans('texts.action'))
->setUrl(url('api/tax_rates/'))
->setOptions('sPaginationType', 'bootstrap')
->setOptions('bFilter', false)
->setOptions('bAutoWidth', false)
->setOptions('aoColumns', [[ "sWidth"=> "40%" ], [ "sWidth"=> "40%" ], ["sWidth"=> "20%"]])
->setOptions('aoColumnDefs', [['bSortable'=>false, 'aTargets'=>[2]]])
->render('datatable') !!}
<script>
window.onDatatableReady = function() {
$('tbody tr').mouseover(function() {
$(this).closest('tr').find('.tr-action').css('visibility','visible');
}).mouseout(function() {
$dropdown = $(this).closest('tr').find('.tr-action');
if (!$dropdown.hasClass('open')) {
$dropdown.css('visibility','hidden');
}
});
}
</script>
@stop

View File

@ -1,4 +1,4 @@
@extends('accounts.nav') @extends('header')
@section('head') @section('head')
@parent @parent
@ -13,9 +13,9 @@
@section('content') @section('content')
@parent @parent
@include('accounts.nav_advanced') @include('accounts.nav', ['selected' => ACCOUNT_TEMPLATES_AND_REMINDERS, 'advanced' => true])
{!! Former::vertical_open()->addClass('col-md-10 col-md-offset-1 warn-on-exit') !!} {!! Former::vertical_open()->addClass('warn-on-exit') !!}
{!! Former::populate($account) !!} {!! Former::populate($account) !!}
@foreach ([ENTITY_INVOICE, ENTITY_QUOTE, ENTITY_PAYMENT, REMINDER1, REMINDER2, REMINDER3] as $type) @foreach ([ENTITY_INVOICE, ENTITY_QUOTE, ENTITY_PAYMENT, REMINDER1, REMINDER2, REMINDER3] as $type)

Some files were not shown because too many files have changed in this diff Show More