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_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
/node_modules
/.DS_Store
/Thumbs.db
Thumbs.db
/.env
/.env.development.php
/.env.php

View File

@ -130,6 +130,16 @@ module.exports = function(grunt) {
options: {
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,
'account_id' => $client->account_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,
'balance' => $client->actual_balance,
]);

View File

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

View File

@ -33,15 +33,22 @@ class SendRecurringInvoices extends Command
$today = new DateTime();
$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');
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'));
$invoice = $this->invoiceRepo->createRecurringInvoice($recurInvoice);
if ($invoice && !$invoice->isPaid()) {
$recurInvoice->account->loadLocalizationSettings($invoice->client);
$this->info('Sending Invoice');
$this->mailer->sendInvoice($invoice);
}
}

View File

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

View File

@ -15,7 +15,6 @@ use Cache;
use Response;
use parseCSV;
use Request;
use App\Models\Affiliate;
use App\Models\License;
use App\Models\User;
@ -37,6 +36,7 @@ use App\Models\Gateway;
use App\Models\Timezone;
use App\Models\Industry;
use App\Models\InvoiceDesign;
use App\Models\TaxRate;
use App\Ninja\Repositories\AccountRepository;
use App\Ninja\Mailers\UserMailer;
use App\Ninja\Mailers\ContactMailer;
@ -129,9 +129,9 @@ class AccountController extends BaseController
Session::put("show_trash:{$entityType}", $visible == 'true');
if ($entityType == 'user') {
return Redirect::to('company/'.ACCOUNT_ADVANCED_SETTINGS.'/'.ACCOUNT_USER_MANAGEMENT);
return Redirect::to('settings/'.ACCOUNT_USER_MANAGEMENT);
} elseif ($entityType == 'token') {
return Redirect::to('company/'.ACCOUNT_ADVANCED_SETTINGS.'/'.ACCOUNT_TOKEN_MANAGEMENT);
return Redirect::to('settings/'.ACCOUNT_API_TOKENS);
} else {
return Redirect::to("{$entityType}s");
}
@ -143,168 +143,260 @@ class AccountController extends BaseController
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 = [];
foreach (AuthService::$providers as $provider) {
$oauthLoginUrls[] = ['label' => $provider, 'url' => '/auth/' . strtolower($provider)];
}
$data = [
'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);
if ($section == ACCOUNT_COMPANY_DETAILS) {
return self::showCompanyDetails();
} elseif ($section == ACCOUNT_USER_DETAILS) {
return self::showUserDetails();
} elseif ($section == ACCOUNT_LOCALIZATION) {
return self::showLocalization();
} elseif ($section == ACCOUNT_PAYMENTS) {
$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')
]);
}
} elseif ($section == ACCOUNT_NOTIFICATIONS) {
$data = [
'account' => Account::with('users')->findOrFail(Auth::user()->account_id),
'title' => trans('texts.notifications'),
];
return View::make('accounts.notifications', $data);
return self::showOnlinePayments();
} elseif ($section == ACCOUNT_INVOICE_SETTINGS) {
return self::showInvoiceSettings();
} elseif ($section == ACCOUNT_IMPORT_EXPORT) {
return View::make('accounts.import_export', ['title' => trans('texts.import_export')]);
} elseif ($section == ACCOUNT_ADVANCED_SETTINGS) {
$account = Auth::user()->account->load('country');
} elseif ($section == ACCOUNT_INVOICE_DESIGN || $section == ACCOUNT_CUSTOMIZE_DESIGN) {
return self::showInvoiceDesign($section);
} elseif ($section === ACCOUNT_TEMPLATES_AND_REMINDERS) {
return self::showTemplates();
} elseif ($section === ACCOUNT_PRODUCTS) {
return self::showProducts();
} elseif ($section === ACCOUNT_TAX_RATES) {
return self::showTaxRates();
} else {
$data = [
'account' => $account,
'feature' => $subSection,
'title' => trans('texts.invoice_settings'),
'account' => Account::with('users')->findOrFail(Auth::user()->account_id),
'title' => trans("texts.{$section}"),
'section' => $section
];
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);
return View::make("accounts.{$section}", $data);
}
}
public function doSection($section = ACCOUNT_DETAILS, $subSection = false)
private function showInvoiceSettings()
{
if ($section == ACCOUNT_DETAILS) {
return AccountController::saveDetails();
} 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_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();
$account = Auth::user()->account;
$recurringHours = [];
for ($i=0; $i<24; $i++) {
if ($account->military_time) {
$format = 'H:i';
} else {
$format = 'g:i a';
}
} 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();
} elseif ($section === ACCOUNT_TAX_RATES) {
return AccountController::saveTaxRates();
}
}
@ -318,7 +410,7 @@ class AccountController extends BaseController
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()
@ -351,7 +443,21 @@ class AccountController extends BaseController
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()
@ -363,14 +469,18 @@ class AccountController extends BaseController
$account->save();
Session::flash('message', trans('texts.updated_settings'));
return Redirect::to('company/products');
return Redirect::to('settings/' . ACCOUNT_PRODUCTS);
}
private function saveInvoiceSettings()
{
if (Auth::user()->account->isPro()) {
$rules = [];
$rules = [
'invoice_number_pattern' => 'has_counter',
'quote_number_pattern' => 'has_counter',
];
$user = Auth::user();
$iframeURL = preg_replace('/[^a-zA-Z0-9_\-\:\/\.]/', '', substr(strtolower(Input::get('iframe_url')), 0, MAX_IFRAME_URL_LENGTH));
$iframeURL = rtrim($iframeURL, "/");
@ -386,7 +496,7 @@ class AccountController extends BaseController
$validator = Validator::make(Input::all(), $rules);
if ($validator->fails()) {
return Redirect::to('company/details')
return Redirect::to('settings/' . ACCOUNT_INVOICE_SETTINGS)
->withErrors($validator)
->withInput();
} 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_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->quote_number_prefix = Input::get('quote_number_prefix');
$account->share_counter = Input::get('share_counter') ? 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) {
$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) {
Session::flash('error', trans('texts.invalid_counter'));
return Redirect::to('company/advanced_settings/invoice_settings')->withInput();
return Redirect::to('settings/' . ACCOUNT_INVOICE_SETTINGS)->withInput();
} else {
$account->save();
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()
@ -447,7 +574,7 @@ class AccountController extends BaseController
}
$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}"));
}
$account->invoice_labels = json_encode($labels);
@ -457,7 +584,7 @@ class AccountController extends BaseController
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()
@ -568,7 +695,7 @@ class AccountController extends BaseController
if ($file == null) {
Session::flash('error', trans('texts.select_file'));
return Redirect::to('company/import_export');
return Redirect::to('settings/' . ACCOUNT_IMPORT_EXPORT);
}
$name = $file->getRealPath();
@ -582,7 +709,7 @@ class AccountController extends BaseController
$message = trans('texts.limit_clients', ['count' => Auth::user()->getMaxNumClients()]);
Session::flash('error', $message);
return Redirect::to('company/import_export');
return Redirect::to('settings/' . ACCOUNT_IMPORT_EXPORT);
}
Session::put('data', $csv->data);
@ -680,26 +807,20 @@ class AccountController extends BaseController
Session::flash('message', trans('texts.updated_settings'));
return Redirect::to('company/notifications');
return Redirect::to('settings/' . ACCOUNT_NOTIFICATIONS);
}
private function saveDetails()
{
$rules = array(
'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);
if ($validator->fails()) {
return Redirect::to('company/details')
return Redirect::to('settings/' . ACCOUNT_COMPANY_DETAILS)
->withErrors($validator)
->withInput();
} else {
@ -717,29 +838,7 @@ class AccountController extends BaseController
$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->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();
$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 */
if ($file = Input::file('logo')) {
@ -770,10 +869,61 @@ class AccountController extends BaseController
Event::fire(new UserSettingsChanged());
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()
{
File::delete('logo/'.Auth::user()->account->account_key.'.jpg');
@ -781,7 +931,7 @@ class AccountController extends BaseController
Session::flash('message', trans('texts.removed_logo'));
return Redirect::to('company/details');
return Redirect::to('settings/' . ACCOUNT_COMPANY_DETAILS);
}
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);
}
$user = Auth::user();
$account = Auth::user()->account;
\Log::info("Canceled Account: {$account->name} - {$user->email}");
$this->accountRepo->unlinkAccount($account);
$account->forceDelete();
@ -879,6 +1032,26 @@ class AccountController extends BaseController
$user = Auth::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
{
public function index()
{
return Redirect::to('settings/' . ACCOUNT_PAYMENTS);
}
public function getDatatable()
{
$query = DB::table('account_gateways')
@ -159,7 +164,6 @@ class AccountGatewayController extends BaseController
'gateways' => $gateways,
'creditCardTypes' => $creditCards,
'tokenBillingOptions' => $tokenBillingOptions,
'showBreadcrumbs' => false,
'countGateways' => count($currentGateways)
];
}
@ -173,7 +177,7 @@ class AccountGatewayController extends BaseController
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');
$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');
$dbType = $database['default'];
@ -94,7 +94,7 @@ class AppController extends BaseController
"MAIL_USERNAME={$mail['username']}\n".
"MAIL_FROM_NAME={$mail['from']['name']}\n".
"MAIL_PASSWORD={$mail['password']}\n\n".
"#PHANTOMJS_CLOUD_KEY='a-demo-key-with-low-quota-per-ip-address'";
"PHANTOMJS_CLOUD_KEY='a-demo-key-with-low-quota-per-ip-address'";
// Write Config Settings
$fp = fopen(base_path()."/.env", 'w');

View File

@ -61,7 +61,7 @@ class AuthController extends Controller {
$this->accountRepo->unlinkUserFromOauth(Auth::user());
Session::flash('message', trans('texts.updated_settings'));
return redirect()->to('/company/details');
return redirect()->to('/settings/' . ACCOUNT_USER_DETAILS);
}
public function getLoginWrapper()
@ -92,7 +92,7 @@ class AuthController extends Controller {
// we're linking a new account
if ($userId && Auth::user()->id != $userId) {
$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
} else {
$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->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();
$data = json_decode(Input::get('data'));

View File

@ -59,16 +59,6 @@ class InvoiceApiController extends Controller
$data = Input::all();
$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'])) {
$client = Client::scope()->whereHas('contacts', function($query) use ($data) {
$query->where('email', '=', $data['email']);
@ -95,6 +85,16 @@ class InvoiceApiController extends Controller
$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 (!isset($data['client_id']) && !isset($data['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\Repositories\InvoiceRepository;
use App\Ninja\Repositories\ClientRepository;
use App\Ninja\Repositories\TaxRateRepository;
use App\Events\InvoiceViewed;
class InvoiceController extends BaseController
@ -39,16 +38,14 @@ class InvoiceController extends BaseController
protected $mailer;
protected $invoiceRepo;
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();
$this->mailer = $mailer;
$this->invoiceRepo = $invoiceRepo;
$this->clientRepo = $clientRepo;
$this->taxRateRepo = $taxRateRepo;
}
public function index()
@ -131,13 +128,22 @@ class InvoiceController extends BaseController
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;
$client = $invoice->client;
$account = $invoice->account;
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)) {
@ -159,9 +165,7 @@ class InvoiceController extends BaseController
} else {
$invoice->invoice_design->javascript = $invoice->invoice_design->pdfmake;
}
$contact = $invitation->contact;
$contact->setVisible([
$contact = $invitation->contact; $contact->setVisible([
'first_name',
'last_name',
'email',
@ -229,6 +233,7 @@ class InvoiceController extends BaseController
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();
$entityType = $invoice->getEntityType();
@ -241,7 +246,7 @@ class InvoiceController extends BaseController
if ($clone) {
$invoice->id = null;
$invoice->invoice_number = Auth::user()->account->getNextInvoiceNumber($invoice->is_quote);
$invoice->invoice_number = $account->getNextInvoiceNumber($invoice);
$invoice->balance = $invoice->amount;
$invoice->invoice_status_id = 0;
$invoice->invoice_date = date_create()->format('Y-m-d');
@ -299,7 +304,6 @@ class InvoiceController extends BaseController
'entityType' => $entityType,
'showBreadcrumbs' => $clone,
'invoice' => $invoice,
'data' => false,
'method' => $method,
'invitationContactIds' => $contactIds,
'url' => $url,
@ -327,7 +331,7 @@ class InvoiceController extends BaseController
if ($invitation->contact_id == $contact->id) {
$contact->email_error = $invitation->email_error;
$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();
}
}
@ -342,23 +346,21 @@ class InvoiceController extends BaseController
public function create($clientPublicId = 0, $isRecurring = false)
{
$client = null;
$invoiceNumber = $isRecurring ? microtime(true) : Auth::user()->account->getNextInvoiceNumber();
$account = Auth::user()->account;
$clientId = null;
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(
'entityType' => ENTITY_INVOICE,
'invoice' => null,
'data' => Input::old('data'),
'invoiceNumber' => $invoiceNumber,
'method' => 'POST',
'url' => 'invoices',
'title' => trans('texts.new_invoice'),
'isRecurring' => $isRecurring,
'client' => $client);
$data = [
'entityType' => $invoice->getEntityType(),
'invoice' => $invoice,
'method' => 'POST',
'url' => 'invoices',
'title' => trans('texts.new_invoice'),
];
$data = array_merge($data, self::getViewModel());
return View::make('invoices.edit', $data);
@ -383,8 +385,9 @@ class InvoiceController extends BaseController
}
return [
'data' => Input::old('data'),
'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'),
'clients' => Client::scope()->with('contacts', 'country')->orderBy('name')->get(),
'taxRates' => TaxRate::scope()->orderBy('name')->get(),
@ -432,9 +435,8 @@ class InvoiceController extends BaseController
if ($errors = $this->invoiceRepo->getErrors($input->invoice)) {
Session::flash('error', trans('texts.invoice_error'));
return Redirect::to("{$entityType}s/create")
->withInput()->withErrors($errors);
$url = "{$entityType}s/" . ($publicId ?: 'create');
return Redirect::to($url)->withInput()->withErrors($errors);
} else {
$invoice = $this->saveInvoice($publicId, $input, $entityType);
$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
if ($input->invoice->client->public_id == '-1') {
$message = $message.' '.trans('texts.and_created_client');
$url = URL::to('clients/'.$input->invoice->client->public_id);
Utils::trackViewed($client->getDisplayName(), ENTITY_CLIENT, $url);
$trackUrl = URL::to('clients/'.$invoice->client->public_id);
Utils::trackViewed($invoice->client->getDisplayName(), ENTITY_CLIENT, $trackUrl);
}
if ($action == 'clone') {
@ -468,8 +470,7 @@ class InvoiceController extends BaseController
if (!Auth::user()->confirmed) {
$errorMessage = trans(Auth::user()->registered ? 'texts.confirmation_required' : 'texts.registration_required');
Session::flash('error', $errorMessage);
Session::flash('message', $message);
return Redirect::to($url);
return Redirect::to('invoices/'.$invoice->public_id.'/edit');
}
if ($invoice->is_recurring) {
@ -491,7 +492,13 @@ class InvoiceController extends BaseController
private function emailRecurringInvoice(&$invoice)
{
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
@ -509,8 +516,6 @@ class InvoiceController extends BaseController
{
$invoice = $input->invoice;
$this->taxRateRepo->save($input->tax_rates);
$clientData = (array) $invoice->client;
$client = $this->clientRepo->save($invoice->client->public_id, $clientData);
@ -518,18 +523,6 @@ class InvoiceController extends BaseController
$invoiceData['client_id'] = $client->id;
$invoice = $this->invoiceRepo->save($publicId, $invoiceData, $entityType);
$account = Auth::user()->account;
if ($account->invoice_taxes != $input->invoice_taxes
|| $account->invoice_item_taxes != $input->invoice_item_taxes
|| $account->invoice_design_id != $input->invoice->invoice_design_id
|| $account->show_item_taxes != $input->show_item_taxes) {
$account->invoice_taxes = $input->invoice_taxes;
$account->invoice_item_taxes = $input->invoice_item_taxes;
$account->invoice_design_id = $input->invoice->invoice_design_id;
$account->show_item_taxes = $input->show_item_taxes;
$account->save();
}
$client->load('contacts');
$sendInvoiceIds = [];

View File

@ -174,7 +174,8 @@ class PaymentController extends BaseController
$acceptedCreditCardTypes = $accountGateway->getCreditcardTypes();
// 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')) {
Session::reflash();
return Redirect::to('view/'.$invitationKey);
@ -418,7 +419,7 @@ class PaymentController extends BaseController
}
$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
if ($accountGateway->gateway_id == GATEWAY_STRIPE) {
@ -439,11 +440,12 @@ class PaymentController extends BaseController
if ($accountGateway->gateway_id == GATEWAY_EWAY) {
$ref = $response->getData()['AccessCode'];
$token = $response->getCardReference();
} elseif ($accountGateway->gateway_id == GATEWAY_TWO_CHECKOUT) {
$ref = $response->getData()['cart_order_id'];
} else {
$ref = $response->getTransactionReference();
}
if (!$ref) {
$this->error('No-Ref', $response->getMessage(), $accountGateway);
@ -467,7 +469,6 @@ class PaymentController extends BaseController
} elseif ($response->isRedirect()) {
$invitation->transaction_reference = $ref;
$invitation->save();
Session::put('transaction_reference', $ref);
Session::save();
$response->redirect();
@ -513,8 +514,8 @@ class PaymentController extends BaseController
}
try {
if (method_exists($gateway, 'completePurchase')) {
$details = $this->paymentService->getPaymentDetails($invitation);
if (method_exists($gateway, 'completePurchase') && !$accountGateway->isGateway(GATEWAY_TWO_CHECKOUT)) {
$details = $this->paymentService->getPaymentDetails($invitation, $accountGateway);
$response = $gateway->completePurchase($details)->send();
$ref = $response->getTransactionReference();

View File

@ -12,58 +12,81 @@ use Session;
use Redirect;
use App\Models\Product;
use App\Models\TaxRate;
class ProductController extends BaseController
{
public function index()
{
return Redirect::to('settings/' . ACCOUNT_PRODUCTS);
}
public function getDatatable()
{
$account = Auth::user()->account;
$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.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)
->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('cost', function ($model) { return Utils::formatMoney($model->cost); })
->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('products/'.$model->public_id).'/edit">'.uctrans('texts.edit_product').'</a></li>
<li class="divider"></li>
<li><a href="'.URL::to('products/'.$model->public_id).'/archive">'.uctrans('texts.archive_product').'</a></li>
</ul>
</div>';
})
->orderColumns(['cost', 'product_key', 'cost'])
->make();
$datatable = Datatable::query($query)
->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('cost', function ($model) { return Utils::formatMoney($model->cost); });
if ($account->invoice_item_taxes) {
$datatable->addColumn('tax_rate', function ($model) { return $model->tax_rate ? ($model->tax_name . ' ' . $model->tax_rate . '%') : ''; });
}
return $datatable->addColumn('dropdown', function ($model) {
return '<div class="btn-group tr-action" style="visibility:hidden;">
<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('products/'.$model->public_id).'/edit">'.uctrans('texts.edit_product').'</a></li>
<li class="divider"></li>
<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)
{
$account = Auth::user()->account;
$data = [
'showBreadcrumbs' => false,
'product' => Product::scope($publicId)->firstOrFail(),
'method' => 'PUT',
'url' => 'products/'.$publicId,
'title' => trans('texts.edit_product'),
];
'account' => $account,
'taxRates' => $account->invoice_item_taxes ? TaxRate::scope()->get(['id', 'name', 'rate']) : null,
'product' => Product::scope($publicId)->firstOrFail(),
'method' => 'PUT',
'url' => 'products/'.$publicId,
'title' => trans('texts.edit_product'),
];
return View::make('accounts.product', $data);
}
public function create()
{
$account = Auth::user()->account;
$data = [
'showBreadcrumbs' => false,
'product' => null,
'method' => 'POST',
'url' => 'products',
'title' => trans('texts.create_product'),
];
'account' => $account,
'taxRates' => $account->invoice_item_taxes ? TaxRate::scope()->get(['id', 'name', 'rate']) : null,
'product' => null,
'method' => 'POST',
'url' => 'products',
'title' => trans('texts.create_product'),
];
return View::make('accounts.product', $data);
}
@ -89,12 +112,14 @@ class ProductController extends BaseController
$product->product_key = trim(Input::get('product_key'));
$product->notes = trim(Input::get('notes'));
$product->cost = trim(Input::get('cost'));
$product->default_tax_rate_id = Input::get('default_tax_rate_id');
$product->save();
$message = $productPublicId ? trans('texts.updated_product') : trans('texts.created_product');
Session::flash('message', $message);
return Redirect::to('company/products');
return Redirect::to('settings/' . ACCOUNT_PRODUCTS);
}
public function archive($publicId)
@ -104,6 +129,6 @@ class ProductController extends BaseController
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()
{
$invitation = $this->getInvitation();
if (!$invitation = $this->getInvitation()) {
return $this->returnError();
}
$account = $invitation->account;
$invoice = $invitation->invoice;
$client = $invoice->client;
@ -32,6 +34,7 @@ class PublicClientController extends BaseController
'color' => $color,
'account' => $account,
'client' => $client,
'hideLogo' => $account->isWhiteLabel(),
];
return response()->view('invited.dashboard', $data);
@ -39,7 +42,9 @@ class PublicClientController extends BaseController
public function activityDatatable()
{
$invitation = $this->getInvitation();
if (!$invitation = $this->getInvitation()) {
return false;
}
$invoice = $invitation->invoice;
$query = DB::table('activities')
@ -58,7 +63,9 @@ class PublicClientController extends BaseController
public function invoiceIndex()
{
$invitation = $this->getInvitation();
if (!$invitation = $this->getInvitation()) {
return $this->returnError();
}
$account = $invitation->account;
$color = $account->primary_color ? $account->primary_color : '#0b4d78';
@ -75,7 +82,9 @@ class PublicClientController extends BaseController
public function invoiceDatatable()
{
$invitation = $this->getInvitation();
if (!$invitation = $this->getInvitation()) {
return false;
}
return $this->invoiceRepo->getClientDatatable($invitation->contact_id, ENTITY_INVOICE, Input::get('sSearch'));
}
@ -83,7 +92,9 @@ class PublicClientController extends BaseController
public function paymentIndex()
{
$invitation = $this->getInvitation();
if (!$invitation = $this->getInvitation()) {
return $this->returnError();
}
$account = $invitation->account;
$color = $account->primary_color ? $account->primary_color : '#0b4d78';
@ -100,7 +111,9 @@ class PublicClientController extends BaseController
public function paymentDatatable()
{
$invitation = $this->getInvitation();
if (!$invitation = $this->getInvitation()) {
return false;
}
$payments = $this->paymentRepo->findForContact($invitation->contact->id, Input::get('sSearch'));
return Datatable::query($payments)
@ -114,7 +127,9 @@ class PublicClientController extends BaseController
public function quoteIndex()
{
$invitation = $this->getInvitation();
if (!$invitation = $this->getInvitation()) {
return $this->returnError();
}
$account = $invitation->account;
$color = $account->primary_color ? $account->primary_color : '#0b4d78';
@ -132,29 +147,39 @@ class PublicClientController extends BaseController
public function quoteDatatable()
{
$invitation = $this->getInvitation();
if (!$invitation = $this->getInvitation()) {
return false;
}
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()
{
$invitationKey = session('invitation_key');
if (!$invitationKey) {
app()->abort(404);
return false;
}
$invitation = Invitation::where('invitation_key', '=', $invitationKey)->first();
if (!$invitation || $invitation->is_deleted) {
app()->abort(404);
return false;
}
$invoice = $invitation->invoice;
if (!$invoice || $invoice->is_deleted) {
app()->abort(404);
return false;
}
return $invitation;

View File

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

View File

@ -33,7 +33,6 @@ class ReportController extends BaseController
}
$data = [
'feature' => ACCOUNT_DATA_VISUALIZATIONS,
'clients' => $clients,
'message' => $message,
];
@ -276,7 +275,6 @@ class ReportController extends BaseController
'startDate' => $startDate->format(Session::get(SESSION_DATE_FORMAT)),
'endDate' => $endDate->format(Session::get(SESSION_DATE_FORMAT)),
'groupBy' => $groupBy,
'feature' => ACCOUNT_CHART_BUILDER,
'displayData' => $displayData,
'columns' => $columns,
'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
{
public function index()
{
return Redirect::to('settings/' . ACCOUNT_API_TOKENS);
}
public function getDatatable()
{
$query = DB::table('account_tokens')
@ -67,7 +72,6 @@ class TokenController extends BaseController
->where('public_id', '=', $publicId)->firstOrFail();
$data = [
'showBreadcrumbs' => false,
'token' => $token,
'method' => 'PUT',
'url' => 'tokens/'.$publicId,
@ -94,12 +98,10 @@ class TokenController extends BaseController
public function create()
{
$data = [
'showBreadcrumbs' => false,
'token' => null,
'method' => 'POST',
'url' => 'tokens',
'title' => trans('texts.add_token'),
'feature' => 'tokens',
];
return View::make('accounts.token', $data);
@ -115,7 +117,7 @@ class TokenController extends BaseController
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);
}
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;
}
public function index()
{
return Redirect::to('settings/' . ACCOUNT_USER_MANAGEMENT);
}
public function getDatatable()
{
$query = DB::table('users')
@ -106,7 +111,6 @@ class UserController extends BaseController
->where('public_id', '=', $publicId)->firstOrFail();
$data = [
'showBreadcrumbs' => false,
'user' => $user,
'method' => 'PUT',
'url' => 'users/'.$publicId,
@ -134,24 +138,22 @@ class UserController extends BaseController
{
if (!Auth::user()->registered) {
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) {
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()) {
$count = User::where('account_id', '=', Auth::user()->account_id)->count();
if ($count >= MAX_NUM_USERS) {
Session::flash('error', trans('texts.limit_users'));
return Redirect::to('company/advanced_settings/user_management');
return Redirect::to('settings/' . ACCOUNT_USER_MANAGEMENT);
}
}
$data = [
'showBreadcrumbs' => false,
'user' => null,
'method' => 'POST',
'url' => 'users',
@ -171,7 +173,7 @@ class UserController extends BaseController
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)
@ -184,7 +186,7 @@ class UserController extends BaseController
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);
}
return Redirect::to('company/advanced_settings/user_management');
return Redirect::to('settings/' . ACCOUNT_USER_MANAGEMENT);
}
public function sendConfirmation($userPublicId)
@ -258,7 +260,7 @@ class UserController extends BaseController
$this->userMailer->sendConfirmation($user, Auth::user());
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
// TRUSTED_PROXIES accepts a comma delimited list of subnets
//
// TRUSTED_PROXIES='10.0.0.0/8,172.16.0.0/12,192.168.0.0/16'
// ie, TRUSTED_PROXIES='10.0.0.0/8,172.16.0.0/12,192.168.0.0/16'
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
if (App::environment() == ENV_PRODUCTION && !Request::secure()) {
return Redirect::secure(Request::getRequestUri());

View File

@ -21,10 +21,10 @@
//Log::error('test');
// Application setup
Route::get('setup', 'AppController@showSetup');
Route::post('setup', 'AppController@doSetup');
Route::get('install', 'AppController@install');
Route::get('update', 'AppController@update');
Route::get('/setup', 'AppController@showSetup');
Route::post('/setup', 'AppController@doSetup');
Route::get('/install', 'AppController@install');
Route::get('/update', 'AppController@update');
/*
// Codeception code coverage
@ -35,11 +35,11 @@ Route::get('/c3.php', function () {
// Public pages
Route::get('/', 'HomeController@showIndex');
Route::get('terms', 'HomeController@showTerms');
Route::get('log_error', 'HomeController@logError');
Route::get('invoice_now', 'HomeController@invoiceNow');
Route::get('keep_alive', 'HomeController@keepAlive');
Route::post('get_started', 'AccountController@getStarted');
Route::get('/terms', 'HomeController@showTerms');
Route::get('/log_error', 'HomeController@logError');
Route::get('/invoice_now', 'HomeController@invoiceNow');
Route::get('/keep_alive', 'HomeController@keepAlive');
Route::post('/get_started', 'AccountController@getStarted');
// Client visible pages
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/submit', 'AccountController@submitSignup');
Route::get('auth/{provider}', 'Auth\AuthController@authLogin');
Route::get('auth_unlink', 'Auth\AuthController@authUnlink');
Route::get('/auth/{provider}', 'Auth\AuthController@authLogin');
Route::get('/auth_unlink', 'Auth\AuthController@authUnlink');
Route::post('hook/email_bounced', 'AppController@emailBounced');
Route::post('hook/email_opened', 'AppController@emailOpened');
Route::post('/hook/email_bounced', 'AppController@emailBounced');
Route::post('/hook/email_opened', 'AppController@emailOpened');
// Laravel auth routes
/*
Route::controllers([
'auth' => 'Auth\AuthController',
'password' => 'Auth\PasswordController',
]);
*/
get('/signup', array('as' => 'signup', 'uses' => 'Auth\AuthController@getRegister'));
post('/signup', array('as' => 'signup', 'uses' => 'Auth\AuthController@postRegister'));
get('/login', array('as' => 'login', 'uses' => 'Auth\AuthController@getLoginWrapper'));
@ -121,14 +114,20 @@ Route::group(['middleware' => 'auth'], function() {
Route::resource('products', 'ProductController');
Route::get('products/{product_id}/archive', 'ProductController@archive');
Route::get('company/advanced_settings/data_visualizations', 'ReportController@d3');
Route::get('company/advanced_settings/charts_and_reports', 'ReportController@showReports');
Route::post('company/advanced_settings/charts_and_reports', 'ReportController@showReports');
Route::get('api/tax_rates', array('as'=>'api.tax_rates', 'uses'=>'TaxRateController@getDatatable'));
Route::resource('tax_rates', 'TaxRateController');
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('company/{section?}/{sub_section?}', 'AccountController@showSection');
Route::post('company/{section?}/{sub_section?}', 'AccountController@doSection');
Route::post('user/setTheme', 'UserController@setTheme');
Route::post('remove_logo', 'AccountController@removeLogo');
Route::post('account/go_pro', 'AccountController@enableProPlan');
@ -183,7 +182,6 @@ Route::group(['middleware' => 'auth'], function() {
Route::post('credits/bulk', 'CreditController@bulk');
get('/resend_confirmation', 'AccountController@resendConfirmation');
//Route::resource('timesheets', 'TimesheetController');
});
// Route group for API
@ -253,21 +251,27 @@ if (!defined('CONTACT_EMAIL')) {
define('PERSON_CONTACT', 'contact');
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_IMPORT_EXPORT', 'import_export');
define('ACCOUNT_PAYMENTS', 'payments');
define('ACCOUNT_PAYMENTS', 'online_payments');
define('ACCOUNT_MAP', 'import_map');
define('ACCOUNT_EXPORT', 'export');
define('ACCOUNT_TAX_RATES', 'tax_rates');
define('ACCOUNT_PRODUCTS', 'products');
define('ACCOUNT_ADVANCED_SETTINGS', 'advanced_settings');
define('ACCOUNT_INVOICE_SETTINGS', 'invoice_settings');
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_DATA_VISUALIZATIONS', 'data_visualizations');
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');
@ -310,12 +314,16 @@ if (!defined('CONTACT_EMAIL')) {
define('RECENTLY_VIEWED_LIMIT', 8);
define('LOGGED_ERROR_LIMIT', 100);
define('RANDOM_KEY_LENGTH', 32);
define('MAX_NUM_CLIENTS', 500);
define('MAX_NUM_CLIENTS_PRO', 20000);
define('MAX_NUM_USERS', 20);
define('MAX_SUBDOMAIN_LENGTH', 30);
define('MAX_IFRAME_URL_LENGTH', 250);
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_SENT', 2);
@ -391,16 +399,16 @@ if (!defined('CONTACT_EMAIL')) {
define('NINJA_GATEWAY_CONFIG', 'NINJA_GATEWAY_CONFIG');
define('NINJA_WEB_URL', 'https://www.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_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('OUTDATE_BROWSER_URL', 'http://browsehappy.com/');
define('PDFMAKE_DOCS', 'http://pdfmake.org/playground.html');
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('COUNT_FREE_DESIGNS', 4);

View File

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

View File

@ -15,6 +15,27 @@ class Account extends Eloquent
protected $dates = ['deleted_at'];
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 = [
'invoice_settings' => 'object',
@ -86,6 +107,11 @@ class Account extends Eloquent
return $this->belongsTo('App\Models\Industry');
}
public function default_tax_rate()
{
return $this->belongsTo('App\Models\TaxRate');
}
public function isGatewayConfigured($gatewayId = 0)
{
$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)
{
foreach ($this->account_gateways as $gateway) {
@ -198,10 +243,131 @@ class Account extends Eloquent
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;
$prefix .= $isQuote ? $this->quote_number_prefix : $this->invoice_number_prefix;
$invoice = Invoice::createNew();
$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;
// confirm the invoice number isn't already taken
@ -214,7 +380,7 @@ class Account extends Eloquent
// update the invoice counter to be caught up
if ($counterOffset > 1) {
if ($isQuote && !$this->share_counter) {
if ($invoice->is_quote && !$this->share_counter) {
$this->quote_number_counter += $counterOffset - 1;
} else {
$this->invoice_number_counter += $counterOffset - 1;
@ -226,29 +392,24 @@ class Account extends Eloquent
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;
} else {
$this->invoice_number_counter += 1;
}
$this->save();
}
public function getLocale()
{
$language = Language::where('id', '=', $this->account->language_id)->first();
return $language->locale;
}
public function loadLocalizationSettings($client = false)
{
$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_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');
}
public function user()
{
return $this->belongsTo('App\Models\User');
}
public function invoices()
{
return $this->hasMany('App\Models\Invoice');
@ -168,6 +173,11 @@ class Client extends EntityModel
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;
protected $hidden = ['id'];
public static function createNew($parent = false)
public static function createNew($context = null)
{
$className = get_called_class();
$entity = new $className();
if ($parent) {
$entity->user_id = $parent instanceof User ? $parent->id : $parent->user_id;
$entity->account_id = $parent->account_id;
if ($context) {
$entity->user_id = $context instanceof User ? $context->id : $context->user_id;
$entity->account_id = $context->account_id;
} elseif (Auth::check()) {
$entity->user_id = Auth::user()->id;
$entity->account_id = Auth::user()->account_id;

View File

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

View File

@ -6,7 +6,10 @@ use Illuminate\Database\Eloquent\SoftDeletes;
class Invoice extends EntityModel
{
use SoftDeletes;
use SoftDeletes {
SoftDeletes::trashed as parentTrashed;
}
protected $dates = ['deleted_at'];
protected $casts = [
@ -15,6 +18,24 @@ class Invoice extends EntityModel
'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()
{
return $this->belongsTo('App\Models\Account');
@ -216,6 +237,117 @@ class Invoice extends EntityModel
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()
{
if (!$this->start_date || strtotime($this->start_date) > strtotime('now')) {
@ -268,7 +400,8 @@ class Invoice extends EntityModel
return false;
}
public function getReminder() {
public function getReminder()
{
for ($i=1; $i<=3; $i++) {
$field = "enable_reminder{$i}";
if (!$this->account->$field) {
@ -320,7 +453,7 @@ class Invoice extends EntityModel
Invoice::creating(function ($invoice) {
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();
}
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()
{
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()
@ -195,7 +203,8 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
{
if (!$user->getOriginal('email')
|| $user->getOriginal('email') == TEST_USERNAME
|| $user->getOriginal('username') == TEST_USERNAME) {
|| $user->getOriginal('username') == TEST_USERNAME
|| $user->getOriginal('email') == 'tests@bitrock.com') {
event(new UserSignedUp());
}

View File

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

View File

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

View File

@ -139,7 +139,7 @@ class AccountRepository
$invoice->user_id = $account->users()->first()->id;
$invoice->public_id = $publicId;
$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->amount = PRO_PLAN_PRICE;
$invoice->balance = PRO_PLAN_PRICE;

View File

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

View File

@ -5,6 +5,7 @@ use Utils;
class TaxRateRepository
{
/*
public function save($taxRates)
{
$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) {
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 (!$isRegistered) {
event(new UserSignedUp());
Session::flash('warning', trans('texts.success_message'));
Session::flash('message', trans('texts.success_message'));
} else {
Session::flash('message', trans('texts.updated_settings'));
return redirect()->to('/company/details');
return redirect()->to('/settings/' . ACCOUNT_USER_DETAILS);
}
} else {
Session::flash('error', $result);

View File

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

View File

@ -29,7 +29,7 @@
"coatesap/omnipay-datacash": "~2.0",
"alfaproject/omnipay-neteller": "1.0.*@dev",
"mfauveau/omnipay-pacnet": "~2.0",
"coatesap/omnipay-paymentsense": "~2.0",
"coatesap/omnipay-paymentsense": "2.0.0",
"coatesap/omnipay-realex": "~2.0",
"fruitcakestudio/omnipay-sisow": "~2.0",
"alfaproject/omnipay-skrill": "dev-master",
@ -38,7 +38,8 @@
"laravelcollective/html": "~5.0",
"wildbit/laravel-postmark-provider": "dev-master",
"Dwolla/omnipay-dwolla": "dev-master",
"laravel/socialite": "~2.0"
"laravel/socialite": "~2.0",
"simshaun/recurr": "dev-master"
},
"require-dev": {
"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' => [
'client_id' => env('GITHUB_CLIENT_ID'),
'client_secret' => env('GITHUB_CLIENT_SECRET'),
'redirect' => 'http://ninja.dev/auth/github'
'redirect' => env('GITHUB_OAUTH_REDIRECT'),
],
'google' => [
'client_id' => '640903115046-dd09j2q24lcc3ilrrv5f2ft2i3n0sreg.apps.googleusercontent.com',
'client_secret' => 'Vsfhldq7mRxsCXQTQI8U_4Ua',
'redirect' => 'http://ninja.dev/auth/google',
'client_id' => env('GOOGLE_CLIENT_ID'),
'client_secret' => env('GOOGLE_CLIENT_SECRET'),
'redirect' => env('GOOGLE_OAUTH_REDIRECT'),
],
'facebook' => [
'client_id' => '635126583203143',
'client_secret' => '7aa7c391019f2ece3c6aa90f4c9b1485',
'redirect' => 'http://ninja.dev/auth/facebook',
'client_id' => env('FACEBOOK_CLIENT_ID'),
'client_secret' => env('FACEBOOK_CLIENT_SECRET'),
'redirect' => env('FACEBOOK_OAUTH_REDIRECT'),
],
'linkedin' => [
'client_id' => '778is2j21w25xj',
'client_secret' => 'DvDExxfBLXUtxc81',
'redirect' => 'http://ninja.dev/auth/linkedin',
'client_id' => env('LINKEDIN_CLIENT_ID'),
'client_secret' => env('LINKEDIN_CLIENT_SECRET'),
'redirect' => env('LINKEDIN_OAUTH_REDIRECT'),
],
];

View File

@ -14,10 +14,10 @@ class AddInvoiceNumberSettings extends Migration {
{
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->text('quote_number_prefix')->nullable();
$table->string('quote_number_prefix')->nullable();
$table->integer('quote_number_counter')->default(1)->nullable();
$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' => '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' => '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) {

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-size: 15px;
}
html {
overflow-y: scroll;
}
.bold { font-weight: 700; }
a {color:#0b4d78;}
/*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;
}
@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) {
@ -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) {
.test-class{color:black;}
@ -3337,7 +3362,18 @@ ul.user-accounts a:hover div.remove {
visibility: visible;
}
.tooltip-inner {
.invoice-contact .tooltip-inner {
text-align:left;
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;
background-color: #f8f8f8;
}
html {
overflow-y: scroll;
}
@media screen and (min-width: 700px) {
.navbar-header {
@ -837,7 +839,7 @@ body {
display: inline-block;
width: 9px;
height: 15px;
background: url({{ asset('images/social/facebook.svg') }}) no-repeat;
background: url('../images/social/facebook.svg') no-repeat;
margin: 0 6px 0 0;
position: relative;
top: 3px;
@ -848,7 +850,7 @@ body {
display: inline-block;
width: 19px;
height: 16px;
background: url({{ asset('images/social/twitter.svg') }}) no-repeat;
background: url('../images/social/twitter.svg') no-repeat;
margin: 0 6px 0 0;
position: relative;
top: 3px;
@ -859,18 +861,18 @@ body {
display: inline-block;
width: 19px;
height: 16px;
background: url({{ asset('images/social/github.png') }}) no-repeat;
background: url('../images/social/github.png') no-repeat;
margin: 0 6px 0 0;
position: relative;
top: 3px;
}
/* Hide bootstrap sort header icons */
table.data-table thead .sorting:after { content: '' !important }
table.data-table thead .sorting_asc:after { content: '' !important }
table.data-table thead .sorting_desc:after { content: '' !important}
table.data-table thead .sorting_asc_disabled:after { content: '' !important }
table.data-table thead .sorting_desc_disabled:after { content: '' !important }
table.table thead .sorting:after { content: '' !important }
table.table thead .sorting_asc:after { content: '' !important }
table.table thead .sorting_desc:after { content: '' !important }
table.table thead .sorting_asc_disabled:after { content: '' !important }
table.table thead .sorting_desc_disabled:after { content: '' !important }
.dataTables_length {
padding-left: 20px;

View File

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

60
public/css/style.css vendored
View File

@ -2,6 +2,9 @@ body { background: #f8f8f8 !important;
font-family: 'Roboto', sans-serif;
font-size: 15px;
}
html {
overflow-y: scroll;
}
.bold { font-weight: 700; }
a {color:#0b4d78;}
/*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;
}
@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) {
@ -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) {
.test-class{color:black;}
@ -987,7 +1012,18 @@ ul.user-accounts a:hover div.remove {
visibility: visible;
}
.tooltip-inner {
.invoice-contact .tooltip-inner {
text-align:left;
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) {
return;
}
var account = invoice.account;
var contact = client.contacts[0];
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;
@ -32070,6 +32071,11 @@ NINJA.clientDetails = function(invoice) {
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 = [
{text:clientName || ' ', style: ['clientName']},
{text:client.id_number},
@ -32079,8 +32085,8 @@ NINJA.clientDetails = function(invoice) {
{text:cityStatePostal},
{text:client.country ? client.country.name : ''},
{text:clientEmail},
{text: invoice.client.custom_value1 ? invoice.account.custom_client_label1 + ' ' + invoice.client.custom_value1 : false},
{text: invoice.client.custom_value2 ? invoice.account.custom_client_label2 + ' ' + invoice.client.custom_value2 : false}
{text: client.custom_value1 && !custom1InPattern ? account.custom_client_label1 + ' ' + client.custom_value1 : false},
{text: client.custom_value2 && !custom2InPattern ? account.custom_client_label2 + ' ' + client.custom_value2 : false}
];
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) {
return;
}
var account = invoice.account;
var contact = client.contacts[0];
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;
@ -497,6 +498,11 @@ NINJA.clientDetails = function(invoice) {
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 = [
{text:clientName || ' ', style: ['clientName']},
{text:client.id_number},
@ -506,8 +512,8 @@ NINJA.clientDetails = function(invoice) {
{text:cityStatePostal},
{text:client.country ? client.country.name : ''},
{text:clientEmail},
{text: invoice.client.custom_value1 ? invoice.account.custom_client_label1 + ' ' + invoice.client.custom_value1 : false},
{text: invoice.client.custom_value2 ? invoice.account.custom_client_label2 + ' ' + invoice.client.custom_value2 : false}
{text: client.custom_value1 && !custom1InPattern ? account.custom_client_label1 + ' ' + client.custom_value1 : false},
{text: client.custom_value2 && !custom2InPattern ? account.custom_client_label2 + ' ' + client.custom_value2 : false}
];
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)
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.
### Installation Options
* [Zip - Free](https://www.invoiceninja.com/knowledgebase/self-host/)
* [Bitnami - Free](https://bitnami.com/stack/invoice-ninja)
* [Softaculous - $30](https://www.softaculous.com/apps/ecommerce/Invoice_Ninja)
* [Self-Host Zip](https://www.invoiceninja.com/knowledgebase/self-host/) - Free
* [Docker File](https://github.com/rollbrettler/Dockerfiles/blob/master/invoice-ninja/Dockerfile) - Free
* [Bitnami](https://bitnami.com/stack/invoice-ninja) - Free
* [Softaculous](https://www.softaculous.com/apps/ecommerce/Invoice_Ninja) - $30
### Features
* 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
* Reminder emails
* Partial payments
* Client portal
* Custom email templates
* [Zapier](https://zapier.com/) integration
* [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
* [bgrins/spectrum](https://github.com/bgrins/spectrum) - The No Hassle JavaScript Colorpicker
* [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.",
"notmasked" => "The values are masked",
"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;',
);
);

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.",
"notmasked" => "The values are masked",
"less_than" => 'The :attribute must be less than :value',
"has_counter" => 'The value must contain {$counter}',
/*
|--------------------------------------------------------------------------

View File

@ -2,19 +2,19 @@
return array(
/*
|--------------------------------------------------------------------------
| Pagination Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are used by the paginator library to build
| the simple pagination links. You are free to change them to anything
| you want to customize your views to better match your application.
|
*/
/*
|--------------------------------------------------------------------------
| Pagination Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are used by the paginator library to build
| the simple pagination links. You are free to change them to anything
| 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 [
/*
|--------------------------------------------------------------------------
| 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 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" => "Passwords must be at least six characters and match the confirmation.",
"user" => "We can't find a user with that e-mail address.",
"token" => "This password reset token is invalid.",
"sent" => "We have e-mailed your password reset link!",
"reset" => "Your password has been reset!",
"password" => "Passwords must be at least six characters and match the confirmation.",
"user" => "We can't find a user with that e-mail address.",
"token" => "This password reset token is invalid.",
"sent" => "We have e-mailed your password reset link!",
"reset" => "Your password has been reset!",
];

View File

@ -2,23 +2,23 @@
return array(
/*
|--------------------------------------------------------------------------
| 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 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" => "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(
/*
|--------------------------------------------------------------------------
| Validation Language Lines
|--------------------------------------------------------------------------
|
| The following language lines contain the default error messages used by
| the validator class. Some of these rules have multiple versions such
| such as the size rules. Feel free to tweak each of these messages.
|
*/
/*
|--------------------------------------------------------------------------
| Validation Language Lines
|--------------------------------------------------------------------------
|
| The following language lines contain the default error messages used by
| the validator class. Some of these rules have multiple versions such
| such as the size rules. Feel free to tweak each of these messages.
|
*/
"accepted" => "The :attribute must be accepted.",
"active_url" => "The :attribute is not a valid URL.",
"after" => "The :attribute must be a date after :date.",
"alpha" => "The :attribute may only contain letters.",
"alpha_dash" => "The :attribute may only contain letters, numbers, and dashes.",
"alpha_num" => "The :attribute may only contain letters and numbers.",
"array" => "The :attribute must be an array.",
"before" => "The :attribute must be a date before :date.",
"between" => array(
"numeric" => "The :attribute must be between :min - :max.",
"file" => "The :attribute must be between :min - :max kilobytes.",
"string" => "The :attribute must be between :min - :max characters.",
"array" => "The :attribute must have between :min - :max items.",
),
"confirmed" => "The :attribute confirmation does not match.",
"date" => "The :attribute is not a valid date.",
"date_format" => "The :attribute does not match the format :format.",
"different" => "The :attribute and :other must be different.",
"digits" => "The :attribute must be :digits digits.",
"digits_between" => "The :attribute must be between :min and :max digits.",
"email" => "The :attribute format is invalid.",
"exists" => "The selected :attribute is invalid.",
"image" => "The :attribute must be an image.",
"in" => "The selected :attribute is invalid.",
"integer" => "The :attribute must be an integer.",
"ip" => "The :attribute must be a valid IP address.",
"max" => array(
"numeric" => "The :attribute may not be greater than :max.",
"file" => "The :attribute may not be greater than :max kilobytes.",
"string" => "The :attribute may not be greater than :max characters.",
"array" => "The :attribute may not have more than :max items.",
),
"mimes" => "The :attribute must be a file of type: :values.",
"min" => array(
"numeric" => "The :attribute must be at least :min.",
"file" => "The :attribute must be at least :min kilobytes.",
"string" => "The :attribute must be at least :min characters.",
"array" => "The :attribute must have at least :min items.",
),
"not_in" => "The selected :attribute is invalid.",
"numeric" => "The :attribute must be a number.",
"regex" => "The :attribute format is invalid.",
"required" => "The :attribute field is required.",
"required_if" => "The :attribute field is required when :other is :value.",
"required_with" => "The :attribute field is required when :values is present.",
"required_without" => "The :attribute field is required when :values is not present.",
"same" => "The :attribute and :other must match.",
"size" => array(
"numeric" => "The :attribute must be :size.",
"file" => "The :attribute must be :size kilobytes.",
"string" => "The :attribute must be :size characters.",
"array" => "The :attribute must contain :size items.",
),
"unique" => "The :attribute has already been taken.",
"url" => "The :attribute format is invalid.",
"positive" => "The :attribute must be greater than zero.",
"has_credit" => "The client does not have enough credit.",
"notmasked" => "The values are masked",
"accepted" => "The :attribute must be accepted.",
"active_url" => "The :attribute is not a valid URL.",
"after" => "The :attribute must be a date after :date.",
"alpha" => "The :attribute may only contain letters.",
"alpha_dash" => "The :attribute may only contain letters, numbers, and dashes.",
"alpha_num" => "The :attribute may only contain letters and numbers.",
"array" => "The :attribute must be an array.",
"before" => "The :attribute must be a date before :date.",
"between" => array(
"numeric" => "The :attribute must be between :min - :max.",
"file" => "The :attribute must be between :min - :max kilobytes.",
"string" => "The :attribute must be between :min - :max characters.",
"array" => "The :attribute must have between :min - :max items.",
),
"confirmed" => "The :attribute confirmation does not match.",
"date" => "The :attribute is not a valid date.",
"date_format" => "The :attribute does not match the format :format.",
"different" => "The :attribute and :other must be different.",
"digits" => "The :attribute must be :digits digits.",
"digits_between" => "The :attribute must be between :min and :max digits.",
"email" => "The :attribute format is invalid.",
"exists" => "The selected :attribute is invalid.",
"image" => "The :attribute must be an image.",
"in" => "The selected :attribute is invalid.",
"integer" => "The :attribute must be an integer.",
"ip" => "The :attribute must be a valid IP address.",
"max" => array(
"numeric" => "The :attribute may not be greater than :max.",
"file" => "The :attribute may not be greater than :max kilobytes.",
"string" => "The :attribute may not be greater than :max characters.",
"array" => "The :attribute may not have more than :max items.",
),
"mimes" => "The :attribute must be a file of type: :values.",
"min" => array(
"numeric" => "The :attribute must be at least :min.",
"file" => "The :attribute must be at least :min kilobytes.",
"string" => "The :attribute must be at least :min characters.",
"array" => "The :attribute must have at least :min items.",
),
"not_in" => "The selected :attribute is invalid.",
"numeric" => "The :attribute must be a number.",
"regex" => "The :attribute format is invalid.",
"required" => "The :attribute field is required.",
"required_if" => "The :attribute field is required when :other is :value.",
"required_with" => "The :attribute field is required when :values is present.",
"required_without" => "The :attribute field is required when :values is not present.",
"same" => "The :attribute and :other must match.",
"size" => array(
"numeric" => "The :attribute must be :size.",
"file" => "The :attribute must be :size kilobytes.",
"string" => "The :attribute must be :size characters.",
"array" => "The :attribute must contain :size items.",
),
"unique" => "The :attribute has already been taken.",
"url" => "The :attribute format is invalid.",
"positive" => "The :attribute must be greater than zero.",
"has_credit" => "The client does not have enough credit.",
"notmasked" => "The values are masked",
"less_than" => 'The :attribute must be less than :value',
"has_counter" => 'The value must contain {$counter}',
/*
|--------------------------------------------------------------------------
| Custom Validation Language Lines
|--------------------------------------------------------------------------
|
| Here you may specify custom validation messages for attributes using the
| convention "attribute.rule" to name the lines. This makes it quick to
| specify a specific custom language line for a given attribute rule.
|
*/
/*
|--------------------------------------------------------------------------
| Custom Validation Language Lines
|--------------------------------------------------------------------------
|
| Here you may specify custom validation messages for attributes using the
| convention "attribute.rule" to name the lines. This makes it quick to
| specify a specific custom language line for a given attribute rule.
|
*/
'custom' => array(),
'custom' => array(),
/*
|--------------------------------------------------------------------------
| Custom Validation Attributes
|--------------------------------------------------------------------------
|
| The following language lines are used to swap attribute place-holders
| with something more reader friendly such as E-Mail Address instead
| of "email". This simply helps us make messages a little cleaner.
|
*/
/*
|--------------------------------------------------------------------------
| Custom Validation Attributes
|--------------------------------------------------------------------------
|
| The following language lines are used to swap attribute place-holders
| with something more reader friendly such as E-Mail Address instead
| 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_signature' => 'Un saludo cordial,',
'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:',
'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',
@ -723,7 +723,6 @@ return array(
'current_user' => 'Usuario Actual',
'new_recurring_invoice' => 'Nueva Factura Recurrente',
'recurring_invoice' => 'Factura Recurrente',
'recurring_too_soon' => 'Es my pronto para crear la siguiente factura recurrente',
'created_by_invoice' => 'Creado por :invoice',
'primary_user' => 'Usuario Primario',
'help' => 'Ayuda',
@ -795,6 +794,16 @@ return array(
'custom_invoice_link' => 'Custom Invoice Link',
'total_invoiced' => 'Total Invoiced',
'open_balance' => 'Open Balance',
'verify_email' => 'Please visit the link in the account confirmation email to verify your email address.',
'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.",
"notmasked" => "The values are masked",
"less_than" => 'The :attribute must be less than :value',
"has_counter" => 'The value must contain {$counter}',
/*
|--------------------------------------------------------------------------
| Custom Validation Language Lines

View File

@ -1,477 +1,476 @@
<?php
<?php
return array(
// client
'organization' => 'Empresa',
'name' => 'Nombre de Empresa',
'website' => 'Sitio Web',
'work_phone' => 'Teléfono',
'address' => 'Dirección',
'address1' => 'Calle',
'address2' => 'Bloq/Pta',
'city' => 'Ciudad',
'state' => 'Provincia',
'postal_code' => 'Código Postal',
'country_id' => 'País',
'contacts' => 'Contactos',
'first_name' => 'Nombres',
'last_name' => 'Apellidos',
'phone' => 'Teléfono',
'email' => 'Email',
'additional_info' => 'Información adicional',
'payment_terms' => 'Plazos de pago', //
'currency_id' => 'Divisa',
'size_id' => 'Tamaño',
'industry_id' => 'Industria',
'private_notes' => 'Notas Privadas',
// client
'organization' => 'Empresa',
'name' => 'Nombre de Empresa',
'website' => 'Sitio Web',
'work_phone' => 'Teléfono',
'address' => 'Dirección',
'address1' => 'Calle',
'address2' => 'Bloq/Pta',
'city' => 'Ciudad',
'state' => 'Provincia',
'postal_code' => 'Código Postal',
'country_id' => 'País',
'contacts' => 'Contactos',
'first_name' => 'Nombres',
'last_name' => 'Apellidos',
'phone' => 'Teléfono',
'email' => 'Email',
'additional_info' => 'Información adicional',
'payment_terms' => 'Plazos de pago', //
'currency_id' => 'Divisa',
'size_id' => 'Tamaño',
'industry_id' => 'Industria',
'private_notes' => 'Notas Privadas',
// invoice
'invoice' => 'Factura',
'client' => 'Cliente',
'invoice_date' => 'Fecha de factura',
'due_date' => 'Fecha de pago',
'invoice_number' => 'Número de Factura',
'invoice_number_short' => 'Factura Nº',
'po_number' => 'Apartado de correo',
'po_number_short' => 'Apdo.',
'frequency_id' => 'Frecuencia',
'discount' => 'Descuento',
'taxes' => 'Impuestos',
'tax' => 'IVA',
'item' => 'Concepto',
'description' => 'Descripción',
'unit_cost' => 'Coste unitario',
'quantity' => 'Cantidad',
'line_total' => 'Total',
'subtotal' => 'Subtotal',
'paid_to_date' => 'Pagado',
'balance_due' => 'Pendiente',
'invoice_design_id' => 'Diseño',
'terms' => 'Términos',
'your_invoice' => 'Tu factura',
'remove_contact' => 'Eliminar contacto',
'add_contact' => 'Añadir contacto',
'create_new_client' => 'Crear nuevo cliente',
'edit_client_details' => 'Editar detalles del cliente',
'enable' => 'Activar',
'learn_more' => 'Saber más',
'manage_rates' => 'Gestionar tarifas',
'note_to_client' => 'Nota para el cliente',
'invoice_terms' => 'Términos de facturación',
'save_as_default_terms' => 'Guardar como términos por defecto',
'download_pdf' => 'Descargar PDF',
'pay_now' => 'Pagar Ahora',
'save_invoice' => 'Guardar factura',
'clone_invoice' => 'Clonar factura',
'archive_invoice' => 'Archivar factura',
'delete_invoice' => 'Eliminar factura',
'email_invoice' => 'Enviar factura por email',
'enter_payment' => 'Agregar pago',
'tax_rates' => 'Tasas de impuesto',
'rate' => 'Tasas',
'settings' => 'Configuración',
'enable_invoice_tax' => 'Activar impuesto <b>para la factura</b>',
'enable_line_item_tax' => 'Activar impuesto <b>por concepto</b>',
// invoice
'invoice' => 'Factura',
'client' => 'Cliente',
'invoice_date' => 'Fecha de factura',
'due_date' => 'Fecha de pago',
'invoice_number' => 'Número de Factura',
'invoice_number_short' => 'Factura Nº',
'po_number' => 'Apartado de correo',
'po_number_short' => 'Apdo.',
'frequency_id' => 'Frecuencia',
'discount' => 'Descuento',
'taxes' => 'Impuestos',
'tax' => 'IVA',
'item' => 'Concepto',
'description' => 'Descripción',
'unit_cost' => 'Coste unitario',
'quantity' => 'Cantidad',
'line_total' => 'Total',
'subtotal' => 'Subtotal',
'paid_to_date' => 'Pagado',
'balance_due' => 'Pendiente',
'invoice_design_id' => 'Diseño',
'terms' => 'Términos',
'your_invoice' => 'Tu factura',
'remove_contact' => 'Eliminar contacto',
'add_contact' => 'Añadir contacto',
'create_new_client' => 'Crear nuevo cliente',
'edit_client_details' => 'Editar detalles del cliente',
'enable' => 'Activar',
'learn_more' => 'Saber más',
'manage_rates' => 'Gestionar tarifas',
'note_to_client' => 'Nota para el cliente',
'invoice_terms' => 'Términos de facturación',
'save_as_default_terms' => 'Guardar como términos por defecto',
'download_pdf' => 'Descargar PDF',
'pay_now' => 'Pagar Ahora',
'save_invoice' => 'Guardar factura',
'clone_invoice' => 'Clonar factura',
'archive_invoice' => 'Archivar factura',
'delete_invoice' => 'Eliminar factura',
'email_invoice' => 'Enviar factura por email',
'enter_payment' => 'Agregar pago',
'tax_rates' => 'Tasas de impuesto',
'rate' => 'Tasas',
'settings' => 'Configuración',
'enable_invoice_tax' => 'Activar impuesto <b>para la factura</b>',
'enable_line_item_tax' => 'Activar impuesto <b>por concepto</b>',
// navigation
'dashboard' => 'Inicio',
'clients' => 'Clientes',
'invoices' => 'Facturas',
'payments' => 'Pagos',
'credits' => 'Créditos',
'history' => 'Historial',
'search' => 'Búsqueda',
'sign_up' => 'Registrarse',
'guest' => 'Invitado',
'company_details' => 'Detalles de la Empresa',
'online_payments' => 'Pagos en Linea',
'notifications' => 'Notificaciones',
'import_export' => 'Importar/Exportar',
'done' => 'Hecho',
'save' => 'Guardar',
'create' => 'Crear',
'upload' => 'Subir',
'import' => 'Importar',
'download' => 'Descargar',
'cancel' => 'Cancelar',
'close' => 'Cerrar',
'provide_email' => 'Por favor facilita una dirección de correo válida.',
'powered_by' => 'Creado por',
'no_items' => 'No hay datos',
// navigation
'dashboard' => 'Inicio',
'clients' => 'Clientes',
'invoices' => 'Facturas',
'payments' => 'Pagos',
'credits' => 'Créditos',
'history' => 'Historial',
'search' => 'Búsqueda',
'sign_up' => 'Registrarse',
'guest' => 'Invitado',
'company_details' => 'Detalles de la Empresa',
'online_payments' => 'Pagos en Linea',
'notifications' => 'Notificaciones',
'import_export' => 'Importar/Exportar',
'done' => 'Hecho',
'save' => 'Guardar',
'create' => 'Crear',
'upload' => 'Subir',
'import' => 'Importar',
'download' => 'Descargar',
'cancel' => 'Cancelar',
'close' => 'Cerrar',
'provide_email' => 'Por favor facilita una dirección de correo válida.',
'powered_by' => 'Creado por',
'no_items' => 'No hay datos',
// recurring invoices
'recurring_invoices' => 'Facturas recurrentes',
'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>Ejemplos de variables dinámicas de factura:</p>
<ul>
<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>"Retainer payment for :QUARTER+1" => "Pago anticipo de pagos para T2"</li>
</ul>',
// recurring invoices
'recurring_invoices' => 'Facturas recurrentes',
'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>Ejemplos de variables dinámicas de factura:</p>
<ul>
<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>"Retainer payment for :QUARTER+1" => "Pago anticipo de pagos para T2"</li>
</ul>',
// dashboard
'in_total_revenue' => 'Ingreso Total',
'billed_client' => 'Cliente Facturado',
'billed_clients' => 'Clientes Facturados',
'active_client' => 'Cliente Activo',
'active_clients' => 'Clientes Activos',
'invoices_past_due' => 'Facturas Vencidas',
'upcoming_invoices' => 'Próximas Facturas',
'average_invoice' => 'Promedio de Facturación',
// dashboard
'in_total_revenue' => 'Ingreso Total',
'billed_client' => 'Cliente Facturado',
'billed_clients' => 'Clientes Facturados',
'active_client' => 'Cliente Activo',
'active_clients' => 'Clientes Activos',
'invoices_past_due' => 'Facturas Vencidas',
'upcoming_invoices' => 'Próximas Facturas',
'average_invoice' => 'Promedio de Facturación',
// list pages
'archive' => 'Archivar',
'delete' => 'Eliminar',
'archive_client' => 'Archivar Cliente',
'delete_client' => 'Eliminar Cliente',
'archive_payment' => 'Archivar Pago',
'delete_payment' => 'Eliminar Pago',
'archive_credit' => 'Archivar Crédito',
'delete_credit' => 'Eliminar Crédito',
'show_archived_deleted' => 'Mostrar archivados o eliminados en ',
'filter' => 'Filtrar',
'new_client' => 'Nuevo Cliente',
'new_invoice' => 'Nueva Factura',
'new_payment' => 'Nuevo Pago',
'new_credit' => 'Nuevo Crédito',
'contact' => 'Contacto',
'date_created' => 'Fecha de Creación',
'last_login' => 'Último Acceso',
'balance' => 'Balance',
'action' => 'Acción',
'status' => 'Estado',
'invoice_total' => 'Total Facturado',
'frequency' => 'Frequencia',
'start_date' => 'Fecha de Inicio',
'end_date' => 'Fecha de Finalización',
'transaction_reference' => 'Referencia de Transacción',
'method' => 'Método',
'payment_amount' => 'Valor del Pago',
'payment_date' => 'Fecha de Pago',
'credit_amount' => 'Cantidad de Crédito',
'credit_balance' => 'Balance de Crédito',
'credit_date' => 'Fecha de Crédito',
'empty_table' => 'Tabla vacía',
'select' => 'Seleccionar',
'edit_client' => 'Editar Cliente',
'edit_invoice' => 'Editar Factura',
// list pages
'archive' => 'Archivar',
'delete' => 'Eliminar',
'archive_client' => 'Archivar Cliente',
'delete_client' => 'Eliminar Cliente',
'archive_payment' => 'Archivar Pago',
'delete_payment' => 'Eliminar Pago',
'archive_credit' => 'Archivar Crédito',
'delete_credit' => 'Eliminar Crédito',
'show_archived_deleted' => 'Mostrar archivados o eliminados en ',
'filter' => 'Filtrar',
'new_client' => 'Nuevo Cliente',
'new_invoice' => 'Nueva Factura',
'new_payment' => 'Nuevo Pago',
'new_credit' => 'Nuevo Crédito',
'contact' => 'Contacto',
'date_created' => 'Fecha de Creación',
'last_login' => 'Último Acceso',
'balance' => 'Balance',
'action' => 'Acción',
'status' => 'Estado',
'invoice_total' => 'Total Facturado',
'frequency' => 'Frequencia',
'start_date' => 'Fecha de Inicio',
'end_date' => 'Fecha de Finalización',
'transaction_reference' => 'Referencia de Transacción',
'method' => 'Método',
'payment_amount' => 'Valor del Pago',
'payment_date' => 'Fecha de Pago',
'credit_amount' => 'Cantidad de Crédito',
'credit_balance' => 'Balance de Crédito',
'credit_date' => 'Fecha de Crédito',
'empty_table' => 'Tabla vacía',
'select' => 'Seleccionar',
'edit_client' => 'Editar Cliente',
'edit_invoice' => 'Editar Factura',
// client view page
'create_invoice' => 'Crear Factura',
'Create Invoice' => 'Crear Factura',
'enter_credit' => 'Agregar Crédito',
'last_logged_in' => 'Último inicio de sesión',
'details' => 'Detalles',
'standing' => 'Situación', //
'credit' => 'Crédito',
'activity' => 'Actividad',
'date' => 'Fecha',
'message' => 'Mensaje',
'adjustment' => 'Ajustes',
'are_you_sure' => '¿Está Seguro?',
// client view page
'create_invoice' => 'Crear Factura',
'Create Invoice' => 'Crear Factura',
'enter_credit' => 'Agregar Crédito',
'last_logged_in' => 'Último inicio de sesión',
'details' => 'Detalles',
'standing' => 'Situación', //
'credit' => 'Crédito',
'activity' => 'Actividad',
'date' => 'Fecha',
'message' => 'Mensaje',
'adjustment' => 'Ajustes',
'are_you_sure' => '¿Está Seguro?',
// payment pages
'payment_type_id' => 'Tipo de pago',
'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',//
// payment pages
'payment_type_id' => 'Tipo de pago',
'amount' => 'Cantidad',
// account/company pages
'work_email' => 'Correo electrónico de la empresa',
'language_id' => 'Idioma',
'timezone_id' => 'Zona horaria',
'date_format_id' => 'Formato de fecha',
'datetime_format_id' => 'Format de fecha/hora',
'users' => 'Usuarios',
'localization' => 'Localización',
'remove_logo' => 'Eliminar logo',
'logo_help' => 'Formatos aceptados: JPEG, GIF y PNG',
'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',
// 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',//
// application messages
'created_client' => 'cliente creado con éxito',
'created_clients' => ':count clientes creados con éxito',
'updated_settings' => 'Configuración actualizada con éxito',
'removed_logo' => 'Logo eliminado 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',
// account/company pages
'work_email' => 'Correo electrónico de la empresa',
'language_id' => 'Idioma',
'timezone_id' => 'Zona horaria',
'date_format_id' => 'Formato de fecha',
'datetime_format_id' => 'Format de fecha/hora',
'users' => 'Usuarios',
'localization' => 'Localización',
'remove_logo' => 'Eliminar logo',
'logo_help' => 'Formatos aceptados: JPEG, GIF y PNG',
'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',
'created_client' => 'Cliente creado con éxito',
'archived_client' => 'Cliente archivado con éxito',
'archived_clients' => ':count clientes archivados con éxito',
'deleted_client' => 'Cliente eliminado con éxito',
'deleted_clients' => ':count clientes eliminados con éxito',
// application messages
'created_client' => 'cliente creado con éxito',
'created_clients' => ':count clientes creados con éxito',
'updated_settings' => 'Configuración actualizada con éxito',
'removed_logo' => 'Logo eliminado 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',
'created_invoice' => 'Factura creada con éxito',
'cloned_invoice' => 'Factura clonada con éxito',
'emailed_invoice' => 'Factura enviada con éxito',
'and_created_client' => 'y cliente creado ',
'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',
'updated_client' => 'Cliente actualizado con éxito',
'created_client' => 'Cliente creado con éxito',
'archived_client' => 'Cliente archivado con éxito',
'archived_clients' => ':count clientes archivados con éxito',
'deleted_client' => 'Cliente eliminado con éxito',
'deleted_clients' => ':count clientes eliminados con éxito',
'created_payment' => 'Pago creado con éxito',
'archived_payment' => 'Pago archivado con éxito',
'archived_payments' => ':count pagos archivados con éxito',
'deleted_payment' => 'Pago eliminado con éxito',
'deleted_payments' => ':count pagos eliminados con éxito',
'applied_payment' => 'Pago aplicado con éxito',
'updated_invoice' => 'Factura actualizada con éxito',
'created_invoice' => 'Factura creada con éxito',
'cloned_invoice' => 'Factura clonada con éxito',
'emailed_invoice' => 'Factura enviada con éxito',
'and_created_client' => 'y cliente creado ',
'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',
'archived_credit' => 'Crédito archivado con éxito',
'archived_credits' => ':count creditos archivados con éxito',
'deleted_credit' => 'Créditos eliminados con éxito',
'deleted_credits' => ':count creditos eliminados con éxito',
'created_payment' => 'Pago creado con éxito',
'archived_payment' => 'Pago archivado con éxito',
'archived_payments' => ':count pagos archivados con éxito',
'deleted_payment' => 'Pago eliminado con éxito',
'deleted_payments' => ':count pagos eliminados con éxito',
'applied_payment' => 'Pago aplicado con éxito',
// Emails
'confirmation_subject' => 'Corfimación de tu cuenta en Invoice Ninja',
'confirmation_header' => 'Confirmación de Cuenta',
'confirmation_message' => 'Por favor, haz clic en el enlace abajo para confirmar tu cuenta.',
'invoice_subject' => 'Nueva factura :invoice de :account',
'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,
'created_credit' => 'Crédito creado con éxito',
'archived_credit' => 'Crédito archivado con éxito',
'archived_credits' => ':count creditos archivados con éxito',
'deleted_credit' => 'Créditos eliminados con éxito',
'deleted_credits' => ':count creditos eliminados con éxito',
// Payment page
'secure_payment' => 'Pago seguro',
'card_number' => 'Número de tarjeta',
'expiration_month' => 'Mes de caducidad',
'expiration_year' => 'Año de caducidad',
'cvv' => 'CVV',
// Emails
'confirmation_subject' => 'Corfimación de tu cuenta en Invoice Ninja',
'confirmation_header' => 'Confirmación de Cuenta',
'confirmation_message' => 'Por favor, haz clic en el enlace abajo para confirmar tu cuenta.',
'invoice_subject' => 'Nueva factura :invoice de :account',
'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
'confide' => array(
'too_many_attempts' => 'Demasiados intentos fallidos. Inténtalo de nuevo en un par de minutos.',
'wrong_credentials' => 'Contraseña o email incorrecto.',
'confirmation' => '¡Tu cuenta se ha confirmado!',
'wrong_confirmation' => 'Código de confirmación incorrecto.',
'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',
),
// Payment page
'secure_payment' => 'Pago seguro',
'card_number' => 'Número de tarjeta',
'expiration_month' => 'Mes de caducidad',
'expiration_year' => 'Año de caducidad',
'cvv' => 'CVV',
// Pro Plan
'pro_plan' => [
'remove_logo' => ':link haz click para eliminar el logo de Invoice Ninja',
'remove_logo_link' => 'Haz click aquí',
],
'logout' => 'Cerrar sesión',
'sign_up_to_save' => 'Registrate para guardar tu trabajo',
'agree_to_terms' =>'Estoy de acuerdo con los términos de Invoice Ninja :terms',
'terms_of_service' => 'Términos de servicio',
'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',
// Security alerts
'confide' => array(
'too_many_attempts' => 'Demasiados intentos fallidos. Inténtalo de nuevo en un par de minutos.',
'wrong_credentials' => 'Contraseña o email incorrecto.',
'confirmation' => '¡Tu cuenta se ha confirmado!',
'wrong_confirmation' => 'Código de confirmación incorrecto.',
'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_product' => 'Plan Pro',
'pro_plan_description' => 'Un año de inscripción al Plan Pro de Invoice Ninja.',
'pro_plan_success' => '¡Gracias por unirte a Invoice Ninja! Al realizar el pago de tu factura, se iniciara tu PLAN PRO.',
'unsaved_changes' => 'Tienes cambios no guardados',
'custom_fields' => 'Campos a medida',
'company_fields' => 'Campos de la empresa',
'client_fields' => 'Campos del cliente',
'field_label' => 'Etiqueta del campo',
'field_value' => 'Valor del campo',
'edit' => 'Editar',
'view_as_recipient' => 'Ver como destinitario',
// Pro Plan
'pro_plan' => [
'remove_logo' => ':link haz click para eliminar el logo de Invoice Ninja',
'remove_logo_link' => 'Haz click aquí',
],
'logout' => 'Cerrar sesión',
'sign_up_to_save' => 'Registrate para guardar tu trabajo',
'agree_to_terms' => 'Estoy de acuerdo con los términos de Invoice Ninja :terms',
'terms_of_service' => 'Términos de servicio',
'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',
// product management
'product_library' => 'Inventario de productos',
'product' => 'Producto',
'products' => 'Productos',
'fill_products' => 'Auto-rellenar productos',
'fill_products_help' => 'Seleccionar un producto automáticamente <b>configurará la descripción y coste</b>',
'update_products' => 'Auto-actualizar productos',
'update_products_help' => 'Actualizar una factura automáticamente <b>actualizará los productos</b>',
'create_product' => 'Crear Producto',
'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',
'pro_plan_product' => 'Plan Pro',
'pro_plan_description' => 'Un año de inscripción al Plan Pro de Invoice Ninja.',
'pro_plan_success' => '¡Gracias por unirte a Invoice Ninja! Al realizar el pago de tu factura, se iniciara tu PLAN PRO.',
'unsaved_changes' => 'Tienes cambios no guardados',
'custom_fields' => 'Campos a medida',
'company_fields' => 'Campos de la empresa',
'client_fields' => 'Campos del cliente',
'field_label' => 'Etiqueta del campo',
'field_value' => 'Valor del campo',
'edit' => 'Editar',
'view_as_recipient' => 'Ver como destinitario',
// Quotes
'quote' => 'Presupuesto',
'quotes' => 'Presupuestos',
'quote_number' => 'Numero de Presupuesto',
'quote_number_short' => 'Presupuesto Nº ',
'quote_date' => 'Fecha Presupuesto',
'quote_total' => 'Total Presupuestado',
'your_quote' => 'Su Presupuesto',
'total' => 'Total',
'clone' => 'Clonar',
// product management
'product_library' => 'Inventario de productos',
'product' => 'Producto',
'products' => 'Productos',
'fill_products' => 'Auto-rellenar productos',
'fill_products_help' => 'Seleccionar un producto automáticamente <b>configurará la descripción y coste</b>',
'update_products' => 'Auto-actualizar productos',
'update_products_help' => 'Actualizar una factura automáticamente <b>actualizará los productos</b>',
'create_product' => 'Crear Producto',
'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',
'create_quote' => 'Crear Presupuesto',
'edit_quote' => 'Editar Presupuesto',
'archive_quote' => 'Archivar Presupuesto',
'delete_quote' => 'Eliminar Presupuesto',
'save_quote' => 'Guardar Presupuesto',
'email_quote' => 'Enviar Presupuesto',
'clone_quote' => 'Clonar Presupuesto',
'convert_to_invoice' => 'Convertir a Factura',
'view_invoice' => 'Ver Factura',
'view_quote' => 'Ver Presupuesto',
'view_client' => 'Ver Cliente',
// Quotes
'quote' => 'Presupuesto',
'quotes' => 'Presupuestos',
'quote_number' => 'Numero de Presupuesto',
'quote_number_short' => 'Presupuesto Nº ',
'quote_date' => 'Fecha Presupuesto',
'quote_total' => 'Total Presupuestado',
'your_quote' => 'Su Presupuesto',
'total' => 'Total',
'clone' => 'Clonar',
'updated_quote' => 'Presupuesto actualizado con éxito',
'created_quote' => 'Presupuesto creado con éxito',
'cloned_quote' => 'Presupuesto clonado con éxito',
'emailed_quote' => 'Presupuesto enviado con éxito',
'archived_quote' => 'Presupuesto archivado con éxito',
'archived_quotes' => ':count Presupuestos archivados con exito',
'deleted_quote' => 'Presupuesto eliminado con éxito',
'deleted_quotes' => ':count Presupuestos eliminados con exito',
'converted_to_invoice' => 'Presupuesto convertido a factura con éxito',
'new_quote' => 'Nuevo Presupuesto',
'create_quote' => 'Crear Presupuesto',
'edit_quote' => 'Editar Presupuesto',
'archive_quote' => 'Archivar Presupuesto',
'delete_quote' => 'Eliminar Presupuesto',
'save_quote' => 'Guardar Presupuesto',
'email_quote' => 'Enviar Presupuesto',
'clone_quote' => 'Clonar Presupuesto',
'convert_to_invoice' => 'Convertir a Factura',
'view_invoice' => 'Ver Factura',
'view_quote' => 'Ver Presupuesto',
'view_client' => 'Ver Cliente',
'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',
'updated_quote' => 'Presupuesto actualizado con éxito',
'created_quote' => 'Presupuesto creado con éxito',
'cloned_quote' => 'Presupuesto clonado con éxito',
'emailed_quote' => 'Presupuesto enviado con éxito',
'archived_quote' => 'Presupuesto archivado con éxito',
'archived_quotes' => ':count Presupuestos archivados con exito',
'deleted_quote' => 'Presupuesto eliminado con éxito',
'deleted_quotes' => ':count Presupuestos eliminados con exito',
'converted_to_invoice' => 'Presupuesto convertido a factura con éxito',
'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_2' => ':link para registrarse en Authorize.net.',
'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_27' => ':link para registrarse en TwoCheckout.',
'gateway_help_1' => ':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_23' => 'Nota: utilizar su clave de API secreta, no es su clave de API publica.',
'gateway_help_27' => ':link para registrarse en TwoCheckout.',
'more_designs' => 'Más diseños',
'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_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_text' => '',
'buy' => 'Comprar',
'bought_designs' => 'Añadidos con exito los diseños de factura',
'more_designs' => 'Más diseños',
'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_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_text' => '',
'buy' => 'Comprar',
'bought_designs' => 'Añadidos con exito los diseños de factura',
'sent' => 'Enviado',
'timesheets' => 'Parte de Horas',
'sent' => 'Enviado',
'timesheets' => 'Parte de Horas',
'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_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.',
'vat_number' => 'NIF/CIF',
'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_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.',
'vat_number' => 'NIF/CIF',
'id_number' => 'Número de Identificación',
'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_link' => 'Marca Blanca" ',
'bought_white_label' => 'Se ha conseguido con exito la licencia de Marca Blanca',
'white_labeled' => 'Marca Blanca',
'id_number' => 'Número de Identificación',
'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_link' => 'Marca Blanca" ',
'bought_white_label' => 'Se ha conseguido con exito la licencia de Marca Blanca',
'white_labeled' => 'Marca Blanca',
'restore' => 'Restaurar',
'restore_invoice' => 'Restaurar Factura',
'restore_quote' => 'Restaurar Presupuesto',
'restore_client' => 'Restaurar Cliente',
'restore_credit' => 'Restaurar Pendiente',
'restore_credit' => 'Restaurar Pendiente',
'restore_payment' => 'Restaurar Pago',
'restored_invoice' => 'Factura restaurada con éxito',
@ -479,7 +478,7 @@ return array(
'restored_client' => 'Cliente restaurada con éxito',
'restored_payment' => 'Pago restaurada con éxito',
'restored_credit' => 'Pendiente restaurada con éxito',
'reason_for_canceling' => 'Ayudenos a mejorar nuestro sitio diciendonos porque se va, Gracias',
'discount_percent' => 'Porcentaje',
'discount_amount' => 'Cantidad',
@ -503,7 +502,7 @@ return array(
'payment_email' => 'Email de Pagos',
'quote_email' => 'Email de Presupuestos',
'reset_all' => 'Restablecer Todos',
'approve' => 'Aprobar',
'approve' => 'Aprobar',
'token_billing_type_id' => 'Token Billing', //¿?
'token_billing_help' => 'Permite almacenar tarjetas de crédito para posteriormente realizarles el cobro.',
@ -604,7 +603,7 @@ return array(
'view_documentation' => 'View Documentation',
'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.',
'rows' => 'rows',
'www' => 'www',
'logo' => 'Logo',
@ -745,14 +744,14 @@ return array(
'current_user' => 'Current User',
'new_recurring_invoice' => 'New 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',
'primary_user' => 'Primary User',
'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>
<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>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>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>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',
'quote_due_date' => 'Valid Until',
@ -766,7 +765,7 @@ return array(
'status_partial' => 'Partial',
'status_paid' => 'Paid',
'show_line_item_tax' => 'Display <b>line item taxes</b> inline',
'iframe_url' => 'Website',
'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.',
@ -805,9 +804,9 @@ return array(
'invoice_charges' => 'Invoice Charges',
'invitation_status' => [
'sent' => 'Email Sent',
'opened' => 'Email Openend',
'viewed' => 'Invoice Viewed',
'sent' => 'Email Sent',
'opened' => 'Email Openend',
'viewed' => 'Invoice Viewed',
],
'notification_invoice_bounced' => 'We were unable to deliver Invoice :invoice to :contact.',
'notification_invoice_bounced_subject' => 'Unable to deliver Invoice :invoice',
@ -817,6 +816,14 @@ return array(
'custom_invoice_link' => 'Custom Invoice Link',
'total_invoiced' => 'Total Invoiced',
'open_balance' => 'Open Balance',
'verify_email' => 'Please visit the link in the account confirmation email to verify your email address.',
'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.",
"notmasked" => "The values are masked",
"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_signature' => 'Cordialement,',
'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 :',
'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',
@ -532,16 +532,16 @@ return array(
'invoice_footer' => 'Pied de facture',
'save_as_default_footer' => 'Définir comme pied de facture par défatu',
'token_management' => 'Token Management',
'tokens' => 'Tokens',
'add_token' => 'Add Token',
'show_deleted_tokens' => 'Show deleted tokens',
'deleted_token' => 'Successfully deleted token',
'created_token' => 'Successfully created token',
'updated_token' => 'Successfully updated token',
'edit_token' => 'Edit Token',
'delete_token' => 'Delete Token',
'token' => 'Token',
'token_management' => 'Gestion des jetons',
'tokens' => 'Jetons',
'add_token' => 'Ajouter jeton',
'show_deleted_tokens' => 'Voir les jetons supprimés',
'deleted_token' => 'Jeton supprimé avec succès',
'created_token' => 'Jeton crée avec succès',
'updated_token' => 'Jeton mis à jour avec succès',
'edit_token' => 'Editer jeton',
'delete_token' => 'Supprimer jeton',
'token' => 'Jeton',
'add_gateway' => 'Ajouter passerelle',
'delete_gateway' => 'Supprimer passerelle',
@ -560,17 +560,17 @@ return array(
'password_error_invalid' => 'Le nouveau mot de passe est invalide',
'updated_password' => 'Mot de passe mis à jour avec succès',
'api_tokens' => 'API Tokens',
'users_and_tokens' => 'Users & Tokens',
'account_login' => 'Account Login',
'api_tokens' => 'Jetons d\'API',
'users_and_tokens' => 'Utilisateurs & jetons',
'account_login' => 'Connexion à votre compte',
'recover_password' => 'Recover your password',
'forgot_password' => 'Mot de passe oublié ?',
'email_address' => 'Adresse email',
'lets_go' => 'Allons-y !',
'password_recovery' => 'Password Recovery',
'send_email' => 'Send email',
'set_password' => 'Set Password',
'converted' => 'Converted',
'password_recovery' => 'Récupération du mot de passe',
'send_email' => 'Envoyer email',
'set_password' => 'Ajouter mot de passe',
'converted' => 'Converti',
'email_approved' => 'Email me when a quote is <b>approved</b>',
'notification_quote_approved_subject' => 'Quote :invoice was approved by :client',
@ -730,14 +730,13 @@ return array(
'invoice_to' => 'Facture pour',
'invoice_no' => 'Facture n°',
'recent_payments' => 'Paiements récents',
'outstanding' => 'Extraordinaire',
'outstanding' => 'Impayé',
'manage_companies' => 'Gérer les sociétés',
'total_revenue' => 'Revenu total',
'current_user' => 'Utilisateur actuel',
'new_recurring_invoice' => 'Nouvelle 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',
'primary_user' => 'Utilisateur principal',
'help' => 'Aide',
@ -763,17 +762,17 @@ return array(
'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.',
'auto_bill' => 'Auto Bill',
'auto_bill' => 'Facturation automatique',
'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',
'subject' => 'Subject',
'body' => 'Body',
'first_reminder' => 'First Reminder',
'second_reminder' => 'Second Reminder',
'third_reminder' => 'Third Reminder',
'subject' => 'Sujet',
'body' => 'Corps',
'first_reminder' => 'Premier rappel',
'second_reminder' => 'Second rappel',
'third_reminder' => 'Troisième rappel',
'num_days_reminder' => 'Days after due date',
'reminder_subject' => 'Reminder: Invoice :invoice from :account',
'reset' => 'Reset',
@ -784,8 +783,8 @@ return array(
'last_sent_on' => 'Last sent on :date',
'page_expire' => 'This page will expire soon, :click_here to keep working',
'upcoming_quotes' => 'Upcoming Quotes',
'expired_quotes' => 'Expired Quotes',
'upcoming_quotes' => 'Devis à venir',
'expired_quotes' => 'Devis expirés',
'sign_up_using' => 'Sign up using',
'invalid_credentials' => 'These credentials do not match our records',
@ -797,9 +796,9 @@ return array(
'invoice_charges' => 'Invoice Charges',
'invitation_status' => [
'sent' => 'Email Sent',
'opened' => 'Email Openend',
'viewed' => 'Invoice Viewed',
'sent' => 'Email envoyé',
'opened' => 'Email ouvert',
'viewed' => 'Facture vue',
],
'notification_invoice_bounced' => 'We were unable to deliver Invoice :invoice to :contact.',
'notification_invoice_bounced_subject' => 'Unable to deliver Invoice :invoice',
@ -809,6 +808,16 @@ return array(
'custom_invoice_link' => 'Custom Invoice Link',
'total_invoiced' => 'Total Invoiced',
'open_balance' => 'Open Balance',
'verify_email' => 'Please visit the link in the account confirmation email to verify your email address.',
'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.",
"notmasked" => "The values are masked",
"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_signature' => 'Cordialement,',
'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 :',
'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',
@ -738,7 +738,7 @@ return array(
'current_user' => 'Current User',
'new_recurring_invoice' => 'New 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',
'primary_user' => 'Primary User',
'help' => 'Help',
@ -810,6 +810,15 @@ return array(
'custom_invoice_link' => 'Custom Invoice Link',
'total_invoiced' => 'Total Invoiced',
'open_balance' => 'Open Balance',
'verify_email' => 'Please visit the link in the account confirmation email to verify your email address.',
'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.",
"notmasked" => "Les valeurs sont masquées",
"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_signature' => 'Distinti saluti,',
'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:',
'notification_invoice_paid_subject' => 'La fattura :invoice è stata pagata da :client',
'notification_invoice_sent_subject' => 'La fattura :invoice è stata inviata a :client',
@ -740,7 +740,7 @@ return array(
'current_user' => 'Current User',
'new_recurring_invoice' => 'New 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',
'primary_user' => 'Primary User',
'help' => 'Help',
@ -812,5 +812,14 @@ return array(
'custom_invoice_link' => 'Custom Invoice Link',
'total_invoiced' => 'Total Invoiced',
'open_balance' => 'Open Balance',
'verify_email' => 'Please visit the link in the account confirmation email to verify your email address.',
'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.",
"notmasked" => "The values are masked",
"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_signature' => 'Regards,',
'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:',
'notification_invoice_paid_subject' => 'Invoice :invoice was paid by :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>',
'update_products' => 'Auto-update products',
'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',
'archive_product' => 'Archive Product',
'updated_product' => 'Successfully updated product',
@ -747,7 +747,7 @@ return array(
'current_user' => 'Current User',
'new_recurring_invoice' => 'New 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',
'primary_user' => 'Primary User',
'help' => 'Help',
@ -819,6 +819,15 @@ return array(
'custom_invoice_link' => 'Custom Invoice Link',
'total_invoiced' => 'Total Invoiced',
'open_balance' => 'Open Balance',
'verify_email' => 'Please visit the link in the account confirmation email to verify your email address.',
'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_signature' => 'Med vennlig hilsen,',
'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:',
'notification_invoice_paid_subject' => 'Faktura :invoice betalt av :client',
'notification_invoice_sent_subject' => 'Faktura :invoice sendt til :client',
@ -740,12 +740,12 @@ return array(
'recent_payments' => 'Recent Payments',
'outstanding' => 'Outstanding',
'manage_companies' => 'Manage Companies',
'total_revenue' => 'Total Revenue',
'total_revenue' => 'Total Revenue',
'current_user' => 'Current User',
'new_recurring_invoice' => 'New 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',
'primary_user' => 'Primary User',
'help' => 'Help',
@ -817,5 +817,14 @@ return array(
'custom_invoice_link' => 'Custom Invoice Link',
'total_invoiced' => 'Total Invoiced',
'open_balance' => 'Open Balance',
'verify_email' => 'Please visit the link in the account confirmation email to verify your email address.',
'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.",
"notmasked" => "Verdiene er skjult",
"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_signature' => 'Met vriendelijke groeten,',
'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:',
'notification_invoice_paid_subject' => 'Factuur :invoice is betaald door :client',
'notification_invoice_sent_subject' => 'Factuur :invoice is gezonden door :client',
@ -740,7 +740,6 @@ return array(
'current_user' => 'Huidige gebruiker',
'new_recurring_invoice' => 'Nieuwe 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',
'primary_user' => 'Primaire gebruiker',
'help' => 'Help',
@ -812,5 +811,15 @@ return array(
'custom_invoice_link' => 'Custom Invoice Link',
'total_invoiced' => 'Total Invoiced',
'open_balance' => 'Open Balance',
'verify_email' => 'Please visit the link in the account confirmation email to verify your email address.',
'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.",
"notmasked" => "De waarden zijn verborgen",
"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.",
"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.",
"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.",
"after" => ":attribute deve ser uma data maior que :date.",
"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.",
"array" => ":attribute deve ser um array.",
"array" => ":attribute deve ser uma lista.",
"before" => ":attribute deve ser uma data anterior a :date.",
"between" => array(
"numeric" => ":attribute deve ser entre :min - :max.",
"file" => ":attribute deve ser entre :min - :max kilobytes.",
"string" => ":attribute deve ser entre :min - :max caracteres.",
"numeric" => ":attribute deve estar entre :min - :max.",
"file" => ":attribute deve estar entre :min - :max kilobytes.",
"string" => ":attribute deve estar entre :min - :max caracteres.",
"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_format" => ":attribute não satisfaz o formato :format.",
"different" => ":attribute e :other devem ser diferentes.",
@ -71,8 +71,9 @@ return array(
"positive" => ":attribute deve ser maior que zero.",
"has_credit" => "O cliente não possui crédito suficiente.",
"notmasked" => "The values are masked",
"less_than" => 'The :attribute must be less than :value',
"notmasked" => "Os valores são mascarados",
"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_signature' => 'Vänliga hälsningar,',
'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:',
'notification_invoice_paid_subject' => 'Faktura :invoice är betald av :client',
'notification_invoice_sent_subject' => 'Faktura :invoice är skickad till :client',
@ -743,7 +743,7 @@ return array(
'current_user' => 'Current User',
'new_recurring_invoice' => 'New 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',
'primary_user' => 'Primary User',
'help' => 'Help',
@ -815,5 +815,14 @@ return array(
'custom_invoice_link' => 'Custom Invoice Link',
'total_invoiced' => 'Total Invoiced',
'open_balance' => 'Open Balance',
'verify_email' => 'Please visit the link in the account confirmation email to verify your email address.',
'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.",
"notmasked" => "The values are masked",
"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')
@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) !!}
@ -11,7 +13,7 @@
<div class="panel-heading">
<h3 class="panel-title">{!! trans($title) !!}</h3>
</div>
<div class="panel-body">
<div class="panel-body form-padding-right">
@if ($accountGateway)
{!! Former::populateField('gateway_id', $accountGateway->gateway_id) !!}
@ -106,7 +108,7 @@
<p/>&nbsp;<p/>
{!! 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'))) !!}
{!! Former::close() !!}

View File

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

View File

@ -1,16 +1,13 @@
@extends('accounts.nav')
@extends('header')
@section('head')
@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">
<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">
@ -27,10 +24,7 @@
@stop
@section('content')
@parent
@include('accounts.nav_advanced')
@parent
<script>
var invoiceDesigns = {!! $invoiceDesigns !!};
@ -155,17 +149,15 @@
{!! Former::select('invoice_design_id')->style('display:inline;width:120px')->fromQuery($invoiceDesigns, 'name', 'id')->onchange('onSelectChange()')->raw() !!}
<div class="pull-right">
{!! 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')) !!}
@if (Auth::user()->isPro())
{!! Button::success(trans('texts.save'))->withAttributes(['onclick' => 'submitForm()'])->appendIcon(Icon::create('floppy-disk')) !!}
@endif
{!! Button::normal(trans('texts.cancel'))->asLinkTo(URL::to('/settings/invoice_design'))->appendIcon(Icon::create('remove-circle')) !!}
{!! Button::success(trans('texts.save'))->withAttributes(['onclick' => 'submitForm()'])->appendIcon(Icon::create('floppy-disk'))->withAttributes(['class' => 'save-button']) !!}
</div>
</div>
<script>
@if (!Auth::user()->isPro())
$(function() {
$('form.warn-on-exit input').prop('disabled', true);
$('form.warn-on-exit input, .save-button').prop('disabled', true);
});
@endif

View File

@ -1,4 +1,4 @@
@extends('accounts.nav')
@extends('header')
@section('content')
@parent
@ -13,27 +13,20 @@
{!! Former::open_for_files()->addClass('warn-on-exit')->rules(array(
'name' => 'required',
'email' => 'email|required'
)) !!}
{{ Former::populate($account) }}
{{ Former::populateField('military_time', intval($account->military_time)) }}
{{ Former::populateField('first_name', $user->first_name) }}
{{ 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
@include('accounts.nav', ['selected' => ACCOUNT_COMPANY_DETAILS])
<div class="row">
<div class="col-md-6">
<div class="col-md-12">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{!! trans('texts.details') !!}</h3>
</div>
<div class="panel-body">
<div class="panel-body form-padding-right">
{!! Former::text('name') !!}
{!! Former::text('id_number') !!}
@ -58,7 +51,7 @@
<div class="panel-heading">
<h3 class="panel-title">{!! trans('texts.address') !!}</h3>
</div>
<div class="panel-body">
<div class="panel-body form-padding-right">
{!! Former::text('address1') !!}
{!! Former::text('address2') !!}
@ -72,133 +65,13 @@
</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>
<center>
{!! Button::success(trans('texts.save'))->submit()->large()->appendIcon(Icon::create('floppy-disk')) !!}
</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() !!}
{!! Form::open(['url' => 'remove_logo', 'class' => 'removeLogoForm']) !!}
@ -209,21 +82,6 @@
$(function() {
$('#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() {
@ -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>
@stop

View File

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

View File

@ -1,9 +1,11 @@
@extends('accounts.nav')
@extends('header')
@section('content')
@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-heading">
<h3 class="panel-title">{!! trans('texts.import_clients') !!}</h3>
@ -16,7 +18,7 @@
{!! 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-heading">
<h3 class="panel-title">{!! trans('texts.export_clients') !!}</h3>
@ -28,7 +30,7 @@
{!! 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-heading">
<h3 class="panel-title">{!! trans('texts.cancel_account') !!}</h3>

View File

@ -1,9 +1,11 @@
@extends('accounts.nav')
@extends('header')
@section('content')
@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-heading">
@ -46,7 +48,7 @@
{!! 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'))) !!}
{!! Former::close() !!}

View File

@ -1,18 +1,15 @@
@extends('accounts.nav')
@extends('header')
@section('head')
@parent
<script src="{{ asset('js/pdf_viewer.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>
<script src="{{ asset('js/pdf.built.js') }}" type="text/javascript"></script>
@stop
@section('content')
@parent
@include('accounts.nav_advanced')
@include('accounts.nav', ['selected' => ACCOUNT_INVOICE_DESIGN, 'advanced' => true])
<script>
var invoiceDesigns = {!! $invoiceDesigns !!};
@ -40,8 +37,8 @@
NINJA.secondaryColor = $('#secondary_color').val();
NINJA.fontSize = parseInt($('#font_size').val());
var fields = ['item', 'description', 'unit_cost', 'quantity'];
invoiceLabels.old = {};
var fields = ['item', 'description', 'unit_cost', 'quantity', 'line_total'];
invoiceLabels.old = {};
for (var i=0; i<fields.length; i++) {
var field = fields[i];
var val = $('#labels_' + field).val();
@ -51,9 +48,9 @@
if (val) {
invoiceLabels[field] = val;
}
}
}
generatePDF(invoice, getDesignJavascript(), true, cb);
generatePDF(invoice, getDesignJavascript(), true, cb);
}
$(function() {
@ -76,7 +73,7 @@
<div class="row">
<div class="col-md-6">
<div class="col-md-12">
{!! Former::open()->addClass('warn-on-exit')->onchange('refreshPDF()') !!}
{!! Former::populate($account) !!}
@ -91,58 +88,74 @@
<div class="panel-heading">
<h3 class="panel-title">{!! trans('texts.invoice_design') !!}</h3>
</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::select('invoice_design_id')->style('display:inline;width:120px')->fromQuery($invoiceDesigns, 'name', 'id')->addOption(trans('texts.more_designs') . '...', '-1') !!}
@else
{!! Former::select('invoice_design_id')->style('display:inline;width:120px')->fromQuery($invoiceDesigns, 'name', 'id') !!}
@endif
{!! 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')) !!}
{!! 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(
Button::primary(trans('texts.customize_design'))->small()->asLinkTo(URL::to('/company/advanced_settings/customize_design'))
) !!}
</div>
</div>
<div role="tabpanel" class="tab-pane" id="invoiceOptions">
<div class="panel-body">
</div>
</div>
{!! 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 class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{!! trans('texts.invoice_labels') !!}</h3>
</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>
</div>
@if (Auth::user()->isPro())
{!! Former::actions( Button::success(trans('texts.save'))->submit()->large()->appendIcon(Icon::create('floppy-disk'))) !!}
@else
<br/>
{!! Former::actions(
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>
$(function() {
$('form.warn-on-exit input').prop('disabled', true);
$('form.warn-on-exit input, .save-button').prop('disabled', true);
});
</script>
@endif
@ -150,11 +163,10 @@
{!! Former::close() !!}
</div>
<div class="col-md-6">
</div>
@include('invoices.pdf', ['account' => Auth::user()->account, 'pdfHeight' => 800])
</div>
</div>
@stop

View File

@ -1,4 +1,4 @@
@extends('accounts.nav')
@extends('header')
@section('head')
@parent
@ -10,7 +10,7 @@
.input-group-addon div.checkbox {
display: inline;
}
span.input-group-addon {
.tab-content .pad-checkbox span.input-group-addon {
padding-right: 30px;
}
</style>
@ -18,9 +18,9 @@
@section('content')
@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::populateField('custom_invoice_taxes1', intval($account->custom_invoice_taxes1)) }}
{{ Former::populateField('custom_invoice_taxes2', intval($account->custom_invoice_taxes2)) }}
@ -32,27 +32,30 @@
<div class="panel-heading">
<h3 class="panel-title">{!! trans('texts.email_settings') !!}</h3>
</div>
<div class="panel-body">
<div class="panel-body form-padding-right">
{!! Former::checkbox('pdf_email_attachment')->text(trans('texts.enable')) !!}
@if (Utils::isNinja())
{!! Former::inline_radios('custom_invoice_link')
->onchange('onCustomLinkChange()')
->radios([
trans('texts.subdomain') => ['value' => 'subdomain', 'name' => 'custom_link'],
trans('texts.website') => ['value' => 'website', 'name' => 'custom_link'],
])->check($account->iframe_url ? 'website' : 'subdomain') !!}
{{ Former::setOption('capitalize_translations', false) }}
{!! Former::text('subdomain')
->placeholder(trans('texts.www'))
->onchange('onSubdomainChange()')
->addGroupClass('subdomain')
->label(' ') !!}
{!! Former::text('iframe_url')
->placeholder('http://www.example.com/invoice')
->appendIcon('question-sign')
->addGroupClass('iframe_url')
->label(' ') !!}
@endif
{{-- Former::select('recurring_hour')->options($recurringHours) --}}
{!! Former::inline_radios('custom_invoice_link')
->onchange('onCustomLinkChange()')
->radios([
trans('texts.subdomain') => ['value' => 'subdomain', 'name' => 'custom_link'],
trans('texts.website') => ['value' => 'website', 'name' => 'custom_link'],
])->check($account->iframe_url ? 'website' : 'subdomain') !!}
{{ Former::setOption('capitalize_translations', false) }}
{!! Former::text('subdomain')
->placeholder(trans('texts.www'))
->onchange('onSubdomainChange()')
->addGroupClass('subdomain')
->label(' ') !!}
{!! Former::text('iframe_url')
->placeholder('http://www.example.com/invoice')
->appendIcon('question-sign')
->addGroupClass('iframe_url')
->label(' ') !!}
</div>
</div>
@ -60,7 +63,8 @@
<div class="panel-heading">
<h3 class="panel-title">{!! trans('texts.invoice_quote_number') !!}</h3>
</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="#invoiceNumber" aria-controls="invoiceNumber" role="tab" data-toggle="tab">{{ trans('texts.invoice_number') }}</a></li>
@ -70,18 +74,56 @@
<div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="invoiceNumber">
<div class="panel-body">
{!! Former::text('invoice_number_prefix')->label(trans('texts.prefix')) !!}
{!! Former::text('invoice_number_counter')->label(trans('texts.counter')) !!}
{!! Former::inline_radios('invoice_number_type')
->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 role="tabpanel" class="tab-pane" id="quoteNumber">
<div class="panel-body">
{!! Former::text('quote_number_prefix')->label(trans('texts.prefix')) !!}
{!! Former::text('quote_number_counter')->label(trans('texts.counter'))
->append(Former::checkbox('share_counter')->raw()->onclick('setQuoteNumberEnabled()') . ' ' . trans('texts.share_invoice_counter')) !!}
{!! Former::inline_radios('quote_number_type')
->onchange('onQuoteNumberTypeChange()')
->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>
@ -90,7 +132,7 @@
<div class="panel-heading">
<h3 class="panel-title">{!! trans('texts.custom_fields') !!}</h3>
</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">
@ -131,10 +173,16 @@
<div role="tabpanel" class="tab-pane" id="invoiceCharges">
<div class="panel-body">
{!! Former::text('custom_invoice_label1')->label(trans('texts.field_label'))
->append(Former::checkbox('custom_invoice_taxes1')->raw() . trans('texts.charge_taxes')) !!}
{!! Former::text('custom_invoice_label2')->label(trans('texts.field_label'))
->append(Former::checkbox('custom_invoice_taxes2')->raw() . trans('texts.charge_taxes')) !!}
{!! Former::text('custom_invoice_label1')
->label(trans('texts.field_label'))
->addGroupClass('pad-checkbox')
->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>
@ -147,12 +195,6 @@
<center>
{!! Button::success(trans('texts.save'))->large()->submit()->appendIcon(Icon::create('floppy-disk')) !!}
</center>
@else
<script>
$(function() {
$('form.warn-on-exit input').prop('disabled', true);
});
</script>
@endif
@ -185,6 +227,40 @@
</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() !!}
@ -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() {
$('#iframeHelpModal').modal('show');
});
$('.number-pattern .input-group-addon').click(function() {
$('#patternHelpModal').modal('show');
});
$(function() {
setQuoteNumberEnabled();
onCustomLinkChange();
onInvoiceNumberTypeChange();
onQuoteNumberTypeChange();
$('#subdomain').change(function() {
$('#iframe_url').val('');
@ -230,7 +334,7 @@
$('#iframe_url').change(function() {
$('#subdomain').val('');
});
});
});
</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">
{!! HTML::nav_link('company/details', 'company_details') !!}
{!! HTML::nav_link('company/payments', 'online_payments', 'gateways') !!}
{!! HTML::nav_link('company/products', 'product_library') !!}
{!! HTML::nav_link('company/notifications', 'notifications') !!}
{!! HTML::nav_link('company/import_export', 'import_export', 'company/import_map') !!}
{!! HTML::nav_link('company/advanced_settings/invoice_design', 'advanced_settings', '*/advanced_settings/*') !!}
</ul>
<div class="col-md-3">
@foreach([
BASIC_SETTINGS => \App\Models\Account::$basicSettings,
ADVANCED_SETTINGS => \App\Models\Account::$advancedSettings,
] as $type => $settings)
<div class="panel panel-default">
<div class="panel-heading" style="color:white">
{{ 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/>
@stop
<div class="col-md-9">

View File

@ -1,9 +1,11 @@
@extends('accounts.nav')
@extends('header')
@section('content')
@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::populateField('notify_sent', intval(Auth::user()->notify_sent)) }}
{{ 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">
<label for="invoice_terms" class="control-label col-lg-4 col-sm-4"></label>
<div class="col-lg-8 col-sm-8">
<div class="form-group">
<label for="invoice_terms" class="control-label col-lg-4 col-sm-4"></label>
<div class="col-lg-8 col-sm-8">
<div id="fb-root"></div>
<script>(function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/en_US/all.js#xfbml=1&appId=635126583203143";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));</script>
<div id="fb-root"></div>
<script>(function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/en_US/all.js#xfbml=1&appId=635126583203143";
fjs.parentNode.insertBefore(js, fjs);
}(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>
<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>
<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>
</div>
</div>
-->
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{!! trans('texts.custom_messages') !!}</h3>

View File

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

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