1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-08 20:22:42 +01:00
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

@ -47,7 +47,7 @@ class Handler extends ExceptionHandler {
if ($e instanceof ModelNotFoundException) { if ($e instanceof ModelNotFoundException) {
return Redirect::to('/'); return Redirect::to('/');
} }
// In production, except for maintenance mode, we'll show a custom error screen // In production, except for maintenance mode, we'll show a custom error screen
if (Utils::isNinjaProd() && !Utils::isDownForMaintenance()) { if (Utils::isNinjaProd() && !Utils::isDownForMaintenance()) {
$data = [ $data = [

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,168 +143,260 @@ 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);
}
$oauthLoginUrls = []; if ($section == ACCOUNT_COMPANY_DETAILS) {
foreach (AuthService::$providers as $provider) { return self::showCompanyDetails();
$oauthLoginUrls[] = ['label' => $provider, 'url' => '/auth/' . strtolower($provider)]; } elseif ($section == ACCOUNT_USER_DETAILS) {
} return self::showUserDetails();
} elseif ($section == ACCOUNT_LOCALIZATION) {
$data = [ return self::showLocalization();
'account' => Account::with('users')->findOrFail(Auth::user()->account_id),
'countries' => Cache::get('countries'),
'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(),
'oauthProviderName' => AuthService::getProviderName(Auth::user()->oauth_provider_id),
'oauthLoginUrls' => $oauthLoginUrls,
];
return View::make('accounts.details', $data);
} elseif ($section == ACCOUNT_PAYMENTS) { } elseif ($section == ACCOUNT_PAYMENTS) {
return self::showOnlinePayments();
$account = Auth::user()->account; } elseif ($section == ACCOUNT_INVOICE_SETTINGS) {
$account->load('account_gateways'); return self::showInvoiceSettings();
$count = count($account->account_gateways);
if ($count == 0) {
return Redirect::to('gateways/create');
} else {
return View::make('accounts.payments', [
'showAdd' => $count < count(Gateway::$paymentTypes),
'title' => trans('texts.online_payments')
]);
}
} elseif ($section == ACCOUNT_NOTIFICATIONS) {
$data = [
'account' => Account::with('users')->findOrFail(Auth::user()->account_id),
'title' => trans('texts.notifications'),
];
return View::make('accounts.notifications', $data);
} elseif ($section == ACCOUNT_IMPORT_EXPORT) { } elseif ($section == ACCOUNT_IMPORT_EXPORT) {
return View::make('accounts.import_export', ['title' => trans('texts.import_export')]); return View::make('accounts.import_export', ['title' => trans('texts.import_export')]);
} elseif ($section == ACCOUNT_ADVANCED_SETTINGS) { } elseif ($section == ACCOUNT_INVOICE_DESIGN || $section == ACCOUNT_CUSTOMIZE_DESIGN) {
$account = Auth::user()->account->load('country'); 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 = [ $data = [
'account' => $account, 'account' => Account::with('users')->findOrFail(Auth::user()->account_id),
'feature' => $subSection, 'title' => trans("texts.{$section}"),
'title' => trans('texts.invoice_settings'), 'section' => $section
]; ];
return View::make("accounts.{$section}", $data);
if ($subSection == ACCOUNT_INVOICE_DESIGN
|| $subSection == ACCOUNT_CUSTOMIZE_DESIGN) {
$invoice = new stdClass();
$client = new stdClass();
$contact = new stdClass();
$invoiceItem = new stdClass();
$client->name = 'Sample Client';
$client->address1 = '';
$client->city = '';
$client->state = '';
$client->postal_code = '';
$client->work_phone = '';
$client->work_email = '';
$invoice->invoice_number = $account->getNextInvoiceNumber();
$invoice->invoice_date = Utils::fromSqlDate(date('Y-m-d'));
$invoice->account = json_decode($account->toJson());
$invoice->amount = $invoice->balance = 100;
$invoice->terms = trim($account->invoice_terms);
$invoice->invoice_footer = trim($account->invoice_footer);
$contact->email = 'contact@gmail.com';
$client->contacts = [$contact];
$invoiceItem->cost = 100;
$invoiceItem->qty = 1;
$invoiceItem->notes = 'Notes';
$invoiceItem->product_key = 'Item';
$invoice->client = $client;
$invoice->invoice_items = [$invoiceItem];
$data['account'] = $account;
$data['invoice'] = $invoice;
$data['invoiceLabels'] = json_decode($account->invoice_labels) ?: [];
$data['title'] = trans('texts.invoice_design');
$data['invoiceDesigns'] = InvoiceDesign::getDesigns();
$design = false;
foreach ($data['invoiceDesigns'] as $item) {
if ($item->id == $account->invoice_design_id) {
$design = $item->javascript;
break;
}
}
if ($subSection == ACCOUNT_CUSTOMIZE_DESIGN) {
$data['customDesign'] = ($account->custom_design && !$design) ? $account->custom_design : $design;
}
} else if ($subSection == ACCOUNT_TEMPLATES_AND_REMINDERS) {
$data['templates'] = [];
$data['defaultTemplates'] = [];
foreach ([ENTITY_INVOICE, ENTITY_QUOTE, ENTITY_PAYMENT, REMINDER1, REMINDER2, REMINDER3] as $type) {
$data['templates'][$type] = [
'subject' => $account->getEmailSubject($type),
'template' => $account->getEmailTemplate($type),
];
$data['defaultTemplates'][$type] = [
'subject' => $account->getDefaultEmailSubject($type),
'template' => $account->getDefaultEmailTemplate($type),
];
}
$data['emailFooter'] = $account->getEmailFooter();
$data['title'] = trans('texts.email_templates');
} else if ($subSection == ACCOUNT_USER_MANAGEMENT) {
$data['title'] = trans('texts.users_and_tokens');
}
return View::make("accounts.{$subSection}", $data);
} 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) private function showInvoiceSettings()
{ {
if ($section == ACCOUNT_DETAILS) { $account = Auth::user()->account;
return AccountController::saveDetails(); $recurringHours = [];
} elseif ($section == ACCOUNT_IMPORT_EXPORT) {
return AccountController::importFile(); for ($i=0; $i<24; $i++) {
} elseif ($section == ACCOUNT_MAP) { if ($account->military_time) {
return AccountController::mapFile(); $format = 'H:i';
} elseif ($section == ACCOUNT_NOTIFICATIONS) { } else {
return AccountController::saveNotifications(); $format = 'g:i a';
} elseif ($section == ACCOUNT_EXPORT) {
return AccountController::export();
} elseif ($section == ACCOUNT_ADVANCED_SETTINGS) {
if ($subSection == ACCOUNT_INVOICE_SETTINGS) {
return AccountController::saveInvoiceSettings();
} elseif ($subSection == ACCOUNT_INVOICE_DESIGN) {
return AccountController::saveInvoiceDesign();
} elseif ($subSection == ACCOUNT_CUSTOMIZE_DESIGN) {
return AccountController::saveCustomizeDesign();
} elseif ($subSection == ACCOUNT_TEMPLATES_AND_REMINDERS) {
return AccountController::saveEmailTemplates();
} }
} elseif ($section == ACCOUNT_PRODUCTS) { $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 = [];
foreach (AuthService::$providers as $provider) {
$oauthLoginUrls[] = ['label' => $provider, 'url' => '/auth/' . strtolower($provider)];
}
$data = [
'account' => Account::with('users')->findOrFail(Auth::user()->account_id),
'title' => trans('texts.user_details'),
'user' => Auth::user(),
'oauthProviderName' => AuthService::getProviderName(Auth::user()->oauth_provider_id),
'oauthLoginUrls' => $oauthLoginUrls,
];
return View::make('accounts.user_details', $data);
}
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->load('account_gateways');
$count = count($account->account_gateways);
if ($count == 0) {
return Redirect::to('gateways/create');
} else {
return View::make('accounts.payments', [
'showAdd' => $count < count(Gateway::$paymentTypes),
'title' => trans('texts.online_payments')
]);
}
}
private function showProducts()
{
$columns = ['product', 'description', 'unit_cost'];
if (Auth::user()->account->invoice_item_taxes) {
$columns[] = 'tax_rate';
}
$columns[] = 'action';
$data = [
'account' => Auth::user()->account,
'title' => trans('texts.product_library'),
'columns' => Utils::trans($columns),
];
return View::make('accounts.products', $data);
}
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');
$invoice = new stdClass();
$client = new stdClass();
$contact = new stdClass();
$invoiceItem = new stdClass();
$client->name = 'Sample Client';
$client->address1 = '';
$client->city = '';
$client->state = '';
$client->postal_code = '';
$client->work_phone = '';
$client->work_email = '';
$invoice->invoice_number = '0000';
$invoice->invoice_date = Utils::fromSqlDate(date('Y-m-d'));
$invoice->account = json_decode($account->toJson());
$invoice->amount = $invoice->balance = 100;
$invoice->terms = trim($account->invoice_terms);
$invoice->invoice_footer = trim($account->invoice_footer);
$contact->email = 'contact@gmail.com';
$client->contacts = [$contact];
$invoiceItem->cost = 100;
$invoiceItem->qty = 1;
$invoiceItem->notes = 'Notes';
$invoiceItem->product_key = 'Item';
$invoice->client = $client;
$invoice->invoice_items = [$invoiceItem];
$data['account'] = $account;
$data['invoice'] = $invoice;
$data['invoiceLabels'] = json_decode($account->invoice_labels) ?: [];
$data['title'] = trans('texts.invoice_design');
$data['invoiceDesigns'] = InvoiceDesign::getDesigns();
$data['section'] = $section;
$design = false;
foreach ($data['invoiceDesigns'] as $item) {
if ($item->id == $account->invoice_design_id) {
$design = $item->javascript;
break;
}
}
if ($section == ACCOUNT_CUSTOMIZE_DESIGN) {
$data['customDesign'] = ($account->custom_design && !$design) ? $account->custom_design : $design;
}
return View::make("accounts.{$section}", $data);
}
private function showTemplates()
{
$account = Auth::user()->account->load('country');
$data['account'] = $account;
$data['templates'] = [];
$data['defaultTemplates'] = [];
foreach ([ENTITY_INVOICE, ENTITY_QUOTE, ENTITY_PAYMENT, REMINDER1, REMINDER2, REMINDER3] as $type) {
$data['templates'][$type] = [
'subject' => $account->getEmailSubject($type),
'template' => $account->getEmailTemplate($type),
];
$data['defaultTemplates'][$type] = [
'subject' => $account->getDefaultEmailSubject($type),
'template' => $account->getDefaultEmailTemplate($type),
];
}
$data['emailFooter'] = $account->getEmailFooter();
$data['title'] = trans('texts.email_templates');
return View::make('accounts.templates_and_reminders', $data);
}
public function doSection($section = ACCOUNT_COMPANY_DETAILS)
{
if ($section === ACCOUNT_COMPANY_DETAILS) {
return AccountController::saveDetails();
} elseif ($section === ACCOUNT_USER_DETAILS) {
return AccountController::saveUserDetails();
} elseif ($section === ACCOUNT_LOCALIZATION) {
return AccountController::saveLocalization();
} elseif ($section === ACCOUNT_IMPORT_EXPORT) {
return AccountController::importFile();
} elseif ($section === ACCOUNT_MAP) {
return AccountController::mapFile();
} elseif ($section === ACCOUNT_NOTIFICATIONS) {
return AccountController::saveNotifications();
} elseif ($section === ACCOUNT_EXPORT) {
return AccountController::export();
} elseif ($section === ACCOUNT_INVOICE_SETTINGS) {
return AccountController::saveInvoiceSettings();
} elseif ($section === ACCOUNT_INVOICE_DESIGN) {
return AccountController::saveInvoiceDesign();
} elseif ($section === ACCOUNT_CUSTOMIZE_DESIGN) {
return AccountController::saveCustomizeDesign();
} elseif ($section === ACCOUNT_TEMPLATES_AND_REMINDERS) {
return AccountController::saveEmailTemplates();
} 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,29 +838,7 @@ 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')) {
@ -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'), 'method' => 'POST',
'invoiceNumber' => $invoiceNumber, 'url' => 'invoices',
'method' => 'POST', 'title' => trans('texts.new_invoice'),
'url' => 'invoices', ];
'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,11 +440,12 @@ 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();
} }
if (!$ref) { if (!$ref) {
$this->error('No-Ref', $response->getMessage(), $accountGateway); $this->error('No-Ref', $response->getMessage(), $accountGateway);
@ -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,58 +12,81 @@ 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) {
return '<div class="btn-group tr-action" style="visibility:hidden;"> if ($account->invoice_item_taxes) {
<button type="button" class="btn btn-xs btn-default dropdown-toggle" data-toggle="dropdown"> $datatable->addColumn('tax_rate', function ($model) { return $model->tax_rate ? ($model->tax_name . ' ' . $model->tax_rate . '%') : ''; });
'.trans('texts.select').' <span class="caret"></span> }
</button>
<ul class="dropdown-menu" role="menu"> return $datatable->addColumn('dropdown', function ($model) {
<li><a href="'.URL::to('products/'.$model->public_id).'/edit">'.uctrans('texts.edit_product').'</a></li> return '<div class="btn-group tr-action" style="visibility:hidden;">
<li class="divider"></li> <button type="button" class="btn btn-xs btn-default dropdown-toggle" data-toggle="dropdown">
<li><a href="'.URL::to('products/'.$model->public_id).'/archive">'.uctrans('texts.archive_product').'</a></li> '.trans('texts.select').' <span class="caret"></span>
</ul> </button>
</div>'; <ul class="dropdown-menu" role="menu">
}) <li><a href="'.URL::to('products/'.$model->public_id).'/edit">'.uctrans('texts.edit_product').'</a></li>
->orderColumns(['cost', 'product_key', 'cost']) <li class="divider"></li>
->make(); <li><a href="'.URL::to('products/'.$model->public_id).'/archive">'.uctrans('texts.archive_product').'</a></li>
</ul>
</div>';
})
->orderColumns(['cost', 'product_key', 'cost'])
->make();
} }
public function edit($publicId) public function edit($publicId)
{ {
$account = Auth::user()->account;
$data = [ $data = [
'showBreadcrumbs' => false, 'account' => $account,
'product' => Product::scope($publicId)->firstOrFail(), 'taxRates' => $account->invoice_item_taxes ? TaxRate::scope()->get(['id', 'name', 'rate']) : null,
'method' => 'PUT', 'product' => Product::scope($publicId)->firstOrFail(),
'url' => 'products/'.$publicId, 'method' => 'PUT',
'title' => trans('texts.edit_product'), 'url' => 'products/'.$publicId,
]; 'title' => trans('texts.edit_product'),
];
return View::make('accounts.product', $data); return View::make('accounts.product', $data);
} }
public function create() public function create()
{ {
$account = Auth::user()->account;
$data = [ $data = [
'showBreadcrumbs' => false, 'account' => $account,
'product' => null, 'taxRates' => $account->invoice_item_taxes ? TaxRate::scope()->get(['id', 'name', 'rate']) : null,
'method' => 'POST', 'product' => null,
'url' => 'products', 'method' => 'POST',
'title' => trans('texts.create_product'), 'url' => 'products',
]; 'title' => trans('texts.create_product'),
];
return View::make('accounts.product', $data); return View::make('accounts.product', $data);
} }
@ -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,25 +81,23 @@ 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(
'account' => $account, $data = [
'invoice' => null, 'entityType' => $invoice->getEntityType(),
'data' => Input::old('data'), 'invoice' => $invoice,
'invoiceNumber' => $invoiceNumber, 'data' => Input::old('data'),
'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::post('company/cancel_account', 'AccountController@cancelAccount'); 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::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,29 +392,24 @@ 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;
} }
$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

60
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,8 +3108,39 @@ 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) {
}
/* Extra Small Devices, Phones */
@media only screen and (min-width : 480px) {
}
/* Small Devices, Tablets */
@media only screen and (min-width : 768px) {
.form-padding-right {
padding-right: 40px;
}
}
/* Medium Devices, Desktops */
@media only screen and (min-width : 992px) {
.form-padding-right {
padding-right: 100px;
}
.medium-dialog {
width: 760px;
}
.large-dialog {
width: 960px;
}
.hide-desktop
{
display: none;
}
} }
@media (max-width: 992px) { @media (max-width: 992px) {
@ -3115,15 +3149,6 @@ box-shadow: 0px 0px 15px 0px rgba(0, 5, 5, 0.2);
} }
} }
@media screen and (min-width: 992px) {
.medium-dialog {
width: 760px;
}
.large-dialog {
width: 960px;
}
}
@media (max-width: 767px) { @media (max-width: 767px) {
.test-class{color:black;} .test-class{color:black;}
@ -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;

60
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,8 +758,39 @@ 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) {
}
/* Extra Small Devices, Phones */
@media only screen and (min-width : 480px) {
}
/* Small Devices, Tablets */
@media only screen and (min-width : 768px) {
.form-padding-right {
padding-right: 40px;
}
}
/* Medium Devices, Desktops */
@media only screen and (min-width : 992px) {
.form-padding-right {
padding-right: 100px;
}
.medium-dialog {
width: 760px;
}
.large-dialog {
width: 960px;
}
.hide-desktop
{
display: none;
}
} }
@media (max-width: 992px) { @media (max-width: 992px) {
@ -765,15 +799,6 @@ box-shadow: 0px 0px 15px 0px rgba(0, 5, 5, 0.2);
} }
} }
@media screen and (min-width: 992px) {
.medium-dialog {
width: 760px;
}
.large-dialog {
width: 960px;
}
}
@media (max-width: 767px) { @media (max-width: 767px) {
.test-class{color:black;} .test-class{color:black;}
@ -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
@ -77,4 +77,5 @@ If you'd like to use our code to sell your own invoicing app email us for detail
* [caouecs/Laravel4-long](https://github.com/caouecs/Laravel4-lang) - List of languages for Laravel4 * [caouecs/Laravel4-long](https://github.com/caouecs/Laravel4-lang) - List of languages for Laravel4
* [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

File diff suppressed because it is too large Load Diff

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

@ -17,4 +17,4 @@ return array(
'next' => 'weiter &raquo;', 'next' => 'weiter &raquo;',
); );

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!",
];

File diff suppressed because it is too large Load Diff

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

@ -2,19 +2,19 @@
return array( return array(
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Pagination Language Lines | Pagination Language Lines
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| The following language lines are used by the paginator library to build | The following language lines are used by the paginator library to build
| the simple pagination links. You are free to change them to anything | the simple pagination links. You are free to change them to anything
| you want to customize your views to better match your application. | you want to customize your views to better match your application.
| |
*/ */
'previous' => '&laquo; Previous', 'previous' => '&laquo; Previous',
'next' => 'Next &raquo;', 'next' => 'Next &raquo;',
); );

View File

@ -2,21 +2,21 @@
return [ return [
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Password Reminder Language Lines | Password Reminder Language Lines
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| The following language lines are the default lines which match reasons | The following language lines are the default lines which match reasons
| that are given by the password broker for a password update attempt | that are given by the password broker for a password update attempt
| has failed, such as for an invalid token or invalid new password. | has failed, such as for an invalid token or invalid new password.
| |
*/ */
"password" => "Passwords must be at least six characters and match the confirmation.", "password" => "Passwords must be at least six characters and match the confirmation.",
"user" => "We can't find a user with that e-mail address.", "user" => "We can't find a user with that e-mail address.",
"token" => "This password reset token is invalid.", "token" => "This password reset token is invalid.",
"sent" => "We have e-mailed your password reset link!", "sent" => "We have e-mailed your password reset link!",
"reset" => "Your password has been reset!", "reset" => "Your password has been reset!",
]; ];

View File

@ -2,23 +2,23 @@
return array( return array(
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Password Reminder Language Lines | Password Reminder Language Lines
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| The following language lines are the default lines which match reasons | The following language lines are the default lines which match reasons
| that are given by the password broker for a password update attempt | that are given by the password broker for a password update attempt
| has failed, such as for an invalid token or invalid new password. | has failed, such as for an invalid token or invalid new password.
| |
*/ */
"password" => "Passwords must be at least six characters and match the confirmation.", "password" => "Passwords must be at least six characters and match the confirmation.",
"user" => "We can't find a user with that e-mail address.", "user" => "We can't find a user with that e-mail address.",
"token" => "This password reset token is invalid.", "token" => "This password reset token is invalid.",
"sent" => "Password reminder sent!", "sent" => "Password reminder sent!",
); );

File diff suppressed because it is too large Load Diff

View File

@ -2,102 +2,103 @@
return array( return array(
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Validation Language Lines | Validation Language Lines
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| The following language lines contain the default error messages used by | The following language lines contain the default error messages used by
| the validator class. Some of these rules have multiple versions such | the validator class. Some of these rules have multiple versions such
| such as the size rules. Feel free to tweak each of these messages. | such as the size rules. Feel free to tweak each of these messages.
| |
*/ */
"accepted" => "The :attribute must be accepted.", "accepted" => "The :attribute must be accepted.",
"active_url" => "The :attribute is not a valid URL.", "active_url" => "The :attribute is not a valid URL.",
"after" => "The :attribute must be a date after :date.", "after" => "The :attribute must be a date after :date.",
"alpha" => "The :attribute may only contain letters.", "alpha" => "The :attribute may only contain letters.",
"alpha_dash" => "The :attribute may only contain letters, numbers, and dashes.", "alpha_dash" => "The :attribute may only contain letters, numbers, and dashes.",
"alpha_num" => "The :attribute may only contain letters and numbers.", "alpha_num" => "The :attribute may only contain letters and numbers.",
"array" => "The :attribute must be an array.", "array" => "The :attribute must be an array.",
"before" => "The :attribute must be a date before :date.", "before" => "The :attribute must be a date before :date.",
"between" => array( "between" => array(
"numeric" => "The :attribute must be between :min - :max.", "numeric" => "The :attribute must be between :min - :max.",
"file" => "The :attribute must be between :min - :max kilobytes.", "file" => "The :attribute must be between :min - :max kilobytes.",
"string" => "The :attribute must be between :min - :max characters.", "string" => "The :attribute must be between :min - :max characters.",
"array" => "The :attribute must have between :min - :max items.", "array" => "The :attribute must have between :min - :max items.",
), ),
"confirmed" => "The :attribute confirmation does not match.", "confirmed" => "The :attribute confirmation does not match.",
"date" => "The :attribute is not a valid date.", "date" => "The :attribute is not a valid date.",
"date_format" => "The :attribute does not match the format :format.", "date_format" => "The :attribute does not match the format :format.",
"different" => "The :attribute and :other must be different.", "different" => "The :attribute and :other must be different.",
"digits" => "The :attribute must be :digits digits.", "digits" => "The :attribute must be :digits digits.",
"digits_between" => "The :attribute must be between :min and :max digits.", "digits_between" => "The :attribute must be between :min and :max digits.",
"email" => "The :attribute format is invalid.", "email" => "The :attribute format is invalid.",
"exists" => "The selected :attribute is invalid.", "exists" => "The selected :attribute is invalid.",
"image" => "The :attribute must be an image.", "image" => "The :attribute must be an image.",
"in" => "The selected :attribute is invalid.", "in" => "The selected :attribute is invalid.",
"integer" => "The :attribute must be an integer.", "integer" => "The :attribute must be an integer.",
"ip" => "The :attribute must be a valid IP address.", "ip" => "The :attribute must be a valid IP address.",
"max" => array( "max" => array(
"numeric" => "The :attribute may not be greater than :max.", "numeric" => "The :attribute may not be greater than :max.",
"file" => "The :attribute may not be greater than :max kilobytes.", "file" => "The :attribute may not be greater than :max kilobytes.",
"string" => "The :attribute may not be greater than :max characters.", "string" => "The :attribute may not be greater than :max characters.",
"array" => "The :attribute may not have more than :max items.", "array" => "The :attribute may not have more than :max items.",
), ),
"mimes" => "The :attribute must be a file of type: :values.", "mimes" => "The :attribute must be a file of type: :values.",
"min" => array( "min" => array(
"numeric" => "The :attribute must be at least :min.", "numeric" => "The :attribute must be at least :min.",
"file" => "The :attribute must be at least :min kilobytes.", "file" => "The :attribute must be at least :min kilobytes.",
"string" => "The :attribute must be at least :min characters.", "string" => "The :attribute must be at least :min characters.",
"array" => "The :attribute must have at least :min items.", "array" => "The :attribute must have at least :min items.",
), ),
"not_in" => "The selected :attribute is invalid.", "not_in" => "The selected :attribute is invalid.",
"numeric" => "The :attribute must be a number.", "numeric" => "The :attribute must be a number.",
"regex" => "The :attribute format is invalid.", "regex" => "The :attribute format is invalid.",
"required" => "The :attribute field is required.", "required" => "The :attribute field is required.",
"required_if" => "The :attribute field is required when :other is :value.", "required_if" => "The :attribute field is required when :other is :value.",
"required_with" => "The :attribute field is required when :values is present.", "required_with" => "The :attribute field is required when :values is present.",
"required_without" => "The :attribute field is required when :values is not present.", "required_without" => "The :attribute field is required when :values is not present.",
"same" => "The :attribute and :other must match.", "same" => "The :attribute and :other must match.",
"size" => array( "size" => array(
"numeric" => "The :attribute must be :size.", "numeric" => "The :attribute must be :size.",
"file" => "The :attribute must be :size kilobytes.", "file" => "The :attribute must be :size kilobytes.",
"string" => "The :attribute must be :size characters.", "string" => "The :attribute must be :size characters.",
"array" => "The :attribute must contain :size items.", "array" => "The :attribute must contain :size items.",
), ),
"unique" => "The :attribute has already been taken.", "unique" => "The :attribute has already been taken.",
"url" => "The :attribute format is invalid.", "url" => "The :attribute format is invalid.",
"positive" => "The :attribute must be greater than zero.", "positive" => "The :attribute must be greater than zero.",
"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}',
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Custom Validation Language Lines | Custom Validation Language Lines
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| Here you may specify custom validation messages for attributes using the | Here you may specify custom validation messages for attributes using the
| convention "attribute.rule" to name the lines. This makes it quick to | convention "attribute.rule" to name the lines. This makes it quick to
| specify a specific custom language line for a given attribute rule. | specify a specific custom language line for a given attribute rule.
| |
*/ */
'custom' => array(), 'custom' => array(),
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Custom Validation Attributes | Custom Validation Attributes
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| The following language lines are used to swap attribute place-holders | The following language lines are used to swap attribute place-holders
| with something more reader friendly such as E-Mail Address instead | with something more reader friendly such as E-Mail Address instead
| of "email". This simply helps us make messages a little cleaner. | of "email". This simply helps us make messages a little cleaner.
| |
*/ */
'attributes' => array(), 'attributes' => array(),
); );

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,7 +74,8 @@ 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}',
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Custom Validation Language Lines | Custom Validation Language Lines

View File

@ -1,477 +1,476 @@
<?php <?php
return array( return array(
// client // client
'organization' => 'Empresa', 'organization' => 'Empresa',
'name' => 'Nombre de Empresa', 'name' => 'Nombre de Empresa',
'website' => 'Sitio Web', 'website' => 'Sitio Web',
'work_phone' => 'Teléfono', 'work_phone' => 'Teléfono',
'address' => 'Dirección', 'address' => 'Dirección',
'address1' => 'Calle', 'address1' => 'Calle',
'address2' => 'Bloq/Pta', 'address2' => 'Bloq/Pta',
'city' => 'Ciudad', 'city' => 'Ciudad',
'state' => 'Provincia', 'state' => 'Provincia',
'postal_code' => 'Código Postal', 'postal_code' => 'Código Postal',
'country_id' => 'País', 'country_id' => 'País',
'contacts' => 'Contactos', 'contacts' => 'Contactos',
'first_name' => 'Nombres', 'first_name' => 'Nombres',
'last_name' => 'Apellidos', 'last_name' => 'Apellidos',
'phone' => 'Teléfono', 'phone' => 'Teléfono',
'email' => 'Email', 'email' => 'Email',
'additional_info' => 'Información adicional', 'additional_info' => 'Información adicional',
'payment_terms' => 'Plazos de pago', // 'payment_terms' => 'Plazos de pago', //
'currency_id' => 'Divisa', 'currency_id' => 'Divisa',
'size_id' => 'Tamaño', 'size_id' => 'Tamaño',
'industry_id' => 'Industria', 'industry_id' => 'Industria',
'private_notes' => 'Notas Privadas', 'private_notes' => 'Notas Privadas',
// invoice // invoice
'invoice' => 'Factura', 'invoice' => 'Factura',
'client' => 'Cliente', 'client' => 'Cliente',
'invoice_date' => 'Fecha de factura', 'invoice_date' => 'Fecha de factura',
'due_date' => 'Fecha de pago', 'due_date' => 'Fecha de pago',
'invoice_number' => 'Número de Factura', 'invoice_number' => 'Número de Factura',
'invoice_number_short' => 'Factura Nº', 'invoice_number_short' => 'Factura Nº',
'po_number' => 'Apartado de correo', 'po_number' => 'Apartado de correo',
'po_number_short' => 'Apdo.', 'po_number_short' => 'Apdo.',
'frequency_id' => 'Frecuencia', 'frequency_id' => 'Frecuencia',
'discount' => 'Descuento', 'discount' => 'Descuento',
'taxes' => 'Impuestos', 'taxes' => 'Impuestos',
'tax' => 'IVA', 'tax' => 'IVA',
'item' => 'Concepto', 'item' => 'Concepto',
'description' => 'Descripción', 'description' => 'Descripción',
'unit_cost' => 'Coste unitario', 'unit_cost' => 'Coste unitario',
'quantity' => 'Cantidad', 'quantity' => 'Cantidad',
'line_total' => 'Total', 'line_total' => 'Total',
'subtotal' => 'Subtotal', 'subtotal' => 'Subtotal',
'paid_to_date' => 'Pagado', 'paid_to_date' => 'Pagado',
'balance_due' => 'Pendiente', 'balance_due' => 'Pendiente',
'invoice_design_id' => 'Diseño', 'invoice_design_id' => 'Diseño',
'terms' => 'Términos', 'terms' => 'Términos',
'your_invoice' => 'Tu factura', 'your_invoice' => 'Tu factura',
'remove_contact' => 'Eliminar contacto', 'remove_contact' => 'Eliminar contacto',
'add_contact' => 'Añadir contacto', 'add_contact' => 'Añadir contacto',
'create_new_client' => 'Crear nuevo cliente', 'create_new_client' => 'Crear nuevo cliente',
'edit_client_details' => 'Editar detalles del cliente', 'edit_client_details' => 'Editar detalles del cliente',
'enable' => 'Activar', 'enable' => 'Activar',
'learn_more' => 'Saber más', 'learn_more' => 'Saber más',
'manage_rates' => 'Gestionar tarifas', 'manage_rates' => 'Gestionar tarifas',
'note_to_client' => 'Nota para el cliente', 'note_to_client' => 'Nota para el cliente',
'invoice_terms' => 'Términos de facturación', 'invoice_terms' => 'Términos de facturación',
'save_as_default_terms' => 'Guardar como términos por defecto', 'save_as_default_terms' => 'Guardar como términos por defecto',
'download_pdf' => 'Descargar PDF', 'download_pdf' => 'Descargar PDF',
'pay_now' => 'Pagar Ahora', 'pay_now' => 'Pagar Ahora',
'save_invoice' => 'Guardar factura', 'save_invoice' => 'Guardar factura',
'clone_invoice' => 'Clonar factura', 'clone_invoice' => 'Clonar factura',
'archive_invoice' => 'Archivar factura', 'archive_invoice' => 'Archivar factura',
'delete_invoice' => 'Eliminar factura', 'delete_invoice' => 'Eliminar factura',
'email_invoice' => 'Enviar factura por email', 'email_invoice' => 'Enviar factura por email',
'enter_payment' => 'Agregar pago', 'enter_payment' => 'Agregar pago',
'tax_rates' => 'Tasas de impuesto', 'tax_rates' => 'Tasas de impuesto',
'rate' => 'Tasas', 'rate' => 'Tasas',
'settings' => 'Configuración', 'settings' => 'Configuración',
'enable_invoice_tax' => 'Activar impuesto <b>para la factura</b>', 'enable_invoice_tax' => 'Activar impuesto <b>para la factura</b>',
'enable_line_item_tax' => 'Activar impuesto <b>por concepto</b>', 'enable_line_item_tax' => 'Activar impuesto <b>por concepto</b>',
// navigation // navigation
'dashboard' => 'Inicio', 'dashboard' => 'Inicio',
'clients' => 'Clientes', 'clients' => 'Clientes',
'invoices' => 'Facturas', 'invoices' => 'Facturas',
'payments' => 'Pagos', 'payments' => 'Pagos',
'credits' => 'Créditos', 'credits' => 'Créditos',
'history' => 'Historial', 'history' => 'Historial',
'search' => 'Búsqueda', 'search' => 'Búsqueda',
'sign_up' => 'Registrarse', 'sign_up' => 'Registrarse',
'guest' => 'Invitado', 'guest' => 'Invitado',
'company_details' => 'Detalles de la Empresa', 'company_details' => 'Detalles de la Empresa',
'online_payments' => 'Pagos en Linea', 'online_payments' => 'Pagos en Linea',
'notifications' => 'Notificaciones', 'notifications' => 'Notificaciones',
'import_export' => 'Importar/Exportar', 'import_export' => 'Importar/Exportar',
'done' => 'Hecho', 'done' => 'Hecho',
'save' => 'Guardar', 'save' => 'Guardar',
'create' => 'Crear', 'create' => 'Crear',
'upload' => 'Subir', 'upload' => 'Subir',
'import' => 'Importar', 'import' => 'Importar',
'download' => 'Descargar', 'download' => 'Descargar',
'cancel' => 'Cancelar', 'cancel' => 'Cancelar',
'close' => 'Cerrar', 'close' => 'Cerrar',
'provide_email' => 'Por favor facilita una dirección de correo válida.', 'provide_email' => 'Por favor facilita una dirección de correo válida.',
'powered_by' => 'Creado por', 'powered_by' => 'Creado por',
'no_items' => 'No hay datos', 'no_items' => 'No hay datos',
// recurring invoices // recurring invoices
'recurring_invoices' => 'Facturas recurrentes', 'recurring_invoices' => 'Facturas recurrentes',
'recurring_help' => '<p>Enviar facturas automáticamente a clientes semanalmente, bi-mensualmente, mensualmente, trimestral o anualmente. </p> 'recurring_help' => '<p>Enviar facturas automáticamente a clientes semanalmente, bi-mensualmente, mensualmente, trimestral o anualmente. </p>
<p>Uso :MONTH, :QUARTER or :YEAR para fechas dinámicas. Matemáticas básicas también funcionan bien. Por ejemplo: :MONTH-1.</p> <p>Uso :MONTH, :QUARTER or :YEAR para fechas dinámicas. Matemáticas básicas también funcionan bien. Por ejemplo: :MONTH-1.</p>
<p>Ejemplos de variables dinámicas de factura:</p> <p>Ejemplos de variables dinámicas de factura:</p>
<ul> <ul>
<li>"Afiliación de gimnasio para el mes de:MONTH" => Afiliación de gimnasio para el mes de julio"</li> <li>"Afiliación de gimnasio para el mes de:MONTH" => Afiliación de gimnasio para el mes de julio"</li>
<li>":YEAR+1 suscripción anual" => "2015 suscripción anual"</li> <li>":YEAR+1 suscripción anual" => "2015 suscripción anual"</li>
<li>"Retainer payment for :QUARTER+1" => "Pago anticipo de pagos para T2"</li> <li>"Retainer payment for :QUARTER+1" => "Pago anticipo de pagos para T2"</li>
</ul>', </ul>',
// dashboard // dashboard
'in_total_revenue' => 'Ingreso Total', 'in_total_revenue' => 'Ingreso Total',
'billed_client' => 'Cliente Facturado', 'billed_client' => 'Cliente Facturado',
'billed_clients' => 'Clientes Facturados', 'billed_clients' => 'Clientes Facturados',
'active_client' => 'Cliente Activo', 'active_client' => 'Cliente Activo',
'active_clients' => 'Clientes Activos', 'active_clients' => 'Clientes Activos',
'invoices_past_due' => 'Facturas Vencidas', 'invoices_past_due' => 'Facturas Vencidas',
'upcoming_invoices' => 'Próximas Facturas', 'upcoming_invoices' => 'Próximas Facturas',
'average_invoice' => 'Promedio de Facturación', 'average_invoice' => 'Promedio de Facturación',
// list pages // list pages
'archive' => 'Archivar', 'archive' => 'Archivar',
'delete' => 'Eliminar', 'delete' => 'Eliminar',
'archive_client' => 'Archivar Cliente', 'archive_client' => 'Archivar Cliente',
'delete_client' => 'Eliminar Cliente', 'delete_client' => 'Eliminar Cliente',
'archive_payment' => 'Archivar Pago', 'archive_payment' => 'Archivar Pago',
'delete_payment' => 'Eliminar Pago', 'delete_payment' => 'Eliminar Pago',
'archive_credit' => 'Archivar Crédito', 'archive_credit' => 'Archivar Crédito',
'delete_credit' => 'Eliminar Crédito', 'delete_credit' => 'Eliminar Crédito',
'show_archived_deleted' => 'Mostrar archivados o eliminados en ', 'show_archived_deleted' => 'Mostrar archivados o eliminados en ',
'filter' => 'Filtrar', 'filter' => 'Filtrar',
'new_client' => 'Nuevo Cliente', 'new_client' => 'Nuevo Cliente',
'new_invoice' => 'Nueva Factura', 'new_invoice' => 'Nueva Factura',
'new_payment' => 'Nuevo Pago', 'new_payment' => 'Nuevo Pago',
'new_credit' => 'Nuevo Crédito', 'new_credit' => 'Nuevo Crédito',
'contact' => 'Contacto', 'contact' => 'Contacto',
'date_created' => 'Fecha de Creación', 'date_created' => 'Fecha de Creación',
'last_login' => 'Último Acceso', 'last_login' => 'Último Acceso',
'balance' => 'Balance', 'balance' => 'Balance',
'action' => 'Acción', 'action' => 'Acción',
'status' => 'Estado', 'status' => 'Estado',
'invoice_total' => 'Total Facturado', 'invoice_total' => 'Total Facturado',
'frequency' => 'Frequencia', 'frequency' => 'Frequencia',
'start_date' => 'Fecha de Inicio', 'start_date' => 'Fecha de Inicio',
'end_date' => 'Fecha de Finalización', 'end_date' => 'Fecha de Finalización',
'transaction_reference' => 'Referencia de Transacción', 'transaction_reference' => 'Referencia de Transacción',
'method' => 'Método', 'method' => 'Método',
'payment_amount' => 'Valor del Pago', 'payment_amount' => 'Valor del Pago',
'payment_date' => 'Fecha de Pago', 'payment_date' => 'Fecha de Pago',
'credit_amount' => 'Cantidad de Crédito', 'credit_amount' => 'Cantidad de Crédito',
'credit_balance' => 'Balance de Crédito', 'credit_balance' => 'Balance de Crédito',
'credit_date' => 'Fecha de Crédito', 'credit_date' => 'Fecha de Crédito',
'empty_table' => 'Tabla vacía', 'empty_table' => 'Tabla vacía',
'select' => 'Seleccionar', 'select' => 'Seleccionar',
'edit_client' => 'Editar Cliente', 'edit_client' => 'Editar Cliente',
'edit_invoice' => 'Editar Factura', 'edit_invoice' => 'Editar Factura',
// client view page // client view page
'create_invoice' => 'Crear Factura', 'create_invoice' => 'Crear Factura',
'Create Invoice' => 'Crear Factura', 'Create Invoice' => 'Crear Factura',
'enter_credit' => 'Agregar Crédito', 'enter_credit' => 'Agregar Crédito',
'last_logged_in' => 'Último inicio de sesión', 'last_logged_in' => 'Último inicio de sesión',
'details' => 'Detalles', 'details' => 'Detalles',
'standing' => 'Situación', // 'standing' => 'Situación', //
'credit' => 'Crédito', 'credit' => 'Crédito',
'activity' => 'Actividad', 'activity' => 'Actividad',
'date' => 'Fecha', 'date' => 'Fecha',
'message' => 'Mensaje', 'message' => 'Mensaje',
'adjustment' => 'Ajustes', 'adjustment' => 'Ajustes',
'are_you_sure' => '¿Está Seguro?', 'are_you_sure' => '¿Está Seguro?',
// payment pages // payment pages
'payment_type_id' => 'Tipo de pago', 'payment_type_id' => 'Tipo de pago',
'amount' => 'Cantidad', 'amount' => 'Cantidad',
// Nuevo texto extraido - New text extracted
'Recommended Gateway' => 'Pasarelas Recomendadas',//
'Accepted Credit Cards' => 'Tarjetas de Credito Permitidas',//
'Payment Gateway' => 'Pasarelas de Pago',//
'Select Gateway' => 'Seleccione Pasarela',//
'Enable' => 'Activo',//
'Api Login Id' => 'Introduzca Api Id',//
'Transaction Key' => 'Clave de Transacción',//
'Create an account' => 'Crear cuenta nueva',//
'Other Options' => 'Otras Opciones',//
// account/company pages // Nuevo texto extraido - New text extracted
'work_email' => 'Correo electrónico de la empresa', 'Recommended Gateway' => 'Pasarelas Recomendadas',//
'language_id' => 'Idioma', 'Accepted Credit Cards' => 'Tarjetas de Credito Permitidas',//
'timezone_id' => 'Zona horaria', 'Payment Gateway' => 'Pasarelas de Pago',//
'date_format_id' => 'Formato de fecha', 'Select Gateway' => 'Seleccione Pasarela',//
'datetime_format_id' => 'Format de fecha/hora', 'Enable' => 'Activo',//
'users' => 'Usuarios', 'Api Login Id' => 'Introduzca Api Id',//
'localization' => 'Localización', 'Transaction Key' => 'Clave de Transacción',//
'remove_logo' => 'Eliminar logo', 'Create an account' => 'Crear cuenta nueva',//
'logo_help' => 'Formatos aceptados: JPEG, GIF y PNG', 'Other Options' => 'Otras Opciones',//
'payment_gateway' => 'Pasarela de pago',
'gateway_id' => 'Proveedor',
'email_notifications' => 'Notificaciones de email',
'email_sent' => 'Avísame por email cuando una factura <b>se envía</b>',
'email_viewed' => 'Avísame por email cuando una factura <b>se visualiza</b>',
'email_paid' => 'Avísame por email cuando una factura <b>se paga</b>',
'site_updates' => 'Actualizaciones del sitio',
'custom_messages' => 'Mensajes a medida',
'default_invoice_terms' => 'Configurar términos de factura por defecto',
'default_email_footer' => 'Configurar firma de email por defecto',
'import_clients' => 'Importar datos del cliente',
'csv_file' => 'Seleccionar archivo CSV',
'export_clients' => 'Exportar datos del cliente',
'select_file' => 'Seleccionar archivo',
'first_row_headers' => 'Usar la primera fila como encabezados',
'column' => 'Columna',
'sample' => 'Ejemplo',
'import_to' => 'Importar a',
'client_will_create' => 'cliente se creará',
'clients_will_create' => 'clientes se crearan',
// application messages // account/company pages
'created_client' => 'cliente creado con éxito', 'work_email' => 'Correo electrónico de la empresa',
'created_clients' => ':count clientes creados con éxito', 'language_id' => 'Idioma',
'updated_settings' => 'Configuración actualizada con éxito', 'timezone_id' => 'Zona horaria',
'removed_logo' => 'Logo eliminado con éxito', 'date_format_id' => 'Formato de fecha',
'sent_message' => 'Mensaje enviado con éxito', 'datetime_format_id' => 'Format de fecha/hora',
'invoice_error' => 'Seleccionar cliente y corregir errores.', 'users' => 'Usuarios',
'limit_clients' => 'Lo sentimos, se ha pasado del límite de :count clientes', 'localization' => 'Localización',
'payment_error' => 'Ha habido un error en el proceso de tu pago. Inténtalo de nuevo más tarde.', 'remove_logo' => 'Eliminar logo',
'registration_required' => 'Inscríbete para enviar una factura', 'logo_help' => 'Formatos aceptados: JPEG, GIF y PNG',
'confirmation_required' => 'Por favor confirma tu dirección de correo electrónico', 'payment_gateway' => 'Pasarela de pago',
'gateway_id' => 'Proveedor',
'email_notifications' => 'Notificaciones de email',
'email_sent' => 'Avísame por email cuando una factura <b>se envía</b>',
'email_viewed' => 'Avísame por email cuando una factura <b>se visualiza</b>',
'email_paid' => 'Avísame por email cuando una factura <b>se paga</b>',
'site_updates' => 'Actualizaciones del sitio',
'custom_messages' => 'Mensajes a medida',
'default_invoice_terms' => 'Configurar términos de factura por defecto',
'default_email_footer' => 'Configurar firma de email por defecto',
'import_clients' => 'Importar datos del cliente',
'csv_file' => 'Seleccionar archivo CSV',
'export_clients' => 'Exportar datos del cliente',
'select_file' => 'Seleccionar archivo',
'first_row_headers' => 'Usar la primera fila como encabezados',
'column' => 'Columna',
'sample' => 'Ejemplo',
'import_to' => 'Importar a',
'client_will_create' => 'cliente se creará',
'clients_will_create' => 'clientes se crearan',
'updated_client' => 'Cliente actualizado con éxito', // application messages
'created_client' => 'Cliente creado con éxito', 'created_client' => 'cliente creado con éxito',
'archived_client' => 'Cliente archivado con éxito', 'created_clients' => ':count clientes creados con éxito',
'archived_clients' => ':count clientes archivados con éxito', 'updated_settings' => 'Configuración actualizada con éxito',
'deleted_client' => 'Cliente eliminado con éxito', 'removed_logo' => 'Logo eliminado con éxito',
'deleted_clients' => ':count clientes eliminados con éxito', 'sent_message' => 'Mensaje enviado con éxito',
'invoice_error' => 'Seleccionar cliente y corregir errores.',
'limit_clients' => 'Lo sentimos, se ha pasado del límite de :count clientes',
'payment_error' => 'Ha habido un error en el proceso de tu pago. Inténtalo de nuevo más tarde.',
'registration_required' => 'Inscríbete para enviar una factura',
'confirmation_required' => 'Por favor confirma tu dirección de correo electrónico',
'updated_invoice' => 'Factura actualizada con éxito', 'updated_client' => 'Cliente actualizado con éxito',
'created_invoice' => 'Factura creada con éxito', 'created_client' => 'Cliente creado con éxito',
'cloned_invoice' => 'Factura clonada con éxito', 'archived_client' => 'Cliente archivado con éxito',
'emailed_invoice' => 'Factura enviada con éxito', 'archived_clients' => ':count clientes archivados con éxito',
'and_created_client' => 'y cliente creado ', 'deleted_client' => 'Cliente eliminado con éxito',
'archived_invoice' => 'Factura archivada con éxito', 'deleted_clients' => ':count clientes eliminados con éxito',
'archived_invoices' => ':count facturas archivados con éxito',
'deleted_invoice' => 'Factura eliminada con éxito',
'deleted_invoices' => ':count facturas eliminadas con éxito',
'created_payment' => 'Pago creado con éxito', 'updated_invoice' => 'Factura actualizada con éxito',
'archived_payment' => 'Pago archivado con éxito', 'created_invoice' => 'Factura creada con éxito',
'archived_payments' => ':count pagos archivados con éxito', 'cloned_invoice' => 'Factura clonada con éxito',
'deleted_payment' => 'Pago eliminado con éxito', 'emailed_invoice' => 'Factura enviada con éxito',
'deleted_payments' => ':count pagos eliminados con éxito', 'and_created_client' => 'y cliente creado ',
'applied_payment' => 'Pago aplicado con éxito', 'archived_invoice' => 'Factura archivada con éxito',
'archived_invoices' => ':count facturas archivados con éxito',
'deleted_invoice' => 'Factura eliminada con éxito',
'deleted_invoices' => ':count facturas eliminadas con éxito',
'created_credit' => 'Crédito creado con éxito', 'created_payment' => 'Pago creado con éxito',
'archived_credit' => 'Crédito archivado con éxito', 'archived_payment' => 'Pago archivado con éxito',
'archived_credits' => ':count creditos archivados con éxito', 'archived_payments' => ':count pagos archivados con éxito',
'deleted_credit' => 'Créditos eliminados con éxito', 'deleted_payment' => 'Pago eliminado con éxito',
'deleted_credits' => ':count creditos eliminados con éxito', 'deleted_payments' => ':count pagos eliminados con éxito',
'applied_payment' => 'Pago aplicado con éxito',
// Emails 'created_credit' => 'Crédito creado con éxito',
'confirmation_subject' => 'Corfimación de tu cuenta en Invoice Ninja', 'archived_credit' => 'Crédito archivado con éxito',
'confirmation_header' => 'Confirmación de Cuenta', 'archived_credits' => ':count creditos archivados con éxito',
'confirmation_message' => 'Por favor, haz clic en el enlace abajo para confirmar tu cuenta.', 'deleted_credit' => 'Créditos eliminados con éxito',
'invoice_subject' => 'Nueva factura :invoice de :account', 'deleted_credits' => ':count creditos eliminados con éxito',
'invoice_message' => 'Para visualizar tu factura por el valor de :amount, haz click en el enlace de abajo.',
'payment_subject' => 'Pago recibido',
'payment_message' => 'Gracias por su pago de :amount.',
'email_salutation' => 'Estimado :name,',
'email_signature' => 'Un cordial saludo,',
'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',
'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_sent_subject' => 'La factura :invoice ha sido enviada a el cliente :client',
'notification_invoice_viewed_subject' => 'La factura :invoice ha sido visualizado por el cliente:client',
'notification_invoice_paid' => 'Un pago por importe de :amount ha sido realizado por el cliente :client correspondiente a la factura :invoice.',
'notification_invoice_sent' => 'La factura :invoice por importe de :amount fue enviada al cliente :cliente.',
'notification_invoice_viewed' => 'La factura :invoice por importe de :amount fue visualizada por el cliente :client.',
'reset_password' => 'Puedes reconfigurar la contraseña de tu cuenta haciendo clic en el siguiente enlace:',
'reset_password_footer' => 'Si no has solicitado un cambio de contraseña, por favor contactate con nosostros: ' . CONTACT_EMAIL,
// Payment page // Emails
'secure_payment' => 'Pago seguro', 'confirmation_subject' => 'Corfimación de tu cuenta en Invoice Ninja',
'card_number' => 'Número de tarjeta', 'confirmation_header' => 'Confirmación de Cuenta',
'expiration_month' => 'Mes de caducidad', 'confirmation_message' => 'Por favor, haz clic en el enlace abajo para confirmar tu cuenta.',
'expiration_year' => 'Año de caducidad', 'invoice_subject' => 'Nueva factura :invoice de :account',
'cvv' => 'CVV', 'invoice_message' => 'Para visualizar tu factura por el valor de :amount, haz click en el enlace de abajo.',
'payment_subject' => 'Pago recibido',
'payment_message' => 'Gracias por su pago de :amount.',
'email_salutation' => 'Estimado :name,',
'email_signature' => 'Un cordial saludo,',
'email_from' => 'El equipo de Invoice Ninja ',
'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:',
'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_viewed_subject' => 'La factura :invoice ha sido visualizado por el cliente:client',
'notification_invoice_paid' => 'Un pago por importe de :amount ha sido realizado por el cliente :client correspondiente a la factura :invoice.',
'notification_invoice_sent' => 'La factura :invoice por importe de :amount fue enviada al cliente :cliente.',
'notification_invoice_viewed' => 'La factura :invoice por importe de :amount fue visualizada por el cliente :client.',
'reset_password' => 'Puedes reconfigurar la contraseña de tu cuenta haciendo clic en el siguiente enlace:',
'reset_password_footer' => 'Si no has solicitado un cambio de contraseña, por favor contactate con nosostros: '.CONTACT_EMAIL,
// Security alerts // Payment page
'confide' => array( 'secure_payment' => 'Pago seguro',
'too_many_attempts' => 'Demasiados intentos fallidos. Inténtalo de nuevo en un par de minutos.', 'card_number' => 'Número de tarjeta',
'wrong_credentials' => 'Contraseña o email incorrecto.', 'expiration_month' => 'Mes de caducidad',
'confirmation' => '¡Tu cuenta se ha confirmado!', 'expiration_year' => 'Año de caducidad',
'wrong_confirmation' => 'Código de confirmación incorrecto.', 'cvv' => 'CVV',
'password_forgot' => 'La información sobre el cambio de tu contraseña se ha enviado a tu dirección de correo electrónico.',
'password_reset' => 'Tu contraseña se ha cambiado con éxito.',
'wrong_password_reset' => 'Contraseña no válida. Inténtalo de nuevo',
),
// Pro Plan // Security alerts
'pro_plan' => [ 'confide' => array(
'remove_logo' => ':link haz click para eliminar el logo de Invoice Ninja', 'too_many_attempts' => 'Demasiados intentos fallidos. Inténtalo de nuevo en un par de minutos.',
'remove_logo_link' => 'Haz click aquí', 'wrong_credentials' => 'Contraseña o email incorrecto.',
], 'confirmation' => '¡Tu cuenta se ha confirmado!',
'logout' => 'Cerrar sesión', 'wrong_confirmation' => 'Código de confirmación incorrecto.',
'sign_up_to_save' => 'Registrate para guardar tu trabajo', 'password_forgot' => 'La información sobre el cambio de tu contraseña se ha enviado a tu dirección de correo electrónico.',
'agree_to_terms' =>'Estoy de acuerdo con los términos de Invoice Ninja :terms', 'password_reset' => 'Tu contraseña se ha cambiado con éxito.',
'terms_of_service' => 'Términos de servicio', 'wrong_password_reset' => 'Contraseña no válida. Inténtalo de nuevo',
'email_taken' => 'Esta dirección de correo electrónico ya se ha registrado', ),
'working' => 'Procesando',
'success' => 'Éxito',
'success_message' => 'Te has registrado con éxito. Por favor, haz clic en el enlace del correo de confirmación para verificar tu dirección de correo electrónico.',
'erase_data' => 'Esta acción eliminará todos tus datos de forma permanente.',
'password' => 'Contraseña',
'pro_plan_product' => 'Plan Pro', // Pro Plan
'pro_plan_description' => 'Un año de inscripción al Plan Pro de Invoice Ninja.', 'pro_plan' => [
'pro_plan_success' => '¡Gracias por unirte a Invoice Ninja! Al realizar el pago de tu factura, se iniciara tu PLAN PRO.', 'remove_logo' => ':link haz click para eliminar el logo de Invoice Ninja',
'unsaved_changes' => 'Tienes cambios no guardados', 'remove_logo_link' => 'Haz click aquí',
'custom_fields' => 'Campos a medida', ],
'company_fields' => 'Campos de la empresa', 'logout' => 'Cerrar sesión',
'client_fields' => 'Campos del cliente', 'sign_up_to_save' => 'Registrate para guardar tu trabajo',
'field_label' => 'Etiqueta del campo', 'agree_to_terms' => 'Estoy de acuerdo con los términos de Invoice Ninja :terms',
'field_value' => 'Valor del campo', 'terms_of_service' => 'Términos de servicio',
'edit' => 'Editar', 'email_taken' => 'Esta dirección de correo electrónico ya se ha registrado',
'view_as_recipient' => 'Ver como destinitario', 'working' => 'Procesando',
'success' => 'Éxito',
'success_message' => 'Te has registrado con éxito. Por favor, haz clic en el enlace del correo de confirmación para verificar tu dirección de correo electrónico.',
'erase_data' => 'Esta acción eliminará todos tus datos de forma permanente.',
'password' => 'Contraseña',
// product management 'pro_plan_product' => 'Plan Pro',
'product_library' => 'Inventario de productos', 'pro_plan_description' => 'Un año de inscripción al Plan Pro de Invoice Ninja.',
'product' => 'Producto', 'pro_plan_success' => '¡Gracias por unirte a Invoice Ninja! Al realizar el pago de tu factura, se iniciara tu PLAN PRO.',
'products' => 'Productos', 'unsaved_changes' => 'Tienes cambios no guardados',
'fill_products' => 'Auto-rellenar productos', 'custom_fields' => 'Campos a medida',
'fill_products_help' => 'Seleccionar un producto automáticamente <b>configurará la descripción y coste</b>', 'company_fields' => 'Campos de la empresa',
'update_products' => 'Auto-actualizar productos', 'client_fields' => 'Campos del cliente',
'update_products_help' => 'Actualizar una factura automáticamente <b>actualizará los productos</b>', 'field_label' => 'Etiqueta del campo',
'create_product' => 'Crear Producto', 'field_value' => 'Valor del campo',
'edit_product' => 'Editar Producto', 'edit' => 'Editar',
'archive_product' => 'Archivar Producto', 'view_as_recipient' => 'Ver como destinitario',
'updated_product' => 'Producto actualizado con éxito',
'created_product' => 'Producto creado con éxito',
'archived_product' => 'Producto archivado con éxito',
'pro_plan_custom_fields' => ':link haz click para para activar campos a medida',
'advanced_settings' => 'Configuración Avanzada',
'pro_plan_advanced_settings' => ':link haz click para para activar la configuración avanzada',
'invoice_design' => 'Diseño de factura',
'specify_colors' => 'Especificar colores',
'specify_colors_label' => 'Seleccionar los colores para usar en las facturas',
'chart_builder' => 'Constructor de graficos',
'ninja_email_footer' => 'Usa :site para facturar a tus clientes y recibir pagos de forma gratuita!',
'go_pro' => 'Hazte Pro',
// Quotes // product management
'quote' => 'Presupuesto', 'product_library' => 'Inventario de productos',
'quotes' => 'Presupuestos', 'product' => 'Producto',
'quote_number' => 'Numero de Presupuesto', 'products' => 'Productos',
'quote_number_short' => 'Presupuesto Nº ', 'fill_products' => 'Auto-rellenar productos',
'quote_date' => 'Fecha Presupuesto', 'fill_products_help' => 'Seleccionar un producto automáticamente <b>configurará la descripción y coste</b>',
'quote_total' => 'Total Presupuestado', 'update_products' => 'Auto-actualizar productos',
'your_quote' => 'Su Presupuesto', 'update_products_help' => 'Actualizar una factura automáticamente <b>actualizará los productos</b>',
'total' => 'Total', 'create_product' => 'Crear Producto',
'clone' => 'Clonar', 'edit_product' => 'Editar Producto',
'archive_product' => 'Archivar Producto',
'updated_product' => 'Producto actualizado con éxito',
'created_product' => 'Producto creado con éxito',
'archived_product' => 'Producto archivado con éxito',
'pro_plan_custom_fields' => ':link haz click para para activar campos a medida',
'advanced_settings' => 'Configuración Avanzada',
'pro_plan_advanced_settings' => ':link haz click para para activar la configuración avanzada',
'invoice_design' => 'Diseño de factura',
'specify_colors' => 'Especificar colores',
'specify_colors_label' => 'Seleccionar los colores para usar en las facturas',
'chart_builder' => 'Constructor de graficos',
'ninja_email_footer' => 'Usa :site para facturar a tus clientes y recibir pagos de forma gratuita!',
'go_pro' => 'Hazte Pro',
'new_quote' => 'Nuevo Presupuesto', // Quotes
'create_quote' => 'Crear Presupuesto', 'quote' => 'Presupuesto',
'edit_quote' => 'Editar Presupuesto', 'quotes' => 'Presupuestos',
'archive_quote' => 'Archivar Presupuesto', 'quote_number' => 'Numero de Presupuesto',
'delete_quote' => 'Eliminar Presupuesto', 'quote_number_short' => 'Presupuesto Nº ',
'save_quote' => 'Guardar Presupuesto', 'quote_date' => 'Fecha Presupuesto',
'email_quote' => 'Enviar Presupuesto', 'quote_total' => 'Total Presupuestado',
'clone_quote' => 'Clonar Presupuesto', 'your_quote' => 'Su Presupuesto',
'convert_to_invoice' => 'Convertir a Factura', 'total' => 'Total',
'view_invoice' => 'Ver Factura', 'clone' => 'Clonar',
'view_quote' => 'Ver Presupuesto',
'view_client' => 'Ver Cliente',
'updated_quote' => 'Presupuesto actualizado con éxito', 'new_quote' => 'Nuevo Presupuesto',
'created_quote' => 'Presupuesto creado con éxito', 'create_quote' => 'Crear Presupuesto',
'cloned_quote' => 'Presupuesto clonado con éxito', 'edit_quote' => 'Editar Presupuesto',
'emailed_quote' => 'Presupuesto enviado con éxito', 'archive_quote' => 'Archivar Presupuesto',
'archived_quote' => 'Presupuesto archivado con éxito', 'delete_quote' => 'Eliminar Presupuesto',
'archived_quotes' => ':count Presupuestos archivados con exito', 'save_quote' => 'Guardar Presupuesto',
'deleted_quote' => 'Presupuesto eliminado con éxito', 'email_quote' => 'Enviar Presupuesto',
'deleted_quotes' => ':count Presupuestos eliminados con exito', 'clone_quote' => 'Clonar Presupuesto',
'converted_to_invoice' => 'Presupuesto convertido a factura con éxito', 'convert_to_invoice' => 'Convertir a Factura',
'view_invoice' => 'Ver Factura',
'view_quote' => 'Ver Presupuesto',
'view_client' => 'Ver Cliente',
'quote_subject' => 'Nuevo Presupuesto de :account', 'updated_quote' => 'Presupuesto actualizado con éxito',
'quote_message' => 'Para ver el presupuesto por un importe de :amount, haga click en el enlace de abajo.', 'created_quote' => 'Presupuesto creado con éxito',
'quote_link_message' => 'Para ver su presupuesto haga click en el enlace de abajo:', 'cloned_quote' => 'Presupuesto clonado con éxito',
'notification_quote_sent_subject' => 'El presupuesto :invoice enviado al cliente :client', 'emailed_quote' => 'Presupuesto enviado con éxito',
'notification_quote_viewed_subject' => 'El presupuesto :invoice fue visto por el cliente :client', 'archived_quote' => 'Presupuesto archivado con éxito',
'notification_quote_sent' => 'El presupuesto :invoice por un valor de :amount, ha sido enviado al cliente :client.', 'archived_quotes' => ':count Presupuestos archivados con exito',
'notification_quote_viewed' => 'El presupuesto :invoice por un valor de :amount ha sido visto por el cliente :client.', 'deleted_quote' => 'Presupuesto eliminado con éxito',
'session_expired' => 'Su sesión ha caducado.', 'deleted_quotes' => ':count Presupuestos eliminados con exito',
'invoice_fields' => 'Campos de Factura', 'converted_to_invoice' => 'Presupuesto convertido a factura con éxito',
'invoice_options' => 'Opciones de Factura',
'hide_quantity' => 'Ocultar Cantidad',
'hide_quantity_help' => 'Si las cantidades de tus partidas siempre son 1, entonces puedes organizar tus facturas mejor al no mostrar este campo.',
'hide_paid_to_date' => 'Ocultar valor pagado a la fecha',
'hide_paid_to_date_help' => 'Solo mostrar la opción “Pagado a la fecha” en sus facturas cuando se ha recibido un pago.',
'charge_taxes' => 'Cargar Impuestos',
'user_management' => 'Gestión de Usuario',
'add_user' => 'Añadir Usuario',
'send_invite' => 'Enviar Invitación', //Context for its use
'sent_invite' => 'Invitación enviada con éxito',
'updated_user' => 'Usario actualizado con éxito',
'invitation_message' => ':invitor te ha invitado a unirte a su cuenta en G-Factura.',
'register_to_add_user' => 'Regístrate para añadir usarios',
'user_state' => 'Estado',
'edit_user' => 'Editar Usario',
'delete_user' => 'Eliminar Usario',
'active' => 'Activo',
'pending' => 'Pendiente',
'deleted_user' => 'Usario eliminado con éxito',
'limit_users' => 'Lo sentimos, esta acción excederá el límite de ' . MAX_NUM_USERS . ' usarios',
'confirm_email_invoice' => '¿Estás seguro que quieres enviar esta factura?',
'confirm_email_quote' => '¿Estás seguro que quieres enviar este presupuesto?',
'confirm_recurring_email_invoice' => 'Se ha marcado esta factura como recurrente, estás seguro que quieres enviar esta factura?',
'cancel_account' => 'Cancelar Cuenta',
'cancel_account_message' => 'AVISO: Esta acción eliminará todos tus datos de forma permanente.',
'go_back' => 'Atrás',
'data_visualizations' => 'Visualización de Datos',
'sample_data' => 'Datos de Ejemplo',
'hide' => 'Ocultar',
'new_version_available' => 'Una nueva versión de :releases_link disponible. Estás utilizando la versión :user_version, la última versión es :latest_version',
'invoice_settings' => 'Configuración de Facturas',
'invoice_number_prefix' => 'Prefijo de Facturación',
'invoice_number_counter' => 'Numeración de Facturación',
'quote_number_prefix' => 'Prejijo de Presupuesto',
'quote_number_counter' => 'Numeración de Presupuestos',
'share_invoice_counter' => 'Compartir la numeración para presupuesto y facturación',
'invoice_issued_to' => 'Factura emitida a',
'invalid_counter' => 'Para evitar posibles conflictos, por favor crea un prefijo de facturación y de presupuesto.',
'mark_sent' => 'Marcar como enviado',
'quote_subject' => 'Nuevo Presupuesto de :account',
'quote_message' => 'Para ver el presupuesto por un importe de :amount, haga click en el enlace de abajo.',
'quote_link_message' => 'Para ver su presupuesto haga click en el enlace de abajo:',
'notification_quote_sent_subject' => 'El presupuesto :invoice enviado al cliente :client',
'notification_quote_viewed_subject' => 'El presupuesto :invoice fue visto por el cliente :client',
'notification_quote_sent' => 'El presupuesto :invoice por un valor de :amount, ha sido enviado al cliente :client.',
'notification_quote_viewed' => 'El presupuesto :invoice por un valor de :amount ha sido visto por el cliente :client.',
'session_expired' => 'Su sesión ha caducado.',
'invoice_fields' => 'Campos de Factura',
'invoice_options' => 'Opciones de Factura',
'hide_quantity' => 'Ocultar Cantidad',
'hide_quantity_help' => 'Si las cantidades de tus partidas siempre son 1, entonces puedes organizar tus facturas mejor al no mostrar este campo.',
'hide_paid_to_date' => 'Ocultar valor pagado a la fecha',
'hide_paid_to_date_help' => 'Solo mostrar la opción “Pagado a la fecha” en sus facturas cuando se ha recibido un pago.',
'charge_taxes' => 'Cargar Impuestos',
'user_management' => 'Gestión de Usuario',
'add_user' => 'Añadir Usuario',
'send_invite' => 'Enviar Invitación', //Context for its use
'sent_invite' => 'Invitación enviada con éxito',
'updated_user' => 'Usario actualizado con éxito',
'invitation_message' => ':invitor te ha invitado a unirte a su cuenta en G-Factura.',
'register_to_add_user' => 'Regístrate para añadir usarios',
'user_state' => 'Estado',
'edit_user' => 'Editar Usario',
'delete_user' => 'Eliminar Usario',
'active' => 'Activo',
'pending' => 'Pendiente',
'deleted_user' => 'Usario eliminado con éxito',
'limit_users' => 'Lo sentimos, esta acción excederá el límite de '.MAX_NUM_USERS.' usarios',
'confirm_email_invoice' => '¿Estás seguro que quieres enviar esta factura?',
'confirm_email_quote' => '¿Estás seguro que quieres enviar este presupuesto?',
'confirm_recurring_email_invoice' => 'Se ha marcado esta factura como recurrente, estás seguro que quieres enviar esta factura?',
'cancel_account' => 'Cancelar Cuenta',
'cancel_account_message' => 'AVISO: Esta acción eliminará todos tus datos de forma permanente.',
'go_back' => 'Atrás',
'data_visualizations' => 'Visualización de Datos',
'sample_data' => 'Datos de Ejemplo',
'hide' => 'Ocultar',
'new_version_available' => 'Una nueva versión de :releases_link disponible. Estás utilizando la versión :user_version, la última versión es :latest_version',
'invoice_settings' => 'Configuración de Facturas',
'invoice_number_prefix' => 'Prefijo de Facturación',
'invoice_number_counter' => 'Numeración de Facturación',
'quote_number_prefix' => 'Prejijo de Presupuesto',
'quote_number_counter' => 'Numeración de Presupuestos',
'share_invoice_counter' => 'Compartir la numeración para presupuesto y facturación',
'invoice_issued_to' => 'Factura emitida a',
'invalid_counter' => 'Para evitar posibles conflictos, por favor crea un prefijo de facturación y de presupuesto.',
'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.',
'gateway_help_23' => 'Nota: utilizar su clave de API secreta, no es su clave de API publica.', 'gateway_help_23' => 'Nota: utilizar su clave de API secreta, no es su clave de API publica.',
'gateway_help_27' => ':link para registrarse en TwoCheckout.', 'gateway_help_27' => ':link para registrarse en TwoCheckout.',
'more_designs' => 'Más diseños', 'more_designs' => 'Más diseños',
'more_designs_title' => 'Diseños adicionales para factura', 'more_designs_title' => 'Diseños adicionales para factura',
'more_designs_cloud_header' => 'Pase a Pro para añadir más diseños de facturas', 'more_designs_cloud_header' => 'Pase a Pro para añadir más diseños de facturas',
'more_designs_cloud_text' => '', 'more_designs_cloud_text' => '',
'more_designs_self_host_header' => 'Obtenga 6 diseños más para facturas por sólo '.INVOICE_DESIGNS_PRICE, // comprobar 'more_designs_self_host_header' => 'Obtenga 6 diseños más para facturas por sólo '.INVOICE_DESIGNS_PRICE, // comprobar
'more_designs_self_host_text' => '', 'more_designs_self_host_text' => '',
'buy' => 'Comprar', 'buy' => 'Comprar',
'bought_designs' => 'Añadidos con exito los diseños de factura', 'bought_designs' => 'Añadidos con exito los diseños de factura',
'sent' => 'Enviado', 'sent' => 'Enviado',
'timesheets' => 'Parte de Horas', 'timesheets' => 'Parte de Horas',
'payment_title' => 'Introduzca su dirección de facturación y la infomración de su tarjeta de crédito', 'payment_title' => 'Introduzca su dirección de facturación y la infomración de su tarjeta de crédito',
'payment_cvv' => '*los tres últimos dígitos de la parte posterior de su tarjeta', 'payment_cvv' => '*los tres últimos dígitos de la parte posterior de su tarjeta',
'payment_footer1' => '*La dirección de facturación debe coincidir con la de la tarjeta de crédito.', 'payment_footer1' => '*La dirección de facturación debe coincidir con la de la tarjeta de crédito.',
'payment_footer2' => '*Por favor, haga clic en "Pagar ahora" sólo una vez - esta operación puede tardar hasta 1 minuto en procesarse.', 'payment_footer2' => '*Por favor, haga clic en "Pagar ahora" sólo una vez - esta operación puede tardar hasta 1 minuto en procesarse.',
'vat_number' => 'NIF/CIF', 'vat_number' => 'NIF/CIF',
'id_number' => 'Número de Identificación', 'id_number' => 'Número de Identificación',
'white_label_link' => 'Marca Blanca" ', 'white_label_link' => 'Marca Blanca" ',
'white_label_text' => 'Obtener una licencia de marca blanca por'.WHITE_LABEL_PRICE.' para quitar la marca Invoice Ninja de la parte superior de las páginas del cliente.', // comprobar 'white_label_text' => 'Obtener una licencia de marca blanca por'.WHITE_LABEL_PRICE.' para quitar la marca Invoice Ninja de la parte superior de las páginas del cliente.', // comprobar
'white_label_link' => 'Marca Blanca" ', 'white_label_link' => 'Marca Blanca" ',
'bought_white_label' => 'Se ha conseguido con exito la licencia de Marca Blanca', 'bought_white_label' => 'Se ha conseguido con exito la licencia de Marca Blanca',
'white_labeled' => 'Marca Blanca', 'white_labeled' => 'Marca Blanca',
'restore' => 'Restaurar', 'restore' => 'Restaurar',
'restore_invoice' => 'Restaurar Factura', 'restore_invoice' => 'Restaurar Factura',
'restore_quote' => 'Restaurar Presupuesto', 'restore_quote' => 'Restaurar Presupuesto',
'restore_client' => 'Restaurar Cliente', 'restore_client' => 'Restaurar Cliente',
'restore_credit' => 'Restaurar Pendiente', 'restore_credit' => 'Restaurar Pendiente',
'restore_payment' => 'Restaurar Pago', 'restore_payment' => 'Restaurar Pago',
'restored_invoice' => 'Factura restaurada con éxito', 'restored_invoice' => 'Factura restaurada con éxito',
@ -479,7 +478,7 @@ return array(
'restored_client' => 'Cliente restaurada con éxito', 'restored_client' => 'Cliente restaurada con éxito',
'restored_payment' => 'Pago restaurada con éxito', 'restored_payment' => 'Pago restaurada con éxito',
'restored_credit' => 'Pendiente restaurada con éxito', 'restored_credit' => 'Pendiente restaurada con éxito',
'reason_for_canceling' => 'Ayudenos a mejorar nuestro sitio diciendonos porque se va, Gracias', 'reason_for_canceling' => 'Ayudenos a mejorar nuestro sitio diciendonos porque se va, Gracias',
'discount_percent' => 'Porcentaje', 'discount_percent' => 'Porcentaje',
'discount_amount' => 'Cantidad', 'discount_amount' => 'Cantidad',
@ -503,7 +502,7 @@ return array(
'payment_email' => 'Email de Pagos', 'payment_email' => 'Email de Pagos',
'quote_email' => 'Email de Presupuestos', 'quote_email' => 'Email de Presupuestos',
'reset_all' => 'Restablecer Todos', 'reset_all' => 'Restablecer Todos',
'approve' => 'Aprobar', 'approve' => 'Aprobar',
'token_billing_type_id' => 'Token Billing', //¿? 'token_billing_type_id' => 'Token Billing', //¿?
'token_billing_help' => 'Permite almacenar tarjetas de crédito para posteriormente realizarles el cobro.', 'token_billing_help' => 'Permite almacenar tarjetas de crédito para posteriormente realizarles el cobro.',
@ -604,7 +603,7 @@ return array(
'view_documentation' => 'View Documentation', 'view_documentation' => 'View Documentation',
'app_title' => 'Free Open-Source Online Invoicing', 'app_title' => 'Free Open-Source Online Invoicing',
'app_description' => 'Invoice Ninja is a free, open-source solution for invoicing and billing customers. With Invoice Ninja, you can easily build and send beautiful invoices from any device that has access to the web. Your clients can print your invoices, download them as pdf files, and even pay you online from within the system.', 'app_description' => 'Invoice Ninja is a free, open-source solution for invoicing and billing customers. With Invoice Ninja, you can easily build and send beautiful invoices from any device that has access to the web. Your clients can print your invoices, download them as pdf files, and even pay you online from within the system.',
'rows' => 'rows', 'rows' => 'rows',
'www' => 'www', 'www' => 'www',
'logo' => 'Logo', 'logo' => 'Logo',
@ -745,14 +744,14 @@ 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',
'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>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>
<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>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>To access a child property using dot notation. For example to show the client name you could use <code>$client.nameValue</code>.</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>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>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>',
'invoice_due_date' => 'Due Date', 'invoice_due_date' => 'Due Date',
'quote_due_date' => 'Valid Until', 'quote_due_date' => 'Valid Until',
@ -766,7 +765,7 @@ return array(
'status_partial' => 'Partial', 'status_partial' => 'Partial',
'status_paid' => 'Paid', 'status_paid' => 'Paid',
'show_line_item_tax' => 'Display <b>line item taxes</b> inline', 'show_line_item_tax' => 'Display <b>line item taxes</b> inline',
'iframe_url' => 'Website', 'iframe_url' => 'Website',
'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.',
@ -805,9 +804,9 @@ return array(
'invoice_charges' => 'Invoice Charges', 'invoice_charges' => 'Invoice Charges',
'invitation_status' => [ 'invitation_status' => [
'sent' => 'Email Sent', 'sent' => 'Email Sent',
'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',
@ -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',
@ -740,12 +740,12 @@ return array(
'recent_payments' => 'Recent Payments', 'recent_payments' => 'Recent Payments',
'outstanding' => 'Outstanding', 'outstanding' => 'Outstanding',
'manage_companies' => 'Manage Companies', 'manage_companies' => 'Manage Companies',
'total_revenue' => 'Total Revenue', 'total_revenue' => 'Total Revenue',
'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">
@ -27,10 +24,7 @@
@stop @stop
@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,8 +37,8 @@
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];
var val = $('#labels_' + field).val(); var val = $('#labels_' + field).val();
@ -51,9 +48,9 @@
if (val) { if (val) {
invoiceLabels[field] = val; invoiceLabels[field] = val;
} }
} }
generatePDF(invoice, getDesignJavascript(), true, cb); generatePDF(invoice, getDesignJavascript(), true, cb);
} }
$(function() { $(function() {
@ -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">
<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">
@if (!Utils::isPro() || \App\Models\InvoiceDesign::count() == COUNT_FREE_DESIGNS_SELF_HOST)
{!! Former::select('invoice_design_id')->style('display:inline')->fromQuery($invoiceDesigns, 'name', 'id')->addOption(trans('texts.more_designs') . '...', '-1') !!}
@else
{!! Former::select('invoice_design_id')->style('display:inline')->fromQuery($invoiceDesigns, 'name', 'id') !!}
@endif
{!! Former::text('font_size')->type('number')->min('0')->step('1') !!}
{!! Former::text('primary_color') !!}
{!! Former::text('secondary_color') !!}
</div>
</div>
<div role="tabpanel" class="tab-pane" id="invoiceLabels">
<div class="panel-body">
@if (!Utils::isPro() || \App\Models\InvoiceDesign::count() == COUNT_FREE_DESIGNS_SELF_HOST) {!! Former::text('labels_item')->label(trans('texts.item')) !!}
{!! Former::select('invoice_design_id')->style('display:inline;width:120px')->fromQuery($invoiceDesigns, 'name', 'id')->addOption(trans('texts.more_designs') . '...', '-1') !!} {!! Former::text('labels_description')->label(trans('texts.description')) !!}
@else {!! Former::text('labels_unit_cost')->label(trans('texts.unit_cost')) !!}
{!! Former::select('invoice_design_id')->style('display:inline;width:120px')->fromQuery($invoiceDesigns, 'name', 'id') !!} {!! Former::text('labels_quantity')->label(trans('texts.quantity')) !!}
@endif {!! Former::text('labels_line_total')->label(trans('texts.line_total')) !!}
{!! Former::text('font_size')->type('number')->min('0')->step('1')->style('width:120px') !!}
{!! Former::text('primary_color') !!}
{!! Former::text('secondary_color') !!}
{!! Former::actions( </div>
Button::primary(trans('texts.customize_design'))->small()->asLinkTo(URL::to('/company/advanced_settings/customize_design')) </div>
) !!} <div role="tabpanel" class="tab-pane" id="invoiceOptions">
<div class="panel-body">
</div> {!! Former::checkbox('hide_quantity')->text(trans('texts.hide_quantity_help')) !!}
</div> {!! Former::checkbox('hide_paid_to_date')->text(trans('texts.hide_paid_to_date_help')) !!}
<div class="panel panel-default"> </div>
<div class="panel-heading"> </div>
<h3 class="panel-title">{!! trans('texts.invoice_labels') !!}</h3> </div>
</div>
<div class="panel-body">
{!! Former::text('labels_item')->label(trans('texts.item')) !!}
{!! Former::text('labels_description')->label(trans('texts.description')) !!}
{!! Former::text('labels_unit_cost')->label(trans('texts.unit_cost')) !!}
{!! Former::text('labels_quantity')->label(trans('texts.quantity')) !!}
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{!! trans('texts.invoice_options') !!}</h3>
</div>
<div class="panel-body">
{!! Former::checkbox('hide_quantity')->text(trans('texts.hide_quantity_help')) !!}
{!! Former::checkbox('hide_paid_to_date')->text(trans('texts.hide_paid_to_date_help')) !!}
</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,27 +32,30 @@
<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::inline_radios('custom_invoice_link') {{-- Former::select('recurring_hour')->options($recurringHours) --}}
->onchange('onCustomLinkChange()')
->radios([ {!! Former::inline_radios('custom_invoice_link')
trans('texts.subdomain') => ['value' => 'subdomain', 'name' => 'custom_link'], ->onchange('onCustomLinkChange()')
trans('texts.website') => ['value' => 'website', 'name' => 'custom_link'], ->radios([
])->check($account->iframe_url ? 'website' : 'subdomain') !!} trans('texts.subdomain') => ['value' => 'subdomain', 'name' => 'custom_link'],
{{ Former::setOption('capitalize_translations', false) }} trans('texts.website') => ['value' => 'website', 'name' => 'custom_link'],
{!! Former::text('subdomain') ])->check($account->iframe_url ? 'website' : 'subdomain') !!}
->placeholder(trans('texts.www')) {{ Former::setOption('capitalize_translations', false) }}
->onchange('onSubdomainChange()')
->addGroupClass('subdomain') {!! Former::text('subdomain')
->label(' ') !!} ->placeholder(trans('texts.www'))
{!! Former::text('iframe_url') ->onchange('onSubdomainChange()')
->placeholder('http://www.example.com/invoice') ->addGroupClass('subdomain')
->appendIcon('question-sign') ->label(' ') !!}
->addGroupClass('iframe_url')
->label(' ') !!} {!! Former::text('iframe_url')
@endif ->placeholder('http://www.example.com/invoice')
->appendIcon('question-sign')
->addGroupClass('iframe_url')
->label(' ') !!}
</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('');
@ -230,7 +334,7 @@
$('#iframe_url').change(function() { $('#iframe_url').change(function() {
$('#subdomain').val(''); $('#subdomain').val('');
}); });
}); });
</script> </script>

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)) }}
@ -24,30 +26,29 @@
<!-- <!--
{!! Former::legend(trans('texts.site_updates')) !!} {!! Former::legend(trans('texts.site_updates')) !!}
<div class="form-group"> <div class="form-group">
<label for="invoice_terms" class="control-label col-lg-4 col-sm-4"></label> <label for="invoice_terms" class="control-label col-lg-4 col-sm-4"></label>
<div class="col-lg-8 col-sm-8"> <div class="col-lg-8 col-sm-8">
<div id="fb-root"></div> <div id="fb-root"></div>
<script>(function(d, s, id) { <script>(function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0]; var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return; if (d.getElementById(id)) return;
js = d.createElement(s); js.id = id; js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/en_US/all.js#xfbml=1&appId=635126583203143"; js.src = "//connect.facebook.net/en_US/all.js#xfbml=1&appId=635126583203143";
fjs.parentNode.insertBefore(js, fjs); fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));</script> }(document, 'script', 'facebook-jssdk'));</script>
<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') !!}

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