diff --git a/.htaccess b/.htaccess index 27a6945d38f8..8b1f582ca455 100644 --- a/.htaccess +++ b/.htaccess @@ -6,3 +6,141 @@ # https://coderwall.com/p/erbaig/laravel-s-htaccess-to-remove-public-from-url # RewriteRule ^(.*)$ public/$1 [L] + +# https://github.com/h5bp/server-configs-apache/blob/master/dist/.htaccess + + +# ###################################################################### +# # INTERNET EXPLORER # +# ###################################################################### + +# ---------------------------------------------------------------------- +# | Iframes cookies | +# ---------------------------------------------------------------------- + +# Allow cookies to be set from iframes in Internet Explorer. +# +# https://msdn.microsoft.com/en-us/library/ms537343.aspx +# http://www.w3.org/TR/2000/CR-P3P-20001215/ + + + Header set P3P "policyref=\"/w3c/p3p.xml\", CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\"" + + + +# ###################################################################### +# # MEDIA TYPES AND CHARACTER ENCODINGS # +# ###################################################################### + +# ---------------------------------------------------------------------- +# | Character encodings | +# ---------------------------------------------------------------------- + +# Serve all resources labeled as `text/html` or `text/plain` +# with the media type `charset` parameter set to `UTF-8`. +# +# https://httpd.apache.org/docs/current/mod/core.html#adddefaultcharset + +AddDefaultCharset utf-8 + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +# Serve the following file types with the media type `charset` +# parameter set to `UTF-8`. +# +# https://httpd.apache.org/docs/current/mod/mod_mime.html#addcharset + + + AddCharset utf-8 .atom \ + .bbaw \ + .css \ + .geojson \ + .js \ + .json \ + .jsonld \ + .manifest \ + .rdf \ + .rss \ + .topojson \ + .vtt \ + .webapp \ + .webmanifest \ + .xloc \ + .xml + + + +# ###################################################################### +# # WEB PERFORMANCE # +# ###################################################################### + +# ---------------------------------------------------------------------- +# | Compression | +# ---------------------------------------------------------------------- + + + + # Force compression for mangled headers. + # https://developer.yahoo.com/blogs/ydn/pushing-beyond-gzipping-25601.html + + + + SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding + RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding + + + + # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + # Map certain file types to the specified encoding type in order to + # make Apache serve them with the appropriate `Content-Encoding` HTTP + # response header (this will NOT make Apache compress them!). + + # If the following file types wouldn't be served without the appropriate + # `Content-Enable` HTTP response header, client applications (e.g.: + # browsers) wouldn't know that they first need to uncompress the response, + # and thus, wouldn't be able to understand the content. + + # http://httpd.apache.org/docs/current/mod/mod_mime.html#addencoding + + + AddEncoding gzip svgz + + + # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + # Compress all output labeled with one of the following media types. + + # IMPORTANT: For Apache versions below 2.3.7 you don't need to enable + # `mod_filter` and can remove the `` & `` + # lines as `AddOutputFilterByType` is still in the core directives. + + + AddOutputFilterByType DEFLATE "application/atom+xml" \ + "application/javascript" \ + "application/json" \ + "application/ld+json" \ + "application/manifest+json" \ + "application/rdf+xml" \ + "application/rss+xml" \ + "application/schema+json" \ + "application/vnd.geo+json" \ + "application/vnd.ms-fontobject" \ + "application/x-font-ttf" \ + "application/x-web-app-manifest+json" \ + "application/xhtml+xml" \ + "application/xml" \ + "font/opentype" \ + "image/svg+xml" \ + "image/x-icon" \ + "text/cache-manifest" \ + "text/css" \ + "text/html" \ + "text/javascript" \ + "text/plain" \ + "text/vtt" \ + "text/x-component" \ + "text/xml" + + + \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 9e8b983e795d..1f66f7aa29f7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -45,7 +45,8 @@ before_script: - php artisan key:generate --no-interaction - sed -i 's/APP_ENV=production/APP_ENV=development/g' .env - sed -i 's/APP_DEBUG=false/APP_DEBUG=true/g' .env - - sed -i 's/REQUIRE_HTTPS=false/NINJA_DEV=true/g' .env + - sed -i '$a NINJA_DEV=true' .env + - sed -i '$a TRAVIS=true' .env # create the database and user - mysql -u root -e "create database IF NOT EXISTS ninja;" - mysql -u root -e "GRANT ALL PRIVILEGES ON ninja.* To 'ninja'@'localhost' IDENTIFIED BY 'ninja'; FLUSH PRIVILEGES;" @@ -89,7 +90,8 @@ after_script: - mysql -u root -e 'select * from invoice_items;' ninja - mysql -u root -e 'select * from payments;' ninja - mysql -u root -e 'select * from credits;' ninja - - cat storage/logs/laravel.log + - cat storage/logs/laravel-error.log + - cat storage/logs/laravel-info.log notifications: email: diff --git a/app/Commands/Command.php b/app/Commands/Command.php index 5bc48501167e..d6a8d61150ae 100644 --- a/app/Commands/Command.php +++ b/app/Commands/Command.php @@ -1,4 +1,4 @@ -faker = Factory::create(); + + $this->clientRepo = $clientRepo; + $this->invoiceRepo = $invoiceRepo; + $this->paymentRepo = $paymentRepo; + $this->vendorRepo = $vendorRepo; + $this->expenseRepo = $expenseRepo; + } + + public function fire() + { + if (Utils::isNinjaProd()) { + return false; + } + + $this->info(date('Y-m-d').' Running CreateTestData...'); + + Auth::loginUsingId(1); + $this->count = $this->argument('count'); + + $this->createClients(); + $this->createVendors(); + + $this->info('Done'); + } + + private function createClients() + { + for ($i=0; $i<$this->count; $i++) { + $data = [ + 'name' => $this->faker->name, + 'address1' => $this->faker->streetAddress, + 'address2' => $this->faker->secondaryAddress, + 'city' => $this->faker->city, + 'state' => $this->faker->state, + 'postal_code' => $this->faker->postcode, + 'contacts' => [[ + 'first_name' => $this->faker->firstName, + 'last_name' => $this->faker->lastName, + 'email' => $this->faker->safeEmail, + 'phone' => $this->faker->phoneNumber, + ]] + ]; + + $client = $this->clientRepo->save($data); + $this->info('Client: ' . $client->name); + + $this->createInvoices($client); + } + } + + private function createInvoices($client) + { + for ($i=0; $i<$this->count; $i++) { + $data = [ + 'client_id' => $client->id, + 'invoice_items' => [[ + 'product_key' => $this->faker->word, + 'qty' => $this->faker->randomDigit + 1, + 'cost' => $this->faker->randomFloat(2, 1, 10), + 'notes' => $this->faker->text($this->faker->numberBetween(50, 300)) + ]] + ]; + + $invoice = $this->invoiceRepo->save($data); + $this->info('Invoice: ' . $invoice->invoice_number); + + $this->createPayment($client, $invoice); + } + } + + private function createPayment($client, $invoice) + { + $data = [ + 'invoice_id' => $invoice->id, + 'client_id' => $client->id, + 'amount' => $this->faker->randomFloat(2, 0, $invoice->amount) + ]; + + $payment = $this->paymentRepo->save($data); + + $this->info('Payment: ' . $payment->amount); + } + + private function createVendors() + { + for ($i=0; $i<$this->count; $i++) { + $data = [ + 'name' => $this->faker->name, + 'address1' => $this->faker->streetAddress, + 'address2' => $this->faker->secondaryAddress, + 'city' => $this->faker->city, + 'state' => $this->faker->state, + 'postal_code' => $this->faker->postcode, + 'vendor_contacts' => [[ + 'first_name' => $this->faker->firstName, + 'last_name' => $this->faker->lastName, + 'email' => $this->faker->safeEmail, + 'phone' => $this->faker->phoneNumber, + ]] + ]; + + $vendor = $this->vendorRepo->save($data); + $this->info('Vendor: ' . $vendor->name); + + $this->createExpense($vendor); + } + } + + private function createExpense($vendor) + { + for ($i=0; $i<$this->count; $i++) { + $data = [ + 'vendor_id' => $vendor->id, + 'amount' => $this->faker->randomFloat(2, 1, 10), + 'expense_date' => null, + 'public_notes' => null, + ]; + + $expense = $this->expenseRepo->save($data); + $this->info('Expense: ' . $expense->amount); + } + } + + protected function getArguments() + { + return array( + //array('example', InputArgument::REQUIRED, 'An example argument.'), + ); + } + + protected function getOptions() + { + return array( + //array('example', null, InputOption::VALUE_OPTIONAL, 'An example option.', null), + ); + } +} diff --git a/app/Console/Commands/GenerateResources.php b/app/Console/Commands/GenerateResources.php index a1dc404f79bb..e7139c85be63 100644 --- a/app/Console/Commands/GenerateResources.php +++ b/app/Console/Commands/GenerateResources.php @@ -1,4 +1,4 @@ -due_date = date('Y-m-d', strtotime('+ 10 days')); $invoice->save(); - $this->mailer->sendInvoice($invoice); - $this->info("Sent invoice to {$client->getDisplayName()}"); + if ($term == PLAN_TERM_YEARLY) { + $this->mailer->sendInvoice($invoice); + $this->info("Sent {$term}ly {$plan} invoice to {$client->getDisplayName()}"); + } else { + $this->info("Created {$term}ly {$plan} invoice for {$client->getDisplayName()}"); + } } $this->info('Done'); diff --git a/app/Console/Commands/TestOFX.php b/app/Console/Commands/TestOFX.php index 243e30744831..f7eb2b748b03 100644 --- a/app/Console/Commands/TestOFX.php +++ b/app/Console/Commands/TestOFX.php @@ -1,4 +1,4 @@ -accountRepo = $accountRepo; } + public function ping() + { + $headers = Utils::getApiHeaders(); + + return Response::make(RESULT_SUCCESS, 200, $headers); + } + public function register(RegisterRequest $request) { diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index cf0e4508610e..5ce42356bf83 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -246,8 +246,7 @@ class AccountController extends BaseController public function getSearchData() { - $account = Auth::user()->account; - $data = $this->accountRepo->getSearchData($account); + $data = $this->accountRepo->getSearchData(Auth::user()); return Response::json($data); } @@ -1294,4 +1293,37 @@ class AccountController extends BaseController return Redirect::to("/settings/$section/", 301); } + + public function previewEmail(\App\Services\TemplateService $templateService) + { + $template = Input::get('template'); + $invoice = Invoice::scope() + ->invoices() + ->withTrashed() + ->first(); + + if ( ! $invoice) { + return trans('texts.create_invoice_for_sample'); + } + + $account = Auth::user()->account; + + // replace the variables with sample data + $data = [ + 'account' => $account, + 'invoice' => $invoice, + 'invitation' => $invoice->invitations->first(), + 'client' => $invoice->client, + 'amount' => $invoice->amount + ]; + + // create the email view + $view = 'emails.' . $account->getTemplateView(ENTITY_INVOICE) . '_html'; + $data = array_merge($data, [ + 'body' => $templateService->processVariables($template, $data), + 'entityType' => ENTITY_INVOICE, + ]); + + return Response::view($view, $data); + } } diff --git a/app/Http/Controllers/AppController.php b/app/Http/Controllers/AppController.php index 9dcf243fbe02..a602ae7ecb85 100644 --- a/app/Http/Controllers/AppController.php +++ b/app/Http/Controllers/AppController.php @@ -266,18 +266,7 @@ class AppController extends BaseController Cache::flush(); Session::flush(); Artisan::call('migrate', array('--force' => true)); - foreach ([ - 'PaymentLibraries', - 'Fonts', - 'Banks', - 'InvoiceStatus', - 'Currencies', - 'DateFormats', - 'InvoiceDesigns', - 'PaymentTerms', - ] as $seeder) { - Artisan::call('db:seed', array('--force' => true, '--class' => "{$seeder}Seeder")); - } + Artisan::call('db:seed', array('--force' => true, '--class' => "UpdateSeeder")); Event::fire(new UserSettingsChanged()); Session::flash('message', trans('texts.processed_updates')); } catch (Exception $e) { @@ -306,7 +295,7 @@ class AppController extends BaseController public function stats() { - if (Input::get('password') != env('RESELLER_PASSWORD')) { + if ( ! hash_equals(Input::get('password'), env('RESELLER_PASSWORD'))) { sleep(3); return ''; } diff --git a/app/Http/Controllers/BaseAPIController.php b/app/Http/Controllers/BaseAPIController.php index 3a3a1e0bece9..0718e95b91e4 100644 --- a/app/Http/Controllers/BaseAPIController.php +++ b/app/Http/Controllers/BaseAPIController.php @@ -2,6 +2,9 @@ use Session; use Utils; +use Auth; +use Log; +use Input; use Response; use Request; use League\Fractal; @@ -9,8 +12,10 @@ use League\Fractal\Manager; use League\Fractal\Resource\Item; use League\Fractal\Resource\Collection; use League\Fractal\Pagination\IlluminatePaginatorAdapter; +use App\Models\EntityModel; use App\Ninja\Serializers\ArraySerializer; use League\Fractal\Serializer\JsonApiSerializer; +use Illuminate\Pagination\LengthAwarePaginator; /** * @SWG\Swagger( @@ -62,6 +67,74 @@ class BaseAPIController extends Controller } else { $this->manager->setSerializer(new ArraySerializer()); } + + if (Utils::isNinjaDev()) { + \DB::enableQueryLog(); + } + } + + protected function handleAction($request) + { + $entity = $request->entity(); + $action = $request->action; + + $repo = Utils::toCamelCase($this->entityType) . 'Repo'; + + $this->$repo->$action($entity); + + return $this->itemResponse($entity); + } + + protected function listResponse($query) + { + $transformerClass = EntityModel::getTransformerName($this->entityType); + $transformer = new $transformerClass(Auth::user()->account, Input::get('serializer')); + + $includes = $transformer->getDefaultIncludes(); + $includes = $this->getRequestIncludes($includes); + + $query->with($includes); + + if ($updatedAt = Input::get('updated_at')) { + $updatedAt = date('Y-m-d H:i:s', $updatedAt); + $query->where(function($query) use ($includes, $updatedAt) { + $query->where('updated_at', '>=', $updatedAt); + foreach ($includes as $include) { + $query->orWhereHas($include, function($query) use ($updatedAt) { + $query->where('updated_at', '>=', $updatedAt); + }); + } + }); + } + + if ($clientPublicId = Input::get('client_id')) { + $filter = function($query) use ($clientPublicId) { + $query->where('public_id', '=', $clientPublicId); + }; + $query->whereHas('client', $filter); + } + + if ( ! Utils::hasPermission('view_all')){ + if ($this->entityType == ENTITY_USER) { + $query->where('id', '=', Auth::user()->id); + } else { + $query->where('user_id', '=', Auth::user()->id); + } + } + + $data = $this->createCollection($query, $transformer, $this->entityType); + + return $this->response($data); + } + + protected function itemResponse($item) + { + $transformerClass = EntityModel::getTransformerName($this->entityType); + $transformer = new $transformerClass(Auth::user()->account, Input::get('serializer')); + + $data = $this->createItem($item, $transformer, $this->entityType); + + return $this->response($data); } protected function createItem($data, $transformer, $entityType) @@ -74,23 +147,31 @@ class BaseAPIController extends Controller return $this->manager->createData($resource)->toArray(); } - protected function createCollection($data, $transformer, $entityType, $paginator = false) + protected function createCollection($query, $transformer, $entityType) { if ($this->serializer && $this->serializer != API_SERIALIZER_JSON) { $entityType = null; } - $resource = new Collection($data, $transformer, $entityType); - - if ($paginator) { - $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + if (is_a($query, "Illuminate\Database\Eloquent\Builder")) { + $limit = min(MAX_API_PAGE_SIZE, Input::get('per_page', DEFAULT_API_PAGE_SIZE)); + $resource = new Collection($query->get(), $transformer, $entityType); + $resource->setPaginator(new IlluminatePaginatorAdapter($query->paginate($limit))); + } else { + $resource = new Collection($query, $transformer, $entityType); } - + return $this->manager->createData($resource)->toArray(); } protected function response($response) { + if (Utils::isNinjaDev()) { + $count = count(\DB::getQueryLog()); + Log::info(Request::method() . ' - ' . Request::url() . ": $count queries"); + Log::info(json_encode(\DB::getQueryLog())); + } + $index = Request::get('index') ?: 'data'; if ($index == 'none') { @@ -123,26 +204,21 @@ class BaseAPIController extends Controller } - - protected function getIncluded() + protected function getRequestIncludes($data) { - $data = ['user']; - $included = Request::get('include'); $included = explode(',', $included); foreach ($included as $include) { if ($include == 'invoices') { $data[] = 'invoices.invoice_items'; - $data[] = 'invoices.user'; + } elseif ($include == 'client') { + $data[] = 'client.contacts'; } elseif ($include == 'clients') { $data[] = 'clients.contacts'; - $data[] = 'clients.user'; } elseif ($include == 'vendors') { - $data[] = 'vendors.vendorcontacts'; - $data[] = 'vendors.user'; - } - elseif ($include) { + $data[] = 'vendors.vendor_contacts'; + } elseif ($include) { $data[] = $include; } } diff --git a/app/Http/Controllers/BaseController.php b/app/Http/Controllers/BaseController.php index 66abc53e2459..2ce7a633f179 100644 --- a/app/Http/Controllers/BaseController.php +++ b/app/Http/Controllers/BaseController.php @@ -3,6 +3,7 @@ use App\Http\Middleware\PermissionsRequired; use Illuminate\Foundation\Bus\DispatchesJobs; use Illuminate\Foundation\Auth\Access\AuthorizesRequests; +use Input; use Auth; use Utils; @@ -10,7 +11,7 @@ class BaseController extends Controller { use DispatchesJobs, AuthorizesRequests; - protected $entity; + protected $entityType; /** * Setup the layout used by the controller. @@ -23,22 +24,4 @@ class BaseController extends Controller $this->layout = View::make($this->layout); } } - - protected function authorizeCreate() { - $this->authorize('create', $this->entity); - } - - protected function authorizeUpdate($input){ - $creating = empty($input['public_id']) || $input['public_id'] == '-1'; - - if($creating){ - $this->authorize('create', $this->entity); - } - else{ - $className = Utils::getEntityName($this->entity); - - $object = call_user_func(array("App\\Models\\{$className}", 'scope'), $input['public_id'])->firstOrFail(); - $this->authorize('edit', $object); - } - } } diff --git a/app/Http/Controllers/ClientApiController.php b/app/Http/Controllers/ClientApiController.php index fd3b33df6aa5..dd82e9116131 100644 --- a/app/Http/Controllers/ClientApiController.php +++ b/app/Http/Controllers/ClientApiController.php @@ -10,27 +10,19 @@ use App\Ninja\Repositories\ClientRepository; use App\Http\Requests\CreateClientRequest; use App\Http\Controllers\BaseAPIController; use App\Ninja\Transformers\ClientTransformer; -use App\Services\ClientService; use App\Http\Requests\UpdateClientRequest; class ClientApiController extends BaseAPIController { protected $clientRepo; - protected $clientService; - public function __construct(ClientRepository $clientRepo, ClientService $clientService) + protected $entityType = ENTITY_CLIENT; + + public function __construct(ClientRepository $clientRepo) { parent::__construct(); $this->clientRepo = $clientRepo; - $this->clientService = $clientService; - } - - public function ping() - { - $headers = Utils::getApiHeaders(); - - return Response::make('', 200, $headers); } /** @@ -52,27 +44,17 @@ class ClientApiController extends BaseAPIController public function index() { $clients = Client::scope() - ->with($this->getIncluded()) - ->orderBy('created_at', 'desc')->withTrashed(); + ->orderBy('created_at', 'desc') + ->withTrashed(); // Filter by email - if (Input::has('email')) { - - $email = Input::get('email'); + if ($email = Input::get('email')) { $clients = $clients->whereHas('contacts', function ($query) use ($email) { $query->where('email', $email); }); - } - - $clients = $clients->paginate(); - - $transformer = new ClientTransformer(Auth::user()->account, Input::get('serializer')); - $paginator = Client::scope()->withTrashed()->paginate(); - - $data = $this->createCollection($clients, $transformer, ENTITY_CLIENT, $paginator); - - return $this->response($data); + + return $this->listResponse($clients); } /** @@ -100,14 +82,7 @@ class ClientApiController extends BaseAPIController { $client = $this->clientRepo->save($request->input()); - $client = Client::scope($client->public_id) - ->with('country', 'contacts', 'industry', 'size', 'currency') - ->first(); - - $transformer = new ClientTransformer(Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($client, $transformer, ENTITY_CLIENT); - - return $this->response($data); + return $this->itemResponse($client); } /** @@ -134,51 +109,15 @@ class ClientApiController extends BaseAPIController public function update(UpdateClientRequest $request, $publicId) { - if ($request->action == ACTION_ARCHIVE) { - - - $client = Client::scope($publicId)->withTrashed()->first(); - - if(!$client) - return $this->errorResponse(['message'=>'Record not found'], 400); - - $this->clientRepo->archive($client); - - $transformer = new ClientTransformer(Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($client, $transformer, ENTITY_CLIENT); - - return $this->response($data); + if ($request->action) { + return $this->handleAction($request); } - else if ($request->action == ACTION_RESTORE){ - - $client = Client::scope($publicId)->withTrashed()->first(); - - if(!$client) - return $this->errorResponse(['message'=>'Client not found.'], 400); - - $this->clientRepo->restore($client); - - $transformer = new ClientTransformer(Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($client, $transformer, ENTITY_CLIENT); - - return $this->response($data); - } - + $data = $request->input(); $data['public_id'] = $publicId; - $this->clientRepo->save($data); + $client = $this->clientRepo->save($data, $request->entity()); - $client = Client::scope($publicId) - ->with('country', 'contacts', 'industry', 'size', 'currency') - ->first(); - - if(!$client) - return $this->errorResponse(['message'=>'Client not found.'],400); - - $transformer = new ClientTransformer(Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($client, $transformer, ENTITY_CLIENT); - - return $this->response($data); + return $this->itemResponse($client); } @@ -204,23 +143,13 @@ class ClientApiController extends BaseAPIController * ) */ - public function destroy($publicId) + public function destroy(UpdateClientRequest $request) { - - $client = Client::scope($publicId)->withTrashed()->first(); + $client = $request->entity(); + $this->clientRepo->delete($client); - $client = Client::scope($publicId) - ->with('country', 'contacts', 'industry', 'size', 'currency') - ->withTrashed() - ->first(); - - $transformer = new ClientTransformer(Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($client, $transformer, ENTITY_CLIENT); - - return $this->response($data); - + return $this->itemResponse($client); } - - -} + +} \ No newline at end of file diff --git a/app/Http/Controllers/ClientController.php b/app/Http/Controllers/ClientController.php index f8690be44ea5..e5b6fd432b1a 100644 --- a/app/Http/Controllers/ClientController.php +++ b/app/Http/Controllers/ClientController.php @@ -28,6 +28,7 @@ use App\Models\Task; use App\Ninja\Repositories\ClientRepository; use App\Services\ClientService; +use App\Http\Requests\ClientRequest; use App\Http\Requests\CreateClientRequest; use App\Http\Requests\UpdateClientRequest; @@ -35,7 +36,7 @@ class ClientController extends BaseController { protected $clientService; protected $clientRepo; - protected $entity = ENTITY_CLIENT; + protected $entityType = ENTITY_CLIENT; public function __construct(ClientRepository $clientRepo, ClientService $clientService) { @@ -81,11 +82,7 @@ class ClientController extends BaseController */ public function store(CreateClientRequest $request) { - $data = $request->input(); - - $this->authorizeUpdate($data); - - $client = $this->clientService->save($data); + $client = $this->clientService->save($request->input()); Session::flash('message', trans('texts.created_client')); @@ -98,11 +95,9 @@ class ClientController extends BaseController * @param int $id * @return Response */ - public function show($publicId) + public function show(ClientRequest $request) { - $client = Client::withTrashed()->scope($publicId)->with('contacts', 'size', 'industry')->firstOrFail(); - - $this->authorize('view', $client); + $client = $request->entity(); $user = Auth::user(); Utils::trackViewed($client->getDisplayName(), ENTITY_CLIENT); @@ -152,10 +147,8 @@ class ClientController extends BaseController * * @return Response */ - public function create() + public function create(ClientRequest $request) { - $this->authorizeCreate(); - if (Client::scope()->withTrashed()->count() > Auth::user()->getMaxNumClients()) { return View::make('error', ['hideHeader' => true, 'error' => "Sorry, you've exceeded the limit of ".Auth::user()->getMaxNumClients()." clients"]); } @@ -178,16 +171,14 @@ class ClientController extends BaseController * @param int $id * @return Response */ - public function edit($publicId) + public function edit(ClientRequest $request) { - $client = Client::scope($publicId)->with('contacts')->firstOrFail(); - - $this->authorize('edit', $client); - + $client = $request->entity(); + $data = [ 'client' => $client, 'method' => 'PUT', - 'url' => 'clients/'.$publicId, + 'url' => 'clients/'.$client->public_id, 'title' => trans('texts.edit_client'), ]; @@ -226,11 +217,7 @@ class ClientController extends BaseController */ public function update(UpdateClientRequest $request) { - $data = $request->input(); - - $this->authorizeUpdate($data); - - $client = $this->clientService->save($data); + $client = $this->clientService->save($request->input(), $request->entity()); Session::flash('message', trans('texts.updated_client')); diff --git a/app/Http/Controllers/CreditController.php b/app/Http/Controllers/CreditController.php index 23577f951389..c4250903fd32 100644 --- a/app/Http/Controllers/CreditController.php +++ b/app/Http/Controllers/CreditController.php @@ -12,12 +12,13 @@ use App\Models\Client; use App\Services\CreditService; use App\Ninja\Repositories\CreditRepository; use App\Http\Requests\CreateCreditRequest; +use App\Http\Requests\CreditRequest; class CreditController extends BaseController { protected $creditRepo; protected $creditService; - protected $entity = ENTITY_CREDIT; + protected $entityType = ENTITY_CREDIT; public function __construct(CreditRepository $creditRepo, CreditService $creditService) { @@ -55,23 +56,21 @@ class CreditController extends BaseController return $this->creditService->getDatatable($clientPublicId, Input::get('sSearch')); } - public function create($clientPublicId = 0) + public function create(CreditRequest $request) { - $this->authorizeCreate(); - $data = array( - 'clientPublicId' => Input::old('client') ? Input::old('client') : $clientPublicId, - //'invoicePublicId' => Input::old('invoice') ? Input::old('invoice') : $invoicePublicId, + 'clientPublicId' => Input::old('client') ? Input::old('client') : ($request->client_id ?: 0), 'credit' => null, 'method' => 'POST', 'url' => 'credits', 'title' => trans('texts.new_credit'), - //'invoices' => Invoice::scope()->with('client', 'invoice_status')->orderBy('invoice_number')->get(), - 'clients' => Client::scope()->with('contacts')->orderBy('name')->get(), ); + 'clients' => Client::scope()->with('contacts')->orderBy('name')->get(), + ); return View::make('credits.edit', $data); } + /* public function edit($publicId) { $credit = Credit::scope($publicId)->firstOrFail(); @@ -90,7 +89,8 @@ class CreditController extends BaseController return View::make('credit.edit', $data); } - + */ + public function store(CreateCreditRequest $request) { $credit = $this->creditRepo->save($request->input()); diff --git a/app/Http/Controllers/DocumentController.php b/app/Http/Controllers/DocumentController.php index c0e25e773d11..d597ba004474 100644 --- a/app/Http/Controllers/DocumentController.php +++ b/app/Http/Controllers/DocumentController.php @@ -12,10 +12,13 @@ use Response; use App\Models\Document; use App\Ninja\Repositories\DocumentRepository; +use App\Http\Requests\DocumentRequest; +use App\Http\Requests\CreateDocumentRequest; + class DocumentController extends BaseController { protected $documentRepo; - protected $entity = ENTITY_DOCUMENT; + protected $entityType = ENTITY_DOCUMENT; public function __construct(DocumentRepository $documentRepo) { @@ -24,14 +27,9 @@ class DocumentController extends BaseController $this->documentRepo = $documentRepo; } - public function get($publicId) + public function get(DocumentRequest $request) { - $document = Document::scope($publicId) - ->firstOrFail(); - - $this->authorize('view', $document); - - return static::getDownloadResponse($document); + return static::getDownloadResponse($request->entity()); } public static function getDownloadResponse($document){ @@ -60,12 +58,9 @@ class DocumentController extends BaseController return $response; } - public function getPreview($publicId) + public function getPreview(DocumentRequest $request) { - $document = Document::scope($publicId) - ->firstOrFail(); - - $this->authorize('view', $document); + $document = $request->entity(); if(empty($document->preview)){ return Response::view('error', array('error'=>'Preview does not exist!'), 404); @@ -83,16 +78,14 @@ class DocumentController extends BaseController return $response; } - public function getVFSJS($publicId, $name){ - $document = Document::scope($publicId) - ->firstOrFail(); + public function getVFSJS(DocumentRequest $request, $publicId, $name) + { + $document = $request->entity(); if(substr($name, -3)=='.js'){ $name = substr($name, 0, -3); } - $this->authorize('view', $document); - if(!$document->isPDFEmbeddable()){ return Response::view('error', array('error'=>'Image does not exist!'), 404); } @@ -106,14 +99,12 @@ class DocumentController extends BaseController return $response; } - public function postUpload() + public function postUpload(CreateDocumentRequest $request) { if (!Utils::hasFeature(FEATURE_DOCUMENTS)) { return; } - $this->authorizeCreate(); - $result = $this->documentRepo->upload(Input::all()['file'], $doc_array); if(is_string($result)){ diff --git a/app/Http/Controllers/ExpenseApiController.php b/app/Http/Controllers/ExpenseApiController.php index 88ff5497cefd..725067aa1f2c 100644 --- a/app/Http/Controllers/ExpenseApiController.php +++ b/app/Http/Controllers/ExpenseApiController.php @@ -1,5 +1,5 @@ withTrashed() + ->with('client', 'invoice', 'vendor') ->orderBy('created_at','desc'); - $expenses = $expenses->paginate(); - - $transformer = new ExpenseTransformer(Auth::user()->account, Input::get('serializer')); - $paginator = Expense::scope()->withTrashed()->paginate(); - - $data = $this->createCollection($expenses, $transformer, ENTITY_EXPENSE, $paginator); - - return $this->response($data); - + return $this->listResponse($expenses); } public function update() diff --git a/app/Http/Controllers/ExpenseController.php b/app/Http/Controllers/ExpenseController.php index e53122e46dcb..d4184abf3fca 100644 --- a/app/Http/Controllers/ExpenseController.php +++ b/app/Http/Controllers/ExpenseController.php @@ -17,6 +17,8 @@ use App\Models\Expense; use App\Models\Client; use App\Services\ExpenseService; use App\Ninja\Repositories\ExpenseRepository; + +use App\Http\Requests\ExpenseRequest; use App\Http\Requests\CreateExpenseRequest; use App\Http\Requests\UpdateExpenseRequest; @@ -25,7 +27,7 @@ class ExpenseController extends BaseController // Expenses protected $expenseRepo; protected $expenseService; - protected $entity = ENTITY_EXPENSE; + protected $entityType = ENTITY_EXPENSE; public function __construct(ExpenseRepository $expenseRepo, ExpenseService $expenseService) { @@ -69,38 +71,35 @@ class ExpenseController extends BaseController return $this->expenseService->getDatatableVendor($vendorPublicId); } - public function create($vendorPublicId = null, $clientPublicId = null) + public function create(ExpenseRequest $request) { - $this->authorizeCreate(); - - if($vendorPublicId != 0) { - $vendor = Vendor::scope($vendorPublicId)->with('vendorcontacts')->firstOrFail(); + if ($request->vendor_id != 0) { + $vendor = Vendor::scope($request->vendor_id)->with('vendor_contacts')->firstOrFail(); } else { $vendor = null; } + $data = array( - 'vendorPublicId' => Input::old('vendor') ? Input::old('vendor') : $vendorPublicId, + 'vendorPublicId' => Input::old('vendor') ? Input::old('vendor') : $request->vendor_id, 'expense' => null, 'method' => 'POST', 'url' => 'expenses', 'title' => trans('texts.new_expense'), - 'vendors' => Vendor::scope()->with('vendorcontacts')->orderBy('name')->get(), + 'vendors' => Vendor::scope()->with('vendor_contacts')->orderBy('name')->get(), 'vendor' => $vendor, 'clients' => Client::scope()->with('contacts')->orderBy('name')->get(), - 'clientPublicId' => $clientPublicId, - ); + 'clientPublicId' => $request->client_id, + ); $data = array_merge($data, self::getViewModel()); return View::make('expenses.edit', $data); } - public function edit($publicId) + public function edit(ExpenseRequest $request) { - $expense = Expense::scope($publicId)->with('documents')->firstOrFail(); - - $this->authorize('edit', $expense); - + $expense = $request->entity(); + $expense->expense_date = Utils::fromSqlDate($expense->expense_date); $actions = []; @@ -108,15 +107,6 @@ class ExpenseController extends BaseController $actions[] = ['url' => URL::to("invoices/{$expense->invoice->public_id}/edit"), 'label' => trans("texts.view_invoice")]; } else { $actions[] = ['url' => 'javascript:submitAction("invoice")', 'label' => trans("texts.invoice_expense")]; - - /* - // 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; @@ -131,10 +121,10 @@ class ExpenseController extends BaseController 'vendor' => null, 'expense' => $expense, 'method' => 'PUT', - 'url' => 'expenses/'.$publicId, + 'url' => 'expenses/'.$expense->public_id, 'title' => 'Edit Expense', 'actions' => $actions, - 'vendors' => Vendor::scope()->with('vendorcontacts')->orderBy('name')->get(), + 'vendors' => Vendor::scope()->with('vendor_contacts')->orderBy('name')->get(), 'vendorPublicId' => $expense->vendor ? $expense->vendor->public_id : null, 'clients' => Client::scope()->with('contacts')->orderBy('name')->get(), 'clientPublicId' => $expense->client ? $expense->client->public_id : null, @@ -155,10 +145,8 @@ class ExpenseController extends BaseController { $data = $request->input(); $data['documents'] = $request->file('documents'); - - $this->authorizeUpdate($data); - - $expense = $this->expenseService->save($data, true); + + $expense = $this->expenseService->save($data, $request->entity()); Session::flash('message', trans('texts.updated_expense')); @@ -174,9 +162,7 @@ class ExpenseController extends BaseController { $data = $request->input(); $data['documents'] = $request->file('documents'); - - $this->authorizeUpdate($data); - + $expense = $this->expenseService->save($data); Session::flash('message', trans('texts.created_expense')); diff --git a/app/Http/Controllers/ExportController.php b/app/Http/Controllers/ExportController.php index db74adb89cee..ebaaa1f6c306 100644 --- a/app/Http/Controllers/ExportController.php +++ b/app/Http/Controllers/ExportController.php @@ -164,12 +164,12 @@ class ExportController extends BaseController if ($request->input(ENTITY_VENDOR)) { $data['clients'] = Vendor::scope() - ->with('user', 'vendorcontacts', 'country') + ->with('user', 'vendor_contacts', 'country') ->withArchived() ->get(); $data['vendor_contacts'] = VendorContact::scope() - ->with('user', 'vendor.contacts') + ->with('user', 'vendor.vendor_contacts') ->withTrashed() ->get(); diff --git a/app/Http/Controllers/ImportController.php b/app/Http/Controllers/ImportController.php index 38618475064b..fe006332ca48 100644 --- a/app/Http/Controllers/ImportController.php +++ b/app/Http/Controllers/ImportController.php @@ -1,4 +1,4 @@ -withTrashed(); - $invoices = Invoice::scope()->withTrashed() - ->with(array_merge(['invoice_items'], $this->getIncluded())); + $invoices = Invoice::scope() + ->withTrashed() + ->with('invoice_items', 'client') + ->orderBy('created_at', 'desc'); - if ($clientPublicId = Input::get('client_id')) { - $filter = function($query) use ($clientPublicId) { - $query->where('public_id', '=', $clientPublicId); - }; - $invoices->whereHas('client', $filter); - $paginator->whereHas('client', $filter); - } - - $invoices = $invoices->orderBy('created_at', 'desc')->paginate(); - - /* - // Add the first invitation link to the data - foreach ($invoices as $key => $invoice) { - foreach ($invoice->invitations as $subKey => $invitation) { - $invoices[$key]['link'] = $invitation->getLink(); - } - unset($invoice['invitations']); - } - */ - - $transformer = new InvoiceTransformer(Auth::user()->account, Input::get('serializer')); - $paginator = $paginator->paginate(); - - $data = $this->createCollection($invoices, $transformer, 'invoices', $paginator); - - return $this->response($data); + return $this->listResponse($invoices); } /** @@ -104,18 +83,9 @@ class InvoiceApiController extends BaseAPIController * ) */ - public function show($publicId) + public function show(InvoiceRequest $request) { - - $invoice = Invoice::scope($publicId)->withTrashed()->first(); - - if(!$invoice) - return $this->errorResponse(['message'=>'Invoice does not exist!'], 404); - - $transformer = new InvoiceTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($invoice, $transformer, 'invoice'); - - return $this->response($data); + return $this->itemResponse($request->entity()); } /** @@ -139,7 +109,7 @@ class InvoiceApiController extends BaseAPIController * ) * ) */ - public function store(CreateInvoiceRequest $request) + public function store(CreateInvoiceAPIRequest $request) { $data = Input::all(); $error = null; @@ -210,11 +180,11 @@ class InvoiceApiController extends BaseAPIController } } - $invoice = Invoice::scope($invoice->public_id)->with('client', 'invoice_items', 'invitations')->first(); - $transformer = new InvoiceTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($invoice, $transformer, 'invoice'); - - return $this->response($data); + $invoice = Invoice::scope($invoice->public_id) + ->with('client', 'invoice_items', 'invitations') + ->first(); + + return $this->itemResponse($invoice); } private function prepareData($data, $client) @@ -300,36 +270,21 @@ class InvoiceApiController extends BaseAPIController $item[$key] = $val; } } - + return $item; } - public function emailInvoice() + public function emailInvoice(InvoiceRequest $request) { - $data = Input::all(); - $error = null; + $invoice = $request->entity(); - $invoice = Invoice::scope($data['id'])->withTrashed()->first(); - - if(!$invoice) - return $this->errorResponse(['message'=>'Invoice does not exist.'], 400); - - - $this->mailer->sendInvoice($invoice, false, false); - - - if($error) { - return $this->errorResponse(['message'=>'There was an error sending the invoice'], 400); - } - else { - $response = json_encode(RESULT_SUCCESS, JSON_PRETTY_PRINT); - } + $this->mailer->sendInvoice($invoice); + $response = json_encode(RESULT_SUCCESS, JSON_PRETTY_PRINT); $headers = Utils::getApiHeaders(); - return Response::make($response, $error ? 400 : 200, $headers); + return Response::make($response, 200, $headers); } - /** * @SWG\Put( * path="/invoices", @@ -351,45 +306,25 @@ class InvoiceApiController extends BaseAPIController * ) * ) */ - public function update(UpdateInvoiceRequest $request, $publicId) + public function update(UpdateInvoiceAPIRequest $request, $publicId) { - if ($request->action == ACTION_ARCHIVE) { - $invoice = Invoice::scope($publicId)->firstOrFail(); - $this->invoiceRepo->archive($invoice); - - $transformer = new InvoiceTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($invoice, $transformer, 'invoice'); - - return $this->response($data); - } - else if ($request->action == ACTION_CONVERT) { - $quote = Invoice::scope($publicId)->firstOrFail(); + if ($request->action == ACTION_CONVERT) { + $quote = $request->entity(); $invoice = $this->invoiceRepo->cloneInvoice($quote, $quote->id); - - $transformer = new InvoiceTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($invoice, $transformer, 'invoice'); - - return $this->response($data); - } - else if ($request->action == ACTION_RESTORE) { - $invoice = Invoice::scope($publicId)->withTrashed()->firstOrFail(); - $this->invoiceRepo->restore($invoice); - - $transformer = new InvoiceTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($invoice, $transformer, 'invoice'); - - return $this->response($data); + return $this->itemResponse($invoice); + } elseif ($request->action) { + return $this->handleAction($request); } $data = $request->input(); $data['public_id'] = $publicId; - $this->invoiceService->save($data); + $this->invoiceService->save($data, $request->entity()); - $invoice = Invoice::scope($publicId)->with('client', 'invoice_items', 'invitations')->firstOrFail(); - $transformer = new InvoiceTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($invoice, $transformer, 'invoice'); - - return $this->response($data); + $invoice = Invoice::scope($publicId) + ->with('client', 'invoice_items', 'invitations') + ->firstOrFail(); + + return $this->itemResponse($invoice); } /** @@ -414,18 +349,13 @@ class InvoiceApiController extends BaseAPIController * ) */ - public function destroy($publicId) + public function destroy(UpdateInvoiceAPIRequest $request) { - $data['public_id'] = $publicId; - $invoice = Invoice::scope($publicId)->firstOrFail(); - + $invoice = $request->entity(); + $this->invoiceRepo->delete($invoice); - $transformer = new InvoiceTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($invoice, $transformer, 'invoice'); - - return $this->response($data); - + return $this->itemResponse($invoice); } } diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index 79ce2aee93b1..9a0dadf494c4 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -27,7 +27,10 @@ use App\Ninja\Repositories\ClientRepository; use App\Ninja\Repositories\DocumentRepository; use App\Services\InvoiceService; use App\Services\RecurringInvoiceService; -use App\Http\Requests\SaveInvoiceWithClientRequest; + +use App\Http\Requests\InvoiceRequest; +use App\Http\Requests\CreateInvoiceRequest; +use App\Http\Requests\UpdateInvoiceRequest; class InvoiceController extends BaseController { @@ -37,7 +40,7 @@ class InvoiceController extends BaseController protected $documentRepo; protected $invoiceService; protected $recurringInvoiceService; - protected $entity = ENTITY_INVOICE; + protected $entityType = ENTITY_INVOICE; public function __construct(Mailer $mailer, InvoiceRepository $invoiceRepo, ClientRepository $clientRepo, InvoiceService $invoiceService, DocumentRepository $documentRepo, RecurringInvoiceService $recurringInvoiceService) { @@ -88,18 +91,13 @@ class InvoiceController extends BaseController return $this->recurringInvoiceService->getDatatable($accountId, $clientPublicId, ENTITY_RECURRING_INVOICE, $search); } - public function edit($publicId, $clone = false) + public function edit(InvoiceRequest $request, $publicId, $clone = false) { $account = Auth::user()->account; - $invoice = Invoice::scope($publicId) - ->with('invitations', 'account.country', 'client.contacts', 'client.country', 'invoice_items', 'documents', 'expenses', 'expenses.documents', 'payments') - ->withTrashed() - ->firstOrFail(); - - $this->authorize('edit', $invoice); + $invoice = $request->entity()->load('invitations', 'account.country', 'client.contacts', 'client.country', 'invoice_items', 'documents', 'expenses', 'expenses.documents', 'payments'); $entityType = $invoice->getEntityType(); - + $contactIds = DB::table('invitations') ->join('contacts', 'contacts.id', '=', 'invitations.contact_id') ->where('invitations.invoice_id', '=', $invoice->id) @@ -120,7 +118,7 @@ class InvoiceController extends BaseController } else { Utils::trackViewed($invoice->getDisplayName().' - '.$invoice->client->getDisplayName(), $invoice->getEntityType()); $method = 'PUT'; - $url = "{$entityType}s/{$publicId}"; + $url = "{$entityType}s/{$invoice->public_id}"; $clients->whereId($invoice->client_id); } @@ -229,28 +227,27 @@ class InvoiceController extends BaseController return View::make('invoices.edit', $data); } - public function create($clientPublicId = 0, $isRecurring = false) + public function create(InvoiceRequest $request, $clientPublicId = 0, $isRecurring = false) { - $this->authorizeCreate(); - $account = Auth::user()->account; + $entityType = $isRecurring ? ENTITY_RECURRING_INVOICE : ENTITY_INVOICE; $clientId = null; - if ($clientPublicId) { - $clientId = Client::getPrivateId($clientPublicId); + if ($request->client_id) { + $clientId = Client::getPrivateId($request->client_id); } $invoice = $account->createInvoice($entityType, $clientId); $invoice->public_id = 0; - if(Session::get('expenses')){ + if (Session::get('expenses')) { $invoice->expenses = Expense::scope(Session::get('expenses'))->with('documents')->get(); } $clients = Client::scope()->with('contacts', 'country')->orderBy('name'); - if(!Auth::user()->hasPermission('view_all')){ + if (!Auth::user()->hasPermission('view_all')) { $clients = $clients->where('clients.user_id', '=', Auth::user()->id); } @@ -267,9 +264,9 @@ class InvoiceController extends BaseController return View::make('invoices.edit', $data); } - public function createRecurring($clientPublicId = 0) + public function createRecurring(InvoiceRequest $request, $clientPublicId = 0) { - return self::create($clientPublicId, true); + return self::create($request, $clientPublicId, true); } private static function getViewModel($invoice) @@ -395,17 +392,15 @@ class InvoiceController extends BaseController * * @return Response */ - public function store(SaveInvoiceWithClientRequest $request) + public function store(CreateInvoiceRequest $request) { $data = $request->input(); $data['documents'] = $request->file('documents'); - - $this->authorizeUpdate($data); - + $action = Input::get('action'); $entityType = Input::get('entityType'); - $invoice = $this->invoiceService->save($data, true); + $invoice = $this->invoiceService->save($data); $entityType = $invoice->getEntityType(); $message = trans("texts.created_{$entityType}"); @@ -434,25 +429,23 @@ class InvoiceController extends BaseController * @param int $id * @return Response */ - public function update(SaveInvoiceWithClientRequest $request) + public function update(UpdateInvoiceRequest $request) { $data = $request->input(); $data['documents'] = $request->file('documents'); - - $this->authorizeUpdate($data); - + $action = Input::get('action'); $entityType = Input::get('entityType'); - $invoice = $this->invoiceService->save($data, true); + $invoice = $this->invoiceService->save($data, $request->entity()); $entityType = $invoice->getEntityType(); $message = trans("texts.updated_{$entityType}"); Session::flash('message', $message); if ($action == 'clone') { - return $this->cloneInvoice($invoice->public_id); + return $this->cloneInvoice($request, $invoice->public_id); } elseif ($action == 'convert') { - return $this->convertQuote($invoice->public_id); + return $this->convertQuote($request, $invoice->public_id); } elseif ($action == 'email') { return $this->emailInvoice($invoice, Input::get('pdfupload')); } @@ -521,7 +514,7 @@ class InvoiceController extends BaseController { Session::reflash(); - return Redirect::to("invoices/{$publicId}/edit"); + return Redirect::to("invoices/$publicId/edit"); } /** @@ -549,23 +542,23 @@ class InvoiceController extends BaseController } } - public function convertQuote($publicId) + public function convertQuote(InvoiceRequest $request) { - $invoice = Invoice::with('invoice_items')->scope($publicId)->firstOrFail(); - $clone = $this->invoiceService->convertQuote($invoice); + $clone = $this->invoiceService->convertQuote($request->entity()); Session::flash('message', trans('texts.converted_to_invoice')); - return Redirect::to('invoices/'.$clone->public_id); + + return Redirect::to('invoices/' . $clone->public_id); } - public function cloneInvoice($publicId) + public function cloneInvoice(InvoiceRequest $request, $publicId) { - return self::edit($publicId, true); + return self::edit($request, $publicId, true); } - public function invoiceHistory($publicId) + public function invoiceHistory(InvoiceRequest $request) { - $invoice = Invoice::withTrashed()->scope($publicId)->firstOrFail(); + $invoice = $request->entity(); $invoice->load('user', 'invoice_items', 'documents', 'expenses', 'expenses.documents', 'account.country', 'client.contacts', 'client.country'); $invoice->invoice_date = Utils::fromSqlDate($invoice->invoice_date); $invoice->due_date = Utils::fromSqlDate($invoice->due_date); @@ -591,7 +584,7 @@ class InvoiceController extends BaseController $backup = json_decode($activity->json_backup); $backup->invoice_date = Utils::fromSqlDate($backup->invoice_date); $backup->due_date = Utils::fromSqlDate($backup->due_date); - $invoice->features = [ + $backup->features = [ 'customize_invoice_design' => Auth::user()->hasFeature(FEATURE_CUSTOMIZE_INVOICE_DESIGN), 'remove_created_by' => Auth::user()->hasFeature(FEATURE_REMOVE_CREATED_BY), 'invoice_settings' => Auth::user()->hasFeature(FEATURE_INVOICE_SETTINGS), diff --git a/app/Http/Controllers/PaymentApiController.php b/app/Http/Controllers/PaymentApiController.php index 7022f0c3e840..3355b6bff8f4 100644 --- a/app/Http/Controllers/PaymentApiController.php +++ b/app/Http/Controllers/PaymentApiController.php @@ -12,18 +12,21 @@ use App\Ninja\Repositories\PaymentRepository; use App\Http\Controllers\BaseAPIController; use App\Ninja\Transformers\PaymentTransformer; use App\Ninja\Transformers\InvoiceTransformer; +use App\Http\Requests\UpdatePaymentRequest; +use App\Http\Requests\CreatePaymentAPIRequest; class PaymentApiController extends BaseAPIController { protected $paymentRepo; + protected $entityType = ENTITY_PAYMENT; + public function __construct(PaymentRepository $paymentRepo, ContactMailer $contactMailer) { parent::__construct(); $this->paymentRepo = $paymentRepo; $this->contactMailer = $contactMailer; - } /** @@ -44,85 +47,49 @@ class PaymentApiController extends BaseAPIController */ public function index() { - $paginator = Payment::scope(); $payments = Payment::scope() - ->with('client.contacts', 'invitation', 'user', 'invoice')->withTrashed(); + ->withTrashed() + ->with(['invoice']) + ->orderBy('created_at', 'desc'); - if ($clientPublicId = Input::get('client_id')) { - $filter = function($query) use ($clientPublicId) { - $query->where('public_id', '=', $clientPublicId); - }; - $payments->whereHas('client', $filter); - $paginator->whereHas('client', $filter); - } - - $payments = $payments->orderBy('created_at', 'desc')->paginate(); - $paginator = $paginator->paginate(); - - $transformer = new PaymentTransformer(Auth::user()->account, Input::get('serializer')); - $data = $this->createCollection($payments, $transformer, 'payments', $paginator); - - return $this->response($data); + return $this->listResponse($payments); } + /** + * @SWG\Put( + * path="/payments/{payment_id", + * summary="Update a payment", + * tags={"payment"}, + * @SWG\Parameter( + * in="body", + * name="body", + * @SWG\Schema(ref="#/definitions/Payment") + * ), + * @SWG\Response( + * response=200, + * description="Update payment", + * @SWG\Schema(type="object", @SWG\Items(ref="#/definitions/Payment")) + * ), + * @SWG\Response( + * response="default", + * description="an ""unexpected"" error" + * ) + * ) + */ - /** - * @SWG\Put( - * path="/payments/{payment_id", - * summary="Update a payment", - * tags={"payment"}, - * @SWG\Parameter( - * in="body", - * name="body", - * @SWG\Schema(ref="#/definitions/Payment") - * ), - * @SWG\Response( - * response=200, - * description="Update payment", - * @SWG\Schema(type="object", @SWG\Items(ref="#/definitions/Payment")) - * ), - * @SWG\Response( - * response="default", - * description="an ""unexpected"" error" - * ) - * ) - */ - - public function update(Request $request, $publicId) - { - $data = Input::all(); - $data['public_id'] = $publicId; - $error = false; - - if ($request->action == ACTION_ARCHIVE) { - $payment = Payment::scope($publicId)->withTrashed()->firstOrFail(); - $this->paymentRepo->archive($payment); - - $transformer = new PaymentTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($payment, $transformer, 'invoice'); - - return $this->response($data); - } - - $payment = $this->paymentRepo->save($data); - - if ($error) { - return $error; - } - - /* - $invoice = Invoice::scope($data['invoice_id'])->with('client', 'invoice_items', 'invitations')->with(['payments' => function($query) { - $query->withTrashed(); - }])->withTrashed()->first(); - */ - - $transformer = new PaymentTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($payment, $transformer, 'invoice'); - - return $this->response($data); - + public function update(UpdatePaymentRequest $request, $publicId) + { + if ($request->action) { + return $this->handleAction($request); } + $data = $request->input(); + $data['public_id'] = $publicId; + $payment = $this->paymentRepo->save($data, $request->entity()); + + return $this->itemResponse($payment); + } + /** * @SWG\Post( @@ -145,89 +112,46 @@ class PaymentApiController extends BaseAPIController * ) * ) */ - public function store() + public function store(CreatePaymentAPIRequest $request) { - $data = Input::all(); - $error = false; - - if (isset($data['invoice_id'])) { - $invoice = Invoice::scope($data['invoice_id'])->with('client')->first(); - - if ($invoice) { - $data['invoice_id'] = $invoice->id; - $data['client_id'] = $invoice->client->id; - } else { - $error = trans('validation.not_in', ['attribute' => 'invoice_id']); - } - } else { - $error = trans('validation.not_in', ['attribute' => 'invoice_id']); - } - - if (!isset($data['transaction_reference'])) { - $data['transaction_reference'] = ''; - } - - if ($error) { - return $error; - } - - $payment = $this->paymentRepo->save($data); + $payment = $this->paymentRepo->save($request->input()); if (Input::get('email_receipt')) { $this->contactMailer->sendPaymentConfirmation($payment); } - /* - $invoice = Invoice::scope($invoice->public_id)->with('client', 'invoice_items', 'invitations')->with(['payments' => function($query) { - $query->withTrashed(); - }])->first(); - */ - - $transformer = new PaymentTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($payment, $transformer, 'invoice'); - - return $this->response($data); - + return $this->itemResponse($payment); } - /** - * @SWG\Delete( - * path="/payments/{payment_id}", - * summary="Delete a payment", - * tags={"payment"}, - * @SWG\Parameter( - * in="body", - * name="body", - * @SWG\Schema(ref="#/definitions/Payment") - * ), - * @SWG\Response( - * response=200, - * description="Delete payment", - * @SWG\Schema(type="object", @SWG\Items(ref="#/definitions/Payment")) - * ), - * @SWG\Response( - * response="default", - * description="an ""unexpected"" error" - * ) - * ) - */ + /** + * @SWG\Delete( + * path="/payments/{payment_id}", + * summary="Delete a payment", + * tags={"payment"}, + * @SWG\Parameter( + * in="body", + * name="body", + * @SWG\Schema(ref="#/definitions/Payment") + * ), + * @SWG\Response( + * response=200, + * description="Delete payment", + * @SWG\Schema(type="object", @SWG\Items(ref="#/definitions/Payment")) + * ), + * @SWG\Response( + * response="default", + * description="an ""unexpected"" error" + * ) + * ) + */ - public function destroy($publicId) - { + public function destroy(UpdatePaymentRequest $request) + { + $payment = $request->entity(); + + $this->clientRepo->delete($payment); - $payment = Payment::scope($publicId)->withTrashed()->first(); - $invoiceId = $payment->invoice->public_id; + return $this->itemResponse($payment); + } - $this->paymentRepo->delete($payment); - - /* - $invoice = Invoice::scope($invoiceId)->with('client', 'invoice_items', 'invitations')->with(['payments' => function($query) { - $query->withTrashed(); - }])->first(); - */ - $transformer = new PaymentTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($payment, $transformer, 'invoice'); - - return $this->response($data); - } } diff --git a/app/Http/Controllers/PaymentController.php b/app/Http/Controllers/PaymentController.php index 84d281478193..8618fafdba58 100644 --- a/app/Http/Controllers/PaymentController.php +++ b/app/Http/Controllers/PaymentController.php @@ -27,12 +27,13 @@ use App\Ninja\Mailers\ContactMailer; use App\Ninja\Mailers\UserMailer; use App\Services\PaymentService; +use App\Http\Requests\PaymentRequest; use App\Http\Requests\CreatePaymentRequest; use App\Http\Requests\UpdatePaymentRequest; class PaymentController extends BaseController { - protected $entity = ENTITY_PAYMENT; + protected $entityType = ENTITY_PAYMENT; public function __construct(PaymentRepository $paymentRepo, InvoiceRepository $invoiceRepo, AccountRepository $accountRepo, ContactMailer $contactMailer, PaymentService $paymentService, UserMailer $userMailer) { @@ -72,10 +73,8 @@ class PaymentController extends BaseController return $this->paymentService->getDatatable($clientPublicId, Input::get('sSearch')); } - public function create($clientPublicId = 0, $invoicePublicId = 0) + public function create(PaymentRequest $request) { - $this->authorizeCreate(); - $invoices = Invoice::scope() ->where('is_recurring', '=', false) ->where('is_quote', '=', false) @@ -84,8 +83,8 @@ class PaymentController extends BaseController ->orderBy('invoice_number')->get(); $data = array( - 'clientPublicId' => Input::old('client') ? Input::old('client') : $clientPublicId, - 'invoicePublicId' => Input::old('invoice') ? Input::old('invoice') : $invoicePublicId, + 'clientPublicId' => Input::old('client') ? Input::old('client') : ($request->client_id ?: 0), + 'invoicePublicId' => Input::old('invoice') ? Input::old('invoice') : ($request->invoice_id ?: 0), 'invoice' => null, 'invoices' => $invoices, 'payment' => null, @@ -99,12 +98,10 @@ class PaymentController extends BaseController return View::make('payments.edit', $data); } - public function edit($publicId) + public function edit(PaymentRequest $request) { - $payment = Payment::scope($publicId)->firstOrFail(); - - $this->authorize('edit', $payment); - + $payment = $request->entity(); + $payment->payment_date = Utils::fromSqlDate($payment->payment_date); $data = array( @@ -114,7 +111,7 @@ class PaymentController extends BaseController ->with('client', 'invoice_status')->orderBy('invoice_number')->get(), 'payment' => $payment, 'method' => 'PUT', - 'url' => 'payments/'.$publicId, + 'url' => 'payments/'.$payment->public_id, 'title' => trans('texts.edit_payment'), 'paymentTypes' => Cache::get('paymentTypes'), 'clients' => Client::scope()->with('contacts')->orderBy('name')->get(), ); @@ -386,9 +383,18 @@ class PaymentController extends BaseController $license->save(); } - return $productId == PRODUCT_INVOICE_DESIGNS ? file_get_contents(storage_path() . '/invoice_designs.txt') : 'valid'; + if ($productId == PRODUCT_INVOICE_DESIGNS) { + return file_get_contents(storage_path() . '/invoice_designs.txt'); + } else { + // temporary fix to enable previous version to work + if (Input::get('get_date')) { + return $license->created_at->format('Y-m-d'); + } else { + return 'valid'; + } + } } else { - return 'invalid'; + return RESULT_FAILURE; } } @@ -563,6 +569,7 @@ class PaymentController extends BaseController if ($account->account_key == NINJA_ACCOUNT_KEY) { Session::flash('trackEventCategory', '/account'); Session::flash('trackEventAction', '/buy_pro_plan'); + Session::flash('trackEventAmount', $payment->amount); } return Redirect::to('view/'.$payment->invitation->invitation_key); @@ -637,7 +644,8 @@ class PaymentController extends BaseController $payment = $this->paymentService->createPayment($invitation, $accountGateway, $token, $payerId); Session::flash('message', trans('texts.applied_payment')); } else { - Session::flash('error', Input::get('message')); + $message = Input::get('message') . ': ' . Input::get('invalid_fields'); + Session::flash('error', $message); } return Redirect::to($invitation->getLink()); } elseif (method_exists($gateway, 'completePurchase') @@ -672,9 +680,7 @@ class PaymentController extends BaseController public function store(CreatePaymentRequest $request) { $input = $request->input(); - - $this->authorizeUpdate($data); - + $input['invoice_id'] = Invoice::getPrivateId($input['invoice']); $input['client_id'] = Client::getPrivateId($input['client']); $payment = $this->paymentRepo->save($input); @@ -691,11 +697,7 @@ class PaymentController extends BaseController public function update(UpdatePaymentRequest $request) { - $input = $request->input(); - - $this->authorizeUpdate($data); - - $payment = $this->paymentRepo->save($input); + $payment = $this->paymentRepo->save($request->input(), $request->entity()); Session::flash('message', trans('texts.updated_payment')); diff --git a/app/Http/Controllers/ProductApiController.php b/app/Http/Controllers/ProductApiController.php index 87bf89403b8a..6a8756eda4b6 100644 --- a/app/Http/Controllers/ProductApiController.php +++ b/app/Http/Controllers/ProductApiController.php @@ -1,103 +1,54 @@ productService = $productService; $this->productRepo = $productRepo; } public function index() { + $products = Product::scope() + ->withTrashed() + ->orderBy('created_at', 'desc'); - $products = Product::scope()->withTrashed(); - $products = $products->paginate(); - - $paginator = Product::scope()->withTrashed()->paginate(); - - $transformer = new ProductTransformer(\Auth::user()->account, $this->serializer); - $data = $this->createCollection($products, $transformer, 'products', $paginator); - - return $this->response($data); - + return $this->listResponse($products); } - public function getDatatable() + public function store(CreateProductRequest $request) { - return $this->productService->getDatatable(Auth::user()->account_id); + $product = $this->productRepo->save($request->input()); + + return $this->itemResponse($product); } - public function store() + public function update(UpdateProductRequest $request, $publicId) { - return $this->save(); - } - - public function update(\Illuminate\Http\Request $request, $publicId) - { - - if ($request->action == ACTION_ARCHIVE) { - $product = Product::scope($publicId)->withTrashed()->firstOrFail(); - $this->productRepo->archive($product); - - $transformer = new ProductTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($product, $transformer, 'products'); - - return $this->response($data); + if ($request->action) { + return $this->handleAction($request); } - else - return $this->save($publicId); + + $data = $request->input(); + $data['public_id'] = $publicId; + $product = $this->productRepo->save($data, $request->entity()); + + return $this->itemResponse($product); } public function destroy($publicId) { //stub } - - private function save($productPublicId = false) - { - if ($productPublicId) { - $product = Product::scope($productPublicId)->firstOrFail(); - } else { - $product = Product::createNew(); - } - - $product->product_key = trim(Input::get('product_key')); - $product->notes = trim(Input::get('notes')); - $product->cost = trim(Input::get('cost')); - //$product->default_tax_rate_id = Input::get('default_tax_rate_id'); - - $product->save(); - - $transformer = new ProductTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($product, $transformer, 'products'); - - return $this->response($data); - - } - - } diff --git a/app/Http/Controllers/QuoteApiController.php b/app/Http/Controllers/QuoteApiController.php deleted file mode 100644 index 8111872b10d9..000000000000 --- a/app/Http/Controllers/QuoteApiController.php +++ /dev/null @@ -1,75 +0,0 @@ -invoiceRepo = $invoiceRepo; - } - - /** - * @SWG\Get( - * path="/quotes", - * tags={"quote"}, - * summary="List of quotes", - * @SWG\Response( - * response=200, - * description="A list with quotes", - * @SWG\Schema(type="array", @SWG\Items(ref="#/definitions/Invoice")) - * ), - * @SWG\Response( - * response="default", - * description="an ""unexpected"" error" - * ) - * ) - */ - public function index() - { - $paginator = Invoice::scope(); - $invoices = Invoice::scope() - ->with('client', 'invitations', 'user', 'invoice_items') - ->where('invoices.is_quote', '=', true); - - if ($clientPublicId = Input::get('client_id')) { - $filter = function($query) use ($clientPublicId) { - $query->where('public_id', '=', $clientPublicId); - }; - $invoices->whereHas('client', $filter); - $paginator->whereHas('client', $filter); - } - - $invoices = $invoices->orderBy('created_at', 'desc')->paginate(); - - $transformer = new InvoiceTransformer(\Auth::user()->account, Input::get('serializer')); - $paginator = $paginator->paginate(); - - $data = $this->createCollection($invoices, $transformer, 'quotes', $paginator); - - return $this->response($data); - } - - /* - public function store() - { - $data = Input::all(); - $invoice = $this->invoiceRepo->save(false, $data, false); - - $response = json_encode($invoice, JSON_PRETTY_PRINT); - $headers = Utils::getApiHeaders(); - return Response::make($response, 200, $headers); - } - */ -} diff --git a/app/Http/Controllers/QuoteController.php b/app/Http/Controllers/QuoteController.php index e2228ab2a0c0..a8ea0beaa476 100644 --- a/app/Http/Controllers/QuoteController.php +++ b/app/Http/Controllers/QuoteController.php @@ -26,6 +26,7 @@ use App\Ninja\Repositories\InvoiceRepository; use App\Ninja\Repositories\ClientRepository; use App\Events\QuoteInvitationWasApproved; use App\Services\InvoiceService; +use App\Http\Requests\InvoiceRequest; class QuoteController extends BaseController { @@ -33,7 +34,7 @@ class QuoteController extends BaseController protected $invoiceRepo; protected $clientRepo; protected $invoiceService; - protected $entity = ENTITY_INVOICE; + protected $entityType = ENTITY_INVOICE; public function __construct(Mailer $mailer, InvoiceRepository $invoiceRepo, ClientRepository $clientRepo, InvoiceService $invoiceService) { @@ -78,10 +79,8 @@ class QuoteController extends BaseController return $this->invoiceService->getDatatable($accountId, $clientPublicId, ENTITY_QUOTE, $search); } - public function create($clientPublicId = 0) + public function create(InvoiceRequest $request, $clientPublicId = 0) { - $this->authorizeCreate(); - if (!Utils::hasFeature(FEATURE_QUOTES)) { return Redirect::to('/invoices/create'); } diff --git a/app/Http/Controllers/TaskApiController.php b/app/Http/Controllers/TaskApiController.php index 926ed2f98ad3..7945008bcc40 100644 --- a/app/Http/Controllers/TaskApiController.php +++ b/app/Http/Controllers/TaskApiController.php @@ -13,6 +13,8 @@ class TaskApiController extends BaseAPIController { protected $taskRepo; + protected $entityType = ENTITY_TASK; + public function __construct(TaskRepository $taskRepo) { parent::__construct(); @@ -38,25 +40,11 @@ class TaskApiController extends BaseAPIController */ public function index() { - $paginator = Task::scope(); - $tasks = Task::scope() - ->with($this->getIncluded()); + $payments = Task::scope() + ->withTrashed() + ->orderBy('created_at', 'desc'); - if ($clientPublicId = Input::get('client_id')) { - $filter = function($query) use ($clientPublicId) { - $query->where('public_id', '=', $clientPublicId); - }; - $tasks->whereHas('client', $filter); - $paginator->whereHas('client', $filter); - } - - $tasks = $tasks->orderBy('created_at', 'desc')->paginate(); - $paginator = $paginator->paginate(); - $transformer = new TaskTransformer(\Auth::user()->account, Input::get('serializer')); - - $data = $this->createCollection($tasks, $transformer, 'tasks', $paginator); - - return $this->response($data); + return $this->listResponse($payments); } /** diff --git a/app/Http/Controllers/TaskController.php b/app/Http/Controllers/TaskController.php index 7ff19436ead2..229a4751116e 100644 --- a/app/Http/Controllers/TaskController.php +++ b/app/Http/Controllers/TaskController.php @@ -18,11 +18,15 @@ use App\Ninja\Repositories\TaskRepository; use App\Ninja\Repositories\InvoiceRepository; use App\Services\TaskService; +use App\Http\Requests\TaskRequest; +use App\Http\Requests\CreateTaskRequest; +use App\Http\Requests\UpdateTaskRequest; + class TaskController extends BaseController { protected $taskRepo; protected $taskService; - protected $entity = ENTITY_TASK; + protected $entityType = ENTITY_TASK; public function __construct(TaskRepository $taskRepo, InvoiceRepository $invoiceRepo, TaskService $taskService) { @@ -66,7 +70,7 @@ class TaskController extends BaseController * * @return Response */ - public function store() + public function store(CreateTaskRequest $request) { return $this->save(); } @@ -83,14 +87,13 @@ class TaskController extends BaseController * * @return Response */ - public function create($clientPublicId = 0) + public function create(TaskRequest $request) { - $this->authorizeCreate(); $this->checkTimezone(); $data = [ 'task' => null, - 'clientPublicId' => Input::old('client') ? Input::old('client') : $clientPublicId, + 'clientPublicId' => Input::old('client') ? Input::old('client') : ($request->client_id ?: 0), 'method' => 'POST', 'url' => 'tasks', 'title' => trans('texts.new_task'), @@ -109,13 +112,11 @@ class TaskController extends BaseController * @param int $id * @return Response */ - public function edit($publicId) + public function edit(TaskRequest $request) { $this->checkTimezone(); - $task = Task::scope($publicId)->with('client', 'invoice')->withTrashed()->firstOrFail(); - - $this->authorize('edit', $task); + $task = $request->entity(); $actions = []; if ($task->invoice) { @@ -143,7 +144,7 @@ class TaskController extends BaseController 'task' => $task, 'clientPublicId' => $task->client ? $task->client->public_id : 0, 'method' => 'PUT', - 'url' => 'tasks/'.$publicId, + 'url' => 'tasks/'.$task->public_id, 'title' => trans('texts.edit_task'), 'duration' => $task->is_running ? $task->getCurrentDuration() : $task->getDuration(), 'actions' => $actions, @@ -163,9 +164,11 @@ class TaskController extends BaseController * @param int $id * @return Response */ - public function update($publicId) + public function update(UpdateTaskRequest $request) { - return $this->save($publicId); + $task = $request->entity(); + + return $this->save($task->public_id); } private static function getViewModel() @@ -180,20 +183,10 @@ class TaskController extends BaseController { $action = Input::get('action'); - $this->authorizeUpdate(array('public_id'=>$publicId)/* Hacky, but works */); - if (in_array($action, ['archive', 'delete', 'restore'])) { return self::bulk(); } - if ($validator = $this->taskRepo->getErrors(Input::all())) { - $url = $publicId ? 'tasks/'.$publicId.'/edit' : 'tasks/create'; - Session::flash('error', trans('texts.task_errors')); - return Redirect::to($url) - ->withErrors($validator) - ->withInput(); - } - $task = $this->taskRepo->save($publicId, Input::all()); Session::flash('message', trans($publicId ? 'texts.updated_task' : 'texts.created_task')); diff --git a/app/Http/Controllers/TaxRateApiController.php b/app/Http/Controllers/TaxRateApiController.php index 7cacfcf8311b..85756205d9a1 100644 --- a/app/Http/Controllers/TaxRateApiController.php +++ b/app/Http/Controllers/TaxRateApiController.php @@ -1,68 +1,54 @@ taxRateService = $taxRateService; $this->taxRateRepo = $taxRateRepo; } public function index() { - $taxRates = TaxRate::scope()->withTrashed(); - $taxRates = $taxRates->paginate(); + $taxRates = TaxRate::scope() + ->withTrashed() + ->orderBy('created_at', 'desc'); - $paginator = TaxRate::scope()->withTrashed()->paginate(); - - $transformer = new TaxRateTransformer(Auth::user()->account, $this->serializer); - $data = $this->createCollection($taxRates, $transformer, 'tax_rates', $paginator); - - return $this->response($data); + return $this->listResponse($taxRates); } public function store(CreateTaxRateRequest $request) { - return $this->save($request); + $taxRate = $this->taxRateRepo->save($request->input()); + + return $this->itemResponse($taxRate); } - public function update(UpdateTaxRateRequest $request, $taxRatePublicId) + public function update(UpdateTaxRateRequest $request, $publicId) { - $taxRate = TaxRate::scope($taxRatePublicId)->firstOrFail(); - - if ($request->action == ACTION_ARCHIVE) { - $this->taxRateRepo->archive($taxRate); - - $transformer = new TaxRateTransformer(Auth::user()->account, $request->serializer); - $data = $this->createItem($taxRate, $transformer, 'tax_rates'); - - return $this->response($data); - } else { - return $this->save($request, $taxRate); + if ($request->action) { + return $this->handleAction($request); } + + $data = $request->input(); + $data['public_id'] = $publicId; + $taxRate = $this->taxRateRepo->save($data, $request->entity()); + + return $this->itemResponse($taxRate); } - private function save($request, $taxRate = false) + public function destroy($publicId) { - $taxRate = $this->taxRateRepo->save($request->input(), $taxRate); - - $transformer = new TaxRateTransformer(\Auth::user()->account, $request->serializer); - $data = $this->createItem($taxRate, $transformer, 'tax_rates'); - - return $this->response($data); + //stub } } diff --git a/app/Http/Controllers/TaxRateController.php b/app/Http/Controllers/TaxRateController.php index 223f7491092e..cba4058756de 100644 --- a/app/Http/Controllers/TaxRateController.php +++ b/app/Http/Controllers/TaxRateController.php @@ -75,9 +75,7 @@ class TaxRateController extends BaseController public function update(UpdateTaxRateRequest $request, $publicId) { - $taxRate = TaxRate::scope($publicId)->firstOrFail(); - - $this->taxRateRepo->save($request->input(), $taxRate); + $this->taxRateRepo->save($request->input(), $request->entity()); Session::flash('message', trans('texts.updated_tax_rate')); return Redirect::to('settings/' . ACCOUNT_TAX_RATES); diff --git a/app/Http/Controllers/TokenController.php b/app/Http/Controllers/TokenController.php index aa5434a2694b..d5ad3b41b739 100644 --- a/app/Http/Controllers/TokenController.php +++ b/app/Http/Controllers/TokenController.php @@ -32,7 +32,7 @@ class TokenController extends BaseController public function getDatatable() { - return $this->tokenService->getDatatable(Auth::user()->account_id); + return $this->tokenService->getDatatable(Auth::user()->id); } public function edit($publicId) diff --git a/app/Http/Controllers/UserApiController.php b/app/Http/Controllers/UserApiController.php index fcd48787b134..2869c3512f5a 100644 --- a/app/Http/Controllers/UserApiController.php +++ b/app/Http/Controllers/UserApiController.php @@ -14,6 +14,8 @@ class UserApiController extends BaseAPIController protected $userService; protected $userRepo; + protected $entityType = ENTITY_USER; + public function __construct(UserService $userService, UserRepository $userRepo) { parent::__construct(); @@ -24,16 +26,11 @@ class UserApiController extends BaseAPIController public function index() { - $user = Auth::user(); - $users = User::whereAccountId($user->account_id)->withTrashed(); - $users = $users->paginate(); - - $paginator = User::whereAccountId($user->account_id)->withTrashed()->paginate(); - - $transformer = new UserTransformer(Auth::user()->account, $this->serializer); - $data = $this->createCollection($users, $transformer, 'users', $paginator); - - return $this->response($data); + $users = User::whereAccountId(Auth::user()->account_id) + ->withTrashed() + ->orderBy('created_at', 'desc'); + + return $this->listResponse($users); } /* @@ -45,11 +42,6 @@ class UserApiController extends BaseAPIController public function update(UpdateUserRequest $request, $userPublicId) { - /* - // temporary fix for ids starting at 0 - $userPublicId -= 1; - $user = User::scope($userPublicId)->firstOrFail(); - */ $user = Auth::user(); if ($request->action == ACTION_ARCHIVE) { diff --git a/app/Http/Controllers/VendorApiController.php b/app/Http/Controllers/VendorApiController.php index 4c32ee1eb3c5..e15207934cd8 100644 --- a/app/Http/Controllers/VendorApiController.php +++ b/app/Http/Controllers/VendorApiController.php @@ -14,6 +14,8 @@ class VendorApiController extends BaseAPIController { protected $vendorRepo; + protected $entityType = ENTITY_VENDOR; + public function __construct(VendorRepository $vendorRepo) { parent::__construct(); @@ -46,17 +48,11 @@ class VendorApiController extends BaseAPIController */ public function index() { - $vendors = Vendor::scope() - ->with($this->getIncluded()) + $vendors = Vendor::scope() ->withTrashed() - ->orderBy('created_at', 'desc') - ->paginate(); + ->orderBy('created_at', 'desc'); - $transformer = new VendorTransformer(Auth::user()->account, Input::get('serializer')); - $paginator = Vendor::scope()->paginate(); - $data = $this->createCollection($vendors, $transformer, ENTITY_VENDOR, $paginator); - - return $this->response($data); + return $this->listResponse($vendors); } /** @@ -85,11 +81,9 @@ class VendorApiController extends BaseAPIController $vendor = $this->vendorRepo->save($request->input()); $vendor = Vendor::scope($vendor->public_id) - ->with('country', 'vendorcontacts', 'industry', 'size', 'currency') + ->with('country', 'vendor_contacts', 'industry', 'size', 'currency') ->first(); - $transformer = new VendorTransformer(Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($vendor, $transformer, ENTITY_VENDOR); - return $this->response($data); + return $this->itemResponse($vendor); } } diff --git a/app/Http/Controllers/VendorController.php b/app/Http/Controllers/VendorController.php index 00f5bbe83e4d..f1952e20dee3 100644 --- a/app/Http/Controllers/VendorController.php +++ b/app/Http/Controllers/VendorController.php @@ -23,14 +23,15 @@ use App\Models\Country; use App\Ninja\Repositories\VendorRepository; use App\Services\VendorService; +use App\Http\Requests\VendorRequest; use App\Http\Requests\CreateVendorRequest; use App\Http\Requests\UpdateVendorRequest; -// vendor + class VendorController extends BaseController { protected $vendorService; protected $vendorRepo; - protected $entity = ENTITY_VENDOR; + protected $entityType = ENTITY_VENDOR; public function __construct(VendorRepository $vendorRepo, VendorService $vendorService) { @@ -38,8 +39,6 @@ class VendorController extends BaseController $this->vendorRepo = $vendorRepo; $this->vendorService = $vendorService; - - } /** @@ -77,11 +76,7 @@ class VendorController extends BaseController */ public function store(CreateVendorRequest $request) { - $data = $request->input(); - - $this->authorizeUpdate($data); - - $vendor = $this->vendorService->save($data); + $vendor = $this->vendorService->save($request->input()); Session::flash('message', trans('texts.created_vendor')); @@ -94,12 +89,10 @@ class VendorController extends BaseController * @param int $id * @return Response */ - public function show($publicId) + public function show(VendorRequest $request) { - $vendor = Vendor::withTrashed()->scope($publicId)->with('vendorcontacts', 'size', 'industry')->firstOrFail(); - - $this->authorize('view', $vendor); - + $vendor = $request->entity(); + Utils::trackViewed($vendor->getDisplayName(), 'vendor'); $actionLinks = [ @@ -125,10 +118,8 @@ class VendorController extends BaseController * * @return Response */ - public function create() + public function create(VendorRequest $request) { - $this->authorizeCreate(); - if (Vendor::scope()->count() > Auth::user()->getMaxNumVendors()) { return View::make('error', ['hideHeader' => true, 'error' => "Sorry, you've exceeded the limit of ".Auth::user()->getMaxNumVendors()." vendors"]); } @@ -151,16 +142,14 @@ class VendorController extends BaseController * @param int $id * @return Response */ - public function edit($publicId) + public function edit(VendorRequest $request) { - $vendor = Vendor::scope($publicId)->with('vendorcontacts')->firstOrFail(); - - $this->authorize('edit', $vendor) + $vendor = $request->entity(); $data = [ 'vendor' => $vendor, 'method' => 'PUT', - 'url' => 'vendors/'.$publicId, + 'url' => 'vendors/'.$vendor->public_id, 'title' => trans('texts.edit_vendor'), ]; @@ -193,11 +182,7 @@ class VendorController extends BaseController */ public function update(UpdateVendorRequest $request) { - $data = $request->input(); - - $this->authorizeUpdate($data); - - $vendor = $this->vendorService->save($data); + $vendor = $this->vendorService->save($request->input(), $request->entity()); Session::flash('message', trans('texts.updated_vendor')); diff --git a/app/Http/Middleware/ApiCheck.php b/app/Http/Middleware/ApiCheck.php index 8b38c60fe2c5..b20b19841fcb 100644 --- a/app/Http/Middleware/ApiCheck.php +++ b/app/Http/Middleware/ApiCheck.php @@ -34,7 +34,8 @@ class ApiCheck { // check for a valid token $token = AccountToken::where('token', '=', Request::header('X-Ninja-Token'))->first(['id', 'user_id']); - if ($token) { + // check if user is archived + if ($token && $token->user) { Auth::loginUsingId($token->user_id); Session::set('token_id', $token->id); } else { diff --git a/app/Http/Middleware/DuplicateSubmissionCheck.php b/app/Http/Middleware/DuplicateSubmissionCheck.php index 407ffab60071..6f3374a47ebf 100644 --- a/app/Http/Middleware/DuplicateSubmissionCheck.php +++ b/app/Http/Middleware/DuplicateSubmissionCheck.php @@ -1,4 +1,4 @@ -account->company; - $company->plan_paid = date_create()->format('Y-m-d'); + $company->plan_term = PLAN_TERM_YEARLY; + $company->plan_paid = $data; + $company->plan_expires = date_create($data)->modify('+1 year')->format('Y-m-d'); $company->plan = PLAN_WHITE_LABEL; $company->save(); diff --git a/app/Http/Requests/ClientRequest.php b/app/Http/Requests/ClientRequest.php new file mode 100644 index 000000000000..ec28cdb77d1d --- /dev/null +++ b/app/Http/Requests/ClientRequest.php @@ -0,0 +1,18 @@ +relationLoaded('contacts')) { + $client->load('contacts'); + } + + return $client; + } +} \ No newline at end of file diff --git a/app/Http/Requests/CreateBankAccountRequest.php b/app/Http/Requests/CreateBankAccountRequest.php index 6c2fea62ec47..eac988349c8d 100644 --- a/app/Http/Requests/CreateBankAccountRequest.php +++ b/app/Http/Requests/CreateBankAccountRequest.php @@ -1,4 +1,4 @@ -user()->can('create', ENTITY_CLIENT); } /** diff --git a/app/Http/Requests/CreateCreditRequest.php b/app/Http/Requests/CreateCreditRequest.php index f2dc44d31aa0..b6f4fe3b37e3 100644 --- a/app/Http/Requests/CreateCreditRequest.php +++ b/app/Http/Requests/CreateCreditRequest.php @@ -1,9 +1,6 @@ -user()->can('create', ENTITY_CREDIT); } /** diff --git a/app/Http/Requests/UpdatePaymentTermRequest.php b/app/Http/Requests/CreateDocumentRequest.php similarity index 60% rename from app/Http/Requests/UpdatePaymentTermRequest.php rename to app/Http/Requests/CreateDocumentRequest.php index b3d4f536bc6e..33330a90895e 100644 --- a/app/Http/Requests/UpdatePaymentTermRequest.php +++ b/app/Http/Requests/CreateDocumentRequest.php @@ -1,9 +1,6 @@ -user()->can('create', ENTITY_DOCUMENT); } /** @@ -23,8 +20,7 @@ class UpdateExpenseRequest extends Request public function rules() { return [ - 'amount' => 'required|positive', + ]; - } } diff --git a/app/Http/Requests/CreateExpenseRequest.php b/app/Http/Requests/CreateExpenseRequest.php index 85a93eb1974d..dd096eebdcbf 100644 --- a/app/Http/Requests/CreateExpenseRequest.php +++ b/app/Http/Requests/CreateExpenseRequest.php @@ -1,9 +1,6 @@ -user()->can('create', ENTITY_EXPENSE); } /** diff --git a/app/Http/Requests/CreateInvoiceAPIRequest.php b/app/Http/Requests/CreateInvoiceAPIRequest.php new file mode 100644 index 000000000000..141d8788abc3 --- /dev/null +++ b/app/Http/Requests/CreateInvoiceAPIRequest.php @@ -0,0 +1,32 @@ +user()->can('create', ENTITY_INVOICE); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + $rules = [ + 'email' => 'required_without:client_id', + 'client_id' => 'required_without:email', + 'invoice_items' => 'valid_invoice_items', + 'invoice_number' => 'unique:invoices,invoice_number,,id,account_id,' . $this->user()->account_id, + 'discount' => 'positive', + ]; + + return $rules; + } +} diff --git a/app/Http/Requests/CreateInvoiceRequest.php b/app/Http/Requests/CreateInvoiceRequest.php index 4a11ea56044a..a3f556d408e9 100644 --- a/app/Http/Requests/CreateInvoiceRequest.php +++ b/app/Http/Requests/CreateInvoiceRequest.php @@ -1,11 +1,6 @@ -user()->can('create', ENTITY_INVOICE); } /** @@ -25,13 +20,18 @@ class CreateInvoiceRequest extends Request public function rules() { $rules = [ - 'email' => 'required_without:client_id', - 'client_id' => 'required_without:email', + 'client.contacts' => 'valid_contacts', 'invoice_items' => 'valid_invoice_items', - 'invoice_number' => 'unique:invoices,invoice_number,,id,account_id,'.Auth::user()->account_id, + 'invoice_number' => 'required|unique:invoices,invoice_number,,id,account_id,' . $this->user()->account_id, 'discount' => 'positive', ]; + /* There's a problem parsing the dates + if (Request::get('is_recurring') && Request::get('start_date') && Request::get('end_date')) { + $rules['end_date'] = 'after' . Request::get('start_date'); + } + */ + return $rules; } } diff --git a/app/Http/Requests/CreatePaymentAPIRequest.php b/app/Http/Requests/CreatePaymentAPIRequest.php new file mode 100644 index 000000000000..08a520c16655 --- /dev/null +++ b/app/Http/Requests/CreatePaymentAPIRequest.php @@ -0,0 +1,48 @@ +user()->can('create', ENTITY_PAYMENT); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + if ( ! $this->invoice_id || ! $this->amount) { + return [ + 'invoice_id' => 'required', + 'amount' => 'required', + ]; + } + + $invoice = Invoice::scope($this->invoice_id)->firstOrFail(); + + $this->merge([ + 'invoice_id' => $invoice->id, + 'client_id' => $invoice->client->id, + ]); + + $rules = array( + 'amount' => "required|less_than:{$invoice->balance}|positive", + ); + + if ($this->payment_type_id == PAYMENT_TYPE_CREDIT) { + $rules['payment_type_id'] = 'has_credit:' . $invoice->client->public_id . ',' . $this->amount; + } + + return $rules; + } +} diff --git a/app/Http/Requests/CreatePaymentRequest.php b/app/Http/Requests/CreatePaymentRequest.php index 52e9d313bf9f..d14d1ddba616 100644 --- a/app/Http/Requests/CreatePaymentRequest.php +++ b/app/Http/Requests/CreatePaymentRequest.php @@ -1,10 +1,8 @@ -user()->can('create', ENTITY_PAYMENT); } /** diff --git a/app/Http/Requests/CreatePaymentTermRequest.php b/app/Http/Requests/CreateProductRequest.php similarity index 57% rename from app/Http/Requests/CreatePaymentTermRequest.php rename to app/Http/Requests/CreateProductRequest.php index d8581793160e..0fad2af14a23 100644 --- a/app/Http/Requests/CreatePaymentTermRequest.php +++ b/app/Http/Requests/CreateProductRequest.php @@ -1,9 +1,6 @@ -user()->can('create', ENTITY_PRODUCT); } /** @@ -23,8 +20,7 @@ class CreatePaymentTermRequest extends Request public function rules() { return [ - 'num_days' => 'required', - 'name' => 'required', + 'product_key' => 'required', ]; } } diff --git a/app/Http/Requests/CreateTaskRequest.php b/app/Http/Requests/CreateTaskRequest.php new file mode 100644 index 000000000000..00d75c2f1249 --- /dev/null +++ b/app/Http/Requests/CreateTaskRequest.php @@ -0,0 +1,26 @@ +user()->can('create', ENTITY_TASK); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + return [ + 'time_log' => 'time_log', + ]; + } +} diff --git a/app/Http/Requests/CreateTaxRateRequest.php b/app/Http/Requests/CreateTaxRateRequest.php index bcf1ad1115b7..d8fef50093b7 100644 --- a/app/Http/Requests/CreateTaxRateRequest.php +++ b/app/Http/Requests/CreateTaxRateRequest.php @@ -1,9 +1,9 @@ -user()->can('create', ENTITY_TAX_RATE); } /** diff --git a/app/Http/Requests/CreateVendorRequest.php b/app/Http/Requests/CreateVendorRequest.php index 9b2accb36524..59bcf668fa68 100644 --- a/app/Http/Requests/CreateVendorRequest.php +++ b/app/Http/Requests/CreateVendorRequest.php @@ -1,9 +1,6 @@ -user()->can('create', ENTITY_VENDOR); } /** diff --git a/app/Http/Requests/CreditRequest.php b/app/Http/Requests/CreditRequest.php new file mode 100644 index 000000000000..7968005555f4 --- /dev/null +++ b/app/Http/Requests/CreditRequest.php @@ -0,0 +1,7 @@ +entity) { + return $this->entity; + } + + // The entity id can appear as invoices, invoice_id, public_id or id + $publicId = false; + foreach (['_id', 's'] as $suffix) { + $field = $this->entityType . $suffix; + if ($this->$field) { + $publicId= $this->$field; + } + } + if ( ! $publicId) { + $publicId = Input::get('public_id') ?: Input::get('id'); + } + if ( ! $publicId) { + return null; + } + + $class = Utils::getEntityClass($this->entityType); + + if (method_exists($class, 'withTrashed')) { + $this->entity = $class::scope($publicId)->withTrashed()->firstOrFail(); + } else { + $this->entity = $class::scope($publicId)->firstOrFail(); + } + + return $this->entity; + } + + public function authorize() + { + if ($this->entity()) { + return $this->user()->can('view', $this->entity()); + } else { + return $this->user()->can('create', $this->entityType); + } + } + + public function rules() + { + return []; + } +} diff --git a/app/Http/Requests/ExpenseRequest.php b/app/Http/Requests/ExpenseRequest.php new file mode 100644 index 000000000000..ae2e83b6d12f --- /dev/null +++ b/app/Http/Requests/ExpenseRequest.php @@ -0,0 +1,18 @@ +relationLoaded('documents')) { + $expense->load('documents'); + } + + return $expense; + } +} \ No newline at end of file diff --git a/app/Http/Requests/InvoiceRequest.php b/app/Http/Requests/InvoiceRequest.php new file mode 100644 index 000000000000..5e2d93139003 --- /dev/null +++ b/app/Http/Requests/InvoiceRequest.php @@ -0,0 +1,19 @@ +relationLoaded('invoice_items')) { + $invoice->load('invoice_items'); + } + + return $invoice; + } + +} \ No newline at end of file diff --git a/app/Http/Requests/PaymentRequest.php b/app/Http/Requests/PaymentRequest.php new file mode 100644 index 000000000000..cb34349f5d20 --- /dev/null +++ b/app/Http/Requests/PaymentRequest.php @@ -0,0 +1,7 @@ + 'valid_contacts', - 'invoice_items' => 'valid_invoice_items', - 'invoice_number' => 'required|unique:invoices,invoice_number,'.$invoiceId.',id,account_id,'.Auth::user()->account_id, - 'discount' => 'positive', - ]; - - /* There's a problem parsing the dates - if (Request::get('is_recurring') && Request::get('start_date') && Request::get('end_date')) { - $rules['end_date'] = 'after' . Request::get('start_date'); - } - */ - - return $rules; - } -} diff --git a/app/Http/Requests/TaskRequest.php b/app/Http/Requests/TaskRequest.php new file mode 100644 index 000000000000..1e2783781f06 --- /dev/null +++ b/app/Http/Requests/TaskRequest.php @@ -0,0 +1,7 @@ +user()->can('edit', $this->entity()); } /** diff --git a/app/Http/Requests/UpdateExpenseRequest.php b/app/Http/Requests/UpdateExpenseRequest.php index a4d3855deaec..e64384b57a54 100644 --- a/app/Http/Requests/UpdateExpenseRequest.php +++ b/app/Http/Requests/UpdateExpenseRequest.php @@ -1,10 +1,6 @@ -user()->can('edit', $this->entity()); } /** diff --git a/app/Http/Requests/UpdateInvoiceAPIRequest.php b/app/Http/Requests/UpdateInvoiceAPIRequest.php new file mode 100644 index 000000000000..6fa3c4e92e45 --- /dev/null +++ b/app/Http/Requests/UpdateInvoiceAPIRequest.php @@ -0,0 +1,36 @@ +user()->can('edit', $this->entity()); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + if ($this->action == ACTION_ARCHIVE) { + return []; + } + + $invoiceId = $this->entity()->id; + + $rules = [ + 'invoice_items' => 'valid_invoice_items', + 'invoice_number' => 'unique:invoices,invoice_number,' . $invoiceId . ',id,account_id,' . $this->user()->account_id, + 'discount' => 'positive', + ]; + + return $rules; + } +} diff --git a/app/Http/Requests/UpdateInvoiceRequest.php b/app/Http/Requests/UpdateInvoiceRequest.php index 4b32bf4ccead..96d112b157c2 100644 --- a/app/Http/Requests/UpdateInvoiceRequest.php +++ b/app/Http/Requests/UpdateInvoiceRequest.php @@ -1,11 +1,6 @@ -user()->can('edit', $this->entity()); } /** @@ -24,19 +19,21 @@ class UpdateInvoiceRequest extends Request */ public function rules() { - if ($this->action == ACTION_ARCHIVE) { - return []; - } - - $publicId = $this->route('invoices'); - $invoiceId = Invoice::getPrivateId($publicId); - + $invoiceId = $this->entity()->id; + $rules = [ + 'client.contacts' => 'valid_contacts', 'invoice_items' => 'valid_invoice_items', - 'invoice_number' => 'unique:invoices,invoice_number,'.$invoiceId.',id,account_id,'.Auth::user()->account_id, + 'invoice_number' => 'required|unique:invoices,invoice_number,' . $invoiceId . ',id,account_id,' . $this->user()->account_id, 'discount' => 'positive', ]; + /* There's a problem parsing the dates + if (Request::get('is_recurring') && Request::get('start_date') && Request::get('end_date')) { + $rules['end_date'] = 'after' . Request::get('start_date'); + } + */ + return $rules; } } diff --git a/app/Http/Requests/UpdatePaymentRequest.php b/app/Http/Requests/UpdatePaymentRequest.php index 29ac70e85e74..70a328d26772 100644 --- a/app/Http/Requests/UpdatePaymentRequest.php +++ b/app/Http/Requests/UpdatePaymentRequest.php @@ -1,9 +1,6 @@ -user()->can('edit', $this->entity()); } /** diff --git a/app/Http/Requests/UpdateProductRequest.php b/app/Http/Requests/UpdateProductRequest.php new file mode 100644 index 000000000000..f45495a26922 --- /dev/null +++ b/app/Http/Requests/UpdateProductRequest.php @@ -0,0 +1,26 @@ +user()->can('edit', $this->entity()); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + return [ + 'product_key' => 'required', + ]; + } +} diff --git a/app/Http/Requests/UpdateTaskRequest.php b/app/Http/Requests/UpdateTaskRequest.php new file mode 100644 index 000000000000..83ce23ab31ab --- /dev/null +++ b/app/Http/Requests/UpdateTaskRequest.php @@ -0,0 +1,26 @@ +user()->can('edit', $this->entity()); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + return [ + 'time_log' => 'time_log', + ]; + } +} diff --git a/app/Http/Requests/UpdateTaxRateRequest.php b/app/Http/Requests/UpdateTaxRateRequest.php index 6e562ac0d0ab..a4bdc6301ca2 100644 --- a/app/Http/Requests/UpdateTaxRateRequest.php +++ b/app/Http/Requests/UpdateTaxRateRequest.php @@ -1,9 +1,9 @@ -user()->can('edit', $this->entity()); } /** diff --git a/app/Http/Requests/UpdateUserRequest.php b/app/Http/Requests/UpdateUserRequest.php index 1bbcc3d7eaea..b3149d2b61ff 100644 --- a/app/Http/Requests/UpdateUserRequest.php +++ b/app/Http/Requests/UpdateUserRequest.php @@ -1,4 +1,4 @@ -user()->can('edit', $this->entity()); } /** diff --git a/app/Http/Requests/UpdateVendorRequest.php b/app/Http/Requests/UpdateVendorRequest.php index 6e17b79bf12b..9e7e2fe513fb 100644 --- a/app/Http/Requests/UpdateVendorRequest.php +++ b/app/Http/Requests/UpdateVendorRequest.php @@ -1,9 +1,6 @@ -user()->can('edit', $this->entity()); } /** diff --git a/app/Http/Requests/VendorRequest.php b/app/Http/Requests/VendorRequest.php new file mode 100644 index 000000000000..8f96e7c55025 --- /dev/null +++ b/app/Http/Requests/VendorRequest.php @@ -0,0 +1,19 @@ +relationLoaded('vendor_contacts')) { + $vendor->load('vendor_contacts'); + } + + return $vendor; + } + +} \ No newline at end of file diff --git a/app/Http/routes.php b/app/Http/routes.php index a3bf59d22289..241049810960 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -56,8 +56,8 @@ Route::group(['middleware' => 'auth:client'], function() { Route::get('client/documents', 'PublicClientController@documentIndex'); Route::get('client/payments', 'PublicClientController@paymentIndex'); Route::get('client/dashboard', 'PublicClientController@dashboard'); - Route::get('client/document/js/{public_id}/{filename}', 'PublicClientController@getDocumentVFSJS'); - Route::get('client/document/{invitation_key}/{public_id}/{filename?}', 'PublicClientController@getDocument'); + Route::get('client/documents/js/{documents}/{filename}', 'PublicClientController@getDocumentVFSJS'); + Route::get('client/documents/{invitation_key}/{documents}/{filename?}', 'PublicClientController@getDocument'); Route::get('client/documents/{invitation_key}/{filename?}', 'PublicClientController@getInvoiceDocumentsZip'); Route::get('api/client.quotes', array('as'=>'api.client.quotes', 'uses'=>'PublicClientController@quoteDatatable')); @@ -121,9 +121,11 @@ Route::group(['middleware' => 'auth:user'], function() { Route::get('view_archive/{entity_type}/{visible}', 'AccountController@setTrashVisible'); Route::get('hide_message', 'HomeController@hideMessage'); Route::get('force_inline_pdf', 'UserController@forcePDFJS'); + Route::get('account/getSearchData', array('as' => 'getSearchData', 'uses' => 'AccountController@getSearchData')); Route::get('settings/user_details', 'AccountController@showUserDetails'); Route::post('settings/user_details', 'AccountController@saveUserDetails'); + Route::post('users/change_password', 'UserController@changePassword'); Route::resource('clients', 'ClientController'); Route::get('api/clients', array('as'=>'api.clients', 'uses'=>'ClientController@getDatatable')); @@ -145,20 +147,20 @@ Route::group(['middleware' => 'auth:user'], function() { Route::get('invoices/create/{client_id?}', 'InvoiceController@create'); Route::get('recurring_invoices/create/{client_id?}', 'InvoiceController@createRecurring'); Route::get('recurring_invoices', 'RecurringInvoiceController@index'); - Route::get('invoices/{public_id}/clone', 'InvoiceController@cloneInvoice'); + Route::get('invoices/{invoices}/clone', 'InvoiceController@cloneInvoice'); Route::post('invoices/bulk', 'InvoiceController@bulk'); Route::post('recurring_invoices/bulk', 'InvoiceController@bulk'); - Route::get('document/{public_id}/{filename?}', 'DocumentController@get'); - Route::get('document/js/{public_id}/{filename}', 'DocumentController@getVFSJS'); - Route::get('document/preview/{public_id}/{filename?}', 'DocumentController@getPreview'); + Route::get('documents/{documents}/{filename?}', 'DocumentController@get'); + Route::get('documents/js/{documents}/{filename}', 'DocumentController@getVFSJS'); + Route::get('documents/preview/{documents}/{filename?}', 'DocumentController@getPreview'); Route::post('document', 'DocumentController@postUpload'); Route::get('quotes/create/{client_id?}', 'QuoteController@create'); - Route::get('quotes/{public_id}/clone', 'InvoiceController@cloneInvoice'); - Route::get('quotes/{public_id}/edit', 'InvoiceController@edit'); - Route::put('quotes/{public_id}', 'InvoiceController@update'); - Route::get('quotes/{public_id}', 'InvoiceController@edit'); + Route::get('quotes/{invoices}/clone', 'InvoiceController@cloneInvoice'); + Route::get('quotes/{invoices}/edit', 'InvoiceController@edit'); + Route::put('quotes/{invoices}', 'InvoiceController@update'); + Route::get('quotes/{invoices}', 'InvoiceController@edit'); Route::post('quotes', 'InvoiceController@store'); Route::get('quotes', 'QuoteController@index'); Route::get('api/quotes/{client_id?}', array('as'=>'api.quotes', 'uses'=>'QuoteController@getDatatable')); @@ -202,7 +204,6 @@ Route::group([ Route::get('start_trial/{plan}', 'AccountController@startTrial') ->where(['plan'=>'pro']); Route::get('restore_user/{user_id}', 'UserController@restoreUser'); - Route::post('users/change_password', 'UserController@changePassword'); Route::get('/switch_account/{user_id}', 'UserController@switchAccount'); Route::get('/unlink_account/{user_account_id}/{user_id}', 'UserController@unlinkAccount'); Route::get('/manage_companies', 'UserController@manageCompanies'); @@ -219,6 +220,7 @@ Route::group([ Route::resource('tax_rates', 'TaxRateController'); Route::post('tax_rates/bulk', 'TaxRateController@bulk'); + Route::get('settings/email_preview', 'AccountController@previewEmail'); Route::get('company/{section}/{subSection?}', 'AccountController@redirectLegacy'); Route::get('settings/data_visualizations', 'ReportController@d3'); Route::get('settings/charts_and_reports', 'ReportController@showReports'); @@ -230,11 +232,6 @@ Route::group([ Route::get('settings/{section?}', 'AccountController@showSection'); Route::post('settings/{section?}', 'AccountController@doSection'); - //Route::get('api/payment_terms', array('as'=>'api.payment_terms', 'uses'=>'PaymentTermController@getDatatable')); - //Route::resource('payment_terms', 'PaymentTermController'); - //Route::post('payment_terms/bulk', 'PaymentTermController@bulk'); - - Route::get('account/getSearchData', array('as' => 'getSearchData', 'uses' => 'AccountController@getSearchData')); Route::post('user/setTheme', 'UserController@setTheme'); Route::post('remove_logo', 'AccountController@removeLogo'); Route::post('account/go_pro', 'AccountController@enableProPlan'); @@ -257,15 +254,15 @@ Route::group([ // Route groups for API Route::group(['middleware' => 'api', 'prefix' => 'api/v1'], function() { - Route::get('ping', 'ClientApiController@ping'); + Route::get('ping', 'AccountApiController@ping'); Route::post('login', 'AccountApiController@login'); Route::post('register', 'AccountApiController@register'); Route::get('static', 'AccountApiController@getStaticData'); Route::get('accounts', 'AccountApiController@show'); Route::put('accounts', 'AccountApiController@update'); Route::resource('clients', 'ClientApiController'); - Route::get('quotes', 'QuoteApiController@index'); - Route::resource('quotes', 'QuoteApiController'); + //Route::get('quotes', 'QuoteApiController@index'); + //Route::resource('quotes', 'QuoteApiController'); Route::get('invoices', 'InvoiceApiController@index'); Route::resource('invoices', 'InvoiceApiController'); Route::get('payments', 'PaymentApiController@index'); @@ -573,24 +570,26 @@ if (!defined('CONTACT_EMAIL')) { define('NINJA_ACCOUNT_KEY', 'zg4ylmzDkdkPOT8yoKQw9LTWaoZJx79h'); define('NINJA_GATEWAY_ID', GATEWAY_STRIPE); define('NINJA_GATEWAY_CONFIG', 'NINJA_GATEWAY_CONFIG'); - define('NINJA_WEB_URL', 'https://www.invoiceninja.com'); - define('NINJA_APP_URL', 'https://app.invoiceninja.com'); - define('NINJA_VERSION', '2.5.1.3'); + define('NINJA_WEB_URL', env('NINJA_WEB_URL', 'https://www.invoiceninja.com')); + define('NINJA_APP_URL', env('NINJA_APP_URL', 'https://app.invoiceninja.com')); + define('NINJA_DATE', '2000-01-01'); + define('NINJA_VERSION', '2.5.1.3' . env('NINJA_VERSION_SUFFIX')); - define('SOCIAL_LINK_FACEBOOK', 'https://www.facebook.com/invoiceninja'); - define('SOCIAL_LINK_TWITTER', 'https://twitter.com/invoiceninja'); - define('SOCIAL_LINK_GITHUB', 'https://github.com/invoiceninja/invoiceninja/'); + define('SOCIAL_LINK_FACEBOOK', env('SOCIAL_LINK_FACEBOOK', 'https://www.facebook.com/invoiceninja')); + define('SOCIAL_LINK_TWITTER', env('SOCIAL_LINK_TWITTER', 'https://twitter.com/invoiceninja')); + define('SOCIAL_LINK_GITHUB', env('SOCIAL_LINK_GITHUB', 'https://github.com/invoiceninja/invoiceninja/')); - define('NINJA_FROM_EMAIL', 'maildelivery@invoiceninja.com'); - define('RELEASES_URL', 'https://trello.com/b/63BbiVVe/invoice-ninja'); - define('ZAPIER_URL', 'https://zapier.com/zapbook/invoice-ninja'); - define('OUTDATE_BROWSER_URL', 'http://browsehappy.com/'); - define('PDFMAKE_DOCS', 'http://pdfmake.org/playground.html'); - define('PHANTOMJS_CLOUD', 'http://api.phantomjscloud.com/api/browser/v2/'); - define('PHP_DATE_FORMATS', 'http://php.net/manual/en/function.date.php'); - define('REFERRAL_PROGRAM_URL', 'https://www.invoiceninja.com/referral-program/'); - define('EMAIL_MARKUP_URL', 'https://developers.google.com/gmail/markup'); - define('OFX_HOME_URL', 'http://www.ofxhome.com/index.php/home/directory/all'); + define('NINJA_FROM_EMAIL', env('NINJA_FROM_EMAIL', 'maildelivery@invoiceninja.com')); + define('RELEASES_URL', env('RELEASES_URL', 'https://trello.com/b/63BbiVVe/invoice-ninja')); + define('ZAPIER_URL', env('ZAPIER_URL', 'https://zapier.com/zapbook/invoice-ninja')); + define('OUTDATE_BROWSER_URL', env('OUTDATE_BROWSER_URL', 'http://browsehappy.com/')); + define('PDFMAKE_DOCS', env('PDFMAKE_DOCS', 'http://pdfmake.org/playground.html')); + define('PHANTOMJS_CLOUD', env('PHANTOMJS_CLOUD', 'http://api.phantomjscloud.com/api/browser/v2/')); + define('PHP_DATE_FORMATS', env('PHP_DATE_FORMATS', 'http://php.net/manual/en/function.date.php')); + define('REFERRAL_PROGRAM_URL', env('REFERRAL_PROGRAM_URL', 'https://www.invoiceninja.com/referral-program/')); + define('EMAIL_MARKUP_URL', env('EMAIL_MARKUP_URL', 'https://developers.google.com/gmail/markup')); + define('OFX_HOME_URL', env('OFX_HOME_URL', 'http://www.ofxhome.com/index.php/home/directory/all')); + define('GOOGLE_ANALYITCS_URL', env('GOOGLE_ANALYITCS_URL', 'https://www.google-analytics.com/collect')); define('BLANK_IMAGE', 'data:image/png;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='); @@ -604,13 +603,12 @@ if (!defined('CONTACT_EMAIL')) { define('INVOICE_DESIGNS_AFFILIATE_KEY', 'T3RS74'); define('SELF_HOST_AFFILIATE_KEY', '8S69AD'); - define('PRO_PLAN_PRICE', 50); - define('PLAN_PRICE_PRO_MONTHLY', 5); - define('PLAN_PRICE_PRO_YEARLY', 50); - define('PLAN_PRICE_ENTERPRISE_MONTHLY', 10); - define('PLAN_PRICE_ENTERPRISE_YEARLY', 100); - define('WHITE_LABEL_PRICE', 20); - define('INVOICE_DESIGNS_PRICE', 10); + define('PLAN_PRICE_PRO_MONTHLY', env('PLAN_PRICE_PRO_MONTHLY', 5)); + define('PLAN_PRICE_PRO_YEARLY', env('PLAN_PRICE_PRO_YEARLY', 50)); + define('PLAN_PRICE_ENTERPRISE_MONTHLY', env('PLAN_PRICE_ENTERPRISE_MONTHLY', 10)); + define('PLAN_PRICE_ENTERPRISE_YEARLY', env('PLAN_PRICE_ENTERPRISE_YEARLY', 100)); + define('WHITE_LABEL_PRICE', env('WHITE_LABEL_PRICE', 20)); + define('INVOICE_DESIGNS_PRICE', env('INVOICE_DESIGNS_PRICE', 10)); define('USER_TYPE_SELF_HOST', 'SELF_HOST'); define('USER_TYPE_CLOUD_HOST', 'CLOUD_HOST'); @@ -619,9 +617,11 @@ if (!defined('CONTACT_EMAIL')) { define('TEST_USERNAME', 'user@example.com'); define('TEST_PASSWORD', 'password'); define('API_SECRET', 'API_SECRET'); + define('DEFAULT_API_PAGE_SIZE', 15); + define('MAX_API_PAGE_SIZE', 100); - define('IOS_PRODUCTION_PUSH','ninjaIOS'); - define('IOS_DEV_PUSH','devNinjaIOS'); + define('IOS_PRODUCTION_PUSH', env('IOS_PRODUCTION_PUSH', 'ninjaIOS')); + define('IOS_DEV_PUSH', env('IOS_DEV_PUSH', 'devNinjaIOS')); define('TOKEN_BILLING_DISABLED', 1); define('TOKEN_BILLING_OPT_IN', 2); @@ -788,34 +788,10 @@ if (!defined('CONTACT_EMAIL')) { } } -/* -// Log all SQL queries to laravel.log -if (Utils::isNinjaDev()) { - Event::listen('illuminate.query', function($query, $bindings, $time, $name) { - $data = compact('bindings', 'time', 'name'); - - // Format binding data for sql insertion - foreach ($bindings as $i => $binding) { - if ($binding instanceof \DateTime) { - $bindings[$i] = $binding->format('\'Y-m-d H:i:s\''); - } elseif (is_string($binding)) { - $bindings[$i] = "'$binding'"; - } - } - - // Insert bindings into query - $query = str_replace(array('%', '?'), array('%%', '%s'), $query); - $query = vsprintf($query, $bindings); - - Log::info($query, $data); - }); -} -*/ - /* if (Utils::isNinjaDev()) { //ini_set('memory_limit','1024M'); //Auth::loginUsingId(1); } -*/ \ No newline at end of file +*/ diff --git a/app/Libraries/OFX.php b/app/Libraries/OFX.php index 5563e917acf7..721c9f529f85 100644 --- a/app/Libraries/OFX.php +++ b/app/Libraries/OFX.php @@ -2,6 +2,8 @@ // https://github.com/denvertimothy/OFX +use Utils; +use Log; use SimpleXMLElement; class OFX @@ -21,13 +23,19 @@ class OFX $c = curl_init(); curl_setopt($c, CURLOPT_URL, $this->bank->url); curl_setopt($c, CURLOPT_POST, 1); - curl_setopt($c, CURLOPT_HTTPHEADER, array('Content-Type: application/x-ofx')); + // User-Agent: http://www.ofxhome.com/ofxforum/viewtopic.php?pid=108091#p108091 + curl_setopt($c, CURLOPT_HTTPHEADER, array('Content-Type: application/x-ofx', 'User-Agent: httpclient')); curl_setopt($c, CURLOPT_POSTFIELDS, $this->request); curl_setopt($c, CURLOPT_RETURNTRANSFER, 1); + $this->response = curl_exec($c); - //print_r($this->response); - //\Log::info(print_r($this->response, true)); + + if (Utils::isNinjaDev()) { + Log::info(print_r($this->response, true)); + } + curl_close($c); + $tmp = explode('', $this->response); $this->responseHeader = $tmp[0]; $this->responseBody = ''.$tmp[1]; @@ -35,14 +43,15 @@ class OFX public function xml() { $xml = $this->responseBody; - self::closeTags($xml); + $xml = self::closeTags($xml); $x = new SimpleXMLElement($xml); return $x; } - public static function closeTags(&$x) + public static function closeTags($x) { - $x = preg_replace('/(<([^<\/]+)>)(?!.*?<\/\2>)([^<]+)/', '\1\3', $x); + $x = preg_replace('/\s+/', '', $x); + return preg_replace('/(<([^<\/]+)>)(?!.*?<\/\2>)([^<]+)/', '\1\3', $x); } } @@ -224,3 +233,4 @@ class Account } } } + diff --git a/app/Libraries/Utils.php b/app/Libraries/Utils.php index e4ad99c6ce45..c02c719169a4 100644 --- a/app/Libraries/Utils.php +++ b/app/Libraries/Utils.php @@ -51,6 +51,11 @@ class Utils return php_sapi_name() == 'cli'; } + public static function isTravis() + { + return env('TRAVIS') == 'true'; + } + public static function isNinja() { return self::isNinjaProd() || self::isNinjaDev(); @@ -135,7 +140,7 @@ class Utils public static function hasAllPermissions($permission) { - return Auth::check() && Auth::user()->hasPermissions($permission); + return Auth::check() && Auth::user()->hasPermission($permission); } public static function isTrial() @@ -336,6 +341,7 @@ class Utils $currency = self::getFromCache($currencyId, 'currencies'); $thousand = $currency->thousand_separator; $decimal = $currency->decimal_separator; + $precision = $currency->precision; $code = $currency->code; $swapSymbol = false; @@ -350,7 +356,7 @@ class Utils } } - $value = number_format($value, $currency->precision, $decimal, $thousand); + $value = number_format($value, $precision, $decimal, $thousand); $symbol = $currency->symbol; if ($showCode || !$symbol) { @@ -669,9 +675,14 @@ class Utils return $year + $offset; } + public static function getEntityClass($entityType) + { + return 'App\\Models\\' . static::getEntityName($entityType); + } + public static function getEntityName($entityType) { - return ucwords(str_replace('_', ' ', $entityType)); + return ucwords(Utils::toCamelCase($entityType)); } public static function getClientDisplayName($model) diff --git a/app/Listeners/ActivityListener.php b/app/Listeners/ActivityListener.php index 9a0aa0f6cd41..1df8edeb6848 100644 --- a/app/Listeners/ActivityListener.php +++ b/app/Listeners/ActivityListener.php @@ -1,4 +1,4 @@ -payment; + $invoice = $payment->invoice; + $account = $payment->account; + + if ($account->account_key != NINJA_ACCOUNT_KEY) { + return; + } + + $analyticsId = env('ANALYTICS_KEY'); + $client = $payment->client; + $amount = $payment->amount; + + $base = "v=1&tid={$analyticsId}&cid{$client->public_id}&cu=USD&ti={$invoice->invoice_number}"; + + $url = $base . "&t=transaction&ta=ninja&tr={$amount}"; + $this->sendAnalytics($url); + //Log::info($url); + + $url = $base . "&t=item&in=plan&ip={$amount}&iq=1"; + $this->sendAnalytics($url); + //Log::info($url); + } + + private function sendAnalytics($data) + { + $data = json_encode($data); + $curl = curl_init(); + + $opts = [ + CURLOPT_URL => GOOGLE_ANALYITCS_URL, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_POST => 'POST', + CURLOPT_POSTFIELDS => $data, + ]; + + curl_setopt_array($curl, $opts); + $response = curl_exec($curl); + curl_close($curl); + } +} diff --git a/app/Listeners/CreditListener.php b/app/Listeners/CreditListener.php index fba2e2c7ff9a..aad2dd4d65b8 100644 --- a/app/Listeners/CreditListener.php +++ b/app/Listeners/CreditListener.php @@ -1,4 +1,4 @@ -load('account_gateways'); + if ( ! $this->relationLoaded('account_gateways')) { + $this->load('account_gateways'); + } if ($gatewayId) { return $this->getGatewayConfig($gatewayId) != false; @@ -241,7 +243,7 @@ class Account extends Eloquent return $this->name; } - $this->load('users'); + //$this->load('users'); $user = $this->users()->first(); return $user->getDisplayName(); @@ -481,10 +483,17 @@ class Account extends Eloquent return Document::getDirectFileUrl($this->logo, $this->getLogoDisk()); } - public function getToken($name) + public function getPrimaryUser() + { + return $this->users() + ->orderBy('id') + ->first(); + } + + public function getToken($userId, $name) { foreach ($this->account_tokens as $token) { - if ($token->name === $name) { + if ($token->user_id == $userId && $token->name === $name) { return $token->token; } } @@ -809,6 +818,10 @@ class Account extends Eloquent public function hasFeature($feature) { + if (Utils::isNinjaDev()) { + return true; + } + $planDetails = $this->getPlanDetails(); $selfHost = !Utils::isNinjaProd(); @@ -1176,13 +1189,18 @@ class Account extends Eloquent return str_replace('/>', ' />', $template); } + public function getTemplateView($view = '') + { + return $this->getEmailDesignId() == EMAIL_DESIGN_PLAIN ? $view : 'design' . $this->getEmailDesignId(); + } + public function getEmailFooter() { if ($this->email_footer) { // Add line breaks if HTML isn't already being used return strip_tags($this->email_footer) == $this->email_footer ? nl2br($this->email_footer) : $this->email_footer; } else { - return "

" . trans('texts.email_signature') . "\n
\$account

"; + return "

" . trans('texts.email_signature') . "\n
\$account

"; } } diff --git a/app/Models/AccountToken.php b/app/Models/AccountToken.php index dd9a98800535..87728b37016e 100644 --- a/app/Models/AccountToken.php +++ b/app/Models/AccountToken.php @@ -16,4 +16,9 @@ class AccountToken extends EntityModel { return $this->belongsTo('App\Models\Account'); } + + public function user() + { + return $this->belongsTo('App\Models\User'); + } } diff --git a/app/Models/Client.php b/app/Models/Client.php index 19a7968bf965..bc6e94e649fc 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -199,6 +199,13 @@ class Client extends EntityModel return $this->name; } + public function getPrimaryContact() + { + return $this->contacts() + ->whereIsPrimary(true) + ->first(); + } + public function getDisplayName() { if ($this->name) { @@ -254,14 +261,18 @@ class Client extends EntityModel } - public function getGatewayToken(&$accountGateway = null) + public function getGatewayToken(&$accountGateway) { - $this->account->load('account_gateways'); + $account = $this->account; + + if ( ! $account->relationLoaded('account_gateways')) { + $account->load('account_gateways'); + } - if (!count($this->account->account_gateways)) { + if (!count($account->account_gateways)) { return false; } - $accountGateway = $this->account->getTokenGateway(); + $accountGateway = $account->getTokenGateway(); if (!$accountGateway) { return false; diff --git a/app/Models/Document.php b/app/Models/Document.php index 0d7fc049aac8..6d9c24857143 100644 --- a/app/Models/Document.php +++ b/app/Models/Document.php @@ -173,11 +173,11 @@ class Document extends EntityModel } public function getUrl(){ - return url('document/'.$this->public_id.'/'.$this->name); + return url('documents/'.$this->public_id.'/'.$this->name); } public function getClientUrl($invitation){ - return url('client/document/'.$invitation->invitation_key.'/'.$this->public_id.'/'.$this->name); + return url('client/documents/'.$invitation->invitation_key.'/'.$this->public_id.'/'.$this->name); } public function isPDFEmbeddable(){ @@ -186,16 +186,16 @@ class Document extends EntityModel public function getVFSJSUrl(){ if(!$this->isPDFEmbeddable())return null; - return url('document/js/'.$this->public_id.'/'.$this->name.'.js'); + return url('documents/js/'.$this->public_id.'/'.$this->name.'.js'); } public function getClientVFSJSUrl(){ if(!$this->isPDFEmbeddable())return null; - return url('client/document/js/'.$this->public_id.'/'.$this->name.'.js'); + return url('client/documents/js/'.$this->public_id.'/'.$this->name.'.js'); } public function getPreviewUrl(){ - return $this->preview?url('document/preview/'.$this->public_id.'/'.$this->name.'.'.pathinfo($this->preview, PATHINFO_EXTENSION)):null; + return $this->preview?url('documents/preview/'.$this->public_id.'/'.$this->name.'.'.pathinfo($this->preview, PATHINFO_EXTENSION)):null; } public function toArray() diff --git a/app/Models/EntityModel.php b/app/Models/EntityModel.php index 8d0da39d3fab..95e85e6acef4 100644 --- a/app/Models/EntityModel.php +++ b/app/Models/EntityModel.php @@ -101,6 +101,16 @@ class EntityModel extends Eloquent return $this->getName(); } + public static function getClassName($entityType) + { + return 'App\\Models\\' . ucwords(Utils::toCamelCase($entityType)); + } + + public static function getTransformerName($entityType) + { + return 'App\\Ninja\\Transformers\\' . ucwords(Utils::toCamelCase($entityType)) . 'Transformer'; + } + public function setNullValues() { foreach ($this->fillable as $field) { diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index 33ce1bf8f648..02fdc73f99bd 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -228,6 +228,12 @@ class Invoice extends EntityModel implements BalanceAffecting return $this->hasMany('App\Models\Expense','invoice_id','id')->withTrashed(); } + public function scopeInvoices($query) + { + return $query->where('is_quote', '=', false) + ->where('is_recurring', '=', false); + } + public function markInvitationsSent($notify = false) { foreach ($this->invitations as $invitation) { @@ -436,6 +442,7 @@ class Invoice extends EntityModel implements BalanceAffecting 'contacts', 'country', 'currency_id', + 'country_id', 'custom_value1', 'custom_value2', ]); @@ -792,7 +799,6 @@ class Invoice extends EntityModel implements BalanceAffecting $invitation = $this->invitations[0]; $link = $invitation->getLink('view', true); $key = env('PHANTOMJS_CLOUD_KEY'); - $curl = curl_init(); if (Utils::isNinjaDev()) { $link = env('TEST_LINK'); diff --git a/app/Models/InvoiceDesign.php b/app/Models/InvoiceDesign.php index 2b53ac6383c7..51f15b035ad6 100644 --- a/app/Models/InvoiceDesign.php +++ b/app/Models/InvoiceDesign.php @@ -1,4 +1,4 @@ -belongsTo('App\Models\Invoice'); } + public function user() + { + return $this->belongsTo('App\Models\User')->withTrashed(); + } + public function product() { return $this->belongsTo('App\Models\Product'); diff --git a/app/Models/Product.php b/app/Models/Product.php index 8de7c7ac5b2c..05944c9fff94 100644 --- a/app/Models/Product.php +++ b/app/Models/Product.php @@ -8,6 +8,14 @@ class Product extends EntityModel use SoftDeletes; protected $dates = ['deleted_at']; + protected $fillable = [ + 'product_key', + 'notes', + 'cost', + 'qty', + 'default_tax_rate_id', + ]; + public function getEntityType() { return ENTITY_PRODUCT; @@ -18,6 +26,11 @@ class Product extends EntityModel return Product::scope()->where('product_key', '=', $key)->first(); } + public function user() + { + return $this->belongsTo('App\Models\User')->withTrashed(); + } + public function default_tax_rate() { return $this->belongsTo('App\Models\TaxRate'); diff --git a/app/Models/TaxRate.php b/app/Models/TaxRate.php index 72ad266b07d8..384ccf933b36 100644 --- a/app/Models/TaxRate.php +++ b/app/Models/TaxRate.php @@ -17,4 +17,9 @@ class TaxRate extends EntityModel { return ENTITY_TAX_RATE; } + + public function user() + { + return $this->belongsTo('App\Models\User')->withTrashed(); + } } diff --git a/app/Models/Vendor.php b/app/Models/Vendor.php index 573a581e4774..6fcd10f14092 100644 --- a/app/Models/Vendor.php +++ b/app/Models/Vendor.php @@ -95,7 +95,7 @@ class Vendor extends EntityModel return $this->hasMany('App\Models\Payment'); } - public function vendorContacts() + public function vendor_contacts() { return $this->hasMany('App\Models\VendorContact'); } @@ -143,7 +143,7 @@ class Vendor extends EntityModel $contact->fill($data); $contact->is_primary = $isPrimary; - return $this->vendorContacts()->save($contact); + return $this->vendor_contacts()->save($contact); } public function getRoute() diff --git a/app/Ninja/Mailers/ContactMailer.php b/app/Ninja/Mailers/ContactMailer.php index 4b176628ebf0..fbebd0c5fdfd 100644 --- a/app/Ninja/Mailers/ContactMailer.php +++ b/app/Ninja/Mailers/ContactMailer.php @@ -1,17 +1,13 @@ templateService = $templateService; + } + public function sendInvoice(Invoice $invoice, $reminder = false, $pdfString = false) { $invoice->load('invitations', 'client.language', 'account'); @@ -144,7 +145,7 @@ class ContactMailer extends Mailer } $data = [ - 'body' => $this->processVariables($body, $variables), + 'body' => $this->templateService->processVariables($body, $variables), 'link' => $invitation->getLink(), 'entityType' => $invoice->getEntityType(), 'invoiceId' => $invoice->id, @@ -160,14 +161,9 @@ class ContactMailer extends Mailer $data['pdfFileName'] = $invoice->getFileName(); } - $subject = $this->processVariables($subject, $variables); + $subject = $this->templateService->processVariables($subject, $variables); $fromEmail = $user->email; - - if ($account->getEmailDesignId() == EMAIL_DESIGN_PLAIN) { - $view = ENTITY_INVOICE; - } else { - $view = 'design' . ($account->getEmailDesignId() - 1); - } + $view = $account->getTemplateView(ENTITY_INVOICE); $response = $this->sendTo($invitation->contact->email, $fromEmail, $account->getDisplayName(), $subject, $view, $data); @@ -230,7 +226,7 @@ class ContactMailer extends Mailer ]; $data = [ - 'body' => $this->processVariables($emailTemplate, $variables), + 'body' => $this->templateService->processVariables($emailTemplate, $variables), 'link' => $invitation->getLink(), 'invoice' => $invoice, 'client' => $client, @@ -244,14 +240,10 @@ class ContactMailer extends Mailer $data['pdfFileName'] = $invoice->getFileName(); } - $subject = $this->processVariables($emailSubject, $variables); + $subject = $this->templateService->processVariables($emailSubject, $variables); $data['invoice_id'] = $payment->invoice->id; - if ($account->getEmailDesignId() == EMAIL_DESIGN_PLAIN) { - $view = 'payment_confirmation'; - } else { - $view = 'design' . ($account->getEmailDesignId() - 1); - } + $view = $account->getTemplateView('payment_confirmation'); if ($user->email && $contact->email) { $this->sendTo($contact->email, $user->email, $accountName, $subject, $view, $data); @@ -281,75 +273,4 @@ class ContactMailer extends Mailer $this->sendTo($email, CONTACT_EMAIL, CONTACT_NAME, $subject, $view, $data); } - - private function processVariables($template, $data) - { - $account = $data['account']; - $client = $data['client']; - $invitation = $data['invitation']; - $invoice = $invitation->invoice; - $passwordHTML = isset($data['password'])?'

'.trans('texts.password').': '.$data['password'].'

':false; - $documentsHTML = ''; - - if($account->hasFeature(FEATURE_DOCUMENTS) && $invoice->hasDocuments()){ - $documentsHTML .= trans('texts.email_documents_header').'

'; - } - - $variables = [ - '$footer' => $account->getEmailFooter(), - '$client' => $client->getDisplayName(), - '$account' => $account->getDisplayName(), - '$dueDate' => $account->formatDate($invoice->due_date), - '$invoiceDate' => $account->formatDate($invoice->invoice_date), - '$contact' => $invitation->contact->getDisplayName(), - '$firstName' => $invitation->contact->first_name, - '$amount' => $account->formatMoney($data['amount'], $client), - '$invoice' => $invoice->invoice_number, - '$quote' => $invoice->invoice_number, - '$link' => $invitation->getLink(), - '$password' => $passwordHTML, - '$viewLink' => $invitation->getLink().'$password', - '$viewButton' => Form::emailViewButton($invitation->getLink(), $invoice->getEntityType()).'$password', - '$paymentLink' => $invitation->getLink('payment').'$password', - '$paymentButton' => Form::emailPaymentButton($invitation->getLink('payment')).'$password', - '$customClient1' => $account->custom_client_label1, - '$customClient2' => $account->custom_client_label2, - '$customInvoice1' => $account->custom_invoice_text_label1, - '$customInvoice2' => $account->custom_invoice_text_label2, - '$documents' => $documentsHTML, - ]; - - // Add variables for available payment types - foreach (Gateway::$paymentTypes as $type) { - $camelType = Gateway::getPaymentTypeName($type); - $type = Utils::toSnakeCase($camelType); - $variables["\${$camelType}Link"] = $invitation->getLink('payment') . "/{$type}"; - $variables["\${$camelType}Button"] = Form::emailPaymentButton($invitation->getLink('payment') . "/{$type}"); - } - - $includesPasswordPlaceholder = strpos($template, '$password') !== false; - - $str = str_replace(array_keys($variables), array_values($variables), $template); - - if(!$includesPasswordPlaceholder && $passwordHTML){ - $pos = strrpos($str, '$password'); - if($pos !== false) - { - $str = substr_replace($str, $passwordHTML, $pos, 9/* length of "$password" */); - } - } - $str = str_replace('$password', '', $str); - $str = autolink($str, 100); - - return $str; - } } diff --git a/app/Ninja/Presenters/InvoicePresenter.php b/app/Ninja/Presenters/InvoicePresenter.php index ebb3297d5c1a..36a2ce2acbd6 100644 --- a/app/Ninja/Presenters/InvoicePresenter.php +++ b/app/Ninja/Presenters/InvoicePresenter.php @@ -18,7 +18,7 @@ class InvoicePresenter extends Presenter { public function balanceDueLabel() { - if ($this->entity->partial) { + if ($this->entity->partial > 0) { return 'partial_due'; } elseif ($this->entity->is_quote) { return 'total'; diff --git a/app/Ninja/Repositories/AccountRepository.php b/app/Ninja/Repositories/AccountRepository.php index e8fe2d041843..38753dde9b9a 100644 --- a/app/Ninja/Repositories/AccountRepository.php +++ b/app/Ninja/Repositories/AccountRepository.php @@ -75,17 +75,19 @@ class AccountRepository return $account; } - public function getSearchData($account) + public function getSearchData($user) { - $data = $this->getAccountSearchData($account); + $data = $this->getAccountSearchData($user); - $data['navigation'] = $this->getNavigationSearchData(); + $data['navigation'] = $user->is_admin ? $this->getNavigationSearchData() : []; return $data; } - private function getAccountSearchData($account) + private function getAccountSearchData($user) { + $account = $user->account; + $data = [ 'clients' => [], 'contacts' => [], @@ -100,11 +102,19 @@ class AccountRepository if ($account->custom_client_label2) { $data[$account->custom_client_label2] = []; } - - $clients = Client::scope() - ->with('contacts', 'invoices') - ->get(); - + + if ($user->hasPermission('view_all')) { + $clients = Client::scope() + ->with('contacts', 'invoices') + ->get(); + } else { + $clients = Client::scope() + ->where('user_id', '=', $user->id) + ->with(['contacts', 'invoices' => function($query) use ($user) { + $query->where('user_id', '=', $user->id); + }])->get(); + } + foreach ($clients as $client) { if ($client->name) { $data['clients'][] = [ @@ -167,6 +177,7 @@ class AccountRepository ENTITY_QUOTE, ENTITY_TASK, ENTITY_EXPENSE, + ENTITY_VENDOR, ENTITY_RECURRING_INVOICE, ENTITY_PAYMENT, ENTITY_CREDIT @@ -183,15 +194,22 @@ class AccountRepository ]; } - $features[] = ['dashboard', '/dashboard']; - $features[] = ['customize_design', '/settings/customize_design']; - $features[] = ['new_tax_rate', '/tax_rates/create']; - $features[] = ['new_product', '/products/create']; - $features[] = ['new_user', '/users/create']; - $features[] = ['custom_fields', '/settings/invoice_settings']; + $features = array_merge($features, [ + ['dashboard', '/dashboard'], + ['customize_design', '/settings/customize_design'], + ['new_tax_rate', '/tax_rates/create'], + ['new_product', '/products/create'], + ['new_user', '/users/create'], + ['custom_fields', '/settings/invoice_settings'], + ['invoice_number', '/settings/invoice_settings'], + ]); $settings = array_merge(Account::$basicSettings, Account::$advancedSettings); + if ( ! Utils::isNinjaProd()) { + $settings[] = ACCOUNT_SYSTEM_SETTINGS; + } + foreach ($settings as $setting) { $features[] = [ $setting, @@ -325,28 +343,42 @@ class AccountRepository { $account->load('users'); $ninjaAccount = $this->getNinjaAccount(); - $client = Client::whereAccountId($ninjaAccount->id)->wherePublicId($account->id)->first(); + $ninjaUser = $ninjaAccount->getPrimaryUser(); + $client = Client::whereAccountId($ninjaAccount->id) + ->wherePublicId($account->id) + ->first(); + $clientExists = $client ? true : false; if (!$client) { $client = new Client(); $client->public_id = $account->id; - $client->user_id = $ninjaAccount->users()->first()->id; + $client->account_id = $ninjaAccount->id; + $client->user_id = $ninjaUser->id; $client->currency_id = 1; - foreach (['name', 'address1', 'address2', 'city', 'state', 'postal_code', 'country_id', 'work_phone', 'language_id'] as $field) { - $client->$field = $account->$field; - } - $ninjaAccount->clients()->save($client); + } + + foreach (['name', 'address1', 'address2', 'city', 'state', 'postal_code', 'country_id', 'work_phone', 'language_id', 'vat_number'] as $field) { + $client->$field = $account->$field; + } + + $client->save(); + if ($clientExists) { + $contact = $client->getPrimaryContact(); + } else { $contact = new Contact(); - $contact->user_id = $ninjaAccount->users()->first()->id; + $contact->user_id = $ninjaUser->id; $contact->account_id = $ninjaAccount->id; $contact->public_id = $account->id; $contact->is_primary = true; - foreach (['first_name', 'last_name', 'email', 'phone'] as $field) { - $contact->$field = $account->users()->first()->$field; - } - $client->contacts()->save($contact); } + + $user = $account->getPrimaryUser(); + foreach (['first_name', 'last_name', 'email', 'phone'] as $field) { + $contact->$field = $user->$field; + } + + $client->contacts()->save($contact); return $client; } @@ -636,7 +668,7 @@ class AccountRepository { $name = trim($name) ?: 'TOKEN'; $users = $this->findUsers($user); - + foreach ($users as $user) { if ($token = AccountToken::whereUserId($user->id)->whereName($name)->first()) { continue; diff --git a/app/Ninja/Repositories/ClientRepository.php b/app/Ninja/Repositories/ClientRepository.php index d3f943a07001..83f08cd97a59 100644 --- a/app/Ninja/Repositories/ClientRepository.php +++ b/app/Ninja/Repositories/ClientRepository.php @@ -66,16 +66,19 @@ class ClientRepository extends BaseRepository return $query; } - public function save($data) + public function save($data, $client = null) { $publicId = isset($data['public_id']) ? $data['public_id'] : false; - if (!$publicId || $publicId == '-1') { + if ($client) { + // do nothing + } elseif (!$publicId || $publicId == '-1') { $client = Client::createNew(); } else { $client = Client::scope($publicId)->with('contacts')->firstOrFail(); + \Log::warning('Entity not set in client repo save'); } - + // convert currency code to id if (isset($data['currency_code'])) { $currencyCode = strtolower($data['currency_code']); @@ -102,9 +105,9 @@ class ClientRepository extends BaseRepository // If the primary is set ensure it's listed first usort($contacts, function ($left, $right) { - return (isset($right['is_primary']) ? $right['is_primary'] : 0) - (isset($left['is_primary']) ? $left['is_primary'] : 0); + return (isset($right['is_primary']) ? $right['is_primary'] : 1) - (isset($left['is_primary']) ? $left['is_primary'] : 0); }); - + foreach ($contacts as $contact) { $contact = $client->addContact($contact, $first); $contactIds[] = $contact->public_id; diff --git a/app/Ninja/Repositories/CreditRepository.php b/app/Ninja/Repositories/CreditRepository.php index 9803b4628af8..068707ace02b 100644 --- a/app/Ninja/Repositories/CreditRepository.php +++ b/app/Ninja/Repositories/CreditRepository.php @@ -27,7 +27,7 @@ class CreditRepository extends BaseRepository DB::raw('COALESCE(clients.currency_id, accounts.currency_id) currency_id'), DB::raw('COALESCE(clients.country_id, accounts.country_id) country_id'), 'credits.public_id', - 'clients.name as client_name', + DB::raw("COALESCE(NULLIF(clients.name,''), NULLIF(CONCAT(contacts.first_name, ' ', contacts.last_name),''), NULLIF(contacts.email,'')) client_name"), 'clients.public_id as client_public_id', 'clients.user_id as client_user_id', 'credits.amount', @@ -59,12 +59,15 @@ class CreditRepository extends BaseRepository return $query; } - public function save($input) + public function save($input, $credit = null) { $publicId = isset($data['public_id']) ? $data['public_id'] : false; - - if ($publicId) { + + if ($credit) { + // do nothing + } elseif ($publicId) { $credit = Credit::scope($publicId)->firstOrFail(); + \Log::warning('Entity not set in credit repo save'); } else { $credit = Credit::createNew(); } diff --git a/app/Ninja/Repositories/DocumentRepository.php b/app/Ninja/Repositories/DocumentRepository.php index 4da51c3797d4..094b4848ebe0 100644 --- a/app/Ninja/Repositories/DocumentRepository.php +++ b/app/Ninja/Repositories/DocumentRepository.php @@ -1,4 +1,4 @@ -addColumn('name', function ($model) { return link_to( - '/client/document/'.$model->invitation_key.'/'.$model->public_id.'/'.$model->name, + '/client/documents/'.$model->invitation_key.'/'.$model->public_id.'/'.$model->name, $model->name, ['target'=>'_blank'] )->toHtml(); diff --git a/app/Ninja/Repositories/ExpenseRepository.php b/app/Ninja/Repositories/ExpenseRepository.php index 3492740d6a94..442af74bd32b 100644 --- a/app/Ninja/Repositories/ExpenseRepository.php +++ b/app/Ninja/Repositories/ExpenseRepository.php @@ -1,4 +1,4 @@ -firstOrFail(); + \Log::warning('Entity not set in expense repo save'); } else { $expense = Expense::createNew(); } @@ -160,7 +163,7 @@ class ExpenseRepository extends BaseRepository $document_ids = !empty($input['document_ids'])?array_map('intval', $input['document_ids']):array();; foreach ($document_ids as $document_id){ $document = Document::scope($document_id)->first(); - if($document && !$checkSubPermissions || Auth::user()->can('edit', $document)){ + if($document && Auth::user()->can('edit', $document)){ $document->invoice_id = null; $document->expense_id = $expense->id; $document->save(); diff --git a/app/Ninja/Repositories/InvoiceRepository.php b/app/Ninja/Repositories/InvoiceRepository.php index f0bc159191e5..fd493ba16d93 100644 --- a/app/Ninja/Repositories/InvoiceRepository.php +++ b/app/Ninja/Repositories/InvoiceRepository.php @@ -1,4 +1,4 @@ -join('accounts', 'accounts.id', '=', 'invitations.account_id') - ->join('invoices', 'invoices.id', '=', 'invitations.invoice_id') - ->join('clients', 'clients.id', '=', 'invoices.client_id') - ->where('invitations.contact_id', '=', $contactId) - ->where('invitations.deleted_at', '=', null) - ->where('invoices.is_quote', '=', $entityType == ENTITY_QUOTE) - ->where('invoices.is_deleted', '=', false) - ->where('clients.deleted_at', '=', null) - ->where('invoices.is_recurring', '=', false) - // This needs to be a setting to also hide the activity on the dashboard page - //->where('invoices.invoice_status_id', '>=', INVOICE_STATUS_SENT) - ->select( + ->join('accounts', 'accounts.id', '=', 'invitations.account_id') + ->join('invoices', 'invoices.id', '=', 'invitations.invoice_id') + ->join('clients', 'clients.id', '=', 'invoices.client_id') + ->join('contacts', 'contacts.client_id', '=', 'clients.id') + ->where('invitations.contact_id', '=', $contactId) + ->where('invitations.deleted_at', '=', null) + ->where('invoices.is_quote', '=', $entityType == ENTITY_QUOTE) + ->where('invoices.is_deleted', '=', false) + ->where('clients.deleted_at', '=', null) + ->where('contacts.deleted_at', '=', null) + ->where('contacts.is_primary', '=', true) + ->where('invoices.is_recurring', '=', false) + // This needs to be a setting to also hide the activity on the dashboard page + //->where('invoices.invoice_status_id', '>=', INVOICE_STATUS_SENT) + ->select( DB::raw('COALESCE(clients.currency_id, accounts.currency_id) currency_id'), DB::raw('COALESCE(clients.country_id, accounts.country_id) country_id'), 'invitations.invitation_key', @@ -217,7 +220,7 @@ class InvoiceRepository extends BaseRepository 'invoices.balance as balance', 'invoices.due_date', 'clients.public_id as client_public_id', - 'clients.name as client_name', + DB::raw("COALESCE(NULLIF(clients.name,''), NULLIF(CONCAT(contacts.first_name, ' ', contacts.last_name),''), NULLIF(contacts.email,'')) client_name"), 'invoices.public_id', 'invoices.amount', 'invoices.start_date', @@ -245,14 +248,16 @@ class InvoiceRepository extends BaseRepository ->make(); } - public function save($data, $checkSubPermissions = false) + public function save($data, $invoice = null) { $account = \Auth::user()->account; $publicId = isset($data['public_id']) ? $data['public_id'] : false; $isNew = !$publicId || $publicId == '-1'; - if ($isNew) { + if ($invoice) { + // do nothing + } elseif ($isNew) { $entityType = ENTITY_INVOICE; if (isset($data['is_recurring']) && filter_var($data['is_recurring'], FILTER_VALIDATE_BOOLEAN)) { $entityType = ENTITY_RECURRING_INVOICE; @@ -268,6 +273,7 @@ class InvoiceRepository extends BaseRepository } } else { $invoice = Invoice::scope($publicId)->firstOrFail(); + \Log::warning('Entity not set in invoice repo save'); } $invoice->fill($data); @@ -349,10 +355,12 @@ class InvoiceRepository extends BaseRepository $invoice->invoice_footer = (isset($data['invoice_footer']) && trim($data['invoice_footer'])) ? trim($data['invoice_footer']) : (!$publicId && $account->invoice_footer ? $account->invoice_footer : ''); $invoice->public_notes = isset($data['public_notes']) ? trim($data['public_notes']) : null; - // process date variables - $invoice->terms = Utils::processVariables($invoice->terms); - $invoice->invoice_footer = Utils::processVariables($invoice->invoice_footer); - $invoice->public_notes = Utils::processVariables($invoice->public_notes); + // process date variables if not recurring + if(!$invoice->is_recurring) { + $invoice->terms = Utils::processVariables($invoice->terms); + $invoice->invoice_footer = Utils::processVariables($invoice->invoice_footer); + $invoice->public_notes = Utils::processVariables($invoice->public_notes); + } if (isset($data['po_number'])) { $invoice->po_number = trim($data['po_number']); @@ -472,7 +480,7 @@ class InvoiceRepository extends BaseRepository $document_ids = !empty($data['document_ids'])?array_map('intval', $data['document_ids']):array();; foreach ($document_ids as $document_id){ $document = Document::scope($document_id)->first(); - if($document && !$checkSubPermissions || Auth::user()->can('edit', $document)){ + if($document && Auth::user()->can('edit', $document)){ if($document->invoice_id && $document->invoice_id != $invoice->id){ // From a clone @@ -525,7 +533,7 @@ class InvoiceRepository extends BaseRepository $task = false; if (isset($item['task_public_id']) && $item['task_public_id']) { $task = Task::scope($item['task_public_id'])->where('invoice_id', '=', null)->firstOrFail(); - if(!$checkSubPermissions || Auth::user()->can('edit', $task)){ + if(Auth::user()->can('edit', $task)){ $task->invoice_id = $invoice->id; $task->client_id = $invoice->client_id; $task->save(); @@ -535,7 +543,7 @@ class InvoiceRepository extends BaseRepository $expense = false; if (isset($item['expense_public_id']) && $item['expense_public_id']) { $expense = Expense::scope($item['expense_public_id'])->where('invoice_id', '=', null)->firstOrFail(); - if(!$checkSubPermissions || Auth::user()->can('edit', $expense)){ + if(Auth::user()->can('edit', $expense)){ $expense->invoice_id = $invoice->id; $expense->client_id = $invoice->client_id; $expense->save(); @@ -546,7 +554,7 @@ class InvoiceRepository extends BaseRepository if (\Auth::user()->account->update_products && ! strtotime($productKey)) { $product = Product::findProductByKey($productKey); if (!$product) { - if(!$checkSubPermissions || Auth::user()->can('create', ENTITY_PRODUCT)){ + if (Auth::user()->can('create', ENTITY_PRODUCT)) { $product = Product::createNew(); $product->product_key = trim($item['product_key']); } @@ -554,7 +562,7 @@ class InvoiceRepository extends BaseRepository $product = null; } } - if($product && (!$checkSubPermissions || Auth::user()->can('edit', $product))){ + if ($product && (Auth::user()->can('edit', $product))) { $product->notes = ($task || $expense) ? '' : $item['notes']; $product->cost = $expense ? 0 : $item['cost']; $product->save(); @@ -568,7 +576,6 @@ class InvoiceRepository extends BaseRepository $invoiceItem->notes = trim($invoice->is_recurring ? $item['notes'] : Utils::processVariables($item['notes'])); $invoiceItem->cost = Utils::parseFloat($item['cost']); $invoiceItem->qty = Utils::parseFloat($item['qty']); - //$invoiceItem->tax_rate = 0; if (isset($item['custom_value1'])) { $invoiceItem->custom_value1 = $item['custom_value1']; @@ -771,8 +778,8 @@ class InvoiceRepository extends BaseRepository $invoice->custom_value2 = $recurInvoice->custom_value2 ?: 0; $invoice->custom_taxes1 = $recurInvoice->custom_taxes1 ?: 0; $invoice->custom_taxes2 = $recurInvoice->custom_taxes2 ?: 0; - $invoice->custom_text_value1 = $recurInvoice->custom_text_value1; - $invoice->custom_text_value2 = $recurInvoice->custom_text_value2; + $invoice->custom_text_value1 = Utils::processVariables($recurInvoice->custom_text_value1); + $invoice->custom_text_value2 = Utils::processVariables($recurInvoice->custom_text_value2); $invoice->is_amount_discount = $recurInvoice->is_amount_discount; $invoice->due_date = $recurInvoice->getDueDate(); $invoice->save(); @@ -788,6 +795,8 @@ class InvoiceRepository extends BaseRepository $item->tax_rate1 = $recurItem->tax_rate1; $item->tax_name2 = $recurItem->tax_name2; $item->tax_rate2 = $recurItem->tax_rate2; + $item->custom_value1 = Utils::processVariables($recurItem->custom_value1); + $item->custom_value2 = Utils::processVariables($recurItem->custom_value2); $invoice->invoice_items()->save($item); } diff --git a/app/Ninja/Repositories/PaymentRepository.php b/app/Ninja/Repositories/PaymentRepository.php index b2022f2e197f..ef6c88096405 100644 --- a/app/Ninja/Repositories/PaymentRepository.php +++ b/app/Ninja/Repositories/PaymentRepository.php @@ -35,7 +35,7 @@ class PaymentRepository extends BaseRepository DB::raw('COALESCE(clients.currency_id, accounts.currency_id) currency_id'), DB::raw('COALESCE(clients.country_id, accounts.country_id) country_id'), 'payments.transaction_reference', - 'clients.name as client_name', + DB::raw("COALESCE(NULLIF(clients.name,''), NULLIF(CONCAT(contacts.first_name, ' ', contacts.last_name),''), NULLIF(contacts.email,'')) client_name"), 'clients.public_id as client_public_id', 'clients.user_id as client_user_id', 'payments.amount', @@ -74,7 +74,14 @@ class PaymentRepository extends BaseRepository if ($filter) { $query->where(function ($query) use ($filter) { - $query->where('clients.name', 'like', '%'.$filter.'%'); + $query->where('clients.name', 'like', '%'.$filter.'%') + ->orWhere('invoices.invoice_number', 'like', '%'.$filter.'%') + ->orWhere('payments.transaction_reference', 'like', '%'.$filter.'%') + ->orWhere('gateways.name', 'like', '%'.$filter.'%') + ->orWhere('payment_types.name', 'like', '%'.$filter.'%') + ->orWhere('contacts.first_name', 'like', '%'.$filter.'%') + ->orWhere('contacts.last_name', 'like', '%'.$filter.'%') + ->orWhere('contacts.email', 'like', '%'.$filter.'%'); }); } @@ -105,7 +112,7 @@ class PaymentRepository extends BaseRepository 'invitations.invitation_key', 'payments.public_id', 'payments.transaction_reference', - 'clients.name as client_name', + DB::raw("COALESCE(NULLIF(clients.name,''), NULLIF(CONCAT(contacts.first_name, ' ', contacts.last_name),''), NULLIF(contacts.email,'')) client_name"), 'clients.public_id as client_public_id', 'payments.amount', 'payments.payment_date', @@ -135,12 +142,15 @@ class PaymentRepository extends BaseRepository return $query; } - public function save($input) + public function save($input, $payment = null) { $publicId = isset($input['public_id']) ? $input['public_id'] : false; - if ($publicId) { + if ($payment) { + // do nothing + } elseif ($publicId) { $payment = Payment::scope($publicId)->firstOrFail(); + \Log::warning('Entity not set in payment repo save'); } else { $payment = Payment::createNew(); } diff --git a/app/Ninja/Repositories/ProductRepository.php b/app/Ninja/Repositories/ProductRepository.php index 417b49f23640..eb0e7383e9e5 100644 --- a/app/Ninja/Repositories/ProductRepository.php +++ b/app/Ninja/Repositories/ProductRepository.php @@ -1,6 +1,7 @@ firstOrFail(); + \Log::warning('Entity not set in product repo save'); + } else { + $product = Product::createNew(); + } + + $product->fill($data); + $product->save(); + + return $product; + } + } \ No newline at end of file diff --git a/app/Ninja/Repositories/TaskRepository.php b/app/Ninja/Repositories/TaskRepository.php index a7655a8abd9e..3a5eca04a112 100644 --- a/app/Ninja/Repositories/TaskRepository.php +++ b/app/Ninja/Repositories/TaskRepository.php @@ -25,7 +25,7 @@ class TaskRepository ->where('clients.deleted_at', '=', null) ->select( 'tasks.public_id', - 'clients.name as client_name', + \DB::raw("COALESCE(NULLIF(clients.name,''), NULLIF(CONCAT(contacts.first_name, ' ', contacts.last_name),''), NULLIF(contacts.email,'')) client_name"), 'clients.public_id as client_public_id', 'clients.user_id as client_user_id', 'contacts.first_name', @@ -64,24 +64,13 @@ class TaskRepository return $query; } - public function getErrors($input) + public function save($publicId, $data, $task = null) { - $rules = [ - 'time_log' => 'time_log', - ]; - $validator = \Validator::make($input, $rules); - - if ($validator->fails()) { - return $validator; - } - - return false; - } - - public function save($publicId, $data) - { - if ($publicId) { + if ($task) { + // do nothing + } elseif ($publicId) { $task = Task::scope($publicId)->firstOrFail(); + \Log::warning('Entity not set in task repo save'); } else { $task = Task::createNew(); } diff --git a/app/Ninja/Repositories/TaxRateRepository.php b/app/Ninja/Repositories/TaxRateRepository.php index 2e325100a016..8f1ad7f6550f 100644 --- a/app/Ninja/Repositories/TaxRateRepository.php +++ b/app/Ninja/Repositories/TaxRateRepository.php @@ -20,14 +20,15 @@ class TaxRateRepository extends BaseRepository ->select('tax_rates.public_id', 'tax_rates.name', 'tax_rates.rate', 'tax_rates.deleted_at'); } - public function save($data, $taxRate = false) + public function save($data, $taxRate = null) { - if ( ! $taxRate) { - if (isset($data['public_id'])) { - $taxRate = TaxRate::scope($data['public_id'])->firstOrFail(); - } else { - $taxRate = TaxRate::createNew(); - } + if ($taxRate) { + // do nothing + } elseif (isset($data['public_id'])) { + $taxRate = TaxRate::scope($data['public_id'])->firstOrFail(); + \Log::warning('Entity not set in tax rate repo save'); + } else { + $taxRate = TaxRate::createNew(); } $taxRate->fill($data); diff --git a/app/Ninja/Repositories/TokenRepository.php b/app/Ninja/Repositories/TokenRepository.php index 5237eb7a0369..af0bbb6533d0 100644 --- a/app/Ninja/Repositories/TokenRepository.php +++ b/app/Ninja/Repositories/TokenRepository.php @@ -13,10 +13,10 @@ class TokenRepository extends BaseRepository return 'App\Models\AccountToken'; } - public function find($accountId) + public function find($userId) { $query = DB::table('account_tokens') - ->where('account_tokens.account_id', '=', $accountId); + ->where('account_tokens.user_id', '=', $userId); if (!Session::get('show_trash:token')) { $query->where('account_tokens.deleted_at', '=', null); diff --git a/app/Ninja/Repositories/VendorRepository.php b/app/Ninja/Repositories/VendorRepository.php index df885f62e12e..82e1bfdd18f1 100644 --- a/app/Ninja/Repositories/VendorRepository.php +++ b/app/Ninja/Repositories/VendorRepository.php @@ -16,7 +16,7 @@ class VendorRepository extends BaseRepository public function all() { return Vendor::scope() - ->with('user', 'vendorcontacts', 'country') + ->with('user', 'vendor_contacts', 'country') ->withTrashed() ->where('is_deleted', '=', false) ->get(); @@ -62,21 +62,24 @@ class VendorRepository extends BaseRepository return $query; } - public function save($data) + public function save($data, $vendor = null) { $publicId = isset($data['public_id']) ? $data['public_id'] : false; - if (!$publicId || $publicId == '-1') { + if ($vendor) { + // do nothing + } elseif (!$publicId || $publicId == '-1') { $vendor = Vendor::createNew(); } else { - $vendor = Vendor::scope($publicId)->with('vendorcontacts')->firstOrFail(); + $vendor = Vendor::scope($publicId)->with('vendor_contacts')->firstOrFail(); + \Log::warning('Entity not set in vendor repo save'); } $vendor->fill($data); $vendor->save(); $first = true; - $vendorcontacts = isset($data['vendorcontact']) ? [$data['vendorcontact']] : $data['vendorcontacts']; + $vendorcontacts = isset($data['vendor_contact']) ? [$data['vendor_contact']] : $data['vendor_contacts']; foreach ($vendorcontacts as $vendorcontact) { $vendorcontact = $vendor->addVendorContact($vendorcontact, $first); diff --git a/app/Ninja/Transformers/AccountTransformer.php b/app/Ninja/Transformers/AccountTransformer.php index 6a1c32f30e09..f56635bad2b1 100644 --- a/app/Ninja/Transformers/AccountTransformer.php +++ b/app/Ninja/Transformers/AccountTransformer.php @@ -14,12 +14,12 @@ class AccountTransformer extends EntityTransformer 'users', 'products', 'taxRates', - 'payments' ]; protected $availableIncludes = [ 'clients', 'invoices', + 'payments', ]; public function includeUsers(Account $account) diff --git a/app/Ninja/Transformers/ClientTransformer.php b/app/Ninja/Transformers/ClientTransformer.php index f7d786a2ee25..ea282f74ceca 100644 --- a/app/Ninja/Transformers/ClientTransformer.php +++ b/app/Ninja/Transformers/ClientTransformer.php @@ -47,7 +47,7 @@ class ClientTransformer extends EntityTransformer protected $availableIncludes = [ 'invoices', 'credits', - 'expenses', + //'expenses', ]; public function includeContacts(Client $client) @@ -58,7 +58,7 @@ class ClientTransformer extends EntityTransformer public function includeInvoices(Client $client) { - $transformer = new InvoiceTransformer($this->account, $this->serializer); + $transformer = new InvoiceTransformer($this->account, $this->serializer, $client); return $this->includeCollection($client->invoices, $transformer, ENTITY_INVOICE); } @@ -77,13 +77,11 @@ class ClientTransformer extends EntityTransformer public function transform(Client $client) { - return [ + return array_merge($this->getDefaults($client), [ 'id' => (int) $client->public_id, 'name' => $client->name, 'balance' => (float) $client->balance, 'paid_to_date' => (float) $client->paid_to_date, - 'user_id' => (int) $client->user->public_id + 1, - 'account_key' => $this->account->account_key, 'updated_at' => $this->getTimestamp($client->updated_at), 'archived_at' => $this->getTimestamp($client->deleted_at), 'address1' => $client->address1, @@ -106,6 +104,6 @@ class ClientTransformer extends EntityTransformer 'currency_id' => (int) $client->currency_id, 'custom_value1' => $client->custom_value1, 'custom_value2' => $client->custom_value2, - ]; + ]); } } \ No newline at end of file diff --git a/app/Ninja/Transformers/ContactTransformer.php b/app/Ninja/Transformers/ContactTransformer.php index e404b73660d4..68172e156c60 100644 --- a/app/Ninja/Transformers/ContactTransformer.php +++ b/app/Ninja/Transformers/ContactTransformer.php @@ -8,7 +8,7 @@ class ContactTransformer extends EntityTransformer { public function transform(Contact $contact) { - return [ + return array_merge($this->getDefaults($contact), [ 'id' => (int) $contact->public_id, 'first_name' => $contact->first_name, 'last_name' => $contact->last_name, @@ -18,8 +18,7 @@ class ContactTransformer extends EntityTransformer 'is_primary' => (bool) $contact->is_primary, 'phone' => $contact->phone, 'last_login' => $contact->last_login, - 'account_key' => $this->account->account_key, 'send_invoice' => (bool) $contact->send_invoice, - ]; + ]); } } \ No newline at end of file diff --git a/app/Ninja/Transformers/CreditTransformer.php b/app/Ninja/Transformers/CreditTransformer.php index a33185d2ff39..39ce5ff1d8f3 100644 --- a/app/Ninja/Transformers/CreditTransformer.php +++ b/app/Ninja/Transformers/CreditTransformer.php @@ -8,17 +8,16 @@ class CreditTransformer extends EntityTransformer { public function transform(Credit $credit) { - return [ + return array_merge($this->getDefaults($credit), [ 'id' => (int) $credit->public_id, 'amount' => (float) $credit->amount, 'balance' => (float) $credit->balance, 'updated_at' => $this->getTimestamp($credit->updated_at), 'archived_at' => $this->getTimestamp($credit->deleted_at), 'is_deleted' => (bool) $credit->is_deleted, - 'account_key' => $this->account->account_key, 'credit_date' => $credit->credit_date, 'credit_number' => $credit->credit_number, 'private_notes' => $credit->private_notes, - ]; + ]); } } \ No newline at end of file diff --git a/app/Ninja/Transformers/DocumentTransformer.php b/app/Ninja/Transformers/DocumentTransformer.php new file mode 100644 index 000000000000..4cbfa0619193 --- /dev/null +++ b/app/Ninja/Transformers/DocumentTransformer.php @@ -0,0 +1,19 @@ +getDefaults($document), [ + 'id' => (int) $document->public_id, + 'name' => $document->name, + 'type' => $document->type, + 'invoice_id' => isset($document->invoice->public_id) ? (int) $document->invoice->public_id : null, + 'expense_id' => isset($document->expense->public_id) ? (int) $document->expense->public_id : null, + ]); + } +} \ No newline at end of file diff --git a/app/Ninja/Transformers/EntityTransformer.php b/app/Ninja/Transformers/EntityTransformer.php index ea0dba263d97..d2d02bd0b6ee 100644 --- a/app/Ninja/Transformers/EntityTransformer.php +++ b/app/Ninja/Transformers/EntityTransformer.php @@ -1,5 +1,6 @@ getTimestamp() : null; } + + public function getDefaultIncludes() + { + return $this->defaultIncludes; + } + + protected function getDefaults($entity) + { + $data = [ + 'account_key' => $this->account->account_key, + 'is_owner' => (bool) Auth::user()->owns($entity), + ]; + + if ($entity->relationLoaded('user')) { + $data['user_id'] = (int) $entity->user->public_id + 1; + } + + return $data; + } } diff --git a/app/Ninja/Transformers/ExpenseTransformer.php b/app/Ninja/Transformers/ExpenseTransformer.php index a6d358d15766..46c334cb3e55 100644 --- a/app/Ninja/Transformers/ExpenseTransformer.php +++ b/app/Ninja/Transformers/ExpenseTransformer.php @@ -6,10 +6,16 @@ use League\Fractal; class ExpenseTransformer extends EntityTransformer { + public function __construct($account = null, $serializer = null, $client = null) + { + parent::__construct($account, $serializer); + + $this->client = $client; + } + public function transform(Expense $expense) { - - return [ + return array_merge($this->getDefaults($expense), [ 'id' => (int) $expense->public_id, 'private_notes' => $expense->private_notes, 'public_notes' => $expense->public_notes, @@ -19,15 +25,14 @@ class ExpenseTransformer extends EntityTransformer 'transaction_id' => $expense->transaction_id, 'bank_id' => $expense->bank_id, 'expense_currency_id' => (int) $expense->expense_currency_id, - 'account_key' => $this->account->account_key, 'amount' => (float) $expense->amount, 'expense_date' => $expense->expense_date, 'exchange_rate' => (float) $expense->exchange_rate, 'invoice_currency_id' => (int) $expense->invoice_currency_id, 'is_deleted' => (bool) $expense->is_deleted, - 'client_id' => isset($expense->client->public_id) ? (int) $expense->client->public_id : null, + 'client_id' => $this->client ? $this->client->public_id : (isset($expense->client->public_id) ? (int) $expense->client->public_id : null), 'invoice_id' => isset($expense->invoice->public_id) ? (int) $expense->invoice->public_id : null, 'vendor_id' => isset($expense->vendor->public_id) ? (int) $expense->vendor->public_id : null, - ]; + ]); } } \ No newline at end of file diff --git a/app/Ninja/Transformers/InvoiceItemTransformer.php b/app/Ninja/Transformers/InvoiceItemTransformer.php index 895d8e8d2f4a..080234f35e1b 100644 --- a/app/Ninja/Transformers/InvoiceItemTransformer.php +++ b/app/Ninja/Transformers/InvoiceItemTransformer.php @@ -8,11 +8,9 @@ class InvoiceItemTransformer extends EntityTransformer { public function transform(InvoiceItem $item) { - return [ + return array_merge($this->getDefaults($item), [ 'id' => (int) $item->public_id, 'product_key' => $item->product_key, - 'account_key' => $this->account->account_key, - 'user_id' => (int) $item->user_id, 'updated_at' => $this->getTimestamp($item->updated_at), 'archived_at' => $this->getTimestamp($item->deleted_at), 'product_key' => $item->product_key, @@ -23,6 +21,6 @@ class InvoiceItemTransformer extends EntityTransformer 'tax_rate1' => (float) $item->tax_rate1, 'tax_name2' => $item->tax_name2 ? $item->tax_name1 : '', 'tax_rate2' => (float) $item->tax_rate2, - ]; + ]); } } \ No newline at end of file diff --git a/app/Ninja/Transformers/InvoiceTransformer.php b/app/Ninja/Transformers/InvoiceTransformer.php index 413f43ce1a10..c866f1213540 100644 --- a/app/Ninja/Transformers/InvoiceTransformer.php +++ b/app/Ninja/Transformers/InvoiceTransformer.php @@ -28,9 +28,16 @@ class InvoiceTransformer extends EntityTransformer 'invitations', 'payments', 'client', - 'expenses', + //'expenses', ]; + public function __construct($account = null, $serializer = null, $client = null) + { + parent::__construct($account, $serializer); + + $this->client = $client; + } + public function includeInvoiceItems(Invoice $invoice) { $transformer = new InvoiceItemTransformer($this->account, $this->serializer); @@ -45,7 +52,7 @@ class InvoiceTransformer extends EntityTransformer public function includePayments(Invoice $invoice) { - $transformer = new PaymentTransformer($this->account, $this->serializer); + $transformer = new PaymentTransformer($this->account, $this->serializer, $invoice); return $this->includeCollection($invoice->payments, $transformer, ENTITY_PAYMENT); } @@ -64,11 +71,11 @@ class InvoiceTransformer extends EntityTransformer public function transform(Invoice $invoice) { - return [ + return array_merge($this->getDefaults($invoice), [ 'id' => (int) $invoice->public_id, 'amount' => (float) $invoice->amount, 'balance' => (float) $invoice->balance, - 'client_id' => (int) $invoice->client->public_id, + 'client_id' => (int) ($this->client ? $this->client->public_id : $invoice->client->public_id), 'invoice_status_id' => (int) $invoice->invoice_status_id, 'updated_at' => $this->getTimestamp($invoice->updated_at), 'archived_at' => $this->getTimestamp($invoice->deleted_at), @@ -98,8 +105,6 @@ class InvoiceTransformer extends EntityTransformer 'partial' => (float) $invoice->partial, 'has_tasks' => (bool) $invoice->has_tasks, 'auto_bill' => (bool) $invoice->auto_bill, - 'account_key' => $this->account->account_key, - 'user_id' => (int) $invoice->user->public_id + 1, 'custom_value1' => (float) $invoice->custom_value1, 'custom_value2' => (float) $invoice->custom_value2, 'custom_taxes1' => (bool) $invoice->custom_taxes1, @@ -108,6 +113,6 @@ class InvoiceTransformer extends EntityTransformer 'quote_invoice_id' => (int) $invoice->quote_invoice_id, 'custom_text_value1' => $invoice->custom_text_value1, 'custom_text_value2' => $invoice->custom_text_value2, - ]; + ]); } } diff --git a/app/Ninja/Transformers/PaymentTransformer.php b/app/Ninja/Transformers/PaymentTransformer.php index a1de09cd6ff4..c4e4328cb845 100644 --- a/app/Ninja/Transformers/PaymentTransformer.php +++ b/app/Ninja/Transformers/PaymentTransformer.php @@ -26,10 +26,11 @@ class PaymentTransformer extends EntityTransformer ]; - public function __construct(Account $account) + public function __construct($account = null, $serializer = null, $invoice = null) { - parent::__construct($account); - + parent::__construct($account, $serializer); + + $this->invoice = $invoice; } public function includeInvoice(Payment $payment) @@ -46,18 +47,16 @@ class PaymentTransformer extends EntityTransformer public function transform(Payment $payment) { - return [ + return array_merge($this->getDefaults($payment), [ 'id' => (int) $payment->public_id, 'amount' => (float) $payment->amount, - 'account_key' => $this->account->account_key, - 'user_id' => (int) $payment->user->public_id + 1, 'transaction_reference' => $payment->transaction_reference, 'payment_date' => $payment->payment_date, 'updated_at' => $this->getTimestamp($payment->updated_at), 'archived_at' => $this->getTimestamp($payment->deleted_at), 'is_deleted' => (bool) $payment->is_deleted, 'payment_type_id' => (int) $payment->payment_type_id, - 'invoice_id' => (int) $payment->invoice->public_id, - ]; + 'invoice_id' => (int) ($this->invoice ? $this->invoice->public_id : $payment->invoice->public_id), + ]); } } \ No newline at end of file diff --git a/app/Ninja/Transformers/ProductTransformer.php b/app/Ninja/Transformers/ProductTransformer.php index 34fbcf7f18ff..309305815fb0 100644 --- a/app/Ninja/Transformers/ProductTransformer.php +++ b/app/Ninja/Transformers/ProductTransformer.php @@ -7,16 +7,15 @@ class ProductTransformer extends EntityTransformer { public function transform(Product $product) { - return [ + return array_merge($this->getDefaults($product), [ 'id' => (int) $product->public_id, 'product_key' => $product->product_key, 'notes' => $product->notes, 'cost' => $product->cost, 'qty' => $product->qty, - 'account_key' =>$this->account->account_key, 'default_tax_rate_id' =>$product->default_tax_rate_id, 'updated_at' =>$this->getTimestamp($product->updated_at), 'archived_at' => $this->getTimestamp($product->deleted_at), - ]; + ]); } } \ No newline at end of file diff --git a/app/Ninja/Transformers/TaskTransformer.php b/app/Ninja/Transformers/TaskTransformer.php index 908a8118aaea..7bfc474a72f2 100644 --- a/app/Ninja/Transformers/TaskTransformer.php +++ b/app/Ninja/Transformers/TaskTransformer.php @@ -39,12 +39,10 @@ class TaskTransformer extends EntityTransformer public function transform(Task $task) { - return [ + return array_merge($this->getDefaults($task), [ 'id' => (int) $task->public_id, - 'account_key' => $this->account->account_key, - 'user_id' => (int) $task->user->public_id + 1, 'description' => $task->description, 'duration' => $task->getDuration() - ]; + ]); } } \ No newline at end of file diff --git a/app/Ninja/Transformers/TaxRateTransformer.php b/app/Ninja/Transformers/TaxRateTransformer.php index f7d307bf7bb4..a0c5aab539a0 100644 --- a/app/Ninja/Transformers/TaxRateTransformer.php +++ b/app/Ninja/Transformers/TaxRateTransformer.php @@ -21,13 +21,12 @@ class TaxRateTransformer extends EntityTransformer public function transform(TaxRate $taxRate) { - return [ + return array_merge($this->getDefaults($taxRate), [ 'id' => (int) $taxRate->public_id, 'name' => $taxRate->name, 'rate' => (float) $taxRate->rate, 'updated_at' => $this->getTimestamp($taxRate->updated_at), 'archived_at' => $this->getTimestamp($taxRate->deleted_at), - 'account_key' => $this->account->account_key, - ]; + ]); } } \ No newline at end of file diff --git a/app/Ninja/Transformers/UserAccountTransformer.php b/app/Ninja/Transformers/UserAccountTransformer.php index bc49a96c546a..e914a25c663d 100644 --- a/app/Ninja/Transformers/UserAccountTransformer.php +++ b/app/Ninja/Transformers/UserAccountTransformer.php @@ -32,7 +32,7 @@ class UserAccountTransformer extends EntityTransformer return [ 'account_key' => $user->account->account_key, 'name' => $user->account->present()->name, - 'token' => $user->account->getToken($this->tokenName), + 'token' => $user->account->getToken($user->id, $this->tokenName), 'default_url' => SITE_URL ]; } diff --git a/app/Ninja/Transformers/VendorContactTransformer.php b/app/Ninja/Transformers/VendorContactTransformer.php index 3b75aee53a28..f277964cec28 100644 --- a/app/Ninja/Transformers/VendorContactTransformer.php +++ b/app/Ninja/Transformers/VendorContactTransformer.php @@ -8,7 +8,7 @@ class VendorContactTransformer extends EntityTransformer { public function transform(VendorContact $contact) { - return [ + return array_merge($this->getDefaults($contact), [ 'id' => (int) $contact->public_id, 'first_name' => $contact->first_name, 'last_name' => $contact->last_name, @@ -17,7 +17,6 @@ class VendorContactTransformer extends EntityTransformer 'archived_at' => $this->getTimestamp($contact->deleted_at), 'is_primary' => (bool) $contact->is_primary, 'phone' => $contact->phone, - 'account_key' => $this->account->account_key, - ]; + ]); } } \ No newline at end of file diff --git a/app/Ninja/Transformers/VendorTransformer.php b/app/Ninja/Transformers/VendorTransformer.php index f0b8fd0415f0..df8dc5ea3461 100644 --- a/app/Ninja/Transformers/VendorTransformer.php +++ b/app/Ninja/Transformers/VendorTransformer.php @@ -35,16 +35,19 @@ class VendorTransformer extends EntityTransformer * @SWG\Property(property="id_number", type="string", example="123456") */ + protected $defaultIncludes = [ + 'vendor_contacts', + ]; + protected $availableIncludes = [ - 'vendorContacts', 'invoices', - 'expenses', + //'expenses', ]; public function includeVendorContacts(Vendor $vendor) { $transformer = new VendorContactTransformer($this->account, $this->serializer); - return $this->includeCollection($vendor->vendorContacts, $transformer, ENTITY_CONTACT); + return $this->includeCollection($vendor->vendor_contacts, $transformer, ENTITY_CONTACT); } public function includeInvoices(Vendor $vendor) @@ -61,13 +64,11 @@ class VendorTransformer extends EntityTransformer public function transform(Vendor $vendor) { - return [ + return array_merge($this->getDefaults($vendor), [ 'id' => (int) $vendor->public_id, 'name' => $vendor->name, 'balance' => (float) $vendor->balance, 'paid_to_date' => (float) $vendor->paid_to_date, - 'user_id' => (int) $vendor->user->public_id + 1, - 'account_key' => $this->account->account_key, 'updated_at' => $this->getTimestamp($vendor->updated_at), 'archived_at' => $this->getTimestamp($vendor->deleted_at), 'address1' => $vendor->address1, @@ -84,6 +85,6 @@ class VendorTransformer extends EntityTransformer 'vat_number' => $vendor->vat_number, 'id_number' => $vendor->id_number, 'currency_id' => (int) $vendor->currency_id - ]; + ]); } } \ No newline at end of file diff --git a/app/Policies/ProductPolicy.php b/app/Policies/ProductPolicy.php index 45ae97e961d7..897fe7404a03 100644 --- a/app/Policies/ProductPolicy.php +++ b/app/Policies/ProductPolicy.php @@ -2,7 +2,7 @@ namespace App\Policies; -class VendorPolicy extends EntityPolicy { +class ProductPolicy extends EntityPolicy { public static function edit($user, $item) { return $user->hasPermission('admin'); } diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index 084304a81de9..227a1d6df65c 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -21,11 +21,12 @@ class AuthServiceProvider extends ServiceProvider \App\Models\Payment::class => \App\Policies\PaymentPolicy::class, \App\Models\Task::class => \App\Policies\TaskPolicy::class, \App\Models\Vendor::class => \App\Policies\VendorPolicy::class, + \App\Models\Product::class => \App\Policies\ProductPolicy::class, + \App\Models\TaxRate::class => \App\Policies\TaxRatePolicy::class, \App\Models\AccountGateway::class => \App\Policies\AccountGatewayPolicy::class, \App\Models\Token::class => \App\Policies\TokenPolicy::class, \App\Models\BankAccount::class => \App\Policies\BankAccountPolicy::class, \App\Models\PaymentTerm::class => \App\Policies\PaymentTermPolicy::class, - ]; /** diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 03e1a4b36a36..b19594dacb87 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -98,6 +98,7 @@ class EventServiceProvider extends ServiceProvider { 'App\Listeners\SubscriptionListener@createdPayment', 'App\Listeners\InvoiceListener@createdPayment', 'App\Listeners\NotificationListener@createdPayment', + 'App\Listeners\AnalyticsListener@trackRevenue', ], 'App\Events\PaymentWasArchived' => [ 'App\Listeners\ActivityListener@archivedPayment', diff --git a/app/Services/BaseService.php b/app/Services/BaseService.php index 67abdf9d8a20..afd6ed1ea1eb 100644 --- a/app/Services/BaseService.php +++ b/app/Services/BaseService.php @@ -1,8 +1,8 @@ clientRepo; } - public function save($data) + public function save($data, $client = null) { if (Auth::user()->account->isNinjaAccount() && isset($data['plan'])) { $this->ninjaRepo->updatePlanDetails($data['public_id'], $data); } - return $this->clientRepo->save($data); + return $this->clientRepo->save($data, $client); } public function getDatatable($search) diff --git a/app/Services/ExpenseService.php b/app/Services/ExpenseService.php index afec1e4f9950..671648ea32a5 100644 --- a/app/Services/ExpenseService.php +++ b/app/Services/ExpenseService.php @@ -28,7 +28,7 @@ class ExpenseService extends BaseService return $this->expenseRepo; } - public function save($data, $checkSubPermissions=false) + public function save($data, $expense = null) { if (isset($data['client_id']) && $data['client_id']) { $data['client_id'] = Client::getPrivateId($data['client_id']); @@ -38,7 +38,7 @@ class ExpenseService extends BaseService $data['vendor_id'] = Vendor::getPrivateId($data['vendor_id']); } - return $this->expenseRepo->save($data, $checkSubPermissions); + return $this->expenseRepo->save($data, $expense); } public function getDatatable($search) diff --git a/app/Services/InvoiceService.php b/app/Services/InvoiceService.php index 66442ac0a31b..edbee8caf84c 100644 --- a/app/Services/InvoiceService.php +++ b/app/Services/InvoiceService.php @@ -30,26 +30,23 @@ class InvoiceService extends BaseService return $this->invoiceRepo; } - public function save($data, $checkSubPermissions = false) + public function save($data, $invoice = null) { if (isset($data['client'])) { - $canSaveClient = !$checkSubPermissions; - if( ! $canSaveClient){ - $clientPublicId = array_get($data, 'client.public_id') ?: array_get($data, 'client.id'); - if (empty($clientPublicId) || $clientPublicId == '-1') { - $canSaveClient = Auth::user()->can('create', ENTITY_CLIENT); - } else { - $canSaveClient = Auth::user()->can('edit', Client::scope($clientPublicId)->first()); - } - } - + $canSaveClient = false; + $clientPublicId = array_get($data, 'client.public_id') ?: array_get($data, 'client.id'); + if (empty($clientPublicId) || $clientPublicId == '-1') { + $canSaveClient = Auth::user()->can('create', ENTITY_CLIENT); + } else { + $canSaveClient = Auth::user()->can('edit', Client::scope($clientPublicId)->first()); + } if ($canSaveClient) { $client = $this->clientRepo->save($data['client']); $data['client_id'] = $client->id; } } - $invoice = $this->invoiceRepo->save($data, $checkSubPermissions); + $invoice = $this->invoiceRepo->save($data, $invoice); $client = $invoice->client; $client->load('contacts'); diff --git a/app/Services/PaymentService.php b/app/Services/PaymentService.php index 3ec068ba91fb..3f938141dd2b 100644 --- a/app/Services/PaymentService.php +++ b/app/Services/PaymentService.php @@ -71,7 +71,6 @@ class PaymentService extends BaseService if ($input) { $data = self::convertInputForOmnipay($input); - $data['email'] = $invitation->contact->email; Session::put($key, $data); } elseif (Session::get($key)) { $data = Session::get($key); @@ -114,12 +113,17 @@ class PaymentService extends BaseService $data = [ 'firstName' => $input['first_name'], 'lastName' => $input['last_name'], + 'email' => $input['email'], 'number' => isset($input['card_number']) ? $input['card_number'] : null, 'expiryMonth' => isset($input['expiration_month']) ? $input['expiration_month'] : null, 'expiryYear' => isset($input['expiration_year']) ? $input['expiration_year'] : null, - 'cvv' => isset($input['cvv']) ? $input['cvv'] : '', ]; - + + // allow space until there's a setting to disable + if (isset($input['cvv']) && $input['cvv'] != ' ') { + $data['cvv'] = $input['cvv']; + } + if (isset($input['country_id'])) { $country = Country::find($input['country_id']); diff --git a/app/Services/TemplateService.php b/app/Services/TemplateService.php new file mode 100644 index 000000000000..5a41c705352d --- /dev/null +++ b/app/Services/TemplateService.php @@ -0,0 +1,80 @@ +invoice; + $passwordHTML = isset($data['password'])?'

'.trans('texts.password').': '.$data['password'].'

':false; + $documentsHTML = ''; + + if ($account->hasFeature(FEATURE_DOCUMENTS) && $invoice->hasDocuments()) { + $documentsHTML .= trans('texts.email_documents_header').'

'; + } + + $variables = [ + '$footer' => $account->getEmailFooter(), + '$client' => $client->getDisplayName(), + '$account' => $account->getDisplayName(), + '$dueDate' => $account->formatDate($invoice->due_date), + '$invoiceDate' => $account->formatDate($invoice->invoice_date), + '$contact' => $invitation->contact->getDisplayName(), + '$firstName' => $invitation->contact->first_name, + '$amount' => $account->formatMoney($data['amount'], $client), + '$invoice' => $invoice->invoice_number, + '$quote' => $invoice->invoice_number, + '$link' => $invitation->getLink(), + '$password' => $passwordHTML, + '$viewLink' => $invitation->getLink().'$password', + '$viewButton' => Form::emailViewButton($invitation->getLink(), $invoice->getEntityType()).'$password', + '$paymentLink' => $invitation->getLink('payment').'$password', + '$paymentButton' => Form::emailPaymentButton($invitation->getLink('payment')).'$password', + '$customClient1' => $account->custom_client_label1, + '$customClient2' => $account->custom_client_label2, + '$customInvoice1' => $account->custom_invoice_text_label1, + '$customInvoice2' => $account->custom_invoice_text_label2, + '$documents' => $documentsHTML, + ]; + + // Add variables for available payment types + foreach (Gateway::$paymentTypes as $type) { + $camelType = Gateway::getPaymentTypeName($type); + $type = Utils::toSnakeCase($camelType); + $variables["\${$camelType}Link"] = $invitation->getLink('payment') . "/{$type}"; + $variables["\${$camelType}Button"] = Form::emailPaymentButton($invitation->getLink('payment') . "/{$type}"); + } + + $includesPasswordPlaceholder = strpos($template, '$password') !== false; + + $str = str_replace(array_keys($variables), array_values($variables), $template); + + if (!$includesPasswordPlaceholder && $passwordHTML) { + $pos = strrpos($str, '$password'); + if ($pos !== false) + { + $str = substr_replace($str, $passwordHTML, $pos, 9/* length of "$password" */); + } + } + $str = str_replace('$password', '', $str); + $str = autolink($str, 100); + + return $str; + } +} \ No newline at end of file diff --git a/app/Services/TokenService.php b/app/Services/TokenService.php index 8b428f0eb199..092f3995d3d7 100644 --- a/app/Services/TokenService.php +++ b/app/Services/TokenService.php @@ -27,9 +27,9 @@ class TokenService extends BaseService } */ - public function getDatatable($accountId) + public function getDatatable($userId) { - $query = $this->tokenRepo->find($accountId); + $query = $this->tokenRepo->find($userId); return $this->createDatatable(ENTITY_TOKEN, $query, false); } diff --git a/app/Services/VendorService.php b/app/Services/VendorService.php index 6022507b452e..41f5fd4664bb 100644 --- a/app/Services/VendorService.php +++ b/app/Services/VendorService.php @@ -26,13 +26,13 @@ class VendorService extends BaseService return $this->vendorRepo; } - public function save($data) + public function save($data, $vendor = null) { if (Auth::user()->account->isNinjaAccount() && isset($data['plan'])) { $this->ninjaRepo->updatePlanDetails($data['public_id'], $data); } - return $this->vendorRepo->save($data); + return $this->vendorRepo->save($data, $vendor); } public function getDatatable($search) diff --git a/database/factories/ModelFactory.php b/database/factories/ModelFactory.php new file mode 100644 index 000000000000..018da7b2357b --- /dev/null +++ b/database/factories/ModelFactory.php @@ -0,0 +1,48 @@ +define(Contact::class, function (Faker\Generator $faker) { + return [ + 'client_id' => function() { + return factory(Client::class)->create()->id; + }, + 'user_id' => 1, + 'account_id' => 1, + 'public_id' => Contact::count() + 1, + 'is_primary' => true, + 'send_invoice' => true, + 'first_name' => $faker->firstName, + 'last_name' => $faker->lastName, + 'email' => $faker->safeEmail, + 'phone' => $faker->phoneNumber, + ]; +}); + +$factory->define(Client::class, function (Faker\Generator $faker) { + return [ + 'user_id' => 1, + 'account_id' => 1, + 'public_id' => Client::count() + 1, + 'name' => $faker->name, + 'address1' => $faker->streetAddress, + 'address2' => $faker->secondaryAddress, + 'city' => $faker->city, + 'state' => $faker->state, + 'postal_code' => $faker->postcode, + 'country_id' => Country::all()->random()->id, + ]; +}); \ No newline at end of file diff --git a/database/migrations/2016_04_16_103943_enterprise_plan.php b/database/migrations/2016_04_16_103943_enterprise_plan.php index 8ca5632ad358..8a3a63717367 100644 --- a/database/migrations/2016_04_16_103943_enterprise_plan.php +++ b/database/migrations/2016_04_16_103943_enterprise_plan.php @@ -127,13 +127,24 @@ class EnterprisePlan extends Migration $company->plan_started = $primaryAccount->pro_plan_paid; $company->plan_paid = $primaryAccount->pro_plan_paid; + $expires = DateTime::createFromFormat('Y-m-d', $primaryAccount->pro_plan_paid); + $expires->modify('+1 year'); + $expires = $expires->format('Y-m-d'); + + // check for self host white label licenses if (!Utils::isNinjaProd()) { - $company->plan = 'white_label'; - $company->plan_term = null; - } elseif ($company->plan_paid != '2000-01-01'/* NINJA_DATE*/) { - $expires = DateTime::createFromFormat('Y-m-d', $primaryAccount->pro_plan_paid); - $expires->modify('+1 year'); - $company->plan_expires = $expires->format('Y-m-d'); + if ($company->plan_paid) { + $company->plan = 'white_label'; + // old ones were unlimited, new ones are yearly + if ($company->plan_paid == NINJA_DATE) { + $company->plan_term = null; + } else { + $company->plan_term = PLAN_TERM_YEARLY; + $company->plan_expires = $expires; + } + } + } elseif ($company->plan_paid != NINJA_DATE) { + $company->plan_expires = $expires; } } diff --git a/database/seeds/CurrenciesSeeder.php b/database/seeds/CurrenciesSeeder.php index b3da84dad01d..3b44d55d8d7f 100644 --- a/database/seeds/CurrenciesSeeder.php +++ b/database/seeds/CurrenciesSeeder.php @@ -8,6 +8,7 @@ class CurrenciesSeeder extends Seeder { Eloquent::unguard(); + // http://www.localeplanet.com/icu/currency.html $currencies = [ ['name' => 'US Dollar', 'code' => 'USD', 'symbol' => '$', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'], ['name' => 'Pound Sterling', 'code' => 'GBP', 'symbol' => '£', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'], @@ -56,6 +57,7 @@ class CurrenciesSeeder extends Seeder ['name' => 'Japanese Yen', 'code' => 'JPY', 'symbol' => '¥', 'precision' => '0', 'thousand_separator' => ',', 'decimal_separator' => '.'], ['name' => 'Maldivian Rufiyaa', 'code' => 'MVR', 'symbol' => '', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'], ['name' => 'Costa Rican Colón', 'code' => 'CRC', 'symbol' => '', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'], + ['name' => 'Pakistani Rupee', 'code' => 'PKR', 'symbol' => 'Rs ', 'precision' => '0', 'thousand_separator' => ',', 'decimal_separator' => '.'], ]; foreach ($currencies as $currency) { diff --git a/database/seeds/DatabaseSeeder.php b/database/seeds/DatabaseSeeder.php index 6117c22e007a..1abaa76b64a8 100644 --- a/database/seeds/DatabaseSeeder.php +++ b/database/seeds/DatabaseSeeder.php @@ -25,5 +25,6 @@ class DatabaseSeeder extends Seeder $this->call('InvoiceDesignsSeeder'); $this->call('PaymentTermsSeeder'); $this->call('PaymentTypesSeeder'); + $this->call('LanguageSeeder'); } } diff --git a/database/seeds/LanguageSeeder.php b/database/seeds/LanguageSeeder.php new file mode 100644 index 000000000000..5fc9569d5cef --- /dev/null +++ b/database/seeds/LanguageSeeder.php @@ -0,0 +1,42 @@ + 'English', 'locale' => 'en'], + ['name' => 'Italian', 'locale' => 'it'], + ['name' => 'German', 'locale' => 'de'], + ['name' => 'French', 'locale' => 'fr'], + ['name' => 'Brazilian Portuguese', 'locale' => 'pt_BR'], + ['name' => 'Dutch', 'locale' => 'nl'], + ['name' => 'Spanish', 'locale' => 'es'], + ['name' => 'Norwegian', 'locale' => 'nb_NO'], + ['name' => 'Danish', 'locale' => 'da'], + ['name' => 'Japanese', 'locale' => 'ja'], + ['name' => 'Swedish', 'locale' => 'sv'], + ['name' => 'Spanish - Spain', 'locale' => 'es_ES'], + ['name' => 'French - Canada', 'locale' => 'fr_CA'], + ['name' => 'Lithuanian', 'locale' => 'lt'], + ['name' => 'Polish', 'locale' => 'pl'], + ['name' => 'Czech', 'locale' => 'cs'], + ]; + + foreach ($languages as $language) { + $record = Language::whereLocale($language['locale'])->first(); + if ($record) { + $record->name = $language['name']; + $record->save(); + } else { + Language::create($language); + } + } + + Eloquent::reguard(); + } +} diff --git a/database/seeds/UpdateSeeder.php b/database/seeds/UpdateSeeder.php index 5cd784a1ec8d..b407a7ba2e63 100644 --- a/database/seeds/UpdateSeeder.php +++ b/database/seeds/UpdateSeeder.php @@ -21,5 +21,6 @@ class UpdateSeeder extends Seeder $this->call('InvoiceDesignsSeeder'); $this->call('PaymentTermsSeeder'); $this->call('PaymentTypesSeeder'); + $this->call('LanguageSeeder'); } } diff --git a/database/seeds/UserTableSeeder.php b/database/seeds/UserTableSeeder.php index 1ec659bf389e..dbaba660b75c 100644 --- a/database/seeds/UserTableSeeder.php +++ b/database/seeds/UserTableSeeder.php @@ -1,9 +1,13 @@ 'Test Account', + 'name' => $faker->name, + 'address1' => $faker->streetAddress, + 'address2' => $faker->secondaryAddress, + 'city' => $faker->city, + 'state' => $faker->state, + 'postal_code' => $faker->postcode, + 'country_id' => Country::all()->random()->id, 'account_key' => str_random(RANDOM_KEY_LENGTH), + 'invoice_terms' => $faker->text($faker->numberBetween(50, 300)), + 'work_phone' => $faker->phoneNumber, + 'work_email' => $faker->safeEmail, + 'invoice_design_id' => min(InvoiceDesign::all()->random()->id, 10), + 'header_font_id' => min(Font::all()->random()->id, 17), + 'body_font_id' => min(Font::all()->random()->id, 17), + 'primary_color' => $faker->hexcolor, 'timezone_id' => 1, 'company_id' => $company->id, ]); @@ -30,6 +48,8 @@ class UserTableSeeder extends Seeder 'password' => Hash::make(TEST_PASSWORD), 'registered' => true, 'confirmed' => true, + 'notify_sent' => false, + 'notify_paid' => false, ]); Affiliate::create([ diff --git a/public/built.js b/public/built.js index bc5272ed1861..14f82399435f 100644 --- a/public/built.js +++ b/public/built.js @@ -30988,7 +30988,7 @@ function searchData(data, key, fuzzy) { matches = fuse.search(q); } else { matches = []; - substrRegex = new RegExp(q, 'i'); + substrRegex = new RegExp(escapeRegExp(q), 'i'); $.each(data, function(i, obj) { if (substrRegex.test(obj[key])) { matches.push(obj); @@ -30999,6 +30999,9 @@ function searchData(data, key, fuzzy) { } }; +function escapeRegExp(str) { + return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); +} var NINJA = NINJA || {}; NINJA.TEMPLATES = { @@ -31193,7 +31196,7 @@ NINJA.decodeJavascript = function(invoice, javascript) field = toSnakeCase(field); var value = getDescendantProp(invoice, field); if (match.indexOf('?') < 0 || value) { - if (invoice.partial && field == 'balance_due') { + if (invoice.partial > 0 && field == 'balance_due') { field = 'partial_due'; } else if (invoice.is_quote) { field = field.replace('invoice', 'quote'); @@ -31248,13 +31251,13 @@ NINJA.notesAndTerms = function(invoice) var data = []; if (invoice.public_notes) { - data.push({stack:[{text: invoice.public_notes, style: ['notes']}]}); + data.push({stack:[{text: invoice.is_recurring ? processVariables(invoice.public_notes) : invoice.public_notes, style: ['notes']}]}); data.push({text:' '}); } if (invoice.terms) { data.push({text:invoiceLabels.terms, style: ['termsLabel']}); - data.push({stack:[{text: invoice.terms, style: ['terms']}]}); + data.push({stack:[{text: invoice.is_recurring ? processVariables(invoice.terms) : invoice.terms, style: ['terms']}]}); } return NINJA.prepareDataList(data, 'notesAndTerms'); @@ -31280,7 +31283,7 @@ NINJA.invoiceColumns = function(invoice) var count = 3; if (account.hide_quantity == '1') { - count--; + count -= 2; } if (account.show_item_taxes == '1') { count++; @@ -31294,10 +31297,16 @@ NINJA.invoiceColumns = function(invoice) NINJA.invoiceFooter = function(invoice) { + var footer = invoice.invoice_footer; + + if (invoice.is_recurring) { + footer = processVariables(footer); + } + if (!invoice.features.invoice_settings && invoice.invoice_design_id == 3) { - return invoice.invoice_footer ? invoice.invoice_footer.substring(0, 200) : ' '; + return footer ? footer.substring(0, 200) : ' '; } else { - return invoice.invoice_footer || ' '; + return footer || ' '; } } @@ -31331,11 +31340,10 @@ NINJA.invoiceLines = function(invoice) { } if (invoice.features.invoice_ettings && account.custom_invoice_item_label2) { grid[0].push({text: account.custom_invoice_item_label2, style: ['tableHeader', 'custom2TableHeader']}); - } - - grid[0].push({text: invoiceLabels.unit_cost, style: ['tableHeader', 'costTableHeader']}); + } if (!hideQuantity) { + grid[0].push({text: invoiceLabels.unit_cost, style: ['tableHeader', 'costTableHeader']}); grid[0].push({text: invoiceLabels.quantity, style: ['tableHeader', 'qtyTableHeader']}); } if (showItemTaxes) { @@ -31353,7 +31361,9 @@ NINJA.invoiceLines = function(invoice) { var notes = item.notes; var productKey = item.product_key; var tax1 = ''; - var tax2 = ''; + var tax2 = ''; + var custom_value1 = item.custom_value1; + var custom_value2 = item.custom_value2; if (showItemTaxes) { if (item.tax_name1) { @@ -31375,6 +31385,8 @@ NINJA.invoiceLines = function(invoice) { if (invoice.is_recurring) { notes = processVariables(notes); productKey = processVariables(productKey); + custom_value1 = processVariables(item.custom_value1); + custom_value2 = processVariables(item.custom_value2); } var lineTotal = roundToTwo(NINJA.parseFloat(item.cost)) * roundToTwo(NINJA.parseFloat(item.qty)); @@ -31387,13 +31399,13 @@ NINJA.invoiceLines = function(invoice) { } row.push({style:["notes", rowStyle], stack:[{text:notes || ' '}]}); if (invoice.features.invoice_settings && account.custom_invoice_item_label1) { - row.push({style:["customValue1", rowStyle], text:item.custom_value1 || ' '}); + row.push({style:["customValue1", rowStyle], text:custom_value1 || ' '}); } if (invoice.features.invoice_settings && account.custom_invoice_item_label2) { - row.push({style:["customValue2", rowStyle], text:item.custom_value2 || ' '}); + row.push({style:["customValue2", rowStyle], text:custom_value2 || ' '}); } - row.push({style:["cost", rowStyle], text:cost}); if (!hideQuantity) { + row.push({style:["cost", rowStyle], text:cost}); row.push({style:["quantity", rowStyle], text:qty || ' '}); } if (showItemTaxes) { @@ -31581,42 +31593,35 @@ NINJA.invoiceDetails = function(invoice) { ], [ {text: (invoice.is_quote ? invoiceLabels.valid_until : invoiceLabels.due_date)}, - {text: invoice.due_date} + {text: invoice.is_recurring ? false : invoice.due_date} ] ]; if (invoice.custom_text_value1) { data.push([ {text: invoice.account.custom_invoice_text_label1}, - {text: invoice.custom_text_value1} + {text: invoice.is_recurring ? processVariables(invoice.custom_text_value1) : invoice.custom_text_value1} ]) } if (invoice.custom_text_value2) { data.push([ {text: invoice.account.custom_invoice_text_label2}, - {text: invoice.custom_text_value2} + {text: invoice.is_recurring ? processVariables(invoice.custom_text_value2) : invoice.custom_text_value2} ]) } - var isPartial = NINJA.parseFloat(invoice.partial); - - if (NINJA.parseFloat(invoice.balance) < NINJA.parseFloat(invoice.amount)) { - data.push([ - {text: invoiceLabels.balance_due}, - {text: formatMoneyInvoice(invoice.amount, invoice)} - ]); - } else if (isPartial) { - data.push([ - {text: invoiceLabels.balance_due}, - {text: formatMoneyInvoice(invoice.total_amount, invoice)} - ]); - } - data.push([ - {text: isPartial ? invoiceLabels.partial_due : invoiceLabels.balance_due, style: ['invoiceDetailBalanceDueLabel']}, - {text: formatMoneyInvoice(invoice.balance_amount, invoice), style: ['invoiceDetailBalanceDue']} + {text: invoiceLabels.balance_due, style: ['invoiceDetailBalanceDueLabel']}, + {text: formatMoneyInvoice(invoice.total_amount, invoice), style: ['invoiceDetailBalanceDue']} ]) + if (NINJA.parseFloat(invoice.partial)) { + data.push([ + {text: invoiceLabels.partial_due, style: ['invoiceDetailBalanceDueLabel']}, + {text: formatMoneyInvoice(invoice.balance_amount, invoice), style: ['invoiceDetailBalanceDue']} + ]) + } + return NINJA.prepareDataPairs(data, 'invoiceDetails'); } diff --git a/public/css/built.css b/public/css/built.css index 6e23e0c29739..c3cebdae86ff 100644 --- a/public/css/built.css +++ b/public/css/built.css @@ -2539,6 +2539,10 @@ ul.dropdown-menu, box-shadow: 0 0 10px 2px rgba(0,0,0,.05); } +.twitter-typeahead .tt-menu { + overflow-x: hidden; +} + .panel-default, canvas { border: 1px solid; diff --git a/public/css/style.css b/public/css/style.css index 0ac30b8a4694..801141c03cc9 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -410,6 +410,10 @@ ul.dropdown-menu, box-shadow: 0 0 10px 2px rgba(0,0,0,.05); } +.twitter-typeahead .tt-menu { + overflow-x: hidden; +} + .panel-default, canvas { border: 1px solid; diff --git a/public/js/pdf.pdfmake.js b/public/js/pdf.pdfmake.js index 1e5d1e93b3f8..8adf2e642481 100644 --- a/public/js/pdf.pdfmake.js +++ b/public/js/pdf.pdfmake.js @@ -192,7 +192,7 @@ NINJA.decodeJavascript = function(invoice, javascript) field = toSnakeCase(field); var value = getDescendantProp(invoice, field); if (match.indexOf('?') < 0 || value) { - if (invoice.partial && field == 'balance_due') { + if (invoice.partial > 0 && field == 'balance_due') { field = 'partial_due'; } else if (invoice.is_quote) { field = field.replace('invoice', 'quote'); @@ -247,13 +247,13 @@ NINJA.notesAndTerms = function(invoice) var data = []; if (invoice.public_notes) { - data.push({stack:[{text: invoice.public_notes, style: ['notes']}]}); + data.push({stack:[{text: invoice.is_recurring ? processVariables(invoice.public_notes) : invoice.public_notes, style: ['notes']}]}); data.push({text:' '}); } if (invoice.terms) { data.push({text:invoiceLabels.terms, style: ['termsLabel']}); - data.push({stack:[{text: invoice.terms, style: ['terms']}]}); + data.push({stack:[{text: invoice.is_recurring ? processVariables(invoice.terms) : invoice.terms, style: ['terms']}]}); } return NINJA.prepareDataList(data, 'notesAndTerms'); @@ -279,7 +279,7 @@ NINJA.invoiceColumns = function(invoice) var count = 3; if (account.hide_quantity == '1') { - count--; + count -= 2; } if (account.show_item_taxes == '1') { count++; @@ -293,10 +293,16 @@ NINJA.invoiceColumns = function(invoice) NINJA.invoiceFooter = function(invoice) { + var footer = invoice.invoice_footer; + + if (invoice.is_recurring) { + footer = processVariables(footer); + } + if (!invoice.features.invoice_settings && invoice.invoice_design_id == 3) { - return invoice.invoice_footer ? invoice.invoice_footer.substring(0, 200) : ' '; + return footer ? footer.substring(0, 200) : ' '; } else { - return invoice.invoice_footer || ' '; + return footer || ' '; } } @@ -330,11 +336,10 @@ NINJA.invoiceLines = function(invoice) { } if (invoice.features.invoice_ettings && account.custom_invoice_item_label2) { grid[0].push({text: account.custom_invoice_item_label2, style: ['tableHeader', 'custom2TableHeader']}); - } - - grid[0].push({text: invoiceLabels.unit_cost, style: ['tableHeader', 'costTableHeader']}); + } if (!hideQuantity) { + grid[0].push({text: invoiceLabels.unit_cost, style: ['tableHeader', 'costTableHeader']}); grid[0].push({text: invoiceLabels.quantity, style: ['tableHeader', 'qtyTableHeader']}); } if (showItemTaxes) { @@ -352,7 +357,9 @@ NINJA.invoiceLines = function(invoice) { var notes = item.notes; var productKey = item.product_key; var tax1 = ''; - var tax2 = ''; + var tax2 = ''; + var custom_value1 = item.custom_value1; + var custom_value2 = item.custom_value2; if (showItemTaxes) { if (item.tax_name1) { @@ -374,6 +381,8 @@ NINJA.invoiceLines = function(invoice) { if (invoice.is_recurring) { notes = processVariables(notes); productKey = processVariables(productKey); + custom_value1 = processVariables(item.custom_value1); + custom_value2 = processVariables(item.custom_value2); } var lineTotal = roundToTwo(NINJA.parseFloat(item.cost)) * roundToTwo(NINJA.parseFloat(item.qty)); @@ -386,13 +395,13 @@ NINJA.invoiceLines = function(invoice) { } row.push({style:["notes", rowStyle], stack:[{text:notes || ' '}]}); if (invoice.features.invoice_settings && account.custom_invoice_item_label1) { - row.push({style:["customValue1", rowStyle], text:item.custom_value1 || ' '}); + row.push({style:["customValue1", rowStyle], text:custom_value1 || ' '}); } if (invoice.features.invoice_settings && account.custom_invoice_item_label2) { - row.push({style:["customValue2", rowStyle], text:item.custom_value2 || ' '}); + row.push({style:["customValue2", rowStyle], text:custom_value2 || ' '}); } - row.push({style:["cost", rowStyle], text:cost}); if (!hideQuantity) { + row.push({style:["cost", rowStyle], text:cost}); row.push({style:["quantity", rowStyle], text:qty || ' '}); } if (showItemTaxes) { @@ -580,42 +589,35 @@ NINJA.invoiceDetails = function(invoice) { ], [ {text: (invoice.is_quote ? invoiceLabels.valid_until : invoiceLabels.due_date)}, - {text: invoice.due_date} + {text: invoice.is_recurring ? false : invoice.due_date} ] ]; if (invoice.custom_text_value1) { data.push([ {text: invoice.account.custom_invoice_text_label1}, - {text: invoice.custom_text_value1} + {text: invoice.is_recurring ? processVariables(invoice.custom_text_value1) : invoice.custom_text_value1} ]) } if (invoice.custom_text_value2) { data.push([ {text: invoice.account.custom_invoice_text_label2}, - {text: invoice.custom_text_value2} + {text: invoice.is_recurring ? processVariables(invoice.custom_text_value2) : invoice.custom_text_value2} ]) } - var isPartial = NINJA.parseFloat(invoice.partial); - - if (NINJA.parseFloat(invoice.balance) < NINJA.parseFloat(invoice.amount)) { - data.push([ - {text: invoiceLabels.balance_due}, - {text: formatMoneyInvoice(invoice.amount, invoice)} - ]); - } else if (isPartial) { - data.push([ - {text: invoiceLabels.balance_due}, - {text: formatMoneyInvoice(invoice.total_amount, invoice)} - ]); - } - data.push([ - {text: isPartial ? invoiceLabels.partial_due : invoiceLabels.balance_due, style: ['invoiceDetailBalanceDueLabel']}, - {text: formatMoneyInvoice(invoice.balance_amount, invoice), style: ['invoiceDetailBalanceDue']} + {text: invoiceLabels.balance_due, style: ['invoiceDetailBalanceDueLabel']}, + {text: formatMoneyInvoice(invoice.total_amount, invoice), style: ['invoiceDetailBalanceDue']} ]) + if (NINJA.parseFloat(invoice.partial)) { + data.push([ + {text: invoiceLabels.partial_due, style: ['invoiceDetailBalanceDueLabel']}, + {text: formatMoneyInvoice(invoice.balance_amount, invoice), style: ['invoiceDetailBalanceDue']} + ]) + } + return NINJA.prepareDataPairs(data, 'invoiceDetails'); } diff --git a/public/js/script.js b/public/js/script.js index 8b53e500ef64..a76e6d37cd0e 100644 --- a/public/js/script.js +++ b/public/js/script.js @@ -1097,7 +1097,7 @@ function searchData(data, key, fuzzy) { matches = fuse.search(q); } else { matches = []; - substrRegex = new RegExp(q, 'i'); + substrRegex = new RegExp(escapeRegExp(q), 'i'); $.each(data, function(i, obj) { if (substrRegex.test(obj[key])) { matches.push(obj); @@ -1107,3 +1107,7 @@ function searchData(data, key, fuzzy) { cb(matches); } }; + +function escapeRegExp(str) { + return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); +} \ No newline at end of file diff --git a/readme.md b/readme.md index 8a5483584427..93b30a8003d4 100644 --- a/readme.md +++ b/readme.md @@ -3,7 +3,7 @@

# Invoice Ninja -### [https://www.invoiceninja.com](https://www.invoiceninja.com) +### [http://www.invoiceninja.org](http://www.invoiceninja.org) [![Build Status](https://travis-ci.org/invoiceninja/invoiceninja.svg?branch=develop)](https://travis-ci.org/invoiceninja/invoiceninja) [![Join the chat at https://gitter.im/hillelcoren/invoice-ninja](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/hillelcoren/invoice-ninja?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) diff --git a/resources/lang/cs/auth.php b/resources/lang/cs/auth.php new file mode 100644 index 000000000000..aec1bc4c0596 --- /dev/null +++ b/resources/lang/cs/auth.php @@ -0,0 +1,19 @@ + 'Tyto přihlašovací údajě neodpovídají žadnému záznamu.', + 'throttle' => 'Příliš mnoho pokusů o přihlášení. Zkuste to prosím znovu za :seconds vteřin.', + +]; diff --git a/resources/lang/cs/pagination.php b/resources/lang/cs/pagination.php new file mode 100644 index 000000000000..0931d77fabfb --- /dev/null +++ b/resources/lang/cs/pagination.php @@ -0,0 +1,19 @@ + '« předchozí', + 'next' => 'další »', + +]; diff --git a/resources/lang/cs/passwords.php b/resources/lang/cs/passwords.php new file mode 100644 index 000000000000..271d609e96da --- /dev/null +++ b/resources/lang/cs/passwords.php @@ -0,0 +1,22 @@ + 'Heslo musí obsahovat alespoň 6 znaků a musí odpovídat.', + 'reset' => 'Heslo bylo obnoveno!', + 'sent' => 'Upomínka ke změně hesla byla odeslána!', + 'token' => 'Klíč pro obnovu hesla je nesprávný.', + 'user' => 'Nepodařilo se najít uživatele s touto e-mailovou adresou.', + +]; diff --git a/resources/lang/cs/texts.php b/resources/lang/cs/texts.php new file mode 100644 index 000000000000..5ef292cdc179 --- /dev/null +++ b/resources/lang/cs/texts.php @@ -0,0 +1,1106 @@ + 'Organizace', + 'name' => 'Jméno', + 'website' => 'Web', + 'work_phone' => 'Telefon', + 'address' => 'Adresa', + 'address1' => 'Ulice', + 'address2' => 'Pokoj', + 'city' => 'Město', + 'state' => 'Oblast', + 'postal_code' => 'PSČ', + 'country_id' => 'Země', + 'contacts' => 'Kontakty', + 'first_name' => 'Jméno', + 'last_name' => 'Příjmení', + 'phone' => 'Telefon', + 'email' => 'Email', + 'additional_info' => 'Další info', + 'payment_terms' => 'Platební podmínky', + 'currency_id' => 'Měna', + 'size_id' => 'Velikost firmy', + 'industry_id' => 'Odvětví', + 'private_notes' => 'Soukromé poznámky', + 'invoice' => 'Faktura', + 'client' => 'Klient', + 'invoice_date' => 'Datum vystavení', + 'due_date' => 'Datum splatnosti', + 'invoice_number' => 'Číslo faktury', + 'invoice_number_short' => 'Faktura #', + 'po_number' => 'PO číslo', + 'po_number_short' => 'PO #', + 'frequency_id' => 'Jak často', + 'discount' => 'Sleva', + 'taxes' => 'Daně', + 'tax' => 'Daň', + 'item' => 'Položka', + 'description' => 'Popis', + 'unit_cost' => 'Jednotková cena', + 'quantity' => 'Množství', + 'line_total' => 'Celkem', + 'subtotal' => 'Mezisoučet', + 'paid_to_date' => 'Zaplaceno ke dni', + 'balance_due' => 'Zbývající zůstatek', + 'invoice_design_id' => 'Design', + 'terms' => 'Podmínky', + 'your_invoice' => 'Vaše faktura', + 'remove_contact' => 'Odstranit kontakt', + 'add_contact' => 'Přidat kontakt', + 'create_new_client' => 'Vytvořit nového klienta', + 'edit_client_details' => 'Editovat detaily klienta', + 'enable' => 'Umožnit', + 'learn_more' => 'Viz více', + 'manage_rates' => 'Spravovat sazby', + 'note_to_client' => 'Poznámka ke klientovi', + 'invoice_terms' => 'Fakturační podmínky', + 'save_as_default_terms' => 'Uložit jako výchozí podmínky', + 'download_pdf' => 'Stáhnout PDF', + 'pay_now' => 'Zaplatit nyní', + 'save_invoice' => 'Uložit fakturu', + 'clone_invoice' => 'Zduplikovat fakturu', + 'archive_invoice' => 'Archivovat fakturu', + 'delete_invoice' => 'Smazat fakturu', + 'email_invoice' => 'Poslat fakturu emailem', + 'enter_payment' => 'Zadat platbu', + 'tax_rates' => 'Sazby daně', + 'rate' => 'Sazba', + 'settings' => 'Nastavení', + 'enable_invoice_tax' => 'Umožnit nastavit daň na faktuře', + 'enable_line_item_tax' => 'Umožnit nastavitdaně u položek', + 'dashboard' => 'Dashboard', + 'clients' => 'Klienti', + 'invoices' => 'Faktury', + 'payments' => 'Platby', + 'credits' => 'Kredity', + 'history' => 'Historie', + 'search' => 'Vyhledat', + 'sign_up' => 'Zaregistrovat se', + 'guest' => 'Host', + 'company_details' => 'Detaily firmy', + 'online_payments' => 'Online platby', + 'notifications' => 'Emailové notifikace', + 'import_export' => 'Import | Export | Zrušit', + 'done' => 'Hotovo', + 'save' => 'Uložit', + 'create' => 'Vytvořit', + 'upload' => 'Nahrát', + 'import' => 'Import', + 'download' => 'Stažení', + 'cancel' => 'Zrušení', + 'close' => 'Zavřít', + 'provide_email' => 'Prosím zadejte platnou adresu', + 'powered_by' => 'Vytvořeno', + 'no_items' => 'Žádné položky', + 'recurring_invoices' => 'Pravidelné faktury', + 'recurring_help' => '

Posílejte klientům stejné faktury týdně, dvakrát za měsíc, měsíčně, čtvrtletně nebo ročně.

+

Použijte :MONTH, :QUARTER or :YEAR pro dynamicky měnící se datumy. Funguje zde i základní matematika například, jako :MONTH-1.

+

Příklady dynamických proměnných na faktuře:

+ ', + 'in_total_revenue' => 'v celkových příjmech', + 'billed_client' => 'klient s fakturací', + 'billed_clients' => 'klienti s fakturací', + 'active_client' => 'aktivní klient', + 'active_clients' => 'aktivní klienti', + 'invoices_past_due' => 'Faktury po splatnosti', + 'upcoming_invoices' => 'Budoucí faktury', + 'average_invoice' => 'Průměrná faktura', + 'archive' => 'Archivovat', + 'delete' => 'Smazat', + 'archive_client' => 'Archivovat klienta', + 'delete_client' => 'Smazat klienta', + 'archive_payment' => 'Archivovat platbu', + 'delete_payment' => 'Smazat platbu', + 'archive_credit' => 'Archivovat kredit', + 'delete_credit' => 'Smazat kredit', + 'show_archived_deleted' => 'Zobrazit archivované/smazané', + 'filter' => 'Filtr', + 'new_client' => 'Nový klient', + 'new_invoice' => 'Nová faktura', + 'new_payment' => 'Nová platba', + 'new_credit' => 'Nový kredit', + 'contact' => 'Kontakt', + 'date_created' => 'Datum vytvoření', + 'last_login' => 'Poslední přihlášení', + 'balance' => 'Zůstatek', + 'action' => 'Akce', + 'status' => 'Status', + 'invoice_total' => 'Faktury celkem', + 'frequency' => 'Frekvence', + 'start_date' => 'Datum počátku', + 'end_date' => 'Datum konce', + 'transaction_reference' => 'Odkaz na transakci', + 'method' => 'Metoda', + 'payment_amount' => 'Částka k platbě', + 'payment_date' => 'Datum platby', + 'credit_amount' => 'Částka kreditu', + 'credit_balance' => 'Zůstatek kreditu', + 'credit_date' => 'Datum kreditu', + 'empty_table' => 'Nejsou dostupná žádná data', + 'select' => 'Zvolit', + 'edit_client' => 'Editovat klienta', + 'edit_invoice' => 'Editovat fakturu', + 'create_invoice' => 'Vytvořit fakturu', + 'enter_credit' => 'Zadat kredit', + 'last_logged_in' => 'Poslední přihlášení', + 'details' => 'Detaily', + 'standing' => 'Trvající', + 'credit' => 'Kredit', + 'activity' => 'Aktivity', + 'date' => 'Datum', + 'message' => 'Vzkaz', + 'adjustment' => 'Úprava', + 'are_you_sure' => 'Jste si jisti?', + 'payment_type_id' => 'Typ platby', + 'amount' => 'Částka', + 'work_email' => 'Email', + 'language_id' => 'Jazyk', + 'timezone_id' => 'Časová zóna', + 'date_format_id' => 'Formát datumu', + 'datetime_format_id' => 'Formátu času a datumu', + 'users' => 'Uživatelé', + 'localization' => 'Lokalizace', + 'remove_logo' => 'Odstranit logo', + 'logo_help' => 'Podporujeme: JPEG, GIF a PNG', + 'payment_gateway' => 'Platební brána', + 'gateway_id' => 'brána', + 'email_notifications' => 'Emailové notifikace', + 'email_sent' => 'Odeslat email pokud je faktura odeslaná', + 'email_viewed' => 'Odeslat email pokud je faktura prohlédnuta', + 'email_paid' => 'Odeslat email pokud je faktura zaplacena', + 'site_updates' => 'Změny na webu', + 'custom_messages' => 'Volitelné vzkazy', + 'default_email_footer' => 'Nastavit výchozí podpis do emailu', + 'select_file' => 'Prosím zvolte soubor', + 'first_row_headers' => 'Použít první řádku jako záhlaví', + 'column' => 'Sloupec', + 'sample' => 'Vzorek', + 'import_to' => 'Importovat do', + 'client_will_create' => 'klient bude vytvořen', + 'clients_will_create' => 'klienti budou vytvořeni', + 'email_settings' => 'Nastavení emailu', + 'client_view_styling' => 'Úprava klientského zobrazení', + 'pdf_email_attachment' => 'Připojit PDF', + 'custom_css' => 'Volitelné CSS', + 'import_clients' => 'Importovat klientská data', + 'csv_file' => 'CSV soubor', + 'export_clients' => 'Exportovat klienty', + 'created_client' => 'Úspěšně vytvořen klient', + 'created_clients' => 'Úspěšně vytvořeno :count klientů', + 'updated_settings' => 'Úspěšně změněno nastavení', + 'removed_logo' => 'Úspěšně odtraněno logo', + 'sent_message' => 'Úspěšně odeslán vzkaz', + 'invoice_error' => 'Ujistěte se, že máte zvoleného klienta a opravte případné chyby', + 'limit_clients' => 'Omlouváme se , to přesahuje limit :count počtu klientů', + 'payment_error' => 'Nastala chyba během zpracování Vaší platby. Zkuste to prosím později znovu.', + 'registration_required' => 'Pro odeslání faktury se zeregistrujte', + 'confirmation_required' => 'Prosím potvrďte vaši emailovou adresu', + 'updated_client' => 'Úspěšně změněn klient', + 'created_client' => 'Úspěšně vytvořen klient', + 'archived_client' => 'Úspěšně archivován klient', + 'archived_clients' => 'Úspěšně archivováno :count klientů', + 'deleted_client' => 'Úspěšně smazán klient', + 'deleted_clients' => 'Úspěšně smazáno :count klientů', + 'updated_invoice' => 'Úspěšně změněna faktura', + 'created_invoice' => 'Úspěšně vytvořena faktura', + 'cloned_invoice' => 'Úspěšně zduplikována faktura', + 'emailed_invoice' => 'Úspěšně odeslána faktura', + 'and_created_client' => 'a vytvořen klient', + 'archived_invoice' => 'Úspěšně archivována faktura', + 'archived_invoices' => 'Úspěšně archivováno :count faktur', + 'deleted_invoice' => 'Úspěšně smazána faktura', + 'deleted_invoices' => 'Úspěšně smazáno :count faktur', + 'created_payment' => 'Úspěšně vytvořena platba', + 'created_payments' => 'Úspěšně vytvořeno :count plateb', + 'archived_payment' => 'Úspěšně archivována platba', + 'archived_payments' => 'Úspěšně archivováno :count plateb', + 'deleted_payment' => 'Úspěšně smazána platba', + 'deleted_payments' => 'Úspěšně smazáno :count plateb', + 'applied_payment' => 'Úspěšně aplikována platba', + 'created_credit' => 'Úspěšně vytvořen kredit', + 'archived_credit' => 'Úspěšně archivován kredit', + 'archived_credits' => 'Úspěšně archivován :count kreditů', + 'deleted_credit' => 'Úspěšně smazán kredit', + 'deleted_credits' => 'Úspěšně smazáno :count kreditů', + 'imported_file' => 'Úspěšně importován soubor', + 'updated_vendor' => 'Úspěšně změněn dodavatel', + 'created_vendor' => 'Úspěšně vytvořen dodavatel', + 'archived_vendor' => 'Úspěšně archivován dodavatel', + 'archived_vendors' => 'Úspěšně archivováno :count dodavatelů', + 'deleted_vendor' => 'Úspěšně smazán dodavatel', + 'deleted_vendors' => 'Úspěšně smazán :count dodavatel', + 'confirmation_subject' => 'Invoice Ninja účet - ověření', + 'confirmation_header' => 'Ověření účtu', + 'confirmation_message' => 'Prosím klikněte na odkaz níže pro potvrzení Vašeho účtu.', + 'invoice_subject' => 'Nová faktura :invoice od :account', + 'invoice_message' => 'Pro zobrazení faktury na :amount, klikněte na odkaz níže.', + 'payment_subject' => 'Platba obdržena', + 'payment_message' => 'Děkujeme za Vaši platbu :amount.', + 'email_salutation' => 'Vážený(á) :name,', + 'email_signature' => 'S pozdravem,', + 'email_from' => 'Invoice Ninja Tým', + 'invoice_link_message' => 'Pro zobrazení faktury klikněte na odkaz níže:', + 'notification_invoice_paid_subject' => 'Faktura :invoice byla zaplacena :client', + 'notification_invoice_sent_subject' => 'Faktura :invoice byla odeslána :client', + 'notification_invoice_viewed_subject' => 'Faktura :invoice byla zobrazena :client', + 'notification_invoice_paid' => 'Platba :amount byla odeslána :client na fakturu :invoice.', + 'notification_invoice_sent' => 'Klientovi :client byla odeslána faktura :invoice na :amount.', + 'notification_invoice_viewed' => 'Klientovi :client se zobrazila faktura :invoice na :amount.', + 'reset_password' => 'Resetovat své heslo můžete kliknutím na následující tlačítko:', + 'secure_payment' => 'Bezpečná platba', + 'card_number' => 'Číslo karty', + 'expiration_month' => 'Expirace měsíc', + 'expiration_year' => 'Expirace rok', + 'cvv' => 'CVV kód', + 'logout' => 'Odhlásit se', + 'sign_up_to_save' => 'Zaregistrujte se pro uložení své práce', + 'agree_to_terms' => 'Souhlasím s podmínkami Ninja :terms', + 'terms_of_service' => 'Obchodní podmínky', + 'email_taken' => 'Tento email už byl registrován', + 'working' => 'Pracuji', + 'success' => 'Úspěšně', + 'success_message' => 'Úspěšně jste se zaregistroval(a)! Prosím klikněte pak na odkaz, který Vám přijde pro ověření do Vašeho emailu.', + 'erase_data' => 'Toto kompletně vymaže navždy Vaše data.', + 'password' => 'Heslo', + 'pro_plan_product' => 'Profi plán', + 'pro_plan_description' => 'Roční využití Invoice Ninja Profi plánu.', + 'pro_plan_success' => 'Děkujeme za použití Invoice Ninja\'s Profi plánu!

 
+ Další kroky

Faktura k úhradě Vám byla zaslána na email spojený s tímto účtem. + Pro povolení všech skvělých profi vlastností si prosím přečtěte instrukce pro provedení platby na zaslané faktuře + pro roční využívání Profi plánu.

+ Nemůžete fakturu najít? Potřebujete pomoc? Rádi Vám pomůžeme na + -- emailu contact@invoiceninja.com', + 'unsaved_changes' => 'Máte neuložené změny', + 'custom_fields' => 'Volitelná pole', + 'company_fields' => 'Pole pro firmu', + 'client_fields' => 'Pole pro klienta', + 'field_label' => 'Popiska pole', + 'field_value' => 'Hodnota pole', + 'edit' => 'Editovat', + 'set_name' => 'Nastavit jméno vaší firmy', + 'view_as_recipient' => 'Vidět jako příjemce', + 'product_library' => 'Katalog produktů', + 'product' => 'Produkt', + 'products' => 'Katalog produktů', + 'fill_products' => 'Automaticky vyplnit produkty', + 'fill_products_help' => 'Výběr produktu automaticky vyplní popis a náklady', + 'update_products' => 'Automaticky měnit produkty', + 'update_products_help' => 'Změna na faktuře automaticky promítne do katalogu produktů', + 'create_product' => 'Přidat produkt', + 'edit_product' => 'Editovat produkt', + 'archive_product' => 'Archivovat produkt', + 'updated_product' => 'Úspěšně změněn produkt', + 'created_product' => 'Úspěšně vytvořen produkt', + 'archived_product' => 'Úspěšně archivován produkt', + 'pro_plan_custom_fields' => ':link pro nastavení volitelných polí připojením k Profi plánu', + 'advanced_settings' => 'Pokročilé nastavení', + 'pro_plan_advanced_settings' => ':link pro nastavení pokročilých nastavení připojením k Profi plánu', + 'invoice_design' => 'Vzhled faktur', + 'specify_colors' => 'Určete barvy', + 'specify_colors_label' => 'Zvolit barvy použité ve faktuře', + 'chart_builder' => 'Generování grafů', + 'ninja_email_footer' => 'Použijte :site k fakturaci Vašim klientům a nechte si platit online zdarma!', + 'go_pro' => 'Přejít na Profi', + 'quote' => 'Nabídka', + 'quotes' => 'Nabídky', + 'quote_number' => 'Číslo nabídky', + 'quote_number_short' => 'Nabídka #', + 'quote_date' => 'Datum nabídky', + 'quote_total' => 'Nabídek celkem', + 'your_quote' => 'Vaše nabídky', + 'total' => 'Celkem', + 'clone' => 'Duplikovat', + 'new_quote' => 'Nová nabídka', + 'create_quote' => 'Vytvořit nabídku', + 'edit_quote' => 'Editovat nabídku', + 'archive_quote' => 'Archivovat nabídku', + 'delete_quote' => 'Smazat nabídku', + 'save_quote' => 'Uložit nabídku', + 'email_quote' => 'Odeslat nabídku emailem', + 'clone_quote' => 'Duplikovat nabídku', + 'convert_to_invoice' => 'Změnit na fakturu', + 'view_invoice' => 'Zobrazit fakturu', + 'view_client' => 'Zobrazit klienta', + 'view_quote' => 'Zobrazit nabídku', + 'updated_quote' => 'Úspěšně změněna nabídka', + 'created_quote' => 'Úspěšně vytvořena nabídka', + 'cloned_quote' => 'Úspěšně zduplikována nabídka', + 'emailed_quote' => 'Úspěšně odeslána nabídka', + 'archived_quote' => 'Úspěšně archivována nabídka', + 'archived_quotes' => 'Úspěšně archiváno :count nabídek', + 'deleted_quote' => 'Úspěšně smazána nabídka', + 'deleted_quotes' => 'Úspěšně smazáno :count nabídek', + 'converted_to_invoice' => 'Úspěšně změněna nabídka na fakturu', + 'quote_subject' => 'Nová nabídka $quote od :account', + 'quote_message' => 'Pro zobrazení nabídky na :amount, klikněte na odkaz níže.', + 'quote_link_message' => 'Pro zobrazení nabídky pro vašeho klienta klikněte na odkaz níže:', + 'notification_quote_sent_subject' => 'Nabídka :invoice byla odeslána :client', + 'notification_quote_viewed_subject' => 'Nabídka :invoice byla zobrazena :client', + 'notification_quote_sent' => 'Klientovi :client byla emailem odeslána nabídka :invoice na :amount.', + 'notification_quote_viewed' => 'Klientovi :client se zobrazila nabídka :invoice na :amount.', + 'session_expired' => 'Vaše přihlášení expirovalo.', + 'invoice_fields' => 'Pole faktury', + 'invoice_options' => 'Možnosti faktury', + 'hide_quantity' => 'Skrýt množství', + 'hide_quantity_help' => 'Pokud množství v řádcích je vždy 1, tak můžete faktury zjednodušit vynecháním tohoto pole.', + 'hide_paid_to_date' => 'Skrýt Zaplaceno ke dni', + 'hide_paid_to_date_help' => 'Pouze zobrazte "Zaplaceno ke dni" na faktuře jakmile přijde platba.', + 'charge_taxes' => 'Použít daně', + 'user_management' => 'Správa uživatelů', + 'add_user' => 'Přidat uživatele', + 'send_invite' => 'Poslat pozvánku', + 'sent_invite' => 'Pozvánka úspěšně odeslána', + 'updated_user' => 'Uživatel úspěšně změněn', + 'invitation_message' => 'Byl(a) jste pozván(a) :invitor. ', + 'register_to_add_user' => 'Prosím zaregistrujte se jako uživatel', + 'user_state' => 'Stav', + 'edit_user' => 'Editovat uživatele', + 'delete_user' => 'Smazat uživatele', + 'active' => 'Aktivní', + 'pending' => 'Nevyřízený', + 'deleted_user' => 'Uživatel úspěšně smazán', + 'confirm_email_invoice' => 'Jste si jistí, že chcete poslat tuto fakturu emailem?', + 'confirm_email_quote' => 'Jste si jistí, že chcete poslat tuto nabídku emailem?', + 'confirm_recurring_email_invoice' => 'Jste si jistí, že chcete poslat tuto fakturu emailem?', + 'cancel_account' => 'Zrušit účet', + 'cancel_account_message' => 'Varování: Tento krok smaže navždy všechny vaše data, není zde žádná cesta zpět.', + 'go_back' => 'Jít zpět', + 'data_visualizations' => 'Vizualizace dat', + 'sample_data' => 'Zobrazit vzorová data', + 'hide' => 'Skrýt', + 'new_version_available' => 'Nová verze :releases_link je k dispozici. Nyní používáte :user_version, poslední je :latest_version', + 'invoice_settings' => 'Nastavení faktury', + 'invoice_number_prefix' => 'Nastavení prefixu čísla faktury', + 'invoice_number_counter' => 'Číselná řada faktur', + 'quote_number_prefix' => 'Prefix čísla nabídky', + 'quote_number_counter' => 'Číselná řada nabídek', + 'share_invoice_counter' => 'Sdílet číselnou řadu faktur', + 'invoice_issued_to' => 'Faktura vystavena', + 'invalid_counter' => 'Pro případný konflikt si raději nastavte prefix pro faktury nebo nabídky', + 'mark_sent' => 'Značka odesláno', + 'gateway_help_1' => ':link zaregistrovat se na Authorize.net.', + 'gateway_help_2' => ':link zaregistrovat se na Authorize.net.', + 'gateway_help_17' => ':link získat PayPal API signature.', + 'gateway_help_27' => ':link zaregistrovat se na TwoCheckout.', + 'more_designs' => 'Více vzhledů', + 'more_designs_title' => 'Další vzhledy faktur', + 'more_designs_cloud_header' => 'Přejděte na Profi plán pro více vzhledů faktur', + 'more_designs_cloud_text' => '', + 'more_designs_self_host_text' => '', + 'buy' => 'Buy', + 'bought_designs' => 'Další vzhledy faktur přidány', + 'sent' => 'sent', + 'vat_number' => 'DIČ', + 'timesheets' => 'Časové výkazy', + 'payment_title' => 'Zadejte Vaší fakturační adresu a informace o platební kartě', + 'payment_cvv' => '*To jsou 3-4 čísla na zadní straně Vaší karty', + 'payment_footer1' => '*Fakturační adresa musí sedět s tou uvedenou u platební karty.', + 'payment_footer2' => '*Prosím kliněte na "Zaplatit nyní " jenom jednou - transkace může trvat až 1 minutu.', + 'id_number' => 'Číslo ID', + 'white_label_link' => 'White label', + 'white_label_header' => 'White Label', + 'bought_white_label' => 'Úspěšně nastavena white label licence', + 'white_labeled' => 'White labeled', + 'restore' => 'Obnovit', + 'restore_invoice' => 'Obnovit fakturu', + 'restore_quote' => 'Obnovit nabídku', + 'restore_client' => 'Obnovit klienta', + 'restore_credit' => 'Obnovit kredit', + 'restore_payment' => 'Obnovit platbu', + 'restored_invoice' => 'Faktura úspěšně obnovena', + 'restored_quote' => 'Nabídka úspěšně obnovena', + 'restored_client' => 'Klient úspěšně obnoven', + 'restored_payment' => 'Platba úspěšně obnovena', + 'restored_credit' => 'Kredit úspěšně obnoven', + 'reason_for_canceling' => 'Když nám řeknete proč odcházíte, pomůžete nám zlepšit náš web. Děkujeme.', + 'discount_percent' => 'Procento', + 'discount_amount' => 'Částka', + 'invoice_history' => 'Historie faktur', + 'quote_history' => 'Historie nabídek', + 'current_version' => 'Současná verze', + 'select_versiony' => 'Zvolte verzi', + 'view_history' => 'Zobrazit historii', + 'edit_payment' => 'Editovat platbu', + 'updated_payment' => 'Platba úspěšně změněna', + 'deleted' => 'Smazáno', + 'restore_user' => 'Obnovit uživatele', + 'restored_user' => 'Uživatel úspěšně obnoven', + 'show_deleted_users' => 'Zobrazit smazané uživatele', + 'email_templates' => 'Emailové šablony', + 'invoice_email' => 'Email pro fakturu', + 'payment_email' => 'Email pro platbu', + 'quote_email' => 'Email pro nabídku', + 'reset_all' => 'Resetovat vše', + 'approve' => 'Schválit', + 'token_billing_type_id' => 'Token účtování', + 'token_billing_help' => 'Umožňuje ukládat platební karty ve vaší platební bráně a zatížit je platbou později.', + 'token_billing_1' => 'Vypnuto', + 'token_billing_2' => 'Opt-in - checkbox je zobrazen nezaškrtnutý', + 'token_billing_3' => 'Opt-out - je zobrazen zaškrtnutý', + 'token_billing_4' => 'Vždy', + 'token_billing_checkbox' => 'Ukládat detaily platební karty', + 'view_in_stripe' => 'Zobrazit ve Stripe', + 'use_card_on_file' => 'Použít uloženou kartu', + 'edit_payment_details' => 'Editovat platební údaje', + 'token_billing' => 'Ukládat platební údaje', + 'token_billing_secure' => 'Data jsou bezpečně uložena u :stripe_link', + 'support' => 'Popdora', + 'contact_information' => 'Kontaktní informace', + '256_encryption' => '256-Bitové šifrování', + 'amount_due' => 'Částka k platbě', + 'billing_address' => 'Fakturační adresa', + 'billing_method' => 'Způsob fakturace', + 'order_overview' => 'Přehled objednávky', + 'match_address' => '*Adresa musí odpovídat té uvedené na platební kartě.', + 'click_once' => '*Prosím klikněte na "Zaplatit nyní" pouze jednou - transkace může trvat až 1 minutu.', + 'invoice_footer' => 'Patička faktury', + 'save_as_default_footer' => 'Uložit jako výchozí patičku', + 'token_management' => 'Správa tokenů', + 'tokens' => 'Tokeny', + 'add_token' => 'Přidat token', + 'show_deleted_tokens' => 'Zobrazit smazané tokeny', + 'deleted_token' => 'Token úspěšně smazán', + 'created_token' => 'Token úspěšně vytvořen', + 'updated_token' => 'Token úspěšně změněn', + 'edit_token' => 'Editovat token', + 'delete_token' => 'Smazat Token', + 'token' => 'Token', + 'add_gateway' => 'Přidat platební bránu', + 'delete_gateway' => 'Smazat platební bránu', + 'edit_gateway' => 'Editovat bránu', + 'updated_gateway' => 'Brána úspěšně změněna', + 'created_gateway' => 'Brána úspěšně vytvořena', + 'deleted_gateway' => 'Brána úspěšně smazána', + 'pay_with_paypal' => 'PayPal', + 'pay_with_card' => 'Platební karty', + 'change_password' => 'Změnit heslo', + 'current_password' => 'Současné heslo', + 'new_password' => 'Nové heslo', + 'confirm_password' => 'Potvrdit heslo', + 'password_error_incorrect' => 'Současné heslo je neplatné.', + 'password_error_invalid' => 'Nové heslo je neplatné.', + 'updated_password' => 'Heslo úspěšně změněno', + 'api_tokens' => 'API Tokeny', + 'users_and_tokens' => 'Uživatelé & Tokeny', + 'account_login' => 'Přihlášení k účtu', + 'recover_password' => 'Obnovit vaše heslo', + 'forgot_password' => 'Zapomněli jste heslo?', + 'email_address' => 'Email', + 'lets_go' => 'Jdeme na to', + 'password_recovery' => 'Obnovení hesla', + 'send_email' => 'Poslat email', + 'set_password' => 'Nastavit heslo', + 'converted' => 'Zkonvertováno', + 'email_approved' => 'Odeslat email po schválení nabídky', + 'notification_quote_approved_subject' => 'Nabídka :invoice byla schválena :client', + 'notification_quote_approved' => 'Klient :client schválil nabídku :invoice na :amount.', + 'resend_confirmation' => 'Znovu poslat potvrzovací email', + 'confirmation_resent' => 'Potvrzení bylo odesláno emailem', + 'gateway_help_42' => ':link zaregistrujte se na BitPay.
Poznámka: použijte Legacy API Key, nikoliv API token.', + 'payment_type_credit_card' => 'Platební karty', + 'payment_type_paypal' => 'PayPal', + 'payment_type_bitcoin' => 'Bitcoin', + 'knowledge_base' => 'Knowledge Base', + 'partial' => 'Část', + 'partial_remaining' => ':partial z :balance', + 'more_fields' => 'Více polí', + 'less_fields' => 'Méně polí', + 'client_name' => 'Jméno klienta', + 'pdf_settings' => 'Nastavení PDF', + 'product_settings' => 'Nastavení produktu', + 'auto_wrap' => 'Automatické zalomení řádky', + 'duplicate_post' => 'Varování: předchozí stránka byla odeslána dvakrát. Druhé odeslání bylo ignorováno.', + 'view_documentation' => 'Zobrazit dokumentaci', + 'app_title' => 'Open source online fakrurace', + 'app_description' => 'Invoice Ninja je bezplatné open-source řešení pro fakturaci a účtování zákazníkům. + S Invoice Ninja, můžete jednoduše vytvářet a posílat hezké faktury z jakéhokoli zařízení, které má přístup na web. Vaši klienti si mohou faktury + vytisknout, stáhnout jako PDF nebo Vám rovnou online zaplatit.', + 'rows' => 'řádky', + 'www' => 'www', + 'logo' => 'Logo', + 'subdomain' => 'subdoména', + 'provide_name_or_email' => 'Prosím zadejte jméno nebo email', + 'charts_and_reports' => 'Grafy & Reporty', + 'chart' => 'Graf', + 'report' => 'Report', + 'group_by' => 'Seskupené podle', + 'paid' => 'Zaplacené', + 'enable_report' => 'Report', + 'enable_chart' => 'Graf', + 'totals' => 'Celkem', + 'run' => 'Běh', + 'export' => 'Export', + 'documentation' => 'Dokumentace', + 'zapier' => 'Zapier', + 'recurring' => 'Pravidelné', + 'last_invoice_sent' => 'Poslední faktura byla odeslána :date', + 'processed_updates' => 'Změna úspěšně provedena', + 'tasks' => 'Úlohy', + 'new_task' => 'Nový úloha', + 'start_time' => 'Počáteční čas', + 'created_task' => 'Úloha úspěšně vytvořena', + 'updated_task' => 'Úloha úspěšně změněna', + 'edit_task' => 'Editovat úlohu', + 'archive_task' => 'Archivovat úlohu', + 'restore_task' => 'Obnovit úlohu', + 'delete_task' => 'Smazat úlohu', + 'stop_task' => 'Zastavit úlohu', + 'time' => 'Čas', + 'start' => 'Začátek', + 'stop' => 'Konec', + 'now' => 'Nyní', + 'timer' => 'Časovač', + 'manual' => 'Manuální', + 'date_and_time' => 'Datum & Čas', + 'second' => 'vteřina', + 'seconds' => 'vteřin', + 'minute' => 'minuta', + 'minutes' => 'minut', + 'hour' => 'hodina', + 'hours' => 'hodiny', + 'task_details' => 'Detaily úlohy', + 'duration' => 'Trvání', + 'end_time' => 'Čas konce', + 'end' => 'Konec', + 'invoiced' => 'Fakturováno', + 'logged' => 'Přihlášen', + 'running' => 'Bežící', + 'task_error_multiple_clients' => 'Úloha nemůže být přiřazena různým klientům', + 'task_error_running' => 'Prosím zatavte napřed běžící úlohy', + 'task_error_invoiced' => 'Úlohy byly vyfakturovány', + 'restored_task' => 'Úloha úspěšně obnovena', + 'archived_task' => 'Úloha úspěšně archivována', + 'archived_tasks' => 'Úspěšně archivováno :count úloh', + 'deleted_task' => 'Úloha úspěšně smazána', + 'deleted_tasks' => 'Úspěšně smazáno :count úloh', + 'create_task' => 'Vytvořit úlohu', + 'stopped_task' => 'Úloha úspěšně zastavena', + 'invoice_task' => 'Fakturační úloha', + 'invoice_labels' => 'Fakturační popisky', + 'prefix' => 'Prefix', + 'counter' => 'Počítadlo', + 'payment_type_dwolla' => 'Dwolla', + 'gateway_help_43' => ':link zaregistrujte se na Dwolla', + 'partial_value' => 'Musí být větší než nula a méně než součet', + 'more_actions' => 'Více akcí', + 'pro_plan_title' => 'NINJA PROFI', + 'pro_plan_call_to_action' => 'Upgradujte nyní!', + 'pro_plan_feature1' => 'Neomezené množství klientů', + 'pro_plan_feature2' => 'Přístup k 10 nádherným šablonám faktur', + 'pro_plan_feature3' => 'Volitelné URLs - "vaseznacka.InvoiceNinja.com"', + 'pro_plan_feature4' => 'Odstranit "Vytvořeno Invoice Ninja"', + 'pro_plan_feature5' => 'Přístup více uživatelů & sledování aktivit', + 'pro_plan_feature6' => 'Vytváření nabídek & proforem', + 'pro_plan_feature7' => 'Úprava popisu polí faktur & číslování', + 'pro_plan_feature8' => 'Možnost připojit PDF soubor do emailu klientům', + 'resume' => 'Pokračovat', + 'break_duration' => 'Přestávka', + 'edit_details' => 'Editovat detaily', + 'work' => 'Práce', + 'timezone_unset' => 'Prosím :link nastavte si vaší časovou zónu', + 'click_here' => 'klikněte zde', + 'email_receipt' => 'Odeslat potvrzení platby klientovi', + 'created_payment_emailed_client' => 'ˇUspěšně vytvořena platba a odesláno info klientovi', + 'add_company' => 'Přidat firmu', + 'untitled' => 'Neoznačené', + 'new_company' => 'Nová firma', + 'associated_accounts' => 'Účty úspěšně spojeny', + 'unlinked_account' => 'Účty úspěšně rozspojeny', + 'login' => 'Přihlášení', + 'or' => 'nebo', + 'email_error' => 'Nastal problém s odesláním emailu', + 'confirm_recurring_timing' => 'Poznámka: Emaily jsou odesílány na záčátku hodiny.', + 'payment_terms_help' => 'Nastavte výchozí datum splatnosti', + 'unlink_account' => 'Odpojit účet', + 'unlink' => 'Odpojit', + 'show_address' => 'Ukázat adresu', + 'show_address_help' => 'Klient musí poskytnout fakturační adresu', + 'update_address' => 'Změnit adresu', + 'update_address_help' => 'Změnit adresu klienta podle poskytnutých detailů', + 'times' => 'Časy', + 'set_now' => 'Nastavit nyní', + 'dark_mode' => 'Tmavý mód', + 'dark_mode_help' => 'Zobrazit bílý text na černém pozadí', + 'add_to_invoice' => 'Přidat k faktuře :invoice', + 'create_new_invoice' => 'Vytvořit novou fakturu', + 'task_errors' => 'Prosím opravte překrývající se časy', + 'from' => 'Od', + 'to' => 'Komu', + 'font_size' => 'Velikost fontu', + 'primary_color' => 'Základní barva', + 'secondary_color' => 'Druhá barva', + 'customize_design' => 'Upravit design', + 'content' => 'Obsah', + 'styles' => 'Styly', + 'defaults' => 'Výchozí', + 'margins' => 'Marže', + 'header' => 'Hlavička', + 'footer' => 'Patička', + 'custom' => 'Volitelné', + 'invoice_to' => 'Fakturovat komu', + 'invoice_no' => 'Faktura č.', + 'recent_payments' => 'Poslední platby', + 'outstanding' => 'Nevyrovnaný', + 'manage_companies' => 'Spravovat firmy', + 'total_revenue' => 'Celkové příjmy', + 'current_user' => 'Aktuální uživatel', + 'new_recurring_invoice' => 'Nová pravidelná faktura', + 'recurring_invoice' => 'Pravidelná faktura', + 'recurring_too_soon' => 'Brzy se vytvoří další pravidelná faktura, je nastavena na :date', + 'created_by_invoice' => 'Vytvořeno :invoice', + 'primary_user' => 'Primární uživatel', + 'help' => 'Pomoc', + 'customize_help' => '

Používáme pdfmake pro definování vzhledu faktur. Pdfmake zkušebna poskytuje skvělou cestu jak knihovnu vidět v akci

+

Pro přístup k podřízené položce se používá tečkové konvence. Například zobrazit pro zobrazení jména klienta použijte $client.name.

+

Pokud potřebujete s něčím pomoci - pošlete dotaz na náš fórum podpory.

', + 'invoice_due_date' => 'Datum splatnosti', + 'quote_due_date' => 'Platí do', + 'valid_until' => 'Platí do', + 'reset_terms' => 'Resetovat podmínky', + 'reset_footer' => 'Resetovat patičku', + 'invoices_sent' => ':count faktur sent|:count faktur odesláno', + 'status_draft' => 'Návrh', + 'status_sent' => 'Odesláno', + 'status_viewed' => 'Zobrazené', + 'status_partial' => 'Částečné', + 'status_paid' => 'Placené', + 'show_line_item_tax' => 'Zobrazit daně v řádku v položkách', + 'iframe_url' => 'Web', + 'iframe_url_help1' => 'Zkopírujte následující kód na Váš web.', + 'iframe_url_help2' => 'Tuto vlastnost můžete vyzkoušet kliknutím na \'Vidět jako příjemce\' jako fakturu.', + 'auto_bill' => 'Automatické fakturování', + 'military_time' => '24 hodinový čas', + 'last_sent' => 'Poslední odeslány', + 'reminder_emails' => 'Připomínky emailem', + 'templates_and_reminders' => 'Šablony & Připomínky', + 'subject' => 'Předmět', + 'body' => 'Tělo', + 'first_reminder' => 'První připomínka', + 'second_reminder' => 'Druhá připomínka', + 'third_reminder' => 'Třetí připomínka', + 'num_days_reminder' => 'Dny po splatnosti', + 'reminder_subject' => 'Připomínka: Faktura :invoice od :account', + 'reset' => 'Resetovat', + 'invoice_not_found' => 'Požadovaná faktura není k dispozici', + 'referral_program' => 'Referral program', + 'referral_code' => 'Referral URL', + 'last_sent_on' => 'Poslední odeslání: :date', + 'page_expire' => 'Tato stránka brzy expiruje, :click_here pro pokračování zobrazení', + 'upcoming_quotes' => 'Nadcházející nabídky', + 'expired_quotes' => 'Prošlé nabídky', + 'sign_up_using' => 'Zaregistrujte se pro použití', + 'invalid_credentials' => 'Tyto údaje neodpovídají našim záznamům.', + 'show_all_options' => 'Zobrazit všechny možnosti', + 'user_details' => 'Uživatelské detaily', + 'oneclick_login' => 'Přihlášení na 1 klik', + 'disable' => 'Vypnout', + 'invoice_quote_number' => 'Čísla faktur a nabídek', + 'invoice_charges' => 'Faktura poplatky', + 'notification_invoice_bounced' => 'Nebyli jsme schopni doručit fakturu :invoice na :contact.', + 'notification_invoice_bounced_subject' => 'Nebylo možné doručit fakturu :invoice', + 'notification_quote_bounced' => 'Nebyli jsme schopni doručit nabídku :invoice na :contact.', + 'notification_quote_bounced_subject' => 'Nebylo možné doručit nabídku :invoice', + 'custom_invoice_link' => 'Odkaz na fakturu', + 'total_invoiced' => 'Celkem fakturováno', + 'open_balance' => 'Zůstatek', + 'verify_email' => 'Prosím klikněte na odkaz v potvrzovacím emailu pro ověření správné adresy.', + 'basic_settings' => 'Základní nastavení', + 'pro' => 'Profi', + 'gateways' => 'Platební brány', + 'next_send_on' => 'Další odeslání: :date', + 'no_longer_running' => 'Tato faktura není nastavena aby proběhla', + 'general_settings' => 'Obecné nastavení', + 'customize' => 'Přizpůsobení', + 'oneclick_login_help' => 'Připojte si účet pro přihlášení bez použití hesla', + 'referral_code_help' => 'Vydělejte si peníze díky sdílení odkazu na naší aplikaci', + 'enable_with_stripe' => 'Povolit | Vyžaduje Stripe', + 'tax_settings' => 'Nastavení daní', + 'create_tax_rate' => 'Přidat daňovou sazbu', + 'updated_tax_rate' => 'Daňová sazba úspěšně změněna', + 'created_tax_rate' => 'Daňová sazba úspěšně vytvořena', + 'edit_tax_rate' => 'Editovat daňovou sazbu', + 'archive_tax_rate' => 'Archivovat daňovou sazbu', + 'archived_tax_rate' => 'Daňová sazba úspěšně archivována', + 'default_tax_rate_id' => 'Výchozí daňová sazba', + 'tax_rate' => 'Daňová sazba', + 'recurring_hour' => 'Pravidelná hodina', + 'pattern' => 'Vzorec', + 'pattern_help_title' => 'Pomoc se vzorcem', + 'pattern_help_1' => 'Vytvořte si čísla faktur a nabídek pomocí nastavení vzorce', + 'pattern_help_2' => 'Dostupné proměnné:', + 'pattern_help_3' => 'Například, :example může být konvertováno na :value', + 'see_options' => 'Zobrazit možnosti', + 'invoice_counter' => 'Počítadlo faktur', + 'quote_counter' => 'Počítadlo nabídek', + 'type' => 'Typ', + 'activity_1' => ':user vytvořil klienta :client', + 'activity_2' => ':user archivoval klienta :client', + 'activity_3' => ':user smazal klienta :client', + 'activity_4' => ':user vytvořil fakturu :invoice', + 'activity_5' => ':user změnil fakturu :invoice', + 'activity_6' => ':user odeslal fakturu :invoice to :contact', + 'activity_7' => ':contact zobrazil fakturu :invoice', + 'activity_8' => ':user archivoval fakturu :invoice', + 'activity_9' => ':user smazal fakturu :invoice', + 'activity_10' => ':contact zadal platbu :payment na :invoice', + 'activity_11' => ':user změnil platbu :payment', + 'activity_12' => ':user archivoval platbu :payment', + 'activity_13' => ':user smazal platbu :payment', + 'activity_14' => ':user zadal :credit kredit', + 'activity_15' => ':user změnil :credit kredit', + 'activity_16' => ':user archivoval :credit kredit', + 'activity_17' => ':user smazal :credit kredit', + 'activity_18' => ':user vytvořil nabídku :quote', + 'activity_19' => ':user změnil nabídku :quote', + 'activity_20' => ':user odeslal nabídku :quote to :contact', + 'activity_21' => ':contact zobrazil nabídku :quote', + 'activity_22' => ':user archivoval nabídku :quote', + 'activity_23' => ':user smazal nabídku :quote', + 'activity_24' => ':user obnovil nabídku :quote', + 'activity_25' => ':user obnovil fakturu :invoice', + 'activity_26' => ':user obnovil klienta :client', + 'activity_27' => ':user obnovil platbu :payment', + 'activity_28' => ':user obnovil :credit kredit', + 'activity_29' => ':contact schválil nabídku :quote', + 'activity_30' => ':user vytvořil :vendor', + 'activity_31' => ':user vytvořil :vendor', + 'activity_32' => ':user vytvořil :vendor', + 'activity_33' => ':user vytvořil :vendor', + 'activity_34' => ':user vytvořil výdaj :expense', + 'activity_35' => ':user vytvořil :vendor', + 'activity_36' => ':user vytvořil :vendor', + 'activity_37' => ':user vytvořil :vendor', + 'payment' => 'Platba', + 'system' => 'Systém', + 'signature' => 'Emailový podpis', + 'default_messages' => 'Výchozí vzkazy', + 'quote_terms' => 'Podmínky nabídky', + 'default_quote_terms' => 'Výchozí podmínky nabídky', + 'default_invoice_terms' => 'Výchozí fakturační podmínky', + 'default_invoice_footer' => 'Výchozí patička faktury', + 'quote_footer' => 'Patička nabídky', + 'free' => 'Zdarma', + 'quote_is_approved' => 'Tato nabídka je schválena', + 'apply_credit' => 'Použít kredit', + 'system_settings' => 'Nastavení systému', + 'archive_token' => 'Archivovat token', + 'archived_token' => 'Token úspěšně archivován', + 'archive_user' => 'Archivovaný uživatel', + 'archived_user' => 'Užival úspěšně archivován', + 'archive_account_gateway' => 'Archivovat bránu', + 'archived_account_gateway' => 'Brána úspěšně archivována', + 'archive_recurring_invoice' => 'Archivovat pravidelnou fakturu', + 'archived_recurring_invoice' => 'Pravidelná faktura úspěšně archivována', + 'delete_recurring_invoice' => 'Smazat pravidelnou fakturu', + 'deleted_recurring_invoice' => 'Pravidelná faktura smazána', + 'restore_recurring_invoice' => 'Obnovit pravidelnou fakturu', + 'restored_recurring_invoice' => 'Pravidelná faktura obnovena', + 'archived' => 'Archivováno', + 'untitled_account' => 'Společnost bez názvu', + 'before' => 'Před', + 'after' => 'Po', + 'reset_terms_help' => 'Resetovat na výchozí podmínky účtu', + 'reset_footer_help' => 'Resetovat na výchozí hlavičku účtu', + 'export_data' => 'Exportovat data', + 'user' => 'Uživatel', + 'country' => 'Země', + 'include' => 'Zahrnout', + 'logo_too_large' => 'Vaše logo je :size, pro rychlejší zobrazení v PDF navrhujeme nahrát soubor menší než 200KB', + 'import_freshbooks' => 'Importovat z FreshBooks', + 'import_data' => 'Importovat data', + 'source' => 'Zdroj', + 'csv' => 'CSV', + 'client_file' => 'Soubor s klienty', + 'invoice_file' => 'Soubor s fakturami', + 'task_file' => 'Soubor s úlohami', + 'no_mapper' => 'Mapování pro soubor není k dispozici', + 'invalid_csv_header' => 'Naplatná hlavička v CSV souboru', + 'client_portal' => 'Klientský portál', + 'admin' => 'Administrátor', + 'disabled' => 'Nepovolen', + 'show_archived_users' => 'Zobrazit archivované uživatele', + 'notes' => 'Poznámky', + 'invoice_will_create' => 'klient bude vytvořen', + 'invoices_will_create' => 'faktury budou vytvořeny', + 'failed_to_import' => 'Následující záznamy selhaly u importu, buď již existují nebo nemají požadovaná pole.', + 'publishable_key' => 'Veřejný klíč', + 'secret_key' => 'Tajný klíč', + 'missing_publishable_key' => 'Nastavte veřejný klíč Stripe pro lepší proces platby', + 'email_design' => 'Vzhled emailu', + 'due_by' => 'Splatnost do :date', + 'enable_email_markup' => 'Umožnit mikroznačky', + 'enable_email_markup_help' => 'Přidejte si mikroznačky schema.org do emailu a usnadněte tak vašim klientům platby.', + 'template_help_title' => 'Nápověda k šablonám', + 'template_help_1' => 'Dostupné proměnné:', + 'email_design_id' => 'Styl emailu', + 'email_design_help' => 'Vytvořte si profesionální vzhled emailů pomocí HTML', + 'plain' => 'Prostý text', + 'light' => 'Světlý', + 'dark' => 'Tmavý', + 'industry_help' => 'Používá se pro porovnání proti průměru u firem podobné velikosti a podobného odvětví.', + 'subdomain_help' => 'Upravte si subdoménu odkazu na fakturu nebo zobrazte faktury na Vašem vlastním webu.', + 'invoice_number_help' => 'Určete prefix nebo použijte upravitelný vzorec pro nastavení číslování faktur.', + 'quote_number_help' => 'Určete prefix nebo použijte upravitelný vzorec pro nastavení číslování nabídek.', + 'custom_client_fields_helps' => 'Když vytváříte klienta - přidejte nové pole a jeho popis a hodnotu pro zobrazení v PDF.', + 'custom_account_fields_helps' => 'Přidejte si pole a hodnotu k detailům společnosti na PDF.', + 'custom_invoice_fields_helps' => 'Přidejte si nové pole když vytváříte fakturu a zobrazte si jeho popis a hodnotu v PDF.', + 'custom_invoice_charges_helps' => 'Přidejte si pole během vytváření faktury a zahrňte ho mezi poplatky do faktury.', + 'token_expired' => 'Validační token expiroval. Prosím vyzkoušejte znovu.', + 'invoice_link' => 'Odkaz na fakturu', + 'button_confirmation_message' => 'Klikněte pro potvrzení Vaší emailové adresy.', + 'confirm' => 'Potvrzuji', + 'email_preferences' => 'Email preference', + 'created_invoices' => 'Úspěšně vytvořeno :count faktur', + 'next_invoice_number' => 'Další číslo faktury je :number.', + 'next_quote_number' => 'Další číslo nabídky je :number.', + 'days_before' => 'dní před', + 'days_after' => 'dní po', + 'field_due_date' => 'datum splatnosti', + 'field_invoice_date' => 'datum vystavení', + 'schedule' => 'Rozvrh', + 'email_designs' => 'Vzhled emailů', + 'assigned_when_sent' => 'Přiřazeno při odeslání', + 'white_label_purchase_link' => 'Zakoupit white label licenci', + 'expense' => 'Náklad', + 'expenses' => 'Náklady', + 'new_expense' => 'Nový náklad', + 'enter_expense' => 'Zadat náklad', + 'vendors' => 'Dodavatelé', + 'new_vendor' => 'Nový dodavatel', + 'payment_terms_net' => 'Net', + 'vendor' => 'Dodavatel', + 'edit_vendor' => 'Editovat dodavatele', + 'archive_vendor' => 'Archivovat dodavatele', + 'delete_vendor' => 'Smazat dodavatele', + 'view_vendor' => 'Zobrazit dodavatele', + 'deleted_expense' => 'Náklad úspěšně smazán', + 'archived_expense' => 'Náklad úspěšně archivován', + 'deleted_expenses' => 'Náklad úspěšně smazán', + 'archived_expenses' => 'Náklady úspěšně archivovány', + 'expense_amount' => 'Částka nákladů', + 'expense_balance' => 'Zůstatek nákladů', + 'expense_date' => 'Datum nákladu', + 'expense_should_be_invoiced' => 'Má tento náklad být fakturován?', + 'public_notes' => 'Veřejné poznámky', + 'invoice_amount' => 'Částka faktury', + 'exchange_rate' => 'Měnový kurz', + 'yes' => 'Ano', + 'no' => 'Ne', + 'should_be_invoiced' => 'Má být fakturován', + 'view_expense' => 'Zobrazit náklad # :expense', + 'edit_expense' => 'Editovat náklad', + 'archive_expense' => 'Archivovat náklad', + 'delete_expense' => 'Smazat náklad', + 'view_expense_num' => 'Náklad # :expense', + 'updated_expense' => 'Náklad úspěšně změněn', + 'created_expense' => 'Náklad úspěšně vytvořen', + 'enter_expense' => 'Zadat náklady', + 'view' => 'Zobrazit', + 'restore_expense' => 'Obnovit náklady', + 'invoice_expense' => 'Fakturovat náklady', + 'expense_error_multiple_clients' => 'Náklady nemohou patřit různým klientům', + 'expense_error_invoiced' => 'Náklady byly již vyfakturovány', + 'convert_currency' => 'Zkonvertovat měnu', + 'num_days' => 'Počet dnů', + 'create_payment_term' => 'Vytvořit platební podmínky', + 'edit_payment_terms' => 'Editovat platební podmínky', + 'edit_payment_term' => 'Editovat platební podmínky', + 'archive_payment_term' => 'Archivovat platební podmínky', + 'recurring_due_dates' => 'Datumy splatnosti pravidelných faktur', + 'recurring_due_date_help' => '

Automaticky nastavit datum splatnosti na fakturách

+

U faktury s měsíčním nebo ročním cyklem bude nastavena měsíční splatnost v dalším měsíci. Invoices on a monthly or yearly cycle set to be due on or before the day they are created will be due the next month. + Faktury se splatností k 29. nebo 30 v měsících, které tyto dny nemají se splatnost nastaví k poslednímu dni v měsíci.

+

Faktury s týdenním cyklem mají jako výchozí týdenní splatnost.

+

Například:

+ ', + 'due' => 'Splatnost', + 'next_due_on' => 'Další splatnost: :date', + 'use_client_terms' => 'Použít podmínky klienta', + 'day_of_month' => ':ordinal den v měsíci', + 'last_day_of_month' => 'poslední den v měsíci', + 'day_of_week_after' => ':ordinal :týden poté', + 'sunday' => 'Neděle', + 'monday' => 'Pondělí', + 'tuesday' => 'Úterý', + 'wednesday' => 'Středa', + 'thursday' => 'Čtvrtek', + 'friday' => 'Pátek', + 'saturday' => 'Sobota', + 'header_font_id' => 'Hlavička font', + 'body_font_id' => 'Font těla', + 'color_font_help' => 'Poznámka: primární barva a fonty jsou rovněž použity v klientském portálu a upravených šablonách emailů.', + 'live_preview' => 'Náhled', + 'invalid_mail_config' => 'Nelze odeslat email, zkontrolujte prosím nastavení emailu.', + 'invoice_message_button' => 'Pro zobrazení faktury na :amount, klikněte na tlačítko níže.', + 'quote_message_button' => 'Pro zobrazení nabídky na :amount, klikněte na tlačítko níže.', + 'payment_message_button' => 'Děkujeme za Vaši platbu :amount.', + 'payment_type_direct_debit' => 'Platba převodem', + 'bank_accounts' => 'Platební karty & Banky', + 'add_bank_account' => 'Přidat bankovní účet', + 'setup_account' => 'Nastavení účtu', + 'import_expenses' => 'Importovat náklady', + 'bank_id' => 'Banka', + 'integration_type' => 'Typ integrace', + 'updated_bank_account' => 'Bankovní účet úspěšně změněn', + 'edit_bank_account' => 'Editovat bankovní účet', + 'archive_bank_account' => 'Archivovat bankovní účet', + 'archived_bank_account' => 'Bankovní účet úspěšně archivován', + 'created_bank_account' => 'Bankovní účet úspěšně vytvořen', + 'validate_bank_account' => 'Ověřit bankovní účet ', + 'bank_password_help' => 'Poznámka: Vaše heslo je bezpečně přeneseno a nikdy není uloženo na našich serverech.', + 'bank_password_warning' => 'Varování: vaše heslo může být přenášeno jako prostý text, zvažte prosím aktivaci HTTPS.', + 'username' => 'Uživatelské jméno', + 'account_number' => 'Číslo účtu', + 'account_name' => 'Název účtu', + 'bank_account_error' => 'Získání detailů účtu selhalo, prosím zkontrolujte si vaše přihlašovací údaje.', + 'status_approved' => 'Schváleno', + 'quote_settings' => 'Nastavení nabídek', + 'auto_convert_quote' => 'Automaticky zkonvertovat nabídku', + 'auto_convert_quote_help' => 'Automaticky zkonvertovat nabídku na fakturu po schválení klientem.', + 'validate' => 'Ověřit', + 'info' => 'Info', + 'imported_expenses' => 'Úspěšně vytvořeno :count_vendors dodavatelů a :count_expenses nákladů', + 'iframe_url_help3' => 'Poznámka: pokud chcete akceptovat kreditní karty nastavte si HTTPS na vašem webu.', + 'expense_error_multiple_currencies' => 'Náklady nemohou být v různých měnách.', + 'expense_error_mismatch_currencies' => 'Měna klienta neodpovídá měně u nákladu.', + 'trello_roadmap' => 'Trello roadmapa', + 'header_footer' => 'Hlavička/Patička', + 'first_page' => 'první stránka', + 'all_pages' => 'všechny stránky', + 'last_page' => 'poslední stránka', + 'all_pages_header' => 'Zobrazit hlavičku', + 'all_pages_footer' => 'Zobrazit patičku', + 'invoice_currency' => 'Měna faktury', + 'enable_https' => 'Pro akceptování platebních karet online používejte vždy HTTPS.', + 'quote_issued_to' => 'Náklad je vystaven', + 'show_currency_code' => 'Kód měny', + 'trial_message' => 'Váš účet získá zdarma 2 týdny zkušební verze Profi plánu.', + 'trial_footer' => 'Vaše zkušební verze trvá :count dnů, :link upradujte nyní.', + 'trial_footer_last_day' => 'Dnes je poslední den Vašeho zkušebního období , :link upradujte nyní.', + 'trial_call_to_action' => 'Vyzkoušet zdarma', + 'trial_success' => '14-ti denní zkušební lhůta úspěšně nastavena', + 'overdue' => 'Po termínu', + + + 'white_label_text' => 'Objednejte si white label licenci na JEDEN ROK $'.WHITE_LABEL_PRICE.' pro odstranění značky Invoice Ninja z klientského portálu a stránek podpory.', + 'user_email_footer' => 'Pro úpravu emailových notifikací prosím navštivte '.SITE_URL.'/settings/notifications', + 'reset_password_footer' => 'Pokud jste nepožádali o resetování hesla, prosím kontaktujte naši podporu na: '.CONTACT_EMAIL, + 'limit_users' => 'Omlouváme se, to už přesáhlo limit '.MAX_NUM_USERS.' uživatelů', + 'more_designs_self_host_header' => 'Získejte 6 dalších vzhledů faktur jen za $'.INVOICE_DESIGNS_PRICE, + 'old_browser' => 'Prosím použijte novější prohlížeč', + 'white_label_custom_css' => ':link za $'.WHITE_LABEL_PRICE.' získáte volitelné úpravy a pomůžete podpoře našeho projektu.', + 'bank_accounts_help' => 'Připojte si bankovní účet pro automatický import nákladů a tvorbu dodavatelů. K dispozici pro American Express přes 400 amerických bank.', + 'security' => [ + 'too_many_attempts' => 'Mnoho přístupů. Zkuste to prosím za několik minut.', + 'wrong_credentials' => 'Neplatný email nebo heslo.', + 'confirmation' => 'Váš účet byl potvrzen!', + 'wrong_confirmation' => 'Chybný potvrzovací kód.', + 'password_forgot' => 'Informace týkající se resetování hesla byla odeslána na Váš email.', + 'password_reset' => 'Heslo bylo změněno úspěšně.', + 'wrong_password_reset' => 'Neplatné heslo. Zkuste znovu', + ], + 'pro_plan' => [ + 'remove_logo' => ':link pro odstranění loga Invoice Ninja připojením se k profi plánu', + 'remove_logo_link' => 'Klikněte zde', + ], + 'invitation_status' => [ + 'sent' => 'Email odeslán', + 'opened' => 'Email otevřen', + 'viewed' => 'Faktura zobrazena', + ], + 'email_errors' => [ + 'inactive_client' => 'Emaily nemohou být odeslány neaktivním klientům', + 'inactive_contact' => 'Emaily nemohou být odeslány neaktivním kontaktům', + 'inactive_invoice' => 'Emaily nemohou být odeslány k neaktivním fakturám', + 'user_unregistered' => 'Pro odesílání emailů si prosím zaregistrujte účet', + 'user_unconfirmed' => 'Pro posílání emailů potvrďte prosím Váš účet.', + 'invalid_contact_email' => 'Neplatný kontaktní email', + ], + + 'navigation' => 'Navigace', + 'list_invoices' => 'Seznam faktur', + 'list_clients' => 'Seznam klientů', + 'list_quotes' => 'Seznam nabídek', + 'list_tasks' => 'Seznam úloh', + 'list_expenses' => 'Seznam nákladů', + 'list_recurring_invoices' => 'Seznam pravidelných faktur', + 'list_payments' => 'Seznam plateb', + 'list_credits' => 'Seznam kreditů', + 'tax_name' => 'Název daně', + 'report_settings' => 'Nastavení reportů', + 'search_hotkey' => 'Zkratka je /', + + 'new_user' => 'Nový uživatel', + 'new_product' => 'Nový produkt', + 'new_tax_rate' => 'Nová sazba daně', + 'invoiced_amount' => 'Fakturovaná částka', + 'invoice_item_fields' => 'Pole položky faktury', + 'custom_invoice_item_fields_help' => 'Během vytváření faktury si přidejte pole a zobrazte si jeho popis a hodnotu v PDF.', + 'recurring_invoice_number' => 'Číslo pravidelné faktury', + 'recurring_invoice_number_prefix_help' => 'Určete prefix, který se přidá k číslu pravidelných faktur. Výchozí hodnota je \'R\'.', + 'enable_client_portal' => 'Dashboard', + 'enable_client_portal_help' => 'Skrýt/zobrazit dashboard v klientském portálu.', + + // Client Passwords + 'enable_portal_password'=>'Faktury chráněné heslem', + 'enable_portal_password_help'=>'Umožní Vám nastavit heslo pro každý kontakt. Pokud heslo nastavíte, tak kontakt ho bude pro zobrazení faktury vždy použít.', + 'send_portal_password'=>'Generovat heslo automaticky', + 'send_portal_password_help'=>'Pokud heslo není nastaveno, bude vygenerováno a zasláno spolu s první fakturou.', + + 'expired' => 'Expirované', + 'invalid_card_number' => 'Číslo platební karty není platné.', + 'invalid_expiry' => 'Datum expirace není platné.', + 'invalid_cvv' => 'CVV není platné.', + 'cost' => 'Cena', + 'create_invoice_for_sample' => 'Poznámka: vytvořte si první fakturu a zde si ji prohlédněte.', + + // User Permissions + 'owner' => 'Vlastník', + 'administrator' => 'Administrátor', + 'administrator_help' => 'Povolit uživatelům spravovat další uživatele, měnit nastavení a všechny záznamy', + 'user_create_all' => 'Vytvářet klienty,faktury atd.', + 'user_view_all' => 'Vidět všechny klienty,faktury atd.', + 'user_edit_all' => 'Editovat všechny klienty,faktury atd.', + 'gateway_help_20' => ':link pro registraci na Sage Pay.', + 'gateway_help_21' => ':link pro registraci na Sage Pay.', + 'partial_due' => 'Částečně splaceno', + 'restore_vendor' => 'Obnovit dodavatele', + 'restored_vendor' => 'Dodavatel úspěšně obnoven', + 'restored_expense' => 'Náklady úspěšně obnoveny', + 'permissions' => 'Práva', + 'create_all_help' => 'Povolit uživatelům měnit záznamy', + 'view_all_help' => 'Povolit uživatelům zobrazit záznamy, které nevytvořili', + 'edit_all_help' => 'Povolit uživatelům měnit záznamy, které nevytvořili', + 'view_payment' => 'Zobrazit platbu', + + 'january' => 'Leden', + 'february' => 'Únor', + 'march' => 'Březen', + 'april' => 'Duben', + 'may' => 'Květen', + 'june' => 'Červen', + 'july' => 'Červenc', + 'august' => 'Srpen', + 'september' => 'Září', + 'october' => 'Říjen', + 'november' => 'Listopad', + 'december' => 'Prosinec', + +); + +return $LANG; + +?>. diff --git a/resources/lang/cs/validation.php b/resources/lang/cs/validation.php new file mode 100644 index 000000000000..85ece14a7f00 --- /dev/null +++ b/resources/lang/cs/validation.php @@ -0,0 +1,115 @@ + ':attribute musí být akceptován.', + 'active_url' => ':attribute není platnou URL adresou.', + 'after' => ':attribute musí být datum po :date.', + 'alpha' => ':attribute může obsahovat pouze písmena.', + 'alpha_dash' => ':attribute může obsahovat pouze písmena, číslice, pomlčky a podtržítka. České znaky (á, é, í, ó, ú, ů, ž, š, č, ř, ď, ť, ň) nejsou podporovány.', + 'alpha_num' => ':attribute může obsahovat pouze písmena a číslice.', + 'array' => ':attribute musí být pole.', + 'before' => ':attribute musí být datum před :date.', + 'between' => [ + 'numeric' => ':attribute musí být hodnota mezi :min a :max.', + 'file' => ':attribute musí být větší než :min a menší než :max Kilobytů.', + 'string' => ':attribute musí být delší než :min a kratší než :max znaků.', + 'array' => ':attribute musí obsahovat nejméně :min a nesmí obsahovat více než :max prvků.', + ], + 'boolean' => ':attribute musí být true nebo false', + 'confirmed' => ':attribute nebylo odsouhlaseno.', + 'date' => ':attribute musí být platné datum.', + 'date_format' => ':attribute není platný formát data podle :format.', + 'different' => ':attribute a :other se musí lišit.', + 'digits' => ':attribute musí být :digits pozic dlouhé.', + 'digits_between' => ':attribute musí být dlouhé nejméně :min a nejvíce :max pozic.', + 'distinct' => 'The :attribute field has a duplicate value.', + 'email' => ':attribute není platný formát.', + 'exists' => 'Zvolená hodnota pro :attribute není platná.', + 'filled' => ':attribute musí být vyplněno.', + 'image' => ':attribute musí být obrázek.', + 'in' => 'Zvolená hodnota pro :attribute není platná.', + 'in_array' => 'The :attribute field does not exist in :other.', + 'integer' => ':attribute musí být celé číslo.', + 'ip' => ':attribute musí být platnou IP adresou.', + 'json' => ':attribute musí být platný JSON řetězec.', + 'max' => [ + 'numeric' => ':attribute musí být nižší než :max.', + 'file' => ':attribute musí být menší než :max Kilobytů.', + 'string' => ':attribute musí být kratší než :max znaků.', + 'array' => ':attribute nesmí obsahovat více než :max prvků.', + ], + 'mimes' => ':attribute musí být jeden z následujících datových typů :values.', + 'min' => [ + 'numeric' => ':attribute musí být větší než :min.', + 'file' => ':attribute musí být větší než :min Kilobytů.', + 'string' => ':attribute musí být delší než :min znaků.', + 'array' => ':attribute musí obsahovat více než :min prvků.', + ], + 'not_in' => 'Zvolená hodnota pro :attribute je neplatná.', + 'numeric' => ':attribute musí být číslo.', + 'present' => 'The :attribute field must be present.', + 'regex' => ':attribute nemá správný formát.', + 'required' => ':attribute musí být vyplněno.', + 'required_if' => ':attribute musí být vyplněno pokud :other je :value.', + 'required_unless' => 'The :attribute field is required unless :other is in :values.', + 'required_with' => ':attribute musí být vyplněno pokud :values je vyplněno.', + 'required_with_all' => ':attribute musí být vyplněno pokud :values je zvoleno.', + 'required_without' => ':attribute musí být vyplněno pokud :values není vyplněno.', + 'required_without_all' => ':attribute musí být vyplněno pokud není žádné z :values zvoleno.', + 'same' => ':attribute a :other se musí shodovat.', + 'size' => [ + 'numeric' => ':attribute musí být přesně :size.', + 'file' => ':attribute musí mít přesně :size Kilobytů.', + 'string' => ':attribute musí být přesně :size znaků dlouhý.', + 'array' => ':attribute musí obsahovat právě :size prvků.', + ], + 'string' => ':attribute musí být řetězec znaků.', + 'timezone' => ':attribute musí být platná časová zóna.', + 'unique' => ':attribute musí být unikátní.', + 'url' => 'Formát :attribute je neplatný.', + + /* + |-------------------------------------------------------------------------- + | Custom Validation Language Lines + |-------------------------------------------------------------------------- + | + | Here you may specify custom validation messages for attributes using the + | convention "attribute.rule" to name the lines. This makes it quick to + | specify a specific custom language line for a given attribute rule. + | + */ + + 'custom' => [ + 'attribute-name' => [ + 'rule-name' => 'custom-message', + ], + ], + + /* + |-------------------------------------------------------------------------- + | Custom Validation Attributes + |-------------------------------------------------------------------------- + | + | The following language lines are used to swap attribute place-holders + | with something more reader friendly such as E-Mail Address instead + | of "email". This simply helps us make messages a little cleaner. + | + */ + + 'attributes' => [ + // + ], + +]; diff --git a/resources/lang/da/texts.php b/resources/lang/da/texts.php index 343d09dfcb65..ca30035e6e07 100644 --- a/resources/lang/da/texts.php +++ b/resources/lang/da/texts.php @@ -493,7 +493,7 @@ return array( 'invoice_history' => 'Faktura historik', 'quote_history' => 'Tilbuds historik', 'current_version' => 'Nuværende version', - 'select_versiony' => 'Vælg version', + 'select_version' => 'Vælg version', 'view_history' => 'Vis historik', 'edit_payment' => 'Redigér betaling', diff --git a/resources/lang/de/texts.php b/resources/lang/de/texts.php index 8561841ad465..9292edba920b 100644 --- a/resources/lang/de/texts.php +++ b/resources/lang/de/texts.php @@ -865,7 +865,7 @@ return array( 'activity_15' => ':user aktualisierte :credit Guthaben', 'activity_16' => ':user archivierte :credit Guthaben', 'activity_17' => ':user löschte :credit Guthaben', - 'activity_18' => ':user löschte Angebot :quote', + 'activity_18' => ':user erstellte Angebot :quote', 'activity_19' => ':user aktualisierte Angebot :quote', 'activity_20' => ':user mailte Angebot :quote an :contact', 'activity_21' => ':contact schaute Angebot :quote an', @@ -916,50 +916,50 @@ return array( 'country' => 'Land', 'include' => 'Hinzufügen', - 'logo_too_large' => 'Your logo is :size, for better PDF performance we suggest uploading an image file less than 200KB', - 'import_freshbooks' => 'Import From FreshBooks', - 'import_data' => 'Import Data', - 'source' => 'Source', + 'logo_too_large' => 'Ihr Logo ist nur :size. Um eine bessere Darstellung im PDF Dokument zu erhalten, empfehlen wir ein Bild größer als 200KB', + 'import_freshbooks' => 'Importiere von FreshBooks', + 'import_data' => 'Importiere Daten', + 'source' => 'Quelle', 'csv' => 'CSV', - 'client_file' => 'Client File', - 'invoice_file' => 'Invoice File', + 'client_file' => 'Kunden Datei', + 'invoice_file' => 'Rechnungs Datei', 'task_file' => 'Task File', - 'no_mapper' => 'No valid mapping for file', - 'invalid_csv_header' => 'Invalid CSV Header', + 'no_mapper' => 'Kein gültiges Mapping für die Datei', + 'invalid_csv_header' => 'Ungültiger CSV Header', 'email_errors' => [ - 'inactive_client' => 'Emails can not be sent to inactive clients', - 'inactive_contact' => 'Emails can not be sent to inactive contacts', - 'inactive_invoice' => 'Emails can not be sent to inactive invoices', - 'user_unregistered' => 'Please register your account to send emails', - 'user_unconfirmed' => 'Please confirm your account to send emails', - 'invalid_contact_email' => 'Invalid contact email', + 'inactive_client' => 'Emails können nicht zu inaktiven Kunden gesendet werden', + 'inactive_contact' => 'Emails können nicht zu inaktiven Kontakten gesendet werden', + 'inactive_invoice' => 'Emails können nicht zu inaktiven Rechnungen gesendet werden', + 'user_unregistered' => 'Bitte registrieren Sie sich um Emails zu versenden', + 'user_unconfirmed' => 'Bitte bestätigen Sie Ihr Konto um Emails zu senden', + 'invalid_contact_email' => 'Ungültige Kontakt Email Adresse', ], - 'client_portal' => 'Client Portal', + 'client_portal' => 'Kunden-Portal', 'admin' => 'Admin', 'disabled' => 'Disabled', - 'show_archived_users' => 'Show archived users', + 'show_archived_users' => 'Zeige archivierte Benutzer', 'notes' => 'Notes', - 'invoice_will_create' => 'client will be created', - 'invoices_will_create' => 'invoices will be created', - 'failed_to_import' => 'The following records failed to import, they either already exist or are missing required fields.', + 'invoice_will_create' => 'Kunde wird erstellt', + 'invoices_will_create' => 'Rechnung wird erstellt', + 'failed_to_import' => 'Die folgenden Daten konnten nicht importiert werden. Entweder sind diese bereits vorhanden oder es fehlen benötigte Felder.', - 'publishable_key' => 'Publishable Key', - 'secret_key' => 'Secret Key', + 'publishable_key' => 'Öffentlicher Schlüssel', + 'secret_key' => 'Geheimer Schlüssel', 'missing_publishable_key' => 'Set your Stripe publishable key for an improved checkout process', 'email_design' => 'Email Design', - 'due_by' => 'Due by :date', + 'due_by' => 'Fällig am :date', 'enable_email_markup' => 'Enable Markup', 'enable_email_markup_help' => 'Make it easier for your clients to pay you by adding schema.org markup to your emails.', - 'template_help_title' => 'Templates Help', - 'template_help_1' => 'Available variables:', + 'template_help_title' => 'Templates Hilfe', + 'template_help_1' => 'Verfügbare Variablen:', 'email_design_id' => 'Email Style', - 'email_design_help' => 'Make your emails look more professional with HTML layouts', - 'plain' => 'Plain', - 'light' => 'Light', - 'dark' => 'Dark', + 'email_design_help' => 'Lassen Sie Ihre Email professioneller mit HTML Layouts aussehen', + 'plain' => 'Einfach', + 'light' => 'Hell', + 'dark' => 'Dunkel', 'industry_help' => 'Used to provide comparisons against the averages of companies of similar size and industry.', 'subdomain_help' => 'Customize the invoice link subdomain or display the invoice on your own website.', @@ -972,19 +972,19 @@ return array( 'color_help' => 'Note: the primary color is also used in the client portal and custom email designs.', 'token_expired' => 'Validation token was expired. Please try again.', - 'invoice_link' => 'Invoice Link', - 'button_confirmation_message' => 'Click to confirm your email address.', - 'confirm' => 'Confirm', - 'email_preferences' => 'Email Preferences', - 'created_invoices' => 'Successfully created :count invoice(s)', - 'next_invoice_number' => 'The next invoice number is :number.', - 'next_quote_number' => 'The next quote number is :number.', + 'invoice_link' => 'Link zur Rechnung', + 'button_confirmation_message' => 'Bitte klicken um Ihre Email-Adresse zu bestätigen.', + 'confirm' => 'Bestätigen', + 'email_preferences' => 'Email Einstellungen', + 'created_invoices' => ':count Rechnung(en) erfolgreich erstellt', + 'next_invoice_number' => 'Die nächste Rechnungsnummer ist :number.', + 'next_quote_number' => 'Die nächste Angebotsnummer ist :number.', - 'days_before' => 'days before', - 'days_after' => 'days after', - 'field_due_date' => 'due date', - 'field_invoice_date' => 'invoice date', - 'schedule' => 'Schedule', + 'days_before' => 'Tage vorher', + 'days_after' => 'Tage danach', + 'field_due_date' => 'Fälligkeitsdatum', + 'field_invoice_date' => 'Rechnungsdatum', + 'schedule' => 'Zeitgesteuert', 'email_designs' => 'Email Designs', 'assigned_when_sent' => 'Assigned when sent', @@ -1036,7 +1036,7 @@ return array( 'convert_currency' => 'Währung umrechnen', // Payment terms - 'num_days' => 'Number of days', + 'num_days' => 'Anzahl Tage', 'create_payment_term' => 'Create Payment Term', 'edit_payment_terms' => 'Edit Payment Term', 'edit_payment_term' => 'Edit Payment Term', @@ -1118,8 +1118,8 @@ return array( 'first_page' => 'Erste Seite', 'all_pages' => 'Alle Seiten', 'last_page' => 'Letzte Seite', - 'all_pages_header' => 'Show header on', - 'all_pages_footer' => 'Show footer on', + 'all_pages_header' => 'Zeige Header auf', + 'all_pages_footer' => 'Zeige Footer auf', 'invoice_currency' => 'Rechnungs-Währung', 'enable_https' => 'Wir empfehlen dringend HTTPS zu verwenden, um Kreditkarten online zu akzeptieren.', 'quote_issued_to' => 'Quote issued to', @@ -1128,77 +1128,77 @@ return array( 'trial_footer' => 'Your free trial lasts :count more days, :link to upgrade now.', 'trial_footer_last_day' => 'Heute ist der letzte Tag Ihrer kostenlosen Probezeit, :link um upzugraden.', 'trial_call_to_action' => 'Kostenlose Probezeit starten', - 'trial_success' => 'Successfully enabled two week free pro plan trial', - 'overdue' => 'Overdue', - 'white_label_text' => 'Purchase a ONE YEAR white label license for $'.WHITE_LABEL_PRICE.' to remove the Invoice Ninja branding from the client portal and help support our project.', + 'trial_success' => 'Erfolgreich eine 2-Wochen Testversion aktiviert', + 'overdue' => 'Überfällig', + 'white_label_text' => 'Kaufen Sie eine 1 Jahres Whitelabel Lizenz zum Preis von $'.WHITE_LABEL_PRICE.' um das Invoice Ninja Branding vom Kundenportal zu entfernen und unser Projekt zu unterstützen.', 'navigation' => 'Navigation', - 'list_invoices' => 'List Invoices', - 'list_clients' => 'List Clients', - 'list_quotes' => 'List Quotes', - 'list_tasks' => 'List Tasks', - 'list_expenses' => 'List Expenses', - 'list_recurring_invoices' => 'List Recurring Invoices', - 'list_payments' => 'List Payments', - 'list_credits' => 'List Credits', - 'tax_name' => 'Tax Name', - 'report_settings' => 'Report Settings', - 'search_hotkey' => 'shortcut is /', + 'list_invoices' => 'Liste Rechnungen', + 'list_clients' => 'Liste Kunden', + 'list_quotes' => 'Liste Angebote', + 'list_tasks' => 'Liste Aufgaben', + 'list_expenses' => 'Liste Ausgaben', + 'list_recurring_invoices' => 'Liste wiederkehrende Rechnungen', + 'list_payments' => 'Liste Zahlungen', + 'list_credits' => 'Liste Guthaben', + 'tax_name' => 'Steuersatz Name', + 'report_settings' => 'Report Einstellungen', + 'search_hotkey' => 'Kürzel ist /', - 'new_user' => 'New User', - 'new_product' => 'New Product', - 'new_tax_rate' => 'New Tax Rate', - 'invoiced_amount' => 'Invoiced Amount', - 'invoice_item_fields' => 'Invoice Item Fields', + 'new_user' => 'Neuer Benutzer', + 'new_product' => 'Neues Produkt', + 'new_tax_rate' => 'Neuer Steuersatz', + 'invoiced_amount' => 'Rechnungsbetrag', + 'invoice_item_fields' => 'Rechnungspositions Feld', 'custom_invoice_item_fields_help' => 'Add a field when creating an invoice item and display the label and value on the PDF.', - 'recurring_invoice_number' => 'Recurring Invoice Number', - 'recurring_invoice_number_prefix_help' => 'Speciy a prefix to be added to the invoice number for recurring invoices. The default value is \'R\'.', + 'recurring_invoice_number' => 'Wiederkehrende Rechnungsnummer', + 'recurring_invoice_number_prefix_help' => 'Geben Sie einen Präfix für wiederkehrende Rechnungen an. Standard ist \'R\'.', 'enable_client_portal' => 'Dashboard', - 'enable_client_portal_help' => 'Show/hide the dashboard page in the client portal.', + 'enable_client_portal_help' => 'Zeige das Dashboard im Kundenportal.', // Client Passwords - 'enable_portal_password'=>'Password protect invoices', - 'enable_portal_password_help'=>'Allows you to set a password for each contact. If a password is set, the contact will be required to enter a password before viewing invoices.', - 'send_portal_password'=>'Generate password automatically', - 'send_portal_password_help'=>'If no password is set, one will be generated and sent with the first invoice.', + 'enable_portal_password'=>'Passwortgeschützte Rechnungen', + 'enable_portal_password_help'=>'Erlaubt Ihnen ein Passwort für jeden Kontakt zu erstellen. Wenn ein Passwort erstellt wurde, muss der Kunde dieses eingeben, bevor er eine Rechnung ansehen darf.', + 'send_portal_password'=>'Erstelle das Passwort automatisch', + 'send_portal_password_help'=>'Wenn kein Passwort gesetzt wurde, wird eins generiert und mit der ersten Rechnung verschickt.', - 'expired' => 'Expired', - 'invalid_card_number' => 'The credit card number is not valid.', - 'invalid_expiry' => 'The expiration date is not valid.', - 'invalid_cvv' => 'The CVV is not valid.', - 'cost' => 'Cost', - 'create_invoice_for_sample' => 'Note: create your first invoice to see a preview here.', + 'expired' => 'Abgelaufen', + 'invalid_card_number' => 'Die Kreditkartennummer ist nicht gültig.', + 'invalid_expiry' => 'Das Ablaufdatum ist nicht gültig.', + 'invalid_cvv' => 'Der CVV Code ist nicht gültig.', + 'cost' => 'Kosten', + 'create_invoice_for_sample' => 'Hinweis: Erstellen Sie Ihre erste Rechnung um hier eine Vorschau zu sehen.', // User Permissions - 'owner' => 'Owner', + 'owner' => 'Eigentümer', 'administrator' => 'Administrator', 'administrator_help' => 'Allow user to manage users, change settings and modify all records', - 'user_create_all' => 'Create clients, invoices, etc.', - 'user_view_all' => 'View all clients, invoices, etc.', - 'user_edit_all' => 'Edit all clients, invoices, etc.', + 'user_create_all' => 'Erstelle Kunden, Rechnungen, usw.', + 'user_view_all' => 'Alle Kunden, Rechnungen, usw. ansehen', + 'user_edit_all' => 'Alle Kunden, Rechnungen, usw. bearbeiten', 'gateway_help_20' => ':link to sign up for Sage Pay.', 'gateway_help_21' => ':link to sign up for Sage Pay.', 'partial_due' => 'Partial Due', 'restore_vendor' => 'Restore Vendor', 'restored_vendor' => 'Successfully restored vendor', 'restored_expense' => 'Successfully restored expense', - 'permissions' => 'Permissions', + 'permissions' => 'Berechtigungen', 'create_all_help' => 'Allow user to create and modify records', 'view_all_help' => 'Allow user to view records they didn\'t create', 'edit_all_help' => 'Allow user to modify records they didn\'t create', - 'view_payment' => 'View Payment', + 'view_payment' => 'Zahlung zeigen', - 'january' => 'January', - 'february' => 'February', - 'march' => 'March', + 'january' => 'Januar', + 'february' => 'Februar', + 'march' => 'März', 'april' => 'April', - 'may' => 'May', - 'june' => 'June', - 'july' => 'July', + 'may' => 'Mai', + 'june' => 'Juni', + 'july' => 'Juli', 'august' => 'August', 'september' => 'September', - 'october' => 'October', + 'october' => 'Oktober', 'november' => 'November', - 'december' => 'December', + 'december' => 'Dezember', ); diff --git a/resources/lang/de/validation.php b/resources/lang/de/validation.php index e8be6d98b63c..11051273e2f5 100644 --- a/resources/lang/de/validation.php +++ b/resources/lang/de/validation.php @@ -76,7 +76,7 @@ return array( "notmasked" => "Die Werte sind maskiert", "less_than" => ':attribute muss weniger als :value sein', "has_counter" => 'Der Wert muss {$counter} beinhalten', - "valid_contacts" => "Alle Kontake müssen entweder einen Namen oder eine E-Mail Adresse haben", + "valid_contacts" => "Alle Kontakte müssen entweder einen Namen oder eine E-Mail Adresse haben", "valid_invoice_items" => "Die Rechnung übersteigt den maximalen Betrag", /* diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index 163538ed4f88..1a7d6a1267bb 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -423,7 +423,7 @@ $LANG = array( 'invoice_history' => 'Invoice History', 'quote_history' => 'Quote History', 'current_version' => 'Current version', - 'select_versiony' => 'Select version', + 'select_version' => 'Select version', 'view_history' => 'View History', 'edit_payment' => 'Edit Payment', 'updated_payment' => 'Successfully updated payment', @@ -1176,6 +1176,9 @@ $LANG = array( 'page_size' => 'Page Size', 'live_preview_disabled' => 'Live preview has been disabled to support selected font', 'invoice_number_padding' => 'Padding', + 'preview' => 'Preview', + 'list_vendors' => 'List Vendors', + 'add_users_not_supported' => 'Upgrade to the Enterprise plan to add additional users to your account.', // Payment updates 'refund_payment' => 'Refund Payment', diff --git a/resources/lang/es/texts.php b/resources/lang/es/texts.php index c5f7358dc72a..d53f4159fb37 100644 --- a/resources/lang/es/texts.php +++ b/resources/lang/es/texts.php @@ -466,7 +466,7 @@ return array( 'invoice_history' => 'Facturar Historial', 'quote_history' => 'Cotizar Historial', 'current_version' => 'Versión actual', - 'select_versiony' => 'Seleccionar versión', + 'select_version' => 'Seleccionar versión', 'view_history' => 'Ver Historial', 'edit_payment' => 'Editar Pago', diff --git a/resources/lang/es_ES/texts.php b/resources/lang/es_ES/texts.php index 9687f82486ba..a5af7020b250 100644 --- a/resources/lang/es_ES/texts.php +++ b/resources/lang/es_ES/texts.php @@ -486,7 +486,7 @@ return array( 'invoice_history' => 'Historial de Facturas', 'quote_history' => 'Historial de Presupuestos', 'current_version' => 'Versión Actual', - 'select_versiony' => 'Seleccione la Versión', + 'select_version' => 'Seleccione la Versión', 'view_history' => 'Ver Historial', 'edit_payment' => 'Editar Pago', diff --git a/resources/lang/fr/texts.php b/resources/lang/fr/texts.php index c6d27620a2c5..23bd4a4d8665 100644 --- a/resources/lang/fr/texts.php +++ b/resources/lang/fr/texts.php @@ -120,7 +120,7 @@ return array( 'active_clients' => 'clients actifs', 'invoices_past_due' => 'Date limite de paiement dépassée', 'upcoming_invoices' => 'Factures à venir', - 'average_invoice' => 'Moyenne de facturation', + 'average_invoice' => 'Facture moyenne', // list pages 'archive' => 'Archiver', @@ -437,8 +437,8 @@ return array( 'invalid_counter' => 'Pour éviter un éventuel conflit, merci de définir un préfixe pour le numéro de facture ou pour le numéro de devis', 'mark_sent' => 'Marquer comme envoyé', - 'gateway_help_1' => ':link to sign up for Authorize.net.', - 'gateway_help_2' => ':link to sign up for Authorize.net.', + 'gateway_help_1' => ':link pour vous inscrire à Authorize.net.', + 'gateway_help_2' => ':link pour vous inscrire à Authorize.net.', 'gateway_help_17' => ':link pour obtenir votre signature PayPal API.', 'gateway_help_27' => ':link pour vous enregistrer sur TwoCheckout.', @@ -503,10 +503,10 @@ return array( 'approve' => 'Accepter', 'token_billing_type_id' => 'Jeton de paiement', - 'token_billing_help' => 'Enables you to store credit cards with your gateway, and charge them at a later date.', + 'token_billing_help' => 'Permet de stocker des cartes de crédit avec votre passerelle, et de déclancher le paiement à une date ultérieure.', 'token_billing_1' => 'Désactiver', - 'token_billing_2' => 'Opt-in - checkbox is shown but not selected', - 'token_billing_3' => 'Opt-out - checkbox is shown and selected', + 'token_billing_2' => 'Opt-in - Case à cocher affichée mais non sélectionnée', + 'token_billing_3' => 'Opt-out - Case à cocher affichée et sélectionné', 'token_billing_4' => 'Toujours', 'token_billing_checkbox' => 'Enregistrer les informations de paiement', 'view_in_gateway' => 'Voir sur :gateway', @@ -522,8 +522,8 @@ return array( 'billing_address' => 'Adresse de facturation', 'billing_method' => 'Méthode de facturation', 'order_overview' => 'Résumé de la commande', - '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.', + 'match_address' => '*L\'adresse doit correspondre à l\'adresse associée à la carte de crédit.', + 'click_once' => '*S\'il vous plaît cliquer sur "PAYER MAINTENANT" une seule fois - la transaction peut prendre jusqu\'à 1 minute.', 'default_invoice_footer' => 'Définir par défaut', 'invoice_footer' => 'Pied de facture', @@ -575,7 +575,7 @@ return array( 'resend_confirmation' => 'Renvoyer l\'email de confirmation', 'confirmation_resent' => 'L\'email de confirmation a été renvoyé', - 'gateway_help_42' => ':link to sign up for BitPay.
Note: use a Legacy API Key, not an API token.', + 'gateway_help_42' => ':link pour vous inscrire à BitPay
Remarque:. utiliser une clé API "Legacy", pas un jeton.', 'payment_type_credit_card' => 'Carte de crédit', 'payment_type_paypal' => 'PayPal', 'payment_type_bitcoin' => 'Bitcoin', @@ -588,7 +588,7 @@ return array( 'client_name' => 'Nom du client', 'pdf_settings' => 'Réglages PDF', 'product_settings' => 'Réglages du produit', - 'auto_wrap' => 'Auto Line Wrap', + 'auto_wrap' => 'Retour à la ligne automatique', 'duplicate_post' => 'Attention: la page précédente a été soumise deux fois. La deuxième soumission a été ignorée.', 'view_documentation' => 'Voir documentation', 'app_title' => 'Outil de facturation gratuit & Open-Source', @@ -755,11 +755,11 @@ return array( 'show_line_item_tax' => 'Display line item taxes inline', 'iframe_url' => 'Site internet', - 'iframe_url_help1' => 'Copy the following code to a page on your site.', - 'iframe_url_help2' => 'You can test the feature by clicking \'View as recipient\' for an invoice.', + 'iframe_url_help1' => 'Copiez le code suivant sur une page de votre site.', + 'iframe_url_help2' => 'Vous pouvez tester la fonctionnalité en cliquant sur \'Voir en tant que destinataire\' pour une facture.', 'auto_bill' => 'Facturation automatique', - 'military_time' => '24 Hour Time', + 'military_time' => '24H', 'last_sent' => 'Dernier envoi', 'reminder_emails' => 'Emails de rappel', @@ -769,21 +769,21 @@ return array( 'first_reminder' => 'Premier rappel', 'second_reminder' => 'Second rappel', 'third_reminder' => 'Troisième rappel', - 'num_days_reminder' => 'Days after due date', - 'reminder_subject' => 'Reminder: Invoice :invoice from :account', + 'num_days_reminder' => 'Jours après la date d\'échéance', + 'reminder_subject' => 'Rappel: Facture :invoice de :account', 'reset' => 'Remettre à zéro', 'invoice_not_found' => 'La facture demandée n\'est pas disponible', - 'referral_program' => 'Referral Program', - 'referral_code' => 'Referral Code', - 'last_sent_on' => 'Last sent on :date', + 'referral_program' => 'Programme de parrainage', + 'referral_code' => 'Code de Parrainage', + 'last_sent_on' => 'Dernier envoi le :date', - 'page_expire' => 'This page will expire soon, :click_here to keep working', + 'page_expire' => 'Cette page va bientôt expirer, :click_here pour continuer à travailler', 'upcoming_quotes' => 'Devis à venir', 'expired_quotes' => 'Devis expirés', 'sign_up_using' => 'Connexion avec', - 'invalid_credentials' => 'These credentials do not match our records', + 'invalid_credentials' => 'Ces informations de connexion sont invalides', 'show_all_options' => 'Voir toutes les options', 'user_details' => 'Utilisateur', 'oneclick_login' => 'Connexion en 1 clic', @@ -801,19 +801,19 @@ return array( 'notification_quote_bounced' => 'We were unable to deliver Quote :invoice to :contact.', 'notification_quote_bounced_subject' => 'Unable to deliver Quote :invoice', - 'custom_invoice_link' => 'Custom Invoice Link', - 'total_invoiced' => 'Total Invoiced', + 'custom_invoice_link' => 'Personnaliser le lien de la facture', + 'total_invoiced' => 'Total facturé', 'open_balance' => 'Open Balance', 'verify_email' => 'Please visit the link in the account confirmation email to verify your email address.', - 'basic_settings' => 'Basic Settings', + 'basic_settings' => 'Paramètres généraux', 'pro' => 'Pro', 'gateways' => 'Passerelles de paiement', - 'recurring_too_soon' => 'Il est trop tôt pour créer la prochaine facture récurrente, it\'s scheduled for :date', + 'recurring_too_soon' => 'Il est trop tôt pour créer la prochaine facture récurrente, prévue pour le :date', - 'next_send_on' => 'Send Next: :date', + 'next_send_on' => 'Envoi suivant: :date', 'no_longer_running' => 'This invoice is not scheduled to run', 'general_settings' => 'Réglages généraux', - 'customize' => 'Customiser', + 'customize' => 'Personnaliser', 'oneclick_login_help' => 'Connectez un compte pour vous connecter sans votre mot de passe', 'referral_code_help' => 'Gagnez de l\'argent en partagent notre outil en ligne', @@ -831,12 +831,12 @@ return array( 'recurring_hour' => 'Recurring Hour', 'pattern' => 'Pattern', 'pattern_help_title' => 'Pattern Help', - 'pattern_help_1' => 'Create custom invoice and quote numbers by specifying a pattern', - 'pattern_help_2' => 'Available variables:', - 'pattern_help_3' => 'For example, :example would be converted to :value', + 'pattern_help_1' => 'Créer des numéros de facture et devis personnalisés selon un modèle', + 'pattern_help_2' => 'Variables disponibles:', + 'pattern_help_3' => 'Par exemple, :example sera converti en :value', 'see_options' => 'Voir les options', - 'invoice_counter' => 'Invoice Counter', - 'quote_counter' => 'Quote Counter', + 'invoice_counter' => 'Compteur de factures', + 'quote_counter' => 'Compteur de devis', 'type' => 'Type', 'activity_1' => ':user a crée le client :client', @@ -869,7 +869,7 @@ return array( 'activity_28' => ':user a restauré le crédit :credit', 'activity_29' => ':contact a approuvé le devis :quote', - 'payment' => 'Paiment', + 'payment' => 'Paiement', 'system' => 'Système', 'signature' => 'Signature email', 'default_messages' => 'Messages par défaut', @@ -908,51 +908,51 @@ return array( 'include' => 'Inclure', 'logo_too_large' => 'Your logo is :size, for better PDF performance we suggest uploading an image file less than 200KB', - 'import_freshbooks' => 'Import From FreshBooks', - 'import_data' => 'Import Data', + 'import_freshbooks' => 'Importer depuis FreshBooks', + 'import_data' => 'Importer des données', 'source' => 'Source', 'csv' => 'CSV', - 'client_file' => 'Client File', - 'invoice_file' => 'Invoice File', - 'task_file' => 'Task File', - 'no_mapper' => 'No valid mapping for file', - 'invalid_csv_header' => 'Invalid CSV Header', + 'client_file' => 'Fichier de clients', + 'invoice_file' => 'Fichier de factures', + 'task_file' => 'Fichier de tâches', + 'no_mapper' => 'Mappage invalide pour ce fichier', + 'invalid_csv_header' => 'En-tête du fichier CSV invalide', 'email_errors' => [ - 'inactive_client' => 'Emails can not be sent to inactive clients', - 'inactive_contact' => 'Emails can not be sent to inactive contacts', - 'inactive_invoice' => 'Emails can not be sent to inactive invoices', - 'user_unregistered' => 'Please register your account to send emails', - 'user_unconfirmed' => 'Please confirm your account to send emails', - 'invalid_contact_email' => 'Invalid contact email', + 'inactive_client' => 'Les mails ne peuvent être envoyés aux clients inactifs', + 'inactive_contact' => 'Les mails ne peuvent être envoyés aux contacts inactifs', + 'inactive_invoice' => 'Les mails ne peuvent être envoyés aux factures inactives', + 'user_unregistered' => 'Veuillez vous inscrire afin d\'envoyer des mails', + 'user_unconfirmed' => 'Veuillez confirmer votre compte afin de permettre l\'envoi de mail', + 'invalid_contact_email' => 'Adresse mail du contact invalide', ], - 'client_portal' => 'Client Portal', + 'client_portal' => 'Portail client', 'admin' => 'Admin', - 'disabled' => 'Disabled', - 'show_archived_users' => 'Show archived users', + 'disabled' => 'Désactivé', + 'show_archived_users' => 'Afficher les utilisateurs archivés', 'notes' => 'Notes', 'invoice_will_create' => 'client will be created', 'invoices_will_create' => 'invoices will be created', 'failed_to_import' => 'The following records failed to import, they either already exist or are missing required fields.', - 'publishable_key' => 'Publishable Key', - 'secret_key' => 'Secret Key', - 'missing_publishable_key' => 'Set your Stripe publishable key for an improved checkout process', + 'publishable_key' => 'Clé publique', + 'secret_key' => 'Clé secrète', + 'missing_publishable_key' => 'Saisissez votre clé publique Stripe pour un processus de commande amélioré', 'email_design' => 'Email Design', - 'due_by' => 'Due by :date', + 'due_by' => 'A échéanche du :date', 'enable_email_markup' => 'Enable Markup', 'enable_email_markup_help' => 'Make it easier for your clients to pay you by adding schema.org markup to your emails.', 'template_help_title' => 'Templates Help', - 'template_help_1' => 'Available variables:', - 'email_design_id' => 'Email Style', + 'template_help_1' => 'Variable disponibles :', + 'email_design_id' => 'Style de mail', 'email_design_help' => 'Make your emails look more professional with HTML layouts', - 'plain' => 'Plain', - 'light' => 'Light', - 'dark' => 'Dark', + 'plain' => 'Brut', + 'light' => 'Clair', + 'dark' => 'Sombre', - 'industry_help' => 'Used to provide comparisons against the averages of companies of similar size and industry.', + 'industry_help' => 'Utilisé dans le but de fournir des statistiques la taille et le secteur de l\'entreprise.', 'subdomain_help' => 'Customize the invoice link subdomain or display the invoice on your own website.', 'invoice_number_help' => 'Specify a prefix or use a custom pattern to dynamically set the invoice number.', 'quote_number_help' => 'Specify a prefix or use a custom pattern to dynamically set the quote number.', @@ -962,36 +962,36 @@ return array( 'custom_invoice_charges_helps' => 'Add a text input to the invoice create/edit page and include the charge in the invoice subtotals.', 'color_help' => 'Note: the primary color is also used in the client portal and custom email designs.', - 'token_expired' => 'Validation token was expired. Please try again.', - 'invoice_link' => 'Invoice Link', - 'button_confirmation_message' => 'Click to confirm your email address.', - 'confirm' => 'Confirm', - 'email_preferences' => 'Email Preferences', - 'created_invoices' => 'Successfully created :count invoice(s)', - 'next_invoice_number' => 'The next invoice number is :number.', - 'next_quote_number' => 'The next quote number is :number.', + 'token_expired' => 'Validation jeton expiré. Veuillez réessayer.', + 'invoice_link' => 'Lien vers la facture', + 'button_confirmation_message' => 'Cliquez pour confirmer votre adresse e-mail.', + 'confirm' => 'Confirmer', + 'email_preferences' => 'Préférences de mail', + 'created_invoices' => ':count factures(s) créées avec succès', + 'next_invoice_number' => 'Le prochain numéro de facture est :number.', + 'next_quote_number' => 'Le prochain numéro de devis est :number.', 'days_before' => 'days before', 'days_after' => 'days after', - 'field_due_date' => 'due date', - 'field_invoice_date' => 'invoice date', + 'field_due_date' => 'date d\'échéance', + 'field_invoice_date' => 'Date de la facture', 'schedule' => 'Schedule', 'email_designs' => 'Email Designs', 'assigned_when_sent' => 'Assigned when sent', 'white_label_custom_css' => ':link for $'.WHITE_LABEL_PRICE.' to enable custom styling and help support our project.', - 'white_label_purchase_link' => 'Purchase a white label license', + 'white_label_purchase_link' => 'Acheter une licence marque blanche', // Expense / vendor - 'expense' => 'Expense', - 'expenses' => 'Expenses', + 'expense' => 'Dépense', + 'expenses' => 'Dépenses', 'new_expense' => 'Enter Expense', 'enter_expense' => 'Enter Expense', - 'vendors' => 'Vendors', - 'new_vendor' => 'New Vendor', + 'vendors' => 'Fournisseurs', + 'new_vendor' => 'Nouveau fournisseur', 'payment_terms_net' => 'Net', 'vendor' => 'Vendor', - 'edit_vendor' => 'Edit Vendor', + 'edit_vendor' => 'Editer le fournisseur', 'archive_vendor' => 'Archive Vendor', 'delete_vendor' => 'Delete Vendor', 'view_vendor' => 'View Vendor', @@ -1001,37 +1001,37 @@ return array( 'archived_expenses' => 'Successfully archived expenses', // Expenses - 'expense_amount' => 'Expense Amount', + 'expense_amount' => 'Montant de la dépense', 'expense_balance' => 'Expense Balance', - 'expense_date' => 'Expense Date', + 'expense_date' => 'Date de la dépense', 'expense_should_be_invoiced' => 'Should this expense be invoiced?', 'public_notes' => 'Public Notes', - 'invoice_amount' => 'Invoice Amount', - 'exchange_rate' => 'Exchange Rate', - 'yes' => 'Yes', - 'no' => 'No', - 'should_be_invoiced' => 'Should be invoiced', - 'view_expense' => 'View expense # :expense', - 'edit_expense' => 'Edit Expense', - 'archive_expense' => 'Archive Expense', - 'delete_expense' => 'Delete Expense', - 'view_expense_num' => 'Expense # :expense', + 'invoice_amount' => 'Montant de la facture', + 'exchange_rate' => 'Taux de change', + 'yes' => 'Oui', + 'no' => 'Non', + 'should_be_invoiced' => 'Devrait être facturé', + 'view_expense' => 'Voir la dépense # :expense', + 'edit_expense' => 'Editer la dépensee', + 'archive_expense' => 'Archiver le dépense', + 'delete_expense' => 'supprimer la dépense', + 'view_expense_num' => 'Dépense # :expense', 'updated_expense' => 'Successfully updated expense', 'created_expense' => 'Successfully created expense', - 'enter_expense' => 'Enter Expense', - 'view' => 'View', - 'restore_expense' => 'Restore Expense', + 'enter_expense' => 'Entrer la dépense', + 'view' => 'Voir', + 'restore_expense' => 'Restorer la dépense', 'invoice_expense' => 'Invoice Expense', 'expense_error_multiple_clients' => 'The expenses can\'t belong to different clients', 'expense_error_invoiced' => 'Expense has already been invoiced', 'convert_currency' => 'Convert currency', // Payment terms - 'num_days' => 'Number of days', - 'create_payment_term' => 'Create Payment Term', - 'edit_payment_terms' => 'Edit Payment Term', - 'edit_payment_term' => 'Edit Payment Term', - 'archive_payment_term' => 'Archive Payment Term', + 'num_days' => 'Nombre de jours', + 'create_payment_term' => 'Créer une condition de paiement', + 'edit_payment_terms' => 'Editer condition de paiement', + 'edit_payment_term' => 'Editer la condition de paiement', + 'archive_payment_term' => 'Archiver la condition de paiement', // recurring due dates 'recurring_due_dates' => 'Recurring Invoice Due Dates', @@ -1048,56 +1048,56 @@ return array(
  • Today is the Friday, due date is the 1st Friday after. The due date will be next Friday, not today.
  • ', - 'due' => 'Due', - 'next_due_on' => 'Due Next: :date', - 'use_client_terms' => 'Use client terms', - 'day_of_month' => ':ordinal day of month', - 'last_day_of_month' => 'Last day of month', + 'due' => 'Dû', + 'next_due_on' => 'Prochaine échéance: :date', + 'use_client_terms' => 'Utiliser les conditions de paiement du client', + 'day_of_month' => ':ordinal jour du mois', + 'last_day_of_month' => 'Dernier jour du mois', 'day_of_week_after' => ':ordinal :day after', - 'sunday' => 'Sunday', - 'monday' => 'Monday', - 'tuesday' => 'Tuesday', - 'wednesday' => 'Wednesday', - 'thursday' => 'Thursday', - 'friday' => 'Friday', - 'saturday' => 'Saturday', + 'sunday' => 'Dimanche', + 'monday' => 'Lundi', + 'tuesday' => 'Mardi', + 'wednesday' => 'Mercredi', + 'thursday' => 'Jeudi', + 'friday' => 'Vendredi', + 'saturday' => 'Samedi', // Fonts 'header_font_id' => 'Header Font', 'body_font_id' => 'Body Font', 'color_font_help' => 'Note: the primary color and fonts are also used in the client portal and custom email designs.', - 'live_preview' => 'Live Preview', - 'invalid_mail_config' => 'Unable to send email, please check that the mail settings are correct.', + 'live_preview' => 'Aperçu', + 'invalid_mail_config' => 'Impossible d\'envoyer le mail, veuillez vérifier que les paramètres de messagerie sont corrects.', - 'invoice_message_button' => 'To view your invoice for :amount, click the button below.', - 'quote_message_button' => 'To view your quote for :amount, click the button below.', - 'payment_message_button' => 'Thank you for your payment of :amount.', - 'payment_type_direct_debit' => 'Direct Debit', - 'bank_accounts' => 'Bank Accounts', - 'add_bank_account' => 'Add Bank Account', - 'setup_account' => 'Setup Account', - 'import_expenses' => 'Import Expenses', - 'bank_id' => 'bank', + 'invoice_message_button' => 'Pour visionner votre facture de :amount, cliquer sur le lien ci-dessous', + 'quote_message_button' => 'Pour visionner votre devis de :amount, cliquer sur le lien ci-dessous', + 'payment_message_button' => 'Merci pour votre paiement de :amount.', + 'payment_type_direct_debit' => 'Prélèvement', + 'bank_accounts' => 'Comptes bancaires', + 'add_bank_account' => 'Ajouter un compte bancaire', + 'setup_account' => 'Paraméter le compte', + 'import_expenses' => 'Importer les dépenses', + 'bank_id' => 'banque', 'integration_type' => 'Integration Type', - 'updated_bank_account' => 'Successfully updated bank account', - 'edit_bank_account' => 'Edit Bank Account', - 'archive_bank_account' => 'Archive Bank Account', - 'archived_bank_account' => 'Successfully archived bank account', - 'created_bank_account' => 'Successfully created bank account', - 'validate_bank_account' => 'Validate Bank Account', + 'updated_bank_account' => 'Compte bancaire mis à jour', + 'edit_bank_account' => 'Editer le compte bancaire', + 'archive_bank_account' => 'Archiver le compte bancaire', + 'archived_bank_account' => 'Compte bancaire archivé', + 'created_bank_account' => 'Compte bancaire créé', + 'validate_bank_account' => 'Valider le compte bancaire', 'bank_accounts_help' => 'Connect a bank account to automatically import expenses and create vendors. Supports American Express and 400+ US banks.', 'bank_password_help' => 'Note: your password is transmitted securely and never stored on our servers.', - 'bank_password_warning' => 'Warning: your password may be transmitted in plain text, consider enabling HTTPS.', - 'username' => 'Username', - 'account_number' => 'Account Number', - 'account_name' => 'Account Name', + 'bank_password_warning' => 'Attention: votre mot de passe peut être transmis en clair, pensez à activer HTTPS.', + 'username' => 'Nom d\'utilisateur', + 'account_number' => 'N° de compte', + 'account_name' => 'Nom du compte', 'bank_account_error' => 'Failed to retreive account details, please check your credentials.', - 'status_approved' => 'Approved', - 'quote_settings' => 'Quote Settings', - 'auto_convert_quote' => 'Auto convert quote', - 'auto_convert_quote_help' => 'Automatically convert a quote to an invoice when approved by a client.', - 'validate' => 'Validate', + 'status_approved' => 'Approuvé', + 'quote_settings' => 'Paramètres des devis', + 'auto_convert_quote' => 'Convertir automatiquement les devis', + 'auto_convert_quote_help' => 'Convertissez automatiquement un devis en facture dès qu\'il est approuvé par le client.', + 'validate' => 'Valider', 'info' => 'Info', 'imported_expenses' => 'Successfully created :count_vendors vendor(s) and :count_expenses expense(s)', @@ -1105,91 +1105,91 @@ return array( 'expense_error_multiple_currencies' => 'The expenses can\'t have different currencies.', 'expense_error_mismatch_currencies' => 'The client\'s currency does not match the expense currency.', 'trello_roadmap' => 'Trello Roadmap', - 'header_footer' => 'Header/Footer', - 'first_page' => 'first page', - 'all_pages' => 'all pages', - 'last_page' => 'last page', + 'header_footer' => 'En-tête/Pied de page', + 'first_page' => 'première page', + 'all_pages' => 'toutes les pages', + 'last_page' => 'dernière page', 'all_pages_header' => 'Show header on', 'all_pages_footer' => 'Show footer on', - 'invoice_currency' => 'Invoice Currency', - 'enable_https' => 'We strongly recommend using HTTPS to accept credit card details online.', - 'quote_issued_to' => 'Quote issued to', - 'show_currency_code' => 'Currency Code', + 'invoice_currency' => 'Devise de la facture', + 'enable_https' => 'Nous vous recommandons fortement d\'activer le HTTPS si vous acceptez les paiements en ligne.', + 'quote_issued_to' => 'Devis à l\'attention de', + 'show_currency_code' => 'Code de la devise', 'trial_message' => 'Your account will receive a free two week trial of our pro plan.', 'trial_footer' => 'Your free trial lasts :count more days, :link to upgrade now.', - 'trial_footer_last_day' => 'This is the last day of your free trial, :link to upgrade now.', - 'trial_call_to_action' => 'Start Free Trial', + 'trial_footer_last_day' => 'Ceci est le dernier jour de votre essai gratuit, :link pour mettre à niveau maintenant.', + 'trial_call_to_action' => 'Commencer l\'essai gratuit', 'trial_success' => 'Successfully enabled two week free pro plan trial', - 'overdue' => 'Overdue', + 'overdue' => 'Impayé', 'white_label_text' => 'Purchase a ONE YEAR white label license for $'.WHITE_LABEL_PRICE.' to remove the Invoice Ninja branding from the client portal and help support our project.', 'navigation' => 'Navigation', - 'list_invoices' => 'List Invoices', - 'list_clients' => 'List Clients', - 'list_quotes' => 'List Quotes', - 'list_tasks' => 'List Tasks', - 'list_expenses' => 'List Expenses', - 'list_recurring_invoices' => 'List Recurring Invoices', - 'list_payments' => 'List Payments', + 'list_invoices' => 'Liste des factures', + 'list_clients' => 'Liste des clients', + 'list_quotes' => 'Liste des devis', + 'list_tasks' => 'Liste des tâches', + 'list_expenses' => 'Liste des dépenses', + 'list_recurring_invoices' => 'Liste des factures récurrentes', + 'list_payments' => 'Liste des paiements', 'list_credits' => 'List Credits', - 'tax_name' => 'Tax Name', + 'tax_name' => 'Nom de la taxe', 'report_settings' => 'Report Settings', 'search_hotkey' => 'shortcut is /', - 'new_user' => 'New User', - 'new_product' => 'New Product', - 'new_tax_rate' => 'New Tax Rate', - 'invoiced_amount' => 'Invoiced Amount', + 'new_user' => 'Nouvel utilisateur', + 'new_product' => 'Nouvel article', + 'new_tax_rate' => 'Nouveau taux de taxe', + 'invoiced_amount' => 'Montant de la facture', 'invoice_item_fields' => 'Invoice Item Fields', 'custom_invoice_item_fields_help' => 'Add a field when creating an invoice item and display the label and value on the PDF.', 'recurring_invoice_number' => 'Recurring Invoice Number', 'recurring_invoice_number_prefix_help' => 'Speciy a prefix to be added to the invoice number for recurring invoices. The default value is \'R\'.', - 'enable_client_portal' => 'Dashboard', - 'enable_client_portal_help' => 'Show/hide the dashboard page in the client portal.', + 'enable_client_portal' => 'Tableau de bord', + 'enable_client_portal_help' => 'Afficher / masquer le tableau de bord sur le portail client.', // Client Passwords - 'enable_portal_password'=>'Password protect invoices', + 'enable_portal_password'=>'Protéger les factures avec un mot de passe', 'enable_portal_password_help'=>'Allows you to set a password for each contact. If a password is set, the contact will be required to enter a password before viewing invoices.', - 'send_portal_password'=>'Generate password automatically', + 'send_portal_password'=>'Générer un mot de passe automatiquement', 'send_portal_password_help'=>'If no password is set, one will be generated and sent with the first invoice.', - 'expired' => 'Expired', - 'invalid_card_number' => 'The credit card number is not valid.', - 'invalid_expiry' => 'The expiration date is not valid.', - 'invalid_cvv' => 'The CVV is not valid.', - 'cost' => 'Cost', + 'expired' => 'Expiré', + 'invalid_card_number' => 'Le numéro de carte bancaire est invalide.', + 'invalid_expiry' => 'La date d\'expiration est invalide.', + 'invalid_cvv' => 'Le code de sécurité est incorrect.', + 'cost' => 'Coût', 'create_invoice_for_sample' => 'Note: create your first invoice to see a preview here.', // User Permissions - 'owner' => 'Owner', - 'administrator' => 'Administrator', - 'administrator_help' => 'Allow user to manage users, change settings and modify all records', - 'user_create_all' => 'Create clients, invoices, etc.', - 'user_view_all' => 'View all clients, invoices, etc.', - 'user_edit_all' => 'Edit all clients, invoices, etc.', - 'gateway_help_20' => ':link to sign up for Sage Pay.', - 'gateway_help_21' => ':link to sign up for Sage Pay.', + 'owner' => 'Propriétaire', + 'administrator' => 'Administrateur', + 'administrator_help' => 'Permettre à l\'utilisateur de gérer les utilisateurs, modifier les paramètres et de modifier tous les enregistrements', + 'user_create_all' => 'Créer des clients, des factures, etc.', + 'user_view_all' => 'Voir tous les clients, les factures, etc.', + 'user_edit_all' => 'Modifier tous les clients, les factures, etc.', + 'gateway_help_20' => ':link pour vous inscrire à Sage Pay.', + 'gateway_help_21' => ':link pour vous inscrire à Sage Pay.', 'partial_due' => 'Partial Due', - 'restore_vendor' => 'Restore Vendor', - 'restored_vendor' => 'Successfully restored vendor', - 'restored_expense' => 'Successfully restored expense', + 'restore_vendor' => 'Restorer le fournisseur', + 'restored_vendor' => 'Fournisseur restoré', + 'restored_expense' => 'Dépense restorée', 'permissions' => 'Permissions', - 'create_all_help' => 'Allow user to create and modify records', + 'create_all_help' => 'Autoriser l\'utilisateur à créer et éditer tous les enregistrements', 'view_all_help' => 'Allow user to view records they didn\'t create', 'edit_all_help' => 'Allow user to modify records they didn\'t create', - 'view_payment' => 'View Payment', + 'view_payment' => 'Voir le paiement', - 'january' => 'January', - 'february' => 'February', - 'march' => 'March', - 'april' => 'April', - 'may' => 'May', - 'june' => 'June', - 'july' => 'July', - 'august' => 'August', - 'september' => 'September', - 'october' => 'October', - 'november' => 'November', - 'december' => 'December', + 'january' => 'Janvier', + 'february' => 'Février', + 'march' => 'Mars', + 'april' => 'Avril', + 'may' => 'Mai', + 'june' => 'Juin', + 'july' => 'Juillet', + 'august' => 'Août', + 'september' => 'Septembre', + 'october' => 'Octobre', + 'november' => 'Novembre', + 'december' => 'Décembre', ); diff --git a/resources/lang/fr_CA/texts.php b/resources/lang/fr_CA/texts.php index e282e42f60fc..7673ce23ee5c 100644 --- a/resources/lang/fr_CA/texts.php +++ b/resources/lang/fr_CA/texts.php @@ -487,7 +487,7 @@ return array( 'invoice_history' => 'Historique des factures', 'quote_history' => 'Historique des soumissions', 'current_version' => 'Version courante', - 'select_versiony' => 'Choix de la verison', + 'select_version' => 'Choix de la verison', 'view_history' => 'Consulter l\'historique', 'edit_payment' => 'Éditer le paiement', diff --git a/resources/lang/it/texts.php b/resources/lang/it/texts.php index 8eff43466e9c..7349f06aaa87 100644 --- a/resources/lang/it/texts.php +++ b/resources/lang/it/texts.php @@ -382,11 +382,11 @@ return array( 'converted_to_invoice' => 'Il preventivo è stato convertito a fattura con successo', 'quote_subject' => 'Nuovo preventivo da :account', - 'quote_message' => 'Per visualizzare il vostro preventivo per :amount, cliccare il collegamento sotto.', - 'quote_link_message' => 'Per visualizzare il preventivo del vostro cliante cliccate il collegamento sotto:', + 'quote_message' => 'Per visualizzare il vostro preventivo di :amount, cliccate il collegamento sotto.', + 'quote_link_message' => 'Per visualizzare il preventivo del vostro cliente cliccate il collegamento sotto:', 'notification_quote_sent_subject' => 'Il preventivo :invoice è stato inviato a :client', 'notification_quote_viewed_subject' => 'Il preventivo :invoice è stato visualizzato da :client', - 'notification_quote_sent' => 'Al seguente cliente :client è stata inviata la fattura :invoice per :amount.', + 'notification_quote_sent' => 'Al seguente cliente :client è stata inviato il preventivo :invoice per un importo di :amount.', 'notification_quote_viewed' => 'Il seguente cliente :client ha visualizzato il preventivo :invoice di :amount.', 'session_expired' => 'La vostra sessione è scaduta.', @@ -489,7 +489,7 @@ return array( 'invoice_history' => 'Invoice History', 'quote_history' => 'Quote History', 'current_version' => 'Current version', - 'select_versiony' => 'Select version', + 'select_version' => 'Select version', 'view_history' => 'View History', 'edit_payment' => 'Edit Payment', @@ -728,12 +728,12 @@ return array( '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', + 'invoice_to' => 'Fattura a', + 'invoice_no' => 'Fattura N.', + 'recent_payments' => 'Pagamenti recenti', + 'outstanding' => 'Inevaso', + 'manage_companies' => 'Gestisci aziende', + 'total_revenue' => 'Ricavo totale', 'current_user' => 'Current User', 'new_recurring_invoice' => 'Nuova Fattura Ricorrente', @@ -784,7 +784,7 @@ return array( 'last_sent_on' => 'Last sent on :date', 'page_expire' => 'This page will expire soon, :click_here to keep working', - 'upcoming_quotes' => 'Upcoming Quotes', + 'upcoming_quotes' => 'Preventivi in scadenza', 'expired_quotes' => 'Preventivi Scaduti', 'sign_up_using' => 'Sign up using', @@ -842,19 +842,19 @@ return array( 'quote_counter' => 'Quote Counter', 'type' => 'Type', - 'activity_1' => ':user created client :client', - 'activity_2' => ':user archived client :client', + 'activity_1' => ':user ha creato il cliente :client', + 'activity_2' => ':user ha archiviato il cliente :client', 'activity_3' => ':user deleted client :client', 'activity_4' => ':user ha creato la fattura :invoice', 'activity_5' => ':user ha aggiornato la fattura :invoice', - 'activity_6' => ':user emailed invoice :invoice to :contact', - 'activity_7' => ':contact viewed invoice :invoice', - 'activity_8' => ':user archived invoice :invoice', - 'activity_9' => ':user deleted invoice :invoice', - 'activity_10' => ':contact entered payment :payment for :invoice', - 'activity_11' => ':user updated payment :payment', - 'activity_12' => ':user archived payment :payment', - 'activity_13' => ':user deleted payment :payment', + 'activity_6' => ':user ha inviato per email la fattura :invoice a :contact', + 'activity_7' => ':contact ha visto la fattura :invoice', + 'activity_8' => ':user ha archiviato la fattura :invoice', + 'activity_9' => ':user ha cancellato la fattura :invoice', + 'activity_10' => ':contact ha inserito il pagamento :payment per :invoice', + 'activity_11' => ':user ha aggiornato il pagamento :payment', + 'activity_12' => ':user ha archiviato il pagamento :payment', + 'activity_13' => ':user ha cancellato il pagamento :payment', 'activity_14' => ':user entered :credit credit', 'activity_15' => ':user updated :credit credit', 'activity_16' => ':user archived :credit credit', @@ -862,7 +862,7 @@ return array( 'activity_18' => ':user created quote :quote', 'activity_19' => ':user updated quote :quote', 'activity_20' => ':user emailed quote :quote to :contact', - 'activity_21' => ':contact viewed quote :quote', + 'activity_21' => ':contact ha visto il preventivo :quote', 'activity_22' => ':user archived quote :quote', 'activity_23' => ':user deleted quote :quote', 'activity_24' => ':user restored quote :quote', @@ -870,7 +870,7 @@ return array( 'activity_26' => ':user restored client :client', 'activity_27' => ':user restored payment :payment', 'activity_28' => ':user restored :credit credit', - 'activity_29' => ':contact approved quote :quote', + 'activity_29' => ':contact ha approvato la fattura :quote', 'payment' => 'Payment', 'system' => 'System', @@ -1004,33 +1004,33 @@ return array( 'archived_expenses' => 'Successfully archived expenses', // Expenses - 'expense_amount' => 'Expense Amount', - 'expense_balance' => 'Expense Balance', - 'expense_date' => 'Expense Date', - 'expense_should_be_invoiced' => 'Should this expense be invoiced?', - 'public_notes' => 'Public Notes', - 'invoice_amount' => 'Invoice Amount', - 'exchange_rate' => 'Exchange Rate', - 'yes' => 'Yes', + 'expense_amount' => 'Importo Spesa', + 'expense_balance' => 'Bilancio Spesa', + 'expense_date' => 'Data Spesa', + 'expense_should_be_invoiced' => 'Questa spesa deve essere fatturata?', + 'public_notes' => 'Note Pubbliche (Descrizione in fattura)', + 'invoice_amount' => 'Importo Fattura', + 'exchange_rate' => 'Tasso di Cambio', + 'yes' => 'Si', 'no' => 'No', - 'should_be_invoiced' => 'Should be invoiced', - 'view_expense' => 'View expense # :expense', - 'edit_expense' => 'Edit Expense', - 'archive_expense' => 'Archive Expense', - 'delete_expense' => 'Delete Expense', - 'view_expense_num' => 'Expense # :expense', - 'updated_expense' => 'Successfully updated expense', - 'created_expense' => 'Successfully created expense', - 'enter_expense' => 'Enter Expense', - 'view' => 'View', - 'restore_expense' => 'Restore Expense', - 'invoice_expense' => 'Invoice Expense', - 'expense_error_multiple_clients' =>'The expenses can\'t belong to different clients', - 'expense_error_invoiced' => 'Expense has already been invoiced', - 'convert_currency' => 'Convert currency', + 'should_be_invoiced' => 'Deve essere fatturata', + 'view_expense' => 'Vedi spesa # :expense', + 'edit_expense' => 'Modifica Spesa', + 'archive_expense' => 'Archivia Spesa', + 'delete_expense' => 'Cancella Spesa', + 'view_expense_num' => 'Spesa # :expense', + 'updated_expense' => 'Spesa aggiornata con successo', + 'created_expense' => 'Spesa creata con successo', + 'enter_expense' => 'Inserisci Spesa', + 'view' => 'Vedi', + 'restore_expense' => 'Ripristina Spesa', + 'invoice_expense' => 'Fattura Spesa', + 'expense_error_multiple_clients' =>'Le spese non possono appartenere a clienti differenti', + 'expense_error_invoiced' => 'La spesa è stata già fatturata', + 'convert_currency' => 'Converti valuta', // Payment terms - 'num_days' => 'Number of days', + 'num_days' => 'Numero di giorni', 'create_payment_term' => 'Create Payment Term', 'edit_payment_terms' => 'Edit Payment Term', 'edit_payment_term' => 'Edit Payment Term', @@ -1053,17 +1053,17 @@ return array( ', 'due' => 'Due', 'next_due_on' => 'Due Next: :date', - 'use_client_terms' => 'Use client terms', - 'day_of_month' => ':ordinal day of month', - 'last_day_of_month' => 'Last day of month', - 'day_of_week_after' => ':ordinal :day after', - 'sunday' => 'Sunday', - 'monday' => 'Monday', - 'tuesday' => 'Tuesday', - 'wednesday' => 'Wednesday', - 'thursday' => 'Thursday', - 'friday' => 'Friday', - 'saturday' => 'Saturday', + 'use_client_terms' => 'Usa i termini del cliente', + 'day_of_month' => ':ordinal giorno del mese', + 'last_day_of_month' => 'L\'ultimo giorno del mese', + 'day_of_week_after' => ':ordinal :day dopo', + 'sunday' => 'Domenica', + 'monday' => 'Lunedì', + 'tuesday' => 'Martedì', + 'wednesday' => 'Mercoledì', + 'thursday' => 'Giovedì', + 'friday' => 'Venerdì', + 'saturday' => 'Sabato', // Fonts 'header_font_id' => 'Header Font', @@ -1182,17 +1182,17 @@ return array( 'edit_all_help' => 'Allow user to modify records they didn\'t create', 'view_payment' => 'View Payment', - 'january' => 'January', - 'february' => 'February', - 'march' => 'March', - 'april' => 'April', - 'may' => 'May', - 'june' => 'June', - 'july' => 'July', - 'august' => 'August', - 'september' => 'September', - 'october' => 'October', - 'november' => 'November', - 'december' => 'December', + 'january' => 'Gennaio', + 'february' => 'Febbraio', + 'march' => 'Marzo', + 'april' => 'Aprile', + 'may' => 'Maggio', + 'june' => 'Giugno', + 'july' => 'Luglio', + 'august' => 'Agosto', + 'september' => 'Settembre', + 'october' => 'Ottobre', + 'november' => 'Novembre', + 'december' => 'Dicembre', ); diff --git a/resources/lang/ja/texts.php b/resources/lang/ja/texts.php index 086d8d2f53cd..3d313528619f 100644 --- a/resources/lang/ja/texts.php +++ b/resources/lang/ja/texts.php @@ -424,7 +424,7 @@ $LANG = array( 'invoice_history' => '請求履歴', 'quote_history' => '見積履歴', 'current_version' => '現在のバージョン', - 'select_versiony' => 'バージョンを選択', + 'select_version' => 'バージョンを選択', 'view_history' => '履歴を閲覧', 'edit_payment' => '支払いを編集', 'updated_payment' => '支払いを更新しました', diff --git a/resources/lang/lt/texts.php b/resources/lang/lt/texts.php index 72f184c2eb8f..4a67c5f0a564 100644 --- a/resources/lang/lt/texts.php +++ b/resources/lang/lt/texts.php @@ -497,7 +497,7 @@ return array( 'invoice_history' => 'Invoice History', 'quote_history' => 'Quote History', 'current_version' => 'Current version', - 'select_versiony' => 'Select version', + 'select_version' => 'Select version', 'view_history' => 'View History', 'edit_payment' => 'Edit Payment', diff --git a/resources/lang/nb_NO/texts.php b/resources/lang/nb_NO/texts.php index f606c2b82a43..18ac0ecc4fbe 100644 --- a/resources/lang/nb_NO/texts.php +++ b/resources/lang/nb_NO/texts.php @@ -493,7 +493,7 @@ return array( 'invoice_history' => 'Faktura Historikk', 'quote_history' => 'Tilbuds Historikk', 'current_version' => 'Nåværende versjon', - 'select_versiony' => 'Velg versjon', + 'select_version' => 'Velg versjon', 'view_history' => 'Vis Historikk', 'edit_payment' => 'Rediger Betaling', diff --git a/resources/lang/nl/texts.php b/resources/lang/nl/texts.php index e17b717fefe0..bd2024f329c2 100644 --- a/resources/lang/nl/texts.php +++ b/resources/lang/nl/texts.php @@ -489,7 +489,7 @@ return array( 'invoice_history' => 'Factuurgeschiedenis', 'quote_history' => 'Offertegeschiedenis', 'current_version' => 'Huidige versie', - 'select_versiony' => 'Selecteer versie', + 'select_version' => 'Selecteer versie', 'view_history' => 'Bekijk geschiedenis', 'edit_payment' => 'Bewerk betaling', diff --git a/resources/lang/pl/pagination.php b/resources/lang/pl/pagination.php new file mode 100644 index 000000000000..950c5e96cc41 --- /dev/null +++ b/resources/lang/pl/pagination.php @@ -0,0 +1,20 @@ + '« Poprzedni', + + 'next' => 'Następny »', + +); diff --git a/resources/lang/pl/passwords.php b/resources/lang/pl/passwords.php new file mode 100644 index 000000000000..28ccb957b349 --- /dev/null +++ b/resources/lang/pl/passwords.php @@ -0,0 +1,22 @@ + "Hasło musi mieć conajmniej sześć znaków i być takie samo jak potwierdzające.", + "user" => "Użytkownik o podanym adresie e-mail nie istnieje.", + "token" => "Wprowadzony token jest nieprawidłowy.", + "sent" => "Link do resetowania hasła został wysłany.", + "reset" => "Twoje hasło zostało zresetowane!", + +]; \ No newline at end of file diff --git a/resources/lang/pl/reminders.php b/resources/lang/pl/reminders.php new file mode 100644 index 000000000000..25824a7f285a --- /dev/null +++ b/resources/lang/pl/reminders.php @@ -0,0 +1,24 @@ + "Hasło musi mieć conajmniej sześć znaków i być takie samo jak potwierdzające.", + + "user" => "Użytkownik o podanym adresie e-mail nie istnieje.", + + "token" => "Wprowadzony token jest nieprawidłowy.", + + "sent" => "Przypomnienie hasła zostało wysłane!", + +); \ No newline at end of file diff --git a/resources/lang/pl/texts.php b/resources/lang/pl/texts.php new file mode 100644 index 000000000000..19b0253937db --- /dev/null +++ b/resources/lang/pl/texts.php @@ -0,0 +1,1184 @@ + 'Organizacja', + 'name' => 'Nazwa', + 'website' => 'Strona internetowa', + 'work_phone' => 'Telefon służbowy', + 'address' => 'Adres', + 'address1' => 'Ulica', + 'address2' => 'Nr', + 'city' => 'Miasto', + 'state' => 'Województwo', + 'postal_code' => 'Kod pocztowy', + 'country_id' => 'Kraj', + 'contacts' => 'Kontakty', + 'first_name' => 'Imię', + 'last_name' => 'Nazwisko', + 'phone' => 'Telefon', + 'email' => 'Email', + 'additional_info' => 'Dodatkowe informacje', + 'payment_terms' => 'Warunki płatnicze', + 'currency_id' => 'Waluta', + 'size_id' => 'Wielkość firmy', + 'industry_id' => 'Branża', + 'private_notes' => 'Prywatne notatki', + 'invoice' => 'Faktura', + 'client' => 'Klient', + 'invoice_date' => 'Data Faktury', + 'due_date' => 'Termin', + 'invoice_number' => 'Numer Faktury', + 'invoice_number_short' => 'Faktura #', + 'po_number' => 'PO Number', + 'po_number_short' => 'PO #', + 'frequency_id' => 'Jak często', + 'discount' => 'Przecena', + 'taxes' => 'Podatki', + 'tax' => 'Podatek', + 'item' => 'Pozycja', + 'description' => 'Opis', + 'unit_cost' => 'Cena jednostkowa', + 'quantity' => 'Ilość', + 'line_total' => 'Wartość całkowita', + 'subtotal' => 'Suma częściowa', + 'paid_to_date' => 'Wypłacono do tej pory', + 'balance_due' => 'Balance Due', + 'invoice_design_id' => 'Szablon', + 'terms' => 'Warunki', + 'your_invoice' => 'Twoja faktura', + 'remove_contact' => 'Usuń kontakt', + 'add_contact' => 'Dodaj kontakt', + 'create_new_client' => 'Dodaj nowego klienta', + 'edit_client_details' => 'Edytuj dane klienta', + 'enable' => 'Aktywuj', + 'learn_more' => 'Więcej informacji', + 'manage_rates' => 'Zarządzaj stawkami', + 'note_to_client' => 'Informacja dla klienta', + 'invoice_terms' => 'Warunki do faktury', + 'save_as_default_terms' => 'Zapisz jako domyślne warunki', + 'download_pdf' => 'Pobierz PDF', + 'pay_now' => 'Zapłać teraz', + 'save_invoice' => 'Zapisz Fakturę', + 'clone_invoice' => 'Skopiuj Fakturę', + 'archive_invoice' => 'Zarchiwizuj Fakturę', + 'delete_invoice' => 'Usuń Fakturę', + 'email_invoice' => 'Wyślij Fakturę', + 'enter_payment' => 'Wprowadź Płatność', + 'tax_rates' => 'Stawki podatkowe', + 'rate' => 'Stawka', + 'settings' => 'Ustawienia', + 'enable_invoice_tax' => 'Aktywuj możliwość ustawienia podatku do faktury', + 'enable_line_item_tax' => 'Aktywuj możliwość ustawienia podatku do pozycji na fakturze', + 'dashboard' => 'Pulpit', + 'clients' => 'Klienci', + 'invoices' => 'Faktury', + 'payments' => 'Płatności', + 'credits' => 'Kredyty', + 'history' => 'Historia', + 'search' => 'Szukaj', + 'sign_up' => 'Zapisz się', + 'guest' => 'Gość', + 'company_details' => 'Dane Firmy', + 'online_payments' => 'Płatności Online', + 'notifications' => 'Powiadomienia E-mail', + 'import_export' => 'Import | Export', + 'done' => 'Gotowe', + 'save' => 'Zapisz', + 'create' => 'Dodaj', + 'upload' => 'Prześlij', + 'import' => 'Import', + 'download' => 'Pobierz', + 'cancel' => 'Anuluj', + 'close' => 'Zamknij', + 'provide_email' => 'Wprowadź poprawny e-mail.', + 'powered_by' => 'Oparte na', + 'no_items' => 'Brak pozycji', + 'recurring_invoices' => 'Faktury okresowe', + 'recurring_help' => '

    Wysyła automatycznie te same faktury klientom tygodniowo, miesięcznie, kwartałowo lub rocznie.

    +

    Użyj :MONTH, :QUARTER lub :YEAR dla dynamicznych dat. Podstawowa arytmetyka działa również, np: :MONTH-1.

    +

    Przykłady dynamicznych zmiennych na fakturze:

    + ', + 'in_total_revenue' => 'całkowity przychód', + 'billed_client' => 'Obciążony klient', + 'billed_clients' => 'Obciążeni klienci', + 'active_client' => 'Aktywny klient', + 'active_clients' => 'Aktywni klienci', + 'invoices_past_due' => 'Opóźnione faktury', + 'upcoming_invoices' => 'Nadchodzące Faktury', + 'average_invoice' => 'Średnia wartość Faktury', + 'archive' => 'Archiwum', + 'delete' => 'Usuń', + 'archive_client' => 'Zarchiwizuj klienta', + 'delete_client' => 'Usuń klienta', + 'archive_payment' => 'Zarchiwizuj płatność', + 'delete_payment' => 'Usuń płatność', + 'archive_credit' => 'Zarchiwizuj kredyt', + 'delete_credit' => 'Usuń kredyt', + 'show_archived_deleted' => 'Pokaż zarchiwizowane/usunięte', + 'filter' => 'Filtruj', + 'new_client' => 'Nowy klient', + 'new_invoice' => 'Nowa Faktura', + 'new_payment' => 'Nowa Płatność', + 'new_credit' => 'Nowy Kredyt', + 'contact' => 'Kontakt', + 'date_created' => 'Data utworzenia', + 'last_login' => 'Ostatnie logowanie', + 'balance' => 'Saldo', + 'action' => 'Akcja', + 'status' => 'Status', + 'invoice_total' => 'Faktura ogółem', + 'frequency' => 'Częstotliwość', + 'start_date' => 'Początkowa data', + 'end_date' => 'Końcowa data', + 'transaction_reference' => 'Referencja transakcji', + 'method' => 'Metoda', + 'payment_amount' => 'Kwota płatności', + 'payment_date' => 'Data płatności', + 'credit_amount' => 'Kwota kredytu', + 'credit_balance' => 'Saldo kredytowe', + 'credit_date' => 'Data kredytu', + 'empty_table' => 'Brak danych w tabeli', + 'select' => 'Wybierz', + 'edit_client' => 'Edytuj klienta', + 'edit_invoice' => 'Edytuj fakturę', + 'create_invoice' => 'Utwórz Fakturę', + 'enter_credit' => 'Wprowadź kredyt', + 'last_logged_in' => 'Ostatnie logowanie w', + 'details' => 'Szczegóły', + 'standing' => 'Standing', + 'credit' => 'Kredyt', + 'activity' => 'Czynność', + 'date' => 'Data', + 'message' => 'Wiadomość', + 'adjustment' => 'Dostosowanie', + 'are_you_sure' => 'Jesteś pewny?', + 'payment_type_id' => 'Typ płatności', + 'amount' => 'Kwota', + 'work_email' => 'Email', + 'language_id' => 'Język', + 'timezone_id' => 'Strefa czasowa', + 'date_format_id' => 'Format daty', + 'datetime_format_id' => 'Format Data/Godzina', + 'users' => 'Użytkownicy', + 'localization' => 'Lokalizacja', + 'remove_logo' => 'Usuń logo', + 'logo_help' => 'Obsługiwane: JPEG, GIF i PNG', + 'payment_gateway' => 'Bramka płatnicza', + 'gateway_id' => 'Bramka płatnicza', + 'email_notifications' => 'Email powiadomienia', + 'email_sent' => 'Powiadom mnie kiedy faktura jest wysłana', + 'email_viewed' => 'Powiadom mnie kiedy faktura jest otworzona', + 'email_paid' => 'Powiadom mnie kiedy faktura jest zapłacona', + 'site_updates' => 'Aktualizacje strony', + 'custom_messages' => 'Niestandardowe komunikaty', + 'default_email_footer' => 'Ustaw domyślny podpis email', + 'select_file' => 'Wybierz plik', + 'first_row_headers' => 'Użyj pierwszego wiersza jako nagłówek', + 'column' => 'Kolumna', + 'sample' => 'Przykład', + 'import_to' => 'Zaimportuj do', + 'client_will_create' => 'klient będzie utworzony', + 'clients_will_create' => 'klienci będą utworzeni', + 'email_settings' => 'Ustawienia e-mail', + 'client_view_styling' => 'Client View Styling', + 'pdf_email_attachment' => 'Attach PDFs', + 'custom_css' => 'Custom CSS', + 'import_clients' => 'Import Client Data', + 'csv_file' => 'CSV file', + 'export_clients' => 'Export Client Data', + 'created_client' => 'Successfully created client', + 'created_clients' => 'Successfully created :count client(s)', + 'updated_settings' => 'Successfully updated settings', + 'removed_logo' => 'Successfully removed logo', + 'sent_message' => 'Successfully sent message', + 'invoice_error' => 'Please make sure to select a client and correct any errors', + 'limit_clients' => 'Sorry, this will exceed the limit of :count clients', + 'payment_error' => 'There was an error processing your payment. Please try again later.', + 'registration_required' => 'Please sign up to email an invoice', + 'confirmation_required' => 'Please confirm your email address', + 'updated_client' => 'Successfully updated client', + 'created_client' => 'Successfully created client', + 'archived_client' => 'Successfully archived client', + 'archived_clients' => 'Successfully archived :count clients', + 'deleted_client' => 'Successfully deleted client', + 'deleted_clients' => 'Successfully deleted :count clients', + 'updated_invoice' => 'Successfully updated invoice', + 'created_invoice' => 'Successfully created invoice', + 'cloned_invoice' => 'Successfully cloned invoice', + 'emailed_invoice' => 'Successfully emailed invoice', + 'and_created_client' => 'and created client', + 'archived_invoice' => 'Successfully archived invoice', + 'archived_invoices' => 'Successfully archived :count invoices', + 'deleted_invoice' => 'Successfully deleted invoice', + 'deleted_invoices' => 'Successfully deleted :count invoices', + 'created_payment' => 'Successfully created payment', + 'created_payments' => 'Successfully created :count payment(s)', + 'archived_payment' => 'Successfully archived payment', + 'archived_payments' => 'Successfully archived :count payments', + 'deleted_payment' => 'Successfully deleted payment', + 'deleted_payments' => 'Successfully deleted :count payments', + 'applied_payment' => 'Successfully applied payment', + 'created_credit' => 'Successfully created credit', + 'archived_credit' => 'Successfully archived credit', + 'archived_credits' => 'Successfully archived :count credits', + 'deleted_credit' => 'Successfully deleted credit', + 'deleted_credits' => 'Successfully deleted :count credits', + 'imported_file' => 'Successfully imported file', + 'updated_vendor' => 'Successfully updated vendor', + 'created_vendor' => 'Successfully created vendor', + 'archived_vendor' => 'Successfully archived vendor', + 'archived_vendors' => 'Successfully archived :count vendors', + 'deleted_vendor' => 'Successfully deleted vendor', + 'deleted_vendors' => 'Successfully deleted :count vendors', + 'confirmation_subject' => 'Invoice Ninja Account Confirmation', + 'confirmation_header' => 'Account Confirmation', + 'confirmation_message' => 'Please access the link below to confirm your account.', + 'invoice_subject' => 'New invoice :invoice from :account', + 'invoice_message' => 'To view your invoice for :amount, click the link below.', + 'payment_subject' => 'Payment Received', + 'payment_message' => 'Thank you for your payment of :amount.', + 'email_salutation' => 'Drogi :name,', + 'email_signature' => 'Pozdrowienia,', + 'email_from' => 'Zespół The Invoice Ninja', + 'invoice_link_message' => 'To view the invoice click the link below:', + 'notification_invoice_paid_subject' => 'Invoice :invoice was paid by :client', + 'notification_invoice_sent_subject' => 'Invoice :invoice was sent to :client', + 'notification_invoice_viewed_subject' => 'Invoice :invoice was viewed by :client', + 'notification_invoice_paid' => 'A payment of :amount was made by client :client towards Invoice :invoice.', + 'notification_invoice_sent' => 'The following client :client was emailed Invoice :invoice for :amount.', + 'notification_invoice_viewed' => 'The following client :client viewed Invoice :invoice for :amount.', + 'reset_password' => 'You can reset your account password by clicking the following button:', + 'secure_payment' => 'Bezpieczna płatność', + 'card_number' => 'Numer karty', + 'expiration_month' => 'Expiration Month', + 'expiration_year' => 'Expiration Year', + 'cvv' => 'CVV', + 'logout' => 'Wyloguj się', + 'sign_up_to_save' => 'Sign up to save your work', + 'agree_to_terms' => 'I agree to the Invoice Ninja :terms', + 'terms_of_service' => 'Terms of Service', + 'email_taken' => 'The email address is already registered', + 'working' => 'Working', + 'success' => 'Success', + 'success_message' => 'You have successfully registered! Please visit the link in the account confirmation email to verify your email address.', + 'erase_data' => 'This will permanently erase your data.', + 'password' => 'Hasło', + 'pro_plan_product' => 'Pro Plan', + 'pro_plan_success' => 'Thanks for choosing Invoice Ninja\'s Pro plan!

     
    + Next Steps

    A payable invoice has been sent to the email + address associated with your account. To unlock all of the awesome + Pro features, please follow the instructions on the invoice to pay + for a year of Pro-level invoicing.

    + Can\'t find the invoice? Need further assistance? We\'re happy to help + -- email us at contact@invoiceninja.com', + 'unsaved_changes' => 'You have unsaved changes', + 'custom_fields' => 'Custom Fields', + 'company_fields' => 'Company Fields', + 'client_fields' => 'Client Fields', + 'field_label' => 'Field Label', + 'field_value' => 'Field Value', + 'edit' => 'Edytuj', + 'set_name' => 'Set your company name', + 'view_as_recipient' => 'View as recipient', + 'product_library' => 'Product Library', + 'product' => 'Produkt', + 'products' => 'Produkty', + 'fill_products' => 'Auto-fill products', + 'fill_products_help' => 'Selecting a product will automatically fill in the description and cost', + 'update_products' => 'Auto-update products', + 'update_products_help' => 'Updating an invoice will automatically update the product library', + 'create_product' => 'Add Product', + 'edit_product' => 'Edit Product', + 'archive_product' => 'Archive Product', + 'updated_product' => 'Successfully updated product', + 'created_product' => 'Successfully created product', + 'archived_product' => 'Successfully archived product', + 'pro_plan_custom_fields' => ':link to enable custom fields by joining the Pro Plan', + 'advanced_settings' => 'Advanced Settings', + 'pro_plan_advanced_settings' => ':link to enable the advanced settings by joining the Pro Plan', + 'invoice_design' => 'Invoice Design', + 'specify_colors' => 'Specify colors', + 'specify_colors_label' => 'Select the colors used in the invoice', + 'chart_builder' => 'Chart Builder', + 'ninja_email_footer' => 'Use :site to invoice your clients and get paid online for free!', + 'go_pro' => 'Go Pro', + 'quote' => 'Oferta', + 'quotes' => 'Oferty', + 'quote_number' => 'Numer oferty', + 'quote_number_short' => 'Oferta #', + 'quote_date' => 'Data oferty', + 'quote_total' => 'Suma oferty', + 'your_quote' => 'Twoja oferta', + 'total' => 'Suma', + 'clone' => 'Clone', + 'new_quote' => 'Nowa oferta', + 'create_quote' => 'Stwórz ofertę', + 'edit_quote' => 'Edytuj ofertę', + 'archive_quote' => 'Archiwizuj ofertę', + 'delete_quote' => 'Usuń ofertę', + 'save_quote' => 'Zapisz ofertę', + 'email_quote' => 'Wyślij ofertę', + 'clone_quote' => 'Clone Quote', + 'convert_to_invoice' => 'Konwertuj do faktury', + 'view_invoice' => 'Zobacz fakturę', + 'view_client' => 'Zobacz klienta', + 'view_quote' => 'Zobacz ofertę', + 'updated_quote' => 'Successfully updated quote', + 'created_quote' => 'Successfully created quote', + 'cloned_quote' => 'Successfully cloned quote', + 'emailed_quote' => 'Successfully emailed quote', + 'archived_quote' => 'Successfully archived quote', + 'archived_quotes' => 'Successfully archived :count quotes', + 'deleted_quote' => 'Successfully deleted quote', + 'deleted_quotes' => 'Successfully deleted :count quotes', + 'converted_to_invoice' => 'Successfully converted quote to invoice', + 'quote_subject' => 'New quote $quote from :account', + 'quote_message' => 'To view your quote for :amount, click the link below.', + 'quote_link_message' => 'To view your client quote click the link below:', + 'notification_quote_sent_subject' => 'Quote :invoice was sent to :client', + 'notification_quote_viewed_subject' => 'Quote :invoice was viewed by :client', + 'notification_quote_sent' => 'The following client :client was emailed Quote :invoice for :amount.', + 'notification_quote_viewed' => 'The following client :client viewed Quote :invoice for :amount.', + 'session_expired' => 'Your session has expired.', + 'invoice_fields' => 'Invoice Fields', + 'invoice_options' => 'Invoice Options', + '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_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.', + 'charge_taxes' => 'Charge taxes', + 'user_management' => 'User Management', + 'add_user' => 'Add User', + 'send_invite' => 'Send invitation', + 'sent_invite' => 'Successfully sent invitation', + 'updated_user' => 'Successfully updated user', + 'invitation_message' => 'You\'ve been invited by :invitor. ', + 'register_to_add_user' => 'Please sign up to add a user', + 'user_state' => 'State', + 'edit_user' => 'Edit User', + 'delete_user' => 'Delete User', + 'active' => 'Active', + 'pending' => 'Pending', + 'deleted_user' => 'Successfully deleted user', + 'confirm_email_invoice' => 'Are you sure you want to email this invoice?', + 'confirm_email_quote' => 'Are you sure you want to email this quote?', + 'confirm_recurring_email_invoice' => 'Are you sure you want this invoice emailed?', + 'cancel_account' => 'Usuń konto', + 'cancel_account_message' => 'Warning: This will permanently erase all of your data, there is no undo.', + 'go_back' => 'Go Back', + 'data_visualizations' => 'Data Visualizations', + 'sample_data' => 'Sample data shown', + 'hide' => 'Hide', + 'new_version_available' => 'A new version of :releases_link is available. You\'re running v:user_version, the latest is v:latest_version', + 'invoice_settings' => 'Invoice Settings', + 'invoice_number_prefix' => 'Invoice Number Prefix', + 'invoice_number_counter' => 'Invoice Number Counter', + 'quote_number_prefix' => 'Quote Number Prefix', + 'quote_number_counter' => 'Quote Number Counter', + 'share_invoice_counter' => 'Share invoice counter', + 'invoice_issued_to' => 'Invoice issued to', + 'invalid_counter' => 'To prevent a possible conflict please set either an invoice or quote number prefix', + 'mark_sent' => 'Mark Sent', + 'gateway_help_1' => ':link to sign up for Authorize.net.', + 'gateway_help_2' => ':link to sign up for Authorize.net.', + 'gateway_help_17' => ':link to get your PayPal API signature.', + 'gateway_help_27' => ':link to sign up for TwoCheckout.', + 'more_designs' => 'More designs', + 'more_designs_title' => 'Additional Invoice Designs', + 'more_designs_cloud_header' => 'Go Pro for more invoice designs', + 'more_designs_cloud_text' => '', + 'more_designs_self_host_text' => '', + 'buy' => 'Buy', + 'bought_designs' => 'Successfully added additional invoice designs', + 'sent' => 'sent', + 'vat_number' => 'VAT Number', + 'timesheets' => 'Timesheets', + 'payment_title' => 'Enter Your Billing Address and Credit Card information', + 'payment_cvv' => '*This is the 3-4 digit number onthe back of your card', + 'payment_footer1' => '*Billing address must match address associated with credit card.', + 'payment_footer2' => '*Please click "PAY NOW" only once - transaction may take up to 1 minute to process.', + 'id_number' => 'ID Number', + 'white_label_link' => 'White label', + 'white_label_header' => 'White Label', + 'bought_white_label' => 'Successfully enabled white label license', + 'white_labeled' => 'White labeled', + 'restore' => 'Przywróć', + 'restore_invoice' => 'Przywróć fakturę', + 'restore_quote' => 'Przywróć ofertę', + 'restore_client' => 'Przywróć klienta', + 'restore_credit' => 'Przywróć kredyt', + 'restore_payment' => 'Przywróć płatność', + 'restored_invoice' => 'Faktura została przywrócona', + 'restored_quote' => 'Oferta została przywrócona', + 'restored_client' => 'Klient został przywrócony', + 'restored_payment' => 'Płatność została przywrócona', + 'restored_credit' => 'Kredyt został przywrócony', + 'reason_for_canceling' => 'Help us improve our site by telling us why you\'re leaving.', + 'discount_percent' => 'Procent', + 'discount_amount' => 'Kwota', + 'invoice_history' => 'Historia faktury', + 'quote_history' => 'Historia oferty', + 'current_version' => 'Aktualna wersja', + 'select_version' => 'Wybierz wersję', + 'view_history' => 'Zobacz historię', + 'edit_payment' => 'Edytuj płatność', + 'updated_payment' => 'Successfully updated payment', + 'deleted' => 'Usunięte', + 'restore_user' => 'Przywróć użytkownika', + 'restored_user' => 'Użytkownik został przywrócony', + 'show_deleted_users' => 'Pokaż usuniętych użytkowników', + 'email_templates' => 'Szablony e-mail', + 'invoice_email' => 'Email faktury', + 'payment_email' => 'Email płatności', + 'quote_email' => 'Email oferty', + 'reset_all' => 'Resetuj wszystko', + 'approve' => 'Zatwierdź', + '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_1' => 'Wyłączone', + 'token_billing_2' => 'Opt-in - checkbox is shown but not selected', + 'token_billing_3' => 'Opt-out - checkbox is shown and selected', + 'token_billing_4' => 'Zawsze', + 'token_billing_checkbox' => 'Store credit card details', + 'view_in_stripe' => 'View in Stripe', + 'use_card_on_file' => 'Use card on file', + 'edit_payment_details' => 'Edit payment details', + 'token_billing' => 'Save card details', + 'token_billing_secure' => 'The data is stored securely by :stripe_link', + 'support' => 'Support', + 'contact_information' => 'Contact Information', + '256_encryption' => '256-Bit Encryption', + 'amount_due' => 'Amount due', + 'billing_address' => 'Adres billingowy (rozliczeniowy)', + 'billing_method' => 'Billing Method', + 'order_overview' => 'Order overview', + '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.', + 'invoice_footer' => 'Stopka faktury', + 'save_as_default_footer' => 'Save as default footer', + 'token_management' => 'Token Management', + 'tokens' => 'Tokens', + 'add_token' => 'Add Token', + 'show_deleted_tokens' => 'Show deleted tokens', + 'deleted_token' => 'Successfully deleted token', + 'created_token' => 'Successfully created token', + 'updated_token' => 'Successfully updated token', + 'edit_token' => 'Edit Token', + 'delete_token' => 'Delete Token', + 'token' => 'Token', + 'add_gateway' => 'Add Gateway', + 'delete_gateway' => 'Delete Gateway', + 'edit_gateway' => 'Edit Gateway', + 'updated_gateway' => 'Successfully updated gateway', + 'created_gateway' => 'Successfully created gateway', + 'deleted_gateway' => 'Successfully deleted gateway', + 'pay_with_paypal' => 'PayPal', + 'pay_with_card' => 'Credit Card', + 'change_password' => 'Zmień hasło', + 'current_password' => 'Aktualne hasło', + 'new_password' => 'Nowe hasło', + 'confirm_password' => 'Potwierdź hasło', + 'password_error_incorrect' => 'The current password is incorrect.', + 'password_error_invalid' => 'The new password is invalid.', + 'updated_password' => 'Successfully updated password', + 'api_tokens' => 'API Tokens', + 'users_and_tokens' => 'Users & Tokens', + 'account_login' => 'Account Login', + 'recover_password' => 'Recover your password', + 'forgot_password' => 'Forgot your password?', + 'email_address' => 'Email address', + 'lets_go' => 'Let\'s go', + 'password_recovery' => 'Password Recovery', + 'send_email' => 'Wyślij email', + 'set_password' => 'Ustaw hasło', + 'converted' => 'Converted', + 'email_approved' => 'Email me when a quote is approved', + 'notification_quote_approved_subject' => 'Quote :invoice was approved by :client', + 'notification_quote_approved' => 'The following client :client approved Quote :invoice for :amount.', + 'resend_confirmation' => 'Resend confirmation email', + 'confirmation_resent' => 'The confirmation email was resent', + 'gateway_help_42' => ':link to sign up for BitPay.
    Note: use a Legacy API Key, not an API token.', + 'payment_type_credit_card' => 'Karta kredytowa', + 'payment_type_paypal' => 'PayPal', + 'payment_type_bitcoin' => 'Bitcoin', + 'knowledge_base' => 'Baza wiedzy', + 'partial' => 'Partial', + 'partial_remaining' => ':partial of :balance', + 'more_fields' => 'More Fields', + 'less_fields' => 'Less Fields', + 'client_name' => 'Nazwa klienta', + 'pdf_settings' => 'Ustawienia PDF', + 'product_settings' => 'Ustawienia produktu', + 'auto_wrap' => 'Auto Line Wrap', + 'duplicate_post' => 'Warning: the previous page was submitted twice. The second submission had been ignored.', + 'view_documentation' => 'View Documentation', + 'app_title' => 'Free Open-Source Online Invoicing', + 'app_description' => 'Invoice Ninja is a free, open-source solution for invoicing and billing customers. With Invoice Ninja, you can easily build and send beautiful invoices from any device that has access to the web. Your clients can print your invoices, download them as pdf files, and even pay you online from within the system.', + 'rows' => 'wierszy', + 'www' => 'www', + 'logo' => 'Logo', + 'subdomain' => 'Subdomain', + 'provide_name_or_email' => 'Proszę podać imię i nazwisko lub adres e-mail', + 'charts_and_reports' => 'Raporty i wykresy', + 'chart' => 'Wykres', + 'report' => 'Raport', + 'group_by' => 'Grupuj według', + 'paid' => 'Zapłacone', + 'enable_report' => 'Raport', + 'enable_chart' => 'Wykres', + 'totals' => 'Suma', + 'run' => 'Run', + 'export' => 'Export', + 'documentation' => 'Documentation', + 'zapier' => 'Zapier', + 'recurring' => 'Okresowe', + 'last_invoice_sent' => 'Last invoice sent :date', + 'processed_updates' => 'Pomyślnie zakończona aktualizacja', + 'tasks' => 'Zadania', + 'new_task' => 'Nowe zadanie', + 'start_time' => 'Czas rozpoczęcia', + 'created_task' => 'Pomyślnie utworzono zadanie', + 'updated_task' => 'Pomyślnie zaktualizowano zadanie', + 'edit_task' => 'Edytuj zadanie', + 'archive_task' => 'Archiwizuj zadanie', + 'restore_task' => 'Przywróć zadanie', + 'delete_task' => 'Usuń zadanie', + 'stop_task' => 'Zatrzymaj zadanie', + 'time' => 'Czas', + 'start' => 'Rozpocznij', + 'stop' => 'Zatrzymaj', + 'now' => 'Teraz', + 'timer' => 'Czasomierz', + 'manual' => 'Manualnie', + 'date_and_time' => 'Data i czas', + 'second' => 'sekunda', + 'seconds' => 'sekund', + 'minute' => 'minuta', + 'minutes' => 'minut', + 'hour' => 'godzina', + 'hours' => 'godzin', + 'task_details' => 'Szczegóły zadania', + 'duration' => 'Czas trwania', + 'end_time' => 'Czas zakończenia', + 'end' => 'Koniec', + 'invoiced' => 'Invoiced', + 'logged' => 'Zalogowany', + 'running' => 'Running', + 'task_error_multiple_clients' => 'The tasks can\'t belong to different clients', + 'task_error_running' => 'Please stop running tasks first', + 'task_error_invoiced' => 'Tasks have already been invoiced', + 'restored_task' => 'Successfully restored task', + 'archived_task' => 'Successfully archived task', + 'archived_tasks' => 'Successfully archived :count tasks', + 'deleted_task' => 'Successfully deleted task', + 'deleted_tasks' => 'Successfully deleted :count tasks', + 'create_task' => 'Create Task', + 'stopped_task' => 'Successfully stopped task', + 'invoice_task' => 'Invoice Task', + 'invoice_labels' => 'Invoice Labels', + 'prefix' => 'Prefix', + 'counter' => 'Counter', + 'payment_type_dwolla' => 'Dwolla', + 'gateway_help_43' => ':link to sign up for Dwolla', + 'partial_value' => 'Must be greater than zero and less than the total', + 'more_actions' => 'More Actions', + 'pro_plan_title' => 'NINJA PRO', + 'pro_plan_call_to_action' => 'Upgrade Now!', + 'pro_plan_feature1' => 'Create Unlimited Clients', + 'pro_plan_feature2' => 'Access to 10 Beautiful Invoice Designs', + 'pro_plan_feature3' => 'Custom URLs - "YourBrand.InvoiceNinja.com"', + 'pro_plan_feature4' => 'Remove "Created by Invoice Ninja"', + 'pro_plan_feature5' => 'Multi-user Access & Activity Tracking', + 'pro_plan_feature6' => 'Create Quotes & Pro-forma Invoices', + 'pro_plan_feature7' => 'Customize Invoice Field Titles & Numbering', + 'pro_plan_feature8' => 'Option to Attach PDFs to Client Emails', + 'resume' => 'Resume', + 'break_duration' => 'Break', + 'edit_details' => 'Edit Details', + 'work' => 'Work', + 'timezone_unset' => 'Please :link to set your timezone', + 'click_here' => 'click here', + 'email_receipt' => 'Wyślij potwierdzenie zapłaty do klienta', + 'created_payment_emailed_client' => 'Successfully created payment and emailed client', + 'add_company' => 'Add Company', + 'untitled' => 'Untitled', + 'new_company' => 'New Company', + 'associated_accounts' => 'Successfully linked accounts', + 'unlinked_account' => 'Successfully unlinked accounts', + 'login' => 'Login', + 'or' => 'or', + 'email_error' => 'There was a problem sending the email', + 'confirm_recurring_timing' => 'Note: emails are sent at the start of the hour.', + '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' => 'krotnie', + 'set_now' => 'Ustaw na teraz', + 'dark_mode' => 'Tryb ciemny', + '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' => 'Ostatnie płatności', + 'outstanding' => 'Zaległe', + 'manage_companies' => 'Manage Companies', + 'total_revenue' => 'Całkowity dochód', + 'current_user' => 'Aktualny użytkownik', + 'new_recurring_invoice' => 'Nowa faktura okresowa', + 'recurring_invoice' => 'Okresowa faktura', + 'recurring_too_soon' => 'It\'s too soon to create the next recurring invoice, it\'s scheduled for :date', + 'created_by_invoice' => 'Utworzona przez :invoice', + 'primary_user' => 'Główny Użytkownik', + 'help' => 'Pomoc', + 'customize_help' => '

    We use pdfmake to define the invoice designs declaratively. The pdfmake playground provide\'s a great way to see the library in action.

    +

    To access a child property using dot notation. For example to show the client name you could use $client.name.

    +

    If you need help figuring something out post a question to our support forum.

    ', + 'invoice_due_date' => 'Termin', + 'quote_due_date' => 'Valid Until', + 'valid_until' => 'Valid Until', + 'reset_terms' => 'Resetuj warunki', + 'reset_footer' => 'Resetuj stópkę', + 'invoices_sent' => ':count faktura wysłana|:count faktury wysłane', + 'status_draft' => 'Wersja robocza', + 'status_sent' => 'Wysłane', + 'status_viewed' => 'Przeglądnięte', + 'status_partial' => 'Częściowo', + 'status_paid' => 'Zapłacone', + 'show_line_item_tax' => 'Wyświetl podatki pozycji w tej samej linii', + 'iframe_url' => 'Website', + 'iframe_url_help1' => 'Copy the following code to a page on your site.', + 'iframe_url_help2' => 'You can test the feature by clicking \'View as recipient\' for an invoice.', + 'auto_bill' => 'Automatyczny Rachunek', + 'military_time' => '24 godzinny czas', + 'last_sent' => 'Ostatnio wysłany', + 'reminder_emails' => 'Reminder Emails', + 'templates_and_reminders' => 'Szablony i przypomnienia', + 'subject' => 'Temat', + 'body' => 'Treść', + 'first_reminder' => 'Pierwsze przypomnienie', + 'second_reminder' => 'Drugie przypomnienie', + 'third_reminder' => 'Trzecie przypomnienie', + 'num_days_reminder' => 'Dni po terminie', + 'reminder_subject' => 'Reminder: Invoice :invoice from :account', + 'reset' => 'Reset', + 'invoice_not_found' => 'The requested invoice is not available', + 'referral_program' => 'Program referencyjny', + 'referral_code' => 'Referencyjny URL', + 'last_sent_on' => 'Ostatnio wysłany: :date', + 'page_expire' => 'This page will expire soon, :click_here to keep working', + 'upcoming_quotes' => 'Nadchodzące oferty', + 'expired_quotes' => 'Wygaśnięte oferty', + 'sign_up_using' => 'Zarejestruj się przy użyciu', + 'invalid_credentials' => 'These credentials do not match our records', + 'show_all_options' => 'Pokaż wszystkie opcje', + 'user_details' => 'Dane użytkownika', + 'oneclick_login' => 'One-Click Logowanie', + 'disable' => 'Wyłącz', + 'invoice_quote_number' => 'Numery faktur i ofert', + 'invoice_charges' => 'Opłaty faktury', + 'notification_invoice_bounced' => 'We were unable to deliver Invoice :invoice to :contact.', + 'notification_invoice_bounced_subject' => 'Unable to deliver Invoice :invoice', + 'notification_quote_bounced' => 'We were unable to deliver Quote :invoice to :contact.', + 'notification_quote_bounced_subject' => 'Unable to deliver Quote :invoice', + 'custom_invoice_link' => 'Custom Invoice Link', + 'total_invoiced' => 'Total Invoiced', + 'open_balance' => 'Open Balance', + 'verify_email' => 'Please visit the link in the account confirmation email to verify your email address.', + 'basic_settings' => 'Ustawienia podstawowe', + 'pro' => 'Pro', + 'gateways' => 'Payment Gateways', + 'next_send_on' => 'Send Next: :date', + 'no_longer_running' => 'This invoice is not scheduled to run', + 'general_settings' => 'General Settings', + 'customize' => 'Customize', + 'oneclick_login_help' => 'Connect an account to login without a password', + 'referral_code_help' => 'Earn money by sharing our app online', + 'enable_with_stripe' => 'Aktywuj | Wymaga Stripe', + 'tax_settings' => 'Tax Settings', + 'create_tax_rate' => 'Dodaj stawkę podatkową', + 'updated_tax_rate' => 'Successfully updated tax rate', + 'created_tax_rate' => 'Successfully created tax rate', + 'edit_tax_rate' => 'Edit tax rate', + 'archive_tax_rate' => 'Archive Tax Rate', + 'archived_tax_rate' => 'Successfully archived the tax rate', + 'default_tax_rate_id' => 'Domyśłna stawka podatkowa', + 'tax_rate' => 'Stawka podatkowa', + 'recurring_hour' => 'Okresowa godzina', + 'pattern' => 'Pattern', + 'pattern_help_title' => 'Pattern Help', + 'pattern_help_1' => 'Create custom invoice and quote numbers by specifying a pattern', + 'pattern_help_2' => 'Available variables:', + 'pattern_help_3' => 'For example, :example would be converted to :value', + 'see_options' => 'See options', + 'invoice_counter' => 'Invoice Counter', + 'quote_counter' => 'Quote Counter', + 'type' => 'Type', + 'activity_1' => ':user created client :client', + 'activity_2' => ':user archived client :client', + 'activity_3' => ':user deleted client :client', + 'activity_4' => ':user created invoice :invoice', + 'activity_5' => ':user updated invoice :invoice', + 'activity_6' => ':user emailed invoice :invoice to :contact', + 'activity_7' => ':contact viewed invoice :invoice', + 'activity_8' => ':user archived invoice :invoice', + 'activity_9' => ':user deleted invoice :invoice', + 'activity_10' => ':contact entered payment :payment for :invoice', + 'activity_11' => ':user updated payment :payment', + 'activity_12' => ':user archived payment :payment', + 'activity_13' => ':user deleted payment :payment', + 'activity_14' => ':user entered :credit credit', + 'activity_15' => ':user updated :credit credit', + 'activity_16' => ':user archived :credit credit', + 'activity_17' => ':user deleted :credit credit', + 'activity_18' => ':user created quote :quote', + 'activity_19' => ':user updated quote :quote', + 'activity_20' => ':user emailed quote :quote to :contact', + 'activity_21' => ':contact viewed quote :quote', + 'activity_22' => ':user archived quote :quote', + 'activity_23' => ':user deleted quote :quote', + 'activity_24' => ':user restored quote :quote', + 'activity_25' => ':user restored invoice :invoice', + 'activity_26' => ':user restored client :client', + 'activity_27' => ':user restored payment :payment', + 'activity_28' => ':user restored :credit credit', + 'activity_29' => ':contact approved quote :quote', + 'activity_30' => ':user created :vendor', + 'activity_31' => ':user created :vendor', + 'activity_32' => ':user created :vendor', + 'activity_33' => ':user created :vendor', + 'activity_34' => ':user created expense :expense', + 'activity_35' => ':user created :vendor', + 'activity_36' => ':user created :vendor', + 'activity_37' => ':user created :vendor', + 'payment' => 'Payment', + 'system' => 'System', + 'signature' => 'Podpis e-mail', + 'default_messages' => 'Default Messages', + 'quote_terms' => 'Warunki oferty', + 'default_quote_terms' => 'Domyślne warunki oferty', + 'default_invoice_terms' => 'Domyślne warunki faktury', + 'default_invoice_footer' => 'Domyślna stopka faktury', + 'quote_footer' => 'Quote Footer', + 'free' => 'Free', + 'quote_is_approved' => 'This quote is approved', + 'apply_credit' => 'Apply Credit', + 'system_settings' => 'System Settings', + 'archive_token' => 'Archive Token', + 'archived_token' => 'Successfully archived token', + 'archive_user' => 'Archive User', + 'archived_user' => 'Successfully archived user', + 'archive_account_gateway' => 'Archive Gateway', + 'archived_account_gateway' => 'Successfully archived gateway', + 'archive_recurring_invoice' => 'Archiwizuj okresową fakturę', + 'archived_recurring_invoice' => 'Okresowa faktura została zarchiwizowana', + 'delete_recurring_invoice' => 'Usuń okresową fakturę', + 'deleted_recurring_invoice' => 'Okresowa faktura została usunięta.', + 'restore_recurring_invoice' => 'Przywróć okresową fakturę', + 'restored_recurring_invoice' => 'Okresowa faktura została przywrócona', + 'archived' => 'Zarchiwizowany', + 'untitled_account' => 'Firma bez nazwy', + 'before' => 'Przed', + 'after' => 'Po', + 'reset_terms_help' => 'Reset to the default account terms', + 'reset_footer_help' => 'Reset to the default account footer', + 'export_data' => 'Exportuj dane', + 'user' => 'Użytkownik', + 'country' => 'Kraj', + 'include' => 'Include', + 'logo_too_large' => 'Your logo is :size, for better PDF performance we suggest uploading an image file less than 200KB', + 'import_freshbooks' => 'Import From FreshBooks', + 'import_data' => 'Import Data', + 'source' => 'Źródło', + 'csv' => 'CSV', + 'client_file' => 'Plik klienta', + 'invoice_file' => 'Plik faktury', + 'task_file' => 'Plik zadania', + 'no_mapper' => 'No valid mapping for file', + 'invalid_csv_header' => 'Invalid CSV Header', + 'client_portal' => 'Portal klienta', + 'admin' => 'Administrator', + 'disabled' => 'Wyłączony', + 'show_archived_users' => 'Pokaż zarchiwizowanych użytkowników', + 'notes' => 'Notatki', + 'invoice_will_create' => 'client will be created', + 'invoices_will_create' => 'invoices will be created', + 'failed_to_import' => 'The following records failed to import, they either already exist or are missing required fields.', + 'publishable_key' => 'Publishable Key', + 'secret_key' => 'Sekretny klucz', + 'missing_publishable_key' => 'Set your Stripe publishable key for an improved checkout process', + 'email_design' => 'Email Design', + 'due_by' => 'Płatny do :date', + 'enable_email_markup' => 'Enable Markup', + 'enable_email_markup_help' => 'Make it easier for your clients to pay you by adding schema.org markup to your emails.', + 'template_help_title' => 'Templates Help', + 'template_help_1' => 'Available variables:', + 'email_design_id' => 'Email Style', + 'email_design_help' => 'Make your emails look more professional with HTML layouts', + 'plain' => 'Plain', + 'light' => 'Light', + 'dark' => 'Dark', + 'industry_help' => 'Used to provide comparisons against the averages of companies of similar size and industry.', + 'subdomain_help' => 'Customize the invoice link subdomain or display the invoice on your own website.', + 'invoice_number_help' => 'Specify a prefix or use a custom pattern to dynamically set the invoice number.', + 'quote_number_help' => 'Specify a prefix or use a custom pattern to dynamically set the quote number.', + 'custom_client_fields_helps' => 'Add a field when creating a client and display the label and value on the PDF.', + 'custom_account_fields_helps' => 'Add a label and value to the company details section of the PDF.', + 'custom_invoice_fields_helps' => 'Add a field when creating an invoice and display the label and value on the PDF.', + 'custom_invoice_charges_helps' => 'Add a field when creating an invoice and include the charge in the invoice subtotals.', + 'token_expired' => 'Validation token was expired. Please try again.', + 'invoice_link' => 'Invoice Link', + 'button_confirmation_message' => 'Click to confirm your email address.', + 'confirm' => 'Confirm', + 'email_preferences' => 'Email Preferences', + 'created_invoices' => 'Successfully created :count invoice(s)', + 'next_invoice_number' => 'The next invoice number is :number.', + 'next_quote_number' => 'The next quote number is :number.', + 'days_before' => 'days before', + 'days_after' => 'days after', + 'field_due_date' => 'termin', + 'field_invoice_date' => 'invoice date', + 'schedule' => 'Schedule', + 'email_designs' => 'Email Designs', + 'assigned_when_sent' => 'Assigned when sent', + 'white_label_purchase_link' => 'Purchase a white label license', + 'expense' => 'Wydatek', + 'expenses' => 'Wydatki', + 'new_expense' => 'Nowy wydatek', + 'enter_expense' => 'Dodaj wydatek', + 'vendors' => 'Vendors', + 'new_vendor' => 'New Vendor', + 'payment_terms_net' => 'Net', + 'vendor' => 'Vendor', + 'edit_vendor' => 'Edit Vendor', + 'archive_vendor' => 'Archive Vendor', + 'delete_vendor' => 'Delete Vendor', + 'view_vendor' => 'View Vendor', + 'deleted_expense' => 'Successfully deleted expense', + 'archived_expense' => 'Successfully archived expense', + 'deleted_expenses' => 'Successfully deleted expenses', + 'archived_expenses' => 'Successfully archived expenses', + 'expense_amount' => 'Expense Amount', + 'expense_balance' => 'Expense Balance', + 'expense_date' => 'Expense Date', + 'expense_should_be_invoiced' => 'Should this expense be invoiced?', + 'public_notes' => 'Public Notes', + 'invoice_amount' => 'Invoice Amount', + 'exchange_rate' => 'Exchange Rate', + 'yes' => 'Yes', + 'no' => 'No', + 'should_be_invoiced' => 'Should be invoiced', + 'view_expense' => 'View expense # :expense', + 'edit_expense' => 'Edit Expense', + 'archive_expense' => 'Archive Expense', + 'delete_expense' => 'Delete Expense', + 'view_expense_num' => 'Expense # :expense', + 'updated_expense' => 'Successfully updated expense', + 'created_expense' => 'Successfully created expense', + 'enter_expense' => 'Dodaj wydatek', + 'view' => 'View', + 'restore_expense' => 'Przywróć wydatek', + 'invoice_expense' => 'Faktura na wydatek', + 'expense_error_multiple_clients' => 'The expenses can\'t belong to different clients', + 'expense_error_invoiced' => 'Expense has already been invoiced', + 'convert_currency' => 'Konwersja waluty', + 'num_days' => 'Liczba dni', + 'create_payment_term' => 'Utwórz warunki płatności', + 'edit_payment_terms' => 'Edytuj warunki płatności', + 'edit_payment_term' => 'Edytuj warunki płatności', + 'archive_payment_term' => 'Zarchiwizuj warunki płatności', + 'recurring_due_dates' => 'Terminy faktur okresowych', + 'recurring_due_date_help' => '

    Automatycznie ustawia termin faktury.

    +

    Invoices on a monthly or yearly cycle set to be due on or before the day they are created will be due the next month. Invoices set to be due on the 29th or 30th in months that don\'t have that day will be due the last day of the month.

    +

    Invoices on a weekly cycle set to be due on the day of the week they are created will be due the next week.

    +

    For example:

    + ', + 'due' => 'Opłata', + 'next_due_on' => 'Następna opłata: :date', + 'use_client_terms' => 'Use client terms', + 'day_of_month' => ':ordinal day of month', + 'last_day_of_month' => 'Last day of month', + 'day_of_week_after' => ':ordinal :day after', + 'sunday' => 'Niedziela', + 'monday' => 'Poniedziałek', + 'tuesday' => 'Wtorek', + 'wednesday' => 'Środa', + 'thursday' => 'Czwartek', + 'friday' => 'Piątek', + 'saturday' => 'Sobota', + 'header_font_id' => 'Czcionka nagłówka', + 'body_font_id' => 'Czcionka', + 'color_font_help' => 'Notatka: Podstawowe kolory i czcionki są wykorzystywane na portalu klienta i w niestandardowych szablonach email-owych.', + 'live_preview' => 'Podgląd', + 'invalid_mail_config' => 'E-mail nie został wysłany. Sprawdź czy ustawienia mailowe są poprawne.', + 'invoice_message_button' => 'Aby wyświetlić fakturę za :amount, kliknij przycisk poniżej.', + 'quote_message_button' => 'Aby wyświetlić swoją ofertę na :amount, kliknij przycisk poniżej.', + 'payment_message_button' => 'Dziekuję za wpłatę :amount.', + 'payment_type_direct_debit' => 'Polecenie zapłaty', + 'bank_accounts' => 'Karty kredytowe i banki', + 'add_bank_account' => 'Dodaj konto bankowe', + 'setup_account' => 'Ustawienia konta', + 'import_expenses' => 'Koszty importu', + 'bank_id' => 'Bank', + 'integration_type' => 'Rodzaj integracji', + 'updated_bank_account' => 'Konto bankowe zostało zaktualizowane', + 'edit_bank_account' => 'Edytuj konto bankowe', + 'archive_bank_account' => 'Archiwizuj konto bankowe', + 'archived_bank_account' => 'Konto bankowe zostało zarchiwizowane.', + 'created_bank_account' => 'Konto bankowe zostało utworzone', + 'validate_bank_account' => 'Zatwierdź konto bankowe', + 'bank_password_help' => 'Notatka: Twoje hasło zostało wysłane bezpiecznie i nie jest przechowywane na naszych serwerach.', + 'bank_password_warning' => 'Ostrzeżenie: Twoje hasło może być wysłane w postaci zwykłego tekstu, rozwaź aktywację protokołu HTTPS.', + 'username' => 'Użytkownik', + 'account_number' => 'Numer konta', + 'account_name' => 'Nazwa konta', + 'bank_account_error' => 'Nie można pobrać danych konta, sprawdź uprawnienia.', + 'status_approved' => 'Zatwierdzono', + 'quote_settings' => 'Ustawienia oferty', + 'auto_convert_quote' => 'Automatycznie konwertuj ofertę', + 'auto_convert_quote_help' => 'Utwórz automatycznie fakturę z oferty zaakceptowanej przez klienta.', + 'validate' => 'Zatwierdź', + 'info' => 'Informacja', + 'imported_expenses' => 'Successfully created :count_vendors vendor(s) and :count_expenses expense(s)', + 'iframe_url_help3' => 'Note: if you plan on accepting credit cards details we strongly recommend enabling HTTPS on your site.', + 'expense_error_multiple_currencies' => 'The expenses can\'t have different currencies.', + 'expense_error_mismatch_currencies' => 'The client\'s currency does not match the expense currency.', + 'trello_roadmap' => 'Trello Roadmap', + 'header_footer' => 'Header/Footer', + 'first_page' => 'Pierwsza strona', + 'all_pages' => 'Wszystkie strony', + 'last_page' => 'Ostatnia strona', + 'all_pages_header' => 'Pokaż nagłówek na', + 'all_pages_footer' => 'Pokaż stopkę na', + 'invoice_currency' => 'Waluta faktury', + 'enable_https' => 'Zalecamy korzystanie z protokołu HTTPS, aby zaakceptować dane karty kredytowej online.', + 'quote_issued_to' => 'Oferta wydana do', + 'show_currency_code' => 'Kod waluty', + 'trial_message' => 'Twoje konto otrzyma bezpłatny dwutygodniowy okres próbny naszego pro planu.', + 'trial_footer' => 'Bezpłatny okres próbny ważny tylko :count dni, aby aktualizować kliknij: :link.', + 'trial_footer_last_day' => 'To ostatni dzień twojego bezpłatnego okresu próbnego, aby zaktualizować kliknij: :link.', + 'trial_call_to_action' => 'Rozpocznij darmowy okres próbny', + 'trial_success' => 'Darmowy okres próbny został włączony', + 'overdue' => 'Zaległy', + + + 'white_label_text' => 'Kup white label licencję na JEDEN ROK za $'.WHITE_LABEL_PRICE.' aby usunąć z portalu klienta logo Invoice Ninja i wesprzeć nasz projekt.', + 'user_email_footer' => 'Aby dostosować ustawienia powiadomień email, zobacz '.SITE_URL.'/settings/notifications', + 'reset_password_footer' => 'If you did not request this password reset please email our support: '.CONTACT_EMAIL, + 'limit_users' => 'Sorry, this will exceed the limit of '.MAX_NUM_USERS.' users', + 'more_designs_self_host_header' => 'Kup 6 szablonów faktur za jedyne $'.INVOICE_DESIGNS_PRICE, + 'old_browser' => 'Proszę użyć nowszej przeglądarki', + 'white_label_custom_css' => ':link for $'.WHITE_LABEL_PRICE.' to enable custom styling and help support our project.', + 'bank_accounts_help' => 'Connect a bank account to automatically import expenses and create vendors. Supports American Express and 400+ US banks.', + 'security' => [ + 'too_many_attempts' => 'Zbyt wiele prób. Spróbuj ponownie za kilka minut.', + 'wrong_credentials' => 'Nieprawidłowy e-mail lub hasło.', + 'confirmation' => 'Twoje konto zostało potwierdzone!', + 'wrong_confirmation' => 'Błędny kod potwierdzający.', + 'password_forgot' => 'Informacje dotyczące resetowania hasła zostały wysłane na Twój adres e-mail.', + 'password_reset' => 'Twoje hasło zostało zmienione.', + 'wrong_password_reset' => 'Nieprawidłowe hasło. Spróbuj ponownie', + ], + 'pro_plan' => [ + 'remove_logo' => ':link to remove the Invoice Ninja logo by joining the Pro Plan', + 'remove_logo_link' => 'Kliknij tutaj', + ], + 'invitation_status' => [ + 'sent' => 'E-mail wysłany', + 'opened' => 'Email otwarty', + 'viewed' => 'Przeglądana faktura', + ], + 'email_errors' => [ + 'inactive_client' => 'E-maile nie mogą być wysyłane do klientów nieaktywnych', + 'inactive_contact' => 'E-mail nie może zostać wysłany do nieaktywnych kontaktów', + 'inactive_invoice' => 'E-mail nie może zostać wysłany do nieaktywnych faktur', + 'user_unregistered' => 'Proszę zarejestrować swoje konto, aby wysyłać e-maile', + 'user_unconfirmed' => 'Proszę potwierdzić swoje konto do wysyłania e-maili', + 'invalid_contact_email' => 'Nieprawidłowy e-mail kontaktowy', + ], + + 'navigation' => 'Nawigacja', + 'list_invoices' => 'Lista faktur', + 'list_clients' => 'Lista klientów', + 'list_quotes' => 'Lista ofert', + 'list_tasks' => 'Lista zadań', + 'list_expenses' => 'Lista wydatków', + 'list_recurring_invoices' => 'Lista faktur okresowych', + 'list_payments' => 'Lista wpłat', + 'list_credits' => 'Lista kredytów', + 'tax_name' => 'Nazwa podatku', + 'report_settings' => 'Ustawienia raportu', + 'search_hotkey' => 'skrót to /', + + 'new_user' => 'Nowy użytkownik', + 'new_product' => 'Nowy produkt', + 'new_tax_rate' => 'Nowa stawka podatkowa', + 'invoiced_amount' => 'Fakturowana kwota', + 'invoice_item_fields' => 'Invoice Item Fields', + 'custom_invoice_item_fields_help' => 'Add a field when creating an invoice item and display the label and value on the PDF.', + 'recurring_invoice_number' => 'Numer faktury okresowej', + 'recurring_invoice_number_prefix_help' => 'Dodaj własny prefix do numeru faktury okresowej. Wartość domyślna to \'R\'.', + + // Client Passwords + 'enable_portal_password'=>'Hasło ochrony faktur', + 'enable_portal_password_help'=>'Allows you to set a password for each contact. If a password is set, the contact will be required to enter a password before viewing invoices.', + 'send_portal_password'=>'Generate password automatically', + 'send_portal_password_help'=>'If no password is set, one will be generated and sent with the first invoice.', + + 'expired' => 'Wygasło', + 'invalid_card_number' => 'Numer karty kredytowej jest nieprawidłowy.', + 'invalid_expiry' => 'Data ważności jest nieprawidłowa.', + 'invalid_cvv' => 'Kod CVV jest nieprawidłowy.', + 'cost' => 'Koszt', + 'create_invoice_for_sample' => 'Notatka: aby zobaczyć podgląd, utwórz fakturę.', + + // User Permissions + 'owner' => 'Właściciel', + 'administrator' => 'Administrator', + 'administrator_help' => 'Allow user to manage users, change settings and modify all records', + 'user_create_all' => 'Create clients, invoices, etc.', + 'user_view_all' => 'View all clients, invoices, etc.', + 'user_edit_all' => 'Edit all clients, invoices, etc.', + 'gateway_help_20' => ':link to sign up for Sage Pay.', + 'gateway_help_21' => ':link to sign up for Sage Pay.', + 'partial_due' => 'Partial Due', + 'restore_vendor' => 'Restore Vendor', + 'restored_vendor' => 'Successfully restored vendor', + 'restored_expense' => 'Successfully restored expense', + 'permissions' => 'Permissions', + 'create_all_help' => 'Allow user to create and modify records', + 'view_all_help' => 'Allow user to view records they didn\'t create', + 'edit_all_help' => 'Allow user to modify records they didn\'t create', + 'view_payment' => 'Zobacz wpłatę', + + 'january' => 'Styczeń', + 'february' => 'Luty', + 'march' => 'Marzec', + 'april' => 'Kwiecień', + 'may' => 'Maj', + 'june' => 'Czerwiec', + 'july' => 'Lipiec', + 'august' => 'Sierpień', + 'september' => 'Wrzesień', + 'october' => 'Październik', + 'november' => 'Listopad', + 'december' => 'Grudzień', + + // Documents + 'documents_header' => 'Dokumenty:', + 'email_documents_header' => 'Dokumenty:', + 'email_documents_example_1' => 'Widgets Receipt.pdf', + 'email_documents_example_2' => 'Final Deliverable.zip', + 'invoice_documents' => 'Dokumenty', + 'expense_documents' => 'Załączone dokumenty', + 'invoice_embed_documents' => 'Embed Documents', + 'invoice_embed_documents_help' => 'Include attached images in the invoice.', + 'document_email_attachment' => 'Załącz dokumenty', + 'download_documents' => 'Ściągnij dokumenty (:size)', + 'documents_from_expenses' => 'From Expenses:', + 'dropzone' => array(// See http://www.dropzonejs.com/#config-dictDefaultMessage + 'DefaultMessage' => 'Upuść pliki lub kliknij, aby przesłać', + 'FallbackMessage' => 'Your browser does not support drag\'n\'drop file uploads.', + 'FallbackText' => 'Please use the fallback form below to upload your files like in the olden days.', + 'FileTooBig' => 'Plik jest zbyt duży ({{filesize}}MiB). Max rozmiar pliku: {{maxFilesize}}MiB.', + 'InvalidFileType' => 'Nie możesz przesłać plików tego typu.', + 'ResponseError' => 'Serwer zwraca {{statusCode}} kod.', + 'CancelUpload' => 'Anuluj przesyłanie', + 'CancelUploadConfirmation' => 'Czy na pewno chcesz anulować przesyłanie pliku?', + 'RemoveFile' => 'Usuń plik', + ), + 'documents' => 'Dokumenty', + 'document_date' => 'Data dokumentu', + 'document_size' => 'Rozmiar', + + 'enable_client_portal' => 'Portal Klienta', + 'enable_client_portal_help' => 'Pokaż/ukryj portal klienta.', + 'enable_client_portal_dashboard' => 'Pulpit', + 'enable_client_portal_dashboard_help' => 'Pokaż/ukryj pulpit w panelu klienta.', + + // Plans + 'account_management' => 'Zarządzanie kontem', + 'plan_status' => 'Plan Status', + + 'plan_upgrade' => 'Aktualizuj', + 'plan_change' => 'Zmień plan', + 'pending_change_to' => 'Zmienia się na', + 'plan_changes_to' => ':plan on :date', + 'plan_term_changes_to' => ':plan (:term) on :date', + 'cancel_plan_change' => 'Anuluj zmianę', + 'plan' => 'Plan', + 'expires' => 'Wygasa', + 'renews' => 'Odnawia', + 'plan_expired' => ':plan Plan Expired', + 'trial_expired' => ':plan Plan Trial Ended', + 'never' => 'Nigdy', + 'plan_free' => 'Darmowy', + 'plan_pro' => 'Pro', + 'plan_enterprise' => 'Enterprise', + 'plan_white_label' => 'Self Hosted (White labeled)', + 'plan_free_self_hosted' => 'Self Hosted (Free)', + 'plan_trial' => 'Trial', + 'plan_term' => 'Term', + 'plan_term_monthly' => 'Miesięcznie', + 'plan_term_yearly' => 'Rocznie', + 'plan_term_month' => 'Miesiąc', + 'plan_term_year' => 'Rok', + 'plan_price_monthly' => '$:price/miesiąc', + 'plan_price_yearly' => '$:price/rok', + 'updated_plan' => 'Ustawienia planu zaktualizowane', + 'plan_paid' => 'Termin rozpoczął', + 'plan_started' => 'Plan rozpoczął', + 'plan_expires' => 'Plan Wygasa', + + 'white_label_button' => 'Biała etykieta', + + 'pro_plan_year_description' => 'One year enrollment in the Invoice Ninja Pro Plan.', + 'pro_plan_month_description' => 'One month enrollment in the Invoice Ninja Pro Plan.', + 'enterprise_plan_product' => 'Plan Enterprise', + 'enterprise_plan_year_description' => 'One year enrollment in the Invoice Ninja Enterprise Plan.', + 'enterprise_plan_month_description' => 'One month enrollment in the Invoice Ninja Enterprise Plan.', + 'plan_credit_product' => 'Kredyt', + 'plan_credit_description' => 'Kredyt za niewykorzystany czas', + 'plan_pending_monthly' => 'Will switch to monthly on :date', + 'plan_refunded' => 'Zwrot został wystawiony.', + + 'live_preview' => 'Podgląd', + 'page_size' => 'Rozmiar strony', + 'live_preview_disabled' => 'Podgląd obrazu na żywo został wyłączony w celu wsparcia wybranej czcionki', + 'invoice_number_padding' => 'Padding', + +); + +return $LANG; + +?>. \ No newline at end of file diff --git a/resources/lang/pl/validation.php b/resources/lang/pl/validation.php new file mode 100644 index 000000000000..d3ffdb6a8155 --- /dev/null +++ b/resources/lang/pl/validation.php @@ -0,0 +1,106 @@ + ":attribute musi być zaakceptowany.", + "active_url" => ":attribute nie jest poprawnym URL-em.", + "after" => ":attribute musi być datą za :date.", + "alpha" => ":attribute może zawierać tylko litery.", + "alpha_dash" => ":attribute może zawierać tylko litery, liczby i myślniki.", + "alpha_num" => ":attribute może zawierać tylko litery i liczby.", + "array" => ":attribute musi być tablicą.", + "before" => ":attribute musi być datą przed :date.", + "between" => array( + "numeric" => ":attribute musi być pomiędzy :min - :max.", + "file" => ":attribute musi mieć rozmiar pomiędzy :min - :max kilobajtów.", + "string" => ":attribute musi mieć pomiędzy :min - :max znaków.", + "array" => ":attribute musi zawierać :min - :max pozycji.", + ), + "confirmed" => ":attribute potwierdzenie nie jest zgodne.", + "date" => ":attribute nie jest prawidłową datą.", + "date_format" => ":attribute nie jest zgodne z formatem :format.", + "different" => ":attribute i :other muszą być różne.", + "digits" => ":attribute musi mieć :digits cyfr.", + "digits_between" => ":attribute musi być w przedziale od :min do :max cyfr.", + "email" => ":attribute format jest nieprawidłowy.", + "exists" => "Zaznaczony :attribute jest niepoprawny.", + "image" => ":attribute musi być zdjęciem.", + "in" => "Zaznaczony :attribute jest niepoprawny.", + "integer" => ":attribute musi być liczbą całkowitą.", + "ip" => ":attribute musi być poprawnym adresem IP.", + "max" => array( + "numeric" => ":attribute nie może być większy niż :max.", + "file" => ":attribute nie może być większy niż :max kilobajtów.", + "string" => ":attribute nie może być dłuższy niż :max znaków.", + "array" => ":attribute nie może zawierać więcej niż :max pozycji.", + ), + "mimes" => ":attribute musi być plikiem o typie: :values.", + "min" => array( + "numeric" => ":attribute musi być przynajmniej :min.", + "file" => ":attribute musi mieć przynajmniej :min kilobajtów.", + "string" => ":attribute musi mieć przynajmniej :min znaków.", + "array" => ":attribute musi zawierać przynajmniej :min pozycji.", + ), + "not_in" => "Zaznaczony :attribute jest niepoprawny.", + "numeric" => ":attribute musi być cyfrą.", + "regex" => ":attribute format jest niepoprawny.", + "required" => ":attribute pole jest wymagane.", + "required_if" => ":attribute pole jest wymagane jeśli :other ma :value.", + "required_with" => ":attribute pole jest wymagane kiedy :values jest obecne.", + "required_without" => ":attribute pole jest wymagane kiedy :values nie występuje.", + "same" => ":attribute i :other muszą być takie same.", + "size" => array( + "numeric" => ":attribute musi mieć :size.", + "file" => ":attribute musi mieć :size kilobajtów.", + "string" => ":attribute musi mieć :size znaków.", + "array" => ":attribute musi zawierać :size pozycji.", + ), + "unique" => ":attribute już istnieje.", + "url" => ":attribute format jest nieprawidłowy.", + + "positive" => ":attribute musi być większe niż zero.", + "has_credit" => "Klient ma niewystarczająco kredytu.", + "notmasked" => "Wartości są maskowane", + "less_than" => ":attribute musi być mniejsze od :value", + "has_counter" => "Wartość musi zawierać {\$counter}", + "valid_contacts" => "Kontakt musi posiadać e-mail lub nazwę", + "valid_invoice_items" => "Faktura przekracza maksymalną kwotę", + + /* + |-------------------------------------------------------------------------- + | Custom Validation Language Lines + |-------------------------------------------------------------------------- + | + | Here you may specify custom validation messages for attributes using the + | convention "attribute.rule" to name the lines. This makes it quick to + | specify a specific custom language line for a given attribute rule. + | + */ + + 'custom' => array(), + + /* + |-------------------------------------------------------------------------- + | Custom Validation Attributes + |-------------------------------------------------------------------------- + | + | The following language lines are used to swap attribute place-holders + | with something more reader friendly such as E-Mail Address instead + | of "email". This simply helps us make messages a little cleaner. + | + */ + + 'attributes' => array(), + +); \ No newline at end of file diff --git a/resources/lang/pt_BR/texts.php b/resources/lang/pt_BR/texts.php index fab6167659b6..47096b7d7c2f 100644 --- a/resources/lang/pt_BR/texts.php +++ b/resources/lang/pt_BR/texts.php @@ -487,7 +487,7 @@ return array( 'invoice_history' => 'Histórico de Faturas', 'quote_history' => 'Histórico de Orçamentos', 'current_version' => 'Versão Atual', - 'select_versiony' => 'Selecionar versão', + 'select_version' => 'Selecionar versão', 'view_history' => 'Visualizar Histórico', 'edit_payment' => 'Editar Pagamento', diff --git a/resources/lang/sv/texts.php b/resources/lang/sv/texts.php index 18e4dd2c431f..e099b44fc1c2 100644 --- a/resources/lang/sv/texts.php +++ b/resources/lang/sv/texts.php @@ -492,7 +492,7 @@ return array( 'invoice_history' => 'Fakturahistorik', 'quote_history' => 'Offerthistorik', 'current_version' => 'Nuvarande version', - 'select_versiony' => 'Välj version', + 'select_version' => 'Välj version', 'view_history' => 'Visa historik', 'edit_payment' => 'Ändra betalning', diff --git a/resources/views/accounts/api_tokens.blade.php b/resources/views/accounts/api_tokens.blade.php index 9c328e6b2643..49029bf8d2b3 100644 --- a/resources/views/accounts/api_tokens.blade.php +++ b/resources/views/accounts/api_tokens.blade.php @@ -6,7 +6,7 @@
    {!! Button::normal(trans('texts.documentation'))->asLinkTo(NINJA_WEB_URL.'/api-documentation/')->withAttributes(['target' => '_blank'])->appendIcon(Icon::create('info-sign')) !!} - @if (Utils::isNinja()) + @if (Utils::isNinja() && !Utils::isReseller()) {!! Button::normal(trans('texts.zapier'))->asLinkTo(ZAPIER_URL)->withAttributes(['target' => '_blank']) !!} @endif @if (Utils::hasFeature(FEATURE_API)) diff --git a/resources/views/accounts/management.blade.php b/resources/views/accounts/management.blade.php index cf9917deb005..cfb7b6eb28ee 100644 --- a/resources/views/accounts/management.blade.php +++ b/resources/views/accounts/management.blade.php @@ -94,9 +94,9 @@
    @endif @if (Utils::isNinjaProd()) - {!! Former::actions( Button::success(trans('texts.plan_upgrade'))->large()->withAttributes(['onclick' => 'showChangePlan()'])->appendIcon(Icon::create('plus-sign'))) !!} - @else - {!! Former::actions( Button::success(trans('texts.white_label_button'))->large()->withAttributes(['onclick' => 'loadImages("#whiteLabelModal");$("#whiteLabelModal").modal("show");'])->appendIcon(Icon::create('plus-sign'))) !!} + {!! Former::actions( Button::success(trans('texts.plan_upgrade'))->large()->withAttributes(['onclick' => 'showChangePlan()'])->appendIcon(Icon::create('plus-sign'))) !!} + @elseif (!$account->hasFeature(FEATURE_WHITE_LABEL)) + {!! Former::actions( Button::success(trans('texts.white_label_button'))->large()->withAttributes(['onclick' => 'loadImages("#whiteLabelModal");$("#whiteLabelModal").modal("show");'])->appendIcon(Icon::create('plus-sign'))) !!} @endif @endif diff --git a/resources/views/accounts/template.blade.php b/resources/views/accounts/template.blade.php index 0d4294af5b6f..68279299f607 100644 --- a/resources/views/accounts/template.blade.php +++ b/resources/views/accounts/template.blade.php @@ -62,11 +62,14 @@
    +

     

    -
    -

     

    +

    @include('partials/quill_toolbar', ['name' => $field])
    +
    + {!! Button::primary(trans('texts.preview'))->withAttributes(['onclick' => 'serverPreview("'.$field.'")'])->small() !!} +
    diff --git a/resources/views/accounts/templates_and_reminders.blade.php b/resources/views/accounts/templates_and_reminders.blade.php index 067433a21763..87453e80dae6 100644 --- a/resources/views/accounts/templates_and_reminders.blade.php +++ b/resources/views/accounts/templates_and_reminders.blade.php @@ -25,11 +25,16 @@ {!! Former::vertical_open()->addClass('warn-on-exit') !!} - {!! Former::populate($account) !!} @foreach ([ENTITY_INVOICE, ENTITY_QUOTE, ENTITY_PAYMENT, REMINDER1, REMINDER2, REMINDER3] as $type) @foreach (['subject', 'template'] as $field) - {!! Former::populateField("email_{$field}_{$type}", $templates[$type][$field]) !!} + {{ Former::populateField("email_{$field}_{$type}", $templates[$type][$field]) }} + @endforeach + @endforeach + + @foreach ([REMINDER1, REMINDER2, REMINDER3] as $type) + @foreach (['enable', 'num_days', 'direction', 'field'] as $field) + {{ Former::populateField("{$field}_{$type}", $account->{"{$field}_{$type}"}) }} @endforeach @endforeach @@ -80,6 +85,26 @@ + + +