1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-09-25 02:37:10 +02:00

Merge remote-tracking branch 'remotes/ninja/master' into ninja

This commit is contained in:
trigras 2015-08-31 15:22:38 +03:00
commit c32f986106
146 changed files with 13894 additions and 5163 deletions

View File

@ -2,7 +2,7 @@ APP_ENV=production
APP_DEBUG=false APP_DEBUG=false
APP_URL=http://ninja.dev APP_URL=http://ninja.dev
APP_CIPHER=rijndael-128 APP_CIPHER=rijndael-128
APP_KEY APP_KEY=SomeRandomString
APP_TIMEZONE APP_TIMEZONE
DB_TYPE=mysql DB_TYPE=mysql
@ -19,5 +19,3 @@ MAIL_USERNAME
MAIL_FROM_ADDRESS MAIL_FROM_ADDRESS
MAIL_FROM_NAME MAIL_FROM_NAME
MAIL_PASSWORD MAIL_PASSWORD
ALLOW_NEW_ACCOUNTS

24
.gitignore vendored
View File

@ -13,18 +13,20 @@
/bootstrap/environment.php /bootstrap/environment.php
/vendor /vendor
/node_modules /node_modules
.env
/.DS_Store /.DS_Store
/Thumbs.db /Thumbs.db
.env.development.php /.env
.env.php /.env.development.php
.idea /.env.php
.project
error_log /error_log
public/error_log /auth.json
/public/error_log
/ninja.sublime-project /ninja.sublime-project
/ninja.sublime-workspace /ninja.sublime-workspace
auth.json /.phpstorm.meta.php
/_ide_helper.php
.phpstorm.meta.php /.idea
_ide_helper.php /.project
tests/_output/

View File

@ -51,7 +51,17 @@ module.exports = function(grunt) {
'public/vendor/knockout-mapping/build/output/knockout.mapping-latest.js', 'public/vendor/knockout-mapping/build/output/knockout.mapping-latest.js',
'public/vendor/knockout-sortable/build/knockout-sortable.min.js', 'public/vendor/knockout-sortable/build/knockout-sortable.min.js',
'public/vendor/underscore/underscore.js', 'public/vendor/underscore/underscore.js',
'public/vendor/bootstrap-datepicker/dist/js/bootstrap-datepicker.js', 'public/vendor/bootstrap-datepicker/dist/js/bootstrap-datepicker.min.js',
'public/vendor/bootstrap-datepicker/dist/locales/bootstrap-datepicker.de.min.js',
'public/vendor/bootstrap-datepicker/dist/locales/bootstrap-datepicker.da.min.js',
'public/vendor/bootstrap-datepicker/dist/locales/bootstrap-datepicker.pt-BR.min.js',
'public/vendor/bootstrap-datepicker/dist/locales/bootstrap-datepicker.nl.min.js',
'public/vendor/bootstrap-datepicker/dist/locales/bootstrap-datepicker.fr.min.js',
'public/vendor/bootstrap-datepicker/dist/locales/bootstrap-datepicker.it.min.js',
'public/vendor/bootstrap-datepicker/dist/locales/bootstrap-datepicker.lt.min.js',
'public/vendor/bootstrap-datepicker/dist/locales/bootstrap-datepicker.no.min.js',
'public/vendor/bootstrap-datepicker/dist/locales/bootstrap-datepicker.es.min.js',
'public/vendor/bootstrap-datepicker/dist/locales/bootstrap-datepicker.sv.min.js',
'public/vendor/typeahead.js/dist/typeahead.min.js', 'public/vendor/typeahead.js/dist/typeahead.min.js',
'public/vendor/accounting/accounting.min.js', 'public/vendor/accounting/accounting.min.js',
'public/vendor/spectrum/spectrum.js', 'public/vendor/spectrum/spectrum.js',

View File

@ -7,6 +7,7 @@ use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputArgument;
use App\Ninja\Mailers\ContactMailer as Mailer; use App\Ninja\Mailers\ContactMailer as Mailer;
use App\Ninja\Repositories\InvoiceRepository;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\InvoiceItem; use App\Models\InvoiceItem;
use App\Models\Invitation; use App\Models\Invitation;
@ -16,12 +17,14 @@ class SendRecurringInvoices extends Command
protected $name = 'ninja:send-invoices'; protected $name = 'ninja:send-invoices';
protected $description = 'Send recurring invoices'; protected $description = 'Send recurring invoices';
protected $mailer; protected $mailer;
protected $invoiceRepo;
public function __construct(Mailer $mailer) public function __construct(Mailer $mailer, InvoiceRepository $invoiceRepo)
{ {
parent::__construct(); parent::__construct();
$this->mailer = $mailer; $this->mailer = $mailer;
$this->invoiceRepo = $invoiceRepo;
} }
public function fire() public function fire()
@ -34,74 +37,14 @@ class SendRecurringInvoices extends Command
$this->info(count($invoices).' recurring invoice(s) found'); $this->info(count($invoices).' recurring invoice(s) found');
foreach ($invoices as $recurInvoice) { foreach ($invoices as $recurInvoice) {
if ($recurInvoice->client->deleted_at) {
continue;
}
if (!$recurInvoice->user->confirmed) {
continue;
}
$this->info('Processing Invoice '.$recurInvoice->id.' - Should send '.($recurInvoice->shouldSendToday() ? 'YES' : 'NO')); $this->info('Processing Invoice '.$recurInvoice->id.' - Should send '.($recurInvoice->shouldSendToday() ? 'YES' : 'NO'));
if (!$recurInvoice->shouldSendToday()) { $invoice = $this->invoiceRepo->createRecurringInvoice($recurInvoice);
continue;
}
$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->amount = $recurInvoice->amount;
$invoice->balance = $recurInvoice->amount;
$invoice->invoice_date = date_create()->format('Y-m-d');
$invoice->discount = $recurInvoice->discount;
$invoice->po_number = $recurInvoice->po_number;
$invoice->public_notes = Utils::processVariables($recurInvoice->public_notes);
$invoice->terms = Utils::processVariables($recurInvoice->terms);
$invoice->invoice_footer = Utils::processVariables($recurInvoice->invoice_footer);
$invoice->tax_name = $recurInvoice->tax_name;
$invoice->tax_rate = $recurInvoice->tax_rate;
$invoice->invoice_design_id = $recurInvoice->invoice_design_id;
$invoice->custom_value1 = $recurInvoice->custom_value1;
$invoice->custom_value2 = $recurInvoice->custom_value2;
$invoice->custom_taxes1 = $recurInvoice->custom_taxes1;
$invoice->custom_taxes2 = $recurInvoice->custom_taxes2;
$invoice->is_amount_discount = $recurInvoice->is_amount_discount;
if ($invoice->client->payment_terms != 0) {
$days = $invoice->client->payment_terms;
if ($days == -1) {
$days = 0;
}
$invoice->due_date = date_create()->modify($days.' day')->format('Y-m-d');
}
$invoice->save();
foreach ($recurInvoice->invoice_items as $recurItem) {
$item = InvoiceItem::createNew($recurItem);
$item->product_id = $recurItem->product_id;
$item->qty = $recurItem->qty;
$item->cost = $recurItem->cost;
$item->notes = Utils::processVariables($recurItem->notes);
$item->product_key = Utils::processVariables($recurItem->product_key);
$item->tax_name = $recurItem->tax_name;
$item->tax_rate = $recurItem->tax_rate;
$invoice->invoice_items()->save($item);
}
foreach ($recurInvoice->invitations as $recurInvitation) {
$invitation = Invitation::createNew($recurInvitation);
$invitation->contact_id = $recurInvitation->contact_id;
$invitation->invitation_key = str_random(RANDOM_KEY_LENGTH);
$invoice->invitations()->save($invitation);
}
if ($invoice) {
$recurInvoice->account->loadLocalizationSettings();
$this->mailer->sendInvoice($invoice); $this->mailer->sendInvoice($invoice);
}
$recurInvoice->last_sent_date = Carbon::now()->toDateTimeString();
$recurInvoice->save();
} }
$this->info('Done'); $this->info('Done');

View File

@ -40,7 +40,6 @@ use App\Ninja\Repositories\AccountRepository;
use App\Ninja\Mailers\UserMailer; use App\Ninja\Mailers\UserMailer;
use App\Ninja\Mailers\ContactMailer; use App\Ninja\Mailers\ContactMailer;
use App\Events\UserLoggedIn; use App\Events\UserLoggedIn;
use App\Events\UserSettingsChanged;
class AccountController extends BaseController class AccountController extends BaseController
{ {
@ -75,18 +74,18 @@ class AccountController extends BaseController
public function getStarted() public function getStarted()
{ {
$user = false;
$guestKey = Input::get('guest_key'); // local storage key to login until registered
$prevUserId = Session::pull(PREV_USER_ID); // last user id used to link to new account
if (Auth::check()) { if (Auth::check()) {
return Redirect::to('invoices/create'); return Redirect::to('invoices/create');
} }
if (!Utils::isNinja() && !Utils::allowNewAccounts() && Account::count() > 0) { if (!Utils::isNinja() && (Account::count() > 0 && !$prevUserId)) {
return Redirect::to('/login'); return Redirect::to('/login');
} }
$user = false;
$guestKey = Input::get('guest_key'); // local storage key to login until registered
$prevUserId = Session::pull(PREV_USER_ID); // last user id used to link to new account
if ($guestKey && !$prevUserId) { if ($guestKey && !$prevUserId) {
$user = User::where('password', '=', $guestKey)->first(); $user = User::where('password', '=', $guestKey)->first();
@ -110,7 +109,8 @@ class AccountController extends BaseController
Auth::login($user, true); Auth::login($user, true);
Event::fire(new UserLoggedIn()); Event::fire(new UserLoggedIn());
return Redirect::to('invoices/create')->with('sign_up', Input::get('sign_up')); $redirectTo = Input::get('redirect_to', 'invoices/create');
return Redirect::to($redirectTo)->with('sign_up', Input::get('sign_up'));
} }
public function enableProPlan() public function enableProPlan()
@ -149,6 +149,7 @@ class AccountController extends BaseController
public function showSection($section = ACCOUNT_DETAILS, $subSection = false) public function showSection($section = ACCOUNT_DETAILS, $subSection = false)
{ {
if ($section == ACCOUNT_DETAILS) { if ($section == ACCOUNT_DETAILS) {
$primaryUser = Auth::user()->account->users()->orderBy('id')->first();
$data = [ $data = [
'account' => Account::with('users')->findOrFail(Auth::user()->account_id), 'account' => Account::with('users')->findOrFail(Auth::user()->account_id),
'countries' => Cache::get('countries'), 'countries' => Cache::get('countries'),
@ -159,8 +160,9 @@ class AccountController extends BaseController
'datetimeFormats' => Cache::get('datetimeFormats'), 'datetimeFormats' => Cache::get('datetimeFormats'),
'currencies' => Cache::get('currencies'), 'currencies' => Cache::get('currencies'),
'languages' => Cache::get('languages'), 'languages' => Cache::get('languages'),
'showUser' => Auth::user()->id === Auth::user()->account->users()->first()->id, 'showUser' => Auth::user()->id === $primaryUser->id,
'title' => trans('texts.company_details'), 'title' => trans('texts.company_details'),
'primaryUser' => $primaryUser,
]; ];
return View::make('accounts.details', $data); return View::make('accounts.details', $data);
@ -188,16 +190,18 @@ class AccountController extends BaseController
} elseif ($section == ACCOUNT_IMPORT_EXPORT) { } elseif ($section == ACCOUNT_IMPORT_EXPORT) {
return View::make('accounts.import_export', ['title' => trans('texts.import_export')]); return View::make('accounts.import_export', ['title' => trans('texts.import_export')]);
} elseif ($section == ACCOUNT_ADVANCED_SETTINGS) { } elseif ($section == ACCOUNT_ADVANCED_SETTINGS) {
$account = Auth::user()->account; $account = Auth::user()->account->load('country');
$data = [ $data = [
'account' => $account, 'account' => $account,
'feature' => $subSection, 'feature' => $subSection,
'title' => trans('texts.invoice_settings'), 'title' => trans('texts.invoice_settings'),
]; ];
if ($subSection == ACCOUNT_INVOICE_DESIGN) { if ($subSection == ACCOUNT_INVOICE_DESIGN
|| $subSection == ACCOUNT_CUSTOMIZE_DESIGN) {
$invoice = new stdClass(); $invoice = new stdClass();
$client = new stdClass(); $client = new stdClass();
$contact = new stdClass();
$invoiceItem = new stdClass(); $invoiceItem = new stdClass();
$client->name = 'Sample Client'; $client->name = 'Sample Client';
@ -208,11 +212,17 @@ class AccountController extends BaseController
$client->work_phone = ''; $client->work_phone = '';
$client->work_email = ''; $client->work_email = '';
$invoice->invoice_number = Auth::user()->account->getNextInvoiceNumber(); $invoice->invoice_number = $account->getNextInvoiceNumber();
$invoice->invoice_date = date_create()->format('Y-m-d'); $invoice->invoice_date = Utils::fromSqlDate(date('Y-m-d'));
$invoice->account = json_decode(Auth::user()->account->toJson()); $invoice->account = json_decode($account->toJson());
$invoice->amount = $invoice->balance = 100; $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->cost = 100;
$invoiceItem->qty = 1; $invoiceItem->qty = 1;
$invoiceItem->notes = 'Notes'; $invoiceItem->notes = 'Notes';
@ -221,10 +231,23 @@ class AccountController extends BaseController
$invoice->client = $client; $invoice->client = $client;
$invoice->invoice_items = [$invoiceItem]; $invoice->invoice_items = [$invoiceItem];
$data['account'] = $account;
$data['invoice'] = $invoice; $data['invoice'] = $invoice;
$data['invoiceDesigns'] = InvoiceDesign::availableDesigns();
$data['invoiceLabels'] = json_decode($account->invoice_labels) ?: []; $data['invoiceLabels'] = json_decode($account->invoice_labels) ?: [];
$data['title'] = trans('texts.invoice_design'); $data['title'] = trans('texts.invoice_design');
$data['invoiceDesigns'] = InvoiceDesign::getDesigns();
$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_EMAIL_TEMPLATES) { } else if ($subSection == ACCOUNT_EMAIL_TEMPLATES) {
$data['invoiceEmail'] = $account->getEmailTemplate(ENTITY_INVOICE); $data['invoiceEmail'] = $account->getEmailTemplate(ENTITY_INVOICE);
$data['quoteEmail'] = $account->getEmailTemplate(ENTITY_QUOTE); $data['quoteEmail'] = $account->getEmailTemplate(ENTITY_QUOTE);
@ -263,6 +286,8 @@ class AccountController extends BaseController
return AccountController::saveInvoiceSettings(); return AccountController::saveInvoiceSettings();
} elseif ($subSection == ACCOUNT_INVOICE_DESIGN) { } elseif ($subSection == ACCOUNT_INVOICE_DESIGN) {
return AccountController::saveInvoiceDesign(); return AccountController::saveInvoiceDesign();
} elseif ($subSection == ACCOUNT_CUSTOMIZE_DESIGN) {
return AccountController::saveCustomizeDesign();
} elseif ($subSection == ACCOUNT_EMAIL_TEMPLATES) { } elseif ($subSection == ACCOUNT_EMAIL_TEMPLATES) {
return AccountController::saveEmailTemplates(); return AccountController::saveEmailTemplates();
} }
@ -271,6 +296,19 @@ class AccountController extends BaseController
} }
} }
private function saveCustomizeDesign() {
if (Auth::user()->account->isPro()) {
$account = Auth::user()->account;
$account->custom_design = Input::get('custom_design');
$account->invoice_design_id = CUSTOM_DESIGN;
$account->save();
Session::flash('message', trans('texts.updated_settings'));
}
return Redirect::to('company/advanced_settings/customize_design');
}
private function saveEmailTemplates() private function saveEmailTemplates()
{ {
if (Auth::user()->account->isPro()) { if (Auth::user()->account->isPro()) {
@ -322,7 +360,6 @@ class AccountController extends BaseController
$account->share_counter = Input::get('share_counter') ? true : false; $account->share_counter = Input::get('share_counter') ? true : false;
$account->pdf_email_attachment = Input::get('pdf_email_attachment') ? true : false; $account->pdf_email_attachment = Input::get('pdf_email_attachment') ? true : false;
$account->utf8_invoices = Input::get('utf8_invoices') ? true : false;
$account->auto_wrap = Input::get('auto_wrap') ? true : false; $account->auto_wrap = Input::get('auto_wrap') ? true : false;
if (!$account->share_counter) { if (!$account->share_counter) {
@ -596,9 +633,10 @@ class AccountController extends BaseController
{ {
$rules = array( $rules = array(
'name' => 'required', 'name' => 'required',
'logo' => 'sometimes|max:1024|mimes:jpeg,gif,png',
); );
$user = Auth::user()->account->users()->first(); $user = Auth::user()->account->users()->orderBy('id')->first();
if (Auth::user()->id === $user->id) { if (Auth::user()->id === $user->id) {
$rules['email'] = 'email|required|unique:users,email,'.$user->id.',id'; $rules['email'] = 'email|required|unique:users,email,'.$user->id.',id';
@ -647,6 +685,9 @@ class AccountController extends BaseController
$user->username = trim(Input::get('email')); $user->username = trim(Input::get('email'));
$user->email = trim(strtolower(Input::get('email'))); $user->email = trim(strtolower(Input::get('email')));
$user->phone = trim(Input::get('phone')); $user->phone = trim(Input::get('phone'));
if (Utils::isNinjaDev()) {
$user->dark_mode = Input::get('dark_mode') ? true : false;
}
$user->save(); $user->save();
} }
@ -654,21 +695,28 @@ class AccountController extends BaseController
if ($file = Input::file('logo')) { if ($file = Input::file('logo')) {
$path = Input::file('logo')->getRealPath(); $path = Input::file('logo')->getRealPath();
File::delete('logo/'.$account->account_key.'.jpg'); File::delete('logo/'.$account->account_key.'.jpg');
File::delete('logo/'.$account->account_key.'.png');
$image = Image::make($path);
$mimeType = $file->getMimeType(); $mimeType = $file->getMimeType();
if ($image->width() == 200 && $mimeType == 'image/jpeg') { if ($mimeType == 'image/jpeg') {
$file->move('logo/', $account->account_key . '.jpg'); $file->move('logo/', $account->account_key . '.jpg');
} else if ($mimeType == 'image/png') {
$file->move('logo/', $account->account_key . '.png');
} else { } else {
if (extension_loaded('fileinfo')) {
$image = Image::make($path);
$image->resize(200, 120, function ($constraint) { $image->resize(200, 120, function ($constraint) {
$constraint->aspectRatio(); $constraint->aspectRatio();
}); });
Image::canvas($image->width(), $image->height(), '#FFFFFF')->insert($image)->save($account->getLogoPath()); Image::canvas($image->width(), $image->height(), '#FFFFFF')
->insert($image)->save('logo/'.$account->account_key.'.jpg');
} else {
Session::flash('warning', 'Warning: To support gifs the fileinfo PHP extension needs to be enabled.');
}
} }
} }
Event::fire(new UserSettingsChanged());
Session::flash('message', trans('texts.updated_settings')); Session::flash('message', trans('texts.updated_settings'));
return Redirect::to('company/details'); return Redirect::to('company/details');
@ -678,6 +726,7 @@ class AccountController extends BaseController
public function removeLogo() public function removeLogo()
{ {
File::delete('logo/'.Auth::user()->account->account_key.'.jpg'); File::delete('logo/'.Auth::user()->account->account_key.'.jpg');
File::delete('logo/'.Auth::user()->account->account_key.'.png');
Session::flash('message', trans('texts.removed_logo')); Session::flash('message', trans('texts.removed_logo'));
@ -719,7 +768,7 @@ class AccountController extends BaseController
$user->registered = true; $user->registered = true;
$user->save(); $user->save();
if (Utils::isNinja()) { if (Utils::isNinjaProd()) {
$this->userMailer->sendConfirmation($user); $this->userMailer->sendConfirmation($user);
} }

View File

@ -257,6 +257,8 @@ class AccountGatewayController extends BaseController
} }
$accountGateway->accepted_credit_cards = $cardCount; $accountGateway->accepted_credit_cards = $cardCount;
$accountGateway->show_address = Input::get('show_address') ? true : false;
$accountGateway->update_address = Input::get('update_address') ? true : false;
$accountGateway->config = json_encode($config); $accountGateway->config = json_encode($config);
if ($accountGatewayPublicId) { if ($accountGatewayPublicId) {
@ -278,7 +280,7 @@ class AccountGatewayController extends BaseController
Session::flash('message', $message); Session::flash('message', $message);
return Redirect::to('company/payments'); return Redirect::to("gateways/{$accountGateway->public_id}/edit");
} }
} }

View File

@ -34,7 +34,7 @@ class AppController extends BaseController
public function showSetup() public function showSetup()
{ {
if (Utils::isNinja() || (Utils::isDatabaseSetup() && Account::count() > 0)) { if (Utils::isNinjaProd() || (Utils::isDatabaseSetup() && Account::count() > 0)) {
return Redirect::to('/'); return Redirect::to('/');
} }
@ -43,7 +43,7 @@ class AppController extends BaseController
public function doSetup() public function doSetup()
{ {
if (Utils::isNinja() || (Utils::isDatabaseSetup() && Account::count() > 0)) { if (Utils::isNinjaProd() || (Utils::isDatabaseSetup() && Account::count() > 0)) {
return Redirect::to('/'); return Redirect::to('/');
} }
@ -88,7 +88,7 @@ class AppController extends BaseController
"MAIL_HOST={$mail['host']}\n". "MAIL_HOST={$mail['host']}\n".
"MAIL_USERNAME={$mail['username']}\n". "MAIL_USERNAME={$mail['username']}\n".
"MAIL_FROM_NAME={$mail['from']['name']}\n". "MAIL_FROM_NAME={$mail['from']['name']}\n".
"MAIL_PASSWORD={$mail['password']}\n"; "MAIL_PASSWORD={$mail['password']}";
// Write Config Settings // Write Config Settings
$fp = fopen(base_path()."/.env", 'w'); $fp = fopen(base_path()."/.env", 'w');
@ -101,6 +101,7 @@ class AppController extends BaseController
if (Industry::count() == 0) { if (Industry::count() == 0) {
Artisan::call('db:seed', array('--force' => true)); Artisan::call('db:seed', array('--force' => true));
} }
Cache::flush();
Artisan::call('optimize', array('--force' => true)); Artisan::call('optimize', array('--force' => true));
$firstName = trim(Input::get('first_name')); $firstName = trim(Input::get('first_name'));
@ -159,7 +160,7 @@ class AppController extends BaseController
public function install() public function install()
{ {
if (!Utils::isNinja() && !Utils::isDatabaseSetup()) { if (!Utils::isNinjaProd() && !Utils::isDatabaseSetup()) {
try { try {
Artisan::call('migrate', array('--force' => true)); Artisan::call('migrate', array('--force' => true));
if (Industry::count() == 0) { if (Industry::count() == 0) {
@ -176,7 +177,7 @@ class AppController extends BaseController
public function update() public function update()
{ {
if (!Utils::isNinja()) { if (!Utils::isNinjaProd()) {
try { try {
Artisan::call('migrate', array('--force' => true)); Artisan::call('migrate', array('--force' => true));
Artisan::call('db:seed', array('--force' => true, '--class' => 'PaymentLibrariesSeeder')); Artisan::call('db:seed', array('--force' => true, '--class' => 'PaymentLibrariesSeeder'));

View File

@ -62,7 +62,7 @@ class AuthController extends Controller {
$userId = Auth::check() ? Auth::user()->id : null; $userId = Auth::check() ? Auth::user()->id : null;
$user = User::where('email', '=', $request->input('email'))->first(); $user = User::where('email', '=', $request->input('email'))->first();
if ($user->failed_logins >= 3) { if ($user && $user->failed_logins >= 3) {
Session::flash('error', 'These credentials do not match our records.'); Session::flash('error', 'These credentials do not match our records.');
return redirect()->to('login'); return redirect()->to('login');
} }

View File

@ -268,7 +268,7 @@ class ClientController extends BaseController
$record = Contact::createNew(); $record = Contact::createNew();
} }
$record->email = trim(strtolower($contact->email)); $record->email = trim($contact->email);
$record->first_name = trim($contact->first_name); $record->first_name = trim($contact->first_name);
$record->last_name = trim($contact->last_name); $record->last_name = trim($contact->last_name);
$record->phone = trim($contact->phone); $record->phone = trim($contact->phone);
@ -306,7 +306,7 @@ class ClientController extends BaseController
Session::flash('message', $message); Session::flash('message', $message);
if ($action == 'restore' && $count == 1) { if ($action == 'restore' && $count == 1) {
return Redirect::to('clients/'.$ids[0]); return Redirect::to('clients/'.Utils::getFirst($ids));
} else { } else {
return Redirect::to('clients'); return Redirect::to('clients');
} }

View File

@ -5,6 +5,7 @@ use DB;
use View; use View;
use App\Models\Activity; use App\Models\Activity;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\Payment;
class DashboardController extends BaseController class DashboardController extends BaseController
{ {
@ -44,45 +45,87 @@ class DashboardController extends BaseController
->where('accounts.id', '=', Auth::user()->account_id) ->where('accounts.id', '=', Auth::user()->account_id)
->where('clients.is_deleted', '=', false) ->where('clients.is_deleted', '=', false)
->where('invoices.is_deleted', '=', false) ->where('invoices.is_deleted', '=', false)
->where('invoices.is_quote', '=', false)
->where('invoices.is_recurring', '=', false)
->groupBy('accounts.id') ->groupBy('accounts.id')
->groupBy(DB::raw('CASE WHEN clients.currency_id IS NULL THEN CASE WHEN accounts.currency_id IS NULL THEN 1 ELSE accounts.currency_id END ELSE clients.currency_id END')) ->groupBy(DB::raw('CASE WHEN clients.currency_id IS NULL THEN CASE WHEN accounts.currency_id IS NULL THEN 1 ELSE accounts.currency_id END ELSE clients.currency_id END'))
->get(); ->get();
$select = DB::raw('SUM(clients.balance) as value, clients.currency_id as currency_id');
$balances = DB::table('accounts')
->select($select)
->leftJoin('clients', 'accounts.id', '=', 'clients.account_id')
->where('accounts.id', '=', Auth::user()->account_id)
->where('clients.is_deleted', '=', false)
->groupBy('accounts.id')
->groupBy(DB::raw('CASE WHEN clients.currency_id IS NULL THEN CASE WHEN accounts.currency_id IS NULL THEN 1 ELSE accounts.currency_id END ELSE clients.currency_id END'))
->get();
$activities = Activity::where('activities.account_id', '=', Auth::user()->account_id) $activities = Activity::where('activities.account_id', '=', Auth::user()->account_id)
->where('activity_type_id', '>', 0) ->where('activity_type_id', '>', 0)
->orderBy('created_at', 'desc')->take(14)->get(); ->orderBy('created_at', 'desc')
->take(50)
->get();
$pastDue = Invoice::scope()->whereHas('client', function($query) { $pastDue = DB::table('invoices')
$query->where('deleted_at', '=', null); ->leftJoin('clients', 'clients.id', '=', 'invoices.client_id')
}) ->leftJoin('contacts', 'contacts.client_id', '=', 'clients.id')
->where('due_date', '<', date('Y-m-d')) ->where('invoices.account_id', '=', Auth::user()->account_id)
->where('balance', '>', 0) ->where('clients.deleted_at', '=', null)
->where('is_recurring', '=', false) ->where('contacts.deleted_at', '=', null)
->where('is_quote', '=', false) ->where('invoices.is_recurring', '=', false)
->where('is_deleted', '=', false) ->where('invoices.is_quote', '=', false)
->orderBy('due_date', 'asc')->take(6)->get(); ->where('invoices.balance', '>', 0)
->where('invoices.is_deleted', '=', false)
->where('contacts.is_primary', '=', true)
->where('invoices.due_date', '<', date('Y-m-d'))
->select(['invoices.due_date', 'invoices.balance', 'invoices.public_id', 'invoices.invoice_number', 'clients.name as client_name', 'contacts.email', 'contacts.first_name', 'contacts.last_name', 'clients.currency_id', 'clients.public_id as client_public_id'])
->orderBy('invoices.due_date', 'asc')
->take(50)
->get();
$upcoming = DB::table('invoices')
->leftJoin('clients', 'clients.id', '=', 'invoices.client_id')
->leftJoin('contacts', 'contacts.client_id', '=', 'clients.id')
->where('invoices.account_id', '=', Auth::user()->account_id)
->where('clients.deleted_at', '=', null)
->where('contacts.deleted_at', '=', null)
->where('invoices.is_recurring', '=', false)
->where('invoices.is_quote', '=', false)
->where('invoices.balance', '>', 0)
->where('invoices.is_deleted', '=', false)
->where('contacts.is_primary', '=', true)
->where('invoices.due_date', '>=', date('Y-m-d'))
->orderBy('invoices.due_date', 'asc')
->take(50)
->select(['invoices.due_date', 'invoices.balance', 'invoices.public_id', 'invoices.invoice_number', 'clients.name as client_name', 'contacts.email', 'contacts.first_name', 'contacts.last_name', 'clients.currency_id', 'clients.public_id as client_public_id'])
->get();
$payments = DB::table('payments')
->leftJoin('clients', 'clients.id', '=', 'payments.client_id')
->leftJoin('contacts', 'contacts.client_id', '=', 'clients.id')
->leftJoin('invoices', 'invoices.id', '=', 'payments.invoice_id')
->where('payments.account_id', '=', Auth::user()->account_id)
->where('clients.deleted_at', '=', null)
->where('contacts.deleted_at', '=', null)
->where('contacts.is_primary', '=', true)
->select(['payments.payment_date', 'payments.amount', 'invoices.public_id', 'invoices.invoice_number', 'clients.name as client_name', 'contacts.email', 'contacts.first_name', 'contacts.last_name', 'clients.currency_id', 'clients.public_id as client_public_id'])
->orderBy('payments.id', 'desc')
->take(50)
->get();
$upcoming = Invoice::scope()->whereHas('client', function($query) {
$query->where('deleted_at', '=', null);
})
->where('due_date', '>=', date('Y-m-d'))
->where('balance', '>', 0)
->where('is_recurring', '=', false)
->where('is_quote', '=', false)
->where('is_deleted', '=', false)
->orderBy('due_date', 'asc')->take(6)->get();
$data = [ $data = [
'account' => Auth::user()->account,
'paidToDate' => $paidToDate, 'paidToDate' => $paidToDate,
'balances' => $balances,
'averageInvoice' => $averageInvoice, 'averageInvoice' => $averageInvoice,
//'billedClients' => $metrics ? $metrics->billed_clients : 0,
'invoicesSent' => $metrics ? $metrics->invoices_sent : 0, 'invoicesSent' => $metrics ? $metrics->invoices_sent : 0,
'activeClients' => $metrics ? $metrics->active_clients : 0, 'activeClients' => $metrics ? $metrics->active_clients : 0,
'activities' => $activities, 'activities' => $activities,
'pastDue' => $pastDue, 'pastDue' => $pastDue,
'upcoming' => $upcoming, 'upcoming' => $upcoming,
'payments' => $payments,
'title' => trans('texts.dashboard'), 'title' => trans('texts.dashboard'),
]; ];

View File

@ -43,14 +43,15 @@ class HomeController extends BaseController
public function invoiceNow() public function invoiceNow()
{ {
if (Auth::check() && Input::get('new_account')) { if (Auth::check() && Input::get('new_company')) {
Session::put(PREV_USER_ID, Auth::user()->id); Session::put(PREV_USER_ID, Auth::user()->id);
Auth::user()->clearSession(); Auth::user()->clearSession();
Auth::logout(); Auth::logout();
} }
if (Auth::check()) { if (Auth::check()) {
return Redirect::to('invoices/create')->with('sign_up', Input::get('sign_up')); $redirectTo = Input::get('redirect_to', 'invoices/create');
return Redirect::to($redirectTo)->with('sign_up', Input::get('sign_up'));
} else { } else {
return View::make('public.header', ['invoiceNow' => true]); return View::make('public.header', ['invoiceNow' => true]);
} }
@ -72,9 +73,9 @@ class HomeController extends BaseController
$user->news_feed_id = $newsFeedId; $user->news_feed_id = $newsFeedId;
$user->save(); $user->save();
} }
}
Session::forget('news_feed_message'); Session::forget('news_feed_message');
}
return 'success'; return 'success';
} }

View File

@ -26,7 +26,11 @@ class InvoiceApiController extends Controller
public function index() public function index()
{ {
$invoices = Invoice::scope()->with('client', 'invitations.account')->where('invoices.is_quote', '=', false)->orderBy('created_at', 'desc')->get(); $invoices = Invoice::scope()
->with('client', 'invitations.account')
->where('invoices.is_quote', '=', false)
->orderBy('created_at', 'desc')
->get();
// Add the first invitation link to the data // Add the first invitation link to the data
foreach ($invoices as $key => $invoice) { foreach ($invoices as $key => $invoice) {
@ -50,12 +54,14 @@ class InvoiceApiController extends Controller
$error = null; $error = null;
// check if the invoice number is set and unique // check if the invoice number is set and unique
if (!isset($data['invoice_number'])) { if (!isset($data['invoice_number']) && !isset($data['id'])) {
$data['invoice_number'] = Auth::user()->account->getNextInvoiceNumber(); $data['invoice_number'] = Auth::user()->account->getNextInvoiceNumber();
} else { } else if (isset($data['invoice_number'])) {
$invoice = Invoice::scope()->where('invoice_number', '=', $data['invoice_number'])->first(); $invoice = Invoice::scope()->where('invoice_number', '=', $data['invoice_number'])->first();
if ($invoice) { if ($invoice) {
$error = trans('validation.unique', ['attribute' => 'texts.invoice_number']); $error = trans('validation.unique', ['attribute' => 'texts.invoice_number']);
} else {
$data['id'] = $invoice->public_id;
} }
} }
@ -100,11 +106,13 @@ class InvoiceApiController extends Controller
$data['client_id'] = $client->id; $data['client_id'] = $client->id;
$invoice = $this->invoiceRepo->save(false, $data, false); $invoice = $this->invoiceRepo->save(false, $data, false);
if (!isset($data['id'])) {
$invitation = Invitation::createNew(); $invitation = Invitation::createNew();
$invitation->invoice_id = $invoice->id; $invitation->invoice_id = $invoice->id;
$invitation->contact_id = $client->contacts[0]->id; $invitation->contact_id = $client->contacts[0]->id;
$invitation->invitation_key = str_random(RANDOM_KEY_LENGTH); $invitation->invitation_key = str_random(RANDOM_KEY_LENGTH);
$invitation->save(); $invitation->save();
}
if (isset($data['email_invoice']) && $data['email_invoice']) { if (isset($data['email_invoice']) && $data['email_invoice']) {
$this->mailer->sendInvoice($invoice); $this->mailer->sendInvoice($invoice);

View File

@ -60,8 +60,7 @@ class InvoiceController extends BaseController
'columns' => Utils::trans(['checkbox', 'invoice_number', 'client', 'invoice_date', 'invoice_total', 'balance_due', 'due_date', 'status', 'action']), 'columns' => Utils::trans(['checkbox', 'invoice_number', 'client', 'invoice_date', 'invoice_total', 'balance_due', 'due_date', 'status', 'action']),
]; ];
$recurringInvoices = Invoice::scope() $recurringInvoices = Invoice::scope()->where('is_recurring', '=', true);
->where('is_recurring', '=', true);
if (Session::get('show_trash:invoice')) { if (Session::get('show_trash:invoice')) {
$recurringInvoices->withTrashed(); $recurringInvoices->withTrashed();
@ -86,11 +85,12 @@ class InvoiceController extends BaseController
} }
$invitation = Invitation::with('account')->where('invitation_key', '=', $invitationKey)->first(); $invitation = Invitation::with('account')->where('invitation_key', '=', $invitationKey)->first();
$color = $invitation->account->primary_color ? $invitation->account->primary_color : '#0b4d78'; $account = $invitation->account;
$color = $account->primary_color ? $account->primary_color : '#0b4d78';
$data = [ $data = [
'color' => $color, 'color' => $color,
'hideLogo' => Session::get('white_label'), 'hideLogo' => $account->isWhiteLabel(),
'title' => trans('texts.invoices'), 'title' => trans('texts.invoices'),
'entityType' => ENTITY_INVOICE, 'entityType' => ENTITY_INVOICE,
'columns' => Utils::trans(['invoice_number', 'invoice_date', 'invoice_total', 'balance_due', 'due_date']), 'columns' => Utils::trans(['invoice_number', 'invoice_date', 'invoice_total', 'balance_due', 'due_date']),
@ -205,7 +205,6 @@ class InvoiceController extends BaseController
Session::set($invitationKey, true); Session::set($invitationKey, true);
Session::set('invitation_key', $invitationKey); Session::set('invitation_key', $invitationKey);
Session::set('white_label', $account->isWhiteLabel());
$account->loadLocalizationSettings(); $account->loadLocalizationSettings();
@ -213,6 +212,12 @@ class InvoiceController extends BaseController
$invoice->due_date = Utils::fromSqlDate($invoice->due_date); $invoice->due_date = Utils::fromSqlDate($invoice->due_date);
$invoice->is_pro = $account->isPro(); $invoice->is_pro = $account->isPro();
if ($invoice->invoice_design_id == CUSTOM_DESIGN) {
$invoice->invoice_design->javascript = $account->custom_design;
} else {
$invoice->invoice_design->javascript = $invoice->invoice_design->pdfmake;
}
$contact = $invitation->contact; $contact = $invitation->contact;
$contact->setVisible([ $contact->setVisible([
'first_name', 'first_name',
@ -250,7 +255,7 @@ class InvoiceController extends BaseController
'invoiceLabels' => $account->getInvoiceLabels(), 'invoiceLabels' => $account->getInvoiceLabels(),
'contact' => $contact, 'contact' => $contact,
'paymentTypes' => $paymentTypes, 'paymentTypes' => $paymentTypes,
'paymentURL' => $paymentURL 'paymentURL' => $paymentURL,
); );
return View::make('invoices.view', $data); return View::make('invoices.view', $data);
@ -277,7 +282,7 @@ class InvoiceController extends BaseController
$method = 'POST'; $method = 'POST';
$url = "{$entityType}s"; $url = "{$entityType}s";
} else { } else {
Utils::trackViewed($invoice->invoice_number.' - '.$invoice->client->getDisplayName(), $invoice->getEntityType()); Utils::trackViewed($invoice->getDisplayName().' - '.$invoice->client->getDisplayName(), $invoice->getEntityType());
$method = 'PUT'; $method = 'PUT';
$url = "{$entityType}s/{$publicId}"; $url = "{$entityType}s/{$publicId}";
} }
@ -321,11 +326,11 @@ class InvoiceController extends BaseController
$actions[] = ['url' => 'javascript:onArchiveClick()', 'label' => trans("texts.archive_{$entityType}")]; $actions[] = ['url' => 'javascript:onArchiveClick()', 'label' => trans("texts.archive_{$entityType}")];
$actions[] = ['url' => 'javascript:onDeleteClick()', 'label' => trans("texts.delete_{$entityType}")]; $actions[] = ['url' => 'javascript:onDeleteClick()', 'label' => trans("texts.delete_{$entityType}")];
$lastSent = ($invoice->is_recurring && $invoice->last_sent_date) ? $invoice->recurring_invoices->last() : null;
$data = array( $data = array(
'entityType' => $entityType, 'entityType' => $entityType,
'showBreadcrumbs' => $clone, 'showBreadcrumbs' => $clone,
'account' => $invoice->account,
'invoice' => $invoice, 'invoice' => $invoice,
'data' => false, 'data' => false,
'method' => $method, 'method' => $method,
@ -333,7 +338,9 @@ class InvoiceController extends BaseController
'url' => $url, 'url' => $url,
'title' => trans("texts.edit_{$entityType}"), 'title' => trans("texts.edit_{$entityType}"),
'client' => $invoice->client, 'client' => $invoice->client,
'actions' => $actions); 'isRecurring' => $invoice->is_recurring,
'actions' => $actions,
'lastSent' => $lastSent);
$data = array_merge($data, self::getViewModel()); $data = array_merge($data, self::getViewModel());
// Set the invitation link on the client's contacts // Set the invitation link on the client's contacts
@ -356,11 +363,10 @@ class InvoiceController extends BaseController
return View::make('invoices.edit', $data); return View::make('invoices.edit', $data);
} }
public function create($clientPublicId = 0) public function create($clientPublicId = 0, $isRecurring = false)
{ {
$client = null; $client = null;
$invoiceNumber = Auth::user()->account->getNextInvoiceNumber(); $invoiceNumber = $isRecurring ? microtime(true) : Auth::user()->account->getNextInvoiceNumber();
$account = Account::with('country')->findOrFail(Auth::user()->account_id);
if ($clientPublicId) { if ($clientPublicId) {
$client = Client::scope($clientPublicId)->firstOrFail(); $client = Client::scope($clientPublicId)->firstOrFail();
@ -368,20 +374,24 @@ class InvoiceController extends BaseController
$data = array( $data = array(
'entityType' => ENTITY_INVOICE, 'entityType' => ENTITY_INVOICE,
'account' => $account,
'invoice' => null, 'invoice' => null,
'data' => Input::old('data'), 'data' => Input::old('data'),
'invoiceNumber' => $invoiceNumber, 'invoiceNumber' => $invoiceNumber,
'method' => 'POST', 'method' => 'POST',
'url' => 'invoices', 'url' => 'invoices',
'title' => trans('texts.new_invoice'), 'title' => trans('texts.new_invoice'),
'client' => $client, 'isRecurring' => $isRecurring,
'tasks' => Session::get('tasks') ? json_encode(Session::get('tasks')) : null); 'client' => $client);
$data = array_merge($data, self::getViewModel()); $data = array_merge($data, self::getViewModel());
return View::make('invoices.edit', $data); return View::make('invoices.edit', $data);
} }
public function createRecurring($clientPublicId = 0)
{
return self::create($clientPublicId, true);
}
private static function getViewModel() private static function getViewModel()
{ {
$recurringHelp = ''; $recurringHelp = '';
@ -396,7 +406,7 @@ class InvoiceController extends BaseController
} }
return [ return [
'account' => Auth::user()->account, 'account' => Auth::user()->account->load('country'),
'products' => Product::scope()->orderBy('id')->get(array('product_key', 'notes', 'cost', 'qty')), 'products' => Product::scope()->orderBy('id')->get(array('product_key', 'notes', 'cost', 'qty')),
'countries' => Cache::get('countries'), 'countries' => Cache::get('countries'),
'clients' => Client::scope()->with('contacts', 'country')->orderBy('name')->get(), 'clients' => Client::scope()->with('contacts', 'country')->orderBy('name')->get(),
@ -405,7 +415,7 @@ class InvoiceController extends BaseController
'sizes' => Cache::get('sizes'), 'sizes' => Cache::get('sizes'),
'paymentTerms' => Cache::get('paymentTerms'), 'paymentTerms' => Cache::get('paymentTerms'),
'industries' => Cache::get('industries'), 'industries' => Cache::get('industries'),
'invoiceDesigns' => InvoiceDesign::availableDesigns(), 'invoiceDesigns' => InvoiceDesign::getDesigns(),
'frequencies' => array( 'frequencies' => array(
1 => 'Weekly', 1 => 'Weekly',
2 => 'Two weeks', 2 => 'Two weeks',
@ -417,7 +427,7 @@ class InvoiceController extends BaseController
), ),
'recurringHelp' => $recurringHelp, 'recurringHelp' => $recurringHelp,
'invoiceLabels' => Auth::user()->account->getInvoiceLabels(), 'invoiceLabels' => Auth::user()->account->getInvoiceLabels(),
'tasks' => Session::get('tasks') ? json_encode(Session::get('tasks')) : null,
]; ];
} }
@ -511,7 +521,16 @@ class InvoiceController extends BaseController
return $this->convertQuote($publicId); return $this->convertQuote($publicId);
} elseif ($action == 'email') { } elseif ($action == 'email') {
if (Auth::user()->confirmed && !Auth::user()->isDemo()) { if (Auth::user()->confirmed && !Auth::user()->isDemo()) {
if ($invoice->is_recurring) {
if ($invoice->shouldSendToday()) {
$invoice = $this->invoiceRepo->createRecurringInvoice($invoice);
$response = $this->mailer->sendInvoice($invoice); $response = $this->mailer->sendInvoice($invoice);
} else {
$response = trans('texts.recurring_too_soon');
}
} else {
$response = $this->mailer->sendInvoice($invoice);
}
if ($response === true) { if ($response === true) {
$message = trans("texts.emailed_{$entityType}"); $message = trans("texts.emailed_{$entityType}");
Session::flash('message', $message); Session::flash('message', $message);
@ -577,7 +596,7 @@ class InvoiceController extends BaseController
} }
if ($action == 'restore' && $count == 1) { if ($action == 'restore' && $count == 1) {
return Redirect::to("{$entityType}s/".$ids[0]); return Redirect::to("{$entityType}s/".Utils::getFirst($ids));
} else { } else {
return Redirect::to("{$entityType}s"); return Redirect::to("{$entityType}s");
} }
@ -646,7 +665,7 @@ class InvoiceController extends BaseController
'invoice' => $invoice, 'invoice' => $invoice,
'versionsJson' => json_encode($versionsJson), 'versionsJson' => json_encode($versionsJson),
'versionsSelect' => $versionsSelect, 'versionsSelect' => $versionsSelect,
'invoiceDesigns' => InvoiceDesign::availableDesigns(), 'invoiceDesigns' => InvoiceDesign::getDesigns(),
]; ];
return View::make('invoices.history', $data); return View::make('invoices.history', $data);

View File

@ -61,11 +61,12 @@ class PaymentController extends BaseController
} }
$invitation = Invitation::with('account')->where('invitation_key', '=', $invitationKey)->first(); $invitation = Invitation::with('account')->where('invitation_key', '=', $invitationKey)->first();
$color = $invitation->account->primary_color ? $invitation->account->primary_color : '#0b4d78'; $account = $invitation->account;
$color = $account->primary_color ? $account->primary_color : '#0b4d78';
$data = [ $data = [
'color' => $color, 'color' => $color,
'hideLogo' => Session::get('white_label'), 'hideLogo' => $account->isWhiteLabel(),
'entityType' => ENTITY_PAYMENT, 'entityType' => ENTITY_PAYMENT,
'title' => trans('texts.payments'), 'title' => trans('texts.payments'),
'columns' => Utils::trans(['invoice', 'transaction_reference', 'method', 'payment_amount', 'payment_date']) 'columns' => Utils::trans(['invoice', 'transaction_reference', 'method', 'payment_amount', 'payment_date'])
@ -233,15 +234,19 @@ class PaymentController extends BaseController
private function convertInputForOmnipay($input) private function convertInputForOmnipay($input)
{ {
$country = Country::find($input['country_id']); $data = [
return [
'firstName' => $input['first_name'], 'firstName' => $input['first_name'],
'lastName' => $input['last_name'], 'lastName' => $input['last_name'],
'number' => $input['card_number'], 'number' => $input['card_number'],
'expiryMonth' => $input['expiration_month'], 'expiryMonth' => $input['expiration_month'],
'expiryYear' => $input['expiration_year'], 'expiryYear' => $input['expiration_year'],
'cvv' => $input['cvv'], 'cvv' => $input['cvv'],
];
if (isset($input['country_id'])) {
$country = Country::find($input['country_id']);
$data = array_merge($data, [
'billingAddress1' => $input['address1'], 'billingAddress1' => $input['address1'],
'billingAddress2' => $input['address2'], 'billingAddress2' => $input['address2'],
'billingCity' => $input['city'], 'billingCity' => $input['city'],
@ -254,12 +259,16 @@ class PaymentController extends BaseController
'shippingState' => $input['state'], 'shippingState' => $input['state'],
'shippingPostcode' => $input['postal_code'], 'shippingPostcode' => $input['postal_code'],
'shippingCountry' => $country->iso_3166_2 'shippingCountry' => $country->iso_3166_2
]; ]);
}
return $data;
} }
private function getPaymentDetails($invitation, $input = null) private function getPaymentDetails($invitation, $input = null)
{ {
$invoice = $invitation->invoice; $invoice = $invitation->invoice;
$account = $invoice->account;
$key = $invoice->account_id.'-'.$invoice->invoice_number; $key = $invoice->account_id.'-'.$invoice->invoice_number;
$currencyCode = $invoice->client->currency ? $invoice->client->currency->code : ($invoice->account->currency ? $invoice->account->currency->code : 'USD'); $currencyCode = $invoice->client->currency ? $invoice->client->currency->code : ($invoice->account->currency ? $invoice->account->currency->code : 'USD');
@ -328,8 +337,10 @@ class PaymentController extends BaseController
'acceptedCreditCardTypes' => $acceptedCreditCardTypes, 'acceptedCreditCardTypes' => $acceptedCreditCardTypes,
'countries' => Cache::get('countries'), 'countries' => Cache::get('countries'),
'currencyId' => $client->getCurrencyId(), 'currencyId' => $client->getCurrencyId(),
'currencyCode' => $client->currency ? $client->currency->code : ($account->currency ? $account->currency->code : 'USD'),
'account' => $client->account, 'account' => $client->account,
'hideLogo' => $account->isWhiteLabel(), 'hideLogo' => $account->isWhiteLabel(),
'showAddress' => $accountGateway->show_address,
]; ];
return View::make('payments.payment', $data); return View::make('payments.payment', $data);
@ -378,6 +389,7 @@ class PaymentController extends BaseController
'currencyId' => 1, 'currencyId' => 1,
'paymentTitle' => $affiliate->payment_title, 'paymentTitle' => $affiliate->payment_title,
'paymentSubtitle' => $affiliate->payment_subtitle, 'paymentSubtitle' => $affiliate->payment_subtitle,
'showAddress' => true,
]; ];
return View::make('payments.payment', $data); return View::make('payments.payment', $data);
@ -498,19 +510,30 @@ class PaymentController extends BaseController
public function do_payment($invitationKey, $onSite = true, $useToken = false) public function do_payment($invitationKey, $onSite = true, $useToken = false)
{ {
$rules = array( $invitation = Invitation::with('invoice.invoice_items', 'invoice.client.currency', 'invoice.client.account.currency', 'invoice.client.account.account_gateways.gateway')->where('invitation_key', '=', $invitationKey)->firstOrFail();
$invoice = $invitation->invoice;
$client = $invoice->client;
$account = $client->account;
$accountGateway = $account->getGatewayByType(Session::get('payment_type'));
$rules = [
'first_name' => 'required', 'first_name' => 'required',
'last_name' => 'required', 'last_name' => 'required',
'card_number' => 'required', 'card_number' => 'required',
'expiration_month' => 'required', 'expiration_month' => 'required',
'expiration_year' => 'required', 'expiration_year' => 'required',
'cvv' => 'required', 'cvv' => 'required',
];
if ($accountGateway->show_address) {
$rules = array_merge($rules, [
'address1' => 'required', 'address1' => 'required',
'city' => 'required', 'city' => 'required',
'state' => 'required', 'state' => 'required',
'postal_code' => 'required', 'postal_code' => 'required',
'country_id' => 'required', 'country_id' => 'required',
); ]);
}
if ($onSite) { if ($onSite) {
$validator = Validator::make(Input::all(), $rules); $validator = Validator::make(Input::all(), $rules);
@ -521,24 +544,18 @@ class PaymentController extends BaseController
->withErrors($validator) ->withErrors($validator)
->withInput(); ->withInput();
} }
}
$invitation = Invitation::with('invoice.invoice_items', 'invoice.client.currency', 'invoice.client.account.currency', 'invoice.client.account.account_gateways.gateway')->where('invitation_key', '=', $invitationKey)->firstOrFail();
$invoice = $invitation->invoice;
$client = $invoice->client;
$account = $client->account;
$accountGateway = $account->getGatewayByType(Session::get('payment_type'));
/* if ($accountGateway->update_address) {
if ($onSite) {
$client->address1 = trim(Input::get('address1')); $client->address1 = trim(Input::get('address1'));
$client->address2 = trim(Input::get('address2')); $client->address2 = trim(Input::get('address2'));
$client->city = trim(Input::get('city')); $client->city = trim(Input::get('city'));
$client->state = trim(Input::get('state')); $client->state = trim(Input::get('state'));
$client->postal_code = trim(Input::get('postal_code')); $client->postal_code = trim(Input::get('postal_code'));
$client->country_id = Input::get('country_id');
$client->save(); $client->save();
} }
*/ }
try { try {
$gateway = self::createGateway($accountGateway); $gateway = self::createGateway($accountGateway);

View File

@ -16,7 +16,11 @@ class QuoteApiController extends Controller
public function index() public function index()
{ {
$invoices = Invoice::scope()->with('client', 'user')->where('invoices.is_quote', '=', true)->orderBy('created_at', 'desc')->get(); $invoices = Invoice::scope()
->with('client', 'user')
->where('invoices.is_quote', '=', true)
->orderBy('created_at', 'desc')
->get();
$invoices = Utils::remapPublicIds($invoices); $invoices = Utils::remapPublicIds($invoices);
$response = json_encode($invoices, JSON_PRETTY_PRINT); $response = json_encode($invoices, JSON_PRETTY_PRINT);

View File

@ -75,11 +75,12 @@ class QuoteController extends BaseController
} }
$invitation = Invitation::with('account')->where('invitation_key', '=', $invitationKey)->first(); $invitation = Invitation::with('account')->where('invitation_key', '=', $invitationKey)->first();
$color = $invitation->account->primary_color ? $invitation->account->primary_color : '#0b4d78'; $account = $invitation->account;
$color = $account->primary_color ? $account->primary_color : '#0b4d78';
$data = [ $data = [
'color' => $color, 'color' => $color,
'hideLogo' => Session::get('white_label'), 'hideLogo' => $account->isWhiteLabel(),
'title' => trans('texts.quotes'), 'title' => trans('texts.quotes'),
'entityType' => ENTITY_QUOTE, 'entityType' => ENTITY_QUOTE,
'columns' => Utils::trans(['quote_number', 'quote_date', 'quote_total', 'due_date']), 'columns' => Utils::trans(['quote_number', 'quote_date', 'quote_total', 'due_date']),
@ -156,8 +157,9 @@ class QuoteController extends BaseController
'sizes' => Cache::get('sizes'), 'sizes' => Cache::get('sizes'),
'paymentTerms' => Cache::get('paymentTerms'), 'paymentTerms' => Cache::get('paymentTerms'),
'industries' => Cache::get('industries'), 'industries' => Cache::get('industries'),
'invoiceDesigns' => InvoiceDesign::availableDesigns(), 'invoiceDesigns' => InvoiceDesign::getDesigns(),
'invoiceLabels' => Auth::user()->account->getInvoiceLabels() 'invoiceLabels' => Auth::user()->account->getInvoiceLabels(),
'isRecurring' => false,
]; ];
} }
@ -183,7 +185,11 @@ class QuoteController extends BaseController
Session::flash('message', $message); Session::flash('message', $message);
} }
return Redirect::to('quotes'); if ($action == 'restore' && $count == 1) {
return Redirect::to("quotes/".Utils::getFirst($ids));
} else {
return Redirect::to("quotes");
}
} }
public function approve($invitationKey) public function approve($invitationKey)

View File

@ -1,6 +1,7 @@
<?php namespace App\Http\Controllers; <?php namespace App\Http\Controllers;
use Auth; use Auth;
use Config;
use Input; use Input;
use Utils; use Utils;
use DB; use DB;
@ -19,7 +20,9 @@ class ReportController extends BaseController
$fileName = storage_path() . '/dataviz_sample.txt'; $fileName = storage_path() . '/dataviz_sample.txt';
if (Auth::user()->account->isPro()) { if (Auth::user()->account->isPro()) {
$account = Account::where('id', '=', Auth::user()->account->id)->with(['clients.invoices.invoice_items', 'clients.contacts'])->first(); $account = Account::where('id', '=', Auth::user()->account->id)
->with(['clients.invoices.invoice_items', 'clients.contacts'])
->first();
$account = $account->hideFieldsForViz(); $account = $account->hideFieldsForViz();
$clients = $account->clients->toJson(); $clients = $account->clients->toJson();
} elseif (file_exists($fileName)) { } elseif (file_exists($fileName)) {
@ -149,22 +152,56 @@ class ReportController extends BaseController
$reportTotals['balance'][$currencyId] += $record->balance; $reportTotals['balance'][$currencyId] += $record->balance;
} }
if ($action == 'export') { if ($action == 'export')
{
self::export($exportData, $reportTotals); self::export($exportData, $reportTotals);
} }
} }
if ($enableChart) { if ($enableChart)
foreach ([ENTITY_INVOICE, ENTITY_PAYMENT, ENTITY_CREDIT] as $entityType) { {
foreach ([ENTITY_INVOICE, ENTITY_PAYMENT, ENTITY_CREDIT] as $entityType)
{
// SQLite does not support the YEAR(), MONTH(), WEEK() and similar functions.
// Let's see if SQLite is being used.
if (Config::get('database.connections.'.Config::get('database.default').'.driver') == 'sqlite')
{
// Replace the unsupported function with it's date format counterpart
switch ($groupBy)
{
case 'MONTH':
$dateFormat = '%m'; // returns 01-12
break;
case 'WEEK':
$dateFormat = '%W'; // returns 00-53
break;
case 'DAYOFYEAR':
$dateFormat = '%j'; // returns 001-366
break;
default:
$dateFormat = '%m'; // MONTH by default
break;
}
// Concatenate the year and the chosen timeframe (Month, Week or Day)
$timeframe = 'strftime("%Y", '.$entityType.'_date) || strftime("'.$dateFormat.'", '.$entityType.'_date)';
}
else
{
// Supported by Laravel's other DBMS drivers (MySQL, MSSQL and PostgreSQL)
$timeframe = 'concat(YEAR('.$entityType.'_date), '.$groupBy.'('.$entityType.'_date))';
}
$records = DB::table($entityType.'s') $records = DB::table($entityType.'s')
->select(DB::raw('sum(amount) as total, concat(YEAR('.$entityType.'_date), '.$groupBy.'('.$entityType.'_date)) as '.$groupBy)) ->select(DB::raw('sum(amount) as total, '.$timeframe.' as '.$groupBy))
->where('account_id', '=', Auth::user()->account_id) ->where('account_id', '=', Auth::user()->account_id)
->where($entityType.'s.is_deleted', '=', false) ->where($entityType.'s.is_deleted', '=', false)
->where($entityType.'s.'.$entityType.'_date', '>=', $startDate->format('Y-m-d')) ->where($entityType.'s.'.$entityType.'_date', '>=', $startDate->format('Y-m-d'))
->where($entityType.'s.'.$entityType.'_date', '<=', $endDate->format('Y-m-d')) ->where($entityType.'s.'.$entityType.'_date', '<=', $endDate->format('Y-m-d'))
->groupBy($groupBy); ->groupBy($groupBy);
if ($entityType == ENTITY_INVOICE) { if ($entityType == ENTITY_INVOICE)
{
$records->where('is_quote', '=', false) $records->where('is_quote', '=', false)
->where('is_recurring', '=', false); ->where('is_recurring', '=', false);
} }
@ -181,12 +218,15 @@ class ReportController extends BaseController
$totals = []; $totals = [];
foreach ($period as $d) { foreach ($period as $d)
{
$dateFormat = $groupBy == 'DAYOFYEAR' ? 'z' : ($groupBy == 'WEEK' ? 'W' : 'n'); $dateFormat = $groupBy == 'DAYOFYEAR' ? 'z' : ($groupBy == 'WEEK' ? 'W' : 'n');
$date = $d->format('Y'.$dateFormat); // MySQL returns 1-366 for DAYOFYEAR, whereas PHP returns 0-365
$date = $groupBy == 'DAYOFYEAR' ? $d->format('Y') . ($d->format($dateFormat) + 1) : $d->format('Y'.$dateFormat);
$totals[] = isset($data[$date]) ? $data[$date] : 0; $totals[] = isset($data[$date]) ? $data[$date] : 0;
if ($entityType == ENTITY_INVOICE) { if ($entityType == ENTITY_INVOICE)
{
$labelFormat = $groupBy == 'DAYOFYEAR' ? 'j' : ($groupBy == 'WEEK' ? 'W' : 'F'); $labelFormat = $groupBy == 'DAYOFYEAR' ? 'j' : ($groupBy == 'WEEK' ? 'W' : 'F');
$label = $d->format($labelFormat); $label = $d->format($labelFormat);
$labels[] = $label; $labels[] = $label;
@ -195,7 +235,8 @@ class ReportController extends BaseController
$max = max($totals); $max = max($totals);
if ($max > 0) { if ($max > 0)
{
$datasets[] = [ $datasets[] = [
'totals' => $totals, 'totals' => $totals,
'colors' => $entityType == ENTITY_INVOICE ? '78,205,196' : ($entityType == ENTITY_CREDIT ? '199,244,100' : '255,107,107'), 'colors' => $entityType == ENTITY_INVOICE ? '78,205,196' : ($entityType == ENTITY_CREDIT ? '199,244,100' : '255,107,107'),

View File

@ -10,34 +10,23 @@ use Validator;
use Redirect; use Redirect;
use Session; use Session;
use DropdownButton; use DropdownButton;
use DateTime;
use DateTimeZone;
use App\Models\Client; use App\Models\Client;
use App\Models\Task; use App\Models\Task;
/*
use Auth;
use Cache;
use App\Models\Activity;
use App\Models\Contact;
use App\Models\Invoice;
use App\Models\Size;
use App\Models\PaymentTerm;
use App\Models\Industry;
use App\Models\Currency;
use App\Models\Country;
*/
use App\Ninja\Repositories\TaskRepository; use App\Ninja\Repositories\TaskRepository;
use App\Ninja\Repositories\InvoiceRepository;
class TaskController extends BaseController class TaskController extends BaseController
{ {
protected $taskRepo; protected $taskRepo;
public function __construct(TaskRepository $taskRepo) public function __construct(TaskRepository $taskRepo, InvoiceRepository $invoiceRepo)
{ {
parent::__construct(); parent::__construct();
$this->taskRepo = $taskRepo; $this->taskRepo = $taskRepo;
$this->invoiceRepo = $invoiceRepo;
} }
/** /**
@ -47,10 +36,7 @@ class TaskController extends BaseController
*/ */
public function index() public function index()
{ {
if (!Auth::user()->account->timezone) { self::checkTimezone();
$link = link_to('/company/details', trans('texts.click_here'), ['target' => '_blank']);
Session::flash('warning', trans('texts.timezone_unset', ['link' => $link]));
}
return View::make('list', array( return View::make('list', array(
'entityType' => ENTITY_TASK, 'entityType' => ENTITY_TASK,
@ -71,8 +57,8 @@ class TaskController extends BaseController
->addColumn('client_name', function ($model) { return $model->client_public_id ? link_to('clients/'.$model->client_public_id, Utils::getClientDisplayName($model)) : ''; }); ->addColumn('client_name', function ($model) { return $model->client_public_id ? link_to('clients/'.$model->client_public_id, Utils::getClientDisplayName($model)) : ''; });
} }
return $table->addColumn('start_time', function($model) { return Utils::fromSqlDateTime($model->start_time); }) return $table->addColumn('created_at', function($model) { return Task::calcStartTime($model); })
->addColumn('duration', function($model) { return gmdate('H:i:s', $model->is_running ? time() - strtotime($model->start_time) : $model->duration); }) ->addColumn('time_log', function($model) { return gmdate('H:i:s', Task::calcDuration($model)); })
->addColumn('description', function($model) { return $model->description; }) ->addColumn('description', function($model) { return $model->description; })
->addColumn('invoice_number', function($model) { return self::getStatusLabel($model); }) ->addColumn('invoice_number', function($model) { return self::getStatusLabel($model); })
->addColumn('dropdown', function ($model) { ->addColumn('dropdown', function ($model) {
@ -142,12 +128,15 @@ class TaskController extends BaseController
*/ */
public function create($clientPublicId = 0) public function create($clientPublicId = 0)
{ {
self::checkTimezone();
$data = [ $data = [
'task' => null, 'task' => null,
'clientPublicId' => Input::old('client') ? Input::old('client') : $clientPublicId, 'clientPublicId' => Input::old('client') ? Input::old('client') : $clientPublicId,
'method' => 'POST', 'method' => 'POST',
'url' => 'tasks', 'url' => 'tasks',
'title' => trans('texts.new_task'), 'title' => trans('texts.new_task'),
'minuteOffset' => Utils::getTiemstampOffset(),
]; ];
$data = array_merge($data, self::getViewModel()); $data = array_merge($data, self::getViewModel());
@ -163,14 +152,24 @@ class TaskController extends BaseController
*/ */
public function edit($publicId) public function edit($publicId)
{ {
self::checkTimezone();
$task = Task::scope($publicId)->with('client', 'invoice')->firstOrFail(); $task = Task::scope($publicId)->with('client', 'invoice')->firstOrFail();
$actions = []; $actions = [];
if ($task->invoice) { if ($task->invoice) {
$actions[] = ['url' => URL::to("inovices/{$task->invoice->public_id}/edit"), 'label' => trans("texts.view_invoice")]; $actions[] = ['url' => URL::to("inovices/{$task->invoice->public_id}/edit"), 'label' => trans("texts.view_invoice")];
} else { } else {
$actions[] = ['url' => 'javascript:submitAction("invoice")', 'label' => trans("texts.invoice_task")]; $actions[] = ['url' => 'javascript:submitAction("invoice")', 'label' => trans("texts.create_invoice")];
// check for any open invoices
$invoices = $task->client_id ? $this->invoiceRepo->findOpenInvoices($task->client_id) : [];
foreach ($invoices as $invoice) {
$actions[] = ['url' => 'javascript:submitAction("add_to_invoice", '.$invoice->public_id.')', 'label' => trans("texts.add_to_invoice", ["invoice" => $invoice->invoice_number])];
} }
}
$actions[] = DropdownButton::DIVIDER; $actions[] = DropdownButton::DIVIDER;
if (!$task->trashed()) { if (!$task->trashed()) {
$actions[] = ['url' => 'javascript:submitAction("archive")', 'label' => trans('texts.archive_task')]; $actions[] = ['url' => 'javascript:submitAction("archive")', 'label' => trans('texts.archive_task')];
@ -185,8 +184,9 @@ class TaskController extends BaseController
'method' => 'PUT', 'method' => 'PUT',
'url' => 'tasks/'.$publicId, 'url' => 'tasks/'.$publicId,
'title' => trans('texts.edit_task'), 'title' => trans('texts.edit_task'),
'duration' => $task->resume_time ? ($task->duration + strtotime('now') - strtotime($task->resume_time)) : (strtotime('now') - strtotime($task->start_time)), 'duration' => $task->is_running ? $task->getCurrentDuration() : $task->getDuration(),
'actions' => $actions 'actions' => $actions,
'minuteOffset' => Utils::getTiemstampOffset(),
]; ];
$data = array_merge($data, self::getViewModel()); $data = array_merge($data, self::getViewModel());
@ -216,7 +216,7 @@ class TaskController extends BaseController
{ {
$action = Input::get('action'); $action = Input::get('action');
if (in_array($action, ['archive', 'delete', 'invoice', 'restore'])) { if (in_array($action, ['archive', 'delete', 'invoice', 'restore', 'add_to_invoice'])) {
return self::bulk(); return self::bulk();
} }
@ -235,8 +235,7 @@ class TaskController extends BaseController
$this->taskRepo->save($ids, ['action' => $action]); $this->taskRepo->save($ids, ['action' => $action]);
Session::flash('message', trans('texts.stopped_task')); Session::flash('message', trans('texts.stopped_task'));
return Redirect::to('tasks'); return Redirect::to('tasks');
} else if ($action == 'invoice') { } else if ($action == 'invoice' || $action == 'add_to_invoice') {
$tasks = Task::scope($ids)->with('client')->get(); $tasks = Task::scope($ids)->with('client')->get();
$clientPublicId = false; $clientPublicId = false;
$data = []; $data = [];
@ -262,12 +261,17 @@ class TaskController extends BaseController
$data[] = [ $data[] = [
'publicId' => $task->public_id, 'publicId' => $task->public_id,
'description' => $task->description, 'description' => $task->description,
'startTime' => Utils::fromSqlDateTime($task->start_time), 'startTime' => $task->getStartTime(),
'duration' => round($task->duration / (60 * 60), 2) 'duration' => $task->getHours(),
]; ];
} }
if ($action == 'invoice') {
return Redirect::to("invoices/create/{$clientPublicId}")->with('tasks', $data); return Redirect::to("invoices/create/{$clientPublicId}")->with('tasks', $data);
} else {
$invoiceId = Input::get('invoice_id');
return Redirect::to("invoices/{$invoiceId}/edit")->with('tasks', $data);
}
} else { } else {
$count = $this->taskRepo->bulk($ids, $action); $count = $this->taskRepo->bulk($ids, $action);
@ -281,4 +285,12 @@ class TaskController extends BaseController
} }
} }
} }
private function checkTimezone()
{
if (!Auth::user()->account->timezone) {
$link = link_to('/company/details?focus=timezone_id', trans('texts.click_here'), ['target' => '_blank']);
Session::flash('warning', trans('texts.timezone_unset', ['link' => $link]));
}
}
} }

View File

@ -95,7 +95,7 @@ class UserController extends BaseController
$user->force_pdfjs = true; $user->force_pdfjs = true;
$user->save(); $user->save();
Session::flash('message', trans('texts.security.updated_settings')); Session::flash('message', trans('texts.updated_settings'));
return Redirect::to('/dashboard'); return Redirect::to('/dashboard');
} }
@ -132,9 +132,12 @@ class UserController extends BaseController
*/ */
public function create() public function create()
{ {
if (!Auth::user()->confirmed) { if (!Auth::user()->registered) {
Session::flash('error', trans('texts.register_to_add_user')); Session::flash('error', trans('texts.register_to_add_user'));
return Redirect::to('company/advanced_settings/user_management');
}
if (!Auth::user()->confirmed) {
Session::flash('error', trans('texts.confirmation_required'));
return Redirect::to('company/advanced_settings/user_management'); return Redirect::to('company/advanced_settings/user_management');
} }
@ -374,6 +377,11 @@ class UserController extends BaseController
Session::put(SESSION_USER_ACCOUNTS, $users); Session::put(SESSION_USER_ACCOUNTS, $users);
Session::flash('message', trans('texts.unlinked_account')); Session::flash('message', trans('texts.unlinked_account'));
return Redirect::to($referer); return Redirect::to('/dashboard');
}
public function manageCompanies()
{
return View::make('users.account_management');
} }
} }

View File

@ -49,6 +49,7 @@ class StartupCheck
'paymentTerms' => 'App\Models\PaymentTerm', 'paymentTerms' => 'App\Models\PaymentTerm',
'paymentTypes' => 'App\Models\PaymentType', 'paymentTypes' => 'App\Models\PaymentType',
'countries' => 'App\Models\Country', 'countries' => 'App\Models\Country',
'invoiceDesigns' => 'App\Models\InvoiceDesign',
]; ];
foreach ($cachedTables as $name => $class) { foreach ($cachedTables as $name => $class) {
if (Input::has('clear_cache')) { if (Input::has('clear_cache')) {
@ -74,7 +75,7 @@ class StartupCheck
$count = Session::get(SESSION_COUNTER, 0); $count = Session::get(SESSION_COUNTER, 0);
Session::put(SESSION_COUNTER, ++$count); Session::put(SESSION_COUNTER, ++$count);
if (!Utils::startsWith($_SERVER['REQUEST_URI'], '/news_feed') && !Session::has('news_feed_id')) { if (isset($_SERVER['REQUEST_URI']) && !Utils::startsWith($_SERVER['REQUEST_URI'], '/news_feed') && !Session::has('news_feed_id')) {
$data = false; $data = false;
if (Utils::isNinja()) { if (Utils::isNinja()) {
$data = Utils::getNewsFeedResponse(); $data = Utils::getNewsFeedResponse();
@ -127,12 +128,13 @@ class StartupCheck
} }
// Check if the user is claiming a license (ie, additional invoices, white label, etc.) // Check if the user is claiming a license (ie, additional invoices, white label, etc.)
if (isset($_SERVER['REQUEST_URI'])) {
$claimingLicense = Utils::startsWith($_SERVER['REQUEST_URI'], '/claim_license'); $claimingLicense = Utils::startsWith($_SERVER['REQUEST_URI'], '/claim_license');
if (!$claimingLicense && Input::has('license_key') && Input::has('product_id')) { if (!$claimingLicense && Input::has('license_key') && Input::has('product_id')) {
$licenseKey = Input::get('license_key'); $licenseKey = Input::get('license_key');
$productId = Input::get('product_id'); $productId = Input::get('product_id');
$data = trim(file_get_contents((Utils::isNinjaDev() ? 'http://www.ninja.dev' : NINJA_APP_URL)."/claim_license?license_key={$licenseKey}&product_id={$productId}")); $data = trim(file_get_contents((Utils::isNinjaDev() ? SITE_URL : NINJA_APP_URL)."/claim_license?license_key={$licenseKey}&product_id={$productId}"));
if ($productId == PRODUCT_INVOICE_DESIGNS) { if ($productId == PRODUCT_INVOICE_DESIGNS) {
if ($data = json_decode($data)) { if ($data = json_decode($data)) {
@ -156,8 +158,9 @@ class StartupCheck
} }
} }
} }
}
if (preg_match('/(?i)msie [2-8]/', $_SERVER['HTTP_USER_AGENT'])) { if (isset($_SERVER['HTTP_USER_AGENT']) && preg_match('/(?i)msie [2-8]/', $_SERVER['HTTP_USER_AGENT'])) {
Session::flash('error', trans('texts.old_browser')); Session::flash('error', trans('texts.old_browser'));
} }

View File

@ -26,6 +26,13 @@ Route::post('setup', 'AppController@doSetup');
Route::get('install', 'AppController@install'); Route::get('install', 'AppController@install');
Route::get('update', 'AppController@update'); Route::get('update', 'AppController@update');
/*
// Codeception code coverage
Route::get('/c3.php', function () {
include '../c3.php';
});
*/
// Public pages // Public pages
Route::get('/', 'HomeController@showIndex'); Route::get('/', 'HomeController@showIndex');
Route::get('terms', 'HomeController@showTerms'); Route::get('terms', 'HomeController@showTerms');
@ -95,6 +102,7 @@ Route::group(['middleware' => 'auth'], function() {
Route::post('users/change_password', 'UserController@changePassword'); Route::post('users/change_password', 'UserController@changePassword');
Route::get('/switch_account/{user_id}', 'UserController@switchAccount'); Route::get('/switch_account/{user_id}', 'UserController@switchAccount');
Route::get('/unlink_account/{user_account_id}/{user_id}', 'UserController@unlinkAccount'); Route::get('/unlink_account/{user_account_id}/{user_id}', 'UserController@unlinkAccount');
Route::get('/manage_companies', 'UserController@manageCompanies');
Route::get('api/tokens', array('as'=>'api.tokens', 'uses'=>'TokenController@getDatatable')); Route::get('api/tokens', array('as'=>'api.tokens', 'uses'=>'TokenController@getDatatable'));
Route::resource('tokens', 'TokenController'); Route::resource('tokens', 'TokenController');
@ -130,7 +138,6 @@ Route::group(['middleware' => 'auth'], function() {
Route::get('tasks/create/{client_id?}', 'TaskController@create'); Route::get('tasks/create/{client_id?}', 'TaskController@create');
Route::post('tasks/bulk', 'TaskController@bulk'); Route::post('tasks/bulk', 'TaskController@bulk');
Route::get('recurring_invoices', 'InvoiceController@recurringIndex');
Route::get('api/recurring_invoices/{client_id?}', array('as'=>'api.recurring_invoices', 'uses'=>'InvoiceController@getRecurringDatatable')); Route::get('api/recurring_invoices/{client_id?}', array('as'=>'api.recurring_invoices', 'uses'=>'InvoiceController@getRecurringDatatable'));
Route::get('invoices/invoice_history/{invoice_id}', 'InvoiceController@invoiceHistory'); Route::get('invoices/invoice_history/{invoice_id}', 'InvoiceController@invoiceHistory');
@ -139,6 +146,7 @@ Route::group(['middleware' => 'auth'], function() {
Route::resource('invoices', 'InvoiceController'); Route::resource('invoices', 'InvoiceController');
Route::get('api/invoices/{client_id?}', array('as'=>'api.invoices', 'uses'=>'InvoiceController@getDatatable')); Route::get('api/invoices/{client_id?}', array('as'=>'api.invoices', 'uses'=>'InvoiceController@getDatatable'));
Route::get('invoices/create/{client_id?}', 'InvoiceController@create'); Route::get('invoices/create/{client_id?}', 'InvoiceController@create');
Route::get('recurring_invoices/create/{client_id?}', 'InvoiceController@createRecurring');
Route::get('invoices/{public_id}/clone', 'InvoiceController@cloneInvoice'); Route::get('invoices/{public_id}/clone', 'InvoiceController@cloneInvoice');
Route::post('invoices/bulk', 'InvoiceController@bulk'); Route::post('invoices/bulk', 'InvoiceController@bulk');
@ -211,6 +219,7 @@ Route::get('/forgot_password', function() {
}); });
if (!defined('CONTACT_EMAIL')) {
define('CONTACT_EMAIL', Config::get('mail.from.address')); define('CONTACT_EMAIL', Config::get('mail.from.address'));
define('CONTACT_NAME', Config::get('mail.from.name')); define('CONTACT_NAME', Config::get('mail.from.name'));
define('SITE_URL', Config::get('app.url')); define('SITE_URL', Config::get('app.url'));
@ -246,6 +255,8 @@ define('ACCOUNT_USER_MANAGEMENT', 'user_management');
define('ACCOUNT_DATA_VISUALIZATIONS', 'data_visualizations'); define('ACCOUNT_DATA_VISUALIZATIONS', 'data_visualizations');
define('ACCOUNT_EMAIL_TEMPLATES', 'email_templates'); define('ACCOUNT_EMAIL_TEMPLATES', 'email_templates');
define('ACCOUNT_TOKEN_MANAGEMENT', 'token_management'); define('ACCOUNT_TOKEN_MANAGEMENT', 'token_management');
define('ACCOUNT_CUSTOMIZE_DESIGN', 'customize_design');
define('ACTIVITY_TYPE_CREATE_CLIENT', 1); define('ACTIVITY_TYPE_CREATE_CLIENT', 1);
define('ACTIVITY_TYPE_ARCHIVE_CLIENT', 2); define('ACTIVITY_TYPE_ARCHIVE_CLIENT', 2);
@ -299,6 +310,7 @@ define('INVOICE_STATUS_PARTIAL', 4);
define('INVOICE_STATUS_PAID', 5); define('INVOICE_STATUS_PAID', 5);
define('PAYMENT_TYPE_CREDIT', 1); define('PAYMENT_TYPE_CREDIT', 1);
define('CUSTOM_DESIGN', 11);
define('FREQUENCY_WEEKLY', 1); define('FREQUENCY_WEEKLY', 1);
define('FREQUENCY_TWO_WEEKS', 2); define('FREQUENCY_TWO_WEEKS', 2);
@ -322,6 +334,7 @@ define('SESSION_LAST_REQUEST_TIME', 'SESSION_LAST_REQUEST_TIME');
define('DEFAULT_TIMEZONE', 'US/Eastern'); define('DEFAULT_TIMEZONE', 'US/Eastern');
define('DEFAULT_CURRENCY', 1); // US Dollar define('DEFAULT_CURRENCY', 1); // US Dollar
define('DEFAULT_LANGUAGE', 1); // English
define('DEFAULT_DATE_FORMAT', 'M j, Y'); define('DEFAULT_DATE_FORMAT', 'M j, Y');
define('DEFAULT_DATE_PICKER_FORMAT', 'M d, yyyy'); define('DEFAULT_DATE_PICKER_FORMAT', 'M d, yyyy');
define('DEFAULT_DATETIME_FORMAT', 'F j, Y, g:i a'); define('DEFAULT_DATETIME_FORMAT', 'F j, Y, g:i a');
@ -360,14 +373,17 @@ define('NINJA_GATEWAY_ID', GATEWAY_STRIPE);
define('NINJA_GATEWAY_CONFIG', ''); define('NINJA_GATEWAY_CONFIG', '');
define('NINJA_WEB_URL', 'https://www.invoiceninja.com'); define('NINJA_WEB_URL', 'https://www.invoiceninja.com');
define('NINJA_APP_URL', 'https://app.invoiceninja.com'); define('NINJA_APP_URL', 'https://app.invoiceninja.com');
define('NINJA_VERSION', '2.2.2'); define('NINJA_VERSION', '2.3.4');
define('NINJA_DATE', '2000-01-01'); define('NINJA_DATE', '2000-01-01');
define('NINJA_FROM_EMAIL', 'maildelivery@invoiceninja.com'); define('NINJA_FROM_EMAIL', 'maildelivery@invoiceninja.com');
define('RELEASES_URL', 'https://github.com/hillelcoren/invoice-ninja/releases/'); define('RELEASES_URL', 'https://github.com/hillelcoren/invoice-ninja/releases/');
define('ZAPIER_URL', 'https://zapier.com/developer/invite/11276/85cf0ee4beae8e802c6c579eb4e351f1/'); define('ZAPIER_URL', 'https://zapier.com/developer/invite/11276/85cf0ee4beae8e802c6c579eb4e351f1/');
define('OUTDATE_BROWSER_URL', 'http://browsehappy.com/'); define('OUTDATE_BROWSER_URL', 'http://browsehappy.com/');
define('PDFMAKE_DOCS', 'http://pdfmake.org/playground.html');
define('COUNT_FREE_DESIGNS', 4); define('COUNT_FREE_DESIGNS', 4);
define('COUNT_FREE_DESIGNS_SELF_HOST', 5); // include the custom design
define('PRODUCT_ONE_CLICK_INSTALL', 1); define('PRODUCT_ONE_CLICK_INSTALL', 1);
define('PRODUCT_INVOICE_DESIGNS', 2); define('PRODUCT_INVOICE_DESIGNS', 2);
define('PRODUCT_WHITE_LABEL', 3); define('PRODUCT_WHITE_LABEL', 3);
@ -384,6 +400,9 @@ define('USER_TYPE_SELF_HOST', 'SELF_HOST');
define('USER_TYPE_CLOUD_HOST', 'CLOUD_HOST'); define('USER_TYPE_CLOUD_HOST', 'CLOUD_HOST');
define('NEW_VERSION_AVAILABLE', 'NEW_VERSION_AVAILABLE'); define('NEW_VERSION_AVAILABLE', 'NEW_VERSION_AVAILABLE');
define('TEST_USERNAME', 'user@example.com');
define('TEST_PASSWORD', 'password');
define('TOKEN_BILLING_DISABLED', 1); define('TOKEN_BILLING_DISABLED', 1);
define('TOKEN_BILLING_OPT_IN', 2); define('TOKEN_BILLING_OPT_IN', 2);
define('TOKEN_BILLING_OPT_OUT', 3); define('TOKEN_BILLING_OPT_OUT', 3);
@ -396,14 +415,6 @@ define('PAYMENT_TYPE_DWOLLA', 'PAYMENT_TYPE_DWOLLA');
define('PAYMENT_TYPE_TOKEN', 'PAYMENT_TYPE_TOKEN'); define('PAYMENT_TYPE_TOKEN', 'PAYMENT_TYPE_TOKEN');
define('PAYMENT_TYPE_ANY', 'PAYMENT_TYPE_ANY'); define('PAYMENT_TYPE_ANY', 'PAYMENT_TYPE_ANY');
/*
define('GATEWAY_AMAZON', 30);
define('GATEWAY_BLUEPAY', 31);
define('GATEWAY_BRAINTREE', 32);
define('GATEWAY_GOOGLE', 33);
define('GATEWAY_QUICKBOOKS', 35);
*/
$creditCards = [ $creditCards = [
1 => ['card' => 'images/credit_cards/Test-Visa-Icon.png', 'text' => 'Visa'], 1 => ['card' => 'images/credit_cards/Test-Visa-Icon.png', 'text' => 'Visa'],
2 => ['card' => 'images/credit_cards/Test-MasterCard-Icon.png', 'text' => 'Master Card'], 2 => ['card' => 'images/credit_cards/Test-MasterCard-Icon.png', 'text' => 'Master Card'],
@ -432,6 +443,7 @@ function otrans($text)
return $string != $english ? $string : ''; return $string != $english ? $string : '';
} }
} }
}
/* /*
// Log all SQL queries to laravel.log // Log all SQL queries to laravel.log

View File

@ -3,6 +3,7 @@
use Auth; use Auth;
use Cache; use Cache;
use DB; use DB;
use App;
use Schema; use Schema;
use Session; use Session;
use Request; use Request;
@ -61,7 +62,7 @@ class Utils
public static function allowNewAccounts() public static function allowNewAccounts()
{ {
return Utils::isNinja() || (isset($_ENV['ALLOW_NEW_ACCOUNTS']) && $_ENV['ALLOW_NEW_ACCOUNTS'] == 'true'); return Utils::isNinja() || Auth::check();
} }
public static function isPro() public static function isPro()
@ -69,6 +70,11 @@ class Utils
return Auth::check() && Auth::user()->isPro(); return Auth::check() && Auth::user()->isPro();
} }
public static function isEnglish()
{
return App::getLocale() == 'en';
}
public static function getUserType() public static function getUserType()
{ {
if (Utils::isNinja()) { if (Utils::isNinja()) {
@ -251,6 +257,10 @@ class Utils
$currency = Currency::find(1); $currency = Currency::find(1);
} }
if (!$value) {
$value = 0;
}
Cache::add('currency', $currency, DEFAULT_QUERY_CACHE); Cache::add('currency', $currency, DEFAULT_QUERY_CACHE);
return $currency->symbol.number_format($value, $currency->precision, $currency->decimal_separator, $currency->thousand_separator); return $currency->symbol.number_format($value, $currency->precision, $currency->decimal_separator, $currency->thousand_separator);
@ -315,16 +325,27 @@ class Utils
return $date->format($format); return $date->format($format);
} }
public static function getTiemstampOffset()
{
$timezone = new DateTimeZone(Session::get(SESSION_TIMEZONE, DEFAULT_TIMEZONE));
$datetime = new DateTime('now', $timezone);
$offset = $timezone->getOffset($datetime);
$minutes = $offset / 60;
return $minutes;
}
public static function toSqlDate($date, $formatResult = true) public static function toSqlDate($date, $formatResult = true)
{ {
if (!$date) { if (!$date) {
return; return;
} }
$timezone = Session::get(SESSION_TIMEZONE, DEFAULT_TIMEZONE); //$timezone = Session::get(SESSION_TIMEZONE, DEFAULT_TIMEZONE);
$format = Session::get(SESSION_DATE_FORMAT, DEFAULT_DATE_FORMAT); $format = Session::get(SESSION_DATE_FORMAT, DEFAULT_DATE_FORMAT);
$dateTime = DateTime::createFromFormat($format, $date, new DateTimeZone($timezone)); //$dateTime = DateTime::createFromFormat($format, $date, new DateTimeZone($timezone));
$dateTime = DateTime::createFromFormat($format, $date);
return $formatResult ? $dateTime->format('Y-m-d') : $dateTime; return $formatResult ? $dateTime->format('Y-m-d') : $dateTime;
} }
@ -335,11 +356,11 @@ class Utils
return ''; return '';
} }
$timezone = Session::get(SESSION_TIMEZONE, DEFAULT_TIMEZONE); //$timezone = Session::get(SESSION_TIMEZONE, DEFAULT_TIMEZONE);
$format = Session::get(SESSION_DATE_FORMAT, DEFAULT_DATE_FORMAT); $format = Session::get(SESSION_DATE_FORMAT, DEFAULT_DATE_FORMAT);
$dateTime = DateTime::createFromFormat('Y-m-d', $date); $dateTime = DateTime::createFromFormat('Y-m-d', $date);
$dateTime->setTimeZone(new DateTimeZone($timezone)); //$dateTime->setTimeZone(new DateTimeZone($timezone));
return $formatResult ? $dateTime->format($format) : $dateTime; return $formatResult ? $dateTime->format($format) : $dateTime;
} }
@ -386,10 +407,12 @@ class Utils
} }
$object = new stdClass(); $object = new stdClass();
$object->accountId = Auth::user()->account_id;
$object->url = $url; $object->url = $url;
$object->name = ucwords($type).': '.$name; $object->name = ucwords($type).': '.$name;
$data = []; $data = [];
$counts = [];
for ($i = 0; $i<count($viewed); $i++) { for ($i = 0; $i<count($viewed); $i++) {
$item = $viewed[$i]; $item = $viewed[$i];
@ -398,12 +421,22 @@ class Utils
continue; continue;
} }
// temporary fix to check for new property in session
if (!property_exists($item, 'accountId')) {
continue;
}
array_unshift($data, $item); array_unshift($data, $item);
if (isset($counts[$item->accountId])) {
$counts[$item->accountId]++;
} else {
$counts[$item->accountId] = 1;
}
} }
array_unshift($data, $object); array_unshift($data, $object);
if (count($data) > RECENTLY_VIEWED_LIMIT) { if (isset($counts[Auth::user()->account_id]) && $counts[Auth::user()->account_id] > RECENTLY_VIEWED_LIMIT) {
array_pop($data); array_pop($data);
} }
@ -677,4 +710,37 @@ class Utils
fwrite($output, "\n"); fwrite($output, "\n");
} }
public static function stringToObjectResolution($baseObject, $rawPath)
{
$val = '';
if (!is_object($baseObject)) {
return $val;
}
$path = preg_split('/->/', $rawPath);
$node = $baseObject;
while (($prop = array_shift($path)) !== null) {
if (property_exists($node, $prop)) {
$val = $node->$prop;
$node = $node->$prop;
} else if (is_object($node) && isset($node->$prop)) {
$node = $node->{$prop};
} else if ( method_exists($node, $prop)) {
$val = call_user_func(array($node, $prop));
}
}
return $val;
}
public static function getFirst($values) {
if (is_array($values)) {
return count($values) ? $values[0] : false;
} else {
return $values;
}
}
} }

View File

@ -27,11 +27,13 @@ class HandleUserSettingsChanged {
*/ */
public function handle(UserSettingsChanged $event) public function handle(UserSettingsChanged $event)
{ {
if (Auth::check()) {
$account = Auth::user()->account; $account = Auth::user()->account;
$account->loadLocalizationSettings(); $account->loadLocalizationSettings();
$users = $this->accountRepo->loadAccounts(Auth::user()->id); $users = $this->accountRepo->loadAccounts(Auth::user()->id);
Session::put(SESSION_USER_ACCOUNTS, $users); Session::put(SESSION_USER_ACCOUNTS, $users);
} }
}
} }

View File

@ -4,7 +4,9 @@ use Eloquent;
use Utils; use Utils;
use Session; use Session;
use DateTime; use DateTime;
use Event;
use App;
use App\Events\UserSettingsChanged;
use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\SoftDeletes;
class Account extends Eloquent class Account extends Eloquent
@ -12,6 +14,12 @@ class Account extends Eloquent
use SoftDeletes; use SoftDeletes;
protected $dates = ['deleted_at']; protected $dates = ['deleted_at'];
/*
protected $casts = [
'hide_quantity' => 'boolean',
];
*/
public function users() public function users()
{ {
return $this->hasMany('App\Models\User'); return $this->hasMany('App\Models\User');
@ -88,6 +96,11 @@ class Account extends Eloquent
} }
} }
public function isEnglish()
{
return !$this->language_id || $this->language_id == DEFAULT_LANGUAGE;
}
public function getDisplayName() public function getDisplayName()
{ {
if ($this->name) { if ($this->name) {
@ -142,7 +155,9 @@ class Account extends Eloquent
public function getLogoPath() public function getLogoPath()
{ {
return 'logo/'.$this->account_key.'.jpg'; $fileName = 'logo/' . $this->account_key;
return file_exists($fileName.'.png') ? $fileName.'.png' : $fileName.'.jpg';
} }
public function getLogoWidth() public function getLogoWidth()
@ -171,35 +186,37 @@ class Account extends Eloquent
{ {
$counter = $isQuote && !$this->share_counter ? $this->quote_number_counter : $this->invoice_number_counter; $counter = $isQuote && !$this->share_counter ? $this->quote_number_counter : $this->invoice_number_counter;
$prefix .= $isQuote ? $this->quote_number_prefix : $this->invoice_number_prefix; $prefix .= $isQuote ? $this->quote_number_prefix : $this->invoice_number_prefix;
$counterOffset = 0;
// confirm the invoice number isn't already taken // confirm the invoice number isn't already taken
do { do {
$number = $prefix.str_pad($counter, 4, "0", STR_PAD_LEFT); $number = $prefix.str_pad($counter, 4, "0", STR_PAD_LEFT);
$check = Invoice::scope(false, $this->id)->whereInvoiceNumber($number)->withTrashed()->first(); $check = Invoice::scope(false, $this->id)->whereInvoiceNumber($number)->withTrashed()->first();
$counter++; $counter++;
$counterOffset++;
} while ($check); } while ($check);
// update the invoice counter to be caught up
if ($counterOffset > 1) {
if ($isQuote && !$this->share_counter) {
$this->quote_number_counter += $counterOffset - 1;
} else {
$this->invoice_number_counter += $counterOffset - 1;
}
$this->save();
}
return $number; return $number;
} }
public function incrementCounter($invoiceNumber, $isQuote = false, $isRecurring) public function incrementCounter($isQuote = false)
{ {
// check if the user modified the invoice number
if (!$isRecurring && $invoiceNumber != $this->getNextInvoiceNumber($isQuote)) {
$number = intval(preg_replace('/[^0-9]/', '', $invoiceNumber));
if ($isQuote && !$this->share_counter) {
$this->quote_number_counter = $number + 1;
} else {
$this->invoice_number_counter = $number + 1;
}
// otherwise, just increment the counter
} else {
if ($isQuote && !$this->share_counter) { if ($isQuote && !$this->share_counter) {
$this->quote_number_counter += 1; $this->quote_number_counter += 1;
} else { } else {
$this->invoice_number_counter += 1; $this->invoice_number_counter += 1;
} }
}
$this->save(); $this->save();
} }
@ -221,6 +238,8 @@ class Account extends Eloquent
Session::put(SESSION_DATETIME_FORMAT, $this->datetime_format ? $this->datetime_format->format : DEFAULT_DATETIME_FORMAT); Session::put(SESSION_DATETIME_FORMAT, $this->datetime_format ? $this->datetime_format->format : DEFAULT_DATETIME_FORMAT);
Session::put(SESSION_CURRENCY, $this->currency_id ? $this->currency_id : DEFAULT_CURRENCY); Session::put(SESSION_CURRENCY, $this->currency_id ? $this->currency_id : DEFAULT_CURRENCY);
Session::put(SESSION_LOCALE, $this->language_id ? $this->language->locale : DEFAULT_LOCALE); Session::put(SESSION_LOCALE, $this->language_id ? $this->language->locale : DEFAULT_LOCALE);
App::setLocale(session(SESSION_LOCALE));
} }
public function getInvoiceLabels() public function getInvoiceLabels()
@ -258,13 +277,18 @@ class Account extends Eloquent
'rate', 'rate',
'hours', 'hours',
'balance', 'balance',
'from',
'to',
'invoice_to',
'details',
'invoice_no',
]; ];
foreach ($fields as $field) { foreach ($fields as $field) {
if (isset($custom[$field]) && $custom[$field]) { if (isset($custom[$field]) && $custom[$field]) {
$data[$field] = $custom[$field]; $data[$field] = $custom[$field];
} else { } else {
$data[$field] = uctrans("texts.$field"); $data[$field] = $this->isEnglish() ? uctrans("texts.$field") : trans("texts.$field");
} }
} }
@ -303,11 +327,11 @@ class Account extends Eloquent
public function isWhiteLabel() public function isWhiteLabel()
{ {
if (Utils::isNinjaProd()) { if (Utils::isNinjaProd()) {
return false; return self::isPro() && $this->pro_plan_paid != NINJA_DATE;
} } else {
return $this->pro_plan_paid == NINJA_DATE; return $this->pro_plan_paid == NINJA_DATE;
} }
}
public function getSubscription($eventId) public function getSubscription($eventId)
{ {
@ -335,6 +359,8 @@ class Account extends Eloquent
'invoice_status_id', 'invoice_status_id',
'invoice_items', 'invoice_items',
'created_at', 'created_at',
'is_recurring',
'is_quote',
]); ]);
foreach ($invoice->invoice_items as $invoiceItem) { foreach ($invoice->invoice_items as $invoiceItem) {
@ -407,9 +433,6 @@ class Account extends Eloquent
} }
} }
Account::updating(function ($account) { Account::updated(function ($account) {
// Lithuanian requires UTF8 support Event::fire(new UserSettingsChanged());
if (!Utils::isPro()) {
$account->utf8_invoices = ($account->language_id == 13) ? 1 : 0;
}
}); });

View File

@ -214,6 +214,8 @@ class Activity extends Eloquent
if ($invoice->isPaid() && $invoice->balance > 0) { if ($invoice->isPaid() && $invoice->balance > 0) {
$invoice->invoice_status_id = INVOICE_STATUS_PARTIAL; $invoice->invoice_status_id = INVOICE_STATUS_PARTIAL;
} elseif ($invoice->invoice_status_id && $invoice->balance == 0) {
$invoice->invoice_status_id = INVOICE_STATUS_PAID;
} }
} }
} }

View File

@ -83,8 +83,6 @@ class Client extends EntityModel
return $this->name; return $this->name;
} }
$this->load('contacts');
$contact = $this->contacts()->first(); $contact = $this->contacts()->first();
return $contact->getDisplayName(); return $contact->getDisplayName();
@ -152,11 +150,15 @@ class Client extends EntityModel
public function getCurrencyId() public function getCurrencyId()
{ {
if ($this->currency_id) {
return $this->currency_id;
}
if (!$this->account) { if (!$this->account) {
$this->load('account'); $this->load('account');
} }
return $this->currency_id ?: ($this->account->currency_id ?: DEFAULT_CURRENCY); return $this->account->currency_id ?: DEFAULT_CURRENCY;
} }
} }

View File

@ -44,7 +44,7 @@ class EntityModel extends Eloquent
public function getActivityKey() public function getActivityKey()
{ {
return '[' . $this->getEntityType().':'.$this->public_id.':'.$this->getName() . ']'; return '[' . $this->getEntityType().':'.$this->public_id.':'.$this->getDisplayName() . ']';
} }
/* /*
@ -83,6 +83,11 @@ class EntityModel extends Eloquent
return $this->public_id; return $this->public_id;
} }
public function getDisplayName()
{
return $this->getName();
}
// Remap ids to public_ids and show name // Remap ids to public_ids and show name
public function toPublicArray() public function toPublicArray()
{ {

View File

@ -48,6 +48,11 @@ class Invoice extends EntityModel
return $this->belongsTo('App\Models\Invoice'); return $this->belongsTo('App\Models\Invoice');
} }
public function recurring_invoices()
{
return $this->hasMany('App\Models\Invoice', 'recurring_invoice_id');
}
public function invitations() public function invitations()
{ {
return $this->hasMany('App\Models\Invitation')->orderBy('invitations.contact_id'); return $this->hasMany('App\Models\Invitation')->orderBy('invitations.contact_id');
@ -55,7 +60,7 @@ class Invoice extends EntityModel
public function getName() public function getName()
{ {
return $this->invoice_number; return $this->is_recurring ? trans('texts.recurring') : $this->invoice_number;
} }
public function getFileName() public function getFileName()
@ -69,9 +74,14 @@ class Invoice extends EntityModel
return storage_path() . '/pdfcache/cache-' . $this->id . '.pdf'; return storage_path() . '/pdfcache/cache-' . $this->id . '.pdf';
} }
public static function calcLink($invoice)
{
return link_to('invoices/' . $invoice->public_id, $invoice->invoice_number);
}
public function getLink() public function getLink()
{ {
return link_to('invoices/'.$this->public_id, $this->invoice_number); return self::calcLink($this);
} }
public function getEntityType() public function getEntityType()
@ -252,8 +262,13 @@ class Invoice extends EntityModel
} }
} }
Invoice::creating(function ($invoice) {
if (!$invoice->is_recurring) {
$invoice->account->incrementCounter($invoice->is_quote);
}
});
Invoice::created(function ($invoice) { Invoice::created(function ($invoice) {
$invoice->account->incrementCounter($invoice->invoice_number, $invoice->is_quote, $invoice->recurring_invoice_id);
Activity::createInvoice($invoice); Activity::createInvoice($invoice);
}); });

View File

@ -2,19 +2,32 @@
use Eloquent; use Eloquent;
use Auth; use Auth;
use Cache;
use App\Models\InvoiceDesign;
class InvoiceDesign extends Eloquent class InvoiceDesign extends Eloquent
{ {
public $timestamps = false; public $timestamps = false;
public function scopeAvailableDesigns($query) public static function getDesigns()
{ {
$designs = $query->where('id', '<=', \Auth::user()->maxInvoiceDesignId())->orderBy('id')->get(); $account = Auth::user()->account;
$designs = Cache::get('invoiceDesigns');
foreach ($designs as $design) { foreach ($designs as $design) {
$fileName = public_path(strtolower("js/templates/{$design->name}.js")); if ($design->id > Auth::user()->maxInvoiceDesignId()) {
if (Auth::user()->account->utf8_invoices && file_exists($fileName)) { $designs->pull($design->id);
$design->javascript = file_get_contents($fileName); }
$design->javascript = $design->pdfmake;
$design->pdfmake = null;
if ($design->id == CUSTOM_DESIGN) {
if ($account->custom_design) {
$design->javascript = $account->custom_design;
} else {
$designs->pop();
}
} }
} }

View File

@ -1,7 +1,7 @@
<?php namespace App\Models; <?php namespace App\Models;
use DB; use DB;
use Utils;
use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\SoftDeletes;
class Task extends EntityModel class Task extends EntityModel
@ -22,6 +22,66 @@ class Task extends EntityModel
{ {
return $this->belongsTo('App\Models\Client')->withTrashed(); return $this->belongsTo('App\Models\Client')->withTrashed();
} }
public static function calcStartTime($task)
{
$parts = json_decode($task->time_log) ?: [];
if (count($parts)) {
return Utils::timestampToDateTimeString($parts[0][0]);
} else {
return '';
}
}
public function getStartTime()
{
return self::calcStartTime($this);
}
public static function calcDuration($task)
{
$duration = 0;
$parts = json_decode($task->time_log) ?: [];
foreach ($parts as $part) {
if (count($part) == 1 || !$part[1]) {
$duration += time() - $part[0];
} else {
$duration += $part[1] - $part[0];
}
}
return $duration;
}
public function getDuration()
{
return self::calcDuration($this);
}
public function getCurrentDuration()
{
$parts = json_decode($this->time_log) ?: [];
$part = $parts[count($parts)-1];
if (count($part) == 1 || !$part[1]) {
return time() - $part[0];
} else {
return 0;
}
}
public function hasPreviousDuration()
{
$parts = json_decode($this->time_log) ?: [];
return count($parts) && (count($parts[0]) && $parts[0][1]);
}
public function getHours()
{
return round($this->getDuration() / (60 * 60), 2);
}
} }
Task::created(function ($task) { Task::created(function ($task) {

View File

@ -2,7 +2,9 @@
use Session; use Session;
use Auth; use Auth;
use Event;
use App\Libraries\Utils; use App\Libraries\Utils;
use App\Events\UserSettingsChanged;
use Illuminate\Auth\Authenticatable; use Illuminate\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Auth\Passwords\CanResetPassword; use Illuminate\Auth\Passwords\CanResetPassword;
@ -100,7 +102,7 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
public function maxInvoiceDesignId() public function maxInvoiceDesignId()
{ {
return $this->isPro() ? 10 : COUNT_FREE_DESIGNS; return $this->isPro() ? 11 : (Utils::isNinja() ? COUNT_FREE_DESIGNS : COUNT_FREE_DESIGNS_SELF_HOST);
} }
public function getDisplayName() public function getDisplayName()
@ -213,3 +215,8 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
User::updating(function ($user) { User::updating(function ($user) {
User::updateUser($user); User::updateUser($user);
}); });
User::updated(function ($user) {
Event::fire(new UserSettingsChanged());
});

View File

@ -23,6 +23,8 @@ class ContactMailer extends Mailer
$emailTemplate = $invoice->account->getEmailTemplate($entityType); $emailTemplate = $invoice->account->getEmailTemplate($entityType);
$invoiceAmount = Utils::formatMoney($invoice->getRequestedAmount(), $invoice->client->getCurrencyId()); $invoiceAmount = Utils::formatMoney($invoice->getRequestedAmount(), $invoice->client->getCurrencyId());
$this->initClosure($invoice);
foreach ($invoice->invitations as $invitation) { foreach ($invoice->invitations as $invitation) {
if (!$invitation->user || !$invitation->user->email || $invitation->user->trashed()) { if (!$invitation->user || !$invitation->user->email || $invitation->user->trashed()) {
return false; return false;
@ -40,7 +42,8 @@ class ContactMailer extends Mailer
'$client' => $invoice->client->getDisplayName(), '$client' => $invoice->client->getDisplayName(),
'$account' => $accountName, '$account' => $accountName,
'$contact' => $invitation->contact->getDisplayName(), '$contact' => $invitation->contact->getDisplayName(),
'$amount' => $invoiceAmount '$amount' => $invoiceAmount,
'$advancedRawInvoice->' => '$'
]; ];
// Add variables for available payment types // Add variables for available payment types
@ -49,6 +52,7 @@ class ContactMailer extends Mailer
} }
$data['body'] = str_replace(array_keys($variables), array_values($variables), $emailTemplate); $data['body'] = str_replace(array_keys($variables), array_values($variables), $emailTemplate);
$data['body'] = preg_replace_callback('/\{\{\$?(.*)\}\}/', $this->advancedTemplateHandler, $data['body']);
$data['link'] = $invitation->getLink(); $data['link'] = $invitation->getLink();
$data['entityType'] = $entityType; $data['entityType'] = $entityType;
$data['invoice_id'] = $invoice->id; $data['invoice_id'] = $invoice->id;
@ -69,6 +73,8 @@ class ContactMailer extends Mailer
} }
Event::fire(new InvoiceSent($invoice)); Event::fire(new InvoiceSent($invoice));
return $response;
} }
public function sendPaymentConfirmation(Payment $payment) public function sendPaymentConfirmation(Payment $payment)
@ -123,4 +129,22 @@ class ContactMailer extends Mailer
$this->sendTo($email, CONTACT_EMAIL, CONTACT_NAME, $subject, $view, $data); $this->sendTo($email, CONTACT_EMAIL, CONTACT_NAME, $subject, $view, $data);
} }
private function initClosure($object)
{
$this->advancedTemplateHandler = function($match) use ($object) {
for ($i = 1; $i < count($match); $i++) {
$blobConversion = $match[$i];
if (isset($$blobConversion)) {
return $$blobConversion;
} else if (preg_match('/trans\(([\w\.]+)\)/', $blobConversion, $regexTranslation)) {
return trans($regexTranslation[1]);
} else if (strpos($blobConversion, '->') !== false) {
return Utils::stringToObjectResolution($object, $blobConversion);
}
}
};
}
} }

View File

@ -17,6 +17,7 @@ class Mailer
try { try {
Mail::send($views, $data, function ($message) use ($toEmail, $fromEmail, $fromName, $subject, $data) { Mail::send($views, $data, function ($message) use ($toEmail, $fromEmail, $fromName, $subject, $data) {
$toEmail = strtolower($toEmail);
$replyEmail = $fromEmail; $replyEmail = $fromEmail;
$fromEmail = CONTACT_EMAIL; $fromEmail = CONTACT_EMAIL;
@ -30,14 +31,22 @@ class Mailer
} }
} }
$message->to($toEmail)->from($fromEmail, $fromName)->replyTo($replyEmail, $fromName)->subject($subject); $message->to($toEmail)
->from($fromEmail, $fromName)
->replyTo($replyEmail, $fromName)
->subject($subject);
}); });
return true; return true;
} catch (Exception $e) { } catch (Exception $exception) {
$response = $e->getResponse()->getBody()->getContents(); if (isset($_ENV['POSTMARK_API_TOKEN'])) {
$response = $exception->getResponse()->getBody()->getContents();
$response = json_decode($response); $response = json_decode($response);
return nl2br($response->Message); return nl2br($response->Message);
} else {
return $exception->getMessage();
}
} }
} }
} }

View File

@ -47,7 +47,7 @@ class UserMailer extends Mailer
'clientName' => $invoice->client->getDisplayName(), 'clientName' => $invoice->client->getDisplayName(),
'accountName' => $invoice->account->getDisplayName(), 'accountName' => $invoice->account->getDisplayName(),
'userName' => $user->getDisplayName(), 'userName' => $user->getDisplayName(),
'invoiceAmount' => Utils::formatMoney($invoice->amount, $invoice->client->getCurrencyId()), 'invoiceAmount' => Utils::formatMoney($invoice->getRequestedAmount(), $invoice->client->getCurrencyId()),
'invoiceNumber' => $invoice->invoice_number, 'invoiceNumber' => $invoice->invoice_number,
'invoiceLink' => SITE_URL."/{$entityType}s/{$invoice->public_id}", 'invoiceLink' => SITE_URL."/{$entityType}s/{$invoice->public_id}",
]; ];

View File

@ -6,7 +6,7 @@ use Session;
use Utils; use Utils;
use DB; use DB;
use stdClass; use stdClass;
use Schema;
use App\Models\AccountGateway; use App\Models\AccountGateway;
use App\Models\Invitation; use App\Models\Invitation;
use App\Models\Invoice; use App\Models\Invoice;
@ -250,6 +250,10 @@ class AccountRepository
public function findUserAccounts($userId1, $userId2 = false) public function findUserAccounts($userId1, $userId2 = false)
{ {
if (!Schema::hasTable('user_accounts')) {
return false;
}
$query = UserAccount::where('user_id1', '=', $userId1) $query = UserAccount::where('user_id1', '=', $userId1)
->orWhere('user_id2', '=', $userId1) ->orWhere('user_id2', '=', $userId1)
->orWhere('user_id3', '=', $userId1) ->orWhere('user_id3', '=', $userId1)
@ -268,7 +272,6 @@ class AccountRepository
} }
public function prepareUsersData($record) { public function prepareUsersData($record) {
if (!$record) { if (!$record) {
return false; return false;
} }
@ -294,7 +297,7 @@ class AccountRepository
$item->account_id = $user->account->id; $item->account_id = $user->account->id;
$item->account_name = $user->account->getDisplayName(); $item->account_name = $user->account->getDisplayName();
$item->pro_plan_paid = $user->account->pro_plan_paid; $item->pro_plan_paid = $user->account->pro_plan_paid;
$item->account_key = file_exists($user->account->getLogoPath()) ? $user->account->account_key : null; $item->logo_path = file_exists($user->account->getLogoPath()) ? $user->account->getLogoPath() : null;
$data[] = $item; $data[] = $item;
} }
@ -312,6 +315,9 @@ class AccountRepository
} }
public function syncUserAccounts($users, $proPlanPaid = false) { public function syncUserAccounts($users, $proPlanPaid = false) {
if (!$users) {
return;
}
if (!$proPlanPaid) { if (!$proPlanPaid) {
foreach ($users as $user) { foreach ($users as $user) {
@ -374,7 +380,6 @@ class AccountRepository
} }
public function unlinkUser($userAccountId, $userId) { public function unlinkUser($userAccountId, $userId) {
$userAccount = UserAccount::whereId($userAccountId)->first(); $userAccount = UserAccount::whereId($userAccountId)->first();
if ($userAccount->hasUserId($userId)) { if ($userAccount->hasUserId($userId)) {
$userAccount->removeUserId($userId); $userAccount->removeUserId($userId);

View File

@ -120,7 +120,7 @@ class ClientRepository
if (isset($data['contact'])) { if (isset($data['contact'])) {
$info = $data['contact']; $info = $data['contact'];
if (isset($info['email'])) { if (isset($info['email'])) {
$contact->email = trim(strtolower($info['email'])); $contact->email = trim($info['email']);
} }
if (isset($info['first_name'])) { if (isset($info['first_name'])) {
$contact->first_name = trim($info['first_name']); $contact->first_name = trim($info['first_name']);
@ -145,7 +145,7 @@ class ClientRepository
} }
if (isset($record['email'])) { if (isset($record['email'])) {
$contact->email = trim(strtolower($record['email'])); $contact->email = trim($record['email']);
} }
if (isset($record['first_name'])) { if (isset($record['first_name'])) {
$contact->first_name = trim($record['first_name']); $contact->first_name = trim($record['first_name']);

View File

@ -1,11 +1,12 @@
<?php namespace App\Ninja\Repositories; <?php namespace App\Ninja\Repositories;
use Carbon;
use Utils;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\InvoiceItem; use App\Models\InvoiceItem;
use App\Models\Invitation; use App\Models\Invitation;
use App\Models\Product; use App\Models\Product;
use App\Models\Task; use App\Models\Task;
use Utils;
class InvoiceRepository class InvoiceRepository
{ {
@ -266,15 +267,18 @@ class InvoiceRepository
$account->save(); $account->save();
} }
$invoice->client_id = $data['client_id']; if (isset($data['invoice_number'])) {
$invoice->invoice_number = trim($data['invoice_number']);
}
$invoice->discount = round(Utils::parseFloat($data['discount']), 2); $invoice->discount = round(Utils::parseFloat($data['discount']), 2);
$invoice->is_amount_discount = $data['is_amount_discount'] ? true : false; $invoice->is_amount_discount = $data['is_amount_discount'] ? true : false;
$invoice->invoice_number = trim($data['invoice_number']);
$invoice->partial = round(Utils::parseFloat($data['partial']), 2); $invoice->partial = round(Utils::parseFloat($data['partial']), 2);
$invoice->invoice_date = isset($data['invoice_date_sql']) ? $data['invoice_date_sql'] : Utils::toSqlDate($data['invoice_date']); $invoice->invoice_date = isset($data['invoice_date_sql']) ? $data['invoice_date_sql'] : Utils::toSqlDate($data['invoice_date']);
$invoice->has_tasks = isset($data['has_tasks']) ? $data['has_tasks'] : false; $invoice->has_tasks = isset($data['has_tasks']) ? $data['has_tasks'] : false;
if (!$publicId) { if (!$publicId) {
$invoice->client_id = $data['client_id'];
$invoice->is_recurring = $data['is_recurring'] && !Utils::isDemo() ? true : false; $invoice->is_recurring = $data['is_recurring'] && !Utils::isDemo() ? true : false;
} }
@ -311,6 +315,7 @@ class InvoiceRepository
} }
$total = 0; $total = 0;
$itemTax = 0;
foreach ($data['invoice_items'] as $item) { foreach ($data['invoice_items'] as $item) {
$item = (array) $item; $item = (array) $item;
@ -320,15 +325,29 @@ class InvoiceRepository
$invoiceItemCost = round(Utils::parseFloat($item['cost']), 2); $invoiceItemCost = round(Utils::parseFloat($item['cost']), 2);
$invoiceItemQty = round(Utils::parseFloat($item['qty']), 2); $invoiceItemQty = round(Utils::parseFloat($item['qty']), 2);
$invoiceItemTaxRate = 0;
if (isset($item['tax_rate']) && Utils::parseFloat($item['tax_rate']) > 0) {
$invoiceItemTaxRate = Utils::parseFloat($item['tax_rate']);
}
$lineTotal = $invoiceItemCost * $invoiceItemQty; $lineTotal = $invoiceItemCost * $invoiceItemQty;
$total += round($lineTotal, 2);
}
$total += round($lineTotal + ($lineTotal * $invoiceItemTaxRate / 100), 2); foreach ($data['invoice_items'] as $item) {
$item = (array) $item;
if (isset($item['tax_rate']) && Utils::parseFloat($item['tax_rate']) > 0) {
$invoiceItemCost = round(Utils::parseFloat($item['cost']), 2);
$invoiceItemQty = round(Utils::parseFloat($item['qty']), 2);
$invoiceItemTaxRate = Utils::parseFloat($item['tax_rate']);
$lineTotal = $invoiceItemCost * $invoiceItemQty;
if ($invoice->discount > 0) {
if ($invoice->is_amount_discount) {
$lineTotal -= round(($lineTotal/$total) * $invoice->discount, 2);
} else {
$lineTotal -= round($lineTotal * ($invoice->discount/100), 2);
}
}
$itemTax += round($lineTotal * $invoiceItemTaxRate / 100, 2);
}
} }
if ($invoice->discount > 0) { if ($invoice->discount > 0) {
@ -354,6 +373,7 @@ class InvoiceRepository
$total += $total * $invoice->tax_rate / 100; $total += $total * $invoice->tax_rate / 100;
$total = round($total, 2); $total = round($total, 2);
$total += $itemTax;
// custom fields not charged taxes // custom fields not charged taxes
if ($invoice->custom_value1 && !$invoice->custom_taxes1) { if ($invoice->custom_value1 && !$invoice->custom_taxes1) {
@ -387,21 +407,20 @@ class InvoiceRepository
$task->invoice_id = $invoice->id; $task->invoice_id = $invoice->id;
$task->client_id = $invoice->client_id; $task->client_id = $invoice->client_id;
$task->save(); $task->save();
} else if ($item['product_key']) { } else if ($item['product_key'] && !$invoice->has_tasks) {
$product = Product::findProductByKey(trim($item['product_key'])); $product = Product::findProductByKey(trim($item['product_key']));
if (\Auth::user()->account->update_products) {
if (!$product) { if (!$product) {
$product = Product::createNew(); $product = Product::createNew();
$product->product_key = trim($item['product_key']); $product->product_key = trim($item['product_key']);
} }
if (\Auth::user()->account->update_products) {
$product->notes = $item['notes']; $product->notes = $item['notes'];
$product->cost = $item['cost']; $product->cost = $item['cost'];
}
$product->save(); $product->save();
} }
}
$invoiceItem = InvoiceItem::createNew(); $invoiceItem = InvoiceItem::createNew();
$invoiceItem->product_id = isset($product) ? $product->id : null; $invoiceItem->product_id = isset($product) ? $product->id : null;
@ -536,4 +555,89 @@ class InvoiceRepository
return count($invoices); return count($invoices);
} }
public function findOpenInvoices($clientId)
{
return Invoice::scope()
->whereClientId($clientId)
->whereIsQuote(false)
->whereIsRecurring(false)
->whereDeletedAt(null)
->whereHasTasks(true)
->where('invoice_status_id', '<', 5)
->select(['public_id', 'invoice_number'])
->get();
}
public function createRecurringInvoice($recurInvoice)
{
$recurInvoice->load('account.timezone', 'invoice_items', 'client', 'user');
if ($recurInvoice->client->deleted_at) {
return false;
}
if (!$recurInvoice->user->confirmed) {
return false;
}
if (!$recurInvoice->shouldSendToday()) {
return false;
}
$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->amount = $recurInvoice->amount;
$invoice->balance = $recurInvoice->amount;
$invoice->invoice_date = date_create()->format('Y-m-d');
$invoice->discount = $recurInvoice->discount;
$invoice->po_number = $recurInvoice->po_number;
$invoice->public_notes = Utils::processVariables($recurInvoice->public_notes);
$invoice->terms = Utils::processVariables($recurInvoice->terms);
$invoice->invoice_footer = Utils::processVariables($recurInvoice->invoice_footer);
$invoice->tax_name = $recurInvoice->tax_name;
$invoice->tax_rate = $recurInvoice->tax_rate;
$invoice->invoice_design_id = $recurInvoice->invoice_design_id;
$invoice->custom_value1 = $recurInvoice->custom_value1;
$invoice->custom_value2 = $recurInvoice->custom_value2;
$invoice->custom_taxes1 = $recurInvoice->custom_taxes1;
$invoice->custom_taxes2 = $recurInvoice->custom_taxes2;
$invoice->is_amount_discount = $recurInvoice->is_amount_discount;
if ($invoice->client->payment_terms != 0) {
$days = $invoice->client->payment_terms;
if ($days == -1) {
$days = 0;
}
$invoice->due_date = date_create()->modify($days.' day')->format('Y-m-d');
}
$invoice->save();
foreach ($recurInvoice->invoice_items as $recurItem) {
$item = InvoiceItem::createNew($recurItem);
$item->product_id = $recurItem->product_id;
$item->qty = $recurItem->qty;
$item->cost = $recurItem->cost;
$item->notes = Utils::processVariables($recurItem->notes);
$item->product_key = Utils::processVariables($recurItem->product_key);
$item->tax_name = $recurItem->tax_name;
$item->tax_rate = $recurItem->tax_rate;
$invoice->invoice_items()->save($item);
}
foreach ($recurInvoice->invitations as $recurInvitation) {
$invitation = Invitation::createNew($recurInvitation);
$invitation->contact_id = $recurInvitation->contact_id;
$invitation->invitation_key = str_random(RANDOM_KEY_LENGTH);
$invoice->invitations()->save($invitation);
}
$recurInvoice->last_sent_date = Carbon::now()->toDateTimeString();
$recurInvoice->save();
return $invoice;
}
} }

View File

@ -23,7 +23,7 @@ class TaskRepository
}) })
->where('contacts.deleted_at', '=', null) ->where('contacts.deleted_at', '=', null)
->where('clients.deleted_at', '=', null) ->where('clients.deleted_at', '=', null)
->select('tasks.public_id', 'clients.name as client_name', 'clients.public_id as client_public_id', 'contacts.first_name', 'contacts.email', 'contacts.last_name', 'invoices.invoice_status_id', 'tasks.start_time', 'tasks.description', 'tasks.duration', 'tasks.is_deleted', 'tasks.deleted_at', 'invoices.invoice_number', 'invoices.public_id as invoice_public_id', 'tasks.is_running'); ->select('tasks.public_id', 'clients.name as client_name', 'clients.public_id as client_public_id', 'contacts.first_name', 'contacts.email', 'contacts.last_name', 'invoices.invoice_status_id', 'tasks.description', 'tasks.is_deleted', 'tasks.deleted_at', 'invoices.invoice_number', 'invoices.public_id as invoice_public_id', 'tasks.is_running', 'tasks.time_log', 'tasks.created_at');
if ($clientPublicId) { if ($clientPublicId) {
$query->where('clients.public_id', '=', $clientPublicId); $query->where('clients.public_id', '=', $clientPublicId);
@ -60,36 +60,26 @@ class TaskRepository
$task->description = trim($data['description']); $task->description = trim($data['description']);
} }
$timeLog = $task->time_log ? json_decode($task->time_log, true) : []; if (isset($data['time_log'])) {
$timeLog = json_decode($data['time_log']);
} elseif ($task->time_log) {
$timeLog = json_decode($task->time_log);
} else {
$timeLog = [];
}
if ($data['action'] == 'start') { if ($data['action'] == 'start') {
$task->start_time = Carbon::now()->toDateTimeString();
$task->is_running = true; $task->is_running = true;
$timeLog[] = [strtotime('now'), false]; $timeLog[] = [strtotime('now'), false];
} else if ($data['action'] == 'resume') { } else if ($data['action'] == 'resume') {
$task->break_duration = strtotime('now') - strtotime($task->start_time) + $task->duration;
$task->resume_time = Carbon::now()->toDateTimeString();
$task->is_running = true; $task->is_running = true;
$timeLog[] = [strtotime('now'), false]; $timeLog[] = [strtotime('now'), false];
} else if ($data['action'] == 'stop' && $task->is_running) { } else if ($data['action'] == 'stop' && $task->is_running) {
if ($task->resume_time) { $timeLog[count($timeLog)-1][1] = time();
$task->duration = $task->duration + strtotime('now') - strtotime($task->resume_time);
$task->resume_time = null;
} else {
$task->duration = strtotime('now') - strtotime($task->start_time);
}
$timeLog[count($timeLog)-1][1] = strtotime('now');
$task->is_running = false; $task->is_running = false;
} else if ($data['action'] == 'save' && !$task->is_running) {
$task->start_time = $data['start_time'];
$task->duration = $data['duration'];
$task->break_duration = $data['break_duration'];
} }
$task->duration = max($task->duration, 0);
$task->break_duration = max($task->break_duration, 0);
$task->time_log = json_encode($timeLog); $task->time_log = json_encode($timeLog);
$task->save(); $task->save();
return $task; return $task;

View File

@ -37,13 +37,19 @@ class AppServiceProvider extends ServiceProvider {
$str = '<li class="dropdown '.$class.'"> $str = '<li class="dropdown '.$class.'">
<a href="'.URL::to($types).'" class="dropdown-toggle">'.trans("texts.$types").'</a> <a href="'.URL::to($types).'" class="dropdown-toggle">'.trans("texts.$types").'</a>
<ul class="dropdown-menu" id="menu1"> <ul class="dropdown-menu" id="menu1">';
<li><a href="'.URL::to($types.'/create').'">'.trans("texts.new_$type").'</a></li>';
if ($type == ENTITY_INVOICE && Auth::user()->isPro()) { if ($type != ENTITY_TASK || Auth::user()->account->timezone_id) {
$str .= '<li><a href="'.URL::to($types.'/create').'">'.trans("texts.new_$type").'</a></li>';
}
if ($type == ENTITY_INVOICE) {
$str .= '<li><a href="'.URL::to('recurring_invoices/create').'">'.trans("texts.new_recurring_invoice").'</a></li>';
if (Auth::user()->isPro()) {
$str .= '<li class="divider"></li> $str .= '<li class="divider"></li>
<li><a href="'.URL::to('quotes').'">'.trans("texts.quotes").'</a></li> <li><a href="'.URL::to('quotes').'">'.trans("texts.quotes").'</a></li>
<li><a href="'.URL::to('quotes/create').'">'.trans("texts.new_quote").'</a></li>'; <li><a href="'.URL::to('quotes/create').'">'.trans("texts.new_quote").'</a></li>';
}
} else if ($type == ENTITY_CLIENT) { } else if ($type == ENTITY_CLIENT) {
$str .= '<li class="divider"></li> $str .= '<li class="divider"></li>
<li><a href="'.URL::to('credits').'">'.trans("texts.credits").'</a></li> <li><a href="'.URL::to('credits').'">'.trans("texts.credits").'</a></li>
@ -66,7 +72,7 @@ class AppServiceProvider extends ServiceProvider {
// Get the breadcrumbs by exploding the current path. // Get the breadcrumbs by exploding the current path.
$basePath = Utils::basePath(); $basePath = Utils::basePath();
$parts = explode('?', $_SERVER['REQUEST_URI']); $parts = explode('?', isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '');
$path = $parts[0]; $path = $parts[0];
if ($basePath != '/') { if ($basePath != '/') {

View File

@ -20,7 +20,8 @@
"d3": "3.4.11", "d3": "3.4.11",
"handsontable": "*", "handsontable": "*",
"pdfmake": "*", "pdfmake": "*",
"moment": "*" "moment": "*",
"jsoneditor": "*"
}, },
"resolutions": { "resolutions": {
"jquery": "~1.11" "jquery": "~1.11"

240
c3.php Executable file
View File

@ -0,0 +1,240 @@
<?php
// @codingStandardsIgnoreFile
// @codeCoverageIgnoreStart
/**
* C3 - Codeception Code Coverage
*
* @author tiger
*/
// $_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE_DEBUG'] = 1;
if (isset($_COOKIE['CODECEPTION_CODECOVERAGE'])) {
$cookie = json_decode($_COOKIE['CODECEPTION_CODECOVERAGE'], true);
// fix for improperly encoded JSON in Code Coverage cookie with WebDriver.
// @see https://github.com/Codeception/Codeception/issues/874
if (!is_array($cookie)) {
$cookie = json_decode($cookie, true);
}
if ($cookie) {
foreach ($cookie as $key => $value) {
$_SERVER["HTTP_X_CODECEPTION_".strtoupper($key)] = $value;
}
}
}
if (!array_key_exists('HTTP_X_CODECEPTION_CODECOVERAGE', $_SERVER)) {
return;
}
if (!function_exists('__c3_error')) {
function __c3_error($message)
{
file_put_contents(C3_CODECOVERAGE_MEDIATE_STORAGE . DIRECTORY_SEPARATOR . 'error.txt', $message);
if (!headers_sent()) {
header('X-Codeception-CodeCoverage-Error: ' . str_replace("\n", ' ', $message), true, 500);
}
setcookie('CODECEPTION_CODECOVERAGE_ERROR', $message);
}
}
// Autoload Codeception classes
if (!class_exists('\\Codeception\\Codecept')) {
if (stream_resolve_include_path(__DIR__ . '/vendor/autoload.php')) {
require_once __DIR__ . '/vendor/autoload.php';
} elseif (file_exists(__DIR__ . '/codecept.phar')) {
require_once 'phar://'.__DIR__ . '/codecept.phar/autoload.php';
} elseif (stream_resolve_include_path('Codeception/autoload.php')) {
require_once 'Codeception/autoload.php';
} else {
__c3_error('Codeception is not loaded. Please check that either PHAR or Composer or PEAR package can be used');
}
}
// Load Codeception Config
$config_file = realpath(__DIR__) . DIRECTORY_SEPARATOR . 'codeception.yml';
if (isset($_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE_CONFIG'])) {
$config_file = realpath(__DIR__) . DIRECTORY_SEPARATOR . $_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE_CONFIG'];
}
if (!file_exists($config_file)) {
__c3_error(sprintf("Codeception config file '%s' not found", $config_file));
}
try {
\Codeception\Configuration::config($config_file);
} catch (\Exception $e) {
__c3_error($e->getMessage());
}
if (!defined('C3_CODECOVERAGE_MEDIATE_STORAGE')) {
// workaround for 'zend_mm_heap corrupted' problem
gc_disable();
if ((integer)ini_get('memory_limit') < 384) {
ini_set('memory_limit', '384M');
}
define('C3_CODECOVERAGE_MEDIATE_STORAGE', Codeception\Configuration::logDir() . 'c3tmp');
define('C3_CODECOVERAGE_PROJECT_ROOT', Codeception\Configuration::projectDir());
define('C3_CODECOVERAGE_TESTNAME', $_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE']);
function __c3_build_html_report(PHP_CodeCoverage $codeCoverage, $path)
{
$writer = new PHP_CodeCoverage_Report_HTML();
$writer->process($codeCoverage, $path . 'html');
if (file_exists($path . '.tar')) {
unlink($path . '.tar');
}
$phar = new PharData($path . '.tar');
$phar->setSignatureAlgorithm(Phar::SHA1);
$files = $phar->buildFromDirectory($path . 'html');
array_map('unlink', $files);
if (in_array('GZ', Phar::getSupportedCompression())) {
if (file_exists($path . '.tar.gz')) {
unlink($path . '.tar.gz');
}
$phar->compress(\Phar::GZ);
// close the file so that we can rename it
unset($phar);
unlink($path . '.tar');
rename($path . '.tar.gz', $path . '.tar');
}
return $path . '.tar';
}
function __c3_build_clover_report(PHP_CodeCoverage $codeCoverage, $path)
{
$writer = new PHP_CodeCoverage_Report_Clover();
$writer->process($codeCoverage, $path . '.clover.xml');
return $path . '.clover.xml';
}
function __c3_send_file($filename)
{
if (!headers_sent()) {
readfile($filename);
}
return __c3_exit();
}
/**
* @param $filename
* @return null|PHP_CodeCoverage
*/
function __c3_factory($filename)
{
$phpCoverage = is_readable($filename)
? unserialize(file_get_contents($filename))
: new PHP_CodeCoverage();
if (isset($_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE_SUITE'])) {
$suite = $_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE_SUITE'];
try {
$settings = \Codeception\Configuration::suiteSettings($suite, \Codeception\Configuration::config());
} catch (Exception $e) {
__c3_error($e->getMessage());
}
} else {
$settings = \Codeception\Configuration::config();
}
try {
\Codeception\Coverage\Filter::setup($phpCoverage)
->whiteList($settings)
->blackList($settings);
} catch (Exception $e) {
__c3_error($e->getMessage());
}
return $phpCoverage;
}
function __c3_exit()
{
if (!isset($_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE_DEBUG'])) {
exit;
}
return null;
}
function __c3_clear()
{
\Codeception\Util\FileSystem::doEmptyDir(C3_CODECOVERAGE_MEDIATE_STORAGE);
}
}
if (!is_dir(C3_CODECOVERAGE_MEDIATE_STORAGE)) {
if (mkdir(C3_CODECOVERAGE_MEDIATE_STORAGE, 0777, true) === false) {
__c3_error('Failed to create directory "' . C3_CODECOVERAGE_MEDIATE_STORAGE . '"');
}
}
// evaluate base path for c3-related files
$path = realpath(C3_CODECOVERAGE_MEDIATE_STORAGE) . DIRECTORY_SEPARATOR . 'codecoverage';
$requested_c3_report = (strpos($_SERVER['REQUEST_URI'], 'c3/report') !== false);
$complete_report = $current_report = $path . '.serialized';
if ($requested_c3_report) {
set_time_limit(0);
$route = ltrim(strrchr($_SERVER['REQUEST_URI'], '/'), '/');
if ($route == 'clear') {
__c3_clear();
return __c3_exit();
}
$codeCoverage = __c3_factory($complete_report);
switch ($route) {
case 'html':
try {
__c3_send_file(__c3_build_html_report($codeCoverage, $path));
} catch (Exception $e) {
__c3_error($e->getMessage());
}
return __c3_exit();
case 'clover':
try {
__c3_send_file(__c3_build_clover_report($codeCoverage, $path));
} catch (Exception $e) {
__c3_error($e->getMessage());
}
return __c3_exit();
case 'serialized':
try {
__c3_send_file($complete_report);
} catch (Exception $e) {
__c3_error($e->getMessage());
}
return __c3_exit();
}
} else {
$codeCoverage = __c3_factory($current_report);
$codeCoverage->start(C3_CODECOVERAGE_TESTNAME);
if (!array_key_exists('HTTP_X_CODECEPTION_CODECOVERAGE_DEBUG', $_SERVER)) {
register_shutdown_function(
function () use ($codeCoverage, $current_report) {
$codeCoverage->stop();
file_put_contents($current_report, serialize($codeCoverage));
}
);
}
}
// @codeCoverageIgnoreEnd

27
codeception.yml Normal file
View File

@ -0,0 +1,27 @@
actor: Tester
paths:
tests: tests
log: tests/_output
data: tests/_data
support: tests/_support
envs: tests/_envs
settings:
bootstrap: _bootstrap.php
colors: true
memory_limit: 1024M
extensions:
enabled:
- Codeception\Extension\RunFailed
- Codeception\Extension\Recorder
config:
Codeception\Extension\Recorder:
delete_successful: false
modules:
config:
Db:
dsn: 'mysql:dbname=ninja;host=localhost;'
user: 'ninja'
password: 'ninja'
dump: tests/_data/dump.sql
populate: true
cleanup: false

View File

@ -41,7 +41,10 @@
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "~4.0", "phpunit/phpunit": "~4.0",
"phpspec/phpspec": "~2.1" "phpspec/phpspec": "~2.1",
"codeception/codeception": "~2.0",
"codeception/c3": "~2.0",
"fzaninotto/faker": "^1.5"
}, },
"autoload": { "autoload": {
"classmap": [ "classmap": [

1081
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -119,6 +119,6 @@ return [
| |
*/ */
'pretend' => false, 'pretend' => env('MAIL_PRETEND'),
]; ];

View File

@ -19,7 +19,7 @@ class AddPartialAmountToInvoices extends Migration {
Schema::table('accounts', function($table) Schema::table('accounts', function($table)
{ {
$table->boolean('utf8_invoices')->default(false); $table->boolean('utf8_invoices')->default(true);
$table->boolean('auto_wrap')->default(false); $table->boolean('auto_wrap')->default(false);
$table->string('subdomain')->nullable(); $table->string('subdomain')->nullable();
}); });

View File

@ -0,0 +1,73 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class SimplifyTasks extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
$tasks = \App\Models\Task::all();
foreach ($tasks as $task) {
$startTime = strtotime($task->start_time);
if (!$task->time_log || !count(json_decode($task->time_log))) {
$task->time_log = json_encode([[$startTime, $startTime + $task->duration]]);
$task->save();
} elseif ($task->getDuration() != intval($task->duration)) {
$task->time_log = json_encode([[$startTime, $startTime + $task->duration]]);
$task->save();
}
}
Schema::table('tasks', function($table)
{
$table->dropColumn('start_time');
$table->dropColumn('duration');
$table->dropColumn('break_duration');
$table->dropColumn('resume_time');
});
Schema::table('users', function($table)
{
$table->boolean('dark_mode')->default(false)->nullable();
});
Schema::table('users', function($table)
{
$table->dropColumn('theme_id');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('tasks', function($table)
{
$table->timestamp('start_time')->nullable();
$table->integer('duration')->nullable();
$table->timestamp('resume_time')->nullable();
$table->integer('break_duration')->nullable();
});
Schema::table('users', function($table)
{
$table->dropColumn('dark_mode');
});
Schema::table('users', function($table)
{
$table->integer('theme_id')->nullable();
});
}
}

View File

@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddCustomDesign extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('accounts', function($table)
{
$table->text('custom_design')->nullable();
});
DB::table('invoice_designs')->insert(['id' => CUSTOM_DESIGN, 'name' => 'Custom']);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('accounts', function($table)
{
$table->dropColumn('custom_design');
});
}
}

View File

@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddPdfmakeSupport extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('invoice_designs', function($table)
{
$table->text('pdfmake')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('invoice_designs', function($table)
{
$table->dropColumn('pdfmake');
});
}
}

View File

@ -13,14 +13,14 @@ class DatabaseSeeder extends Seeder {
Eloquent::unguard(); Eloquent::unguard();
$this->call('UserTableSeeder');
$this->call('ConstantsSeeder'); $this->call('ConstantsSeeder');
$this->command->info('Seeded the constants');
$this->call('CountriesSeeder'); $this->call('CountriesSeeder');
$this->command->info('Seeded the countries!'); $this->command->info('Seeded the countries');
$this->call('PaymentLibrariesSeeder'); $this->call('PaymentLibrariesSeeder');
$this->command->info('Seeded the Payment Libraries!'); $this->command->info('Seeded the Payment Libraries');
} }
} }

View File

@ -5,6 +5,7 @@ use App\Models\PaymentTerm;
use App\Models\Currency; use App\Models\Currency;
use App\Models\DateFormat; use App\Models\DateFormat;
use App\Models\DatetimeFormat; use App\Models\DatetimeFormat;
use App\Models\InvoiceDesign;
class PaymentLibrariesSeeder extends Seeder class PaymentLibrariesSeeder extends Seeder
{ {
@ -16,6 +17,7 @@ class PaymentLibrariesSeeder extends Seeder
$this->createPaymentTerms(); $this->createPaymentTerms();
$this->createDateFormats(); $this->createDateFormats();
$this->createDatetimeFormats(); $this->createDatetimeFormats();
$this->createInvoiceDesigns();
} }
private function createGateways() { private function createGateways() {
@ -62,7 +64,7 @@ class PaymentLibrariesSeeder extends Seeder
['name' => 'US Dollar', 'code' => 'USD', 'symbol' => '$', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'], ['name' => 'US Dollar', 'code' => 'USD', 'symbol' => '$', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['name' => 'Pound Sterling', 'code' => 'GBP', 'symbol' => '£', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'], ['name' => 'Pound Sterling', 'code' => 'GBP', 'symbol' => '£', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['name' => 'Euro', 'code' => 'EUR', 'symbol' => '€', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'], ['name' => 'Euro', 'code' => 'EUR', 'symbol' => '€', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['name' => 'Rand', 'code' => 'ZAR', 'symbol' => 'R', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'], ['name' => 'South African Rand', 'code' => 'ZAR', 'symbol' => 'R', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['name' => 'Danish Krone', 'code' => 'DKK', 'symbol' => 'kr ', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'], ['name' => 'Danish Krone', 'code' => 'DKK', 'symbol' => 'kr ', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['name' => 'Israeli Shekel', 'code' => 'ILS', 'symbol' => 'NIS ', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'], ['name' => 'Israeli Shekel', 'code' => 'ILS', 'symbol' => 'NIS ', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['name' => 'Swedish Krona', 'code' => 'SEK', 'symbol' => 'kr ', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'], ['name' => 'Swedish Krona', 'code' => 'SEK', 'symbol' => 'kr ', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
@ -80,10 +82,16 @@ class PaymentLibrariesSeeder extends Seeder
['name' => 'Malaysian Ringgit', 'code' => 'MYR', 'symbol' => 'RM', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'], ['name' => 'Malaysian Ringgit', 'code' => 'MYR', 'symbol' => 'RM', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['name' => 'Brazilian Real', 'code' => 'BRL', 'symbol' => 'R$', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'], ['name' => 'Brazilian Real', 'code' => 'BRL', 'symbol' => 'R$', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['name' => 'Thai baht', 'code' => 'THB', 'symbol' => 'THB ', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'], ['name' => 'Thai baht', 'code' => 'THB', 'symbol' => 'THB ', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['name' => 'Nigerian Naira', 'code' => 'NGN', 'symbol' => 'NGN ', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['name' => 'Argentine Peso', 'code' => 'ARS', 'symbol' => '$', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
]; ];
foreach ($currencies as $currency) { foreach ($currencies as $currency) {
if (!DB::table('currencies')->whereName($currency['name'])->get()) { $record = Currency::whereCode($currency['code'])->first();
if ($record) {
$record->name = $currency['name'];
$record->save();
} else {
Currency::create($currency); Currency::create($currency);
} }
} }
@ -131,4 +139,37 @@ class PaymentLibrariesSeeder extends Seeder
} }
} }
private function createInvoiceDesigns() {
$designs = [
'Clean',
'Bold',
'Modern',
'Plain',
'Business',
'Creative',
'Elegant',
'Hipster',
'Playful',
'Photo',
];
for ($i=0; $i<count($designs); $i++) {
$design = $designs[$i];
$fileName = storage_path() . '/templates/' . strtolower($design) . '.js';
if (file_exists($fileName)) {
$pdfmake = file_get_contents($fileName);
if ($pdfmake) {
$record = InvoiceDesign::whereName($design)->first();
if (!$record) {
$record = new InvoiceDesign;
$record->id = $i + 1;
$record->name = $design;
}
$record->pdfmake = $pdfmake;
$record->save();
}
}
}
}
} }

View File

@ -1,11 +1,31 @@
<?php <?php
use App\Models\User;
use App\Models\Account;
class UserTableSeeder extends Seeder class UserTableSeeder extends Seeder
{ {
public function run() public function run()
{ {
$this->command->info('Running UserTableSeeder');
Eloquent::unguard();
$account = Account::create([
'name' => 'Test Account',
'account_key' => str_random(16),
'timezone_id' => 1,
]);
User::create([
'email' => TEST_USERNAME,
'username' => TEST_USERNAME,
'account_id' => $account->id,
'password' => Hash::make(TEST_PASSWORD),
'registered' => true,
'confirmed' => true,
]);
} }
} }

View File

@ -13,4 +13,7 @@
RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L] RewriteRule ^ index.php [L]
# Enable this to enforce https access
# RewriteCond %{SERVER_PORT} 80
# RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R,L]
</IfModule> </IfModule>

View File

@ -2433,6 +2433,8 @@ th {border-left: 1px solid #d26b26; }
.table>thead>tr>th, .table>tbody>tr>th, .table>tfoot>tr>th, .table>thead>tr>td, .table>tbody>tr>td, .table>tfoot>tr>td { .table>thead>tr>th, .table>tbody>tr>th, .table>tfoot>tr>th, .table>thead>tr>td, .table>tbody>tr>td, .table>tfoot>tr>td {
vertical-align: middle; vertical-align: middle;
border-top: none; border-top: none;
}
table.invoice-table>thead>tr>th, table.invoice-table>tbody>tr>th, table.invoice-table>tfoot>tr>th, table.invoice-table>thead>tr>td, table.invoice-table>tbody>tr>td, table.invoice-table>tfoot>tr>td {
border-bottom: 1px solid #dfe0e1; border-bottom: 1px solid #dfe0e1;
} }
table.dataTable.no-footer { table.dataTable.no-footer {
@ -2837,15 +2839,12 @@ background-clip: padding-box;
.dashboard .panel-body {padding: 0;} .dashboard .panel-body {padding: 0;}
.dashboard .table-striped>tbody>tr>td, .table-striped>tbody>tr>th { background-color: #fbfbfb;}
.dashboard .table-striped>tbody>tr:nth-child(odd)>tr, .table-striped>tbody>tr:nth-child(odd)>th {
background-color: #fff;
}
.dashboard th { .dashboard th {
border-left: none; border-left: none;
background-color: #fbfbfb; background-color: #fbfbfb;
border-bottom: 1px solid #dfe0e1; border-bottom: 1px solid #dfe0e1;
} }
.dashboard table.table thead > tr > th { .dashboard table.table thead > tr > th {
border-bottom-width: 1px; border-bottom-width: 1px;
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

1
public/css/jsoneditor.min.css vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -83,6 +83,8 @@ th {border-left: 1px solid #d26b26; }
.table>thead>tr>th, .table>tbody>tr>th, .table>tfoot>tr>th, .table>thead>tr>td, .table>tbody>tr>td, .table>tfoot>tr>td { .table>thead>tr>th, .table>tbody>tr>th, .table>tfoot>tr>th, .table>thead>tr>td, .table>tbody>tr>td, .table>tfoot>tr>td {
vertical-align: middle; vertical-align: middle;
border-top: none; border-top: none;
}
table.invoice-table>thead>tr>th, table.invoice-table>tbody>tr>th, table.invoice-table>tfoot>tr>th, table.invoice-table>thead>tr>td, table.invoice-table>tbody>tr>td, table.invoice-table>tfoot>tr>td {
border-bottom: 1px solid #dfe0e1; border-bottom: 1px solid #dfe0e1;
} }
table.dataTable.no-footer { table.dataTable.no-footer {
@ -487,15 +489,12 @@ background-clip: padding-box;
.dashboard .panel-body {padding: 0;} .dashboard .panel-body {padding: 0;}
.dashboard .table-striped>tbody>tr>td, .table-striped>tbody>tr>th { background-color: #fbfbfb;}
.dashboard .table-striped>tbody>tr:nth-child(odd)>tr, .table-striped>tbody>tr:nth-child(odd)>th {
background-color: #fff;
}
.dashboard th { .dashboard th {
border-left: none; border-left: none;
background-color: #fbfbfb; background-color: #fbfbfb;
border-bottom: 1px solid #dfe0e1; border-bottom: 1px solid #dfe0e1;
} }
.dashboard table.table thead > tr > th { .dashboard table.table thead > tr > th {
border-bottom-width: 1px; border-bottom-width: 1px;
} }

File diff suppressed because one or more lines are too long

44
public/js/jsoneditor.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,72 +1,65 @@
var NINJA = NINJA || {}; var NINJA = NINJA || {};
NINJA.TEMPLATES = {
CLEAN: "1",
BOLD:"2",
MODERN: "3",
NORMAL:"4",
BUSINESS:"5",
CREATIVE:"6",
ELEGANT:"7",
HIPSTER:"8",
PLAYFUL:"9",
PHOTO:"10"
};
function GetPdfMake(invoice, javascript, callback) { function GetPdfMake(invoice, javascript, callback) {
var account = invoice.account;
var baseDD = {
pageMargins: [40, 40, 40, 40],
styles: {
bold: {
bold: true
},
cost: {
alignment: 'right'
},
quantity: {
alignment: 'right'
},
tax: {
alignment: 'right'
},
lineTotal: {
alignment: 'right'
},
right: {
alignment: 'right'
},
subtotals: {
alignment: 'right'
},
termsLabel: {
bold: true,
margin: [0, 10, 0, 4]
}
},
footer: function(){
f = [{ text:invoice.invoice_footer?processVariables(invoice.invoice_footer):"", margin: [40, 0]}]
if (!invoice.is_pro && logoImages.imageLogo1) {
f.push({
image: logoImages.imageLogo1,
width: 150,
margin: [40,0]
});
}
return f;
},
javascript = NINJA.decodeJavascript(invoice, javascript);
function jsonCallBack(key, val) {
if ((val+'').indexOf('$firstAndLast') === 0) {
var parts = val.split(':');
return function (i, node) {
return (i === 0 || i === node.table.body.length) ? parseFloat(parts[1]) : 0;
}; };
} else if ((val+'').indexOf('$none') === 0) {
return function (i, node) {
return 0;
};
} else if ((val+'').indexOf('$notFirst') === 0) {
var parts = val.split(':');
return function (i, node) {
return i === 0 ? 0 : parseFloat(parts[1]);
};
} else if ((val+'').indexOf('$amount') === 0) {
var parts = val.split(':');
return function (i, node) {
return parseFloat(parts[1]);
};
} else if ((val+'').indexOf('$primaryColor') === 0) {
var parts = val.split(':');
return NINJA.primaryColor || parts[1];
} else if ((val+'').indexOf('$secondaryColor') === 0) {
var parts = val.split(':');
return NINJA.secondaryColor || parts[1];
}
eval(javascript); return val;
dd = $.extend(true, baseDD, dd); }
//console.log(javascript);
var dd = JSON.parse(javascript, jsonCallBack);
if (!invoice.is_pro && dd.hasOwnProperty('footer') && dd.footer.hasOwnProperty('columns')) {
dd.footer.columns.push({image: logoImages.imageLogo1, alignment: 'right', width: 130})
}
//console.log(JSON.stringify(dd));
/* /*
pdfMake.fonts = { var fonts = {
wqy: {
normal: 'wqy.ttf',
bold: 'wqy.ttf',
italics: 'wqy.ttf',
bolditalics: 'wqy.ttf'
}
};
*/
/*
pdfMake.fonts = {
NotoSansCJKsc: {
normal: 'NotoSansCJKsc-Regular.ttf',
bold: 'NotoSansCJKsc-Medium.ttf',
italics: 'NotoSansCJKsc-Italic.ttf',
bolditalics: 'NotoSansCJKsc-Italic.ttf'
},
Roboto: { Roboto: {
normal: 'Roboto-Regular.ttf', normal: 'Roboto-Regular.ttf',
bold: 'Roboto-Medium.ttf', bold: 'Roboto-Medium.ttf',
@ -83,56 +76,152 @@ function GetPdfMake(invoice, javascript, callback) {
return doc; return doc;
} }
NINJA.decodeJavascript = function(invoice, javascript)
{
var account = invoice.account;
var blankImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVQYV2NgYAAAAAMAAWgmWQ0AAAAASUVORK5CYII=';
// search/replace variables
var json = {
'accountName': account.name || ' ',
'accountLogo': window.accountLogo || blankImage,
'accountDetails': NINJA.accountDetails(invoice),
'accountAddress': NINJA.accountAddress(invoice),
'invoiceDetails': NINJA.invoiceDetails(invoice),
'invoiceDetailsHeight': NINJA.invoiceDetails(invoice).length * 22,
'invoiceLineItems': NINJA.invoiceLines(invoice),
'invoiceLineItemColumns': NINJA.invoiceColumns(invoice),
'clientDetails': NINJA.clientDetails(invoice),
'notesAndTerms': NINJA.notesAndTerms(invoice),
'subtotals': NINJA.subtotals(invoice),
'subtotalsHeight': NINJA.subtotals(invoice).length * 22,
'subtotalsWithoutBalance': NINJA.subtotals(invoice, true),
'balanceDue': formatMoney(invoice.balance_amount, invoice.client.currency_id),
'invoiceFooter': invoice.invoice_footer || ' ',
'invoiceNumber': invoice.invoice_number || ' ',
'entityType': invoice.is_quote ? invoiceLabels.quote : invoiceLabels.invoice,
'entityTypeUC': (invoice.is_quote ? invoiceLabels.quote : invoiceLabels.invoice).toUpperCase(),
'fontSize': NINJA.fontSize,
'fontSizeLarger': NINJA.fontSize + 1,
'fontSizeLargest': NINJA.fontSize + 2,
}
for (var key in json) {
var regExp = new RegExp('"\\$'+key+'"', 'g');
var val = JSON.stringify(json[key]);
val = doubleDollarSign(val);
javascript = javascript.replace(regExp, val);
}
// search/replace labels
var regExp = new RegExp('"\\$\\\w*?Label(UC)?(:)?(\\\?)?"', 'g');
var matches = javascript.match(regExp);
if (matches) {
for (var i=0; i<matches.length; i++) {
var match = matches[i];
field = match.substring(2, match.indexOf('Label'));
field = toSnakeCase(field);
var value = getDescendantProp(invoice, field);
if (match.indexOf('?') < 0 || value) {
if (invoice.partial && field == 'balance_due') {
field = 'amount_due';
}
var label = invoiceLabels[field];
if (match.indexOf('UC') >= 0) {
if (!label) console.log('match: ' + field);
label = label.toUpperCase();
}
if (match.indexOf(':') >= 0) {
label = label + ':';
}
} else {
label = ' ';
}
javascript = javascript.replace(match, '"'+label+'"');
}
}
// search/replace values
var regExp = new RegExp('"\\$[\\\w\\\.]*?Value"', 'g');
var matches = javascript.match(regExp);
if (matches) {
for (var i=0; i<matches.length; i++) {
var match = matches[i];
field = match.substring(2, match.indexOf('Value'));
field = toSnakeCase(field);
var value = getDescendantProp(invoice, field) || ' ';
value = doubleDollarSign(value);
if (field.toLowerCase().indexOf('date') >= 0 && value != ' ') {
value = moment(value, 'YYYY-MM-DD').format('MMM D YYYY');
}
javascript = javascript.replace(match, '"'+value+'"');
}
}
return javascript;
}
NINJA.notesAndTerms = function(invoice) NINJA.notesAndTerms = function(invoice)
{ {
var text = []; var data = [];
if (invoice.public_notes) { if (invoice.public_notes) {
text.push({text:processVariables(invoice.public_notes), style:'notes'}); data.push({text:invoice.public_notes, style: ['notes']});
data.push({text:' '});
} }
if (invoice.terms) { if (invoice.terms) {
text.push({text:invoiceLabels.terms, style:'termsLabel'}); data.push({text:invoiceLabels.terms, style: ['termsLabel']});
text.push({text:processVariables(invoice.terms), style:'terms'}); data.push({text:invoice.terms, style: ['terms']});
} }
return text; return NINJA.prepareDataList(data, 'notesAndTerms');
}
NINJA.invoiceColumns = function(invoice)
{
if (invoice.account.hide_quantity == '1') {
return ["15%", "*", "10%", "15%"];
} else {
return ["15%", "*", "10%", "auto", "15%"];
}
} }
NINJA.invoiceLines = function(invoice) { NINJA.invoiceLines = function(invoice) {
var grid = [
[
{text: invoiceLabels.item, style: 'tableHeader'},
{text: invoiceLabels.description, style: 'tableHeader'},
{text: invoiceLabels.unit_cost, style: 'tableHeader'},
{text: invoiceLabels.quantity, style: 'tableHeader'},
{text: invoice.has_taxes?invoiceLabels.tax:'', style: 'tableHeader'},
{text: invoiceLabels.line_total, style: 'tableHeader'}
]
];
var total = 0; var total = 0;
var shownItem = false; var shownItem = false;
var currencyId = invoice && invoice.client ? invoice.client.currency_id : 1; var currencyId = invoice && invoice.client ? invoice.client.currency_id : 1;
var hideQuantity = invoice.account.hide_quantity == '1'; var hideQuantity = invoice.account.hide_quantity == '1';
var grid = [[
{text: invoiceLabels.item, style: ['tableHeader', 'itemTableHeader']},
{text: invoiceLabels.description, style: ['tableHeader', 'descriptionTableHeader']},
{text: invoiceLabels.unit_cost, style: ['tableHeader', 'costTableHeader']}
]];
if (!hideQuantity) {
grid[0].push({text: invoiceLabels.quantity, style: ['tableHeader', 'qtyTableHeader']});
}
grid[0].push({text: invoiceLabels.line_total, style: ['tableHeader', 'lineTotalTableHeader']});
for (var i = 0; i < invoice.invoice_items.length; i++) { for (var i = 0; i < invoice.invoice_items.length; i++) {
var row = []; var row = [];
var item = invoice.invoice_items[i]; var item = invoice.invoice_items[i];
var cost = formatMoney(item.cost, currencyId, true); var cost = formatMoney(item.cost, currencyId, true);
var qty = NINJA.parseFloat(item.qty) ? roundToTwo(NINJA.parseFloat(item.qty)) + '' : ''; var qty = NINJA.parseFloat(item.qty) ? roundToTwo(NINJA.parseFloat(item.qty)) + '' : '';
var notes = item.notes; var notes = item.notes;
var productKey = item.product_key; var productKey = item.product_key;
var tax = "";
if (item.tax && parseFloat(item.tax.rate)) {
tax = parseFloat(item.tax.rate);
} else if (item.tax_rate && parseFloat(item.tax_rate)) {
tax = parseFloat(item.tax_rate);
}
// show at most one blank line // show at most one blank line
if (shownItem && (!cost || cost == '0.00') && !notes && !productKey) { if (shownItem && (!cost || cost == '0.00') && !notes && !productKey) {
continue; continue;
} }
shownItem = true; shownItem = true;
// process date variables // process date variables
@ -142,147 +231,178 @@ NINJA.invoiceLines = function(invoice) {
} }
var lineTotal = roundToTwo(NINJA.parseFloat(item.cost)) * roundToTwo(NINJA.parseFloat(item.qty)); var lineTotal = roundToTwo(NINJA.parseFloat(item.cost)) * roundToTwo(NINJA.parseFloat(item.qty));
if (tax) {
lineTotal += lineTotal * tax / 100;
}
if (lineTotal) {
total += lineTotal;
}
lineTotal = formatMoney(lineTotal, currencyId); lineTotal = formatMoney(lineTotal, currencyId);
rowStyle = i%2===0?'odd':'even'; rowStyle = (i % 2 == 0) ? 'odd' : 'even';
row[0] = {style:["productKey", rowStyle], text:productKey}; row.push({style:["productKey", rowStyle], text:productKey || ' '}); // product key can be blank when selecting from a datalist
row[1] = {style:["notes", rowStyle], text:notes}; row.push({style:["notes", rowStyle], text:notes || ' '});
row[2] = {style:["cost", rowStyle], text:cost}; row.push({style:["cost", rowStyle], text:cost});
row[3] = {style:["quantity", rowStyle], text:qty}; if (!hideQuantity) {
row[4] = {style:["tax", rowStyle], text:""+tax}; row.push({style:["quantity", rowStyle], text:qty || ' '});
row[5] = {style:["lineTotal", rowStyle], text:lineTotal}; }
row.push({style:["lineTotal", rowStyle], text:lineTotal || ' '});
grid.push(row); grid.push(row);
} }
return grid; return NINJA.prepareDataTable(grid, 'invoiceItems');
} }
NINJA.subtotals = function(invoice) NINJA.subtotals = function(invoice, hideBalance)
{ {
if (!invoice) { if (!invoice) {
return; return;
} }
var data = [ var account = invoice.account;
[invoiceLabels.subtotal, formatMoney(invoice.subtotal_amount, invoice.client.currency_id)], var data = [];
]; data.push([{text: invoiceLabels.subtotal}, {text: formatMoney(invoice.subtotal_amount, invoice.client.currency_id)}]);
if (invoice.discount_amount != 0) { if (invoice.discount_amount != 0) {
data.push([invoiceLabels.discount, formatMoney(invoice.discount_amount, invoice.client.currency_id)]); data.push([{text: invoiceLabels.discount}, {text: formatMoney(invoice.discount_amount, invoice.client.currency_id)}]);
} }
if (NINJA.parseFloat(invoice.custom_value1) && invoice.custom_taxes1 == '1') { if (NINJA.parseFloat(invoice.custom_value1) && invoice.custom_taxes1 == '1') {
data.push([invoiceLabels.custom_invoice_label1, formatMoney(invoice.custom_value1, invoice.client.currency_id)]); data.push([{text: account.custom_invoice_label1}, {text: formatMoney(invoice.custom_value1, invoice.client.currency_id)}]);
} }
if (NINJA.parseFloat(invoice.custom_value2) && invoice.custom_taxes2 == '1') { if (NINJA.parseFloat(invoice.custom_value2) && invoice.custom_taxes2 == '1') {
data.push([invoiceLabels.custom_invoice_label2, formatMoney(invoice.custom_value2, invoice.client.currency_id)]); data.push([{text: account.custom_invoice_label2}, {text: formatMoney(invoice.custom_value2, invoice.client.currency_id)}]);
}
for (var key in invoice.item_taxes) {
if (invoice.item_taxes.hasOwnProperty(key)) {
var taxRate = invoice.item_taxes[key];
var taxStr = taxRate.name + ' ' + (taxRate.rate*1).toString() + '%';
data.push([{text: taxStr}, {text: formatMoney(taxRate.amount, invoice.client.currency_id)}]);
}
} }
if (invoice.tax && invoice.tax.name || invoice.tax_name) { if (invoice.tax && invoice.tax.name || invoice.tax_name) {
data.push([invoiceLabels.tax, formatMoney(invoice.tax_amount, invoice.client.currency_id)]); var taxStr = invoice.tax_name + ' ' + (invoice.tax_rate*1).toString() + '%';
data.push([{text: taxStr}, {text: formatMoney(invoice.tax_amount, invoice.client.currency_id)}]);
} }
if (NINJA.parseFloat(invoice.custom_value1) && invoice.custom_taxes1 != '1') { if (NINJA.parseFloat(invoice.custom_value1) && invoice.custom_taxes1 != '1') {
data.push([invoiceLabels.custom_invoice_label1, formatMoney(invoice.custom_value1, invoice.client.currency_id)]); data.push([{text: account.custom_invoice_label1}, {text: formatMoney(invoice.custom_value1, invoice.client.currency_id)}]);
} }
if (NINJA.parseFloat(invoice.custom_value2) && invoice.custom_taxes2 != '1') { if (NINJA.parseFloat(invoice.custom_value2) && invoice.custom_taxes2 != '1') {
data.push([invoiceLabels.custom_invoice_label2, formatMoney(invoice.custom_value2, invoice.client.currency_id)]); data.push([{text: account.custom_invoice_label2}, {text: formatMoney(invoice.custom_value2, invoice.client.currency_id)}]);
} }
var paid = invoice.amount - invoice.balance; var paid = invoice.amount - invoice.balance;
if (invoice.account.hide_paid_to_date != '1' || paid) { if (invoice.account.hide_paid_to_date != '1' || paid) {
data.push([invoiceLabels.paid_to_date, formatMoney(paid, invoice.client.currency_id)]); data.push([{text:invoiceLabels.paid_to_date}, {text:formatMoney(paid, invoice.client.currency_id)}]);
} }
data.push([{text:invoice.is_quote ? invoiceLabels.total : invoiceLabels.balance_due, style:'balanceDueLabel'}, if (!hideBalance) {
{text:formatMoney(invoice.balance_amount, invoice.client.currency_id), style:'balanceDueValue'}]); var isPartial = NINJA.parseFloat(invoice.partial);
return data; data.push([
{text: isPartial ? invoiceLabels.amount_due : invoiceLabels.balance_due, style:['balanceDueLabel']},
{text: formatMoney(invoice.balance_amount, invoice.client.currency_id), style:['balanceDue']}
]);
} }
NINJA.accountDetails = function(account) { return NINJA.prepareDataPairs(data, 'subtotals');
var data = [];
if(account.name) data.push({text:account.name, style:'accountName'});
if(account.id_number) data.push({text:account.id_number, style:'accountDetails'});
if(account.vat_number) data.push({text:account.vat_number, style:'accountDetails'});
if(account.work_email) data.push({text:account.work_email, style:'accountDetails'});
if(account.work_phone) data.push({text:account.work_phone, style:'accountDetails'});
return data;
} }
NINJA.accountAddress = function(account) { NINJA.accountDetails = function(invoice) {
var address = ''; var account = invoice.account;
var data = [
{text:account.name, style: ['accountName']},
{text:account.id_number},
{text:account.vat_number},
{text:account.work_email},
{text:account.work_phone}
];
return NINJA.prepareDataList(data, 'accountDetails');
}
NINJA.accountAddress = function(invoice) {
var account = invoice.account;
var cityStatePostal = '';
if (account.city || account.state || account.postal_code) { if (account.city || account.state || account.postal_code) {
address = ((account.city ? account.city + ', ' : '') + account.state + ' ' + account.postal_code).trim(); cityStatePostal = ((account.city ? account.city + ', ' : '') + account.state + ' ' + account.postal_code).trim();
} }
var data = [];
if(account.address1) data.push({text:account.address1, style:'accountDetails'}); var data = [
if(account.address2) data.push({text:account.address2, style:'accountDetails'}); {text: account.address1},
if(address) data.push({text:address, style:'accountDetails'}); {text: account.address2},
if(account.country) data.push({text:account.country.name, style: 'accountDetails'}); {text: cityStatePostal},
if(account.custom_label1 && account.custom_value1) data.push({text:account.custom_label1 +' '+ account.custom_value1, style: 'accountDetails'}); {text: account.country ? account.country.name : ''},
if(account.custom_label2 && account.custom_value2) data.push({text:account.custom_label2 +' '+ account.custom_value2, style: 'accountDetails'}); {text: invoice.account.custom_value1 ? invoice.account.custom_label1 + ' ' + invoice.account.custom_value1 : false},
return data; {text: invoice.account.custom_value2 ? invoice.account.custom_label2 + ' ' + invoice.account.custom_value2 : false}
];
return NINJA.prepareDataList(data, 'accountAddress');
} }
NINJA.invoiceDetails = function(invoice) { NINJA.invoiceDetails = function(invoice) {
var data = [ var data = [
[ [
invoice.is_quote ? invoiceLabels.quote_number : invoiceLabels.invoice_number, {text: (invoice.is_quote ? invoiceLabels.quote_number : invoiceLabels.invoice_number), style: ['invoiceNumberLabel']},
{style: 'bold', text: invoice.invoice_number}, {text: invoice.invoice_number, style: ['invoiceNumber']}
], ],
[ [
invoice.is_quote ? invoiceLabels.quote_date : invoiceLabels.invoice_date, {text: invoiceLabels.po_number},
invoice.invoice_date, {text: invoice.po_number}
], ],
[ [
invoice.is_quote ? invoiceLabels.total : invoiceLabels.balance_due, {text: (invoice.is_quote ? invoiceLabels.quote_date : invoiceLabels.invoice_date)},
formatMoney(invoice.balance_amount, invoice.client.currency_id), {text: invoice.invoice_date}
], ],
[
{text: invoiceLabels.due_date},
{text: invoice.due_date}
]
]; ];
return data;
var isPartial = NINJA.parseFloat(invoice.partial);
if (NINJA.parseFloat(invoice.balance) < NINJA.parseFloat(invoice.amount)) {
data.push([
{text: invoiceLabels.total},
{text: formatMoney(invoice.amount, invoice.client.currency_id)}
]);
} else if (isPartial) {
data.push([
{text: invoiceLabels.total},
{text: formatMoney(invoice.total_amount, invoice.client.currency_id)}
]);
}
data.push([
{text: isPartial ? invoiceLabels.amount_due : invoiceLabels.balance_due, style: ['invoiceDetailBalanceDueLabel']},
{text: formatMoney(invoice.balance_amount, invoice.client.currency_id), style: ['invoiceDetailBalanceDue']}
])
return NINJA.prepareDataPairs(data, 'invoiceDetails');
} }
NINJA.clientDetails = function(invoice) { NINJA.clientDetails = function(invoice) {
var client = invoice.client; var client = invoice.client;
var data;
if (!client) { if (!client) {
return; return;
} }
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;
var fields = [ data = [
getClientDisplayName(client), {text:clientName || ' ', style: ['clientName']},
client.id_number, {text:client.address1},
client.vat_number, {text:concatStrings(client.city, client.state, client.postal_code)},
concatStrings(client.address1, client.address2), {text:client.country ? client.country.name : ''},
concatStrings(client.city, client.state, client.postal_code), {text:clientEmail},
client.country ? client.country.name : false, {text: invoice.client.custom_value1 ? invoice.account.custom_client_label1 + ' ' + invoice.client.custom_value1 : false},
invoice.contact && getClientDisplayName(client) != invoice.contact.email ? invoice.contact.email : false, {text: invoice.client.custom_value2 ? invoice.account.custom_client_label2 + ' ' + invoice.client.custom_value2 : false}
invoice.client.custom_value1 ? invoice.account['custom_client_label1'] + ' ' + invoice.client.custom_value1 : false,
invoice.client.custom_value2 ? invoice.account['custom_client_label2'] + ' ' + invoice.client.custom_value2 : false,
]; ];
var data = []; return NINJA.prepareDataList(data, 'clientDetails');
for (var i=0; i<fields.length; i++) {
var field = fields[i];
if (!field) {
continue;
} }
data.push([field]);
}
if (!data.length) {
data.push(['']);
}
return data;
}
NINJA.getPrimaryColor = function(defaultColor) { NINJA.getPrimaryColor = function(defaultColor) {
return NINJA.primaryColor ? NINJA.primaryColor : defaultColor; return NINJA.primaryColor ? NINJA.primaryColor : defaultColor;
@ -292,6 +412,62 @@ NINJA.getSecondaryColor = function(defaultColor) {
return NINJA.primaryColor ? NINJA.secondaryColor : defaultColor; return NINJA.primaryColor ? NINJA.secondaryColor : defaultColor;
} }
NINJA.getEntityLabel = function(invoice) { // remove blanks and add section style to all elements
return invoice.is_quote ? invoiceLabels.quote : invoiceLabels.invoice; NINJA.prepareDataList = function(oldData, section) {
var newData = [];
for (var i=0; i<oldData.length; i++) {
var item = NINJA.processItem(oldData[i], section);
if (item.text) {
newData.push(item);
}
}
return newData;
}
NINJA.prepareDataTable = function(oldData, section) {
var newData = [];
for (var i=0; i<oldData.length; i++) {
var row = oldData[i];
var newRow = [];
for (var j=0; j<row.length; j++) {
var item = NINJA.processItem(row[j], section);
if (item.text) {
newRow.push(item);
}
}
if (newRow.length) {
newData.push(newRow);
}
}
return newData;
}
NINJA.prepareDataPairs = function(oldData, section) {
var newData = [];
for (var i=0; i<oldData.length; i++) {
var row = oldData[i];
var isBlank = false;
for (var j=0; j<row.length; j++) {
var item = NINJA.processItem(row[j], section);
if (!item.text) {
isBlank = true;
}
if (j == 1) {
NINJA.processItem(row[j], section + "Value");
}
}
if (!isBlank) {
newData.push(oldData[i]);
}
}
return newData;
}
NINJA.processItem = function(item, section) {
if (item.style && item.style instanceof Array) {
item.style.push(section);
} else {
item.style = [section];
}
return item;
} }

View File

@ -8,33 +8,51 @@ var isIE = /*@cc_on!@*/false || !!document.documentMode; // At least IE6
var invoiceOld; var invoiceOld;
var refreshTimer;
function generatePDF(invoice, javascript, force, cb) { function generatePDF(invoice, javascript, force, cb) {
if (!invoice || !javascript) {
return;
}
//console.log('== generatePDF - force: %s', force);
if (force || !invoiceOld) {
refreshTimer = null;
} else {
if (refreshTimer) {
clearTimeout(refreshTimer);
}
refreshTimer = setTimeout(function() {
generatePDF(invoice, javascript, true, cb);
}, 500);
return;
}
invoice = calculateAmounts(invoice); invoice = calculateAmounts(invoice);
var a = copyInvoice(invoice); var a = copyObject(invoice);
var b = copyInvoice(invoiceOld); var b = copyObject(invoiceOld);
if (!force && _.isEqual(a, b)) { if (!force && _.isEqual(a, b)) {
return; return;
} }
pdfmakeMarker = "//pdfmake";
invoiceOld = invoice; invoiceOld = invoice;
report_id = invoice.invoice_design_id; pdfmakeMarker = "{";
if(javascript.slice(0, pdfmakeMarker.length) === pdfmakeMarker) { if(javascript.slice(0, pdfmakeMarker.length) === pdfmakeMarker) {
doc = GetPdfMake(invoice, javascript, cb); doc = GetPdfMake(invoice, javascript, cb);
//doc.getDataUrl(cb);
} else { } else {
doc = GetPdf(invoice, javascript); doc = GetPdf(invoice, javascript);
doc.getDataUrl = function(cb) { doc.getDataUrl = function(cb) {
cb( this.output("datauristring")); cb( this.output("datauristring"));
}; };
} }
if (cb) {
doc.getDataUrl(cb);
}
return doc; return doc;
} }
function copyInvoice(orig) { function copyObject(orig) {
if (!orig) return false; if (!orig) return false;
var copy = JSON.stringify(orig); return JSON.parse(JSON.stringify(orig));
var copy = JSON.parse(copy);
return copy;
} }
@ -58,12 +76,14 @@ function GetPdf(invoice, javascript){
lineTotalRight: 550 lineTotalRight: 550
}; };
/*
if (invoice.has_taxes) if (invoice.has_taxes)
{ {
layout.descriptionLeft -= 20; layout.descriptionLeft -= 20;
layout.unitCostRight -= 40; layout.unitCostRight -= 40;
layout.qtyRight -= 40; layout.qtyRight -= 40;
} }
*/
/* /*
@param orientation One of "portrait" or "landscape" (or shortcuts "p" (Default), "l") @param orientation One of "portrait" or "landscape" (or shortcuts "p" (Default), "l")
@ -859,9 +879,6 @@ function displayGrid(doc, invoice, data, x, y, layout, options) {
key = invoice.account[key]; key = invoice.account[key];
} else if (key === 'tax' && invoice.tax_name) { } else if (key === 'tax' && invoice.tax_name) {
key = invoice.tax_name + ' ' + (invoice.tax_rate*1).toString() + '%'; key = invoice.tax_name + ' ' + (invoice.tax_rate*1).toString() + '%';
if (invoice.tax_name.toLowerCase().indexOf(invoiceLabels['tax'].toLowerCase()) == -1) {
key = invoiceLabels['tax'] + ': ' + key;
}
} else if (key === 'discount' && NINJA.parseFloat(invoice.discount) && !parseInt(invoice.is_amount_discount)) { } else if (key === 'discount' && NINJA.parseFloat(invoice.discount) && !parseInt(invoice.is_amount_discount)) {
key = invoiceLabels[key] + ' ' + parseFloat(invoice.discount) + '%'; key = invoiceLabels[key] + ' ' + parseFloat(invoice.discount) + '%';
} else { } else {
@ -913,22 +930,49 @@ function displayNotesAndTerms(doc, layout, invoice, y)
function calculateAmounts(invoice) { function calculateAmounts(invoice) {
var total = 0; var total = 0;
var hasTaxes = false; var hasTaxes = false;
var taxes = {};
// sum line item
for (var i=0; i<invoice.invoice_items.length; i++) {
var item = invoice.invoice_items[i];
var lineTotal = roundToTwo(NINJA.parseFloat(item.cost)) * roundToTwo(NINJA.parseFloat(item.qty));
if (lineTotal) {
total += lineTotal;
}
}
for (var i=0; i<invoice.invoice_items.length; i++) { for (var i=0; i<invoice.invoice_items.length; i++) {
var item = invoice.invoice_items[i]; var item = invoice.invoice_items[i];
var tax = 0; var taxRate = 0;
var taxName = '';
// the object structure differs if it's read from the db or created by knockoutJS
if (item.tax && parseFloat(item.tax.rate)) { if (item.tax && parseFloat(item.tax.rate)) {
tax = parseFloat(item.tax.rate); taxRate = parseFloat(item.tax.rate);
taxName = item.tax.name;
} else if (item.tax_rate && parseFloat(item.tax_rate)) { } else if (item.tax_rate && parseFloat(item.tax_rate)) {
tax = parseFloat(item.tax_rate); taxRate = parseFloat(item.tax_rate);
taxName = item.tax_name;
} }
// calculate line item tax
var lineTotal = roundToTwo(NINJA.parseFloat(item.cost)) * roundToTwo(NINJA.parseFloat(item.qty)); var lineTotal = roundToTwo(NINJA.parseFloat(item.cost)) * roundToTwo(NINJA.parseFloat(item.qty));
if (tax) { if (invoice.discount != 0) {
lineTotal += roundToTwo(lineTotal * tax / 100); if (parseInt(invoice.is_amount_discount)) {
lineTotal -= roundToTwo((lineTotal/total) * invoice.discount);
} else {
lineTotal -= roundToTwo(lineTotal * (invoice.discount/100));
}
}
var taxAmount = roundToTwo(lineTotal * taxRate / 100);
if (taxRate) {
var key = taxName + taxRate;
if (taxes.hasOwnProperty(key)) {
taxes[key].amount += taxAmount;
} else {
taxes[key] = {name: taxName, rate:taxRate, amount:taxAmount};
} }
if (lineTotal) {
total += lineTotal;
} }
if ((item.tax && item.tax.name) || item.tax_name) { if ((item.tax && item.tax.name) || item.tax_name) {
@ -968,6 +1012,12 @@ function calculateAmounts(invoice) {
total = parseFloat(total) + parseFloat(tax); total = parseFloat(total) + parseFloat(tax);
} }
for (var key in taxes) {
if (taxes.hasOwnProperty(key)) {
total += taxes[key].amount;
}
}
// custom fields w/o with taxes // custom fields w/o with taxes
if (NINJA.parseFloat(invoice.custom_value1) && invoice.custom_taxes1 != '1') { if (NINJA.parseFloat(invoice.custom_value1) && invoice.custom_taxes1 != '1') {
total += roundToTwo(invoice.custom_value1); total += roundToTwo(invoice.custom_value1);
@ -979,7 +1029,7 @@ function calculateAmounts(invoice) {
invoice.total_amount = roundToTwo(total) - (roundToTwo(invoice.amount) - roundToTwo(invoice.balance)); invoice.total_amount = roundToTwo(total) - (roundToTwo(invoice.amount) - roundToTwo(invoice.balance));
invoice.discount_amount = discount; invoice.discount_amount = discount;
invoice.tax_amount = tax; invoice.tax_amount = tax;
invoice.has_taxes = hasTaxes; invoice.item_taxes = taxes;
if (NINJA.parseFloat(invoice.partial)) { if (NINJA.parseFloat(invoice.partial)) {
invoice.balance_amount = roundToTwo(invoice.partial); invoice.balance_amount = roundToTwo(invoice.partial);
@ -1024,11 +1074,12 @@ function displayInvoiceHeader(doc, invoice, layout) {
} }
doc.text(totalX, layout.tableTop, invoiceLabels.line_total); doc.text(totalX, layout.tableTop, invoiceLabels.line_total);
/*
if (invoice.has_taxes) if (invoice.has_taxes)
{ {
doc.text(taxX, layout.tableTop, invoiceLabels.tax); doc.text(taxX, layout.tableTop, invoiceLabels.tax);
} }
*/
} }
function displayInvoiceItems(doc, invoice, layout) { function displayInvoiceItems(doc, invoice, layout) {
@ -1211,10 +1262,11 @@ function displayInvoiceItems(doc, invoice, layout) {
doc.line(qtyX-45, y-16,qtyX-45, y+55); doc.line(qtyX-45, y-16,qtyX-45, y+55);
/*
if (invoice.has_taxes) { if (invoice.has_taxes) {
doc.line(taxX-15, y-16,taxX-15, y+55); doc.line(taxX-15, y-16,taxX-15, y+55);
} }
*/
doc.line(totalX-27, y-16,totalX-27, y+55); doc.line(totalX-27, y-16,totalX-27, y+55);
} }
@ -1277,9 +1329,11 @@ function displayInvoiceItems(doc, invoice, layout) {
doc.line(layout.descriptionLeft-8, topX,layout.descriptionLeft-8, y); doc.line(layout.descriptionLeft-8, topX,layout.descriptionLeft-8, y);
doc.line(layout.unitCostRight-55, topX,layout.unitCostRight-55, y); doc.line(layout.unitCostRight-55, topX,layout.unitCostRight-55, y);
doc.line(layout.qtyRight-50, topX,layout.qtyRight-50, y); doc.line(layout.qtyRight-50, topX,layout.qtyRight-50, y);
/*
if (invoice.has_taxes) { if (invoice.has_taxes) {
doc.line(layout.taxRight-28, topX,layout.taxRight-28, y); doc.line(layout.taxRight-28, topX,layout.taxRight-28, y);
} }
*/
doc.line(totalX-25, topX,totalX-25, y+90); doc.line(totalX-25, topX,totalX-25, y+90);
doc.line(totalX+45, topX,totalX+45, y+90); doc.line(totalX+45, topX,totalX+45, y+90);
} }
@ -1575,3 +1629,19 @@ function twoDigits(value) {
} }
return value; return value;
} }
function toSnakeCase(str) {
if (!str) return '';
return str.replace(/([A-Z])/g, function($1){return "_"+$1.toLowerCase();});
}
function getDescendantProp(obj, desc) {
var arr = desc.split(".");
while(arr.length && (obj = obj[arr.shift()]));
return obj;
}
function doubleDollarSign(str) {
if (!str) return '';
return str.replace(/\$/g, '\$\$\$');
}

View File

@ -1,162 +0,0 @@
//pdfmake
/*
var dd = {
content: 'wqy中文wqy',
defaultStyle: {
font: 'wqy'
}
};
*/
var dd = {
content: [
{
columns: [
[
invoice.image?
{
image: invoice.image,
fit: [150, 80]
}:""
],
{
stack: NINJA.accountDetails(account)
},
{
stack: NINJA.accountAddress(account)
}
]
},
{
text:(NINJA.getEntityLabel(invoice)).toUpperCase(),
margin: [8, 70, 8, 16],
style: 'primaryColor',
fontSize: NINJA.fontSize + 2
},
{
table: {
headerRows: 1,
widths: ['auto', 'auto', '*'],
body: [
[
{
table: {
body: NINJA.invoiceDetails(invoice),
},
layout: 'noBorders',
},
{
table: {
body: NINJA.clientDetails(invoice),
},
layout: 'noBorders',
},
''
]
]
},
layout: {
hLineWidth: function (i, node) {
return (i === 0 || i === node.table.body.length) ? .5 : 0;
},
vLineWidth: function (i, node) {
return 0;
},
hLineColor: function (i, node) {
return '#D8D8D8';
},
paddingLeft: function(i, node) { return 8; },
paddingRight: function(i, node) { return 8; },
paddingTop: function(i, node) { return 4; },
paddingBottom: function(i, node) { return 4; }
}
},
'\n',
{
table: {
headerRows: 1,
widths: ['15%', '*', 'auto', 'auto', 'auto', 'auto'],
body: NINJA.invoiceLines(invoice),
},
layout: {
hLineWidth: function (i, node) {
return i === 0 ? 0 : .5;
},
vLineWidth: function (i, node) {
return 0;
},
hLineColor: function (i, node) {
return '#D8D8D8';
},
paddingLeft: function(i, node) { return 8; },
paddingRight: function(i, node) { return 8; },
paddingTop: function(i, node) { return 8; },
paddingBottom: function(i, node) { return 8; }
},
},
'\n',
{
columns: [
NINJA.notesAndTerms(invoice),
{
style: 'subtotals',
table: {
widths: ['*', '*'],
body: NINJA.subtotals(invoice),
},
layout: {
hLineWidth: function (i, node) {
return 0;
},
vLineWidth: function (i, node) {
return 0;
},
paddingLeft: function(i, node) { return 8; },
paddingRight: function(i, node) { return 8; },
paddingTop: function(i, node) { return 4; },
paddingBottom: function(i, node) { return 4; }
},
}
]
},
],
defaultStyle: {
//font: 'arialuni',
fontSize: NINJA.fontSize,
margin: [8, 4, 8, 4]
},
styles: {
primaryColor:{
color: NINJA.getPrimaryColor('#299CC2')
},
accountName: {
margin: [4, 2, 4, 2],
color: NINJA.getPrimaryColor('#299CC2')
},
accountDetails: {
margin: [4, 2, 4, 2],
color: '#AAA9A9'
},
even: {
},
odd: {
fillColor:'#F4F4F4'
},
productKey: {
color: NINJA.getPrimaryColor('#299CC2')
},
tableHeader: {
bold: true
},
balanceDueLabel: {
fontSize: NINJA.fontSize + 2
},
balanceDueValue: {
fontSize: NINJA.fontSize + 2,
color: NINJA.getPrimaryColor('#299CC2')
},
},
pageMargins: [40, 40, 40, 40],
};

View File

@ -3,9 +3,13 @@
If you'd like to use our code to sell your own invoicing app email us for details about our affiliate program. If you'd like to use our code to sell your own invoicing app email us for details about our affiliate program.
### Getting Started ### Installation Options
To setup the site you can either use the [zip file](https://www.invoiceninja.com/knowledgebase/self-host/) (easier to run) or checkout the code from GitHub (easier to make changes). * [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)
### Getting Started
If you have any questions or comments please use our [support forum](https://www.invoiceninja.com/forums/forum/support/). For updates follow [@invoiceninja](https://twitter.com/invoiceninja) or join the [Facebook Group](https://www.facebook.com/invoiceninja). If you have any questions or comments please use our [support forum](https://www.invoiceninja.com/forums/forum/support/). For updates follow [@invoiceninja](https://twitter.com/invoiceninja) or join the [Facebook Group](https://www.facebook.com/invoiceninja).
@ -32,11 +36,10 @@ If you'd like to translate the site please use [caouecs/Laravel4-long](https://g
* [Sigitas Limontas](https://lt.linkedin.com/in/sigitaslimontas) * [Sigitas Limontas](https://lt.linkedin.com/in/sigitaslimontas)
### Documentation ### Documentation
* [Self Host](https://www.invoiceninja.com/knowledgebase/self-host/)
* [Ubuntu and Apache](http://blog.technerdservices.com/index.php/2015/04/techpop-how-to-install-invoice-ninja-on-ubuntu-14-04/) * [Ubuntu and Apache](http://blog.technerdservices.com/index.php/2015/04/techpop-how-to-install-invoice-ninja-on-ubuntu-14-04/)
* [Debian and Nginx](https://www.rosehosting.com/blog/install-invoice-ninja-on-a-debian-7-vps/) * [Debian and Nginx](https://www.rosehosting.com/blog/install-invoice-ninja-on-a-debian-7-vps/)
* [API Documentation](https://www.invoiceninja.com/knowledgebase/api-documentation/) * [API Documentation](https://www.invoiceninja.com/knowledgebase/api-documentation/)
* [User Guide](https://www.invoiceninja.com/user-guide/)
* [Developer Guide](https://www.invoiceninja.com/knowledgebase/developer-guide/) * [Developer Guide](https://www.invoiceninja.com/knowledgebase/developer-guide/)
### Frameworks/Libraries ### Frameworks/Libraries
@ -68,3 +71,4 @@ If you'd like to translate the site please use [caouecs/Laravel4-long](https://g
* [caouecs/Laravel4-long](https://github.com/caouecs/Laravel4-lang) - List of languages for Laravel4 * [caouecs/Laravel4-long](https://github.com/caouecs/Laravel4-lang) - List of languages for Laravel4
* [bgrins/spectrum](https://github.com/bgrins/spectrum) - The No Hassle JavaScript Colorpicker * [bgrins/spectrum](https://github.com/bgrins/spectrum) - The No Hassle JavaScript Colorpicker
* [lokesh/lightbox2](https://github.com/lokesh/lightbox2/) - The original lightbox script * [lokesh/lightbox2](https://github.com/lokesh/lightbox2/) - The original lightbox script
* [josdejong/jsoneditor](https://github.com/josdejong/jsoneditor/) - A web-based tool to view, edit and format JSON

View File

@ -5,8 +5,6 @@ return array(
// client // client
'organization' => 'Organisation', 'organization' => 'Organisation',
'name' => 'Navn', 'name' => 'Navn',
'id_number' => 'SE/CVR nummer',
'vat_number' => 'SE/CVR nummer',
'website' => 'Webside', 'website' => 'Webside',
'work_phone' => 'Telefon', 'work_phone' => 'Telefon',
'address' => 'Adresse', 'address' => 'Adresse',
@ -34,9 +32,9 @@ return array(
'invoice_date' => 'Faktureringsdato', 'invoice_date' => 'Faktureringsdato',
'due_date' => 'Betalingsfrist', 'due_date' => 'Betalingsfrist',
'invoice_number' => 'Fakturanummer', 'invoice_number' => 'Fakturanummer',
'invoice_number_short' => 'Faktura #', 'invoice_number_short' => 'Faktura nr.: #',
'po_number' => 'Ordrenummer', 'po_number' => 'Ordrenummer',
'po_number_short' => 'Ordre #', 'po_number_short' => 'Ordre nr.: #',
'frequency_id' => 'Frekvens', 'frequency_id' => 'Frekvens',
'discount' => 'Rabat', 'discount' => 'Rabat',
'taxes' => 'Skatter', 'taxes' => 'Skatter',
@ -54,16 +52,16 @@ return array(
'your_invoice' => 'Din faktura', 'your_invoice' => 'Din faktura',
'remove_contact' => 'Fjern kontakt', 'remove_contact' => 'Fjern kontakt',
'add_contact' => 'Tilføj til kontakt', 'add_contact' => 'Tilføj kontakt',
'create_new_client' => 'Opret ny klient', 'create_new_client' => 'Opret ny klient',
'edit_client_details' => 'Ændre klientdetaljer', 'edit_client_details' => 'Redigér klient detaljer',
'enable' => 'Aktiver', 'enable' => 'Aktiver',
'learn_more' => 'Lær mere', 'learn_more' => 'Lær mere',
'manage_rates' => 'Administrer priser', 'manage_rates' => 'Administrer priser',
'note_to_client' => 'Note til klient', 'note_to_client' => 'Bemærkninger til klient',
'invoice_terms' => 'Vilkår for fakturaen', 'invoice_terms' => 'Vilkår for fakturaen',
'save_as_default_terms' => 'Gem som standard vilkår', 'save_as_default_terms' => 'Gem som standard vilkår',
'download_pdf' => 'Download PDF', 'download_pdf' => 'Hent PDF',
'pay_now' => 'Betal nu', 'pay_now' => 'Betal nu',
'save_invoice' => 'Gem faktura', 'save_invoice' => 'Gem faktura',
'clone_invoice' => 'Kopier faktura', 'clone_invoice' => 'Kopier faktura',
@ -75,10 +73,10 @@ return array(
'rate' => 'Sats', 'rate' => 'Sats',
'settings' => 'Indstillinger', 'settings' => 'Indstillinger',
'enable_invoice_tax' => 'Aktiver for at specificere en <b>faktura skat</b>', 'enable_invoice_tax' => 'Aktiver for at specificere en <b>faktura skat</b>',
'enable_line_item_tax' => 'Aktiver for at specificere per <b>faktura linje skat</b>', 'enable_line_item_tax' => 'Aktiver for at specificere per linje<b>faktura skat</b>',
// navigation // navigation
'dashboard' => 'Dashboard', 'dashboard' => 'Oversigt',
'clients' => 'Klienter', 'clients' => 'Klienter',
'invoices' => 'Fakturaer', 'invoices' => 'Fakturaer',
'payments' => 'Betalinger', 'payments' => 'Betalinger',
@ -86,31 +84,31 @@ return array(
'history' => 'Historie', 'history' => 'Historie',
'search' => 'Søg', 'search' => 'Søg',
'sign_up' => 'Registrer dig', 'sign_up' => 'Registrer dig',
'guest' => 'Gjest', 'guest' => 'Gæst',
'company_details' => 'Firmainformation', 'company_details' => 'Firmainformation',
'online_payments' => 'Online betaling', 'online_payments' => 'On-line betaling',
'notifications' => 'Varsler', 'notifications' => 'Notifikationer',
'import_export' => 'Import/Export', 'import_export' => 'Import/Eksport',
'done' => 'Færdig', 'done' => 'Færdig',
'save' => 'Gem', 'save' => 'Gem',
'create' => 'Opret', 'create' => 'Opret',
'upload' => 'Upload', 'upload' => 'Send',
'import' => 'Importer', 'import' => 'Importer',
'download' => 'Download', 'download' => 'Hent',
'cancel' => 'Afbrud', 'cancel' => 'Afbryd',
'close' => 'Luk', 'close' => 'Luk',
'provide_email' => 'Venligst opgiv en gyldig e-mail', 'provide_email' => 'Opgiv venligst en gyldig e-mail',
'powered_by' => 'Drevet af', 'powered_by' => 'Drevet af',
'no_items' => 'Ingen elementer', 'no_items' => 'Ingen elementer',
// recurring invoices // recurring invoices
'recurring_invoices' => 'Gentagende fakturaer', 'recurring_invoices' => 'Gentagende fakturaer',
'recurring_help' => '<p>Automatisk send klienter de samme fakturaene ugentlig, bi-månedlig, månedlig, kvartalsvis eller årlig.</p> 'recurring_help' => '<p>Send automatisk klienter de samme fakturaer ugentligt, bi-månedligt, månedligt, kvartalsvis eller årligt.</p>
<p>Brug :MONTH, :QUARTER eller :YEAR for dynamiske datoer. Grundliggende matematik fungerer også, for eksempel :MONTH-1.</p> <p>Brug :MONTH, :QUARTER eller :YEAR for dynamiske datoer. Grundliggende matematik fungerer også, for eksempel :MONTH-1.</p>
<p>Eksempler dynamiske faktura variabler:</p> <p>Eksempler dynamiske faktura variabler:</p>
<ul> <ul>
<li>"Træningsmedlemskab for måneden :MONTH" => "Træningsmedlemskab for måneden Juli"</li> <li>"Træningsmedlemskab for måneden :MONTH" => "Træningsmedlemskab for måneden Juli"</li>
<li>":YEAR+1 årlig abonnement" => "2015 årlig abonnement"</li> <li>":YEAR+1 årligt abonnement" => "2015 årligt abonnement"</li>
<li>"Forudbetaling for :QUARTER+1" => "Forudbetaling for Q2"</li> <li>"Forudbetaling for :QUARTER+1" => "Forudbetaling for Q2"</li>
</ul>', </ul>',
@ -121,8 +119,8 @@ return array(
'active_client' => 'aktiv klient', 'active_client' => 'aktiv klient',
'active_clients' => 'aktive klienter', 'active_clients' => 'aktive klienter',
'invoices_past_due' => 'Forfaldne fakturaer', 'invoices_past_due' => 'Forfaldne fakturaer',
'upcoming_invoices' => 'Forestående fakturaer', 'upcoming_invoices' => 'Kommende fakturaer',
'average_invoice' => 'Gennemsnitlige fakturaer', 'average_invoice' => 'Gennemsnitlig fakturaer',
// list pages // list pages
'archive' => 'Arkiv', 'archive' => 'Arkiv',
@ -133,7 +131,7 @@ return array(
'delete_payment' => 'Slet betaling', 'delete_payment' => 'Slet betaling',
'archive_credit' => 'Arkiver kredit', 'archive_credit' => 'Arkiver kredit',
'delete_credit' => 'Slet kredit', 'delete_credit' => 'Slet kredit',
'show_archived_deleted' => 'Vis slettet/arkiverede', 'show_archived_deleted' => 'Vis arkiverede/slettede',
'filter' => 'Filter', 'filter' => 'Filter',
'new_client' => 'Ny klient', 'new_client' => 'Ny klient',
'new_invoice' => 'Ny faktura', 'new_invoice' => 'Ny faktura',
@ -141,7 +139,7 @@ return array(
'new_credit' => 'Ny kredit', 'new_credit' => 'Ny kredit',
'contact' => 'Kontakt', 'contact' => 'Kontakt',
'date_created' => 'Dato oprettet', 'date_created' => 'Dato oprettet',
'last_login' => 'Sidst på-logging', 'last_login' => 'Sidste log ind',
'balance' => 'Balance', 'balance' => 'Balance',
'action' => 'Handling', 'action' => 'Handling',
'status' => 'Status', 'status' => 'Status',
@ -162,9 +160,9 @@ return array(
'edit_invoice' => 'Rediger faktura', 'edit_invoice' => 'Rediger faktura',
// client view page // client view page
'create_invoice' => 'Lag faktura', 'create_invoice' => 'Opret faktura',
'enter_credit' => 'Tilføj kredit', 'enter_credit' => 'Tilføj kredit',
'last_logged_in' => 'Sidst på-logget', 'last_logged_in' => 'Sidste log ind',
'details' => 'Detaljer', 'details' => 'Detaljer',
'standing' => 'Stående', 'standing' => 'Stående',
'credit' => 'Kredit', 'credit' => 'Kredit',
@ -187,13 +185,13 @@ return array(
'users' => 'Brugere', 'users' => 'Brugere',
'localization' => 'Lokalisering', 'localization' => 'Lokalisering',
'remove_logo' => 'Fjern logo', 'remove_logo' => 'Fjern logo',
'logo_help' => 'Understøttede filtyper: JPEG, GIF og PNG. Anbefalet størrelse: 200px brede og 120px højde', 'logo_help' => 'Understøttede filtyper: JPEG, GIF og PNG. Anbefalet størrelse: 200px bredde og 120px højde',
'payment_gateway' => 'Betalingsløsning', 'payment_gateway' => 'Betalingsløsning',
'gateway_id' => 'Tilbyder', 'gateway_id' => 'Kort betalings udbyder',
'email_notifications' => 'Varsel via e-mail', 'email_notifications' => 'Notifikation via e-mail',
'email_sent' => 'Varsle når en faktura er <b>sendt</b>', 'email_sent' => 'Notifikation når en faktura er <b>sendt</b>',
'email_viewed' => 'Varsle når en faktura er <b>set</b>', 'email_viewed' => 'Notifikation når en faktura er <b>set</b>',
'email_paid' => 'Varsle når en faktura er <b>betalt</b>', 'email_paid' => 'Notifikation når en faktura er <b>betalt</b>',
'site_updates' => 'Webside opdateringer', 'site_updates' => 'Webside opdateringer',
'custom_messages' => 'Tilpassede meldinger', 'custom_messages' => 'Tilpassede meldinger',
'default_invoice_terms' => 'Sæt standard fakturavilkår', 'default_invoice_terms' => 'Sæt standard fakturavilkår',
@ -208,27 +206,27 @@ return array(
'import_to' => 'Importer til', 'import_to' => 'Importer til',
'client_will_create' => 'Klient vil blive oprettet', 'client_will_create' => 'Klient vil blive oprettet',
'clients_will_create' => 'Klienter vil blive oprettet', 'clients_will_create' => 'Klienter vil blive oprettet',
'email_settings' => 'Email Settings', 'email_settings' => 'E-mail Indstillinger',
'pdf_email_attachment' => 'Attach PDF to Emails', 'pdf_email_attachment' => 'Vedhæft PDF til e-mails',
// application messages // application messages
'created_client' => 'Klient oprettet succesfuldt', 'created_client' => 'Klient oprettet succesfuldt',
'created_clients' => 'Klienter oprettet succesfuldt', 'created_clients' => 'Klienter oprettet succesfuldt',
'updated_settings' => 'Indstillinger opdateret', 'updated_settings' => 'Indstillinger opdateret',
'removed_logo' => 'Logo fjernet', 'removed_logo' => 'Logo fjernet',
'sent_message' => 'Melding sendt', 'sent_message' => 'Besked sendt',
'invoice_error' => 'Venligst vælge en klient og rette eventuelle fejl', 'invoice_error' => 'Vælg venligst en klient og ret eventuelle fejl',
'limit_clients' => 'Desværre, dette vil overstige græsen på :count klienter', 'limit_clients' => 'Desværre, dette vil overstige grænsen på :count klienter',
'payment_error' => 'Det opstod en fejl under din betaling. Venligst prøv igen senere.', 'payment_error' => 'Det opstod en fejl under din betaling. Venligst prøv igen senere.',
'registration_required' => 'Venligst registrer dig for at sende e-mail faktura', 'registration_required' => 'Venligst registrer dig for at sende e-mail faktura',
'confirmation_required' => 'Venligst bekræft din e-mail', 'confirmation_required' => 'Venligst bekræft din e-mail adresse',
'updated_client' => 'Klient opdateret', 'updated_client' => 'Klient opdateret',
'created_client' => 'Klient lagret', 'created_client' => 'Klient oprettet',
'archived_client' => 'Klient arkiveret', 'archived_client' => 'Klient arkiveret',
'archived_clients' => 'Arkiverede :count klienter', 'archived_clients' => 'Arkiverede :count klienter',
'deleted_client' => 'Klient slettet', 'deleted_client' => 'Klient slettet',
'deleted_clients' => 'Slettet :count klienter', 'deleted_clients' => 'Slettede :count klienter',
'updated_invoice' => 'Faktura opdateret', 'updated_invoice' => 'Faktura opdateret',
'created_invoice' => 'Faktura oprettet', 'created_invoice' => 'Faktura oprettet',
@ -238,42 +236,42 @@ return array(
'archived_invoice' => 'Faktura arkiveret', 'archived_invoice' => 'Faktura arkiveret',
'archived_invoices' => 'Arkiverede :count fakturaer', 'archived_invoices' => 'Arkiverede :count fakturaer',
'deleted_invoice' => 'Faktura slettet', 'deleted_invoice' => 'Faktura slettet',
'deleted_invoices' => 'Slettet :count fakturaer', 'deleted_invoices' => 'Slettede :count fakturaer',
'created_payment' => 'Betaling oprettet', 'created_payment' => 'Betaling oprettet',
'archived_payment' => 'Betaling arkiveret', 'archived_payment' => 'Betaling arkiveret',
'archived_payments' => 'Arkiverede :count betalinger', 'archived_payments' => 'Arkiverede :count betalinger',
'deleted_payment' => 'Betaling slettet', 'deleted_payment' => 'Betaling slettet',
'deleted_payments' => 'Slettet :count betalinger', 'deleted_payments' => 'Slettede :count betalinger',
'applied_payment' => 'Betaling lagret', 'applied_payment' => 'Betaling ajourført',
'created_credit' => 'Kredit oprettet', 'created_credit' => 'Kredit oprettet',
'archived_credit' => 'Kredit arkiveret', 'archived_credit' => 'Kredit arkiveret',
'archived_credits' => 'Arkiverede :count kreditter', 'archived_credits' => 'Arkiverede :count kreditter',
'deleted_credit' => 'Kredit slettet', 'deleted_credit' => 'Kredit slettet',
'deleted_credits' => 'Slettet :count kreditter', 'deleted_credits' => 'Slettede :count kreditter',
// Emails // Emails
'confirmation_subject' => 'Invoice Ninja konto bekræftelse', 'confirmation_subject' => 'Invoice Ninja konto bekræftelse',
'confirmation_header' => 'Konto bekræftelse', 'confirmation_header' => 'Konto bekræftelse',
'confirmation_message' => 'Venligst åbne linket nedenfor for å bekræfte din konto.', 'confirmation_message' => 'Venligst klik på linket nedenfor for at bekræfte din konto.',
'invoice_subject' => 'Ny faktura :invoice fra :account', 'invoice_subject' => 'Ny faktura :invoice fra :account',
'invoice_message' => 'For at se din faktura på :amount, klik på linket nedenfor.', 'invoice_message' => 'For at se din faktura på :amount, klik på linket nedenfor.',
'payment_subject' => 'Betaling modtaget', 'payment_subject' => 'Betaling modtaget',
'payment_message' => 'Tak for din betaling pålydende :amount.', 'payment_message' => 'Tak for din betaling pålydende :amount.',
'email_salutation' => 'Kære :name,', 'email_salutation' => 'Kære :name,',
'email_signature' => 'Med venlig hilsen,', 'email_signature' => 'Med venlig hilsen,',
'email_from' => 'The Invoice Ninja Team', 'email_from' => 'Invoice Ninja Teamet',
'user_email_footer' => 'For at justere varslingsindstillingene venligst besøg '.SITE_URL.'/company/notifications', 'user_email_footer' => 'For at justere varslings indstillingene besøg venligst'.SITE_URL.'/company/notifications',
'invoice_link_message' => 'Hvis du vil se din klient faktura klik på linket under:', 'invoice_link_message' => 'Hvis du vil se din klient faktura klik på linket under:',
'notification_invoice_paid_subject' => 'Faktura :invoice betalt af :client', 'notification_invoice_paid_subject' => 'Faktura :invoice betalt af :client',
'notification_invoice_sent_subject' => 'Faktura :invoice sendt til :client', 'notification_invoice_sent_subject' => 'Faktura :invoice sendt til :client',
'notification_invoice_viewed_subject' => 'Faktura :invoice set av :client', 'notification_invoice_viewed_subject' => 'Faktura :invoice set af :client',
'notification_invoice_paid' => 'En betaling pålydende :amount blev gjort af :client for faktura :invoice.', 'notification_invoice_paid' => 'En betaling pålydende :amount blev betalt af :client for faktura :invoice.',
'notification_invoice_sent' => 'E-mail er blevet sendt til :client - Faktura :invoice pålydende :amount.', 'notification_invoice_sent' => 'En e-mail er blevet sendt til :client med faktura :invoice pålydende :amount.',
'notification_invoice_viewed' => ':client har set faktura :invoice pålydende :amount.', 'notification_invoice_viewed' => ':client har set faktura :invoice pålydende :amount.',
'reset_password' => 'Du kan nulstille din adgangskode ved at besøge følgende link:', 'reset_password' => 'Du kan nulstille din adgangskode ved at besøge følgende link:',
'reset_password_footer' => 'Hvis du ikke bad om at få nulstillet din adgangskode venligst kontakt kundeservice: ' . CONTACT_EMAIL, 'reset_password_footer' => 'Hvis du ikke bad om at få nulstillet din adgangskode kontakt venligst kundeservice: ' . CONTACT_EMAIL,
// Payment page // Payment page
@ -281,14 +279,14 @@ return array(
'card_number' => 'Kortnummer', 'card_number' => 'Kortnummer',
'expiration_month' => 'Udløbsdato', 'expiration_month' => 'Udløbsdato',
'expiration_year' => 'Udløbsår', 'expiration_year' => 'Udløbsår',
'cvv' => 'CVV', 'cvv' =>'Kontrolcifre',
// Security alerts // Security alerts
'security' => [ 'security' => [
'too_many_attempts' => 'For mange forsøg. Prøv igen om nogen få minutter.', 'too_many_attempts' => 'For mange forsøg. Prøv igen om nogen få minutter.',
'wrong_credentials' => 'Fejl e-mail eller adgangskode.', 'wrong_credentials' => 'Forkert e-mail eller adgangskode.',
'confirmation' => 'Din konto har blevet bekræftet!', 'confirmation' => 'Din konto har blevet bekræftet!',
'wrong_confirmation' => 'Fejl bekræftelseskode.', 'wrong_confirmation' => 'Forkert bekræftelseskode.',
'password_forgot' => 'Informationen om nulstilling af din adgangskode er blevet sendt til din e-mail.', 'password_forgot' => 'Informationen om nulstilling af din adgangskode er blevet sendt til din e-mail.',
'password_reset' => 'Adgangskode ændret', 'password_reset' => 'Adgangskode ændret',
'wrong_password_reset' => 'Ugyldig adgangskode. Prøv på ny', 'wrong_password_reset' => 'Ugyldig adgangskode. Prøv på ny',
@ -296,33 +294,32 @@ return array(
// Pro Plan // Pro Plan
'pro_plan' => [ 'pro_plan' => [
'remove_logo' => ':link for å fjerne Invoice Ninja-logoen, oppgrader til en Pro Plan', 'remove_logo' => ':link for at fjerne Invoice Ninja-logoet, opgrader til en Pro Plan',
'remove_logo_link' => 'Klik her', 'remove_logo_link' => 'Klik her',
], ],
'logout' => 'Log ud', 'logout' => 'Log ud',
'sign_up_to_save' => 'Registrer dig for at gemme dit arbejde', 'sign_up_to_save' => 'Registrer dig for at gemme dit arbejde',
'agree_to_terms' =>'Jeg acceptere Invoice Ninja :terms', 'agree_to_terms' =>'Jeg accepterer Invoice Ninja :terms',
'terms_of_service' => 'vilkår for brug', 'terms_of_service' => 'Vilkår for brug',
'email_taken' => 'E-mailadressen er allerede registreret', 'email_taken' => 'E-mailadressen er allerede registreret',
'working' => 'Arbejder', 'working' => 'Arbejder',
'success' => 'Succes', 'success' => 'Succes',
'success_message' => 'Du er blevet registreret. Klik på linket som du har modtaget i e-mail bekræftelsen for at bekræfte din e-mail adresse.',
'success_message' => 'Du er blevet registreret. Venligst gå ind på linket som du har modtaget i e-mailbekræftelsen for at bekræfte e-mailaddressen.', 'erase_data' => 'Dette vil permanent slette dine oplysninger.',
'erase_data' => 'Dette vil permanent slette dine data.',
'password' => 'Adgangskode', 'password' => 'Adgangskode',
'pro_plan_product' => 'Pro Plan', 'pro_plan_product' => 'Pro Plan',
'pro_plan_description' => 'Et års indmelding i Invoice Ninja Pro Plan.', 'pro_plan_description' => 'Et års indmelding i Invoice Ninja Pro Plan.',
'pro_plan_success' => 'Tak for at du valgte Invoice Ninja\'s Pro plan!<p/>&nbsp;<br/> 'pro_plan_success' => 'Tak fordi du valgte Invoice Ninja\'s Pro plan!<p/>&nbsp;<br/>
<b>De næste skridt</b><p/>En betalbar faktura er send til e-emailaddesse <b>De næste skridt</b><p/>En betalbar faktura er sendt til den e-email adresse
som er tilknyttet din konto. For at låse op for alle de utrolige som er tilknyttet din konto. For at låse op for alle de utrolige
Pro-funktioner, kan du følge instruktionerne fakturaen til at Pro-funktioner, skal du følge instruktionerne fakturaen til at
betale for et år med Pro-niveau funktionerer.<p/> betale for et år med Pro-niveau funktionerer.<p/>
Kan du ikke finde fakturaen? Har behov for mere hjælp? Vi hjælper dig gerne om det skulle være noget Kan du ikke finde fakturaen? Har behov for mere hjælp? Vi hjælper dig gerne hvis der skulle være noget galt
-- kontakt os contact@invoiceninja.com', -- kontakt os contact@invoiceninja.com',
'unsaved_changes' => 'Du har ugemente ændringer', 'unsaved_changes' => 'Du har ikke gemte ændringer',
'custom_fields' => 'Egendefineret felt', 'custom_fields' => 'Egendefineret felt',
'company_fields' => 'Selskabets felt', 'company_fields' => 'Selskabets felt',
'client_fields' => 'Klientens felt', 'client_fields' => 'Klientens felt',
@ -344,18 +341,18 @@ return array(
'edit_product' => 'Rediger produkt', 'edit_product' => 'Rediger produkt',
'archive_product' => 'Arkiver produkt', 'archive_product' => 'Arkiver produkt',
'updated_product' => 'Produkt opdateret', 'updated_product' => 'Produkt opdateret',
'created_product' => 'Produkt lagret', 'created_product' => 'Produkt oprettet',
'archived_product' => 'Produkt arkiveret', 'archived_product' => 'Produkt arkiveret',
'pro_plan_custom_fields' => ':link for at aktivere egendefinerede felter ved at bliver medlem af Pro Plan', 'pro_plan_custom_fields' => ':link for at aktivere egendefinerede felter skal du have en Pro Plan',
'advanced_settings' => 'avancerede indstillinger', 'advanced_settings' => 'Aavancerede indstillinger',
'pro_plan_advanced_settings' => ':link for at aktivere avancerede indstillinger felter ved at bliver medlem af Pro Plan', 'pro_plan_advanced_settings' => ':link for at aktivere avancerede indstillinger skal du være have en Pro Plan',
'invoice_design' => 'Fakturadesign', 'invoice_design' => 'Fakturadesign',
'specify_colors' => 'Egendefineret farve', 'specify_colors' => 'Egendefineret farve',
'specify_colors_label' => 'Vælg farver som bruges i fakturaen', 'specify_colors_label' => 'Vælg de farver som skal bruges i fakturaen',
'chart_builder' => 'Diagram bygger', 'chart_builder' => 'Diagram bygger',
'ninja_email_footer' => 'Brug :sitet til at fakturere dine kunder og få betalt på nettet - gratis!', 'ninja_email_footer' => 'Brug :sitet til at fakturere dine kunder og bliv betalt på nettet - gratis!',
'go_pro' => 'Vælg Pro', 'go_pro' => 'Vælg Pro',
// Quotes // Quotes
@ -370,11 +367,11 @@ return array(
'clone' => 'Kopier', 'clone' => 'Kopier',
'new_quote' => 'Nyt tilbud', 'new_quote' => 'Nyt tilbud',
'create_quote' => 'Gem tilbud', 'create_quote' => 'Opret tilbud',
'edit_quote' => 'Rediger tilbud', 'edit_quote' => 'Rediger tilbud',
'archive_quote' => 'Arkiver tilbud', 'archive_quote' => 'Arkiver tilbud',
'delete_quote' => 'Slet tilbud', 'delete_quote' => 'Slet tilbud',
'save_quote' => 'Lagre tilbud', 'save_quote' => 'Gem tilbud',
'email_quote' => 'E-mail tilbudet', 'email_quote' => 'E-mail tilbudet',
'clone_quote' => 'Kopier tilbud', 'clone_quote' => 'Kopier tilbud',
'convert_to_invoice' => 'Konverter til en faktura', 'convert_to_invoice' => 'Konverter til en faktura',
@ -382,18 +379,18 @@ return array(
'view_client' => 'Vis klient', 'view_client' => 'Vis klient',
'view_quote' => 'Se tilbud', 'view_quote' => 'Se tilbud',
'updated_quote' => 'Tilbud opdatert', 'updated_quote' => 'Tilbud opdateret',
'created_quote' => 'Tilbud oprettet', 'created_quote' => 'Tilbud oprettet',
'cloned_quote' => 'Tilbud kopieret', 'cloned_quote' => 'Tilbud kopieret',
'emailed_quote' => 'Tilbud sendt som e-mail', 'emailed_quote' => 'Tilbud sendt som e-mail',
'archived_quote' => 'Tilbud arkiveret', 'archived_quote' => 'Tilbud arkiveret',
'archived_quotes' => 'Arkiverede :count tilbud', 'archived_quotes' => 'Arkiverede :count tilbud',
'deleted_quote' => 'Tilbud slettet', 'deleted_quote' => 'Tilbud slettet',
'deleted_quotes' => 'Slettet :count tilbud', 'deleted_quotes' => 'Slettede :count tilbud',
'converted_to_invoice' => 'Tilbud konverteret til faktura', 'converted_to_invoice' => 'Tilbud konverteret til faktura',
'quote_subject' => 'Nyt tilbud fra :account', 'quote_subject' => 'Nyt tilbud fra :account',
'quote_message' => 'For å se dit tilbud pålydende :amount, klik på linket nedenfor.', 'quote_message' => 'For at se dit tilbud pålydende :amount, klik på linket nedenfor.',
'quote_link_message' => 'Hvis du vil se din klients tilbud, klik på linket under:', 'quote_link_message' => 'Hvis du vil se din klients tilbud, klik på linket under:',
'notification_quote_sent_subject' => 'Tilbud :invoice sendt til :client', 'notification_quote_sent_subject' => 'Tilbud :invoice sendt til :client',
'notification_quote_viewed_subject' => 'Tilbudet :invoice er set af :client', 'notification_quote_viewed_subject' => 'Tilbudet :invoice er set af :client',
@ -403,7 +400,7 @@ return array(
'session_expired' => 'Session er udløbet.', 'session_expired' => 'Session er udløbet.',
'invoice_fields' => 'Faktura felt', 'invoice_fields' => 'Faktura felt',
'invoice_options' => 'Faktura alternativer', 'invoice_options' => 'Faktura indstillinger',
'hide_quantity' => 'Skjul antal', 'hide_quantity' => 'Skjul antal',
'hide_quantity_help' => 'Hvis du altid kun har 1 som antal på dine fakturalinjer på fakturaen, kan du vælge ikke at vise antal på fakturaen.', 'hide_quantity_help' => 'Hvis du altid kun har 1 som antal på dine fakturalinjer på fakturaen, kan du vælge ikke at vise antal på fakturaen.',
'hide_paid_to_date' => 'Skjul delbetalinger', 'hide_paid_to_date' => 'Skjul delbetalinger',
@ -415,26 +412,26 @@ return array(
'send_invite' => 'Send invitation', 'send_invite' => 'Send invitation',
'sent_invite' => 'Invitation sendt', 'sent_invite' => 'Invitation sendt',
'updated_user' => 'Bruger opdateret', 'updated_user' => 'Bruger opdateret',
'invitation_message' => 'Du har blevet inviteret af :invitor. ', 'invitation_message' => 'Du er blevet inviteret af :invitor. ',
'register_to_add_user' => 'Venligst registrer dig for at oprette en bruger', 'register_to_add_user' => 'Du skal registrer dig for at oprette en bruger',
'user_state' => 'Status', 'user_state' => 'Status',
'edit_user' => 'Rediger bruger', 'edit_user' => 'Rediger bruger',
'delete_user' => 'Slet bruger', 'delete_user' => 'Slet bruger',
'active' => 'Aktiv', 'active' => 'Aktiv',
'pending' => 'Afventer', 'pending' => 'Afventer',
'deleted_user' => 'Bruger slettet', 'deleted_user' => 'Bruger slettet',
'limit_users' => 'Desværre, vil dette overstiger grænsen på ' . MAX_NUM_USERS . ' brugere', 'limit_users' => 'Desværre, dette vil overstige grænsen på ' . MAX_NUM_USERS . ' brugere',
'confirm_email_invoice' => 'Er du sikker på at du vil sende denne faktura?', 'confirm_email_invoice' => 'Er du sikker på at du vil sende en e-mail med denne faktura?',
'confirm_email_quote' => 'Er du sikker på du ville sende dette tilbud?', 'confirm_email_quote' => 'Er du sikker på du ville sende en e-mail med dette tilbud?',
'confirm_recurring_email_invoice' => 'Gentagende er slået til, er du sikker på du vil have denne faktura sendt?', 'confirm_recurring_email_invoice' => 'Gentagende er slået til, er du sikker på du sende en e-mail med denne faktura?',
'cancel_account' => 'Annuller konto', 'cancel_account' => 'Annuller konto',
'cancel_account_message' => 'Advarsel: Dette ville slette alle dine data og kan ikke omgøres', 'cancel_account_message' => 'Advarsel: Dette ville slette alle dine data og kan ikke fortrydes',
'go_back' => 'Go Back', 'go_back' => 'Gå tilbage',
'data_visualizations' => 'Data visualisering', 'data_visualizations' => 'Data visualisering',
'sample_data' => 'Eksempel data vis', 'sample_data' => 'Eksempel data vist',
'hide' => 'Skjul', 'hide' => 'Skjul',
'new_version_available' => 'En ny version af :releases_link er tilgængelig. Din nuværende version er v:user_version, den nyeste version er v:latest_version', 'new_version_available' => 'En ny version af :releases_link er tilgængelig. Din nuværende version er v:user_version, den nyeste version er v:latest_version',
@ -445,268 +442,318 @@ return array(
'quote_number_counter' => 'Tilbuds nummer tæller', 'quote_number_counter' => 'Tilbuds nummer tæller',
'share_invoice_counter' => 'Del faktura nummer tæller', 'share_invoice_counter' => 'Del faktura nummer tæller',
'invoice_issued_to' => 'Faktura udstedt til', 'invoice_issued_to' => 'Faktura udstedt til',
'invalid_counter' => 'For at undgå mulige overlap, sæt venligst et faktura eller tilbuds nummer præfiks', 'invalid_counter' => 'For at undgå mulige overlap, sæt et faktura eller tilbuds nummer præfiks',
'mark_sent' => 'Marker som sendt', 'mark_sent' => 'Markér som sendt',
'gateway_help_1' => ':link to sign up for Authorize.net.', 'gateway_help_1' => ':link til at registrere dig hos Authorize.net.',
'gateway_help_2' => ':link to sign up for Authorize.net.', 'gateway_help_2' => ':link til at registrere dig hos Authorize.net.',
'gateway_help_17' => ':link to get your PayPal API signature.', 'gateway_help_17' => ':link til at hente din PayPal API signatur.',
'gateway_help_23' => 'Note: use your secret API key, not your publishable API key.', 'gateway_help_23' => 'Note: brug din hemmelige API nøgle, IKKE din publicerede API nøgle.',
'gateway_help_27' => ':link to sign up for TwoCheckout.', 'gateway_help_27' => ':link til at registrere dig hos TwoCheckout.',
'more_designs' => 'More designs', 'more_designs' => 'Flere designs',
'more_designs_title' => 'Additional Invoice Designs', 'more_designs_title' => 'Yderligere faktura designs',
'more_designs_cloud_header' => 'Go Pro for more invoice designs', 'more_designs_cloud_header' => 'Skift til Pro for flere faktura designs',
'more_designs_cloud_text' => '', 'more_designs_cloud_text' => '',
'more_designs_self_host_header' => 'Get 6 more invoice designs for just $'.INVOICE_DESIGNS_PRICE, 'more_designs_self_host_header' => 'Få 6 flere faktura designs for kun $'.INVOICE_DESIGNS_PRICE,
'more_designs_self_host_text' => '', 'more_designs_self_host_text' => '',
'buy' => 'Buy', 'buy' => 'Køb',
'bought_designs' => 'Successfully added additional invoice designs', 'bought_designs' => 'Yderligere faktura designs tilføjet',
'sent' => 'sendt',
'sent' => 'sent', 'vat_number' => 'SE/CVR nummer',
'timesheets' => 'Timesheets', 'timesheets' => 'Timesedler',
'payment_title' => 'Enter Your Billing Address and Credit Card information', 'payment_title' => 'Indtast din faktura adresse og kreditkort information',
'payment_cvv' => '*This is the 3-4 digit number onthe back of your card', 'payment_cvv' => '*Dette er det 3-4 cifrede nummer på bagsiden af dit kort',
'payment_footer1' => '*Billing address must match address associated with credit card.', 'payment_footer1' => '*Faktura adressen skal matche den der er tilknyttet kortet.',
'payment_footer2' => '*Please click "PAY NOW" only once - transaction may take up to 1 minute to process.', 'payment_footer2' => '*Klik kun på "Betal Nu" én gang - transaktionen kan tage helt op til 1 minut inden den er færdig.',
'id_number' => 'ID Number', 'id_number' => 'SE/CVR nummer',
'white_label_link' => 'White label', 'white_label_link' => 'Hvid Label',
'white_label_text' => 'Purchase a white label license for $'.WHITE_LABEL_PRICE.' to remove the Invoice Ninja branding from the top of the client pages.', 'white_label_text' => 'Køb en Hvid Label licens til $'.WHITE_LABEL_PRICE.' for at fjerne Invoice Ninja mærket fra toppen af klient siderne.',
'white_label_header' => 'White Label', 'white_label_header' => 'Hvid Label',
'bought_white_label' => 'Successfully enabled white label license', 'bought_white_label' => 'Hvid Label licens accepteret',
'white_labeled' => 'White labeled', 'white_labeled' => 'Hvid Label',
'restore' => 'Restore', 'restore' => 'Genskab',
'restore_invoice' => 'Restore Invoice', 'restore_invoice' => 'Genskab faktura',
'restore_quote' => 'Restore Quote', 'restore_quote' => 'Genskab tilbud',
'restore_client' => 'Restore Client', 'restore_client' => 'Genskab Klient',
'restore_credit' => 'Restore Credit', 'restore_credit' => 'Genskab Kredit',
'restore_payment' => 'Restore Payment', 'restore_payment' => 'Genskab Betaling',
'restored_invoice' => 'Successfully restored invoice', 'restored_invoice' => 'Faktura genskabt',
'restored_quote' => 'Successfully restored quote', 'restored_quote' => 'Tilbud genskabt',
'restored_client' => 'Successfully restored client', 'restored_client' => 'Klient genskabt',
'restored_payment' => 'Successfully restored payment', 'restored_payment' => 'Betaling genskabt',
'restored_credit' => 'Successfully restored credit', 'restored_credit' => 'Kredit genskabt',
'reason_for_canceling' => 'Help us improve our site by telling us why you\'re leaving.', 'reason_for_canceling' => 'Hjælp os med at blive bedre ved at fortælle os hvorfor du forlader os.',
'discount_percent' => 'Percent', 'discount_percent' => 'Procent',
'discount_amount' => 'Amount', 'discount_amount' => 'Beløb',
'invoice_history' => 'Invoice History', 'invoice_history' => 'Faktura historik',
'quote_history' => 'Quote History', 'quote_history' => 'Tilbuds historik',
'current_version' => 'Current version', 'current_version' => 'Nuværende version',
'select_versiony' => 'Select version', 'select_versiony' => 'Vælg version',
'view_history' => 'View History', 'view_history' => 'Vis historik',
'edit_payment' => 'Edit Payment', 'edit_payment' => 'Redigér betaling',
'updated_payment' => 'Successfully updated payment', 'updated_payment' => 'Betaling opdateret',
'deleted' => 'Deleted', 'deleted' => 'Slettet',
'restore_user' => 'Restore User', 'restore_user' => 'Genskab bruger',
'restored_user' => 'Successfully restored user', 'restored_user' => 'Bruger genskabt',
'show_deleted_users' => 'Show deleted users', 'show_deleted_users' => 'Vis slettede brugere',
'email_templates' => 'Email Templates', 'email_templates' => 'E-mail skabeloner',
'invoice_email' => 'Invoice Email', 'invoice_email' => 'Faktura e-mail',
'payment_email' => 'Payment Email', 'payment_email' => 'Betalings e-mail',
'quote_email' => 'Quote Email', 'quote_email' => 'Tilbuds e-mail',
'reset_all' => 'Reset All', 'reset_all' => 'Nulstil alle',
'approve' => 'Approve', 'approve' => 'Godkend',
'token_billing_type_id' => 'Token Billing', 'token_billing_type_id' => 'Tokensk Fakturering',
'token_billing_help' => 'Enables you to store credit cards with your gateway, and charge them at a later date.', 'token_billing_help' => 'Tillader dig at gemme kredit kort oplysninger, for at kunne fakturere dem senere.',
'token_billing_1' => 'Disabled', 'token_billing_1' => 'Slukket',
'token_billing_2' => 'Opt-in - checkbox is shown but not selected', 'token_billing_2' => 'Tilvalg - checkboks er vist men ikke valgt',
'token_billing_3' => 'Opt-out - checkbox is shown and selected', 'token_billing_3' => 'Fravalg - checkboks er vist og valgt',
'token_billing_4' => 'Always', 'token_billing_4' => 'Altid',
'token_billing_checkbox' => 'Store credit card details', 'token_billing_checkbox' => 'Opbevar kreditkort oplysninger',
'view_in_stripe' => 'View in Stripe', 'view_in_stripe' => 'Vis i Stripe ',
'use_card_on_file' => 'Use card on file', 'use_card_on_file' => 'Brug allerede gemt kort',
'edit_payment_details' => 'Edit payment details', 'edit_payment_details' => 'Redigér betalings detaljer',
'token_billing' => 'Save card details', 'token_billing' => 'Gem kort detaljer',
'token_billing_secure' => 'The data is stored securely by :stripe_link', 'token_billing_secure' => 'Kort data er opbevaret sikkert hos :stripe_link',
'support' => 'Support', 'support' => 'Kundeservice',
'contact_information' => 'Contact information', 'contact_information' => 'Kontakt information',
'256_encryption' => '256-Bit Encryption', '256_encryption' => '256-Bit Kryptering',
'amount_due' => 'Amount due', 'amount_due' => 'Beløb forfaldent',
'billing_address' => 'Billing address', 'billing_address' => 'Faktura adresse',
'billing_method' => 'Billing method', 'billing_method' => 'Faktura metode',
'order_overview' => 'Order overview', 'order_overview' => 'Ordre oversigt',
'match_address' => '*Address must match address associated with credit card.', 'match_address' => '*Adressen skal matche det tilknyttede kredit kort.',
'click_once' => '*Please click "PAY NOW" only once - transaction may take up to 1 minute to process.', 'click_once' => '**Klik kun på "Betal Nu" én gang - transaktionen kan tage helt op til 1 minut inden den er færdig.',
'default_invoice_footer' => 'Set default invoice footer', 'default_invoice_footer' => 'Sæt standard faktura fodnoter',
'invoice_footer' => 'Invoice footer', 'invoice_footer' => 'Faktura fodnoter',
'save_as_default_footer' => 'Save as default footer', 'save_as_default_footer' => 'Gem som standard fodnoter',
'token_management' => 'Token Management', 'token_management' => 'Token Administration',
'tokens' => 'Tokens', 'tokens' => 'Token\'s',
'add_token' => 'Add Token', 'add_token' => 'Tilføj token',
'show_deleted_tokens' => 'Show deleted tokens', 'show_deleted_tokens' => 'Vis slettede token\'s',
'deleted_token' => 'Successfully deleted token', 'deleted_token' => 'Token slettet',
'created_token' => 'Successfully created token', 'created_token' => 'Token oprettet',
'updated_token' => 'Successfully updated token', 'updated_token' => 'Token opdateret',
'edit_token' => 'Edit Token', 'edit_token' => 'Redigér token',
'delete_token' => 'Delete Token', 'delete_token' => 'Slet token',
'token' => 'Token', 'token' => 'Token',
'add_gateway' => 'Add Gateway', 'add_gateway' => 'Tilføj betalings portal',
'delete_gateway' => 'Delete Gateway', 'delete_gateway' => 'Slet betalings portal',
'edit_gateway' => 'Edit Gateway', 'edit_gateway' => 'Redigér betalings portal',
'updated_gateway' => 'Successfully updated gateway', 'updated_gateway' => 'Betalings portal opdateret',
'created_gateway' => 'Successfully created gateway', 'created_gateway' => 'Betalings portal oprettet',
'deleted_gateway' => 'Successfully deleted gateway', 'deleted_gateway' => 'Betalings portal slettet',
'pay_with_paypal' => 'PayPal', 'pay_with_paypal' => 'PayPal',
'pay_with_card' => 'Credit card', 'pay_with_card' => 'Kredit kort',
'change_password' => 'Change password', 'change_password' => 'Skift adgangskode',
'current_password' => 'Current password', 'current_password' => 'Nuværende adgangskode',
'new_password' => 'New password', 'new_password' => 'Ny adgangskode',
'confirm_password' => 'Confirm password', 'confirm_password' => 'Bekræft adgangskode',
'password_error_incorrect' => 'The current password is incorrect.', 'password_error_incorrect' => 'Den nuværende adgangskode er forkert.',
'password_error_invalid' => 'The new password is invalid.', 'password_error_invalid' => 'Den nye adgangskode er ugyldig.',
'updated_password' => 'Successfully updated password', 'updated_password' => 'Adgangskode opdateret',
'api_tokens' => 'API Tokens', 'api_tokens' => 'API Token\'s',
'users_and_tokens' => 'Users & Tokens', 'users_and_tokens' => 'Brugere & Token\'s',
'account_login' => 'Account Login', 'account_login' => 'Konto Log ind',
'recover_password' => 'Recover your password', 'recover_password' => 'Generhverv din adgangskode',
'forgot_password' => 'Forgot your password?', 'forgot_password' => 'Glemt din adgangskode?',
'email_address' => 'Email address', 'email_address' => 'E-mail adresse',
'lets_go' => 'Lets go', 'lets_go' => 'Og så afsted',
'password_recovery' => 'Password Recovery', 'password_recovery' => 'Adgangskode Generhvervelse',
'send_email' => 'Send email', 'send_email' => 'Send e-mail',
'set_password' => 'Set Password', 'set_password' => 'Sæt adgangskode',
'converted' => 'Converted', 'converted' => 'Konverteret',
'email_approved' => 'Email me when a quote is <b>approved</b>', 'email_approved' => 'E-mail mig når et tilbud er <b>accepteret</b>',
'notification_quote_approved_subject' => 'Quote :invoice was approved by :client', 'notification_quote_approved_subject' => 'Tilbudet :invoice blev accepteret af :client',
'notification_quote_approved' => 'The following client :client approved Quote :invoice for :amount.', 'notification_quote_approved' => 'Den følgende klient :client accepterede tilbud :invoice lydende på :amount.',
'resend_confirmation' => 'Resend confirmation email', 'resend_confirmation' => 'Send bekræftelses e-mail igen',
'confirmation_resent' => 'The confirmation email was resent', 'confirmation_resent' => 'Bekræftelses e-mail er afsendt',
'gateway_help_42' => ':link to sign up for BitPay.<br/>Note: use a Legacy API Key, not an API token.', 'gateway_help_42' => ':link til registrering hos BitPay.<br/>Bemærk: brug en use a Legacy API nøgle, og ikke en API token.',
'payment_type_credit_card' => 'Credit card', 'payment_type_credit_card' => 'Kredit kort',
'payment_type_paypal' => 'PayPal', 'payment_type_paypal' => 'PayPal',
'payment_type_bitcoin' => 'Bitcoin', 'payment_type_bitcoin' => 'Bitcoin',
'knowledge_base' => 'Knowledge Base', 'knowledge_base' => 'Vidensbase',
'partial' => 'Partial', 'partial' => 'Delvis',
'partial_remaining' => ':partial of :balance', 'partial_remaining' => ':partial af :balance',
'more_fields' => 'More Fields', 'more_fields' => 'Flere felter',
'less_fields' => 'Less Fields', 'less_fields' => 'Mindre felter',
'client_name' => 'Client Name', 'client_name' => 'Klient navn',
'pdf_settings' => 'PDF Settings', 'pdf_settings' => 'PDF Indstillinger',
'utf8_invoices' => 'Cyrillic Support <sup>Beta</sup>', 'utf8_invoices' => 'Ny PDF driver <sup>Beta</sup>',
'product_settings' => 'Product Settings', 'product_settings' => 'Produkt Indstillinger',
'auto_wrap' => 'Auto Line Wrap', 'auto_wrap' => 'Automatisk linie ombrydning',
'duplicate_post' => 'Warning: the previous page was submitted twice. The second submission had been ignored.', 'duplicate_post' => 'Advarsel: den foregående side blev sendt to gange. Den anden afsendelse er blevet ignoreret.',
'view_documentation' => 'View Documentation', 'view_documentation' => 'Vis dokumentation',
'app_title' => 'Free Open-Source Online Invoicing', 'app_title' => 'Gratis Open-Source Online fakturering',
'app_description' => 'Invoice Ninja is a free, open-source solution for invoicing and billing customers. With Invoice Ninja, you can easily build and send beautiful invoices from any device that has access to the web. Your clients can print your invoices, download them as pdf files, and even pay you online from within the system.', 'app_description' => 'Invoice Ninja er en gratis, open-source løsning til at håndtere fakturering og betaling af dine kunder. Med Invoice Ninja, kan du let generere og sende smukke fakturaer fra et hvilket som helst udstyr der har adgang til internettet. Dine klienter kan udskrive dine fakturarer, hente dem som PDF filer, og endda betale dem on-line inde fra Invoice Ninja.',
'rows' => 'rows', 'rows' => 'rækker',
'www' => 'www', 'www' => 'www',
'logo' => 'Logo', 'logo' => 'Logo',
'subdomain' => 'Subdomain', 'subdomain' => 'Underdomain',
'provide_name_or_email' => 'Please provide a contact name or email', 'provide_name_or_email' => 'Opgiv et kontakt navn eller e-mail adresse',
'charts_and_reports' => 'Charts & Reports', 'charts_and_reports' => 'Diagrammer & Rapporter',
'chart' => 'Chart', 'chart' => 'Diagram',
'report' => 'Report', 'report' => 'Rapport',
'group_by' => 'Group by', 'group_by' => 'Gruppér efter',
'paid' => 'Paid', 'paid' => 'Betalt',
'enable_report' => 'Report', 'enable_report' => 'Rapport',
'enable_chart' => 'Chart', 'enable_chart' => 'Diagram',
'totals' => 'Totals', 'totals' => 'Totaler',
'run' => 'Run', 'run' => 'Kør',
'export' => 'Export', 'export' => 'Eksport',
'documentation' => 'Documentation', 'documentation' => 'Dokumentation',
'zapier' => 'Zapier <sup>Beta</sup>', 'zapier' => 'Zapier <sup>Beta</sup>',
'recurring' => 'Recurring', 'recurring' => 'Gentagne',
'last_invoice_sent' => 'Last invoice sent :date', 'last_invoice_sent' => 'Sidste faktura sendt :date',
'processed_updates' => 'Successfully completed update', 'processed_updates' => 'Opdatering gennemført',
'tasks' => 'Tasks', 'tasks' => 'Opogaver',
'new_task' => 'New Task', 'new_task' => 'Ny opgave',
'start_time' => 'Start Time', 'start_time' => 'Start Tidspunkt',
'created_task' => 'Successfully created task', 'created_task' => 'Opgave oprettet',
'updated_task' => 'Successfully updated task', 'updated_task' => 'Opgave opdateret',
'edit_task' => 'Edit Task', 'edit_task' => 'Redigér opgave',
'archive_task' => 'Archive Task', 'archive_task' => 'Arkiver opgave',
'restore_task' => 'Restore Task', 'restore_task' => 'Genskab opgave',
'delete_task' => 'Delete Task', 'delete_task' => 'Slet opgave',
'stop_task' => 'Stop Task', 'stop_task' => 'Stop opgave',
'time' => 'Time', 'time' => 'Tid',
'start' => 'Start', 'start' => 'Start',
'stop' => 'Stop', 'stop' => 'Stop',
'now' => 'Now', 'now' => 'Nu',
'timer' => 'Timer', 'timer' => 'Tidtager',
'manual' => 'Manual', 'manual' => 'Manuelt',
'date_and_time' => 'Date & Time', 'date_and_time' => 'Dato & Tid',
'second' => 'second', 'second' => 'sekund',
'seconds' => 'seconds', 'seconds' => 'sekunder',
'minute' => 'minute', 'minute' => 'minut',
'minutes' => 'minutes', 'minutes' => 'minutter',
'hour' => 'hour', 'hour' => 'time',
'hours' => 'hours', 'hours' => 'timer',
'task_details' => 'Task Details', 'task_details' => 'Opgave detaljer',
'duration' => 'Duration', 'duration' => 'Varighed',
'end_time' => 'End Time', 'end_time' => 'Slut tidspunkt',
'end' => 'End', 'end' => 'Slut',
'invoiced' => 'Invoiced', 'invoiced' => 'Faktureret',
'logged' => 'Logged', 'logged' => 'Ajourført',
'running' => 'Running', 'running' => 'Kører',
'task_error_multiple_clients' => 'The tasks can\'t belong to different clients', 'task_error_multiple_clients' => 'Opgaverne kan ikke tilhøre forskellige klienter',
'task_error_running' => 'Please stop running tasks first', 'task_error_running' => 'Stop alle kørende opgaver først',
'task_error_invoiced' => 'Tasks have already been invoiced', 'task_error_invoiced' => 'Opgaver er allerede faktureret',
'restored_task' => 'Successfully restored task', 'restored_task' => 'Opgave genskabt',
'archived_task' => 'Successfully archived task', 'archived_task' => 'Opgave arkiveret',
'archived_tasks' => 'Successfully archived :count tasks', 'archived_tasks' => 'Antal arkiverede opgaver: :count',
'deleted_task' => 'Successfully deleted task', 'deleted_task' => 'Opgave slettet',
'deleted_tasks' => 'Successfully deleted :count tasks', 'deleted_tasks' => 'Antal opgaver slettet: :count',
'create_task' => 'Create Task', 'create_task' => 'Opret opgave',
'stopped_task' => 'Successfully stopped task', 'stopped_task' => 'Opgave stoppet',
'invoice_task' => 'Invoice Task', 'invoice_task' => 'Fakturer opgave',
'invoice_labels' => 'Invoice Labels', 'invoice_labels' => 'Faktura labels',
'prefix' => 'Prefix', 'prefix' => 'Præfix',
'counter' => 'Counter', 'counter' => 'Tæller',
'payment_type_dwolla' => 'Dwolla', 'payment_type_dwolla' => 'Dwolla',
'gateway_help_43' => ':link to sign up for Dwolla.', 'gateway_help_43' => ':link til at registrere dig hos Dwolla.',
'partial_value' => 'Must be greater than zero and less than the total', 'partial_value' => 'Skal være større end nul og mindre end totalen',
'more_actions' => 'More Actions', 'more_actions' => 'Flere handlinger',
'pro_plan_title' => 'NINJA PRO', 'pro_plan_title' => 'NINJA PRO',
'pro_plan_call_to_action' => 'Upgrade Now!', 'pro_plan_call_to_action' => 'Opgradér Nu!',
'pro_plan_feature1' => 'Create Unlimited Clients', 'pro_plan_feature1' => 'Opret ubegrænset antal klienter',
'pro_plan_feature2' => 'Access to 10 Beautiful Invoice Designs', 'pro_plan_feature2' => 'Adgang til 10 smukke faktura designs',
'pro_plan_feature3' => 'Custom URLs - "YourBrand.InvoiceNinja.com"', 'pro_plan_feature3' => 'Tilpasset URL - "YourBrand.InvoiceNinja.com"',
'pro_plan_feature4' => 'Remove "Created by Invoice Ninja"', 'pro_plan_feature4' => 'Fjern "Created by Invoice Ninja"',
'pro_plan_feature5' => 'Multi-user Access & Activity Tracking', 'pro_plan_feature5' => 'Multi bruger adgang og aktivitets log',
'pro_plan_feature6' => 'Create Quotes & Pro-forma Invoices', 'pro_plan_feature6' => 'Opret tilbud og proforma fakturaer',
'pro_plan_feature7' => 'Customize Invoice Field Titles & Numbering', 'pro_plan_feature7' => 'Brugerdefinerede faktura felt titler og nummerering',
'pro_plan_feature8' => 'Option to Attach PDFs to Client Emails', 'pro_plan_feature8' => 'Mulighed for at vedhæfte PDF filer til klient e-mails',
'resume' => 'Resume', 'resume' => 'Genoptag',
'break_duration' => 'Break', 'break_duration' => 'Pause',
'edit_details' => 'Edit Details', 'edit_details' => 'Rediger detaljer',
'work' => 'Work', 'work' => 'Arbejde',
'timezone_unset' => 'Please :link to set your timezone', 'timezone_unset' => 'Indstil :link din tidszone',
'click_here' => 'click here', 'click_here' => 'Klik her',
'email_receipt' => 'Email payment receipt to the client', 'email_receipt' => 'Send e-mail kvittering til klienten',
'created_payment_emailed_client' => 'Successfully created payment and emailed client', 'created_payment_emailed_client' => 'Betaling modtaget og e-mail kvittering sendt til klienten',
'add_account' => 'Add Account', 'add_account' => 'Tilføj konto',
'untitled' => 'Untitled', 'untitled' => 'Ingen titel',
'new_account' => 'New Account', 'new_account' => 'Ny konto',
'associated_accounts' => 'Successfully linked accounts', 'associated_accounts' => 'Konti sammenkædet',
'unlinked_account' => 'Successfully unlinked accounts', 'unlinked_account' => 'Sammenkædning af konti ophævet',
'login' => 'Login', 'login' => 'Log ind',
'or' => 'or', 'or' => 'eller',
'email_error' => 'Der opstod et problem ved afsendelse af e-mailen',
'created_by_recurring' => 'Oprettet af gentaget faktura nr.: :invoice',
'confirm_recurring_timing' => 'Bemærk: e-mail bliver sendt i starten af timen.',
'old_browser' => 'Din browser er registreret som værende af ældre dato og den vil ikke kunne bruges, skift til en <a href="'.OUTDATE_BROWSER_URL.'" target="_blank">nyere version browser</a>',
'payment_terms_help' => 'Sætter standard fakturaens forfalds dato',
'unlink_account' => 'Fjern sammenkædning af konti',
'unlink' => 'Fjern sammenkædning',
'show_address' => 'Vis adresse',
'show_address_help' => 'Kræv at klienten angiver deres fakturerings adresse',
'update_address' => 'Opdater adresse',
'update_address_help' => 'Opdater klientens adresse med de opgivne detaljer',
'times' => 'Gange',
'set_now' => 'Sæt nu',
'dark_mode' => 'Mørk tilstand',
'dark_mode_help' => 'Vis hvid tekst på sort baggrund',
'add_to_invoice' => 'Tilføj til faktura nr.: :invoice',
'create_new_invoice' => 'Opret ny faktura',
'task_errors' => 'Ret venligst de overlappende tider',
'from' => 'Fra',
'to' => 'Til',
'font_size' => 'Font Størrelse',
'primary_color' => 'Primær Farve',
'secondary_color' => 'Sekundær Farve',
'customize_design' => 'Tilpas Design',
'content' => 'Indhold',
'styles' => 'Stilarter',
'defaults' => 'Standarder',
'margins' => 'Marginer',
'header' => 'Hoved',
'footer' => 'Fodnote',
'custom' => 'Brugertilpasset',
'invoice_to' => 'Fakturer til',
'invoice_no' => 'Faktura Nr.',
'recent_payments' => 'Nylige betalinger',
'outstanding' => 'Forfaldne',
'manage_companies' => 'Manage Companies',
'total_revenue' => 'Total Revenue',
'current_user' => 'Nuværende bruger',
'new_recurring_invoice' => 'Ny gentaget fakture',
'recurring_invoice' => 'Gentaget faktura',
'recurring_too_soon' => 'Det er for tidligt at generere den næste faktura',
'created_by_invoice' => 'Oprettet fra :invoice',
'primary_user' => 'Primær bruger',
'help' => 'Hjælp',
'customize_help' => '<p>Vi bruger <a href="http://pdfmake.org/" target="_blank">pdfmake</a> til at definere faktura design felter. pdfmake <a href="http://pdfmake.org/playground.html" target="_blank">legeplads</a> giver en god mulighed for at se biblioteket i aktion.</p>
<p>Du kan tilgå alle faktura felter ved at tilføje <code>Value</code> til slutningen. For eksempel viser <code>$invoiceNumberValue</code> fakturanummeret.</p>
<p>For at tilgå under indstillingerne ved hjælp af dot notation. For eksempel kan man for at vise klient navnet bruge <code>$client.nameValue</code>.</p>
<p>Hvis du mangler svar nogen spørgsmål post et spørgsmål i vores <a href="https://www.invoiceninja.com/forums/forum/support/" target="_blank">support forum</a>.</p>',
); );

View File

@ -31,7 +31,7 @@ return array(
'client' => 'Kunde', 'client' => 'Kunde',
'invoice_date' => 'Rechnungsdatum', 'invoice_date' => 'Rechnungsdatum',
'due_date' => 'Fällig am', 'due_date' => 'Fällig am',
'invoice_number' => 'Rechungsnummer', 'invoice_number' => 'Rechnungsnummer',
'invoice_number_short' => 'Rechnung #', 'invoice_number_short' => 'Rechnung #',
'po_number' => 'Bestellnummer', 'po_number' => 'Bestellnummer',
'po_number_short' => 'BN #', 'po_number_short' => 'BN #',
@ -46,7 +46,7 @@ return array(
'line_total' => 'Summe', 'line_total' => 'Summe',
'subtotal' => 'Zwischensumme', 'subtotal' => 'Zwischensumme',
'paid_to_date' => 'Bereits gezahlt', 'paid_to_date' => 'Bereits gezahlt',
'balance_due' => 'Rechnungsbetrag', 'balance_due' => 'Geschuldeter Betrag',
'invoice_design_id' => 'Design', 'invoice_design_id' => 'Design',
'terms' => 'Bedingungen', 'terms' => 'Bedingungen',
'your_invoice' => 'Ihre Rechnung', 'your_invoice' => 'Ihre Rechnung',
@ -96,7 +96,8 @@ return array(
'import' => 'Importieren', 'import' => 'Importieren',
'download' => 'Downloaden', 'download' => 'Downloaden',
'cancel' => 'Abbrechen', 'cancel' => 'Abbrechen',
'provide_email' => 'Bitte gib eine gültige E-Mail Adresse an', 'close' => 'Schließen',
'provide_email' => 'Bitte gib eine gültige E-Mail-Adresse an',
'powered_by' => 'Powered by', 'powered_by' => 'Powered by',
'no_items' => 'Keine Objekte', 'no_items' => 'Keine Objekte',
@ -106,7 +107,7 @@ return array(
<p>Benutze :MONTH, :QUARTER oder :YEAR für ein dynamisches Datum. Grundlegende Mathematik funktioniert genauso gut, zum Beispiel :MONTH-1.</p> <p>Benutze :MONTH, :QUARTER oder :YEAR für ein dynamisches Datum. Grundlegende Mathematik funktioniert genauso gut, zum Beispiel :MONTH-1.</p>
<p>Beispiel zu dynamischen Rechnungs-Variabeln:</p> <p>Beispiel zu dynamischen Rechnungs-Variabeln:</p>
<ul> <ul>
<li>"Fitnessstudio Mitgliedschaft für den Monat :MONTH" => "Fitnessstudio Mitgliedschaft für den Monat Juli"</li> <li>"Fitnessstudio-Mitgliedschaft für den Monat :MONTH" => "Fitnessstudio-Mitgliedschaft für den Monat Juli"</li>
<li>":YEAR+1 Jahresbeitrag" => "2015 Jahresbeitrag"</li> <li>":YEAR+1 Jahresbeitrag" => "2015 Jahresbeitrag"</li>
<li>"Vorschusszahlung für :QUARTER+1" => "Vorschusszahlung für Q2"</li> <li>"Vorschusszahlung für :QUARTER+1" => "Vorschusszahlung für Q2"</li>
</ul>', </ul>',
@ -205,7 +206,7 @@ return array(
'import_to' => 'Importieren nach', 'import_to' => 'Importieren nach',
'client_will_create' => 'Kunde wird erstellt', 'client_will_create' => 'Kunde wird erstellt',
'clients_will_create' => 'Kunden werden erstellt', 'clients_will_create' => 'Kunden werden erstellt',
'email_settings' => 'E-Mail Einstellungen', 'email_settings' => 'E-Mail-Einstellungen',
'pdf_email_attachment' => 'PDF an E-Mails anhängen', 'pdf_email_attachment' => 'PDF an E-Mails anhängen',
// application messages // application messages
@ -218,7 +219,7 @@ return array(
'limit_clients' => 'Entschuldige, das überschreitet das Limit von :count Kunden', 'limit_clients' => 'Entschuldige, das überschreitet das Limit von :count Kunden',
'payment_error' => 'Es ist ein Fehler während der Zahlung aufgetreten. Bitte versuche es später noch einmal.', 'payment_error' => 'Es ist ein Fehler während der Zahlung aufgetreten. Bitte versuche es später noch einmal.',
'registration_required' => 'Bitte melde dich an um eine Rechnung zu versenden', 'registration_required' => 'Bitte melde dich an um eine Rechnung zu versenden',
'confirmation_required' => 'Bitte bestätige deine E-Mail Adresse', 'confirmation_required' => 'Bitte bestätige deine E-Mail-Adresse',
'updated_client' => 'Kunde erfolgreich aktualisiert', 'updated_client' => 'Kunde erfolgreich aktualisiert',
'created_client' => 'Kunde erfolgreich erstellt', 'created_client' => 'Kunde erfolgreich erstellt',
@ -254,13 +255,14 @@ return array(
'confirmation_subject' => 'InvoiceNinja Kontobestätigung', 'confirmation_subject' => 'InvoiceNinja Kontobestätigung',
'confirmation_header' => 'Kontobestätigung', 'confirmation_header' => 'Kontobestätigung',
'confirmation_message' => 'Bitte klicke auf den folgenden Link um dein Konto zu bestätigen.', 'confirmation_message' => 'Bitte klicke auf den folgenden Link um dein Konto zu bestätigen.',
'invoice_message' => 'Um Ihre Rechnung über :amount einzusehen, klicken Sie bitte auf den folgenden Link.', 'invoice_subject' => 'Neue Rechnung :invoice von :account',
'invoice_message' => 'Um Ihre Rechnung über :amount einzusehen, klicken Sie bitte auf den folgenden Link:',
'payment_subject' => 'Zahlungseingang', 'payment_subject' => 'Zahlungseingang',
'payment_message' => 'Vielen Dank für Ihre Zahlung von :amount.', 'payment_message' => 'Vielen Dank für Ihre Zahlung von :amount.',
'email_salutation' => 'Sehr geehrte/r :name,', 'email_salutation' => 'Sehr geehrte/r :name,',
'email_signature' => 'Mit freundlichen Grüßen,', 'email_signature' => 'Mit freundlichen Grüßen,',
'email_from' => 'Das InvoiceNinja Team', 'email_from' => 'Das InvoiceNinja Team',
'user_email_footer' => 'Um deine E-Mail Benachrichtigungen anzupassen besuche bitte '.SITE_URL.'/company/notifications', 'user_email_footer' => 'Um deine E-Mail-Benachrichtigungen anzupassen besuche bitte '.SITE_URL.'/company/notifications',
'invoice_link_message' => 'Um deine Kundenrechnung anzuschauen, klicke auf den folgenden Link:', 'invoice_link_message' => 'Um deine Kundenrechnung anzuschauen, klicke auf den folgenden Link:',
'notification_invoice_paid_subject' => 'Die Rechnung :invoice wurde von :client bezahlt', 'notification_invoice_paid_subject' => 'Die Rechnung :invoice wurde von :client bezahlt',
'notification_invoice_sent_subject' => 'Die Rechnung :invoice wurde an :client versendet', 'notification_invoice_sent_subject' => 'Die Rechnung :invoice wurde an :client versendet',
@ -269,7 +271,8 @@ return array(
'notification_invoice_sent' => 'Dem folgenden Kunden :client wurde die Rechnung :invoice über :amount zugesendet.', 'notification_invoice_sent' => 'Dem folgenden Kunden :client wurde die Rechnung :invoice über :amount zugesendet.',
'notification_invoice_viewed' => 'Der folgende Kunde :client hat sich Rechnung :invoice über :amount angesehen.', 'notification_invoice_viewed' => 'Der folgende Kunde :client hat sich Rechnung :invoice über :amount angesehen.',
'reset_password' => 'Du kannst dein Passwort zurücksetzen, indem du auf den folgenden Link klickst:', 'reset_password' => 'Du kannst dein Passwort zurücksetzen, indem du auf den folgenden Link klickst:',
'reset_password_footer' => 'Wenn du das Zurücksetzen des Passworts nicht beantragt hast benachrichtige bitte unseren Support: ' . CONTACT_EMAIL, 'reset_password_footer' => 'Wenn du das Zurücksetzen des Passworts nicht beantragt hast, benachrichtige bitte unseren Support: ' . CONTACT_EMAIL,
// Payment page // Payment page
'secure_payment' => 'Sichere Zahlung', 'secure_payment' => 'Sichere Zahlung',
@ -281,7 +284,7 @@ return array(
// Security alerts // Security alerts
'security' => array( 'security' => array(
'too_many_attempts' => 'Zu viele Versuche. Bitte probiere es in ein paar Minuten erneut.', 'too_many_attempts' => 'Zu viele Versuche. Bitte probiere es in ein paar Minuten erneut.',
'wrong_credentials' => 'Falsche E-Mail Adresse oder falsches Passwort.', 'wrong_credentials' => 'Falsche E-Mail-Adresse oder falsches Passwort.',
'confirmation' => 'Dein Konto wurde bestätigt!', 'confirmation' => 'Dein Konto wurde bestätigt!',
'wrong_confirmation' => 'Falscher Bestätigungscode.', 'wrong_confirmation' => 'Falscher Bestätigungscode.',
'password_forgot' => 'Weitere Informationen um das Passwort zurückzusetzen wurden dir per E-Mail zugeschickt.', 'password_forgot' => 'Weitere Informationen um das Passwort zurückzusetzen wurden dir per E-Mail zugeschickt.',
@ -291,7 +294,7 @@ return array(
// Pro Plan // Pro Plan
'pro_plan' => [ 'pro_plan' => [
'remove_logo' => ':link, um das Invoice Ninja Logo zu entfernen, indem du dem Pro Plan beitrittst', 'remove_logo' => ':link, um das InvoiceNinja-Logo zu entfernen, indem du dem Pro Plan beitrittst',
'remove_logo_link' => 'Klicke hier', 'remove_logo_link' => 'Klicke hier',
], ],
@ -299,19 +302,23 @@ return array(
'sign_up_to_save' => 'Melde dich an, um deine Arbeit zu speichern', 'sign_up_to_save' => 'Melde dich an, um deine Arbeit zu speichern',
'agree_to_terms' =>'Ich akzeptiere die InvoiceNinja :terms', 'agree_to_terms' =>'Ich akzeptiere die InvoiceNinja :terms',
'terms_of_service' => 'Service-Bedingungen', 'terms_of_service' => 'Service-Bedingungen',
'email_taken' => 'Diese E-Mail Adresse ist bereits registriert', 'email_taken' => 'Diese E-Mail-Adresse ist bereits registriert',
'working' => 'Wird bearbeitet', 'working' => 'Wird bearbeitet',
'success' => 'Erfolg', 'success' => 'Erfolg',
'success_message' => 'Du hast dich erfolgreich registriert. Bitte besuche den Link in deiner Bestätigungsmail um deine E-Mail Adresse zu verifizieren.', 'success_message' => 'Du hast dich erfolgreich registriert. Bitte besuche den Link in deiner Bestätigungsmail um deine E-Mail-Adresse zu verifizieren.',
'erase_data' => 'Diese Aktion wird deine Daten dauerhaft löschen.', 'erase_data' => 'Diese Aktion wird deine Daten dauerhaft löschen.',
'password' => 'Passwort', 'password' => 'Passwort',
'invoice_subject' => 'Neue Rechnung :invoice von :account',
'close' => 'Schließen', 'close' => 'Schließen',
'pro_plan_product' => 'Pro Plan', 'pro_plan_product' => 'Pro Plan',
'pro_plan_description' => 'Jahresmitgliedschaft beim Invoice Ninja Pro Plan.', 'pro_plan_description' => 'Jahresmitgliedschaft beim Invoice Ninja Pro Plan.',
'pro_plan_success' => 'Danke für den Beitritt! Sobald die Rechnung bezahlt wurde, beginnt deine Pro Plan Mitgliedschaft.', 'pro_plan_success' => 'Danke, dass Sie Invoice Ninja\'s Pro gewählt haben!<p/>&nbsp;<br/>
'pro_plan_success' => 'Danke für den Beitritt! Sobald die Rechnung bezahlt wurde,Beim Auswählen eines Produktes werden beginnt deine Pro Plan Mitgliedschaft.', <b>Nächste Schritte</b>Eine bezahlbare Rechnung wurde an die Mailadresse,
welche mit Ihrem Account verbunden ist, geschickt. Um alle der umfangreichen
Pro Funktionen freizuschalten, folgen Sie bitte den Anweisungen in der Rechnung um ein Jahr
die Pro Funktionen zu nutzen.
Sie finden die Rechnung nicht? Sie benötigen weitere Hilfe? Wir helfen gerne
-- schicken Sie uns doch eine Email an contact@invoice-ninja.com',
'unsaved_changes' => 'Es liegen ungespeicherte Änderungen vor', 'unsaved_changes' => 'Es liegen ungespeicherte Änderungen vor',
'custom_fields' => 'Benutzerdefinierte Felder', 'custom_fields' => 'Benutzerdefinierte Felder',
@ -320,6 +327,7 @@ return array(
'field_label' => 'Feldbezeichnung', 'field_label' => 'Feldbezeichnung',
'field_value' => 'Feldwert', 'field_value' => 'Feldwert',
'edit' => 'Bearbeiten', 'edit' => 'Bearbeiten',
'set_name' => 'Den Firmennamen setzen',
'view_as_recipient' => 'Als Empfänger betrachten', 'view_as_recipient' => 'Als Empfänger betrachten',
// product management // product management
@ -336,7 +344,7 @@ return array(
'created_product' => 'Produkt erfolgreich erstellt', 'created_product' => 'Produkt erfolgreich erstellt',
'archived_product' => 'Produkt erfolgreich archiviert', 'archived_product' => 'Produkt erfolgreich archiviert',
'product_library' => 'Produktbibliothek', 'product_library' => 'Produktbibliothek',
'pro_plan_custom_fields' => ':link to enable custom fields by joining the Pro Plan', 'pro_plan_custom_fields' => ':link um durch eine Pro-Mitgliedschaft erweiterte Felder zu aktivieren',
'advanced_settings' => 'Erweiterte Einstellungen', 'advanced_settings' => 'Erweiterte Einstellungen',
'pro_plan_advanced_settings' => ':link um durch eine Pro-Mitgliedschaft erweiterte Einstellungen zu aktivieren', 'pro_plan_advanced_settings' => ':link um durch eine Pro-Mitgliedschaft erweiterte Einstellungen zu aktivieren',
@ -346,7 +354,7 @@ return array(
'chart_builder' => 'Diagrammersteller', 'chart_builder' => 'Diagrammersteller',
'ninja_email_footer' => 'Nutze :site um Kunden Rechnungen zu stellen und online bezahlt zu werden, kostenlos!', 'ninja_email_footer' => 'Nutze :site um Kunden Rechnungen zu stellen und online bezahlt zu werden, kostenlos!',
'go_pro' => 'Werde Pro-Mitglied', 'go_pro' => 'Go Pro',
// Quotes // Quotes
'quote' => 'Angebot', 'quote' => 'Angebot',
@ -394,19 +402,19 @@ return array(
'invoice_fields' => 'Rechnungsfelder', 'invoice_fields' => 'Rechnungsfelder',
'invoice_options' => 'Rechnungsoptionen', 'invoice_options' => 'Rechnungsoptionen',
'hide_quantity' => 'Anzahl verbergen', 'hide_quantity' => 'Menge verbergen',
'hide_quantity_help' => 'Wenn deine Menge immer 1 beträgt, kannst du deine Rechnung einfach halten, indem du dieses Feld entfernst.', 'hide_quantity_help' => 'Wenn deine Menge immer 1 beträgt, kannst du deine Rechnung einfach halten, indem du dieses Feld entfernst.',
'hide_paid_to_date' => 'Bereits gezahlt ausblenden', 'hide_paid_to_date' => '"Bereits gezahlt" ausblenden',
'hide_paid_to_date_help' => 'Bereits gezahlt nur anzeigen, wenn eine Zahlung eingegangen ist.', 'hide_paid_to_date_help' => '"Bereits gezahlt" nur anzeigen, wenn eine Zahlung eingegangen ist.',
'charge_taxes' => 'Steuern erheben', 'charge_taxes' => 'Steuern erheben',
'user_management' => 'Benutzerverwaltung', 'user_management' => 'Benutzerverwaltung',
'add_user' => 'Add User', 'add_user' => 'Benutzer hinzufügen',
'send_invite' => 'Einladung senden', 'send_invite' => 'Einladung senden',
'sent_invite' => 'Einladung erfolgreich gesendet', 'sent_invite' => 'Einladung erfolgreich gesendet',
'updated_user' => 'Benutzer erfolgreich aktualisiert', 'updated_user' => 'Benutzer erfolgreich aktualisiert',
'invitation_message' => 'Du wurdest von :invitor eingeladen.', 'invitation_message' => 'Du wurdest von :invitor eingeladen.',
'register_to_add_user' => 'Bitte registrieren um einen Benutzer hinzuzufügen', 'register_to_add_user' => 'Bitte registrieren, um einen Benutzer hinzuzufügen',
'user_state' => 'Status', 'user_state' => 'Status',
'edit_user' => 'Benutzer bearbeiten', 'edit_user' => 'Benutzer bearbeiten',
'delete_user' => 'Benutzer löschen', 'delete_user' => 'Benutzer löschen',
@ -452,15 +460,15 @@ return array(
'more_designs_self_host_text' => '', 'more_designs_self_host_text' => '',
'buy' => 'Kaufen', 'buy' => 'Kaufen',
'bought_designs' => 'Die zusätzliche Rechnungsvorlagen wurden erfolgreich hinzugefügt', 'bought_designs' => 'Die zusätzliche Rechnungsvorlagen wurden erfolgreich hinzugefügt',
'sent' => 'gesendet', 'sent' => 'gesendet',
'timesheets' => 'Timesheets',
'vat_number' => 'USt-IdNr.',
'timesheets' => 'Zeittabellen',
'payment_title' => 'Geben Sie Ihre Rechnungsadresse und Ihre Kreditkarteninformationen ein', 'payment_title' => 'Geben Sie Ihre Rechnungsadresse und Ihre Kreditkarteninformationen ein',
'payment_cvv' => '*Dies ist die 3-4-stellige Nummer auf der Rückseite Ihrer Kreditkarte', 'payment_cvv' => '*Dies ist die 3-4-stellige Nummer auf der Rückseite Ihrer Kreditkarte',
'payment_footer1' => '*Die Rechnungsadresse muss mit der Adresse der Kreditkarte übereinstimmen.', 'payment_footer1' => '*Die Rechnungsadresse muss mit der Adresse der Kreditkarte übereinstimmen.',
'payment_footer2' => '*Bitte drücken Sie nur einmal auf "Jetzt bezahlen" - die Verarbeitung der Transaktion kann bis zu einer Minute dauern.', 'payment_footer2' => '*Bitte drücken Sie nur einmal auf "Jetzt bezahlen" - die Verarbeitung der Transaktion kann bis zu einer Minute dauern.',
'vat_number' => 'USt-IdNr.',
'id_number' => 'ID-Nummer', 'id_number' => 'ID-Nummer',
'white_label_link' => 'Branding entfernen', 'white_label_link' => 'Branding entfernen',
@ -506,7 +514,7 @@ return array(
'approve' => 'Zustimmen', 'approve' => 'Zustimmen',
'token_billing_type_id' => 'Token Billing', 'token_billing_type_id' => 'Token Billing',
'token_billing_help' => 'Enables you to store credit cards with your gateway, and charge them at a later date.', 'token_billing_help' => 'Ermöglicht Ihnen, Kreditkarten mit Ihrem Gateway zu speichern und diese zu einem späteren Zeitpunkt zu belasten.',
'token_billing_1' => 'Deaktiviert', 'token_billing_1' => 'Deaktiviert',
'token_billing_2' => 'Opt-in - Kontrollkästchen wird angezeigt ist aber nicht ausgewählt', 'token_billing_2' => 'Opt-in - Kontrollkästchen wird angezeigt ist aber nicht ausgewählt',
'token_billing_3' => 'Opt-out - Kontrollkästchen wird angezeigt und ist ausgewählt', 'token_billing_3' => 'Opt-out - Kontrollkästchen wird angezeigt und ist ausgewählt',
@ -519,8 +527,8 @@ return array(
'token_billing_secure' => 'Die Daten werden sicher von :stripe_link gespeichert.', 'token_billing_secure' => 'Die Daten werden sicher von :stripe_link gespeichert.',
'support' => 'Support', 'support' => 'Support',
'contact_information' => 'Kontakt Informationen', 'contact_information' => 'Kontakt-Informationen',
'256_encryption' => '256-Bit Verschlüsselung', '256_encryption' => '256-Bit-Verschlüsselung',
'amount_due' => 'Fälliger Betrag', 'amount_due' => 'Fälliger Betrag',
'billing_address' => 'Rechnungsadresse', 'billing_address' => 'Rechnungsadresse',
'billing_method' => 'Abrechnungsmethode', 'billing_method' => 'Abrechnungsmethode',
@ -528,9 +536,9 @@ return array(
'match_address' => '*Die Rechnungsadresse muss mit der Adresse der Kreditkarte übereinstimmen.', 'match_address' => '*Die Rechnungsadresse muss mit der Adresse der Kreditkarte übereinstimmen.',
'click_once' => '*Bitte drücken Sie nur einmal auf "Jetzt bezahlen" - die Verarbeitung der Transaktion kann bis zu einer Minute dauern.', 'click_once' => '*Bitte drücken Sie nur einmal auf "Jetzt bezahlen" - die Verarbeitung der Transaktion kann bis zu einer Minute dauern.',
'default_invoice_footer' => 'Standard Fußzeile festlegen', 'default_invoice_footer' => 'Standard-Fußzeile festlegen',
'invoice_footer' => 'Fußzeile', 'invoice_footer' => 'Fußzeile',
'save_as_default_footer' => 'Als Standard Fußzeile speichern', 'save_as_default_footer' => 'Als Standard-Fußzeile speichern',
'token_management' => 'Token Verwaltung', 'token_management' => 'Token Verwaltung',
'tokens' => 'Token', 'tokens' => 'Token',
@ -567,7 +575,7 @@ return array(
'forgot_password' => 'Passwort vergessen?', 'forgot_password' => 'Passwort vergessen?',
'email_address' => 'E-Mail-Adresse', 'email_address' => 'E-Mail-Adresse',
'lets_go' => "Auf geht's", 'lets_go' => "Auf geht's",
'password_recovery' => 'Passwort Wiederherstellung', 'password_recovery' => 'Passwort-Wiederherstellung',
'send_email' => 'E-Mail verschicken', 'send_email' => 'E-Mail verschicken',
'set_password' => 'Passwort festlegen', 'set_password' => 'Passwort festlegen',
'converted' => 'Umgewandelt', 'converted' => 'Umgewandelt',
@ -578,25 +586,24 @@ return array(
'resend_confirmation' => 'Bestätigungsmail erneut senden', 'resend_confirmation' => 'Bestätigungsmail erneut senden',
'confirmation_resent' => 'Bestätigungsemail wurde erneut gesendet', 'confirmation_resent' => 'Bestätigungsemail wurde erneut gesendet',
'gateway_help_42' => ':link to sign up for BitPay.<br/>Note: use a Legacy API Key, not an API token.', 'gateway_help_42' => ':link zum Registrieren auf BitPay.<br/>Hinweis: benutze einen Legacy API Key, keinen API token.',
'payment_type_credit_card' => 'Kreditkarte', 'payment_type_credit_card' => 'Kreditkarte',
'payment_type_paypal' => 'PayPal', 'payment_type_paypal' => 'PayPal',
'payment_type_bitcoin' => 'Bitcoin', 'payment_type_bitcoin' => 'Bitcoin',
'knowledge_base' => 'Wissensdatenbank', 'knowledge_base' => 'FAQ',
'partial' => 'Parziell', 'partial' => 'Partiell',
'partial_remaining' => ':partial von :balance', 'partial_remaining' => ':partial von :balance',
'more_fields' => 'Weitere Felder', 'more_fields' => 'Weitere Felder',
'less_fields' => 'Weniger Felder', 'less_fields' => 'Weniger Felder',
'client_name' => 'Kundenname', 'client_name' => 'Kundenname',
'pdf_settings' => 'PDF Einstellungen', 'pdf_settings' => 'PDF Einstellungen',
'utf8_invoices' => 'Cyrillic Unterstützung <sup>Beta</sup>',
'product_settings' => 'Produkt Einstellungen', 'product_settings' => 'Produkt Einstellungen',
'auto_wrap' => 'Automatischer Zeilenumbruch', 'auto_wrap' => 'Automatischer Zeilenumbruch',
'duplicate_post' => 'Achtung: Die vorherige Seite wurde zweimal abgeschickt. Das zweite Abschicken wurde ignoriert.', 'duplicate_post' => 'Achtung: Die vorherige Seite wurde zweimal abgeschickt. Das zweite Abschicken wurde ignoriert.',
'view_documentation' => 'Dokumentation anzeigen', 'view_documentation' => 'Dokumentation anzeigen',
'app_title' => 'Kostenlose Online Open-Source Rechnungsausstellung', 'app_title' => 'Kostenlose Online Open-Source Rechnungsausstellung',
'app_description' => 'Invoice Ninja is a free, open-source solution for invoicing and billing customers. With Invoice Ninja, you can easily build and send beautiful invoices from any device that has access to the web. Your clients can print your invoices, download them as pdf files, and even pay you online from within the system.', 'app_description' => 'InvoiceNinja ist eine kostenlose, quelloffene Lösung für die Rechnungsstellung und Abrechnung von Kunden. Mit Invoice Ninja kannst du einfach schöne Rechnungen erstellen und verschicken, von jedem Gerät mit Internetzugang. Deine Kunden können die Rechnungen drucken, als PDF Datei herunterladen und sogar online im System bezahlen.',
'rows' => 'Zeilen', 'rows' => 'Zeilen',
'www' => 'www', 'www' => 'www',
@ -642,7 +649,7 @@ return array(
'minutes' => 'Minuten', 'minutes' => 'Minuten',
'hour' => 'Stunde', 'hour' => 'Stunde',
'hours' => 'Stunden', 'hours' => 'Stunden',
'task_details' => 'Aufgaben Details', 'task_details' => 'Aufgaben-Details',
'duration' => 'Dauer', 'duration' => 'Dauer',
'end_time' => 'Endzeit', 'end_time' => 'Endzeit',
'end' => 'Ende', 'end' => 'Ende',
@ -665,16 +672,16 @@ return array(
'counter' => 'Zähler', 'counter' => 'Zähler',
'payment_type_dwolla' => 'Dwolla', 'payment_type_dwolla' => 'Dwolla',
'gateway_help_43' => ':link to sign up for Dwolla.', 'gateway_help_43' => ':link zum Registrieren auf Dwolla.',
'partial_value' => 'Must be greater than zero and less than the total', 'partial_value' => 'Muss größer als Null und kleiner als der Gesamtbetrag sein',
'more_actions' => 'More Actions', 'more_actions' => 'Weitere Aktionen',
'pro_plan_title' => 'NINJA PRO', 'pro_plan_title' => 'NINJA PRO',
'pro_plan_call_to_action' => 'Jetzt Upgraden!', 'pro_plan_call_to_action' => 'Jetzt Upgraden!',
'pro_plan_feature1' => 'Unlimitierte Anzahl Kunden erstellen', 'pro_plan_feature1' => 'Unlimitierte Anzahl Kunden erstellen',
'pro_plan_feature2' => 'Zugriff ui 10 schönen Rechnungsdesigns', 'pro_plan_feature2' => 'Zugriff auf 10 schöne Rechnungsdesigns',
'pro_plan_feature3' => 'Benutzerdefinierte URLs - "DeineFirma.InvoiceNinja.com"', 'pro_plan_feature3' => 'Benutzerdefinierte URLs - "DeineFirma.InvoiceNinja.com"',
'pro_plan_feature4' => '"Created by Invoice Ninja" entfernen', 'pro_plan_feature4' => '"Erstellt durch Invoice Ninja" entfernen',
'pro_plan_feature5' => 'Multi-Benutzer Zugriff & Aktivitätstracking', 'pro_plan_feature5' => 'Multi-Benutzer Zugriff & Aktivitätstracking',
'pro_plan_feature6' => 'Angebote & pro-forma Rechnungen erstellen', 'pro_plan_feature6' => 'Angebote & pro-forma Rechnungen erstellen',
'pro_plan_feature7' => 'Rechungstitelfelder und Nummerierung anpassen', 'pro_plan_feature7' => 'Rechungstitelfelder und Nummerierung anpassen',
@ -689,13 +696,63 @@ return array(
'email_receipt' => 'Zahlungsbestätigung an Kunden per E-Mail senden', 'email_receipt' => 'Zahlungsbestätigung an Kunden per E-Mail senden',
'created_payment_emailed_client' => 'Zahlung erfolgreich erstellt und Kunde per E-Mail benachrichtigt', 'created_payment_emailed_client' => 'Zahlung erfolgreich erstellt und Kunde per E-Mail benachrichtigt',
'add_account' => 'Konto hinzufügen', 'add_company' => 'Konto hinzufügen',
'untitled' => 'Unbenannt', 'untitled' => 'Unbenannt',
'new_account' => 'Neues Konto', 'new_company' => 'Neues Konto',
'associated_accounts' => 'Konten erfolgreich verlinkt', 'associated_accounts' => 'Konten erfolgreich verlinkt',
'unlinked_account' => 'Konten erfolgreich getrennt', 'unlinked_account' => 'Konten erfolgreich getrennt',
'login' => 'Login', 'login' => 'Login',
'or' => 'oder', 'or' => 'oder',
'email_error' => 'Es gab ein Problem beim Senden dieses E-Mails.',
'confirm_recurring_timing' => 'Beachten Sie: E-Mails werden zu Beginn der Stunde gesendet.',
'old_browser' => 'Bitte verwenden Sie einen <a href="'.OUTDATE_BROWSER_URL.'" target="_blank">neueren Browser</a>',
'payment_terms_help' => 'Setzt das Standardfälligkeitsdatum',
'unlink_account' => 'Konten Trennen',
'unlink' => 'Trennen',
'show_address' => 'Adresse Anzeigen',
'show_address_help' => 'Verlange von Kunden ihre Rechnungsadresse anzugeben',
'update_address' => 'Adresse Aktualisieren',
'update_address_help' => 'Kundenadresse mit den gemachten Angaben aktualisieren',
'times' => 'Zeiten',
'set_now' => 'Jetzt setzen',
'dark_mode' => 'Dunkler Modus',
'dark_mode_help' => 'Weißer Text auf schwarzem Hintergrund anzeigen',
'add_to_invoice' => 'Zur Rechnung :invoice hinzufügen',
'create_new_invoice' => 'Neue Rechnung erstellen',
'task_errors' => 'Bitte korrigieren Sie alle überlappenden Zeiten',
'from' => 'Von',
'to' => 'An',
'font_size' => 'Schriftgröße',
'primary_color' => 'Primäre Farbe',
'secondary_color' => 'Sekundäre Farbe',
'customize_design' => 'Design Anpassen',
'content' => 'Inhalt',
'styles' => 'Stile',
'defaults' => 'Standards',
'margins' => 'Außenabstände',
'header' => 'Kopfzeile',
'footer' => 'Fußzeile',
'custom' => 'Benutzerdefiniert',
'invoice_to' => 'Rechnung an',
'invoice_no' => 'Rechnung Nr.',
'recent_payments' => 'Kürzliche Zahlungen',
'outstanding' => 'Ausstehend',
'manage_companies' => 'Unternehmen verwalten',
'total_revenue' => 'Gesamteinnahmen',
'current_user' => 'Aktueller Benutzer',
'new_recurring_invoice' => 'Neue wiederkehrende Rechnung',
'recurring_invoice' => 'Wiederkehrende Rechnung',
'recurring_too_soon' => 'Es ist zu früh, um die nächste wiederkehrende Rechnung zu erstellen',
'created_by_invoice' => 'Erstellt durch :invoice',
'primary_user' => 'Primärer Benutzer',
'help' => 'Hilfe',
'customize_help' => '<p>We use <a href="http://pdfmake.org/" target="_blank">pdfmake</a> to define the invoice designs declaratively. The pdfmake <a href="http://pdfmake.org/playground.html" target="_blank">playground</a> provide\'s a great way to see the library in action.</p>
<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>'
); );

View File

@ -41,7 +41,7 @@ return array(
'tax' => 'Tax', 'tax' => 'Tax',
'item' => 'Item', 'item' => 'Item',
'description' => 'Description', 'description' => 'Description',
'unit_cost' => 'Unit Cost', 'unit_cost' => 'Cost',
'quantity' => 'Quantity', 'quantity' => 'Quantity',
'line_total' => 'Line Total', 'line_total' => 'Line Total',
'subtotal' => 'Subtotal', 'subtotal' => 'Subtotal',
@ -119,8 +119,8 @@ return array(
'active_client' => 'active client', 'active_client' => 'active client',
'active_clients' => 'active clients', 'active_clients' => 'active clients',
'invoices_past_due' => 'Invoices Past Due', 'invoices_past_due' => 'Invoices Past Due',
'upcoming_invoices' => 'Upcoming invoices', 'upcoming_invoices' => 'Upcoming Invoices',
'average_invoice' => 'Average invoice', 'average_invoice' => 'Average Invoice',
// list pages // list pages
'archive' => 'Archive', 'archive' => 'Archive',
@ -276,9 +276,9 @@ return array(
// Payment page // Payment page
'secure_payment' => 'Secure Payment', 'secure_payment' => 'Secure Payment',
'card_number' => 'Card number', 'card_number' => 'Card Number',
'expiration_month' => 'Expiration month', 'expiration_month' => 'Expiration Month',
'expiration_year' => 'Expiration year', 'expiration_year' => 'Expiration Year',
'cvv' => 'CVV', 'cvv' => 'CVV',
// Security alerts // Security alerts
@ -401,9 +401,9 @@ return array(
'invoice_fields' => 'Invoice Fields', 'invoice_fields' => 'Invoice Fields',
'invoice_options' => 'Invoice Options', 'invoice_options' => 'Invoice Options',
'hide_quantity' => 'Hide quantity', 'hide_quantity' => 'Hide Quantity',
'hide_quantity_help' => 'If your line items quantities are always 1, then you can declutter invoices by no longer displaying this field.', 'hide_quantity_help' => 'If your line items quantities are always 1, then you can declutter invoices by no longer displaying this field.',
'hide_paid_to_date' => 'Hide paid to date', 'hide_paid_to_date' => 'Hide Paid to Date',
'hide_paid_to_date_help' => 'Only display the "Paid to Date" area on your invoices once a payment has been received.', 'hide_paid_to_date_help' => 'Only display the "Paid to Date" area on your invoices once a payment has been received.',
'charge_taxes' => 'Charge taxes', 'charge_taxes' => 'Charge taxes',
@ -526,11 +526,11 @@ return array(
'token_billing_secure' => 'The data is stored securely by :stripe_link', 'token_billing_secure' => 'The data is stored securely by :stripe_link',
'support' => 'Support', 'support' => 'Support',
'contact_information' => 'Contact information', 'contact_information' => 'Contact Information',
'256_encryption' => '256-Bit Encryption', '256_encryption' => '256-Bit Encryption',
'amount_due' => 'Amount due', 'amount_due' => 'Amount due',
'billing_address' => 'Billing address', 'billing_address' => 'Billing Address',
'billing_method' => 'Billing method', 'billing_method' => 'Billing Method',
'order_overview' => 'Order overview', 'order_overview' => 'Order overview',
'match_address' => '*Address must match address associated with credit card.', 'match_address' => '*Address must match address associated with credit card.',
'click_once' => '*Please click "PAY NOW" only once - transaction may take up to 1 minute to process.', 'click_once' => '*Please click "PAY NOW" only once - transaction may take up to 1 minute to process.',
@ -573,7 +573,8 @@ return array(
'recover_password' => 'Recover your password', 'recover_password' => 'Recover your password',
'forgot_password' => 'Forgot your password?', 'forgot_password' => 'Forgot your password?',
'email_address' => 'Email address', 'email_address' => 'Email address',
'lets_go' => 'Lets go', 'lets_go' => 'Let\'s go',
//'lets_go' => 'Login',
'password_recovery' => 'Password Recovery', 'password_recovery' => 'Password Recovery',
'send_email' => 'Send email', 'send_email' => 'Send email',
'set_password' => 'Set Password', 'set_password' => 'Set Password',
@ -597,7 +598,6 @@ return array(
'less_fields' => 'Less Fields', 'less_fields' => 'Less Fields',
'client_name' => 'Client Name', 'client_name' => 'Client Name',
'pdf_settings' => 'PDF Settings', 'pdf_settings' => 'PDF Settings',
'utf8_invoices' => 'Cyrillic Support <sup>Beta</sup>',
'product_settings' => 'Product Settings', 'product_settings' => 'Product Settings',
'auto_wrap' => 'Auto Line Wrap', 'auto_wrap' => 'Auto Line Wrap',
'duplicate_post' => 'Warning: the previous page was submitted twice. The second submission had been ignored.', 'duplicate_post' => 'Warning: the previous page was submitted twice. The second submission had been ignored.',
@ -696,20 +696,64 @@ return array(
'email_receipt' => 'Email payment receipt to the client', 'email_receipt' => 'Email payment receipt to the client',
'created_payment_emailed_client' => 'Successfully created payment and emailed client', 'created_payment_emailed_client' => 'Successfully created payment and emailed client',
'add_account' => 'Add Account', 'add_company' => 'Add Company',
'untitled' => 'Untitled', 'untitled' => 'Untitled',
'new_account' => 'New Account', 'new_company' => 'New Company',
'associated_accounts' => 'Successfully linked accounts', 'associated_accounts' => 'Successfully linked accounts',
'unlinked_account' => 'Successfully unlinked accounts', 'unlinked_account' => 'Successfully unlinked accounts',
'login' => 'Login', 'login' => 'Login',
'or' => 'or', 'or' => 'or',
'email_error' => 'There was a problem sending the email', 'email_error' => 'There was a problem sending the email',
'created_by_recurring' => 'Created by recurring invoice :invoice',
'confirm_recurring_timing' => 'Note: emails are sent at the start of the hour.', 'confirm_recurring_timing' => 'Note: emails are sent at the start of the hour.',
'old_browser' => 'Please use a <a href="'.OUTDATE_BROWSER_URL.'" target="_blank">newer browser</a>', 'old_browser' => 'Please use a <a href="'.OUTDATE_BROWSER_URL.'" target="_blank">newer browser</a>',
'payment_terms_help' => 'Sets the default invoice due date', 'payment_terms_help' => 'Sets the default invoice due date',
'unlink_account' => 'Unlink Account', 'unlink_account' => 'Unlink Account',
'unlink' => 'Unlink', 'unlink' => 'Unlink',
'show_address' => 'Show Address',
'show_address_help' => 'Require client to provide their billing address',
'update_address' => 'Update Address',
'update_address_help' => 'Update client\'s address with provided details',
'times' => 'Times',
'set_now' => 'Set now',
'dark_mode' => 'Dark Mode',
'dark_mode_help' => 'Show white text on black background',
'add_to_invoice' => 'Add to invoice :invoice',
'create_new_invoice' => 'Create new invoice',
'task_errors' => 'Please correct any overlapping times',
'from' => 'From',
'to' => 'To',
'font_size' => 'Font Size',
'primary_color' => 'Primary Color',
'secondary_color' => 'Secondary Color',
'customize_design' => 'Customize Design',
'content' => 'Content',
'styles' => 'Styles',
'defaults' => 'Defaults',
'margins' => 'Margins',
'header' => 'Header',
'footer' => 'Footer',
'custom' => 'Custom',
'invoice_to' => 'Invoice to',
'invoice_no' => 'Invoice No.',
'recent_payments' => 'Recent Payments',
'outstanding' => 'Outstanding',
'manage_companies' => 'Manage Companies',
'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',
'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>'
); );

View File

@ -417,264 +417,321 @@ return array(
'invalid_counter' => 'Para evitar posibles conflictos, por favor crea un prefijo de facturación y de cotización.', 'invalid_counter' => 'Para evitar posibles conflictos, por favor crea un prefijo de facturación y de cotización.',
'mark_sent' => 'Marcar como enviado', 'mark_sent' => 'Marcar como enviado',
'gateway_help_1' => ':link to sign up for Authorize.net.', 'gateway_help_1' => ':link para registrarse con Authorize.net.',
'gateway_help_2' => ':link to sign up for Authorize.net.', 'gateway_help_2' => ':link para registrarse con Authorize.net.',
'gateway_help_17' => ':link to get your PayPal API signature.', 'gateway_help_17' => ':link para obtener su firma del API de PayPal.',
'gateway_help_23' => 'Note: use your secret API key, not your publishable API key.', 'gateway_help_23' => 'Nota: use use llave secreta del API, no la llave pública.',
'gateway_help_27' => ':link to sign up for TwoCheckout.', 'gateway_help_27' => ':link para registrarse con TwoCheckout.',
'more_designs' => 'More designs', 'more_designs' => 'Más diseños',
'more_designs_title' => 'Additional Invoice Designs', 'more_designs_title' => 'Diseños Adicionales de Facturas',
'more_designs_cloud_header' => 'Go Pro for more invoice designs', 'more_designs_cloud_header' => 'Vete Pro para más diseños de facturas',
'more_designs_cloud_text' => '', 'more_designs_cloud_text' => '',
'more_designs_self_host_header' => 'Get 6 more invoice designs for just $'.INVOICE_DESIGNS_PRICE, 'more_designs_self_host_header' => 'Adquiera 6 diseños adicionales de facturas por solo $'.INVOICE_DESIGNS_PRICE,
'more_designs_self_host_text' => '', 'more_designs_self_host_text' => '',
'buy' => 'Buy', 'buy' => 'Comprar',
'bought_designs' => 'Successfully added additional invoice designs', 'bought_designs' => 'Diseños adicionales de facturas agregados con éxito',
'sent' => 'sent', 'sent' => 'enviado',
'timesheets' => 'Timesheets', 'timesheets' => 'Hohas de Tiempo',
'payment_title' => 'Enter Your Billing Address and Credit Card information', 'payment_title' => 'Ingrese la Dirección de Facturación de su Tareta de Crédito',
'payment_cvv' => '*This is the 3-4 digit number onthe back of your card', 'payment_cvv' => '*Este es el número de 3-4 dígitos en la parte posterior de su tarjeta de crédito',
'payment_footer1' => '*Billing address must match address associated with credit card.', 'payment_footer1' => '*La dirección debe coincidir con la dirección asociada a la tarjeta de crédito.',
'payment_footer2' => '*Please click "PAY NOW" only once - transaction may take up to 1 minute to process.', 'payment_footer2' => '*Por favor haga clic en "PAGAR AHORA" sólo una vez - la transacción puede demorarse hasta un minuto en ser procesada.',
'vat_number' => 'Vat Number', 'vat_number' => 'Número de Impuesto',
'id_number' => 'ID Number', 'id_number' => 'ID Number',
'white_label_link' => 'White label', 'white_label_link' => 'Etiqueta Blanca',
'white_label_text' => 'Purchase a white label license for $'.WHITE_LABEL_PRICE.' to remove the Invoice Ninja branding from the top of the client pages.', 'white_label_text' => 'Adquiera una licencia de etiqueta blanca por $'.WHITE_LABEL_PRICE.' para eliminar la marca de Invoice Ninja branding de la parte superior de las páginas de los clientes.',
'white_label_header' => 'White Label', 'white_label_header' => 'Etiqueta Blanca',
'bought_white_label' => 'Successfully enabled white label license', 'bought_white_label' => 'Licencia de etiqueta blanca habilitada con éxito',
'white_labeled' => 'White labeled', 'white_labeled' => 'Etiqueta Blanca',
'restore' => 'Restore', 'restore' => 'Restaurar',
'restore_invoice' => 'Restore Invoice', 'restore_invoice' => 'Restaurar Invoice',
'restore_quote' => 'Restore Quote', 'restore_quote' => 'Restaurar Quote',
'restore_client' => 'Restore Client', 'restore_client' => 'Restaurar Client',
'restore_credit' => 'Restore Credit', 'restore_credit' => 'Restaurar Credit',
'restore_payment' => 'Restore Payment', 'restore_payment' => 'Restaurar Payment',
'restored_invoice' => 'Successfully restored invoice', 'restored_invoice' => 'Factura restaurada con éxito',
'restored_quote' => 'Successfully restored quote', 'restored_quote' => 'Cotización restaurada con éxito',
'restored_client' => 'Successfully restored client', 'restored_client' => 'Cliente restaurado con éxito',
'restored_payment' => 'Successfully restored payment', 'restored_payment' => 'Pago restaurado con éxito',
'restored_credit' => 'Successfully restored credit', 'restored_credit' => 'Crédito restaurado con éxito',
'reason_for_canceling' => 'Help us improve our site by telling us why you\'re leaving.', 'reason_for_canceling' => 'Ayúdenos a mejorar contándonos porqué se va.',
'discount_percent' => 'Percent', 'discount_percent' => 'Porcentaje',
'discount_amount' => 'Amount', 'discount_amount' => 'Cantidad',
'invoice_history' => 'Invoice History', 'invoice_history' => 'Facturar Historial',
'quote_history' => 'Quote History', 'quote_history' => 'Cotizar Historial',
'current_version' => 'Current version', 'current_version' => 'Versión actual',
'select_versiony' => 'Select version', 'select_versiony' => 'Seleccionar versión',
'view_history' => 'View History', 'view_history' => 'Ver Historial',
'edit_payment' => 'Edit Payment', 'edit_payment' => 'Editar Pago',
'updated_payment' => 'Successfully updated payment', 'updated_payment' => 'Pago actualizado con éxito',
'deleted' => 'Deleted', 'deleted' => 'Eliminado',
'restore_user' => 'Restore User', 'restore_user' => 'Restaurar Usuario',
'restored_user' => 'Successfully restored user', 'restored_user' => 'Usuario restaurado con éxito',
'show_deleted_users' => 'Show deleted users', 'show_deleted_users' => 'Mostrar usuarios eliminados',
'email_templates' => 'Email Templates', 'email_templates' => 'Plantillas de Correos',
'invoice_email' => 'Invoice Email', 'invoice_email' => 'Correo de Factura',
'payment_email' => 'Payment Email', 'payment_email' => 'Correo de Pago',
'quote_email' => 'Quote Email', 'quote_email' => 'Correo de Cotizacion',
'reset_all' => 'Reset All', 'reset_all' => 'Reiniciar Todos',
'approve' => 'Approve', 'approve' => 'Aprobar',
'token_billing_type_id' => 'Token Billing', 'token_billing_type_id' => 'Token de Facturación',
'token_billing_help' => 'Enables you to store credit cards with your gateway, and charge them at a later date.', 'token_billing_help' => 'Permite almacenar tarjetas de crédito con su gateway de pagos, y facturar en una fecha posterior.',
'token_billing_1' => 'Disabled', 'token_billing_1' => 'Deshabilitado',
'token_billing_2' => 'Opt-in - checkbox is shown but not selected', 'token_billing_2' => 'Opt-in - el checkbox es mostrado pero no seleccionado',
'token_billing_3' => 'Opt-out - checkbox is shown and selected', 'token_billing_3' => 'Opt-out - el checkbox es mostrado y seleccionado',
'token_billing_4' => 'Always', 'token_billing_4' => 'Siempre',
'token_billing_checkbox' => 'Store credit card details', 'token_billing_checkbox' => 'Almacenar detalles de la tarjeta de crédito',
'view_in_stripe' => 'View in Stripe', 'view_in_stripe' => 'Ver en Stripe',
'use_card_on_file' => 'Use card on file', 'use_card_on_file' => 'Usar la tarjeta en el archivo',
'edit_payment_details' => 'Edit payment details', 'edit_payment_details' => 'Editar detalles del pago',
'token_billing' => 'Save card details', 'token_billing' => 'Guardar detalles de la tarjeta',
'token_billing_secure' => 'The data is stored securely by :stripe_link', 'token_billing_secure' => 'La información es almacenada de manera segura por :stripe_link',
'support' => 'Support', 'support' => 'Soporte',
'contact_information' => 'Contact information', 'contact_information' => 'Información de Contacto',
'256_encryption' => '256-Bit Encryption', '256_encryption' => 'Encripción de 256-Bit',
'amount_due' => 'Amount due', 'amount_due' => 'Valor por cobrar',
'billing_address' => 'Billing address', 'billing_address' => 'Dirección de facturación',
'billing_method' => 'Billing method', 'billing_method' => 'Método de facturación',
'order_overview' => 'Order overview', 'order_overview' => 'Resumen de la orden',
'match_address' => '*Address must match address associated with credit card.', 'match_address' => '*La dirección debe coincidir con la dirección asociada a la tarjeta de crédito.',
'click_once' => '*Please click "PAY NOW" only once - transaction may take up to 1 minute to process.', 'click_once' => '*Por favor haga clic en "PAGAR AHORA" sólo una vez - la transacción puede demorarse hasta un minuto en ser procesada.',
'default_invoice_footer' => 'Set default invoice footer', 'default_invoice_footer' => 'Asignar pié de página por defecto para la factura',
'invoice_footer' => 'Invoice footer', 'invoice_footer' => 'Pié de págia de la factura',
'save_as_default_footer' => 'Save as default footer', 'save_as_default_footer' => 'Guardar como el pié de página por defecto',
'token_management' => 'Token Management', 'token_management' => 'Administración de Tokens',
'tokens' => 'Tokens', 'tokens' => 'Tokens',
'add_token' => 'Add Token', 'add_token' => 'Agregar Token',
'show_deleted_tokens' => 'Show deleted tokens', 'show_deleted_tokens' => 'Mostrar los tokens eliminados',
'deleted_token' => 'Successfully deleted token', 'deleted_token' => 'Token eliminado con éxito',
'created_token' => 'Successfully created token', 'created_token' => 'Token creado con éxito',
'updated_token' => 'Successfully updated token', 'updated_token' => 'Token actualizado con éxito',
'edit_token' => 'Edit Token', 'edit_token' => 'Editar Token',
'delete_token' => 'Delete Token', 'delete_token' => 'Eliminar Token',
'token' => 'Token', 'token' => 'Token',
'add_gateway' => 'Add Gateway', 'add_gateway' => 'Agregar Gateway',
'delete_gateway' => 'Delete Gateway', 'delete_gateway' => 'Eliminar Gateway',
'edit_gateway' => 'Edit Gateway', 'edit_gateway' => 'Editar Gateway',
'updated_gateway' => 'Successfully updated gateway', 'updated_gateway' => 'Gateway actualizado con éxito',
'created_gateway' => 'Successfully created gateway', 'created_gateway' => 'Gateway creado con éxito',
'deleted_gateway' => 'Successfully deleted gateway', 'deleted_gateway' => 'Gateway eliminado con éxito',
'pay_with_paypal' => 'PayPal', 'pay_with_paypal' => 'PayPal',
'pay_with_card' => 'Credit card', 'pay_with_card' => 'Tarjeta de Crédito',
'change_password' => 'Change password', 'change_password' => 'Cambiar contraseña',
'current_password' => 'Current password', 'current_password' => 'contraseña actual',
'new_password' => 'New password', 'new_password' => 'Nueva contraseña',
'confirm_password' => 'Confirm password', 'confirm_password' => 'Confirmar contraseña',
'password_error_incorrect' => 'The current password is incorrect.', 'password_error_incorrect' => 'La contraseña actual es incorrecta.',
'password_error_invalid' => 'The new password is invalid.', 'password_error_invalid' => 'La nueva contraseña es inválida.',
'updated_password' => 'Successfully updated password', 'updated_password' => 'Contraseñaactualizada con éxito',
'api_tokens' => 'API Tokens', 'api_tokens' => 'API Tokens',
'users_and_tokens' => 'Users & Tokens', 'users_and_tokens' => 'Usuarios y Tokens',
'account_login' => 'Account Login', 'account_login' => 'Ingreso',
'recover_password' => 'Recover your password', 'recover_password' => 'Recupere su contraseña',
'forgot_password' => 'Forgot your password?', 'forgot_password' => 'Olvidó su Contraseña?',
'email_address' => 'Email address', 'email_address' => 'Correo electrónico',
'lets_go' => 'Lets go', 'lets_go' => 'Vamos',
'password_recovery' => 'Password Recovery', 'password_recovery' => 'Recuperación de Contraseña',
'send_email' => 'Send email', 'send_email' => 'Enviar correo',
'set_password' => 'Set Password', 'set_password' => 'Asignar Contraseña',
'converted' => 'Converted', 'converted' => 'Convertido',
'email_approved' => 'Email me when a quote is <b>approved</b>', 'email_approved' => 'Enviarme un correo cuando una cotización sea <b>aprobada</b>',
'notification_quote_approved_subject' => 'Quote :invoice was approved by :client', 'notification_quote_approved_subject' => 'Cotización :invoice fue aprobada por :client',
'notification_quote_approved' => 'The following client :client approved Quote :invoice for :amount.', 'notification_quote_approved' => 'El cliente :client ha aprobado la cotización :invoice por el valor :amount.',
'resend_confirmation' => 'Resend confirmation email', 'resend_confirmation' => 'Reenviar correo de confirmación',
'confirmation_resent' => 'The confirmation email was resent', 'confirmation_resent' => 'El correo de confirmación fue reenviado',
'gateway_help_42' => ':link to sign up for BitPay.<br/>Note: use a Legacy API Key, not an API token.', 'gateway_help_42' => ':link para registrarse en BitPay.<br/>Nota: use una llave del API legacy, no un token API.',
'payment_type_credit_card' => 'Credit card', 'payment_type_credit_card' => 'Tarjeta de Crédito',
'payment_type_paypal' => 'PayPal', 'payment_type_paypal' => 'PayPal',
'payment_type_bitcoin' => 'Bitcoin', 'payment_type_bitcoin' => 'Bitcoin',
'knowledge_base' => 'Knowledge Base', 'knowledge_base' => 'Base de Conocimiento',
'partial' => 'Partial', 'partial' => 'Parcial',
'partial_remaining' => ':partial of :balance', 'partial_remaining' => ':partial de :balance',
'more_fields' => 'More Fields', 'more_fields' => ' Más Campos',
'less_fields' => 'Less Fields', 'less_fields' => 'Menos Campos',
'client_name' => 'Client Name', 'client_name' => 'Nombre del Cliente',
'pdf_settings' => 'PDF Settings', 'pdf_settings' => 'Configuración de PDF',
'utf8_invoices' => 'Cyrillic Support <sup>Beta</sup>', 'product_settings' => 'Configuración del Producto',
'product_settings' => 'Product Settings', 'auto_wrap' => 'Ajuste Automático de Linea',
'auto_wrap' => 'Auto Line Wrap', 'duplicate_post' => 'Advertencia: la página anterior fue enviada dos veces. El segundo envío ha sido ignorado.',
'duplicate_post' => 'Warning: the previous page was submitted twice. The second submission had been ignored.', 'view_documentation' => 'Ver Documentación',
'view_documentation' => 'View Documentation', 'app_title' => 'Facturación Open-Source Gratuita',
'app_title' => 'Free Open-Source Online Invoicing', 'app_description' => 'Invoice Ninja es una solución open-source gratuita para manejar la facturación de sus clientes. Con Invoice Ninja, se pueden crear y enviar hermosas facturas desde cualquier dispositivo que tenga acceso a Internet. Sus clientes pueden imprimir sus facturas, descargarlas en formato PDF o inclusive pagarlas en linea desde esta misma plataforma',
'app_description' => 'Invoice Ninja is a free, open-source solution for invoicing and billing customers. With Invoice Ninja, you can easily build and send beautiful invoices from any device that has access to the web. Your clients can print your invoices, download them as pdf files, and even pay you online from within the system.',
'rows' => 'rows', 'rows' => 'filas',
'www' => 'www', 'www' => 'www',
'logo' => 'Logo', 'logo' => 'Logo',
'subdomain' => 'Subdomain', 'subdomain' => 'Subdominio',
'provide_name_or_email' => 'Please provide a contact name or email', 'provide_name_or_email' => 'Por favor provea un nombre o correo electrónico de contacto',
'charts_and_reports' => 'Charts & Reports', 'charts_and_reports' => 'Gráficas y Reportes',
'chart' => 'Chart', 'chart' => 'Gráfica',
'report' => 'Report', 'report' => 'Reporte',
'group_by' => 'Group by', 'group_by' => 'Agrupar por',
'paid' => 'Paid', 'paid' => 'Pagado',
'enable_report' => 'Report', 'enable_report' => 'Reportes',
'enable_chart' => 'Chart', 'enable_chart' => 'Gráficas',
'totals' => 'Totals', 'totals' => 'Totales',
'run' => 'Run', 'run' => 'Ejecutar',
'export' => 'Export', 'export' => 'Exportar',
'documentation' => 'Documentation', 'documentation' => 'Documentación',
'zapier' => 'Zapier <sup>Beta</sup>', 'zapier' => 'Zapier <sup>Beta</sup>',
'recurring' => 'Recurring', 'recurring' => 'Recurrente',
'last_invoice_sent' => 'Last invoice sent :date', 'last_invoice_sent' => 'Ultima factura enviada en :date',
'processed_updates' => 'Successfully completed update', 'processed_updates' => 'Actualización completada con éxito',
'tasks' => 'Tasks', 'tasks' => 'Tareas',
'new_task' => 'New Task', 'new_task' => 'Nueva Tarea',
'start_time' => 'Start Time', 'start_time' => 'Tiempo de Inicio',
'created_task' => 'Successfully created task', 'created_task' => 'Tarea creada con éxito',
'updated_task' => 'Successfully updated task', 'updated_task' => 'Tarea actualizada con éxito',
'edit_task' => 'Edit Task', 'edit_task' => 'Editar Tarea',
'archive_task' => 'Archive Task', 'archive_task' => 'Archivar Tarea',
'restore_task' => 'Restore Task', 'restore_task' => 'Restaurar Tarea',
'delete_task' => 'Delete Task', 'delete_task' => 'Eliminar Tarea',
'stop_task' => 'Stop Task', 'stop_task' => 'Detener Tarea',
'time' => 'Time', 'time' => 'Tiempo',
'start' => 'Start', 'start' => 'Iniciar',
'stop' => 'Stop', 'stop' => 'Detener',
'now' => 'Now', 'now' => 'Ahora',
'timer' => 'Timer', 'timer' => 'Timer',
'manual' => 'Manual', 'manual' => 'Manual',
'date_and_time' => 'Date & Time', 'date_and_time' => 'Fecha y Hora',
'second' => 'second', 'second' => 'segundo',
'seconds' => 'seconds', 'seconds' => 'segundos',
'minute' => 'minute', 'minute' => 'minuto',
'minutes' => 'minutes', 'minutes' => 'minutos',
'hour' => 'hour', 'hour' => 'hora',
'hours' => 'hours', 'hours' => 'horas',
'task_details' => 'Task Details', 'task_details' => 'Detalles de la Tarea',
'duration' => 'Duration', 'duration' => 'Duración',
'end_time' => 'End Time', 'end_time' => 'Tiempo Final',
'end' => 'End', 'end' => 'Fin',
'invoiced' => 'Invoiced', 'invoiced' => 'Facturado',
'logged' => 'Logged', 'logged' => 'Registrado',
'running' => 'Running', 'running' => 'Corriendo',
'task_error_multiple_clients' => 'The tasks can\'t belong to different clients', 'task_error_multiple_clients' => 'Las tareas no pueden pertenecer a diferentes clientes',
'task_error_running' => 'Please stop running tasks first', 'task_error_running' => 'Por favor primero detenga las tareas que se estén ejecutando',
'task_error_invoiced' => 'Tasks have already been invoiced', 'task_error_invoiced' => 'Las tareas ya han sido facturadas',
'restored_task' => 'Successfully restored task', 'restored_task' => 'Tarea restaurada con éxito',
'archived_task' => 'Successfully archived task', 'archived_task' => 'Tarea archivada con éxito',
'archived_tasks' => 'Successfully archived :count tasks', 'archived_tasks' => ':count tareas archivadas con éxito',
'deleted_task' => 'Successfully deleted task', 'deleted_task' => 'Tarea eliminada con éxito',
'deleted_tasks' => 'Successfully deleted :count tasks', 'deleted_tasks' => ':count tareas eliminadas con éxito',
'create_task' => 'Create Task', 'create_task' => 'Crear Tarea',
'stopped_task' => 'Successfully stopped task', 'stopped_task' => 'Tarea detenida con éxito',
'invoice_task' => 'Invoice Task', 'invoice_task' => 'Tarea de Factura',
'invoice_labels' => 'Invoice Labels', 'invoice_labels' => 'Etiquetas',
'prefix' => 'Prefix', 'prefix' => 'Prefijo',
'counter' => 'Counter', 'counter' => 'Contador',
'payment_type_dwolla' => 'Dwolla', 'payment_type_dwolla' => 'Dwolla',
'gateway_help_43' => ':link to sign up for Dwolla.', 'gateway_help_43' => ':link para registrarse con Dwolla.',
'partial_value' => 'Must be greater than zero and less than the total', 'partial_value' => 'Debe ser mayor que cero y menor que el total',
'more_actions' => 'More Actions', 'more_actions' => 'Más Acciones',
'pro_plan_title' => 'NINJA PRO', 'pro_plan_title' => 'NINJA PRO',
'pro_plan_call_to_action' => 'Upgrade Now!', 'pro_plan_call_to_action' => 'Actualícese Ahora!',
'pro_plan_feature1' => 'Create Unlimited Clients', 'pro_plan_feature1' => 'Cree Clientes Ilimitados',
'pro_plan_feature2' => 'Access to 10 Beautiful Invoice Designs', 'pro_plan_feature2' => 'Acceda a 10 hermosos diseños de factura',
'pro_plan_feature3' => 'Custom URLs - "YourBrand.InvoiceNinja.com"', 'pro_plan_feature3' => 'Custom URLs - "SuMarca.InvoiceNinja.com"',
'pro_plan_feature4' => 'Remove "Created by Invoice Ninja"', 'pro_plan_feature4' => 'Remove "Creado por Invoice Ninja"',
'pro_plan_feature5' => 'Multi-user Access & Activity Tracking', 'pro_plan_feature5' => 'Acceso Multi-usuario y seguimiento de actividades',
'pro_plan_feature6' => 'Create Quotes & Pro-forma Invoices', 'pro_plan_feature6' => 'Cree Cotizaciones y facturas Pro-forma',
'pro_plan_feature7' => 'Customize Invoice Field Titles & Numbering', 'pro_plan_feature7' => 'Personalice los Títulos de los Campos y Numeración de las Cuentas de Cobro',
'pro_plan_feature8' => 'Option to Attach PDFs to Client Emails', 'pro_plan_feature8' => 'Opción para adjuntarle documentos PDF a los correos dirigidos a los clientes',
'resume' => 'Resume', 'resume' => 'Continuar',
'break_duration' => 'Break', 'break_duration' => 'Descanso',
'edit_details' => 'Edit Details', 'edit_details' => 'Editar Detalles',
'work' => 'Work', 'work' => 'Trabajo',
'timezone_unset' => 'Please :link to set your timezone', 'timezone_unset' => 'Por favor :link para configurar su Uso Horario',
'click_here' => 'click here', 'click_here' => 'haga clic aquí',
'resume' => 'Continuar',
'break_duration' => 'Descanso',
'edit_details' => 'Editar Detalles',
'work' => 'Tranajo',
'timezone_unset' => 'Por favor :link para configurar su Uso Horario',
'click_here' => 'haga clic aquí',
'email_receipt' => 'Enviar por correo electrónico el recibo de pago al cliente',
'created_payment_emailed_client' => 'Pago creado y enviado al cliente con éxito',
'add_company' => 'Agregar Compañía',
'untitled' => 'Sin Título',
'new_company' => 'Nueva Compañia',
'associated_accounts' => 'Cuentas conectadas con éxito',
'unlinked_account' => 'Cuentas desconectadas con éxito',
'login' => 'Ingresar',
'or' => 'o',
'email_error' => 'Hubo un problema enviando el correo',
'confirm_recurring_timing' => 'Nota: los correos son enviados al inicio de la hora.',
'old_browser' => 'Por favor utilice <a href="'.OUTDATE_BROWSER_URL.'" target="_blank">más reciente</a>',
'payment_terms_help' => 'Asigna la fecha de vencimiento por defecto de la factura',
'unlink_account' => 'Desconectar Cuenta',
'unlink' => 'Desconectar',
'show_address' => 'Actualizar Dirección',
'show_address_help' => 'Requerir que el cliente provea su dirección de facturación',
'update_address' => 'Actualizar Dirección',
'update_address_help' => 'Actualizar la dirección del cliente con los detalles proporcionados',
'times' => 'Tiempos',
'set_now' => 'Asignar ahora',
'dark_mode' => 'Modo Oscuro',
'dark_mode_help' => 'Mostrar texto blanco sobre fondo negro',
'add_to_invoice' => 'Agregar a cuenta :invoice',
'create_new_invoice' => 'Crear Nueva Cuenta',
'task_errors' => 'Por favor corrija cualquier tiempo que se sobreponga con otro',
'from' => 'De',
'to' => 'Para',
'font_size' => 'Tamaño de Letra',
'primary_color' => 'Color Primario',
'secondary_color' => 'Color Secundario',
'customize_design' => 'Personalizar el Diseño',
'content' => 'Contenido',
'styles' => 'Estílos',
'defaults' => 'Valores por Defecto',
'margins' => 'Márgenes',
'header' => 'encabezado',
'footer' => 'Pié de Página',
'custom' => 'Personalizado',
'invoice_to' => 'Factura para',
'invoice_no' => 'Factura No.',
'recent_payments' => 'Pagos Recientes',
'outstanding' => 'Sobresaliente',
'manage_companies' => 'Administrar Compañías',
'total_revenue' => 'Ingresos Totales',
'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',
'customize_help' => '<p>Nosotros usamos <a href="http://pdfmake.org/" target="_blank">pdfmake</a> para definir los diseños de las cuentas de cobro de manera declarativa. El <a href="http://pdfmake.org/playground.html" target="_blank">playground</a> de pdfmake es una excelente manera de ver a la librería en acción.</p>
<p>Puede acceder cualquier campo de una factura agregando <code>Value</code> al final. Por ejemplo, <code>$invoiceNumberValue</code> muestra el número de factura.</p>
<p>Para acceder a una propiedad hija usando notación de punto.Por ejemplo, para mostrar el nombre de un cliente se puede usar <code>$client.nameValue</code>.</p>
<p>Si necesita ayuda entendiendo algo puede preguntar en nuestro <a href="https://www.invoiceninja.com/forums/forum/support/" target="_blank">foro de soporte</a>.</p>'
'email_receipt' => 'Email payment receipt to the client',
'created_payment_emailed_client' => 'Successfully created payment and emailed client',
'add_account' => 'Add Account',
'untitled' => 'Untitled',
'new_account' => 'New Account',
'associated_accounts' => 'Successfully linked accounts',
'unlinked_account' => 'Successfully unlinked accounts',
'login' => 'Login',
'or' => 'or',
); );

View File

@ -598,7 +598,6 @@ return array(
'less_fields' => 'Less Fields', 'less_fields' => 'Less Fields',
'client_name' => 'Client Name', 'client_name' => 'Client Name',
'pdf_settings' => 'PDF Settings', 'pdf_settings' => 'PDF Settings',
'utf8_invoices' => 'Cyrillic Support <sup>Beta</sup>',
'product_settings' => 'Product Settings', 'product_settings' => 'Product Settings',
'auto_wrap' => 'Auto Line Wrap', 'auto_wrap' => 'Auto Line Wrap',
'duplicate_post' => 'Warning: the previous page was submitted twice. The second submission had been ignored.', 'duplicate_post' => 'Warning: the previous page was submitted twice. The second submission had been ignored.',
@ -697,14 +696,64 @@ return array(
'email_receipt' => 'Email payment receipt to the client', 'email_receipt' => 'Email payment receipt to the client',
'created_payment_emailed_client' => 'Successfully created payment and emailed client', 'created_payment_emailed_client' => 'Successfully created payment and emailed client',
'add_account' => 'Add Account', 'add_company' => 'Add Company',
'untitled' => 'Untitled', 'untitled' => 'Untitled',
'new_account' => 'New Account', 'new_company' => 'New Company',
'associated_accounts' => 'Successfully linked accounts', 'associated_accounts' => 'Successfully linked accounts',
'unlinked_account' => 'Successfully unlinked accounts', 'unlinked_account' => 'Successfully unlinked accounts',
'login' => 'Login', 'login' => 'Login',
'or' => 'or', 'or' => 'or',
'email_error' => 'There was a problem sending the email',
'confirm_recurring_timing' => 'Note: emails are sent at the start of the hour.',
'old_browser' => 'Please use a <a href="'.OUTDATE_BROWSER_URL.'" target="_blank">newer browser</a>',
'payment_terms_help' => 'Sets the default invoice due date',
'unlink_account' => 'Unlink Account',
'unlink' => 'Unlink',
'show_address' => 'Show Address',
'show_address_help' => 'Require client to provide their billing address',
'update_address' => 'Update Address',
'update_address_help' => 'Update client\'s address with provided details',
'times' => 'Times',
'set_now' => 'Set now',
'dark_mode' => 'Dark Mode',
'dark_mode_help' => 'Show white text on black background',
'add_to_invoice' => 'Add to invoice :invoice',
'create_new_invoice' => 'Create new invoice',
'task_errors' => 'Please correct any overlapping times',
'from' => 'From',
'to' => 'To',
'font_size' => 'Font Size',
'primary_color' => 'Primary Color',
'secondary_color' => 'Secondary Color',
'customize_design' => 'Customize Design',
'content' => 'Content',
'styles' => 'Styles',
'defaults' => 'Defaults',
'margins' => 'Margins',
'header' => 'Header',
'footer' => 'Footer',
'custom' => 'Custom',
'invoice_to' => 'Invoice to',
'invoice_no' => 'Invoice No.',
'recent_payments' => 'Recent Payments',
'outstanding' => 'Outstanding',
'manage_companies' => 'Manage Companies',
'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',
'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>'
); );

View File

@ -590,7 +590,6 @@ return array(
'less_fields' => 'Moins de champs', 'less_fields' => 'Moins de champs',
'client_name' => 'Nom du client', 'client_name' => 'Nom du client',
'pdf_settings' => 'Réglages PDF', 'pdf_settings' => 'Réglages PDF',
'utf8_invoices' => 'Cyrillic Support <sup>Beta</sup>',
'product_settings' => 'Réglages du produit', 'product_settings' => 'Réglages du produit',
'auto_wrap' => 'Auto Line Wrap', 'auto_wrap' => 'Auto Line Wrap',
'duplicate_post' => 'Warning: the previous page was submitted twice. The second submission had been ignored.', 'duplicate_post' => 'Warning: the previous page was submitted twice. The second submission had been ignored.',
@ -628,7 +627,7 @@ return array(
'archive_task' => 'Archiver tâche', 'archive_task' => 'Archiver tâche',
'restore_task' => 'Restaurer tâche', 'restore_task' => 'Restaurer tâche',
'delete_task' => 'Supprimer tâche', 'delete_task' => 'Supprimer tâche',
'stop_task' => 'Arrêter tâcher', 'stop_task' => 'Arrêter tâche',
'time' => 'Temps', 'time' => 'Temps',
'start' => 'Début', 'start' => 'Début',
'stop' => 'Fin', 'stop' => 'Fin',
@ -682,21 +681,71 @@ return array(
'resume' => 'Resume', 'resume' => 'Resume',
'break_duration' => 'Break', 'break_duration' => 'Break',
'edit_details' => 'Editer détails', 'edit_details' => 'Modifier',
'work' => 'Travail', 'work' => 'Travail',
'timezone_unset' => 'Please :link to set your timezone', 'timezone_unset' => 'Please :link to set your timezone',
'click_here' => 'cliquer ici', 'click_here' => 'cliquer ici',
'email_receipt' => 'Email payment receipt to the client', 'email_receipt' => 'Email payment receipt to the client',
'created_payment_emailed_client' => 'Paiement crée avec succès et envoyé au client', 'created_payment_emailed_client' => 'Paiement crée avec succès et envoyé au client',
'add_account' => 'Ajouter compte', 'add_company' => 'Ajouter compte',
'untitled' => 'Sans titre', 'untitled' => 'Sans titre',
'new_account' => 'Nouveau compte', 'new_company' => 'Nouveau compte',
'associated_accounts' => 'Successfully linked accounts', 'associated_accounts' => 'Successfully linked accounts',
'unlinked_account' => 'Successfully unlinked accounts', 'unlinked_account' => 'Successfully unlinked accounts',
'login' => 'Connexion', 'login' => 'Connexion',
'or' => 'ou', 'or' => 'ou',
'email_error' => 'There was a problem sending the email',
'confirm_recurring_timing' => 'Note: emails are sent at the start of the hour.',
'old_browser' => 'Please use a <a href="'.OUTDATE_BROWSER_URL.'" target="_blank">newer browser</a>',
'payment_terms_help' => 'Sets the default invoice due date',
'unlink_account' => 'Unlink Account',
'unlink' => 'Unlink',
'show_address' => 'Show Address',
'show_address_help' => 'Require client to provide their billing address',
'update_address' => 'Update Address',
'update_address_help' => 'Update client\'s address with provided details',
'times' => 'Times',
'set_now' => 'Set now',
'dark_mode' => 'Dark Mode',
'dark_mode_help' => 'Show white text on black background',
'add_to_invoice' => 'Add to invoice :invoice',
'create_new_invoice' => 'Create new invoice',
'task_errors' => 'Please correct any overlapping times',
'from' => 'From',
'to' => 'To',
'font_size' => 'Font Size',
'primary_color' => 'Primary Color',
'secondary_color' => 'Secondary Color',
'customize_design' => 'Customize Design',
'content' => 'Content',
'styles' => 'Styles',
'defaults' => 'Defaults',
'margins' => 'Margins',
'header' => 'Header',
'footer' => 'Footer',
'custom' => 'Custom',
'invoice_to' => 'Invoice to',
'invoice_no' => 'Invoice No.',
'recent_payments' => 'Recent Payments',
'outstanding' => 'Outstanding',
'manage_companies' => 'Manage Companies',
'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',
'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>'
); );

View File

@ -590,7 +590,6 @@ return array(
'less_fields' => 'Less Fields', 'less_fields' => 'Less Fields',
'client_name' => 'Client Name', 'client_name' => 'Client Name',
'pdf_settings' => 'PDF Settings', 'pdf_settings' => 'PDF Settings',
'utf8_invoices' => 'Cyrillic Support <sup>Beta</sup>',
'product_settings' => 'Product Settings', 'product_settings' => 'Product Settings',
'auto_wrap' => 'Auto Line Wrap', 'auto_wrap' => 'Auto Line Wrap',
'duplicate_post' => 'Warning: the previous page was submitted twice. The second submission had been ignored.', 'duplicate_post' => 'Warning: the previous page was submitted twice. The second submission had been ignored.',
@ -619,50 +618,50 @@ return array(
'last_invoice_sent' => 'Last invoice sent :date', 'last_invoice_sent' => 'Last invoice sent :date',
'processed_updates' => 'Successfully completed update', 'processed_updates' => 'Successfully completed update',
'tasks' => 'Tasks', 'tasks' => 'Tâches',
'new_task' => 'New Task', 'new_task' => 'Nouvelle Tâche',
'start_time' => 'Start Time', 'start_time' => 'Démarrée à',
'created_task' => 'Successfully created task', 'created_task' => 'Tâche créée avec succès',
'updated_task' => 'Successfully updated task', 'updated_task' => 'Tâche modifiée avec succès',
'edit_task' => 'Edit Task', 'edit_task' => 'Edit Task',
'archive_task' => 'Archive Task', 'archive_task' => 'Archiver la Tâche',
'restore_task' => 'Restore Task', 'restore_task' => 'Restaurer la Tâche',
'delete_task' => 'Delete Task', 'delete_task' => 'Supprimer la Tâche',
'stop_task' => 'Stop Task', 'stop_task' => 'Arrêter la Tâche',
'time' => 'Time', 'time' => 'Time',
'start' => 'Start', 'start' => 'Démarrer',
'stop' => 'Stop', 'stop' => 'Arrêter',
'now' => 'Now', 'now' => 'Now',
'timer' => 'Timer', 'timer' => 'Timer',
'manual' => 'Manual', 'manual' => 'Manual',
'date_and_time' => 'Date & Time', 'date_and_time' => 'Date & Time',
'second' => 'second', 'second' => 'seconde',
'seconds' => 'seconds', 'seconds' => 'secondes',
'minute' => 'minute', 'minute' => 'minute',
'minutes' => 'minutes', 'minutes' => 'minutes',
'hour' => 'hour', 'hour' => 'heure',
'hours' => 'hours', 'hours' => 'heures',
'task_details' => 'Task Details', 'task_details' => 'Détails de la Tâche',
'duration' => 'Duration', 'duration' => 'Durée',
'end_time' => 'End Time', 'end_time' => 'Arrêtée à',
'end' => 'End', 'end' => 'End',
'invoiced' => 'Invoiced', 'invoiced' => 'Invoiced',
'logged' => 'Logged', 'logged' => 'Logged',
'running' => 'Running', 'running' => 'Running',
'task_error_multiple_clients' => 'The tasks can\'t belong to different clients', 'task_error_multiple_clients' => 'Une tâche ne peut appartenir à plusieurs clients',
'task_error_running' => 'Please stop running tasks first', 'task_error_running' => 'Merci d\'arrêter les tâches en cours',
'task_error_invoiced' => 'Tasks have already been invoiced', 'task_error_invoiced' => 'Ces tâches ont déjà été facturées',
'restored_task' => 'Successfully restored task', 'restored_task' => 'Tâche restaurée avec succès',
'archived_task' => 'Successfully archived task', 'archived_task' => 'Tâche archivée avec succès',
'archived_tasks' => 'Successfully archived :count tasks', 'archived_tasks' => ':count tâches archivées avec succès',
'deleted_task' => 'Successfully deleted task', 'deleted_task' => 'Tâche supprimée avec succès',
'deleted_tasks' => 'Successfully deleted :count tasks', 'deleted_tasks' => ':count tâches supprimées avec succès',
'create_task' => 'Create Task', 'create_task' => 'Créer une Tâche',
'stopped_task' => 'Successfully stopped task', 'stopped_task' => 'Tâche arrêtée avec succès',
'invoice_task' => 'Invoice Task', 'invoice_task' => 'Facturer Tâche',
'invoice_labels' => 'Invoice Labels', 'invoice_labels' => 'Invoice Labels',
'prefix' => 'Prefix', 'prefix' => 'Préfixe',
'counter' => 'Counter', 'counter' => 'Compteur',
'payment_type_dwolla' => 'Dwolla', 'payment_type_dwolla' => 'Dwolla',
'gateway_help_43' => ':link to sign up for Dwolla.', 'gateway_help_43' => ':link to sign up for Dwolla.',
@ -681,22 +680,73 @@ return array(
'pro_plan_feature7' => 'Customize Invoice Field Titles & Numbering', 'pro_plan_feature7' => 'Customize Invoice Field Titles & Numbering',
'pro_plan_feature8' => 'Option to Attach PDFs to Client Emails', 'pro_plan_feature8' => 'Option to Attach PDFs to Client Emails',
'resume' => 'Resume', 'resume' => 'Continuer',
'break_duration' => 'Break', 'break_duration' => 'Pause',
'edit_details' => 'Edit Details', 'edit_details' => 'Modifier',
'work' => 'Work', 'work' => 'Travail',
'timezone_unset' => 'Please :link to set your timezone', 'timezone_unset' => 'Please :link to set your timezone',
'click_here' => 'click here', 'click_here' => 'cliquer içi',
'email_receipt' => 'Email payment receipt to the client', 'email_receipt' => 'Email payment receipt to the client',
'created_payment_emailed_client' => 'Successfully created payment and emailed client', 'created_payment_emailed_client' => 'Successfully created payment and emailed client',
'add_account' => 'Add Account', 'add_company' => 'Add Company',
'untitled' => 'Untitled', 'untitled' => 'Untitled',
'new_account' => 'New Account', 'new_company' => 'New Company',
'associated_accounts' => 'Successfully linked accounts', 'associated_accounts' => 'Successfully linked accounts',
'unlinked_account' => 'Successfully unlinked accounts', 'unlinked_account' => 'Successfully unlinked accounts',
'login' => 'Login', 'login' => 'Login',
'or' => 'or', 'or' => 'or',
'email_error' => 'There was a problem sending the email',
'confirm_recurring_timing' => 'Note: emails are sent at the start of the hour.',
'old_browser' => 'Please use a <a href="'.OUTDATE_BROWSER_URL.'" target="_blank">newer browser</a>',
'payment_terms_help' => 'Sets the default invoice due date',
'unlink_account' => 'Unlink Account',
'unlink' => 'Unlink',
'show_address' => 'Show Address',
'show_address_help' => 'Require client to provide their billing address',
'update_address' => 'Update Address',
'update_address_help' => 'Update client\'s address with provided details',
'times' => 'Times',
'set_now' => 'Set now',
'dark_mode' => 'Dark Mode',
'dark_mode_help' => 'Show white text on black background',
'add_to_invoice' => 'Add to invoice :invoice',
'create_new_invoice' => 'Create new invoice',
'task_errors' => 'Please correct any overlapping times',
'from' => 'From',
'to' => 'To',
'font_size' => 'Font Size',
'primary_color' => 'Primary Color',
'secondary_color' => 'Secondary Color',
'customize_design' => 'Customize Design',
'content' => 'Content',
'styles' => 'Styles',
'defaults' => 'Defaults',
'margins' => 'Margins',
'header' => 'Header',
'footer' => 'Footer',
'custom' => 'Custom',
'invoice_to' => 'Invoice to',
'invoice_no' => 'Invoice No.',
'recent_payments' => 'Recent Payments',
'outstanding' => 'Outstanding',
'manage_companies' => 'Manage Companies',
'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',
'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>'
); );

View File

@ -592,7 +592,6 @@ return array(
'less_fields' => 'Less Fields', 'less_fields' => 'Less Fields',
'client_name' => 'Client Name', 'client_name' => 'Client Name',
'pdf_settings' => 'PDF Settings', 'pdf_settings' => 'PDF Settings',
'utf8_invoices' => 'Cyrillic Support <sup>Beta</sup>',
'product_settings' => 'Product Settings', 'product_settings' => 'Product Settings',
'auto_wrap' => 'Auto Line Wrap', 'auto_wrap' => 'Auto Line Wrap',
'duplicate_post' => 'Warning: the previous page was submitted twice. The second submission had been ignored.', 'duplicate_post' => 'Warning: the previous page was submitted twice. The second submission had been ignored.',
@ -692,14 +691,64 @@ return array(
'email_receipt' => 'Email payment receipt to the client', 'email_receipt' => 'Email payment receipt to the client',
'created_payment_emailed_client' => 'Successfully created payment and emailed client', 'created_payment_emailed_client' => 'Successfully created payment and emailed client',
'add_account' => 'Add Account', 'add_company' => 'Add Company',
'untitled' => 'Untitled', 'untitled' => 'Untitled',
'new_account' => 'New Account', 'new_company' => 'New Company',
'associated_accounts' => 'Successfully linked accounts', 'associated_accounts' => 'Successfully linked accounts',
'unlinked_account' => 'Successfully unlinked accounts', 'unlinked_account' => 'Successfully unlinked accounts',
'login' => 'Login', 'login' => 'Login',
'or' => 'or', 'or' => 'or',
'email_error' => 'There was a problem sending the email',
'confirm_recurring_timing' => 'Note: emails are sent at the start of the hour.',
'old_browser' => 'Please use a <a href="'.OUTDATE_BROWSER_URL.'" target="_blank">newer browser</a>',
'payment_terms_help' => 'Sets the default invoice due date',
'unlink_account' => 'Unlink Account',
'unlink' => 'Unlink',
'show_address' => 'Show Address',
'show_address_help' => 'Require client to provide their billing address',
'update_address' => 'Update Address',
'update_address_help' => 'Update client\'s address with provided details',
'times' => 'Times',
'set_now' => 'Set now',
'dark_mode' => 'Dark Mode',
'dark_mode_help' => 'Show white text on black background',
'add_to_invoice' => 'Add to invoice :invoice',
'create_new_invoice' => 'Create new invoice',
'task_errors' => 'Please correct any overlapping times',
'from' => 'From',
'to' => 'To',
'font_size' => 'Font Size',
'primary_color' => 'Primary Color',
'secondary_color' => 'Secondary Color',
'customize_design' => 'Customize Design',
'content' => 'Content',
'styles' => 'Styles',
'defaults' => 'Defaults',
'margins' => 'Margins',
'header' => 'Header',
'footer' => 'Footer',
'custom' => 'Custom',
'invoice_to' => 'Invoice to',
'invoice_no' => 'Invoice No.',
'recent_payments' => 'Recent Payments',
'outstanding' => 'Outstanding',
'manage_companies' => 'Manage Companies',
'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',
'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>'
); );

View File

@ -1,20 +1,19 @@
<?php <?php
return array( return [
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Pagination Language Lines | Puslapiavimo kalbos eilutės
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| The following language lines are used by the paginator library to build | Šios kalbos eilutės yra naudojamas puslapiavimo bibliotekos kurti
| the simple pagination links. You are free to change them to anything | paprastas puslapiavimo nuorodas. Jūs galite laisvai keisti jas
| you want to customize your views to better match your application. | į bet kokias kitas labiau tinkančias Jūsų programai.
| |
*/ */
'previous' => '&laquo; Previous', 'previous' => '&laquo; Ankstesnis',
'next' => 'Sekantis &raquo;',
'next' => 'Next &raquo;', ];
);

View File

@ -1,24 +1,21 @@
<?php <?php
return array( return [
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Password Reminder Language Lines | Slaptažodžio priminimo kalbos eilutės
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| The following language lines are the default lines which match reasons | Sekančios kalbos eilutės yra numatytos elutės, atitinkančios priežastims,
| that are given by the password broker for a password update attempt | pateikiamoms slatažodžių tarpininko, kai nepavyksta slaptažodžio atnaujinimo
| has failed, such as for an invalid token or invalid new password. | bandymas, tokioms kaip negaliojanti žymė ar neteisingas naujas slaptažodis..
| |
*/ */
"password" => "Passwords must be at least six characters and match the confirmation.", "password" => "Slaptažodis turi būti bent šešių simbolių ir sutapti su patvirtinimu.",
"user" => "Vartotojas su tokiu el. pašu nerastas.",
"user" => "We can't find a user with that e-mail address.", "token" => "Šis slaptažodžio raktas yra neteisingas.",
"sent" => "Naujo slaptažodžio nustatymo nuoroda išsiųsta",
"token" => "This password reset token is invalid.", "reset" => "Nustatytas naujas slaptažodis!",
];
"sent" => "Password reminder sent!",
);

View File

@ -600,7 +600,6 @@ return array(
'less_fields' => 'Less Fields', 'less_fields' => 'Less Fields',
'client_name' => 'Client Name', 'client_name' => 'Client Name',
'pdf_settings' => 'PDF Settings', 'pdf_settings' => 'PDF Settings',
'utf8_invoices' => 'Cyrillic Support <sup>Beta</sup>',
'product_settings' => 'Product Settings', 'product_settings' => 'Product Settings',
'auto_wrap' => 'Auto Line Wrap', 'auto_wrap' => 'Auto Line Wrap',
'duplicate_post' => 'Warning: the previous page was submitted twice. The second submission had been ignored.', 'duplicate_post' => 'Warning: the previous page was submitted twice. The second submission had been ignored.',
@ -699,14 +698,65 @@ return array(
'email_receipt' => 'Email payment receipt to the client', 'email_receipt' => 'Email payment receipt to the client',
'created_payment_emailed_client' => 'Successfully created payment and emailed client', 'created_payment_emailed_client' => 'Successfully created payment and emailed client',
'add_account' => 'Add Account', 'add_company' => 'Add Company',
'untitled' => 'Untitled', 'untitled' => 'Untitled',
'new_account' => 'New Account', 'new_company' => 'New Company',
'associated_accounts' => 'Successfully linked accounts', 'associated_accounts' => 'Successfully linked accounts',
'unlinked_account' => 'Successfully unlinked accounts', 'unlinked_account' => 'Successfully unlinked accounts',
'login' => 'Login', 'login' => 'Login',
'or' => 'or', 'or' => 'or',
'email_error' => 'There was a problem sending the email',
'confirm_recurring_timing' => 'Note: emails are sent at the start of the hour.',
'old_browser' => 'Please use a <a href="'.OUTDATE_BROWSER_URL.'" target="_blank">newer browser</a>',
'payment_terms_help' => 'Sets the default invoice due date',
'unlink_account' => 'Unlink Account',
'unlink' => 'Unlink',
'show_address' => 'Show Address',
'show_address_help' => 'Require client to provide their billing address',
'update_address' => 'Update Address',
'update_address_help' => 'Update client\'s address with provided details',
'times' => 'Times',
'set_now' => 'Set now',
'dark_mode' => 'Dark Mode',
'dark_mode_help' => 'Show white text on black background',
'add_to_invoice' => 'Add to invoice :invoice',
'create_new_invoice' => 'Create new invoice',
'task_errors' => 'Please correct any overlapping times',
'from' => 'From',
'to' => 'To',
'font_size' => 'Font Size',
'primary_color' => 'Primary Color',
'secondary_color' => 'Secondary Color',
'customize_design' => 'Customize Design',
'content' => 'Content',
'styles' => 'Styles',
'defaults' => 'Defaults',
'margins' => 'Margins',
'header' => 'Header',
'footer' => 'Footer',
'custom' => 'Custom',
'invoice_to' => 'Invoice to',
'invoice_no' => 'Invoice No.',
'recent_payments' => 'Recent Payments',
'outstanding' => 'Outstanding',
'manage_companies' => 'Manage Companies',
'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',
'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>'
); );

View File

@ -1,103 +1,108 @@
<?php <?php
return array( return [
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Validation Language Lines | Patvirtinimo kalbos eilutės
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| The following language lines contain the default error messages used by | Sekančios kalbos eilutėse yra numatyti klaidos pranešimai naudojami
| the validator class. Some of these rules have multiple versions such | patvirtinimo klasėje. Kai kurios šių eilučių turi keletą versijų
| such as the size rules. Feel free to tweak each of these messages. | tokių kaip dydžio taisyklės. Galite laisvai pataisyti bet kuriuos pranešimus.
| |
*/ */
"accepted" => "The :attribute must be accepted.", "accepted" => "Laukas :attribute turi būti priimtas.",
"active_url" => "The :attribute is not a valid URL.", "active_url" => "Laukas :attribute nėra galiojantis internetinis adresas.",
"after" => "The :attribute must be a date after :date.", "after" => "Laukelyje :attribute turi būti data po :date.",
"alpha" => "The :attribute may only contain letters.", "alpha" => "Laukas :attribute gali turėti tik raides.",
"alpha_dash" => "The :attribute may only contain letters, numbers, and dashes.", "alpha_dash" => "Laukas :attribute gali turėti tik raides, skaičius ir brūkšnelius.",
"alpha_num" => "The :attribute may only contain letters and numbers.", "alpha_num" => "Laukas :attribute gali turėti tik raides ir skaičius.",
"array" => "The :attribute must be an array.", "array" => "Laukas :attribute turi būti masyvas.",
"before" => "The :attribute must be a date before :date.", "before" => "Laukas :attribute turi būti data prieš :date.",
"between" => array( "between" => [
"numeric" => "The :attribute must be between :min - :max.", "numeric" => "Lauko :attribute reikšmė turi būti tarp :min ir :max.",
"file" => "The :attribute must be between :min - :max kilobytes.", "file" => "Failo dydis lauke :attribute turi būti tarp :min ir :max kilobaitų.",
"string" => "The :attribute must be between :min - :max characters.", "string" => "Simbolių skaičius lauke :attribute turi būti tarp :min ir :max.",
"array" => "The :attribute must have between :min - :max items.", "array" => "Elementų skaičius lauke :attribute turi turėti nuo :min iki :max.",
), ],
"confirmed" => "The :attribute confirmation does not match.", "boolean" => "Lauko reikšmė :attribute turi būti 'taip' arba 'ne'.",
"date" => "The :attribute is not a valid date.", "confirmed" => "Lauko :attribute patvirtinimas nesutampa.",
"date_format" => "The :attribute does not match the format :format.", "date" => "Lauko :attribute reikšmė nėra galiojanti data.",
"different" => "The :attribute and :other must be different.", "date_format" => "Lauko :attribute reikšmė neatitinka formato :format.",
"digits" => "The :attribute must be :digits digits.", "different" => "Laukų :attribute ir :other reikšmės turi skirtis.",
"digits_between" => "The :attribute must be between :min and :max digits.", "digits" => "Laukas :attribute turi būti sudarytas iš :digits skaitmenų.",
"email" => "The :attribute format is invalid.", "digits_between" => "Laukas :attribute tuti turėti nuo :min iki :max skaitmenų.",
"exists" => "The selected :attribute is invalid.", "email" => "Lauko :attribute reikšmė turi būti galiojantis el. pašto adresas.",
"image" => "The :attribute must be an image.", "filled" => "Laukas :attribute turi būti užpildytas.",
"in" => "The selected :attribute is invalid.", "exists" => "Pasirinkta negaliojanti :attribute reikšmė.",
"integer" => "The :attribute must be an integer.", "image" => "Lauko :attribute reikšmė turi būti paveikslėlis.",
"ip" => "The :attribute must be a valid IP address.", "in" => "Pasirinkta negaliojanti :attribute reikšmė.",
"max" => array( "integer" => "Lauko :attribute reikšmė turi būti veikasis skaičius.",
"numeric" => "The :attribute may not be greater than :max.", "ip" => "Lauko :attribute reikšmė turi būti galiojantis IP adresas.",
"file" => "The :attribute may not be greater than :max kilobytes.", "max" => [
"string" => "The :attribute may not be greater than :max characters.", "numeric" => "Lauko :attribute reikšmė negali būti didesnė nei :max.",
"array" => "The :attribute may not have more than :max items.", "file" => "Failo dydis lauke :attribute reikšmė negali būti didesnė nei :max kilobaitų.",
), "string" => "Simbolių kiekis lauke :attribute reikšmė negali būti didesnė nei :max simbolių.",
"mimes" => "The :attribute must be a file of type: :values.", "array" => "Elementų kiekis lauke :attribute negali turėti daugiau nei :max elementų.",
"min" => array( ],
"numeric" => "The :attribute must be at least :min.", "mimes" => "Lauko reikšmė :attribute turi būti failas vieno iš sekančių tipų: :values.",
"file" => "The :attribute must be at least :min kilobytes.", "min" => [
"string" => "The :attribute must be at least :min characters.", "numeric" => "Lauko :attribute reikšmė turi būti ne mažesnė nei :min.",
"array" => "The :attribute must have at least :min items.", "file" => "Failo dydis lauke :attribute turi būti ne mažesnis nei :min kilobaitų.",
), "string" => "Simbolių kiekis lauke :attribute turi būti ne mažiau nei :min.",
"not_in" => "The selected :attribute is invalid.", "array" => "Elementų kiekis lauke :attribute turi būti ne mažiau nei :min.",
"numeric" => "The :attribute must be a number.", ],
"regex" => "The :attribute format is invalid.", "not_in" => "Pasirinkta negaliojanti reikšmė :attribute.",
"required" => "The :attribute field is required.", "numeric" => "Lauko :attribute reikšmė turi būti skaičius.",
"required_if" => "The :attribute field is required when :other is :value.", "regex" => "Negaliojantis lauko :attribute formatas.",
"required_with" => "The :attribute field is required when :values is present.", "required" => "Privaloma užpildyti lauką :attribute.",
"required_without" => "The :attribute field is required when :values is not present.", "required_if" => "Privaloma užpildyti lauką :attribute kai :other yra :value.",
"same" => "The :attribute and :other must match.", "required_with" => "Privaloma užpildyti lauką :attribute kai pateikta :values.",
"size" => array( "required_with_all" => "Privaloma užpildyti lauką :attribute kai pateikta :values.",
"numeric" => "The :attribute must be :size.", "required_without" => "Privaloma užpildyti lauką :attribute kai nepateikta :values.",
"file" => "The :attribute must be :size kilobytes.", "required_without_all" => "Privaloma užpildyti lauką :attribute kai nepateikta nei viena iš reikšmių :values.",
"string" => "The :attribute must be :size characters.", "same" => "Laukai :attribute ir :other turi sutapti.",
"array" => "The :attribute must contain :size items.", "size" => [
), "numeric" => "Lauko :attribute reikšmė turi būti :size.",
"unique" => "The :attribute has already been taken.", "file" => "Failo dydis lauke :attribute turi būti :size kilobaitai.",
"url" => "The :attribute format is invalid.", "string" => "Simbolių skaičius lauke :attribute turi būti :size.",
"array" => "Elementų kiekis lauke :attribute turi būti :size.",
"positive" => "The :attribute must be greater than zero.", ],
"has_credit" => "The client does not have enough credit.", "string" => "The :attribute must be a string.",
"notmasked" => "The values are masked", "timezone" => "Lauko :attribute reikšmė turi būti galiojanti laiko zona.",
"less_than" => 'The :attribute must be less than :value', "unique" => "Tokia :attribute reikšmė jau pasirinkta.",
"url" => "Negaliojantis lauko :attribute formatas.",
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Custom Validation Language Lines | Pasirinktiniai patvirtinimo kalbos eilutės
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| Here you may specify custom validation messages for attributes using the | Čia galite nurodyti pasirinktinius patvirtinimo pranešimus, naudodami
| convention "attribute.rule" to name the lines. This makes it quick to | konvenciją "attribute.rule" eilučių pavadinimams. Tai leidžia greitai
| specify a specific custom language line for a given attribute rule. | nurodyti konkrečią pasirinktinę kalbos eilutę tam tikrai atributo taisyklei.
| |
*/ */
'custom' => array(), 'custom' => [
'attribute-name' => [
'rule-name' => 'custom-message',
],
],
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Custom Validation Attributes | Pasirinktiniai patvirtinimo atributai
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| The following language lines are used to swap attribute place-holders | Sekančios kalbos eilutės naudojamos pakeisti vietos žymes
| with something more reader friendly such as E-Mail Address instead | kuo nors labiau priimtinu skaitytojui (pvz. "El.Pašto Adresas" vietoj
| of "email". This simply helps us make messages a little cleaner. | "email". TTai tiesiog padeda mums padaryti žinutes truputi aiškesnėmis.
| |
*/ */
'attributes' => array(), 'attributes' => [],
); ];

View File

@ -598,7 +598,6 @@ return array(
'less_fields' => 'Less Fields', 'less_fields' => 'Less Fields',
'client_name' => 'Client Name', 'client_name' => 'Client Name',
'pdf_settings' => 'PDF Settings', 'pdf_settings' => 'PDF Settings',
'utf8_invoices' => 'Cyrillic Support <sup>Beta</sup>',
'product_settings' => 'Product Settings', 'product_settings' => 'Product Settings',
'auto_wrap' => 'Auto Line Wrap', 'auto_wrap' => 'Auto Line Wrap',
'duplicate_post' => 'Warning: the previous page was submitted twice. The second submission had been ignored.', 'duplicate_post' => 'Warning: the previous page was submitted twice. The second submission had been ignored.',
@ -697,14 +696,64 @@ return array(
'email_receipt' => 'Email payment receipt to the client', 'email_receipt' => 'Email payment receipt to the client',
'created_payment_emailed_client' => 'Successfully created payment and emailed client', 'created_payment_emailed_client' => 'Successfully created payment and emailed client',
'add_account' => 'Add Account', 'add_company' => 'Add Company',
'untitled' => 'Untitled', 'untitled' => 'Untitled',
'new_account' => 'New Account', 'new_company' => 'New Company',
'associated_accounts' => 'Successfully linked accounts', 'associated_accounts' => 'Successfully linked accounts',
'unlinked_account' => 'Successfully unlinked accounts', 'unlinked_account' => 'Successfully unlinked accounts',
'login' => 'Login', 'login' => 'Login',
'or' => 'or', 'or' => 'or',
'email_error' => 'There was a problem sending the email',
'confirm_recurring_timing' => 'Note: emails are sent at the start of the hour.',
'old_browser' => 'Please use a <a href="'.OUTDATE_BROWSER_URL.'" target="_blank">newer browser</a>',
'payment_terms_help' => 'Sets the default invoice due date',
'unlink_account' => 'Unlink Account',
'unlink' => 'Unlink',
'show_address' => 'Show Address',
'show_address_help' => 'Require client to provide their billing address',
'update_address' => 'Update Address',
'update_address_help' => 'Update client\'s address with provided details',
'times' => 'Times',
'set_now' => 'Set now',
'dark_mode' => 'Dark Mode',
'dark_mode_help' => 'Show white text on black background',
'add_to_invoice' => 'Add to invoice :invoice',
'create_new_invoice' => 'Create new invoice',
'task_errors' => 'Please correct any overlapping times',
'from' => 'From',
'to' => 'To',
'font_size' => 'Font Size',
'primary_color' => 'Primary Color',
'secondary_color' => 'Secondary Color',
'customize_design' => 'Customize Design',
'content' => 'Content',
'styles' => 'Styles',
'defaults' => 'Defaults',
'margins' => 'Margins',
'header' => 'Header',
'footer' => 'Footer',
'custom' => 'Custom',
'invoice_to' => 'Invoice to',
'invoice_no' => 'Invoice No.',
'recent_payments' => 'Recent Payments',
'outstanding' => 'Outstanding',
'manage_companies' => 'Manage Companies',
'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',
'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>'
); );

View File

@ -313,38 +313,38 @@ return array(
'pro_plan_success' => 'Bedankt voor het aanmelden! Zodra je factuur betaald is zal je Pro Plan lidmaatschap beginnen.', 'pro_plan_success' => 'Bedankt voor het aanmelden! Zodra je factuur betaald is zal je Pro Plan lidmaatschap beginnen.',
'unsaved_changes' => 'Je hebt niet bewaarde wijzigingen', 'unsaved_changes' => 'Je hebt niet bewaarde wijzigingen',
'custom_fields' => 'Custom fields', 'custom_fields' => 'Aangepaste velden',
'company_fields' => 'Company Fields', 'company_fields' => 'Velden Bedrijf',
'client_fields' => 'Client Fields', 'client_fields' => 'Velden Klant',
'field_label' => 'Field Label', 'field_label' => 'Label Veld',
'field_value' => 'Field Value', 'field_value' => 'Waarde Veld',
'edit' => 'Bewerk', 'edit' => 'Bewerk',
'view_invoice' => 'Bekijk factuur', 'view_invoice' => 'Bekijk factuur',
'view_as_recipient' => 'Bekijk als ontvanger', 'view_as_recipient' => 'Bekijk als ontvanger',
// product management // product management
'product_library' => 'Product Library', 'product_library' => 'Product Bibliotheek',
'product' => 'Product', 'product' => 'Product',
'products' => 'Products', 'products' => 'Producten',
'fill_products' => 'Auto-fill products', 'fill_products' => 'Producten Automatisch aanvullen',
'fill_products_help' => 'Selecting a product will automatically <b>set the description and cost</b>', 'fill_products_help' => 'Een product selecteren zal automatisch <b>de beschrijving en kost instellen</b>',
'update_products' => 'Auto-update products', 'update_products' => 'Producten automatisch aanpassen',
'update_products_help' => 'Updating an invoice will automatically <b>update the products</b>', 'update_products_help' => 'Aanpassen van een factuur zal automatisch <b>de producten aanpassen</b>',
'create_product' => 'Create Product', 'create_product' => 'Product maken',
'edit_product' => 'Edit Product', 'edit_product' => 'Product aanpassen',
'archive_product' => 'Archive Product', 'archive_product' => 'Product Archiveren',
'updated_product' => 'Successfully updated product', 'updated_product' => 'Product Succesvol aangepast',
'created_product' => 'Successfully created product', 'created_product' => 'Product Succesvol aangemaakt',
'archived_product' => 'Successfully archived product', 'archived_product' => 'Product Succesvol gearchiveerd',
'pro_plan_custom_fields' => ':link to enable custom fields by joining the Pro Plan', 'pro_plan_custom_fields' => ':link om aangepaste velden in te schakelen door het Pro Plan te nemen',
'advanced_settings' => 'Geavanceerde instellingen', 'advanced_settings' => 'Geavanceerde instellingen',
'pro_plan_advanced_settings' => ':link to enable the advanced settings by joining the Pro Plan', 'pro_plan_advanced_settings' => ':link om de geavanceerde instellingen te activeren door het Pro Plan te nemen',
'invoice_design' => 'Factuur ontwerp', 'invoice_design' => 'Factuur ontwerp',
'specify_colors' => 'Kies kleuren', 'specify_colors' => 'Kies kleuren',
'specify_colors_label' => 'Kies de kleuren die in de factuur gebruikt worden', 'specify_colors_label' => 'Kies de kleuren die in de factuur gebruikt worden',
'chart_builder' => 'Chart Builder', 'chart_builder' => 'Grafiek bouwer',
'ninja_email_footer' => 'Gebruik :site om uw klanten gratis te factureren en betalingen te ontvangen!', 'ninja_email_footer' => 'Gebruik :site om uw klanten gratis te factureren en betalingen te ontvangen!',
'go_pro' => 'Go Pro', 'go_pro' => 'Go Pro',
@ -395,9 +395,9 @@ return array(
'invoice_fields' => 'Factuur Velden', 'invoice_fields' => 'Factuur Velden',
'invoice_options' => 'Factuur Opties', 'invoice_options' => 'Factuur Opties',
'hide_quantity' => 'Verberg aantallen', 'hide_quantity' => 'Verberg aantallen',
'hide_quantity_help' => 'Als us artikel-aantallen altijd 1 zijn, kunt u uw facturen er netter uit laten zien door dit veld te verbergen.', 'hide_quantity_help' => 'Als de artikel-aantallen altijd 1 zijn, kunt u uw facturen netter maken door dit veld te verbergen.',
'hide_paid_to_date' => 'Hide paid to date', 'hide_paid_to_date' => 'Verberg "Reeds betaald"',
'hide_paid_to_date_help' => 'Only display the "Paid to Date" area on your invoices once a payment has been received.', 'hide_paid_to_date_help' => 'Toon alleen het "Reeds betaald" gebied op je facturen als er een betaling gemaakt is.',
'charge_taxes' => 'Charge taxes', 'charge_taxes' => 'Charge taxes',
'user_management' => 'Gebruikersbeheer', 'user_management' => 'Gebruikersbeheer',
@ -439,15 +439,15 @@ return array(
'mark_sent' => 'Markeer als verzonden', 'mark_sent' => 'Markeer als verzonden',
'gateway_help_1' => ':link to sign up for Authorize.net.', 'gateway_help_1' => ':link om in te schrijven voor Authorize.net.',
'gateway_help_2' => ':link to sign up for Authorize.net.', 'gateway_help_2' => ':link om in te schrijven voor Authorize.net.',
'gateway_help_17' => ':link to get your PayPal API signature.', 'gateway_help_17' => ':link om je PayPal API signature te krijgen.',
'gateway_help_23' => 'Note: use your secret API key, not your publishable API key.', 'gateway_help_23' => 'Opmerking: gebruik je gehieme API key, niet je publiceerbare API key.',
'gateway_help_27' => ':link to sign up for TwoCheckout.', 'gateway_help_27' => ':link om in te schrijven voor TwoCheckout.',
'more_designs' => 'Meer ontwerpen', 'more_designs' => 'Meer ontwerpen',
'more_designs_title' => 'Aanvullende Factuur Ontwerpen', 'more_designs_title' => 'Aanvullende Factuur Ontwerpen',
'more_designs_cloud_header' => 'Go Pro for more invoice designs', 'more_designs_cloud_header' => 'Neem Pro Plan voor meer factuur ontwerpen',
'more_designs_cloud_text' => '', 'more_designs_cloud_text' => '',
'more_designs_self_host_header' => 'Krijg 6 extra factuurontwerpen voor maar $'.INVOICE_DESIGNS_PRICE, 'more_designs_self_host_header' => 'Krijg 6 extra factuurontwerpen voor maar $'.INVOICE_DESIGNS_PRICE,
'more_designs_self_host_text' => '', 'more_designs_self_host_text' => '',
@ -458,12 +458,12 @@ return array(
'sent' => 'verzonden', 'sent' => 'verzonden',
'timesheets' => 'Timesheets', 'timesheets' => 'Timesheets',
'payment_title' => 'Enter Your Billing Address and Credit Card information', 'payment_title' => 'Geef je betalingsadres en kredietkaart gegevens op',
'payment_cvv' => '*This is the 3-4 digit number onthe back of your card', 'payment_cvv' => '*Dit is de code van 3-4 tekens op de achterkant van je kaart',
'payment_footer1' => '*Billing address must match address associated with credit card.', 'payment_footer1' => '*Betalingsadres moet overeenkomen met het adres dat aan je kaart gekoppekd is.',
'payment_footer2' => '*Please click "PAY NOW" only once - transaction may take up to 1 minute to process.', 'payment_footer2' => '*Klik alstublieft slechts 1 keer op "PAY NOW" - verwerking kan tot 1 minuut duren.',
'vat_number' => 'Vat Number', 'vat_number' => 'BTW Nummer',
'id_number' => 'ID Number', 'id_number' => 'ID Nummer',
'white_label_link' => 'White label', 'white_label_link' => 'White label',
'white_label_text' => 'Koop een white label licentie voor $'.WHITE_LABEL_PRICE.' om de Invoice Ninja merknaam te verwijderen uit de bovenkant van de klantenpagina\'s.', 'white_label_text' => 'Koop een white label licentie voor $'.WHITE_LABEL_PRICE.' om de Invoice Ninja merknaam te verwijderen uit de bovenkant van de klantenpagina\'s.',
@ -508,18 +508,18 @@ return array(
'reset_all' => 'Reset Alles', 'reset_all' => 'Reset Alles',
'approve' => 'Goedkeuren', 'approve' => 'Goedkeuren',
'token_billing_type_id' => 'Token Billing', 'token_billing_type_id' => 'Betalingstoken',
'token_billing_help' => 'Enables you to store credit cards with your gateway, and charge them at a later date.', 'token_billing_help' => 'Laat je toe om kredietkaart gegevens bij je gateway op te slaan en ze later te gebruiken.',
'token_billing_1' => 'Disabled', 'token_billing_1' => 'Inactief',
'token_billing_2' => 'Opt-in - checkbox is shown but not selected', 'token_billing_2' => 'Opt-in - checkbox is getoond maar niet geselecteerd',
'token_billing_3' => 'Opt-out - checkbox is shown and selected', 'token_billing_3' => 'Opt-out - checkbox is getoond en geselecteerd',
'token_billing_4' => 'Always', 'token_billing_4' => 'Altijd',
'token_billing_checkbox' => 'Store credit card details', 'token_billing_checkbox' => 'Sla kredietkaart gegevens op',
'view_in_stripe' => 'View in Stripe', 'view_in_stripe' => 'In Stripe bekijken',
'use_card_on_file' => 'Use card on file', 'use_card_on_file' => 'Use card on file',
'edit_payment_details' => 'Edit payment details', 'edit_payment_details' => 'Betalingsdetails aanpassen',
'token_billing' => 'Save card details', 'token_billing' => 'Kaartgegevens opslaan',
'token_billing_secure' => 'The data is stored securely by :stripe_link', 'token_billing_secure' => 'De gegevens zijn succesvol veilig opgeslaan door :stripe_link',
'support' => 'Ondersteuning', 'support' => 'Ondersteuning',
'contact_information' => 'Contact informatie', 'contact_information' => 'Contact informatie',
@ -546,14 +546,14 @@ return array(
'delete_token' => 'Verwijder Token', 'delete_token' => 'Verwijder Token',
'token' => 'Token', 'token' => 'Token',
'add_gateway' => 'Add Gateway', 'add_gateway' => 'Gateway Toevoegen',
'delete_gateway' => 'Delete Gateway', 'delete_gateway' => 'Gateway Verwijderen',
'edit_gateway' => 'Edit Gateway', 'edit_gateway' => 'Gateway Aanpassen',
'updated_gateway' => 'Successfully updated gateway', 'updated_gateway' => 'Gateway Succesvol aangepast',
'created_gateway' => 'Successfully created gateway', 'created_gateway' => 'Gateway Succesvol aangemaakt',
'deleted_gateway' => 'Successfully deleted gateway', 'deleted_gateway' => 'Gateway Succesvol verwijderd',
'pay_with_paypal' => 'PayPal', 'pay_with_paypal' => 'PayPal',
'pay_with_card' => 'Credit card', 'pay_with_card' => 'Kredietkaart',
'change_password' => 'Verander wachtwoord', 'change_password' => 'Verander wachtwoord',
'current_password' => 'Huidig wachtwoord', 'current_password' => 'Huidig wachtwoord',
@ -575,130 +575,179 @@ return array(
'set_password' => 'Stel wachtwoord in', 'set_password' => 'Stel wachtwoord in',
'converted' => 'Omgezet', 'converted' => 'Omgezet',
'email_approved' => 'Email me when a quote is <b>approved</b>', 'email_approved' => 'Email me wanneer een offerte is <b>goedgekeurd</b>',
'notification_quote_approved_subject' => 'Quote :invoice was approved by :client', 'notification_quote_approved_subject' => 'Offerte :invoice is goedgekeurd door :client',
'notification_quote_approved' => 'The following client :client approved Quote :invoice for :amount.', 'notification_quote_approved' => 'De volgende Klant :client heeft Offerte :invoice goedgekeurd voor :amount.',
'resend_confirmation' => 'Resend confirmation email', 'resend_confirmation' => 'Verstuurd bevestingsmail opnieuw',
'confirmation_resent' => 'The confirmation email was resent', 'confirmation_resent' => 'De bevestigingsmail is opnieuw verstuurd',
'gateway_help_42' => ':link to sign up for BitPay.<br/>Note: use a Legacy API Key, not an API token.', 'gateway_help_42' => ':link om te registreren voor BitPay.<br/>Opmerking: gebruik een Legacy API Key, niet een API token.',
'payment_type_credit_card' => 'Credit card', 'payment_type_credit_card' => 'Kredietkaart',
'payment_type_paypal' => 'PayPal', 'payment_type_paypal' => 'PayPal',
'payment_type_bitcoin' => 'Bitcoin', 'payment_type_bitcoin' => 'Bitcoin',
'knowledge_base' => 'Knowledge Base', 'knowledge_base' => 'Kennis databank',
'partial' => 'Partial', 'partial' => 'Gedeeld',
'partial_remaining' => ':partial of :balance', 'partial_remaining' => ':partial / :balance',
'more_fields' => 'More Fields', 'more_fields' => 'Meer velden',
'less_fields' => 'Less Fields', 'less_fields' => 'Minder velden',
'client_name' => 'Client Name', 'client_name' => 'Klant Naam',
'pdf_settings' => 'PDF Settings', 'pdf_settings' => 'PDF Instellingen',
'utf8_invoices' => 'Cyrillic Support <sup>Beta</sup>', 'product_settings' => 'Product Instellingen',
'product_settings' => 'Product Settings', 'auto_wrap' => 'Automatisch Lijn afbreken',
'auto_wrap' => 'Auto Line Wrap', 'duplicate_post' => 'Opgelet: de volgende pagina is twee keer doorgestuurd. De tweede verzending is genegeerd.',
'duplicate_post' => 'Warning: the previous page was submitted twice. The second submission had been ignored.', 'view_documentation' => 'Bekijk Documentatie',
'view_documentation' => 'View Documentation', 'app_title' => 'Gratis Open-Source Online Facturatie',
'app_title' => 'Free Open-Source Online Invoicing', 'app_description' => 'Invoice Ninja is een gratis, open-source oplossing voor het aanmkaen en versturen van facturen aan klanten. Met Invoice Ninja, kan je gemakkelijk mooie facturen aanmaken en verzenden van om het even welk toestel met internettoegang. Je klanten kunnen je facturen afprinten, downloaden als pdf bestanden, en je zelfs online betalen vanuit het systeem.',
'app_description' => 'Invoice Ninja is a free, open-source solution for invoicing and billing customers. With Invoice Ninja, you can easily build and send beautiful invoices from any device that has access to the web. Your clients can print your invoices, download them as pdf files, and even pay you online from within the system.',
'rows' => 'rows', 'rows' => 'rijen',
'www' => 'www', 'www' => 'www',
'logo' => 'Logo', 'logo' => 'Logo',
'subdomain' => 'Subdomain', 'subdomain' => 'Subdomein',
'provide_name_or_email' => 'Please provide a contact name or email', 'provide_name_or_email' => 'Geef aub een contact naam en email op',
'charts_and_reports' => 'Charts & Reports', 'charts_and_reports' => 'Grafieken & Rapporten',
'chart' => 'Chart', 'chart' => 'Grafiek',
'report' => 'Report', 'report' => 'Rapport',
'group_by' => 'Group by', 'group_by' => 'Groepeer per',
'paid' => 'Paid', 'paid' => 'Betaald',
'enable_report' => 'Report', 'enable_report' => 'Rapport',
'enable_chart' => 'Chart', 'enable_chart' => 'Grafiek',
'totals' => 'Totals', 'totals' => 'Totalen',
'run' => 'Run', 'run' => 'Uitvoeren',
'export' => 'Export', 'export' => 'Exporteer',
'documentation' => 'Documentation', 'documentation' => 'Documentatie',
'zapier' => 'Zapier <sup>Beta</sup>', 'zapier' => 'Zapier <sup>Beta</sup>',
'recurring' => 'Recurring', 'recurring' => 'Terugkerend',
'last_invoice_sent' => 'Last invoice sent :date', 'last_invoice_sent' => 'Laatste factuur verzonden :date',
'processed_updates' => 'Successfully completed update', 'processed_updates' => 'Update succesvol uitgevoerd',
'tasks' => 'Tasks', 'tasks' => 'Taken',
'new_task' => 'New Task', 'new_task' => 'Nieuwe Taak',
'start_time' => 'Start Time', 'start_time' => 'Start Tijd',
'created_task' => 'Successfully created task', 'created_task' => 'Taak succesvol aangemaakt',
'updated_task' => 'Successfully updated task', 'updated_task' => 'Taak succesvol aangepast',
'edit_task' => 'Edit Task', 'edit_task' => 'Pas Taak aan',
'archive_task' => 'Archive Task', 'archive_task' => 'Archiveer Taak',
'restore_task' => 'Restore Task', 'restore_task' => 'Taak herstellen',
'delete_task' => 'Delete Task', 'delete_task' => 'Verwijder Taak',
'stop_task' => 'Stop Task', 'stop_task' => 'Stop Taak',
'time' => 'Time', 'time' => 'Tijd',
'start' => 'Start', 'start' => 'Start',
'stop' => 'Stop', 'stop' => 'Stop',
'now' => 'Now', 'now' => 'Nu',
'timer' => 'Timer', 'timer' => 'Timer',
'manual' => 'Manual', 'manual' => 'Manueel',
'date_and_time' => 'Date & Time', 'date_and_time' => 'Datum & Tijd',
'second' => 'second', 'second' => 'second',
'seconds' => 'seconds', 'seconds' => 'seconden',
'minute' => 'minute', 'minute' => 'minuut',
'minutes' => 'minutes', 'minutes' => 'minuten',
'hour' => 'hour', 'hour' => 'uur',
'hours' => 'hours', 'hours' => 'uren',
'task_details' => 'Task Details', 'task_details' => 'Taak Details',
'duration' => 'Duration', 'duration' => 'Duur',
'end_time' => 'End Time', 'end_time' => 'Eind Tijd',
'end' => 'End', 'end' => 'Einde',
'invoiced' => 'Invoiced', 'invoiced' => 'Gefactureerd',
'logged' => 'Logged', 'logged' => 'Gelogd',
'running' => 'Running', 'running' => 'Lopend',
'task_error_multiple_clients' => 'The tasks can\'t belong to different clients', 'task_error_multiple_clients' => 'Taken kunnen niet tot meerdere klanten behoren',
'task_error_running' => 'Please stop running tasks first', 'task_error_running' => 'Stop aub de lopende taken eerst',
'task_error_invoiced' => 'Tasks have already been invoiced', 'task_error_invoiced' => 'Deze taken zijn al gefactureerd',
'restored_task' => 'Successfully restored task', 'restored_task' => 'Taak succesvol hersteld',
'archived_task' => 'Successfully archived task', 'archived_task' => 'Taak succesvol gearchiveerd',
'archived_tasks' => 'Successfully archived :count tasks', 'archived_tasks' => ':count taken succesvol gearchiveerd',
'deleted_task' => 'Successfully deleted task', 'deleted_task' => 'Taak succesvol verwijderd',
'deleted_tasks' => 'Successfully deleted :count tasks', 'deleted_tasks' => ':count taken succesvol verwijderd',
'create_task' => 'Create Task', 'create_task' => 'Taak aanmaken',
'stopped_task' => 'Successfully stopped task', 'stopped_task' => 'Taak succesvol gestopt',
'invoice_task' => 'Invoice Task', 'invoice_task' => 'Factuur taak',
'invoice_labels' => 'Invoice Labels', 'invoice_labels' => 'Factuur labels',
'prefix' => 'Prefix', 'prefix' => 'Voorvoegsel',
'counter' => 'Counter', 'counter' => 'Teller',
'payment_type_dwolla' => 'Dwolla', 'payment_type_dwolla' => 'Dwolla',
'gateway_help_43' => ':link to sign up for Dwolla.', 'gateway_help_43' => ':link om in te schrijven voor Dwolla.',
'partial_value' => 'Must be greater than zero and less than the total', 'partial_value' => 'Moet groter zijn dan nul en minder dan het totaal',
'more_actions' => 'More Actions', 'more_actions' => 'Meer acties',
'pro_plan_title' => 'NINJA PRO', 'pro_plan_title' => 'NINJA PRO',
'pro_plan_call_to_action' => 'Upgrade Now!', 'pro_plan_call_to_action' => 'Nu upgraden!',
'pro_plan_feature1' => 'Create Unlimited Clients', 'pro_plan_feature1' => 'Maak ongelimiteerd klanten aan',
'pro_plan_feature2' => 'Access to 10 Beautiful Invoice Designs', 'pro_plan_feature2' => 'Toegang tot 10 mooie factuur ontwerpen',
'pro_plan_feature3' => 'Custom URLs - "YourBrand.InvoiceNinja.com"', 'pro_plan_feature3' => 'Aangepaste URLs - "YourBrand.InvoiceNinja.com"',
'pro_plan_feature4' => 'Remove "Created by Invoice Ninja"', 'pro_plan_feature4' => 'Verwijder "Aangemaakt door Invoice Ninja"',
'pro_plan_feature5' => 'Multi-user Access & Activity Tracking', 'pro_plan_feature5' => 'Multi-user toegang & Activeit Tracking',
'pro_plan_feature6' => 'Create Quotes & Pro-forma Invoices', 'pro_plan_feature6' => 'Maak offertes & Pro-forma facturen aan',
'pro_plan_feature7' => 'Customize Invoice Field Titles & Numbering', 'pro_plan_feature7' => 'Pas factuur veld titels & nummering aan',
'pro_plan_feature8' => 'Option to Attach PDFs to Client Emails', 'pro_plan_feature8' => 'Optie om PDFs toe te voegen aan de emails naar klanten',
'resume' => 'Resume', 'resume' => 'Doorgaan',
'break_duration' => 'Break', 'break_duration' => 'Pauze',
'edit_details' => 'Edit Details', 'edit_details' => 'Details aanpassen',
'work' => 'Work', 'work' => 'Werk',
'timezone_unset' => 'Please :link to set your timezone', 'timezone_unset' => ':link om je tijdszone aan te passen',
'click_here' => 'click here', 'click_here' => 'Klik hier',
'email_receipt' => 'Email payment receipt to the client', 'email_receipt' => 'Mail betalingsbewijs naar de klant',
'created_payment_emailed_client' => 'Successfully created payment and emailed client', 'created_payment_emailed_client' => 'Betaling succesvol toegevoegd en gemaild naar de klant',
'add_account' => 'Add Account', 'add_company' => 'Bedrijf toevoegen',
'untitled' => 'Untitled', 'untitled' => 'Zonder titel',
'new_account' => 'New Account', 'new_company' => 'Nieuw bedrijf',
'associated_accounts' => 'Successfully linked accounts', 'associated_accounts' => 'Accounts succesvol gekoppeld',
'unlinked_account' => 'Successfully unlinked accounts', 'unlinked_account' => 'Accounts succesvol losgekoppeld',
'login' => 'Login', 'login' => 'Login',
'or' => 'or', 'or' => 'of',
'email_error' => 'Er was een probleem om de email te verzenden',
'confirm_recurring_timing' => 'Opmerking: emails worden aan het begin van het uur verzonden.',
'old_browser' => 'Gebruik aub een <a href="'.OUTDATE_BROWSER_URL.'" target="_blank">nieuwere browser</a>',
'payment_terms_help' => 'Stel de standaard factuur vervaldatum in',
'unlink_account' => 'Koppel account los',
'unlink' => 'Koppel los',
'show_address' => 'Toon Adres',
'show_address_help' => 'Verplicht de klant om zijn factuur adres op te geven',
'update_address' => 'Adres aanpassen',
'update_address_help' => 'Pas het adres van de klant aan met de ingevulde gegevens',
'times' => 'Tijden',
'set_now' => 'Start nu',
'dark_mode' => 'Donkere modus',
'dark_mode_help' => 'Toon witte tekst op een donkere achtergrond',
'add_to_invoice' => 'Toevoegen aan factuur :invoice',
'create_new_invoice' => 'Maak een nieuwe factuur',
'task_errors' => 'Pas overlappende tijden aan aub.',
'from' => 'Van',
'to' => 'Aan',
'font_size' => 'Tekstgrootte',
'primary_color' => 'Primaire kleur',
'secondary_color' => 'Secundaire kleur',
'customize_design' => 'Pas design aan',
'content' => 'Inhoud',
'styles' => 'Stijlen',
'defaults' => 'Standaardwaarden',
'margins' => 'Marges',
'header' => 'Header',
'footer' => 'Footer',
'custom' => 'Aangepast',
'invoice_to' => 'Factuur aan',
'invoice_no' => 'Factuur Nr.',
'recent_payments' => 'Recente betalingen',
'outstanding' => 'Uitstaand',
'manage_companies' => 'Beheer bedrijven',
'total_revenue' => 'Totale opbrengst',
'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',
'customize_help' => '<p>We gebruiken <a href="http://pdfmake.org/" target="_blank">pdfmake</a> om de factuur ontwerpen declaratief te definieren. De pdfmake <a href="http://pdfmake.org/playground.html" target="_blank">playground</a> is een interessante manier om de library in actie te zien.</p>
<p>Je kan elk factuur veld gebruiken door <code>Veld</code> toe te voegen op het einde. Bijvoorbeeld <code>$invoiceNumberValue</code> toont de factuur nummer.</p>
<p>Gebruik dot notatie om een "kind eigenschap" te gebruiken. Bijvoorbeeld voor de klant naam te tonen gebruik je <code>$client.nameValue</code>.</p>
<p>Als je ergens hulp bij nodig hebt, post dan een vraag op ons <a href="https://www.invoiceninja.com/forums/forum/support/" target="_blank">support forum</a>.</p>'

View File

@ -73,8 +73,8 @@ return array(
"positive" => ":attribute moet groter zijn dan nul.", "positive" => ":attribute moet groter zijn dan nul.",
"has_credit" => "De klant heeft niet voldoende krediet.", "has_credit" => "De klant heeft niet voldoende krediet.",
"notmasked" => "The values are masked", "notmasked" => "De waarden zijn verborgen",
"less_than" => 'The :attribute must be less than :value', "less_than" => 'Het :attribute moet minder zijn dan :value',
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

View File

@ -593,7 +593,6 @@ return array(
'less_fields' => 'Less Fields', 'less_fields' => 'Less Fields',
'client_name' => 'Client Name', 'client_name' => 'Client Name',
'pdf_settings' => 'PDF Settings', 'pdf_settings' => 'PDF Settings',
'utf8_invoices' => 'Cyrillic Support <sup>Beta</sup>',
'product_settings' => 'Product Settings', 'product_settings' => 'Product Settings',
'auto_wrap' => 'Auto Line Wrap', 'auto_wrap' => 'Auto Line Wrap',
'duplicate_post' => 'Warning: the previous page was submitted twice. The second submission had been ignored.', 'duplicate_post' => 'Warning: the previous page was submitted twice. The second submission had been ignored.',
@ -692,13 +691,64 @@ return array(
'email_receipt' => 'Email payment receipt to the client', 'email_receipt' => 'Email payment receipt to the client',
'created_payment_emailed_client' => 'Successfully created payment and emailed client', 'created_payment_emailed_client' => 'Successfully created payment and emailed client',
'add_account' => 'Add Account', 'add_company' => 'Add Company',
'untitled' => 'Untitled', 'untitled' => 'Untitled',
'new_account' => 'New Account', 'new_company' => 'New Company',
'associated_accounts' => 'Successfully linked accounts', 'associated_accounts' => 'Successfully linked accounts',
'unlinked_account' => 'Successfully unlinked accounts', 'unlinked_account' => 'Successfully unlinked accounts',
'login' => 'Login', 'login' => 'Login',
'or' => 'or', 'or' => 'or',
'email_error' => 'There was a problem sending the email',
'confirm_recurring_timing' => 'Note: emails are sent at the start of the hour.',
'old_browser' => 'Please use a <a href="'.OUTDATE_BROWSER_URL.'" target="_blank">newer browser</a>',
'payment_terms_help' => 'Sets the default invoice due date',
'unlink_account' => 'Unlink Account',
'unlink' => 'Unlink',
'show_address' => 'Show Address',
'show_address_help' => 'Require client to provide their billing address',
'update_address' => 'Update Address',
'update_address_help' => 'Update client\'s address with provided details',
'times' => 'Times',
'set_now' => 'Set now',
'dark_mode' => 'Dark Mode',
'dark_mode_help' => 'Show white text on black background',
'add_to_invoice' => 'Add to invoice :invoice',
'create_new_invoice' => 'Create new invoice',
'task_errors' => 'Please correct any overlapping times',
'from' => 'From',
'to' => 'To',
'font_size' => 'Font Size',
'primary_color' => 'Primary Color',
'secondary_color' => 'Secondary Color',
'customize_design' => 'Customize Design',
'content' => 'Content',
'styles' => 'Styles',
'defaults' => 'Defaults',
'margins' => 'Margins',
'header' => 'Header',
'footer' => 'Footer',
'custom' => 'Custom',
'invoice_to' => 'Invoice to',
'invoice_no' => 'Invoice No.',
'recent_payments' => 'Recent Payments',
'outstanding' => 'Outstanding',
'manage_companies' => 'Manage Companies',
'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',
'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>'
); );

View File

@ -596,7 +596,6 @@ return array(
'less_fields' => 'Less Fields', 'less_fields' => 'Less Fields',
'client_name' => 'Client Name', 'client_name' => 'Client Name',
'pdf_settings' => 'PDF Settings', 'pdf_settings' => 'PDF Settings',
'utf8_invoices' => 'Cyrillic Support <sup>Beta</sup>',
'product_settings' => 'Product Settings', 'product_settings' => 'Product Settings',
'auto_wrap' => 'Auto Line Wrap', 'auto_wrap' => 'Auto Line Wrap',
'duplicate_post' => 'Warning: the previous page was submitted twice. The second submission had been ignored.', 'duplicate_post' => 'Warning: the previous page was submitted twice. The second submission had been ignored.',
@ -695,14 +694,65 @@ return array(
'email_receipt' => 'Email payment receipt to the client', 'email_receipt' => 'Email payment receipt to the client',
'created_payment_emailed_client' => 'Successfully created payment and emailed client', 'created_payment_emailed_client' => 'Successfully created payment and emailed client',
'add_account' => 'Add Account', 'add_company' => 'Add Company',
'untitled' => 'Untitled', 'untitled' => 'Untitled',
'new_account' => 'New Account', 'new_company' => 'New Company',
'associated_accounts' => 'Successfully linked accounts', 'associated_accounts' => 'Successfully linked accounts',
'unlinked_account' => 'Successfully unlinked accounts', 'unlinked_account' => 'Successfully unlinked accounts',
'login' => 'Login', 'login' => 'Login',
'or' => 'or', 'or' => 'or',
'email_error' => 'There was a problem sending the email',
'confirm_recurring_timing' => 'Note: emails are sent at the start of the hour.',
'old_browser' => 'Please use a <a href="'.OUTDATE_BROWSER_URL.'" target="_blank">newer browser</a>',
'payment_terms_help' => 'Sets the default invoice due date',
'unlink_account' => 'Unlink Account',
'unlink' => 'Unlink',
'show_address' => 'Show Address',
'show_address_help' => 'Require client to provide their billing address',
'update_address' => 'Update Address',
'update_address_help' => 'Update client\'s address with provided details',
'times' => 'Times',
'set_now' => 'Set now',
'dark_mode' => 'Dark Mode',
'dark_mode_help' => 'Show white text on black background',
'add_to_invoice' => 'Add to invoice :invoice',
'create_new_invoice' => 'Create new invoice',
'task_errors' => 'Please correct any overlapping times',
'from' => 'From',
'to' => 'To',
'font_size' => 'Font Size',
'primary_color' => 'Primary Color',
'secondary_color' => 'Secondary Color',
'customize_design' => 'Customize Design',
'content' => 'Content',
'styles' => 'Styles',
'defaults' => 'Defaults',
'margins' => 'Margins',
'header' => 'Header',
'footer' => 'Footer',
'custom' => 'Custom',
'invoice_to' => 'Invoice to',
'invoice_no' => 'Invoice No.',
'recent_payments' => 'Recent Payments',
'outstanding' => 'Outstanding',
'manage_companies' => 'Manage Companies',
'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',
'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>'
); );

View File

@ -14,9 +14,12 @@
<div class="panel-body"> <div class="panel-body">
@if ($accountGateway) @if ($accountGateway)
{!! Former::populateField('payment_type_id', $paymentTypeId) !!}
{!! Former::populateField('gateway_id', $accountGateway->gateway_id) !!} {!! Former::populateField('gateway_id', $accountGateway->gateway_id) !!}
{!! Former::populateField('payment_type_id', $paymentTypeId) !!}
{!! Former::populateField('recommendedGateway_id', $accountGateway->gateway_id) !!} {!! Former::populateField('recommendedGateway_id', $accountGateway->gateway_id) !!}
{!! Former::populateField('show_address', intval($accountGateway->show_address)) !!}
{!! Former::populateField('update_address', intval($accountGateway->update_address)) !!}
@if ($config) @if ($config)
@foreach ($accountGateway->fields as $field => $junk) @foreach ($accountGateway->fields as $field => $junk)
@if (in_array($field, $hiddenFields)) @if (in_array($field, $hiddenFields))
@ -28,6 +31,8 @@
@endif @endif
@else @else
{!! Former::populateField('gateway_id', GATEWAY_STRIPE) !!} {!! Former::populateField('gateway_id', GATEWAY_STRIPE) !!}
{!! Former::populateField('show_address', 1) !!}
{!! Former::populateField('update_address', 1) !!}
@endif @endif
{!! Former::select('payment_type_id') {!! Former::select('payment_type_id')
@ -77,6 +82,15 @@
@endforeach @endforeach
{!! Former::checkbox('show_address')
->label(trans('texts.billing_address'))
->text(trans('texts.show_address_help'))
->addGroupClass('gateway-option') !!}
{!! Former::checkbox('update_address')
->label(' ')
->text(trans('texts.update_address_help'))
->addGroupClass('gateway-option') !!}
{!! Former::checkboxes('creditCardTypes[]') {!! Former::checkboxes('creditCardTypes[]')
->label('Accepted Credit Cards') ->label('Accepted Credit Cards')
->checkboxes($creditCardTypes) ->checkboxes($creditCardTypes)
@ -131,11 +145,25 @@
} }
} }
function enableUpdateAddress(event) {
var disabled = !$('#show_address').is(':checked');
$('#update_address').prop('disabled', disabled);
$('label[for=update_address]').css('color', disabled ? '#888' : '#000');
if (disabled) {
$('#update_address').prop('checked', false);
} else if (event) {
$('#update_address').prop('checked', true);
}
}
$(function() { $(function() {
setPaymentType(); setPaymentType();
@if ($accountGateway) @if ($accountGateway)
$('.payment-type-option').hide(); $('.payment-type-option').hide();
@endif @endif
$('#show_address').change(enableUpdateAddress);
enableUpdateAddress();
}) })
</script> </script>

View File

@ -0,0 +1,211 @@
@extends('accounts.nav')
@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>
<style type="text/css">
select.form-control {
background: #FFFFFF !important;
margin-right: 12px;
}
table {
background: #FFFFFF !important;
}
</style>
@stop
@section('content')
@parent
@include('accounts.nav_advanced')
<script>
var invoiceDesigns = {!! $invoiceDesigns !!};
var invoice = {!! json_encode($invoice) !!};
var sections = ['content', 'styles', 'defaultStyle', 'pageMargins', 'header', 'footer'];
var customDesign = origCustomDesign = {!! $customDesign ?: 'JSON.parse(invoiceDesigns[0].javascript);' !!};
function getPDFString(cb, force) {
invoice.is_pro = {!! Auth::user()->isPro() ? 'true' : 'false' !!};
invoice.account.hide_quantity = {!! Auth::user()->account->hide_quantity ? 'true' : 'false' !!};
invoice.account.hide_paid_to_date = {!! Auth::user()->account->hide_paid_to_date ? 'true' : 'false' !!};
invoice.invoice_design_id = {!! Auth::user()->account->invoice_design_id !!};
NINJA.primaryColor = '{!! Auth::user()->account->primary_color !!}';
NINJA.secondaryColor = '{!! Auth::user()->account->secondary_color !!}';
NINJA.fontSize = {!! Auth::user()->account->font_size !!};
generatePDF(invoice, getDesignJavascript(), force, cb);
}
function getDesignJavascript() {
var id = $('#invoice_design_id').val();
if (id == '-1') {
showMoreDesigns();
$('#invoice_design_id').val(1);
return invoiceDesigns[0].javascript;
} else if (customDesign) {
return JSON.stringify(customDesign);
} else {
return invoiceDesigns[0].javascript;
}
}
function loadEditor(section)
{
editorSection = section;
editor.set(customDesign[section]);
// the function throws an error if the editor is in code view
try {
editor.expandAll();
} catch(err) {}
}
function saveEditor(data)
{
setTimeout(function() {
customDesign[editorSection] = editor.get();
refreshPDF();
}, 100)
}
function onSelectChange()
{
var id = $('#invoice_design_id').val();
if (parseInt(id)) {
var design = _.find(invoiceDesigns, function(design){ return design.id == id});
customDesign = JSON.parse(design.javascript);
} else {
customDesign = origCustomDesign;
}
loadEditor(editorSection);
refreshPDF(true);
}
function submitForm()
{
$('#custom_design').val(JSON.stringify(customDesign));
$('form.warn-on-exit').submit();
}
$(function() {
refreshPDF(true);
var container = document.getElementById("jsoneditor");
var options = {
mode: 'form',
modes: ['form', 'code'],
change: function() {
saveEditor();
}
};
window.editor = new JSONEditor(container, options);
loadEditor('content');
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
var target = $(e.target).attr("href") // activated tab
target = target.substring(1); // strip leading #
loadEditor(target);
});
});
</script>
<div class="row">
<div class="col-md-6">
{!! Former::open()->addClass('warn-on-exit') !!}
{!! Former::populateField('invoice_design_id', $account->invoice_design_id) !!}
<div style="display:none">
{!! Former::text('custom_design') !!}
</div>
<div role="tabpanel">
<ul class="nav nav-tabs" role="tablist" style="border: none">
<li role="presentation" class="active"><a href="#content" aria-controls="content" role="tab" data-toggle="tab">{{ trans('texts.content') }}</a></li>
<li role="presentation"><a href="#styles" aria-controls="styles" role="tab" data-toggle="tab">{{ trans('texts.styles') }}</a></li>
<li role="presentation"><a href="#defaultStyle" aria-controls="defaultStyle" role="tab" data-toggle="tab">{{ trans('texts.defaults') }}</a></li>
<li role="presentation"><a href="#pageMargins" aria-controls="margins" role="tab" data-toggle="tab">{{ trans('texts.margins') }}</a></li>
<li role="presentation"><a href="#header" aria-controls="header" role="tab" data-toggle="tab">{{ trans('texts.header') }}</a></li>
<li role="presentation"><a href="#footer" aria-controls="footer" role="tab" data-toggle="tab">{{ trans('texts.footer') }}</a></li>
</ul>
</div>
<div id="jsoneditor" style="width: 550px; height: 743px;"></div>
<p>&nbsp;</p>
<div>
{!! 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
</div>
</div>
<script>
@if (!Auth::user()->isPro())
$(function() {
$('form.warn-on-exit input').prop('disabled', true);
});
@endif
function showHelp() {
$('#helpModal').modal('show');
}
</script>
{!! Former::close() !!}
<div class="modal fade" id="helpModal" tabindex="-1" role="dialog" aria-labelledby="helpModalLabel" 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="helpModalLabel">{{ trans('texts.help') }}</h4>
</div>
<div class="panel-body" style="background-color: #fff">
{!! trans('texts.customize_help') !!}
</div>
<div class="modal-footer" style="margin-top: 0px">
<button type="button" class="btn btn-default" data-dismiss="modal">{{ trans('texts.close') }}</button>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6">
@include('invoices.pdf', ['account' => Auth::user()->account, 'pdfHeight' => 800])
</div>
</div>
@stop

View File

@ -18,10 +18,13 @@
{{ Former::populate($account) }} {{ Former::populate($account) }}
@if ($showUser) @if ($showUser)
{{ Former::populateField('first_name', $account->users()->first()->first_name) }} {{ Former::populateField('first_name', $primaryUser->first_name) }}
{{ Former::populateField('last_name', $account->users()->first()->last_name) }} {{ Former::populateField('last_name', $primaryUser->last_name) }}
{{ Former::populateField('email', $account->users()->first()->email) }} {{ Former::populateField('email', $primaryUser->email) }}
{{ Former::populateField('phone', $account->users()->first()->phone) }} {{ Former::populateField('phone', $primaryUser->phone) }}
@if (Utils::isNinjaDev())
{{ Former::populateField('dark_mode', intval($primaryUser->dark_mode)) }}
@endif
@endif @endif
<div class="row"> <div class="row">
@ -48,7 +51,7 @@
@if (file_exists($account->getLogoPath())) @if (file_exists($account->getLogoPath()))
<center> <center>
{!! HTML::image($account->getLogoPath().'?no_cache='.time(), "Logo") !!} &nbsp; {!! HTML::image($account->getLogoPath().'?no_cache='.time(), 'Logo', ['width' => 200]) !!} &nbsp;
<a href="#" onclick="deleteLogo()">{{ trans('texts.remove_logo') }}</a> <a href="#" onclick="deleteLogo()">{{ trans('texts.remove_logo') }}</a>
</center><br/> </center><br/>
@endif @endif
@ -81,13 +84,17 @@
@if ($showUser) @if ($showUser)
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<h3 class="panel-title">{!! trans('texts.users') !!}</h3> <h3 class="panel-title">{!! trans('texts.primary_user') !!}</h3>
</div> </div>
<div class="panel-body"> <div class="panel-body">
{!! Former::text('first_name') !!} {!! Former::text('first_name') !!}
{!! Former::text('last_name') !!} {!! Former::text('last_name') !!}
{!! Former::text('email') !!} {!! Former::text('email') !!}
{!! Former::text('phone') !!} {!! Former::text('phone') !!}
@if (Utils::isNinjaDev())
{!! Former::checkbox('dark_mode')->text(trans('texts.dark_mode_help')) !!}
@endif
@if (Auth::user()->confirmed) @if (Auth::user()->confirmed)
{!! Former::actions( Button::primary(trans('texts.change_password'))->small()->withAttributes(['onclick'=>'showChangePassword()'])) !!} {!! Former::actions( Button::primary(trans('texts.change_password'))->small()->withAttributes(['onclick'=>'showChangePassword()'])) !!}
@elseif (Auth::user()->registered) @elseif (Auth::user()->registered)

View File

@ -108,7 +108,7 @@
} }
keys = ['footer', 'account', 'client', 'amount', 'link', 'contact']; keys = ['footer', 'account', 'client', 'amount', 'link', 'contact'];
vals = [{!! json_encode($emailFooter) !!}, '{!! Auth::user()->account->getDisplayName() !!}', 'Client Name', formatMoney(100), '{!! NINJA_WEB_URL !!}', 'Contact Name']; vals = [{!! json_encode($emailFooter) !!}, '{!! Auth::user()->account->getDisplayName() !!}', 'Client Name', formatMoney(100), '{!! SITE_URL . '/view/...' !!}', 'Contact Name'];
// Add any available payment method links // Add any available payment method links
@foreach (\App\Models\Gateway::getPaymentTypeLinks() as $type) @foreach (\App\Models\Gateway::getPaymentTypeLinks() as $type)

View File

@ -3,13 +3,10 @@
@section('head') @section('head')
@parent @parent
<script src="{!! asset('js/pdf_viewer.js') !!}" type="text/javascript"></script> <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/compatibility.js') }}" type="text/javascript"></script>
@if (Auth::user()->account->utf8_invoices)
<script src="{{ asset('js/pdfmake.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/vfs_fonts.js') }}" type="text/javascript"></script>
@endif
@stop @stop
@ -28,7 +25,8 @@
$('#invoice_design_id').val(1); $('#invoice_design_id').val(1);
return invoiceDesigns[0].javascript; return invoiceDesigns[0].javascript;
} else { } else {
return invoiceDesigns[id-1].javascript; var design = _.find(invoiceDesigns, function(design){ return design.id == id});
return design ? design.javascript : '';
} }
} }
@ -55,8 +53,7 @@
} }
} }
doc = generatePDF(invoice, getDesignJavascript(), true); generatePDF(invoice, getDesignJavascript(), true, cb);
doc.getDataUrl(cb);
} }
$(function() { $(function() {
@ -96,19 +93,20 @@
<div class="panel-body"> <div class="panel-body">
@if (!Utils::isPro() || \App\Models\InvoiceDesign::count() == COUNT_FREE_DESIGNS) @if (!Utils::isPro() || \App\Models\InvoiceDesign::count() == COUNT_FREE_DESIGNS_SELF_HOST)
{!! Former::select('invoice_design_id')->style('display:inline;width:120px')->fromQuery($invoiceDesigns, 'name', 'id')->addOption(trans('texts.more_designs') . '...', '-1') !!} {!! Former::select('invoice_design_id')->style('display:inline;width:120px')->fromQuery($invoiceDesigns, 'name', 'id')->addOption(trans('texts.more_designs') . '...', '-1') !!}
@else @else
{!! Former::select('invoice_design_id')->style('display:inline;width:120px')->fromQuery($invoiceDesigns, 'name', 'id') !!} {!! Former::select('invoice_design_id')->style('display:inline;width:120px')->fromQuery($invoiceDesigns, 'name', 'id') !!}
@endif @endif
@if (Auth::user()->account->utf8_invoices)
{!! Former::text('font_size')->type('number')->min('0')->step('1')->style('width:120px') !!} {!! Former::text('font_size')->type('number')->min('0')->step('1')->style('width:120px') !!}
@endif
{!! Former::text('primary_color') !!} {!! Former::text('primary_color') !!}
{!! Former::text('secondary_color') !!} {!! Former::text('secondary_color') !!}
{!! Former::actions(
Button::primary(trans('texts.customize_design'))->small()->asLinkTo(URL::to('/company/advanced_settings/customize_design'))
) !!}
</div> </div>
</div> </div>

View File

@ -23,8 +23,6 @@
{{ Former::populateField('custom_invoice_taxes2', intval($account->custom_invoice_taxes2)) }} {{ Former::populateField('custom_invoice_taxes2', intval($account->custom_invoice_taxes2)) }}
{{ Former::populateField('share_counter', intval($account->share_counter)) }} {{ Former::populateField('share_counter', intval($account->share_counter)) }}
{{ Former::populateField('pdf_email_attachment', intval($account->pdf_email_attachment)) }} {{ Former::populateField('pdf_email_attachment', intval($account->pdf_email_attachment)) }}
{{ Former::populateField('utf8_invoices', intval($account->utf8_invoices)) }}
{{ Former::populateField('auto_wrap', intval($account->auto_wrap)) }}
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
@ -98,10 +96,6 @@
</div> </div>
<div class="panel-body"> <div class="panel-body">
{!! Former::checkbox('pdf_email_attachment')->text(trans('texts.enable')) !!} {!! Former::checkbox('pdf_email_attachment')->text(trans('texts.enable')) !!}
{!! Former::checkbox('utf8_invoices')->text(trans('texts.enable')) !!}
<div style="display:none">
{!! Former::checkbox('auto_wrap')->text(trans('texts.enable')) !!}
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -8,7 +8,7 @@
{!! HTML::nav_link('company/products', 'product_library') !!} {!! HTML::nav_link('company/products', 'product_library') !!}
{!! HTML::nav_link('company/notifications', 'notifications') !!} {!! HTML::nav_link('company/notifications', 'notifications') !!}
{!! HTML::nav_link('company/import_export', 'import_export', 'company/import_map') !!} {!! HTML::nav_link('company/import_export', 'import_export', 'company/import_map') !!}
{!! HTML::nav_link('company/advanced_settings/invoice_settings', 'advanced_settings', '*/advanced_settings/*') !!} {!! HTML::nav_link('company/advanced_settings/invoice_design', 'advanced_settings', '*/advanced_settings/*') !!}
</ul> </ul>
<br/> <br/>

View File

@ -1,6 +1,6 @@
<ul class="nav nav-tabs nav nav-justified"> <ul class="nav nav-tabs nav nav-justified">
{!! HTML::nav_link('company/advanced_settings/invoice_settings', 'invoice_settings') !!}
{!! HTML::nav_link('company/advanced_settings/invoice_design', 'invoice_design') !!} {!! HTML::nav_link('company/advanced_settings/invoice_design', 'invoice_design') !!}
{!! HTML::nav_link('company/advanced_settings/invoice_settings', 'invoice_settings') !!}
{!! HTML::nav_link('company/advanced_settings/email_templates', 'email_templates') !!} {!! HTML::nav_link('company/advanced_settings/email_templates', 'email_templates') !!}
{!! HTML::nav_link('company/advanced_settings/charts_and_reports', 'charts_and_reports') !!} {!! HTML::nav_link('company/advanced_settings/charts_and_reports', 'charts_and_reports') !!}
{!! HTML::nav_link('company/advanced_settings/user_management', 'users_and_tokens') !!} {!! HTML::nav_link('company/advanced_settings/user_management', 'users_and_tokens') !!}

View File

@ -20,7 +20,7 @@
@endif @endif
{!! Former::text('product_key')->label('texts.product') !!} {!! Former::text('product_key')->label('texts.product') !!}
{!! Former::textarea('notes')->data_bind("value: wrapped_notes, valueUpdate: 'afterkeydown'") !!} {!! Former::textarea('notes') !!}
{!! Former::text('cost') !!} {!! Former::text('cost') !!}
</div> </div>
@ -35,29 +35,6 @@
<script type="text/javascript"> <script type="text/javascript">
function ViewModel(data) {
var self = this;
@if ($product)
self.notes = ko.observable(wordWrapText('{{ str_replace(["\r\n","\r","\n"], '\n', addslashes($product->notes)) }}', 300));
@else
self.notes = ko.observable('');
@endif
self.wrapped_notes = ko.computed({
read: function() {
return self.notes();
},
write: function(value) {
value = wordWrapText(value, 235);
self.notes(value);
},
owner: this
});
}
window.model = new ViewModel();
ko.applyBindings(model);
$(function() { $(function() {
$('#product_key').focus(); $('#product_key').focus();
}); });

View File

@ -13,7 +13,7 @@
<div class="pull-right"> <div class="pull-right">
{!! Button::normal(trans('texts.documentation'))->asLinkTo(NINJA_WEB_URL.'/knowledgebase/api-documentation/')->withAttributes(['target' => '_blank']) !!} {!! Button::normal(trans('texts.documentation'))->asLinkTo(NINJA_WEB_URL.'/knowledgebase/api-documentation/')->withAttributes(['target' => '_blank'])->appendIcon(Icon::create('info-sign')) !!}
@if (Utils::isNinja()) @if (Utils::isNinja())
{!! Button::normal(trans('texts.zapier'))->asLinkTo(ZAPIER_URL)->withAttributes(['target' => '_blank']) !!} {!! Button::normal(trans('texts.zapier'))->asLinkTo(ZAPIER_URL)->withAttributes(['target' => '_blank']) !!}
@endif @endif

View File

@ -79,11 +79,11 @@
{!! Former::hidden('remember')->raw() !!} {!! Former::hidden('remember')->raw() !!}
</p> </p>
<p>{!! Button::success(trans(Input::get('new_account') && Utils::allowNewAccounts() ? 'texts.login' : 'texts.lets_go'))->large()->submit()->block() !!}</p> <p>{!! Button::success(trans(Input::get('new_company') ? 'texts.login' : 'texts.lets_go'))->large()->submit()->block() !!}</p>
@if (Input::get('new_account') && Utils::allowNewAccounts()) @if (Input::get('new_company') && Utils::allowNewAccounts())
<center><p>- {{ trans('texts.or') }} -</p></center> <center><p>- {{ trans('texts.or') }} -</p></center>
<p>{!! Button::primary(trans('texts.new_account'))->asLinkTo(URL::to('/invoice_now?new_account=true'))->large()->submit()->block() !!}</p> <p>{!! Button::primary(trans('texts.new_company'))->asLinkTo(URL::to('/invoice_now?new_company=true&sign_up=true'))->large()->submit()->block() !!}</p>
@endif @endif

View File

@ -6,7 +6,10 @@
<div class="col-md-4"> <div class="col-md-4">
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-body"> <div class="panel-body">
<img src="{{ asset('images/totalincome.png') }}" class="in-image"/> <img src="{{ asset('images/totalinvoices.png') }}" class="in-image"/>
<div class="in-thin">
{{ trans('texts.total_revenue') }}
</div>
<div class="in-bold"> <div class="in-bold">
@if (count($paidToDate)) @if (count($paidToDate))
@foreach ($paidToDate as $item) @foreach ($paidToDate as $item)
@ -16,9 +19,6 @@
{{ Utils::formatMoney(0) }} {{ Utils::formatMoney(0) }}
@endif @endif
</div> </div>
<div class="in-thin">
{{ trans('texts.in_total_revenue') }}
</div>
</div> </div>
</div> </div>
</div> </div>
@ -44,12 +44,18 @@
<div class="col-md-4"> <div class="col-md-4">
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-body"> <div class="panel-body">
<img src="{{ asset('images/totalinvoices.png') }}" class="in-image"/> <img src="{{ asset('images/totalincome.png') }}" class="in-image"/>
<div class="in-bold">
{{ $invoicesSent }}
</div>
<div class="in-thin"> <div class="in-thin">
{{ Utils::pluralize('invoice', $invoicesSent) }} {{ trans('texts.sent') }} {{ trans('texts.outstanding') }}
</div>
<div class="in-bold">
@if (count($balances))
@foreach ($balances as $item)
{{ Utils::formatMoney($item->value, $item->currency_id) }}<br/>
@endforeach
@else
{{ Utils::formatMoney(0) }}
@endif
</div> </div>
</div> </div>
</div> </div>
@ -61,13 +67,16 @@
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<div class="panel panel-default dashboard" style="min-height:660px"> <div class="panel panel-default dashboard" style="height:320px">
<div class="panel-heading" style="background-color:#0b4d78 !important"> <div class="panel-heading" style="background-color:#0b4d78 !important">
<h3 class="panel-title in-bold-white"> <h3 class="panel-title in-bold-white">
<i class="glyphicon glyphicon-exclamation-sign"></i> {{ trans('texts.notifications') }} <i class="glyphicon glyphicon-exclamation-sign"></i> {{ trans('texts.notifications') }}
<div class="pull-right" style="font-size:14px;padding-top:4px">
{{ $invoicesSent }} {{ Utils::pluralize('invoice', $invoicesSent) }} {{ trans('texts.sent') }}
</div>
</h3> </h3>
</div> </div>
<ul class="panel-body list-group"> <ul class="panel-body list-group" style="height:276px;overflow-y:auto;">
@foreach ($activities as $activity) @foreach ($activities as $activity)
<li class="list-group-item"> <li class="list-group-item">
<span style="color:#888;font-style:italic">{{ Utils::timestampToDateString(strtotime($activity->created_at)) }}:</span> <span style="color:#888;font-style:italic">{{ Utils::timestampToDateString(strtotime($activity->created_at)) }}:</span>
@ -76,15 +85,43 @@
@endforeach @endforeach
</ul> </ul>
</div> </div>
<div class="panel panel-default dashboard" style="height:320px;">
<div class="panel-heading" style="margin:0; background-color: #f5f5f5 !important;">
<h3 class="panel-title" style="color: black !important">
<i class="glyphicon glyphicon-ok-sign"></i> {{ trans('texts.recent_payments') }}
</h3>
</div>
<div class="panel-body" style="height:274px;overflow-y:auto;">
<table class="table table-striped">
<thead>
<th>{{ trans('texts.invoice_number_short') }}</th>
<th>{{ trans('texts.client') }}</th>
<th>{{ trans('texts.payment_date') }}</th>
<th>{{ trans('texts.amount') }}</th>
</thead>
<tbody>
@foreach ($payments as $payment)
<tr>
<td>{!! \App\Models\Invoice::calcLink($payment) !!}</td>
<td>{!! link_to('/clients/'.$payment->client_public_id, trim($payment->client_name) ?: (trim($payment->first_name . ' ' . $payment->last_name) ?: $payment->email)) !!}</td>
<td>{{ Utils::fromSqlDate($payment->payment_date) }}</td>
<td>{{ Utils::formatMoney($payment->amount, $payment->currency_id ?: ($account->currency_id ?: DEFAULT_CURRENCY)) }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<div class="panel panel-default dashboard" style="min-height:320px"> <div class="panel panel-default dashboard" style="height:320px">
<div class="panel-heading" style="background-color:#e37329 !important"> <div class="panel-heading" style="background-color:#e37329 !important">
<h3 class="panel-title in-bold-white"> <h3 class="panel-title in-bold-white">
<i class="glyphicon glyphicon-time"></i> {{ trans('texts.invoices_past_due') }} <i class="glyphicon glyphicon-time"></i> {{ trans('texts.invoices_past_due') }}
</h3> </h3>
</div> </div>
<div class="panel-body"> <div class="panel-body" style="height:274px;overflow-y:auto;">
<table class="table table-striped"> <table class="table table-striped">
<thead> <thead>
<th>{{ trans('texts.invoice_number_short') }}</th> <th>{{ trans('texts.invoice_number_short') }}</th>
@ -95,23 +132,23 @@
<tbody> <tbody>
@foreach ($pastDue as $invoice) @foreach ($pastDue as $invoice)
<tr> <tr>
<td>{!! $invoice->getLink() !!}</td> <td>{!! \App\Models\Invoice::calcLink($invoice) !!}</td>
<td>{{ $invoice->client->getDisplayName() }}</td> <td>{!! link_to('/clients/'.$invoice->client_public_id, trim($invoice->client_name) ?: (trim($invoice->first_name . ' ' . $invoice->last_name) ?: $invoice->email)) !!}</td>
<td>{{ Utils::fromSqlDate($invoice->due_date) }}</td> <td>{{ Utils::fromSqlDate($invoice->due_date) }}</td>
<td>{{ Utils::formatMoney($invoice->balance, $invoice->client->getCurrencyId()) }}</td> <td>{{ Utils::formatMoney($invoice->balance, $invoice->currency_id ?: ($account->currency_id ?: DEFAULT_CURRENCY)) }}</td>
</tr> </tr>
@endforeach @endforeach
</tbody> </tbody>
</table> </table>
</div> </div>
</div> </div>
<div class="panel panel-default dashboard" style="min-height:320px;"> <div class="panel panel-default dashboard" style="height:320px;">
<div class="panel-heading" style="margin:0; background-color: #f5f5f5 !important;"> <div class="panel-heading" style="margin:0; background-color: #f5f5f5 !important;">
<h3 class="panel-title" style="color: black !important"> <h3 class="panel-title" style="color: black !important">
<i class="glyphicon glyphicon-time"></i> {{ trans('texts.upcoming_invoices') }} <i class="glyphicon glyphicon-time"></i> {{ trans('texts.upcoming_invoices') }}
</h3> </h3>
</div> </div>
<div class="panel-body"> <div class="panel-body" style="height:274px;overflow-y:auto;">
<table class="table table-striped"> <table class="table table-striped">
<thead> <thead>
<th>{{ trans('texts.invoice_number_short') }}</th> <th>{{ trans('texts.invoice_number_short') }}</th>
@ -122,10 +159,10 @@
<tbody> <tbody>
@foreach ($upcoming as $invoice) @foreach ($upcoming as $invoice)
<tr> <tr>
<td>{!! $invoice->getLink() !!}</td> <td>{!! \App\Models\Invoice::calcLink($invoice) !!}</td>
<td>{{ $invoice->client->getDisplayName() }}</td> <td>{!! link_to('/clients/'.$invoice->client_public_id, trim($invoice->client_name) ?: (trim($invoice->first_name . ' ' . $invoice->last_name) ?: $invoice->email)) !!}</td>
<td>{{ Utils::fromSqlDate($invoice->due_date) }}</td> <td>{{ Utils::fromSqlDate($invoice->due_date) }}</td>
<td>{{ Utils::formatMoney($invoice->balance, $invoice->client->getCurrencyId()) }}</td> <td>{{ Utils::formatMoney($invoice->balance, $invoice->currency_id ?: ($account->currency_id ?: DEFAULT_CURRENCY)) }}</td>
</tr> </tr>
@endforeach @endforeach
</tbody> </tbody>

View File

@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en-US"> <html lang="{{App::getLocale()}}">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
</head> </head>

View File

@ -1,4 +1,8 @@
<html> <!DOCTYPE html>
<html lang="{{ App::getLocale() }}">
<head>
<meta charset="utf-8">
</head>
<body> <body>
@if (false && !$invitationMessage) @if (false && !$invitationMessage)
@include('emails.confirm_action', ['user' => $user]) @include('emails.confirm_action', ['user' => $user])

View File

@ -1,4 +1,8 @@
<html> <!DOCTYPE html>
<html lang="{{ App::getLocale() }}">
<head>
<meta charset="utf-8">
</head>
<body> <body>
@if (false) @if (false)
@include('emails.view_action', ['link' => $link, 'entityType' => $entityType]) @include('emails.view_action', ['link' => $link, 'entityType' => $entityType])

View File

@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html lang="{{ App::getLocale() }}">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
</head> </head>

View File

@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html lang="{{ App::getLocale() }}">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
</head> </head>

View File

@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en-US"> <html lang="{{ App::getLocale() }}">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
</head> </head>

View File

@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html lang="{{ App::getLocale() }}">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
</head> </head>

View File

@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html lang="{{ App::getLocale() }}">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
</head> </head>

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