Merge pull request #1 from invoiceninja/master

Actualizacion desde proyecto original
This commit is contained in:
ridgarou 2016-02-09 10:56:16 +01:00
commit 0766f77713
153 changed files with 13615 additions and 10446 deletions

View File

@ -101,6 +101,7 @@ module.exports = function(grunt) {
'public/vendor/jspdf/dist/jspdf.min.js',
'public/vendor/moment/min/moment.min.js',
'public/vendor/moment-timezone/builds/moment-timezone-with-data.min.js',
'public/vendor/stacktrace-js/dist/stacktrace-with-polyfills.min.js',
//'public/vendor/moment-duration-format/lib/moment-duration-format.js',
//'public/vendor/handsontable/dist/jquery.handsontable.full.min.js',
//'public/vendor/pdfmake/build/pdfmake.min.js',
@ -111,7 +112,7 @@ module.exports = function(grunt) {
'public/js/script.js',
'public/js/pdf.pdfmake.js',
],
dest: 'public/js/built.js',
dest: 'public/built.js',
nonull: true
},
js_public: {
@ -126,7 +127,7 @@ module.exports = function(grunt) {
'public/js/bootstrap-combobox.js',
],
dest: 'public/js/built.public.js',
dest: 'public/built.public.js',
nonull: true
},
css: {
@ -171,7 +172,7 @@ module.exports = function(grunt) {
'public/js/pdfmake.min.js',
'public/js/vfs_fonts.js',
],
dest: 'public/js/pdf.built.js',
dest: 'public/pdf.built.js',
nonull: true
}
}

View File

@ -1,5 +1,5 @@
Attribution Assurance License
Copyright (c) 2015 by Hillel Coren
Copyright (c) 2016 by Hillel Coren
http://www.hillelcoren.com
All Rights Reserved

View File

@ -19,12 +19,14 @@ class TestOFX extends Command
{
$this->info(date('Y-m-d').' Running TestOFX...');
/*
$bankId = env('TEST_BANK_ID');
$username = env('TEST_BANK_USERNAME');
$password = env('TEST_BANK_PASSWORD');
$data = $this->bankAccountService->loadBankAccounts($bankId, $username, $password, false);
print "<pre>".print_r($data, 1)."</pre>";
echo json_encode($data);
*/
}
}

View File

@ -4,6 +4,7 @@ use Auth;
use Utils;
use Response;
use Input;
use Validator;
use Cache;
use App\Models\Client;
use App\Models\Account;
@ -18,6 +19,8 @@ use App\Ninja\Transformers\UserAccountTransformer;
use App\Http\Controllers\BaseAPIController;
use Swagger\Annotations as SWG;
use App\Http\Requests\UpdateAccountRequest;
class AccountApiController extends BaseAPIController
{
protected $accountRepo;
@ -101,4 +104,15 @@ class AccountApiController extends BaseAPIController
{
return $this->processLogin($request);
}
public function update(UpdateAccountRequest $request)
{
$account = Auth::user()->account;
$this->accountRepo->save($request->input(), $account);
$transformer = new AccountTransformer(null, $request->serializer);
$account = $this->createItem($account, $transformer, 'account');
return $this->response($account);
}
}

View File

@ -29,6 +29,8 @@ use App\Events\UserLoggedIn;
use App\Events\UserSettingsChanged;
use App\Services\AuthService;
use App\Http\Requests\UpdateAccountRequest;
class AccountController extends BaseController
{
protected $accountRepo;
@ -611,11 +613,12 @@ class AccountController extends BaseController
$iframeURL = rtrim($iframeURL, "/");
$subdomain = preg_replace('/[^a-zA-Z0-9_\-\.]/', '', substr(strtolower(Input::get('subdomain')), 0, MAX_SUBDOMAIN_LENGTH));
if ($iframeURL || !$subdomain || in_array($subdomain, ['www', 'app', 'mail', 'admin', 'blog', 'user', 'contact', 'payment', 'payments', 'billing', 'invoice', 'business', 'owner'])) {
if ($iframeURL) {
$subdomain = null;
}
if ($subdomain) {
$rules['subdomain'] = "unique:accounts,subdomain,{$user->account_id},id";
$exclude = ['www', 'app', 'mail', 'admin', 'blog', 'user', 'contact', 'payment', 'payments', 'billing', 'invoice', 'business', 'owner', 'info', 'ninja'];
$rules['subdomain'] = "unique:accounts,subdomain,{$user->account_id},id|not_in:" . implode(',', $exclude);
}
$validator = Validator::make(Input::all(), $rules);
@ -704,7 +707,9 @@ class AccountController extends BaseController
$account->quote_number_prefix = null;
}
if (!$account->share_counter && $account->invoice_number_prefix == $account->quote_number_prefix) {
if (!$account->share_counter
&& $account->invoice_number_prefix == $account->quote_number_prefix
&& $account->invoice_number_pattern == $account->quote_number_pattern) {
Session::flash('error', trans('texts.invalid_counter'));
return Redirect::to('settings/'.ACCOUNT_INVOICE_SETTINGS)->withInput();
@ -724,6 +729,8 @@ class AccountController extends BaseController
$account = Auth::user()->account;
$account->hide_quantity = Input::get('hide_quantity') ? true : false;
$account->hide_paid_to_date = Input::get('hide_paid_to_date') ? true : false;
$account->all_pages_header = Input::get('all_pages_header') ? true : false;
$account->all_pages_footer = Input::get('all_pages_footer') ? true : false;
$account->header_font_id = Input::get('header_font_id');
$account->body_font_id = Input::get('body_font_id');
$account->primary_color = Input::get('primary_color');
@ -762,37 +769,10 @@ class AccountController extends BaseController
return Redirect::to('settings/'.ACCOUNT_NOTIFICATIONS);
}
private function saveDetails()
public function updateDetails(UpdateAccountRequest $request)
{
$rules = array(
'name' => 'required',
'logo' => 'sometimes|max:'.MAX_LOGO_FILE_SIZE.'|mimes:jpeg,gif,png',
);
$validator = Validator::make(Input::all(), $rules);
if ($validator->fails()) {
return Redirect::to('settings/'.ACCOUNT_COMPANY_DETAILS)
->withErrors($validator)
->withInput();
} else {
$account = Auth::user()->account;
$account->name = trim(Input::get('name'));
$account->id_number = trim(Input::get('id_number'));
$account->vat_number = trim(Input::get('vat_number'));
$account->work_email = trim(Input::get('work_email'));
$account->website = trim(Input::get('website'));
$account->work_phone = trim(Input::get('work_phone'));
$account->address1 = trim(Input::get('address1'));
$account->address2 = trim(Input::get('address2'));
$account->city = trim(Input::get('city'));
$account->state = trim(Input::get('state'));
$account->postal_code = trim(Input::get('postal_code'));
$account->country_id = Input::get('country_id') ? Input::get('country_id') : null;
$account->size_id = Input::get('size_id') ? Input::get('size_id') : null;
$account->industry_id = Input::get('industry_id') ? Input::get('industry_id') : null;
$account->email_footer = Input::get('email_footer');
$account->save();
$this->accountRepo->save($request->input(), $account);
/* Logo image file */
if ($file = Input::file('logo')) {
@ -836,7 +816,6 @@ class AccountController extends BaseController
return Redirect::to('settings/'.ACCOUNT_COMPANY_DETAILS);
}
}
private function saveUserDetails()
{
@ -882,6 +861,7 @@ class AccountController extends BaseController
$account->currency_id = Input::get('currency_id') ? Input::get('currency_id') : 1; // US Dollar
$account->language_id = Input::get('language_id') ? Input::get('language_id') : 1; // English
$account->military_time = Input::get('military_time') ? true : false;
$account->show_currency_code = Input::get('show_currency_code') ? true : false;
$account->save();
event(new UserSettingsChanged());

View File

@ -89,6 +89,10 @@ class AccountGatewayController extends BaseController
->orderBy('name')->get();
$data['hiddenFields'] = Gateway::$hiddenFields;
if ( ! \Request::secure() && ! Utils::isNinjaDev()) {
Session::flash('warning', trans('texts.enable_https'));
}
return View::make('accounts.account_gateway', $data);
}
@ -195,6 +199,8 @@ class AccountGatewayController extends BaseController
if ($gatewayId == GATEWAY_DWOLLA) {
$optional = array_merge($optional, ['key', 'secret']);
} elseif ($gatewayId == GATEWAY_STRIPE) {
$rules['publishable_key'] = 'required';
}
foreach ($fields as $field => $details) {

View File

@ -212,9 +212,9 @@ class AppController extends BaseController
];
try {
$this->mailer->sendTo($email, $email, $fromName, 'Test email', 'contact', $data);
$response = $this->mailer->sendTo($email, $email, $fromName, 'Test email', 'contact', $data);
return 'Sent';
return $response === true ? 'Sent' : $response;
} catch (Exception $e) {
return $e->getMessage();
}
@ -224,6 +224,7 @@ class AppController extends BaseController
{
if (!Utils::isNinjaProd() && !Utils::isDatabaseSetup()) {
try {
set_time_limit(60 * 5); // shouldn't take this long but just in case
Artisan::call('migrate', array('--force' => true));
if (Industry::count() == 0) {
Artisan::call('db:seed', array('--force' => true));
@ -241,12 +242,19 @@ class AppController extends BaseController
{
if (!Utils::isNinjaProd()) {
try {
set_time_limit(60 * 5);
Cache::flush();
Session::flush();
Artisan::call('optimize', array('--force' => true));
Artisan::call('migrate', array('--force' => true));
Artisan::call('db:seed', array('--force' => true, '--class' => 'PaymentLibrariesSeeder'));
Artisan::call('db:seed', array('--force' => true, '--class' => 'FontsSeeder'));
foreach ([
'PaymentLibraries',
'Fonts',
'Banks',
'InvoiceStatus'
] as $seeder) {
Artisan::call('db:seed', array('--force' => true, '--class' => "{$seeder}Seeder"));
}
Artisan::call('optimize', array('--force' => true));
Event::fire(new UserSettingsChanged());
Session::flash('message', trans('texts.processed_updates'));
} catch (Exception $e) {
@ -271,4 +279,36 @@ class AppController extends BaseController
return RESULT_SUCCESS;
}
public function stats()
{
if (Input::get('password') != env('RESELLER_PASSWORD')) {
sleep(3);
return '';
}
if (Utils::getResllerType() == RESELLER_REVENUE_SHARE) {
$payments = DB::table('accounts')
->leftJoin('payments', 'payments.account_id', '=', 'accounts.id')
->leftJoin('clients', 'clients.id', '=', 'payments.client_id')
->where('accounts.account_key', '=', NINJA_ACCOUNT_KEY)
->where('payments.is_deleted', '=', false)
->get([
'clients.public_id as client_id',
'payments.public_id as payment_id',
'payments.payment_date',
'payments.amount'
]);
} else {
$payments = DB::table('accounts')
->leftJoin('payments', 'payments.account_id', '=', 'accounts.id')
->leftJoin('clients', 'clients.id', '=', 'payments.client_id')
->where('accounts.account_key', '=', NINJA_ACCOUNT_KEY)
->where('payments.is_deleted', '=', false)
->groupBy('clients.id')
->count();
}
return json_encode($payments);
}
}

View File

@ -1,6 +1,5 @@
<?php namespace App\Http\Controllers;
use Crypt;
use Cache;
use Auth;
use Datatable;
@ -11,23 +10,27 @@ use Session;
use View;
use Validator;
use stdClass;
use Crypt;
use URL;
use Utils;
use App\Models\Gateway;
use App\Models\Account;
use App\Models\BankAccount;
use App\Ninja\Repositories\AccountRepository;
use App\Ninja\Repositories\BankAccountRepository;
use App\Services\BankAccountService;
use App\Http\Requests\CreateBankAccountRequest;
class BankAccountController extends BaseController
{
protected $bankAccountService;
protected $bankAccountRepo;
public function __construct(BankAccountService $bankAccountService)
public function __construct(BankAccountService $bankAccountService, BankAccountRepository $bankAccountRepo)
{
parent::__construct();
$this->bankAccountService = $bankAccountService;
$this->bankAccountRepo = $bankAccountRepo;
}
public function index()
@ -43,11 +46,8 @@ class BankAccountController extends BaseController
public function edit($publicId)
{
$bankAccount = BankAccount::scope($publicId)->firstOrFail();
$bankAccount->username = str_repeat('*', 16);
$data = [
'url' => 'bank_accounts/' . $publicId,
'method' => 'PUT',
'title' => trans('texts.edit_bank_account'),
'banks' => Cache::get('banks'),
'bankAccount' => $bankAccount,
@ -61,11 +61,6 @@ class BankAccountController extends BaseController
return $this->save($publicId);
}
public function store()
{
return $this->save();
}
/**
* Displays the form for account creation
*
@ -73,9 +68,6 @@ class BankAccountController extends BaseController
public function create()
{
$data = [
'url' => 'bank_accounts',
'method' => 'POST',
'title' => trans('texts.add_bank_account'),
'banks' => Cache::get('banks'),
'bankAccount' => null,
];
@ -94,59 +86,40 @@ class BankAccountController extends BaseController
return Redirect::to('settings/' . ACCOUNT_BANKS);
}
/**
* Stores new account
*
*/
public function save($bankAccountPublicId = false)
public function validateAccount()
{
$account = Auth::user()->account;
$publicId = Input::get('public_id');
$username = trim(Input::get('bank_username'));
$password = trim(Input::get('bank_password'));
if ($publicId) {
$bankAccount = BankAccount::scope($publicId)->firstOrFail();
if ($username != $bankAccount->username) {
// TODO update username
}
$username = Crypt::decrypt($username);
$bankId = $bankAccount->bank_id;
} else {
$bankId = Input::get('bank_id');
$username = Input::get('bank_username');
$rules = [
'bank_id' => $bankAccountPublicId ? '' : 'required',
'bank_username' => 'required',
];
$validator = Validator::make(Input::all(), $rules);
if ($validator->fails()) {
return Redirect::to('bank_accounts/create')
->withErrors($validator)
->withInput();
} else {
if ($bankAccountPublicId) {
$bankAccount = BankAccount::scope($bankAccountPublicId)->firstOrFail();
} else {
$bankAccount = BankAccount::createNew();
$bankAccount->bank_id = $bankId;
}
if ($username != str_repeat('*', strlen($username))) {
$bankAccount->username = Crypt::encrypt(trim($username));
return json_encode($this->bankAccountService->loadBankAccounts($bankId, $username, $password, $publicId));
}
if ($bankAccountPublicId) {
$bankAccount->save();
$message = trans('texts.updated_bank_account');
} else {
$account->bank_accounts()->save($bankAccount);
$message = trans('texts.created_bank_account');
}
Session::flash('message', $message);
return Redirect::to("bank_accounts/{$bankAccount->public_id}/edit");
}
}
public function test()
public function store(CreateBankAccountRequest $request)
{
$bankAccount = $this->bankAccountRepo->save(Input::all());
$bankId = Input::get('bank_id');
$username = Input::get('bank_username');
$password = Input::get('bank_password');
$username = trim(Input::get('bank_username'));
$password = trim(Input::get('bank_password'));
return json_encode($this->bankAccountService->loadBankAccounts($bankId, $username, $password, false));
return json_encode($this->bankAccountService->loadBankAccounts($bankId, $username, $password, true));
}
public function importExpenses($bankId)
{
return $this->bankAccountService->importExpenses($bankId, Input::all());
}
}

View File

@ -107,6 +107,17 @@ class BaseAPIController extends Controller
return Response::make($response, 200, $headers);
}
protected function errorResponse($response)
{
$error['error'] = $response;
$error = json_encode($error, JSON_PRETTY_PRINT);
$headers = Utils::getApiHeaders();
return Response::make($error, 400, $headers);
}
protected function getIncluded()
{
$data = ['user'];

View File

@ -9,16 +9,20 @@ 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)
public function __construct(ClientRepository $clientRepo, ClientService $clientService)
{
parent::__construct();
$this->clientRepo = $clientRepo;
$this->clientService = $clientService;
}
public function ping()
@ -48,11 +52,22 @@ class ClientApiController extends BaseAPIController
{
$clients = Client::scope()
->with($this->getIncluded())
->orderBy('created_at', 'desc')
->paginate();
->orderBy('created_at', 'desc')->withTrashed();
// Filter by email
if (Input::has('email')) {
$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()->paginate();
$paginator = Client::scope()->withTrashed()->paginate();
$data = $this->createCollection($clients, $transformer, ENTITY_CLIENT, $paginator);
@ -93,4 +108,95 @@ class ClientApiController extends BaseAPIController
return $this->response($data);
}
/**
* @SWG\Put(
* path="/clients/{client_id}",
* tags={"client"},
* summary="Update a client",
* @SWG\Parameter(
* in="body",
* name="body",
* @SWG\Schema(ref="#/definitions/Client")
* ),
* @SWG\Response(
* response=200,
* description="Update client",
* @SWG\Schema(type="object", @SWG\Items(ref="#/definitions/Client"))
* ),
* @SWG\Response(
* response="default",
* description="an ""unexpected"" error"
* )
* )
*/
public function update(UpdateClientRequest $request, $publicId)
{
if ($request->action == ACTION_ARCHIVE) {
$client = Client::scope($publicId)->firstOrFail();
$this->clientRepo->archive($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 = Client::scope($publicId)
->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);
}
/**
* @SWG\Delete(
* path="/clients/{client_id}",
* tags={"client"},
* summary="Delete a client",
* @SWG\Parameter(
* in="body",
* name="body",
* @SWG\Schema(ref="#/definitions/Client")
* ),
* @SWG\Response(
* response=200,
* description="Delete client",
* @SWG\Schema(type="object", @SWG\Items(ref="#/definitions/Client"))
* ),
* @SWG\Response(
* response="default",
* description="an ""unexpected"" error"
* )
* )
*/
public function destroy($publicId)
{
$client = Client::scope($publicId)->withTrashed()->first();
$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);
}
}

View File

@ -112,7 +112,7 @@ class DashboardController extends BaseController
->where('payments.account_id', '=', Auth::user()->account_id)
->where('payments.is_deleted', '=', false)
->where('invoices.is_deleted', '=', false)
->where('clients.deleted_at', '=', null)
->where('clients.is_deleted', '=', false)
->where('contacts.deleted_at', '=', null)
->where('contacts.is_primary', '=', true)
->select(['payments.payment_date', 'payments.amount', 'invoices.public_id', 'invoices.invoice_number', 'clients.name as client_name', 'contacts.email', 'contacts.first_name', 'contacts.last_name', 'clients.currency_id', 'clients.public_id as client_public_id'])

View File

@ -48,6 +48,7 @@ class ExpenseController extends BaseController
'columns' => Utils::trans([
'checkbox',
'vendor',
'client',
'expense_date',
'amount',
'public_notes',
@ -59,7 +60,7 @@ class ExpenseController extends BaseController
public function getDatatable($expensePublicId = null)
{
return $this->expenseService->getDatatable($expensePublicId, Input::get('sSearch'));
return $this->expenseService->getDatatable(Input::get('sSearch'));
}
public function getDatatableVendor($vendorPublicId = null)
@ -152,7 +153,7 @@ class ExpenseController extends BaseController
*/
public function update(UpdateExpenseRequest $request)
{
$expense = $this->expenseRepo->save($request->input());
$expense = $this->expenseService->save($request->input());
Session::flash('message', trans('texts.updated_expense'));
@ -166,7 +167,7 @@ class ExpenseController extends BaseController
public function store(CreateExpenseRequest $request)
{
$expense = $this->expenseRepo->save($request->input());
$expense = $this->expenseService->save($request->input());
Session::flash('message', trans('texts.created_expense'));
@ -181,22 +182,30 @@ class ExpenseController extends BaseController
switch($action)
{
case 'invoice':
$expenses = Expense::scope($ids)->get();
$expenses = Expense::scope($ids)->with('client')->get();
$clientPublicId = null;
$currencyId = null;
$data = [];
// Validate that either all expenses do not have a client or if there is a client, it is the same client
foreach ($expenses as $expense)
{
if ($expense->client_id) {
if ($expense->client) {
if (!$clientPublicId) {
$clientPublicId = $expense->client_id;
} elseif ($clientPublicId != $expense->client_id) {
$clientPublicId = $expense->client->public_id;
} elseif ($clientPublicId != $expense->client->public_id) {
Session::flash('error', trans('texts.expense_error_multiple_clients'));
return Redirect::to('expenses');
}
}
if (!$currencyId) {
$currencyId = $expense->invoice_currency_id;
} elseif ($currencyId != $expense->invoice_currency_id && $expense->invoice_currency_id) {
Session::flash('error', trans('texts.expense_error_multiple_currencies'));
return Redirect::to('expenses');
}
if ($expense->invoice_id) {
Session::flash('error', trans('texts.expense_error_invoiced'));
return Redirect::to('expenses');
@ -211,7 +220,9 @@ class ExpenseController extends BaseController
];
}
return Redirect::to("invoices/create/{$clientPublicId}")->with('expenses', $data);
return Redirect::to("invoices/create/{$clientPublicId}")
->with('expenseCurrencyId', $currencyId)
->with('expenses', $data);
break;
default:

View File

@ -185,6 +185,10 @@ class InvoiceApiController extends BaseAPIController
'partial' => 0
];
if (!isset($data['invoice_status_id']) || $data['invoice_status_id'] == 0) {
$data['invoice_status_id'] = INVOICE_STATUS_DRAFT;
}
if (!isset($data['invoice_date'])) {
$fields['invoice_date_sql'] = date_create()->format('Y-m-d');
}
@ -250,20 +254,14 @@ class InvoiceApiController extends BaseAPIController
$data = Input::all();
$error = null;
if (!isset($data['id'])) {
$error = trans('validation.required', ['attribute' => 'id']);
} else {
$invoice = Invoice::scope($data['id'])->first();
if (!$invoice) {
$error = trans('validation.not_in', ['attribute' => 'id']);
} else {
$invoice = Invoice::scope($data['id'])->firstOrFail();
$this->mailer->sendInvoice($invoice);
}
}
if($error) {
$response = json_encode($error, JSON_PRETTY_PRINT);
} else {
$response['error'] = "There was an error sending the invoice";
}
else {
$response = json_encode(RESULT_SUCCESS, JSON_PRETTY_PRINT);
}
@ -271,6 +269,7 @@ class InvoiceApiController extends BaseAPIController
return Response::make($response, $error ? 400 : 200, $headers);
}
/**
* @SWG\Put(
* path="/invoices",
@ -297,11 +296,25 @@ class InvoiceApiController extends BaseAPIController
if ($request->action == ACTION_ARCHIVE) {
$invoice = Invoice::scope($publicId)->firstOrFail();
$this->invoiceRepo->archive($invoice);
/*
$response = json_encode(RESULT_SUCCESS, JSON_PRETTY_PRINT);
$headers = Utils::getApiHeaders();
return Response::make($response, 200, $headers);
*/
$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();
$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');

View File

@ -296,7 +296,7 @@ class InvoiceController extends BaseController
return [
'data' => Input::old('data'),
'account' => Auth::user()->account->load('country'),
'products' => Product::scope()->with('default_tax_rate')->orderBy('id')->get(),
'products' => Product::scope()->with('default_tax_rate')->orderBy('product_key')->get(),
'taxRates' => TaxRate::scope()->orderBy('name')->get(),
'currencies' => Cache::get('currencies'),
'languages' => Cache::get('languages'),
@ -320,6 +320,7 @@ class InvoiceController extends BaseController
'invoiceLabels' => Auth::user()->account->getInvoiceLabels(),
'tasks' => Session::get('tasks') ? json_encode(Session::get('tasks')) : null,
'expenses' => Session::get('expenses') ? json_encode(Session::get('expenses')) : null,
'expenseCurrencyId' => Session::get('expenseCurrencyId') ?: null,
];
}

View File

@ -1,6 +1,8 @@
<?php namespace App\Http\Controllers;
use App\Ninja\Mailers\ContactMailer;
use Auth;
use Illuminate\Http\Request;
use Input;
use Utils;
use Response;
@ -9,16 +11,19 @@ use App\Models\Invoice;
use App\Ninja\Repositories\PaymentRepository;
use App\Http\Controllers\BaseAPIController;
use App\Ninja\Transformers\PaymentTransformer;
use App\Ninja\Transformers\InvoiceTransformer;
class PaymentApiController extends BaseAPIController
{
protected $paymentRepo;
public function __construct(PaymentRepository $paymentRepo)
public function __construct(PaymentRepository $paymentRepo, ContactMailer $contactMailer)
{
parent::__construct();
$this->paymentRepo = $paymentRepo;
$this->contactMailer = $contactMailer;
}
/**
@ -41,7 +46,7 @@ class PaymentApiController extends BaseAPIController
{
$paginator = Payment::scope();
$payments = Payment::scope()
->with('client.contacts', 'invitation', 'user', 'invoice');
->with('client.contacts', 'invitation', 'user', 'invoice')->withTrashed();
if ($clientPublicId = Input::get('client_id')) {
$filter = function($query) use ($clientPublicId) {
@ -60,6 +65,63 @@ class PaymentApiController extends BaseAPIController
return $this->response($data);
}
/**
* @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);
$invoice = Invoice::scope($data['invoice_id'])->with('client')->with(['payments' => function($query) {
$query->withTrashed();
}])->first();
$transformer = new InvoiceTransformer(\Auth::user()->account, Input::get('serializer'));
$data = $this->createItem($invoice, $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 InvoiceTransformer(\Auth::user()->account, Input::get('serializer'));
$data = $this->createItem($invoice, $transformer, 'invoice');
return $this->response($data);
}
/**
* @SWG\Post(
* path="/payments",
@ -107,12 +169,57 @@ class PaymentApiController extends BaseAPIController
return $error;
}
$payment = $this->paymentRepo->save($data);
$payment = Payment::scope($payment->public_id)->with('client', 'contact', 'user', 'invoice')->first();
$transformer = new PaymentTransformer(Auth::user()->account, Input::get('serializer'));
$data = $this->createItem($payment, $transformer, 'payment');
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 InvoiceTransformer(\Auth::user()->account, Input::get('serializer'));
$data = $this->createItem($invoice, $transformer, 'invoice');
return $this->response($data);
}
/**
* @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)
{
$payment = Payment::scope($publicId)->withTrashed()->first();
$invoiceId = $payment->invoice->public_id;
$this->paymentRepo->delete($payment);
$invoice = Invoice::scope($invoiceId)->with('client', 'invoice_items', 'invitations')->with(['payments' => function($query) {
$query->withTrashed();
}])->first();
$transformer = new InvoiceTransformer(\Auth::user()->account, Input::get('serializer'));
$data = $this->createItem($invoice, $transformer, 'invoice');
return $this->response($data);
}

View File

@ -0,0 +1,103 @@
<?php namespace App\Http\Controllers;
use App\Ninja\Repositories\ProductRepository;
use App\Ninja\Transformers\ProductTransformer;
use Auth;
use Str;
use DB;
use Datatable;
use Utils;
use URL;
use View;
use Input;
use Session;
use Redirect;
use App\Models\Product;
use App\Models\TaxRate;
use App\Services\ProductService;
class ProductApiController extends BaseAPIController
{
protected $productService;
protected $productRepo;
public function __construct(ProductService $productService, ProductRepository $productRepo)
{
parent::__construct();
$this->productService = $productService;
$this->productRepo = $productRepo;
}
public function index()
{
$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);
}
public function getDatatable()
{
return $this->productService->getDatatable(Auth::user()->account_id);
}
public function store()
{
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);
}
else
return $this->save($publicId);
}
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);
}
}

View File

@ -145,6 +145,7 @@ class TaskController extends BaseController
'actions' => $actions,
'timezone' => Auth::user()->account->timezone ? Auth::user()->account->timezone->name : DEFAULT_TIMEZONE,
'datetimeFormat' => Auth::user()->account->getMomentDateTimeFormat(),
//'entityStatus' => $task->present()->status,
];
$data = array_merge($data, self::getViewModel());

View File

@ -0,0 +1,68 @@
<?php namespace App\Http\Controllers;
use App\Services\TaxRateService;
use App\Ninja\Repositories\TaxRateRepository;
use App\Ninja\Transformers\TaxRateTransformer;
use Auth;
use App\Models\TaxRate;
use App\Http\Requests\CreateTaxRateRequest;
use App\Http\Requests\UpdateTaxRateRequest;
class TaxRateApiController extends BaseAPIController
{
protected $taxRateService;
protected $taxRateRepo;
public function __construct(TaxRateService $taxRateService, TaxRateRepository $taxRateRepo)
{
parent::__construct();
$this->taxRateService = $taxRateService;
$this->taxRateRepo = $taxRateRepo;
}
public function index()
{
$taxRates = TaxRate::scope()->withTrashed();
$taxRates = $taxRates->paginate();
$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);
}
public function store(CreateTaxRateRequest $request)
{
return $this->save($request);
}
public function update(UpdateTaxRateRequest $request, $taxRatePublicId)
{
$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);
}
}
private function save($request, $taxRate = false)
{
$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);
}
}

View File

@ -13,16 +13,22 @@ use Redirect;
use App\Models\TaxRate;
use App\Services\TaxRateService;
use App\Ninja\Repositories\TaxRateRepository;
use App\Http\Requests\CreateTaxRateRequest;
use App\Http\Requests\UpdateTaxRateRequest;
class TaxRateController extends BaseController
{
protected $taxRateService;
protected $taxRateRepo;
public function __construct(TaxRateService $taxRateService)
public function __construct(TaxRateService $taxRateService, TaxRateRepository $taxRateRepo)
{
parent::__construct();
$this->taxRateService = $taxRateService;
$this->taxRateRepo = $taxRateRepo;
}
public function index()
@ -59,34 +65,25 @@ class TaxRateController extends BaseController
return View::make('accounts.tax_rate', $data);
}
public function store()
public function store(CreateTaxRateRequest $request)
{
return $this->save();
}
public function update($publicId)
{
return $this->save($publicId);
}
private function save($publicId = false)
{
if ($publicId) {
$taxRate = TaxRate::scope($publicId)->firstOrFail();
} else {
$taxRate = TaxRate::createNew();
}
$taxRate->name = trim(Input::get('name'));
$taxRate->rate = Utils::parseFloat(Input::get('rate'));
$taxRate->save();
$message = $publicId ? trans('texts.updated_tax_rate') : trans('texts.created_tax_rate');
Session::flash('message', $message);
$this->taxRateRepo->save($request->input());
Session::flash('message', trans('texts.created_tax_rate'));
return Redirect::to('settings/' . ACCOUNT_TAX_RATES);
}
public function update(UpdateTaxRateRequest $request, $publicId)
{
$taxRate = TaxRate::scope($publicId)->firstOrFail();
$this->taxRateRepo->save($request->input(), $taxRate);
Session::flash('message', trans('texts.updated_tax_rate'));
return Redirect::to('settings/' . ACCOUNT_TAX_RATES);
}
public function bulk()
{
$action = Input::get('bulk_action');

View File

@ -0,0 +1,76 @@
<?php namespace App\Http\Controllers;
use App\Services\UserService;
use App\Ninja\Repositories\UserRepository;
use App\Ninja\Transformers\UserTransformer;
use Auth;
use App\Models\User;
use App\Http\Requests\CreateUserRequest;
use App\Http\Requests\UpdateUserRequest;
class UserApiController extends BaseAPIController
{
protected $userService;
protected $userRepo;
public function __construct(UserService $userService, UserRepository $userRepo)
{
parent::__construct();
$this->userService = $userService;
$this->userRepo = $userRepo;
}
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);
}
/*
public function store(CreateUserRequest $request)
{
return $this->save($request);
}
*/
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) {
$this->userRepo->archive($user);
$transformer = new UserTransformer(Auth::user()->account, $request->serializer);
$data = $this->createItem($user, $transformer, 'users');
return $this->response($data);
} else {
return $this->save($request, $user);
}
}
private function save($request, $user = false)
{
$user = $this->userRepo->save($request->input(), $user);
$transformer = new UserTransformer(\Auth::user()->account, $request->serializer);
$data = $this->createItem($user, $transformer, 'users');
return $this->response($data);
}
}

View File

@ -55,7 +55,8 @@ class VendorController extends BaseController
'columns' => Utils::trans([
'checkbox',
'vendor',
'contact',
'city',
'phone',
'email',
'date_created',
''

View File

@ -7,7 +7,9 @@ class VerifyCsrfToken extends BaseVerifier {
private $openRoutes = [
'signup/register',
'api/v1/*',
'api/v1/login',
'api/v1/clients/*',
'api/v1/clients',
'api/v1/invoices/*',
'api/v1/invoices',
@ -18,6 +20,7 @@ class VerifyCsrfToken extends BaseVerifier {
'api/v1/hooks',
'hook/email_opened',
'hook/email_bounced',
'reseller_stats',
];
/**

View File

@ -0,0 +1,32 @@
<?php namespace app\Http\Requests;
use App\Http\Requests\Request;
use Illuminate\Validation\Factory;
class CreateBankAccountRequest extends Request
{
// Expenses
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'bank_id' => 'required',
'bank_username' => 'required',
'bank_password' => 'required',
];
}
}

View File

@ -0,0 +1,31 @@
<?php namespace app\Http\Requests;
use App\Http\Requests\Request;
use Illuminate\Validation\Factory;
class CreateTaxRateRequest extends Request
{
// Expenses
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'name' => 'required',
'rate' => 'required',
];
}
}

View File

@ -23,10 +23,11 @@ class CreateVendorRequest extends Request
public function rules()
{
return [
'vendorcontacts' => 'valid_contacts',
'name' => 'required',
];
}
/*
public function validator($factory)
{
// support submiting the form with a single contact record
@ -41,4 +42,5 @@ class CreateVendorRequest extends Request
$this->input(), $this->container->call([$this, 'rules']), $this->messages()
);
}
*/
}

View File

@ -0,0 +1,31 @@
<?php namespace app\Http\Requests;
use App\Http\Requests\Request;
use Illuminate\Validation\Factory;
class UpdateAccountRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'name' => 'required',
'logo' => 'sometimes|max:'.MAX_LOGO_FILE_SIZE.'|mimes:jpeg,gif,png',
];
}
}
//

View File

@ -0,0 +1,31 @@
<?php namespace app\Http\Requests;
use App\Http\Requests\Request;
use Illuminate\Validation\Factory;
class UpdateTaxRateRequest extends Request
{
// Expenses
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'name' => 'required',
'rate' => 'required',
];
}
}

View File

@ -0,0 +1,33 @@
<?php namespace app\Http\Requests;
use Auth;
use App\Http\Requests\Request;
use Illuminate\Validation\Factory;
class UpdateUserRequest extends Request
{
// Expenses
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'email' => 'email|required|unique:users,email,' . Auth::user()->id . ',id',
'first_name' => 'required',
'last_name' => 'required',
];
}
}

View File

@ -23,7 +23,7 @@ class UpdateVendorRequest extends Request
public function rules()
{
return [
'vendor_contacts' => 'valid_contacts',
'name' => 'required',
];
}
}

View File

@ -1,5 +1,6 @@
<?php
/*
|--------------------------------------------------------------------------
| Application Routes
@ -82,6 +83,10 @@ if (Utils::isNinja()) {
Route::get('/demo', 'AccountController@demo');
}
if (Utils::isReseller()) {
Route::post('/reseller_stats', 'AppController@stats');
}
Route::group(['middleware' => 'auth'], function() {
Route::get('dashboard', 'DashboardController@index');
Route::get('view_archive/{entity_type}/{visible}', 'AccountController@setTrashVisible');
@ -116,6 +121,7 @@ Route::group(['middleware' => 'auth'], function() {
Route::post('settings/charts_and_reports', 'ReportController@showReports');
Route::post('settings/cancel_account', 'AccountController@cancelAccount');
Route::post('settings/company_details', 'AccountController@updateDetails');
Route::get('settings/{section?}', 'AccountController@showSection');
Route::post('settings/{section?}', 'AccountController@doSection');
@ -140,7 +146,8 @@ Route::group(['middleware' => 'auth'], function() {
Route::resource('bank_accounts', 'BankAccountController');
Route::get('api/bank_accounts', array('as'=>'api.bank_accounts', 'uses'=>'BankAccountController@getDatatable'));
Route::post('bank_accounts/bulk', 'BankAccountController@bulk');
Route::post('bank_accounts/test', 'BankAccountController@test');
Route::post('bank_accounts/validate', 'BankAccountController@validateAccount');
Route::post('bank_accounts/import_expenses/{bank_id}', 'BankAccountController@importExpenses');
Route::resource('clients', 'ClientController');
Route::get('api/clients', array('as'=>'api.clients', 'uses'=>'ClientController@getDatatable'));
@ -209,10 +216,11 @@ Route::group(['middleware' => 'auth'], function() {
// Route groups for API
Route::group(['middleware' => 'api', 'prefix' => 'api/v1'], function()
{
Route::resource('ping', 'ClientApiController@ping');
Route::get('ping', 'ClientApiController@ping');
Route::post('login', 'AccountApiController@login');
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');
@ -225,6 +233,9 @@ Route::group(['middleware' => 'api', 'prefix' => 'api/v1'], function()
Route::post('hooks', 'IntegrationController@subscribe');
Route::post('email_invoice', 'InvoiceApiController@emailInvoice');
Route::get('user_accounts', 'AccountApiController@getUserAccounts');
Route::resource('products', 'ProductApiController');
Route::resource('tax_rates', 'TaxRateApiController');
Route::resource('users', 'UserApiController');
// Vendor
Route::resource('vendors', 'VendorApiController');
@ -283,7 +294,6 @@ if (!defined('CONTACT_EMAIL')) {
define('ENTITY_QUOTE', 'quote');
define('ENTITY_TASK', 'task');
define('ENTITY_ACCOUNT_GATEWAY', 'account_gateway');
define('ENTITY_BANK_ACCOUNT', 'bank_account');
define('ENTITY_USER', 'user');
define('ENTITY_TOKEN', 'token');
define('ENTITY_TAX_RATE', 'tax_rate');
@ -294,6 +304,8 @@ if (!defined('CONTACT_EMAIL')) {
define('ENTITY_EXPENSE', 'expense');
define('ENTITY_PAYMENT_TERM', 'payment_term');
define('ENTITY_EXPENSE_ACTIVITY', 'expense_activity');
define('ENTITY_BANK_ACCOUNT', 'bank_account');
define('ENTITY_BANK_SUBACCOUNT', 'bank_subaccount');
define('PERSON_CONTACT', 'contact');
define('PERSON_USER', 'user');
@ -309,6 +321,7 @@ if (!defined('CONTACT_EMAIL')) {
define('ACCOUNT_IMPORT_EXPORT', 'import_export');
define('ACCOUNT_PAYMENTS', 'online_payments');
define('ACCOUNT_BANKS', 'bank_accounts');
define('ACCOUNT_IMPORT_EXPENSES', 'import_expenses');
define('ACCOUNT_MAP', 'import_map');
define('ACCOUNT_EXPORT', 'export');
define('ACCOUNT_TAX_RATES', 'tax_rates');
@ -329,6 +342,7 @@ if (!defined('CONTACT_EMAIL')) {
define('ACTION_RESTORE', 'restore');
define('ACTION_ARCHIVE', 'archive');
define('ACTION_CONVERT', 'convert');
define('ACTION_DELETE', 'delete');
define('ACTIVITY_TYPE_CREATE_CLIENT', 1);
@ -512,6 +526,7 @@ if (!defined('CONTACT_EMAIL')) {
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('COUNT_FREE_DESIGNS', 4);
define('COUNT_FREE_DESIGNS_SELF_HOST', 5); // include the custom design
@ -577,6 +592,9 @@ if (!defined('CONTACT_EMAIL')) {
define('BANK_LIBRARY_OFX', 1);
define('RESELLER_REVENUE_SHARE', 'A');
define('RESELLER_LIMITED_USERS', 'B');
$creditCards = [
1 => ['card' => 'images/credit_cards/Test-Visa-Icon.png', 'text' => 'Visa'],
2 => ['card' => 'images/credit_cards/Test-MasterCard-Icon.png', 'text' => 'Master Card'],

View File

@ -24,8 +24,9 @@ class OFX
curl_setopt($c, CURLOPT_HTTPHEADER, array('Content-Type: application/x-ofx'));
curl_setopt($c, CURLOPT_POSTFIELDS, $this->request);
curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
//curl_setopt($c, CURLOPT_SSL_VERIFYPEER, false);
$this->response = curl_exec($c);
//print_r($this->response);
//\Log::info(print_r($this->response, true));
curl_close($c);
$tmp = explode('<OFX>', $this->response);
$this->responseHeader = $tmp[0];

View File

@ -58,6 +58,10 @@ class Utils
public static function isNinjaProd()
{
if (Utils::isReseller()) {
return true;
}
return isset($_ENV['NINJA_PROD']) && $_ENV['NINJA_PROD'] == 'true';
}
@ -71,6 +75,16 @@ class Utils
return Utils::isNinjaProd() || (isset($_ENV['REQUIRE_HTTPS']) && $_ENV['REQUIRE_HTTPS'] == 'true');
}
public static function isReseller()
{
return Utils::getResllerType() ? true : false;
}
public static function getResllerType()
{
return isset($_ENV['RESELLER_TYPE']) ? $_ENV['RESELLER_TYPE'] : false;
}
public static function isOAuthEnabled()
{
$providers = [
@ -210,7 +224,7 @@ class Utils
$count = Session::get('error_count', 0);
Session::put('error_count', ++$count);
if ($count > 100) {
if ($count > 200) {
return 'logged';
}

View File

@ -21,6 +21,30 @@ class Account extends Eloquent
protected $dates = ['deleted_at'];
protected $hidden = ['ip'];
protected $fillable = [
'name',
'id_number',
'vat_number',
'work_email',
'website',
'work_phone',
'address1',
'address2',
'city',
'state',
'postal_code',
'country_id',
'size_id',
'industry_id',
'email_footer',
'timezone_id',
'date_format_id',
'datetime_format_id',
'currency_id',
'language_id',
'military_time',
];
public static $basicSettings = [
ACCOUNT_COMPANY_DETAILS,
ACCOUNT_USER_DETAILS,
@ -140,6 +164,24 @@ class Account extends Eloquent
return $this->belongsTo('App\Models\TaxRate');
}
public function setIndustryIdAttribute($value)
{
$this->attributes['industry_id'] = $value ?: null;
}
public function setCountryIdAttribute($value)
{
$this->attributes['country_id'] = $value ?: null;
}
public function setSizeIdAttribute($value)
{
$this->attributes['size_id'] = $value ?: null;
}
public function isGatewayConfigured($gatewayId = 0)
{
$this->load('account_gateways');
@ -248,6 +290,8 @@ class Account extends Eloquent
$countryId = false;
}
$hideSymbol = $this->show_currency_code || $hideSymbol;
return Utils::formatMoney($amount, $currencyId, $countryId, $hideSymbol);
}
@ -604,6 +648,7 @@ class Account extends Eloquent
'quote_number',
'total',
'invoice_issued_to',
'quote_issued_to',
//'date',
'rate',
'hours',

View File

@ -19,5 +19,9 @@ class BankAccount extends EntityModel
return $this->belongsTo('App\Models\Bank');
}
public function bank_subaccounts()
{
return $this->hasMany('App\Models\BankSubaccount');
}
}

View File

@ -0,0 +1,23 @@
<?php namespace App\Models;
use Crypt;
use App\Models\Bank;
use Illuminate\Database\Eloquent\SoftDeletes;
class BankSubaccount extends EntityModel
{
use SoftDeletes;
protected $dates = ['deleted_at'];
public function getEntityType()
{
return ENTITY_BANK_SUBACCOUNT;
}
public function bank_account()
{
return $this->belongsTo('App\Models\BankAccount');
}
}

View File

@ -136,6 +136,12 @@ class Client extends EntityModel
return $this->belongsTo('App\Models\Industry');
}
public function credits()
{
return $this->hasMany('App\Models\Credit');
}
public function addContact($data, $isPrimary = false)
{
$publicId = isset($data['public_id']) ? $data['public_id'] : false;

View File

@ -18,12 +18,15 @@ class Expense extends EntityModel
protected $fillable = [
'client_id',
'vendor_id',
'currency_id',
'expense_currency_id',
'invoice_currency_id',
'amount',
'foreign_amount',
'exchange_rate',
'private_notes',
'public_notes',
'bank_id',
'transaction_id',
];
public function account()
{

View File

@ -429,6 +429,8 @@ class Invoice extends EntityModel implements BalanceAffecting
'secondary_color',
'hide_quantity',
'hide_paid_to_date',
'all_pages_header',
'all_pages_footer',
'custom_invoice_label1',
'custom_invoice_label2',
'pdf_email_attachment',

View File

@ -7,6 +7,11 @@ class TaxRate extends EntityModel
use SoftDeletes;
protected $dates = ['deleted_at'];
protected $fillable = [
'name',
'rate'
];
public function getEntityType()
{
return ENTITY_TAX_RATE;

View File

@ -29,7 +29,13 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
*
* @var array
*/
protected $fillable = ['first_name', 'last_name', 'email', 'password'];
protected $fillable = [
'first_name',
'last_name',
'email',
'password',
'phone',
];
/**
* The attributes excluded from the model's JSON form.
@ -51,6 +57,11 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
return $this->belongsTo('App\Models\Theme');
}
public function setEmailAttribute($value)
{
$this->attributes['email'] = $this->attributes['username'] = $value;
}
public function getName()
{
return $this->getDisplayName();

View File

@ -29,6 +29,7 @@ class Vendor extends EntityModel
'private_notes',
'currency_id',
'website',
'transaction_name',
];
public static $fieldName = 'name';

View File

@ -19,13 +19,14 @@ class ContactMailer extends Mailer
public static $variableFields = [
'footer',
'account',
'dueDate',
'invoiceDate',
'client',
'amount',
'contact',
'firstName',
'invoice',
'quote',
'dueDate',
'viewLink',
'viewButton',
'paymentLink',
@ -234,13 +235,14 @@ class ContactMailer extends Mailer
'$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(),
'$dueDate' => $account->formatDate($invoice->due_date),
'$viewLink' => $invitation->getLink(),
'$viewButton' => HTML::emailViewButton($invitation->getLink(), $invoice->getEntityType()),
'$paymentLink' => $invitation->getLink('payment'),

View File

@ -40,8 +40,8 @@ class UserMailer extends Mailer
return;
}
$entityType = $notificationType == 'approved' ? ENTITY_QUOTE : ENTITY_INVOICE;
$view = "{$entityType}_{$notificationType}";
$entityType = $invoice->getEntityType();
$view = ($notificationType == 'approved' ? ENTITY_QUOTE : ENTITY_INVOICE) . "_{$notificationType}";
$account = $user->account;
$client = $invoice->client;

View File

@ -9,4 +9,23 @@ class ClientPresenter extends Presenter {
{
return $this->entity->country ? $this->entity->country->name : '';
}
public function status()
{
$class = $text = '';
if ($this->entity->is_deleted) {
$class = 'danger';
$text = trans('texts.deleted');
} elseif ($this->entity->trashed()) {
$class = 'warning';
$text = trans('texts.archived');
} else {
$class = 'success';
$text = trans('texts.active');
}
return "<span class=\"label label-{$class}\">{$text}</span>";
}
}

View File

@ -36,4 +36,23 @@ class TaskPresenter extends Presenter {
return implode("\n", $times);
}
public function status()
{
$class = $text = '';
if ($this->entity->is_deleted) {
$class = 'danger';
$text = trans('texts.deleted');
} elseif ($this->entity->trashed()) {
$class = 'warning';
$text = trans('texts.archived');
} else {
$class = 'success';
$text = trans('texts.active');
}
return "<span class=\"label label-{$class}\">{$text}</span>";
}
}

View File

@ -516,4 +516,10 @@ class AccountRepository
return $userAccount ? $userAccount->id : false;
}
public function save($data, $account)
{
$account->fill($data);
$account->save();
}
}

View File

@ -1,9 +1,11 @@
<?php namespace App\Ninja\Repositories;
use DB;
use Crypt;
use Utils;
use Session;
use App\Models\BankAccount;
use App\Models\BankSubaccount;
use App\Ninja\Repositories\BaseRepository;
class BankAccountRepository extends BaseRepository
@ -19,6 +21,34 @@ class BankAccountRepository extends BaseRepository
->join('banks', 'banks.id', '=', 'bank_accounts.bank_id')
->where('bank_accounts.deleted_at', '=', null)
->where('bank_accounts.account_id', '=', $accountId)
->select('bank_accounts.public_id', 'banks.name as bank_name', 'bank_accounts.deleted_at', 'banks.bank_library_id');
->select(
'bank_accounts.public_id',
'banks.name as bank_name',
'bank_accounts.deleted_at',
'banks.bank_library_id'
);
}
public function save($input)
{
$bankAccount = BankAccount::createNew();
$bankAccount->bank_id = $input['bank_id'];
$bankAccount->username = Crypt::encrypt(trim($input['bank_username']));
$account = \Auth::user()->account;
$account->bank_accounts()->save($bankAccount);
foreach ($input['bank_accounts'] as $data) {
if ( ! isset($data['include']) || ! filter_var($data['include'], FILTER_VALIDATE_BOOLEAN)) {
continue;
}
$subaccount = BankSubaccount::createNew();
$subaccount->account_name = trim($data['account_name']);
$subaccount->account_number = trim($data['hashed_account_number']);
$bankAccount->bank_subaccounts()->save($subaccount);
}
return $bankAccount;
}
}

View File

@ -26,11 +26,12 @@ class ExpenseRepository extends BaseRepository
public function findVendor($vendorPublicId)
{
$vendorId = Vendor::getPrivateId($vendorPublicId);
$accountid = \Auth::user()->account_id;
$query = DB::table('expenses')
->join('accounts', 'accounts.id', '=', 'expenses.account_id')
->where('expenses.account_id', '=', $accountid)
->where('expenses.vendor_id', '=', $vendorPublicId)
->where('expenses.vendor_id', '=', $vendorId)
->select(
'expenses.id',
'expenses.expense_date',
@ -51,12 +52,20 @@ class ExpenseRepository extends BaseRepository
$query = DB::table('expenses')
->join('accounts', 'accounts.id', '=', 'expenses.account_id')
->leftjoin('clients', 'clients.id', '=', 'expenses.client_id')
->leftJoin('contacts', 'contacts.client_id', '=', 'clients.id')
->leftjoin('vendors', 'vendors.id', '=', 'expenses.vendor_id')
->leftJoin('invoices', 'invoices.id', '=', 'expenses.invoice_id')
->where('expenses.account_id', '=', $accountid)
->select('expenses.account_id',
->where('contacts.deleted_at', '=', null)
->where('vendors.deleted_at', '=', null)
->where('clients.deleted_at', '=', null)
->where(function ($query) {
$query->where('contacts.is_primary', '=', true)
->orWhere('contacts.is_primary', '=', null);
})
->select(
'expenses.account_id',
'expenses.amount',
'expenses.currency_id',
'expenses.deleted_at',
'expenses.exchange_rate',
'expenses.expense_date',
@ -68,11 +77,16 @@ class ExpenseRepository extends BaseRepository
'expenses.public_notes',
'expenses.should_be_invoiced',
'expenses.vendor_id',
'expenses.expense_currency_id',
'expenses.invoice_currency_id',
'invoices.public_id as invoice_public_id',
'vendors.name as vendor_name',
'vendors.public_id as vendor_public_id',
'accounts.country_id as account_country_id',
'accounts.currency_id as account_currency_id',
'clients.name as client_name',
'clients.public_id as client_public_id',
'contacts.first_name',
'contacts.email',
'contacts.last_name',
'clients.country_id as client_country_id'
);
@ -84,7 +98,9 @@ class ExpenseRepository extends BaseRepository
if ($filter) {
$query->where(function ($query) use ($filter) {
$query->where('expenses.public_notes', 'like', '%'.$filter.'%');
$query->where('expenses.public_notes', 'like', '%'.$filter.'%')
->orWhere('clients.name', 'like', '%'.$filter.'%')
->orWhere('vendors.name', 'like', '%'.$filter.'%');
});
}
@ -105,10 +121,20 @@ class ExpenseRepository extends BaseRepository
$expense->fill($input);
$expense->expense_date = Utils::toSqlDate($input['expense_date']);
if (isset($input['private_notes'])) {
$expense->private_notes = trim($input['private_notes']);
}
$expense->public_notes = trim($input['public_notes']);
$expense->should_be_invoiced = isset($input['should_be_invoiced']) || $expense->client_id ? true : false;
if ( ! $expense->expense_currency_id) {
$expense->expense_currency_id = \Auth::user()->account->getCurrencyId();
}
if ( ! $expense->invoice_currency_id) {
$expense->invoice_currency_id = \Auth::user()->account->getCurrencyId();
}
$rate = isset($input['exchange_rate']) ? Utils::parseFloat($input['exchange_rate']) : 1;
$expense->exchange_rate = round($rate, 4);
$expense->amount = round(Utils::parseFloat($input['amount']), 2);

View File

@ -244,6 +244,13 @@ class InvoiceRepository extends BaseRepository
$invoice->invoice_date = Utils::toSqlDate($data['invoice_date']);
}
if(isset($data['invoice_status_id'])) {
if($data['invoice_status_id'] == 0) {
$data['invoice_status_id'] = INVOICE_STATUS_DRAFT;
}
$invoice->invoice_status_id = $data['invoice_status_id'];
}
if ($invoice->is_recurring) {
if ($invoice->start_date && $invoice->start_date != Utils::toSqlDate($data['start_date'])) {
$invoice->last_sent_date = null;
@ -395,6 +402,7 @@ class InvoiceRepository extends BaseRepository
continue;
}
$task = false;
if (isset($item['task_public_id']) && $item['task_public_id']) {
$task = Task::scope($item['task_public_id'])->where('invoice_id', '=', null)->firstOrFail();
$task->invoice_id = $invoice->id;
@ -402,6 +410,7 @@ class InvoiceRepository extends BaseRepository
$task->save();
}
$expense = false;
if (isset($item['expense_public_id']) && $item['expense_public_id']) {
$expense = Expense::scope($item['expense_public_id'])->where('invoice_id', '=', null)->firstOrFail();
$expense->invoice_id = $invoice->id;
@ -417,11 +426,8 @@ class InvoiceRepository extends BaseRepository
$product = Product::createNew();
$product->product_key = trim($item['product_key']);
}
$product->notes = $invoice->has_tasks ? '' : $item['notes'];
$product->notes = $invoice->has_expenses ? '' : $item['notes'];
$product->cost = $item['cost'];
$product->notes = ($task || $expense) ? '' : $item['notes'];
$product->cost = $expense ? 0 : $item['cost'];
$product->save();
}
}
@ -667,6 +673,8 @@ class InvoiceRepository extends BaseRepository
$sql = implode(' OR ', $dates);
$invoices = Invoice::whereAccountId($account->id)
->where('balance', '>', 0)
->where('is_quote', '=', false)
->where('is_recurring', '=', false)
->whereRaw('('.$sql.')')
->get();

View File

@ -23,7 +23,23 @@ class TaskRepository
})
->where('contacts.deleted_at', '=', null)
->where('clients.deleted_at', '=', null)
->select('tasks.public_id', 'clients.name as client_name', 'clients.public_id as client_public_id', 'contacts.first_name', 'contacts.email', 'contacts.last_name', 'invoices.invoice_status_id', 'tasks.description', 'tasks.is_deleted', 'tasks.deleted_at', 'invoices.invoice_number', 'invoices.public_id as invoice_public_id', 'tasks.is_running', 'tasks.time_log', 'tasks.created_at');
->select(
'tasks.public_id',
'clients.name as client_name',
'clients.public_id as client_public_id',
'contacts.first_name',
'contacts.email',
'contacts.last_name',
'invoices.invoice_status_id',
'tasks.description',
'tasks.is_deleted',
'tasks.deleted_at',
'invoices.invoice_number',
'invoices.public_id as invoice_public_id',
'tasks.is_running',
'tasks.time_log',
'tasks.created_at'
);
if ($clientPublicId) {
$query->where('clients.public_id', '=', $clientPublicId);

View File

@ -20,6 +20,22 @@ 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)
{
if ( ! $taxRate) {
if (isset($data['public_id'])) {
$taxRate = TaxRate::scope($data['public_id'])->firstOrFail();
} else {
$taxRate = TaxRate::createNew();
}
}
$taxRate->fill($data);
$taxRate->save();
return $taxRate;
}
/*
public function save($taxRates)
{

View File

@ -26,4 +26,13 @@ class UserRepository extends BaseRepository
return $query;
}
public function save($data, $user)
{
$user->fill($data);
$user->save();
return $user;
}
}

View File

@ -39,6 +39,7 @@ class VendorRepository extends BaseRepository
'vendor_contacts.last_name',
'vendors.created_at',
'vendors.work_phone',
'vendors.city',
'vendor_contacts.email',
'vendors.deleted_at',
'vendors.is_deleted'
@ -73,10 +74,6 @@ class VendorRepository extends BaseRepository
$vendor->fill($data);
$vendor->save();
if ( ! isset($data['vendorcontact']) && ! isset($data['vendorcontacts'])) {
return $vendor;
}
$first = true;
$vendorcontacts = isset($data['vendorcontact']) ? [$data['vendorcontact']] : $data['vendorcontacts'];

View File

@ -43,6 +43,7 @@ class ClientTransformer extends EntityTransformer
protected $availableIncludes = [
'contacts',
'invoices',
'credits',
];
public function includeContacts(Client $client)
@ -57,6 +58,12 @@ class ClientTransformer extends EntityTransformer
return $this->includeCollection($client->invoices, $transformer, ENTITY_INVOICE);
}
public function includeCredits(Client $client)
{
$transformer = new CreditTransformer($this->account, $this->serializer);
return $this->includeCollection($client->credits, $transformer, ENTITY_CREDIT);
}
public function transform(Client $client)
{
return [

View File

@ -19,6 +19,7 @@ class ContactTransformer extends EntityTransformer
'phone' => $contact->phone,
'last_login' => $contact->last_login,
'account_key' => $this->account->account_key,
'send_invoice' => (bool) $contact->send_invoice,
];
}
}

View File

@ -0,0 +1,24 @@
<?php namespace App\Ninja\Transformers;
use App\Models\Account;
use App\Models\Credit;
use League\Fractal;
class CreditTransformer extends EntityTransformer
{
public function transform(Credit $credit)
{
return [
'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,
];
}
}

View File

@ -0,0 +1,21 @@
<?php namespace App\Ninja\Transformers;
use App\Models\Account;
use App\Models\Contact;
use App\Models\Invitation;
use League\Fractal;
class InvitationTransformer extends EntityTransformer
{
public function transform(Invitation $invitation)
{
return [
'id' => (int) $invitation->public_id,
'key' => $invitation->getName(),
'status' => $invitation->getStatus(),
'link' => $invitation->getLink(),
'sent_date' => $invitation->sent_date,
'viewed_date' => $invitation->sent_date,
];
}
}

View File

@ -25,12 +25,22 @@ class InvoiceTransformer extends EntityTransformer
'payments'
];
protected $availableIncludes = [
'invitations',
];
public function includeInvoiceItems(Invoice $invoice)
{
$transformer = new InvoiceItemTransformer($this->account, $this->serializer);
return $this->includeCollection($invoice->invoice_items, $transformer, ENTITY_INVOICE_ITEMS);
}
public function includeInvitations(Invoice $invoice)
{
$transformer = new InvitationTransformer($this->account, $this->serializer);
return $this->includeCollection($invoice->invitations, $transformer, ENTITY_INVITATION);
}
public function includePayments(Invoice $invoice)
{
$transformer = new PaymentTransformer($this->account, $this->serializer);
@ -78,6 +88,7 @@ class InvoiceTransformer extends EntityTransformer
'custom_taxes1' => (bool) $invoice->custom_taxes1,
'custom_taxes2' => (bool) $invoice->custom_taxes2,
'has_expenses' => (bool) $invoice->has_expenses,
'quote_invoice_id' => (int) $invoice->quote_invoice_id,
];
}
}

View File

@ -16,6 +16,7 @@ class ProductTransformer extends EntityTransformer
'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),
];
}
}

View File

@ -13,7 +13,7 @@ class TaxRateTransformer extends EntityTransformer
/**
* @SWG\Property(property="id", type="integer", example=1, readOnly=true)
* @SWG\Property(property="name", type="string", example="GST")
* @SWG\Property(property="account_key", type="string", example="34erfdf33fdff" readOnly=true)
* @SWG\Property(property="account_key", type="string", example="asimplestring", readOnly=true)
* @SWG\Property(property="rate", type="float", example=17.5)
* @SWG\Property(property="updated_at", type="date-time", example="2016-01-01 12:10:00")
* @SWG\Property(property="archived_at", type="date-time", example="2016-01-01 12:10:00")

View File

@ -17,7 +17,7 @@ class UserTransformer extends EntityTransformer
'updated_at' => $this->getTimestamp($user->updated_at),
'deleted_at' => $this->getTimestamp($user->deleted_at),
'phone' => $user->phone,
'username' => $user->username,
//'username' => $user->username,
'registered' => (bool) $user->registered,
'confirmed' => (bool) $user->confirmed,
'oauth_user_id' => $user->oauth_user_id,

View File

@ -93,7 +93,7 @@ class AppServiceProvider extends ServiceProvider {
->render();
});
HTML::macro('breadcrumbs', function() {
HTML::macro('breadcrumbs', function($status = false) {
$str = '<ol class="breadcrumb">';
// Get the breadcrumbs by exploding the current path.
@ -128,6 +128,11 @@ class AppServiceProvider extends ServiceProvider {
$str .= '<li>'.link_to($crumb, $name).'</li>';
}
}
if ($status) {
$str .= '&nbsp;&nbsp;&nbsp;&nbsp;' . $status;
}
return $str . '</ol>';
});

View File

@ -3,21 +3,30 @@
use stdClass;
use Utils;
use URL;
use Hash;
use App\Models\Gateway;
use App\Models\BankSubaccount;
use App\Models\Vendor;
use App\Models\Expense;
use App\Services\BaseService;
use App\Ninja\Repositories\BankAccountRepository;
use App\Ninja\Repositories\ExpenseRepository;
use App\Ninja\Repositories\VendorRepository;
use App\Libraries\Finance;
use App\Libraries\Login;
class BankAccountService extends BaseService
{
protected $bankAccountRepo;
protected $expenseRepo;
protected $vendorRepo;
protected $datatableService;
public function __construct(BankAccountRepository $bankAccountRepo, DatatableService $datatableService)
public function __construct(BankAccountRepository $bankAccountRepo, ExpenseRepository $expenseRepo, VendorRepository $vendorRepo, DatatableService $datatableService)
{
$this->bankAccountRepo = $bankAccountRepo;
$this->vendorRepo = $vendorRepo;
$this->expenseRepo = $expenseRepo;
$this->datatableService = $datatableService;
}
@ -26,22 +35,31 @@ class BankAccountService extends BaseService
return $this->bankAccountRepo;
}
/*
public function save()
{
return null;
}
*/
public function loadBankAccounts($bankId, $username, $password, $includeTransactions = true)
{
if ( ! $bankId || ! $username || ! $password) {
return false;
}
$expenses = Expense::scope()
->whereBankId($bankId)
->where('transaction_id', '!=', '')
->withTrashed()
->get(['transaction_id'])
->toArray();
$expenses = array_flip(array_map(function($val) {
return $val['transaction_id'];
}, $expenses));
$bankAccounts = BankSubaccount::scope()
->whereHas('bank_account', function($query) use ($bankId) {
$query->where('bank_id', '=', $bankId);
})
->get();
$bank = Utils::getFromCache($bankId, 'banks');
$data = [];
// load OFX trnansactions
try {
$finance = new Finance();
$finance->banks[$bankId] = $bank->getOFXBank($finance);
@ -52,11 +70,9 @@ class BankAccountService extends BaseService
$login->setup();
foreach ($login->accounts as $account) {
$account->setup($includeTransactions);
$obj = new stdClass;
$obj->account_number = Utils::maskAccountNumber($account->id);
$obj->type = $account->type;
$obj->balance = Utils::formatMoney($account->ledgerBalance, CURRENCY_DOLLAR);
$data[] = $obj;
if ($account = $this->parseBankAccount($account, $bankAccounts, $expenses, $includeTransactions)) {
$data[] = $account;
}
}
}
}
@ -67,6 +83,124 @@ class BankAccountService extends BaseService
}
}
private function parseBankAccount($account, $bankAccounts, $expenses, $includeTransactions)
{
$obj = new stdClass;
$obj->account_name = '';
// look up bank account name
foreach ($bankAccounts as $bankAccount) {
if (Hash::check($account->id, $bankAccount->account_number)) {
$obj->account_name = $bankAccount->account_name;
}
}
// if we can't find a match skip the account
if (count($bankAccounts) && ! $obj->account_name) {
return false;
}
$obj->masked_account_number = Utils::maskAccountNumber($account->id);
$obj->hashed_account_number = bcrypt($account->id);
$obj->type = $account->type;
$obj->balance = Utils::formatMoney($account->ledgerBalance, CURRENCY_DOLLAR);
if ($includeTransactions) {
$ofxParser = new \OfxParser\Parser;
$ofx = $ofxParser->loadFromString($account->response);
$obj->start_date = $ofx->BankAccount->Statement->startDate;
$obj->end_date = $ofx->BankAccount->Statement->endDate;
$obj->transactions = [];
foreach ($ofx->BankAccount->Statement->transactions as $transaction) {
// ensure transactions aren't imported as expenses twice
if (isset($expenses[$transaction->uniqueId])) {
continue;
}
if ($transaction->amount >= 0) {
continue;
}
$transaction->vendor = $this->prepareValue(substr($transaction->name, 0, 20));
$transaction->info = $this->prepareValue(substr($transaction->name, 20));
$transaction->memo = $this->prepareValue($transaction->memo);
$transaction->date = \Auth::user()->account->formatDate($transaction->date);
$transaction->amount *= -1;
$obj->transactions[] = $transaction;
}
}
return $obj;
}
private function prepareValue($value) {
return ucwords(strtolower(trim($value)));
}
public function importExpenses($bankId, $input) {
$countVendors = 0;
$countExpenses = 0;
// create a vendor map
$vendorMap = [];
$vendors = Vendor::scope()
->withTrashed()
->get(['id', 'name', 'transaction_name']);
foreach ($vendors as $vendor) {
$vendorMap[strtolower($vendor->name)] = $vendor;
$vendorMap[strtolower($vendor->transaction_name)] = $vendor;
}
foreach ($input as $transaction) {
$vendorName = $transaction['vendor'];
$key = strtolower($vendorName);
$info = $transaction['info'];
// find vendor otherwise create it
if (isset($vendorMap[$key])) {
$vendor = $vendorMap[$key];
} else {
$field = $this->determineInfoField($info);
$vendor = $this->vendorRepo->save([
$field => $info,
'name' => $vendorName,
'transaction_name' => $transaction['vendor_orig'],
'vendorcontact' => []
]);
$vendorMap[$key] = $vendor;
$vendorMap[$transaction['vendor_orig']] = $vendor;
$countVendors++;
}
// create the expense record
$this->expenseRepo->save([
'vendor_id' => $vendor->id,
'amount' => $transaction['amount'],
'public_notes' => $transaction['memo'],
'expense_date' => $transaction['date'],
'transaction_id' => $transaction['id'],
'bank_id' => $bankId,
'should_be_invoiced' => true,
]);
$countExpenses++;
}
return trans('texts.imported_expenses', [
'count_vendors' => $countVendors,
'count_expenses' => $countExpenses
]);
}
private function determineInfoField($value) {
if (preg_match("/^[0-9\-\(\)\.]+$/", $value)) {
return 'work_phone';
} elseif (strpos($value, '.') !== false) {
return 'private_notes';
} else {
return 'city';
}
}
public function getDatatable($accountId)
{
$query = $this->bankAccountRepo->find($accountId);

View File

@ -5,7 +5,8 @@ use Utils;
use URL;
use App\Services\BaseService;
use App\Ninja\Repositories\ExpenseRepository;
use App\Models\Client;
use App\Models\Vendor;
class ExpenseService extends BaseService
{
@ -26,6 +27,14 @@ class ExpenseService extends BaseService
public function save($data)
{
if (isset($data['client_id']) && $data['client_id']) {
$data['client_id'] = Client::getPrivateId($data['client_id']);
}
if (isset($data['vendor_id']) && $data['vendor_id']) {
$data['vendor_id'] = Vendor::getPrivateId($data['vendor_id']);
}
return $this->expenseRepo->save($data);
}
@ -56,24 +65,38 @@ class ExpenseService extends BaseService
if ($model->vendor_public_id) {
return link_to("vendors/{$model->vendor_public_id}", $model->vendor_name);
} else {
return 'No vendor' ;
return '';
}
}
],
[
'client_name',
function ($model)
{
if ($model->client_public_id) {
return link_to("clients/{$model->client_public_id}", Utils::getClientDisplayName($model));
} else {
return '';
}
}
],
[
'expense_date',
function ($model) {
return Utils::fromSqlDate($model->expense_date);
return link_to("expenses/{$model->public_id}/edit", Utils::fromSqlDate($model->expense_date));
}
],
[
'amount',
function ($model) {
$str = Utils::formatMoney($model->amount, $model->account_currency_id, $model->account_country_id, true);
// show both the amount and the converted amount
if ($model->exchange_rate != 1) {
$str .= ' | ' . Utils::formatMoney(round($model->amount * $model->exchange_rate,2), $model->currency_id, $model->client_country_id, true);
$converted = round($model->amount * $model->exchange_rate, 2);
return Utils::formatMoney($model->amount, $model->expense_currency_id) . ' | ' .
Utils::formatMoney($converted, $model->invoice_currency_id);
} else {
return Utils::formatMoney($model->amount, $model->expense_currency_id);
}
return $str;
}
],
[
@ -97,7 +120,7 @@ class ExpenseService extends BaseService
[
'expense_date',
function ($model) {
return $model->expense_date;
return Utils::dateToString($model->expense_date);
}
],
[

View File

@ -218,7 +218,9 @@ class PaymentService extends BaseService
$account = Account::with('users')->find($invoice->client->public_id);
if ($account->pro_plan_paid && $account->pro_plan_paid != '0000-00-00') {
$date = DateTime::createFromFormat('Y-m-d', $account->pro_plan_paid);
$account->pro_plan_paid = $date->modify('+1 year')->format('Y-m-d');
$date->modify('+1 year');
$date = max($date, date_create());
$account->pro_plan_paid = $date->format('Y-m-d');
} else {
$account->pro_plan_paid = date_create()->format('Y-m-d');
}

View File

@ -50,9 +50,15 @@ class VendorService extends BaseService
}
],
[
'first_name',
'city',
function ($model) {
return link_to("vendors/{$model->public_id}", $model->first_name.' '.$model->last_name);
return $model->city;
}
],
[
'work_phone',
function ($model) {
return $model->work_phone;
}
],
[

View File

@ -24,7 +24,8 @@
"jsoneditor": "*",
"moment-timezone": "~0.4.0",
"quill": "~0.20.0",
"datetimepicker": "~2.4.5"
"datetimepicker": "~2.4.5",
"stacktrace-js": "~1.0.1"
},
"resolutions": {
"jquery": "~1.11"

View File

@ -7,16 +7,13 @@
{
"name": "Hillel Coren",
"email": "hillelcoren@gmail.com"
},
{
"name": "Jeramy Simpson",
"email": "jeramy.n.simpson@gmail.com"
}
],
"require": {
"omnipay/mollie": "dev-master#22956c1a62a9662afa5f5d119723b413770ac525",
"omnipay/2checkout": "dev-master#e9c079c2dde0d7ba461903b3b7bd5caf6dee1248",
"omnipay/gocardless": "dev-master",
"omnipay/stripe": "2.3.0",
"laravel/framework": "5.0.*",
"patricktalmadge/bootstrapper": "5.5.x",
"anahkiasen/former": "4.0.*@dev",
@ -25,7 +22,7 @@
"omnipay/omnipay": "~2.3.0",
"intervention/image": "dev-master",
"webpatser/laravel-countries": "dev-master",
"barryvdh/laravel-ide-helper": "^2.1",
"barryvdh/laravel-ide-helper": "dev-master",
"doctrine/dbal": "2.5.x",
"jsanc623/phpbenchtime": "2.x",
"lokielse/omnipay-alipay": "dev-master",
@ -66,7 +63,8 @@
"jlapp/swaggervel": "master-dev",
"maatwebsite/excel": "~2.0",
"ezyang/htmlpurifier": "~v4.7",
"cerdic/css-tidy": "~v1.5"
"cerdic/css-tidy": "~v1.5",
"asgrim/ofxparser": "^1.1"
},
"require-dev": {
"phpunit/phpunit": "~4.0",

386
composer.lock generated
View File

@ -4,8 +4,8 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "9ed916108c17ac2634ffc4b9676098d1",
"content-hash": "e5177cc53f079158f3180a7e4ef0ded6",
"hash": "6e219bb4f5ffaf8423177bd6fccc89f8",
"content-hash": "4778ab164bfb93c4af1bb51c4320ea4c",
"packages": [
{
"name": "agmscode/omnipay-agms",
@ -123,12 +123,12 @@
"source": {
"type": "git",
"url": "https://github.com/alfaproject/omnipay-skrill.git",
"reference": "2fa2ba8083fd5289366660f8de1b46b5f49ac052"
"reference": "41a7a03c5b90d496720e288bebc157d898837ccd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/alfaproject/omnipay-skrill/zipball/41a7a03c5b90d496720e288bebc157d898837ccd",
"reference": "2fa2ba8083fd5289366660f8de1b46b5f49ac052",
"reference": "41a7a03c5b90d496720e288bebc157d898837ccd",
"shasum": ""
},
"require": {
@ -169,7 +169,7 @@
"payment",
"skrill"
],
"time": "2014-02-25 13:40:07"
"time": "2016-01-13 16:33:07"
},
{
"name": "anahkiasen/former",
@ -323,6 +323,58 @@
],
"time": "2015-03-19 21:32:19"
},
{
"name": "asgrim/ofxparser",
"version": "1.1.1",
"source": {
"type": "git",
"url": "https://github.com/asgrim/ofxparser.git",
"reference": "7652efea77a1c5dda007f9764d8061f870248ea1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/asgrim/ofxparser/zipball/7652efea77a1c5dda007f9764d8061f870248ea1",
"reference": "7652efea77a1c5dda007f9764d8061f870248ea1",
"shasum": ""
},
"require": {
"php": ">=5.4"
},
"type": "library",
"autoload": {
"psr-0": {
"OfxParser": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Guillaume Bailleul",
"email": "contact@guillaume-bailleul.fr",
"homepage": "http://www.guillaume-bailleul.fr"
},
{
"name": "James Titcumb",
"email": "hello@jamestitcumb.com",
"homepage": "http://www.jamestitcumb.com/"
},
{
"name": "Oliver Lowe",
"email": "mrtriangle@gmail.com"
}
],
"description": "Simple OFX file parser",
"keywords": [
"finance",
"ofx",
"open financial exchange",
"parser"
],
"time": "2015-12-11 11:08:57"
},
{
"name": "barryvdh/laravel-debugbar",
"version": "v2.0.6",
@ -379,25 +431,25 @@
},
{
"name": "barryvdh/laravel-ide-helper",
"version": "v2.0.6",
"version": "dev-master",
"source": {
"type": "git",
"url": "https://github.com/barryvdh/laravel-ide-helper.git",
"reference": "037386153630a7515a1542f29410d8c267651689"
"reference": "aa8f772a46c35ecdcb7119f38772ac2ec952a941"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/037386153630a7515a1542f29410d8c267651689",
"reference": "037386153630a7515a1542f29410d8c267651689",
"url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/aa8f772a46c35ecdcb7119f38772ac2ec952a941",
"reference": "aa8f772a46c35ecdcb7119f38772ac2ec952a941",
"shasum": ""
},
"require": {
"illuminate/console": "5.0.x|5.1.x",
"illuminate/filesystem": "5.0.x|5.1.x",
"illuminate/support": "5.0.x|5.1.x",
"illuminate/console": "5.0.x|5.1.x|5.2.x",
"illuminate/filesystem": "5.0.x|5.1.x|5.2.x",
"illuminate/support": "5.0.x|5.1.x|5.2.x",
"php": ">=5.4.0",
"phpdocumentor/reflection-docblock": "2.0.4",
"symfony/class-loader": "~2.3"
"symfony/class-loader": "~2.3|~3.0"
},
"require-dev": {
"doctrine/dbal": "~2.3"
@ -408,7 +460,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0-dev"
"dev-master": "2.1-dev"
}
},
"autoload": {
@ -438,7 +490,7 @@
"phpstorm",
"sublime"
],
"time": "2015-06-25 08:58:59"
"time": "2016-01-22 13:33:15"
},
{
"name": "cardgate/omnipay-cardgate",
@ -1272,33 +1324,33 @@
},
{
"name": "doctrine/cache",
"version": "v1.5.4",
"version": "v1.6.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/cache.git",
"reference": "47cdc76ceb95cc591d9c79a36dc3794975b5d136"
"reference": "f8af318d14bdb0eff0336795b428b547bd39ccb6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/cache/zipball/47cdc76ceb95cc591d9c79a36dc3794975b5d136",
"reference": "47cdc76ceb95cc591d9c79a36dc3794975b5d136",
"url": "https://api.github.com/repos/doctrine/cache/zipball/f8af318d14bdb0eff0336795b428b547bd39ccb6",
"reference": "f8af318d14bdb0eff0336795b428b547bd39ccb6",
"shasum": ""
},
"require": {
"php": ">=5.3.2"
"php": "~5.5|~7.0"
},
"conflict": {
"doctrine/common": ">2.2,<2.4"
},
"require-dev": {
"phpunit/phpunit": ">=3.7",
"phpunit/phpunit": "~4.8|~5.0",
"predis/predis": "~1.0",
"satooshi/php-coveralls": "~0.6"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.5.x-dev"
"dev-master": "1.6.x-dev"
}
},
"autoload": {
@ -1338,7 +1390,7 @@
"cache",
"caching"
],
"time": "2015-12-19 05:03:47"
"time": "2015-12-31 16:37:02"
},
{
"name": "doctrine/collections",
@ -1481,16 +1533,16 @@
},
{
"name": "doctrine/dbal",
"version": "v2.5.3",
"version": "v2.5.4",
"source": {
"type": "git",
"url": "https://github.com/doctrine/dbal.git",
"reference": "2fbcea96eae34a53183377cdbb0b9bec33974648"
"reference": "abbdfd1cff43a7b99d027af3be709bc8fc7d4769"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/dbal/zipball/2fbcea96eae34a53183377cdbb0b9bec33974648",
"reference": "2fbcea96eae34a53183377cdbb0b9bec33974648",
"url": "https://api.github.com/repos/doctrine/dbal/zipball/abbdfd1cff43a7b99d027af3be709bc8fc7d4769",
"reference": "abbdfd1cff43a7b99d027af3be709bc8fc7d4769",
"shasum": ""
},
"require": {
@ -1548,7 +1600,7 @@
"persistence",
"queryobject"
],
"time": "2015-12-25 16:28:24"
"time": "2016-01-05 22:11:12"
},
{
"name": "doctrine/inflector",
@ -2091,16 +2143,16 @@
},
{
"name": "guzzlehttp/psr7",
"version": "1.2.1",
"version": "1.2.2",
"source": {
"type": "git",
"url": "https://github.com/guzzle/psr7.git",
"reference": "4d0bdbe1206df7440219ce14c972aa57cc5e4982"
"reference": "f5d04bdd2881ac89abde1fb78cc234bce24327bb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/4d0bdbe1206df7440219ce14c972aa57cc5e4982",
"reference": "4d0bdbe1206df7440219ce14c972aa57cc5e4982",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/f5d04bdd2881ac89abde1fb78cc234bce24327bb",
"reference": "f5d04bdd2881ac89abde1fb78cc234bce24327bb",
"shasum": ""
},
"require": {
@ -2145,7 +2197,7 @@
"stream",
"uri"
],
"time": "2015-11-03 01:34:55"
"time": "2016-01-23 01:23:02"
},
{
"name": "illuminate/html",
@ -2254,12 +2306,12 @@
"source": {
"type": "git",
"url": "https://github.com/Intervention/image.git",
"reference": "e6acb1609ce89f2c1ec7864fbbda0a20a3eeca70"
"reference": "86dfe2f2a95aa9eed58faf1cc1dae6534a6ce931"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Intervention/image/zipball/86dfe2f2a95aa9eed58faf1cc1dae6534a6ce931",
"reference": "e6acb1609ce89f2c1ec7864fbbda0a20a3eeca70",
"reference": "86dfe2f2a95aa9eed58faf1cc1dae6534a6ce931",
"shasum": ""
},
"require": {
@ -2308,7 +2360,7 @@
"thumbnail",
"watermark"
],
"time": "2015-12-04 17:09:36"
"time": "2016-01-10 11:20:02"
},
{
"name": "ircmaxell/password-compat",
@ -3232,12 +3284,12 @@
"source": {
"type": "git",
"url": "https://github.com/lokielse/omnipay-alipay.git",
"reference": "cbfbee089e0a84a58c73e9d3794894b81a6a82d6"
"reference": "f39ce21a5cbfe5c7cd4108d264b398dbd42be05b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/lokielse/omnipay-alipay/zipball/cbfbee089e0a84a58c73e9d3794894b81a6a82d6",
"reference": "cbfbee089e0a84a58c73e9d3794894b81a6a82d6",
"url": "https://api.github.com/repos/lokielse/omnipay-alipay/zipball/f39ce21a5cbfe5c7cd4108d264b398dbd42be05b",
"reference": "f39ce21a5cbfe5c7cd4108d264b398dbd42be05b",
"shasum": ""
},
"require": {
@ -3273,7 +3325,7 @@
"payment",
"purchase"
],
"time": "2015-10-07 09:33:48"
"time": "2016-01-19 15:08:12"
},
{
"name": "maatwebsite/excel",
@ -3463,12 +3515,12 @@
"source": {
"type": "git",
"url": "https://github.com/meebio/omnipay-secure-trading.git",
"reference": "42f97ee5ad1d28605550d816fc1893919e19e502"
"reference": "992224a3c8dd834ee18f6f253a77ecb4c87c1c1a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/meebio/omnipay-secure-trading/zipball/992224a3c8dd834ee18f6f253a77ecb4c87c1c1a",
"reference": "42f97ee5ad1d28605550d816fc1893919e19e502",
"reference": "992224a3c8dd834ee18f6f253a77ecb4c87c1c1a",
"shasum": ""
},
"require": {
@ -3513,7 +3565,7 @@
"secure trading",
"securetrading"
],
"time": "2015-12-01 10:03:20"
"time": "2016-01-05 09:26:36"
},
{
"name": "mfauveau/omnipay-pacnet",
@ -4363,16 +4415,16 @@
},
{
"name": "omnipay/firstdata",
"version": "v2.2.0",
"version": "v2.3.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/omnipay-firstdata.git",
"reference": "0853bba0ee313f5557eb1c696d3ce5538dbd4aca"
"reference": "e33826821db88d90886cad6c81a29452d3cf91a2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/omnipay-firstdata/zipball/0853bba0ee313f5557eb1c696d3ce5538dbd4aca",
"reference": "0853bba0ee313f5557eb1c696d3ce5538dbd4aca",
"url": "https://api.github.com/repos/thephpleague/omnipay-firstdata/zipball/e33826821db88d90886cad6c81a29452d3cf91a2",
"reference": "e33826821db88d90886cad6c81a29452d3cf91a2",
"shasum": ""
},
"require": {
@ -4417,7 +4469,7 @@
"pay",
"payment"
],
"time": "2015-07-28 17:50:44"
"time": "2016-01-14 06:24:28"
},
{
"name": "omnipay/gocardless",
@ -5112,16 +5164,16 @@
},
{
"name": "omnipay/paypal",
"version": "2.5.0",
"version": "v2.5.1",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/omnipay-paypal.git",
"reference": "67efe5a927dec13fc7520e29bc44f15fd3a728e9"
"reference": "b546d24241725061d44e60516f0fbce202336963"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/omnipay-paypal/zipball/67efe5a927dec13fc7520e29bc44f15fd3a728e9",
"reference": "67efe5a927dec13fc7520e29bc44f15fd3a728e9",
"url": "https://api.github.com/repos/thephpleague/omnipay-paypal/zipball/b546d24241725061d44e60516f0fbce202336963",
"reference": "b546d24241725061d44e60516f0fbce202336963",
"shasum": ""
},
"require": {
@ -5166,20 +5218,20 @@
"paypal",
"purchase"
],
"time": "2015-11-11 21:48:00"
"time": "2016-01-13 07:03:27"
},
{
"name": "omnipay/pin",
"version": "v2.1.0",
"version": "v2.2.1",
"source": {
"type": "git",
"url": "https://github.com/omnipay/pin.git",
"reference": "04e778e9689882d4c40419263014068b69b93168"
"url": "https://github.com/thephpleague/omnipay-pin.git",
"reference": "c2252e41f3674267b2bbe79eaeec73b6b1e4ee58"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/omnipay/pin/zipball/04e778e9689882d4c40419263014068b69b93168",
"reference": "04e778e9689882d4c40419263014068b69b93168",
"url": "https://api.github.com/repos/thephpleague/omnipay-pin/zipball/c2252e41f3674267b2bbe79eaeec73b6b1e4ee58",
"reference": "c2252e41f3674267b2bbe79eaeec73b6b1e4ee58",
"shasum": ""
},
"require": {
@ -5210,11 +5262,11 @@
},
{
"name": "Omnipay Contributors",
"homepage": "https://github.com/omnipay/pin/contributors"
"homepage": "https://github.com/thephpleague/omnipay-pin/contributors"
}
],
"description": "Pin Payments driver for the Omnipay payment processing library",
"homepage": "https://github.com/omnipay/pin",
"homepage": "https://github.com/thephpleague/omnipay-pin",
"keywords": [
"gateway",
"merchant",
@ -5223,20 +5275,20 @@
"payment",
"pin"
],
"time": "2014-04-14 11:26:15"
"time": "2016-01-13 07:00:17"
},
{
"name": "omnipay/sagepay",
"version": "v2.2.0",
"version": "2.3.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/omnipay-sagepay.git",
"reference": "899507095428fa54276ba5ca89f11fd7f8fd78ab"
"reference": "4208d23b33b2f8a59176e44ad22d304c461ecb62"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/omnipay-sagepay/zipball/899507095428fa54276ba5ca89f11fd7f8fd78ab",
"reference": "899507095428fa54276ba5ca89f11fd7f8fd78ab",
"url": "https://api.github.com/repos/thephpleague/omnipay-sagepay/zipball/4208d23b33b2f8a59176e44ad22d304c461ecb62",
"reference": "4208d23b33b2f8a59176e44ad22d304c461ecb62",
"shasum": ""
},
"require": {
@ -5282,7 +5334,7 @@
"sage pay",
"sagepay"
],
"time": "2015-04-02 17:46:20"
"time": "2016-01-12 12:43:31"
},
{
"name": "omnipay/securepay",
@ -5512,6 +5564,54 @@
],
"time": "2014-09-17 00:37:18"
},
{
"name": "paragonie/random_compat",
"version": "1.1.5",
"source": {
"type": "git",
"url": "https://github.com/paragonie/random_compat.git",
"reference": "dd8998b7c846f6909f4e7a5f67fabebfc412a4f7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/dd8998b7c846f6909f4e7a5f67fabebfc412a4f7",
"reference": "dd8998b7c846f6909f4e7a5f67fabebfc412a4f7",
"shasum": ""
},
"require": {
"php": ">=5.2.0"
},
"require-dev": {
"phpunit/phpunit": "4.*|5.*"
},
"suggest": {
"ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
},
"type": "library",
"autoload": {
"files": [
"lib/random.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Paragon Initiative Enterprises",
"email": "security@paragonie.com",
"homepage": "https://paragonie.com"
}
],
"description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
"keywords": [
"csprng",
"pseudorandom",
"random"
],
"time": "2016-01-06 13:31:20"
},
{
"name": "patricktalmadge/bootstrapper",
"version": "5.5.3",
@ -6058,28 +6158,28 @@
},
{
"name": "symfony/class-loader",
"version": "v2.8.1",
"version": "v3.0.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/class-loader.git",
"reference": "ec74b0a279cf3a9bd36172b3e3061591d380ce6c"
"reference": "6294f616bb9888ba2e13c8bfdcc4d306c1c95da7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/class-loader/zipball/ec74b0a279cf3a9bd36172b3e3061591d380ce6c",
"reference": "ec74b0a279cf3a9bd36172b3e3061591d380ce6c",
"url": "https://api.github.com/repos/symfony/class-loader/zipball/6294f616bb9888ba2e13c8bfdcc4d306c1c95da7",
"reference": "6294f616bb9888ba2e13c8bfdcc4d306c1c95da7",
"shasum": ""
},
"require": {
"php": ">=5.3.9"
"php": ">=5.5.9"
},
"require-dev": {
"symfony/finder": "~2.0,>=2.0.5|~3.0.0"
"symfony/finder": "~2.8|~3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.8-dev"
"dev-master": "3.0-dev"
}
},
"autoload": {
@ -6106,11 +6206,11 @@
],
"description": "Symfony ClassLoader Component",
"homepage": "https://symfony.com",
"time": "2015-12-05 17:37:59"
"time": "2015-12-05 17:45:07"
},
{
"name": "symfony/console",
"version": "v2.6.12",
"version": "v2.6.13",
"target-dir": "Symfony/Component/Console",
"source": {
"type": "git",
@ -6168,16 +6268,16 @@
},
{
"name": "symfony/css-selector",
"version": "v2.8.1",
"version": "v2.8.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/css-selector.git",
"reference": "eaa3320e32f09a01dc432c6efbe8051aee59cfef"
"reference": "ac06d8173bd80790536c0a4a634a7d705b91f54f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/css-selector/zipball/eaa3320e32f09a01dc432c6efbe8051aee59cfef",
"reference": "eaa3320e32f09a01dc432c6efbe8051aee59cfef",
"url": "https://api.github.com/repos/symfony/css-selector/zipball/ac06d8173bd80790536c0a4a634a7d705b91f54f",
"reference": "ac06d8173bd80790536c0a4a634a7d705b91f54f",
"shasum": ""
},
"require": {
@ -6217,11 +6317,11 @@
],
"description": "Symfony CssSelector Component",
"homepage": "https://symfony.com",
"time": "2015-12-05 17:37:59"
"time": "2016-01-03 15:33:41"
},
{
"name": "symfony/debug",
"version": "v2.6.12",
"version": "v2.6.13",
"target-dir": "Symfony/Component/Debug",
"source": {
"type": "git",
@ -6282,16 +6382,16 @@
},
{
"name": "symfony/event-dispatcher",
"version": "v2.8.1",
"version": "v2.8.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
"reference": "a5eb815363c0388e83247e7e9853e5dbc14999cc"
"reference": "ee278f7c851533e58ca307f66305ccb9188aceda"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/a5eb815363c0388e83247e7e9853e5dbc14999cc",
"reference": "a5eb815363c0388e83247e7e9853e5dbc14999cc",
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/ee278f7c851533e58ca307f66305ccb9188aceda",
"reference": "ee278f7c851533e58ca307f66305ccb9188aceda",
"shasum": ""
},
"require": {
@ -6338,20 +6438,20 @@
],
"description": "Symfony EventDispatcher Component",
"homepage": "https://symfony.com",
"time": "2015-10-30 20:15:42"
"time": "2016-01-13 10:28:07"
},
{
"name": "symfony/filesystem",
"version": "v2.8.1",
"version": "v2.8.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/filesystem.git",
"reference": "a7ad724530a764d70c168d321ac226ba3d2f10fc"
"reference": "637b64d0ee10f44ae98dbad651b1ecdf35a11e8c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/a7ad724530a764d70c168d321ac226ba3d2f10fc",
"reference": "a7ad724530a764d70c168d321ac226ba3d2f10fc",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/637b64d0ee10f44ae98dbad651b1ecdf35a11e8c",
"reference": "637b64d0ee10f44ae98dbad651b1ecdf35a11e8c",
"shasum": ""
},
"require": {
@ -6387,11 +6487,11 @@
],
"description": "Symfony Filesystem Component",
"homepage": "https://symfony.com",
"time": "2015-12-22 10:25:57"
"time": "2016-01-13 10:28:07"
},
{
"name": "symfony/finder",
"version": "v2.6.12",
"version": "v2.6.13",
"target-dir": "Symfony/Component/Finder",
"source": {
"type": "git",
@ -6441,7 +6541,7 @@
},
{
"name": "symfony/http-foundation",
"version": "v2.6.12",
"version": "v2.6.13",
"target-dir": "Symfony/Component/HttpFoundation",
"source": {
"type": "git",
@ -6495,17 +6595,17 @@
},
{
"name": "symfony/http-kernel",
"version": "v2.6.12",
"version": "v2.6.13",
"target-dir": "Symfony/Component/HttpKernel",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-kernel.git",
"reference": "498866a8ca0bcbcd3f3824b1520fa568ff7ca3b6"
"reference": "cdd991d304fed833514dc44d6aafcf19397c26cb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/498866a8ca0bcbcd3f3824b1520fa568ff7ca3b6",
"reference": "498866a8ca0bcbcd3f3824b1520fa568ff7ca3b6",
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/cdd991d304fed833514dc44d6aafcf19397c26cb",
"reference": "cdd991d304fed833514dc44d6aafcf19397c26cb",
"shasum": ""
},
"require": {
@ -6569,7 +6669,7 @@
],
"description": "Symfony HttpKernel Component",
"homepage": "https://symfony.com",
"time": "2015-11-23 11:37:53"
"time": "2016-01-14 10:11:16"
},
{
"name": "symfony/polyfill-php56",
@ -6681,7 +6781,7 @@
},
{
"name": "symfony/process",
"version": "v2.6.12",
"version": "v2.6.13",
"target-dir": "Symfony/Component/Process",
"source": {
"type": "git",
@ -6731,7 +6831,7 @@
},
{
"name": "symfony/routing",
"version": "v2.6.12",
"version": "v2.6.13",
"target-dir": "Symfony/Component/Routing",
"source": {
"type": "git",
@ -6800,20 +6900,21 @@
},
{
"name": "symfony/security-core",
"version": "v2.6.12",
"version": "v2.6.13",
"target-dir": "Symfony/Component/Security/Core",
"source": {
"type": "git",
"url": "https://github.com/symfony/security-core.git",
"reference": "05f58bb3814e8a853332dc448e3b7addaa87679c"
"reference": "813cf2aaacccbbe1a4705aef8d4ac0d79d993a76"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/security-core/zipball/05f58bb3814e8a853332dc448e3b7addaa87679c",
"reference": "05f58bb3814e8a853332dc448e3b7addaa87679c",
"url": "https://api.github.com/repos/symfony/security-core/zipball/813cf2aaacccbbe1a4705aef8d4ac0d79d993a76",
"reference": "813cf2aaacccbbe1a4705aef8d4ac0d79d993a76",
"shasum": ""
},
"require": {
"paragonie/random_compat": "~1.0",
"php": ">=5.3.3"
},
"require-dev": {
@ -6860,11 +6961,11 @@
],
"description": "Symfony Security Component - Core Library",
"homepage": "https://symfony.com",
"time": "2015-07-22 10:08:40"
"time": "2016-01-14 09:04:34"
},
{
"name": "symfony/translation",
"version": "v2.6.12",
"version": "v2.6.13",
"target-dir": "Symfony/Component/Translation",
"source": {
"type": "git",
@ -6923,7 +7024,7 @@
},
{
"name": "symfony/var-dumper",
"version": "v2.6.12",
"version": "v2.6.13",
"target-dir": "Symfony/Component/VarDumper",
"source": {
"type": "git",
@ -7030,16 +7131,16 @@
},
{
"name": "true/punycode",
"version": "v2.0.1",
"version": "v2.0.2",
"source": {
"type": "git",
"url": "https://github.com/true/php-punycode.git",
"reference": "b672918d992b84f8016bbe353a42516928393c63"
"reference": "74fa01d4de396c40e239794123b3874cb594a30c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/true/php-punycode/zipball/b672918d992b84f8016bbe353a42516928393c63",
"reference": "b672918d992b84f8016bbe353a42516928393c63",
"url": "https://api.github.com/repos/true/php-punycode/zipball/74fa01d4de396c40e239794123b3874cb594a30c",
"reference": "74fa01d4de396c40e239794123b3874cb594a30c",
"shasum": ""
},
"require": {
@ -7072,7 +7173,7 @@
"idna",
"punycode"
],
"time": "2015-09-01 14:53:31"
"time": "2016-01-07 17:12:58"
},
{
"name": "twbs/bootstrap",
@ -7347,16 +7448,16 @@
},
{
"name": "zircote/swagger-php",
"version": "2.0.4",
"version": "2.0.5",
"source": {
"type": "git",
"url": "https://github.com/zircote/swagger-php.git",
"reference": "be5d96e56c23cbe52c5bc5e267851323d95c57cd"
"reference": "c19af4edcc13c00e82fabeee926335b1fe1d92e9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zircote/swagger-php/zipball/be5d96e56c23cbe52c5bc5e267851323d95c57cd",
"reference": "be5d96e56c23cbe52c5bc5e267851323d95c57cd",
"url": "https://api.github.com/repos/zircote/swagger-php/zipball/c19af4edcc13c00e82fabeee926335b1fe1d92e9",
"reference": "c19af4edcc13c00e82fabeee926335b1fe1d92e9",
"shasum": ""
},
"require": {
@ -7403,7 +7504,7 @@
"rest",
"service discovery"
],
"time": "2015-11-13 13:50:11"
"time": "2016-01-15 09:39:28"
}
],
"packages-dev": [
@ -7593,16 +7694,16 @@
},
{
"name": "facebook/webdriver",
"version": "1.1.0",
"version": "1.1.1",
"source": {
"type": "git",
"url": "https://github.com/facebook/php-webdriver.git",
"reference": "518a9e0635e69777f07e41619cdf5d82fae284b6"
"reference": "1c98108ba3eb435b681655764de11502a0653705"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/facebook/php-webdriver/zipball/518a9e0635e69777f07e41619cdf5d82fae284b6",
"reference": "518a9e0635e69777f07e41619cdf5d82fae284b6",
"url": "https://api.github.com/repos/facebook/php-webdriver/zipball/1c98108ba3eb435b681655764de11502a0653705",
"reference": "1c98108ba3eb435b681655764de11502a0653705",
"shasum": ""
},
"require": {
@ -7632,7 +7733,7 @@
"selenium",
"webdriver"
],
"time": "2015-12-08 17:04:30"
"time": "2015-12-31 15:58:49"
},
{
"name": "fzaninotto/faker",
@ -7722,16 +7823,16 @@
},
{
"name": "phpspec/phpspec",
"version": "2.4.0",
"version": "2.4.1",
"source": {
"type": "git",
"url": "https://github.com/phpspec/phpspec.git",
"reference": "1d3938e6d9ffb1bd4805ea8ddac62ea48767f358"
"reference": "5528ce1e93a1efa090c9404aba3395c329b4e6ed"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpspec/phpspec/zipball/1d3938e6d9ffb1bd4805ea8ddac62ea48767f358",
"reference": "1d3938e6d9ffb1bd4805ea8ddac62ea48767f358",
"url": "https://api.github.com/repos/phpspec/phpspec/zipball/5528ce1e93a1efa090c9404aba3395c329b4e6ed",
"reference": "5528ce1e93a1efa090c9404aba3395c329b4e6ed",
"shasum": ""
},
"require": {
@ -7796,7 +7897,7 @@
"testing",
"tests"
],
"time": "2015-11-29 02:03:49"
"time": "2016-01-01 10:17:54"
},
{
"name": "phpspec/prophecy",
@ -8599,16 +8700,16 @@
},
{
"name": "symfony/browser-kit",
"version": "v2.8.1",
"version": "v2.8.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/browser-kit.git",
"reference": "dd2cfb20fabd4efca14cf3b2345d40b3dd5e9aca"
"reference": "a93dffaf763182acad12a4c42c7efc372899891e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/browser-kit/zipball/dd2cfb20fabd4efca14cf3b2345d40b3dd5e9aca",
"reference": "dd2cfb20fabd4efca14cf3b2345d40b3dd5e9aca",
"url": "https://api.github.com/repos/symfony/browser-kit/zipball/a93dffaf763182acad12a4c42c7efc372899891e",
"reference": "a93dffaf763182acad12a4c42c7efc372899891e",
"shasum": ""
},
"require": {
@ -8652,20 +8753,20 @@
],
"description": "Symfony BrowserKit Component",
"homepage": "https://symfony.com",
"time": "2015-12-26 13:37:56"
"time": "2016-01-12 17:46:01"
},
{
"name": "symfony/dom-crawler",
"version": "v2.8.1",
"version": "v2.8.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/dom-crawler.git",
"reference": "a2712aff8b250d9601ad6bd23a2ff82a12730e8e"
"reference": "650d37aacb1fa0dcc24cced483169852b3a0594e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/dom-crawler/zipball/a2712aff8b250d9601ad6bd23a2ff82a12730e8e",
"reference": "a2712aff8b250d9601ad6bd23a2ff82a12730e8e",
"url": "https://api.github.com/repos/symfony/dom-crawler/zipball/650d37aacb1fa0dcc24cced483169852b3a0594e",
"reference": "650d37aacb1fa0dcc24cced483169852b3a0594e",
"shasum": ""
},
"require": {
@ -8708,7 +8809,7 @@
],
"description": "Symfony DomCrawler Component",
"homepage": "https://symfony.com",
"time": "2015-12-23 17:16:29"
"time": "2016-01-03 15:33:41"
},
{
"name": "symfony/polyfill-mbstring",
@ -8771,16 +8872,16 @@
},
{
"name": "symfony/yaml",
"version": "v2.8.1",
"version": "v2.8.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
"reference": "ac84cbb98b68a6abbc9f5149eb96ccc7b07b8966"
"reference": "34c8a4b51e751e7ea869b8262f883d008a2b81b8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/yaml/zipball/ac84cbb98b68a6abbc9f5149eb96ccc7b07b8966",
"reference": "ac84cbb98b68a6abbc9f5149eb96ccc7b07b8966",
"url": "https://api.github.com/repos/symfony/yaml/zipball/34c8a4b51e751e7ea869b8262f883d008a2b81b8",
"reference": "34c8a4b51e751e7ea869b8262f883d008a2b81b8",
"shasum": ""
},
"require": {
@ -8816,7 +8917,7 @@
],
"description": "Symfony Yaml Component",
"homepage": "https://symfony.com",
"time": "2015-12-26 13:37:56"
"time": "2016-01-13 10:28:07"
}
],
"aliases": [],
@ -8829,6 +8930,7 @@
"chumper/datatable": 20,
"intervention/image": 20,
"webpatser/laravel-countries": 20,
"barryvdh/laravel-ide-helper": 20,
"lokielse/omnipay-alipay": 20,
"alfaproject/omnipay-neteller": 20,
"alfaproject/omnipay-skrill": 20,

View File

@ -96,12 +96,11 @@ class CreateVendorsTable extends Migration
$table->softDeletes();
$table->unsignedInteger('user_id');
$table->unsignedInteger('account_id');
$table->unsignedInteger('public_id')->index();
//$table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
//$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->unsignedInteger('public_id')->index();
$table->unique(array('account_id', 'public_id'));
//$table->unique(array('account_id', 'public_id'));
});
// Update public id
@ -112,12 +111,16 @@ class CreateVendorsTable extends Migration
$i = 1;
foreach ($paymentTerms as $pTerm) {
$data = ['public_id' => $i++];
DB::table('paymet_terms')->where('id', $pTerm->id)->update($data);
DB::table('payment_terms')->where('id', $pTerm->id)->update($data);
}
Schema::table('invoices', function (Blueprint $table) {
$table->boolean('has_expenses')->default(false);
});
Schema::table('payment_terms', function (Blueprint $table) {
$table->unique(array('account_id', 'public_id'));
});
}
/**

View File

@ -11,8 +11,6 @@ class AddInvoiceFontSupport extends Migration
*/
public function up()
{
Schema::dropIfExists('fonts');
Schema::create('fonts', function ($t) {
$t->increments('id');
@ -30,18 +28,20 @@ class AddInvoiceFontSupport extends Migration
});
// Create fonts
$seeder = new FontsSeeder();
$seeder->run();
//$seeder = new FontsSeeder();
//$seeder->run();
Schema::table('accounts', function ($table) {
$table->unsignedInteger('header_font_id')->default(1);
$table->unsignedInteger('body_font_id')->default(1);
});
/*
Schema::table('accounts', function ($table) {
$table->foreign('header_font_id')->references('id')->on('fonts');
$table->foreign('body_font_id')->references('id')->on('fonts');
});
*/
}
/**
@ -53,14 +53,14 @@ class AddInvoiceFontSupport extends Migration
{
if (Schema::hasColumn('accounts', 'header_font_id')) {
Schema::table('accounts', function ($table) {
$table->dropForeign('accounts_header_font_id_foreign');
//$table->dropForeign('accounts_header_font_id_foreign');
$table->dropColumn('header_font_id');
});
}
if (Schema::hasColumn('accounts', 'body_font_id')) {
Schema::table('accounts', function ($table) {
$table->dropForeign('accounts_body_font_id_foreign');
//$table->dropForeign('accounts_body_font_id_foreign');
$table->dropColumn('body_font_id');
});
}

View File

@ -18,10 +18,12 @@ class AddQuoteToInvoiceOption extends Migration
});
// we need to create the last status to resolve a foreign key constraint
if (DB::table('invoice_statuses')->count() == 5) {
DB::table('invoice_statuses')->insert([
'id' => 6,
'name' => 'Paid'
]);
}
DB::table('invoices')
->whereIn('invoice_status_id', [4, 5])

View File

@ -0,0 +1,69 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddBankSubaccounts extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('bank_subaccounts', function($table)
{
$table->increments('id');
$table->unsignedInteger('account_id');
$table->unsignedInteger('user_id');
$table->unsignedInteger('bank_account_id');
$table->string('account_name');
$table->string('account_number');
$table->timestamps();
$table->softDeletes();
$table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->foreign('bank_account_id')->references('id')->on('bank_accounts')->onDelete('cascade');
$table->unsignedInteger('public_id')->index();
$table->unique(['account_id', 'public_id']);
});
Schema::table('expenses', function($table)
{
$table->string('transaction_id');
$table->unsignedInteger('bank_id');
});
Schema::table('vendors', function($table)
{
$table->string('transaction_name');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('bank_subaccounts');
Schema::table('expenses', function($table)
{
$table->dropColumn('transaction_id');
$table->dropColumn('bank_id');
});
Schema::table('vendors', function($table)
{
$table->dropColumn('transaction_name');
});
}
}

View File

@ -0,0 +1,64 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddHeaderFooterOption extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('accounts', function($table)
{
$table->boolean('all_pages_footer');
$table->boolean('all_pages_header');
$table->boolean('show_currency_code');
$table->date('pro_plan_trial')->nullable();
});
Schema::table('gateways', function($table)
{
$table->boolean('is_offsite');
$table->boolean('is_secure');
});
Schema::table('expenses', function($table)
{
$table->string('transaction_id')->nullable()->change();
$table->unsignedInteger('bank_id')->nullable()->change();
});
Schema::table('vendors', function($table)
{
$table->string('transaction_name')->nullable()->change();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('accounts', function($table)
{
$table->dropColumn('all_pages_footer');
$table->dropColumn('all_pages_header');
$table->dropColumn('show_currency_code');
$table->dropColumn('pro_plan_trial');
});
Schema::table('gateways', function($table)
{
$table->dropColumn('is_offsite');
$table->dropColumn('is_secure');
});
}
}

View File

@ -0,0 +1,48 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddSourceCurrencyToExpenses extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('expenses', function (Blueprint $table) {
$table->dropColumn('foreign_amount');
$table->unsignedInteger('currency_id')->nullable(false)->change();
$table->renameColumn('currency_id', 'invoice_currency_id');
$table->unsignedInteger('expense_currency_id');
});
Schema::table('expenses', function (Blueprint $table) {
// set account value so we're able to create foreign constraint
DB::statement('update expenses e
left join accounts a on a.id = e.account_id
set e.expense_currency_id = COALESCE(a.currency_id, 1)');
$table->foreign('invoice_currency_id')->references('id')->on('currencies');
$table->foreign('expense_currency_id')->references('id')->on('currencies');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('expenses', function($table) {
});
}
}

File diff suppressed because one or more lines are too long

View File

@ -18,7 +18,6 @@ class DatabaseSeeder extends Seeder
$this->call('PaymentLibrariesSeeder');
$this->call('FontsSeeder');
$this->call('BanksSeeder');
$this->call('FontsSeeder');
$this->call('InvoiceStatusSeeder');
}
}

View File

@ -224,6 +224,7 @@ class FontsSeeder extends Seeder
foreach ($fonts as $font) {
if (!DB::table('fonts')->where('name', '=', $font['name'])->get()) {
$font['is_early_access'] = false;
Font::create($font);
}
}

View File

@ -125,6 +125,7 @@ class PaymentLibrariesSeeder extends Seeder
['name' => 'Ghanaian Cedi', 'code' => 'GHS', 'symbol' => '', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['name' => 'Bulgarian Lev', 'code' => 'BGN', 'symbol' => '', 'precision' => '2', 'thousand_separator' => ' ', 'decimal_separator' => '.'],
['name' => 'Aruban Florin', 'code' => 'AWG', 'symbol' => 'Afl. ', 'precision' => '2', 'thousand_separator' => ' ', 'decimal_separator' => '.'],
['name' => 'Turkish Lira', 'code' => 'TRY', 'symbol' => 'TL ', 'precision' => '2', 'thousand_separator' => '.', 'decimal_separator' => ','],
];
foreach ($currencies as $currency) {
@ -151,7 +152,7 @@ class PaymentLibrariesSeeder extends Seeder
['format' => 'M j, Y', 'picker_format' => 'M d, yyyy', 'label' => 'Mar 10, 2013'],
['format' => 'F j, Y', 'picker_format' => 'MM d, yyyy', 'label' => 'March 10, 2013'],
['format' => 'D M j, Y', 'picker_format' => 'D MM d, yyyy', 'label' => 'Mon March 10, 2013'],
['format' => 'Y-M-d', 'picker_format' => 'yyyy-M-dd', 'label' => '2013-03-10'],
['format' => 'Y-m-d', 'picker_format' => 'yyyy-mm-dd', 'label' => '2013-03-10'],
['format' => 'd-m-Y', 'picker_format' => 'dd-mm-yyyy', 'label' => '20-03-2013'],
['format' => 'm/d/Y', 'picker_format' => 'mm/dd/yyyy', 'label' => '03/20/2013']
];
@ -207,7 +208,7 @@ class PaymentLibrariesSeeder extends Seeder
'label' => 'Mon March 10th, 2013 6:15 pm'
],
[
'format' => 'Y-M-d g:i a',
'format' => 'Y-m-d g:i a',
'format_moment' => 'YYYY-MMM-DD h:mm:ss a',
'label' => '2013-03-10 6:15 pm'
],

View File

@ -1,187 +0,0 @@
<?php
/**
* Adds data to invitation for a user/client so that the
* public/payment/{aaabbb} page is accessable (aaabbb is the invitation_key)
**/
class SecurePaymentFormSeeder extends Seeder
{
public function run()
{
Eloquent::unguard();
//Delete table content
DB::table('invitations')->delete();
DB::table('invoices')->delete();
DB::table('contacts')->delete();
DB::table('clients')->delete();
DB::table('account_gateways')->delete();
//To reset the auto increment
$statement = "
ALTER TABLE invitations AUTO_INCREMENT = 1;
ALTER TABLE invoices AUTO_INCREMENT = 1;
ALTER TABLE contacts AUTO_INCREMENT = 1;
ALTER TABLE clients AUTO_INCREMENT = 1;
ALTER TABLE account_gateways AUTO_INCREMENT = 1;
";
DB::unprepared($statement);
//$firstName = 'Oscar';
// $lastName = 'Thompson';
// $firstName_2 = 'Philip';
// $lastName_2 = 'Jonsson';
//
// $user = AccountGateway::create(array(
// 'account_id' => 1,
// 'user_id' => 1,
// 'gateway_id' => 4,
// 'config' => '{"bla":"vla","bli":"cla"}',
// 'public_id' => 1,
// 'accepted_credit_cards' => 8
// ));
//
// $user2 = AccountGateway::create(array(
// 'account_id' => 2,
// 'user_id' => 2,
// 'gateway_id' => 5,
// 'config' => '{"bla":"vla","bli":"cla"}',
// 'public_id' => 2,
// 'accepted_credit_cards' => 7
// ));
//
// $client = Client::create(array(
// 'user_id' => 1,
// 'account_id' => 1,
// 'currency_id' => 1,
// 'name' => $firstName.' '.$lastName,
// 'address1' => '2119 Howe Course',
// 'address2' => '2118 Howe Course',
// 'city' => 'West Chazport',
// 'state' => 'Utah',
// 'postal_code' => '31572',
// 'country_id' => 752,
// 'work_phone' => '012-345678',
// 'private_notes' => 'bla bla bla bla bla bla bla',
// 'balance' => 10.4,
// 'paid_to_date' => 10.2,
// 'website' => 'awebsite.com',
// 'industry_id' => 8,
// 'is_deleted' => 0,
// 'payment_terms' => 2,
// 'public_id' => 1,
// 'custom_value1' => $firstName,
// 'custom_value2' => $firstName
// ));
//
// $client2 = Client::create(array(
// 'user_id' => 2,
// 'account_id' => 2,
// 'currency_id' => 1,
// 'name' => $firstName_2.' '.$lastName_2,
// 'address1' => '1118 Muma Road',
// 'address2' => '1118 Muma Road',
// 'city' => 'New Orleans',
// 'state' => 'Arizona',
// 'postal_code' => '31572',
// 'country_id' => 752,
// 'work_phone' => '012-345678',
// 'private_notes' => 'bla bla bla bla bla bla bla',
// 'balance' => 10.4,
// 'paid_to_date' => 10.2,
// 'website' => 'bodosite.com',
// 'industry_id' => 8,
// 'is_deleted' => 0,
// 'payment_terms' => 2,
// 'public_id' => 1,
// 'custom_value1' => $firstName_2,
// 'custom_value2' => $firstName_2
// ));
//
// $contact = Contact::create(array(
// 'account_id' => 1,
// 'user_id' => 1,
// 'client_id' => 1,
// 'is_primary' => 0,
// 'send_invoice' => 0,
// 'first_name' => $firstName,
// 'last_name' => $lastName,
// 'email' => 'an@email.com',
// 'phone' => '012-345678',
// 'public_id' => 1
// ));
//
// $contact2 = Contact::create(array(
// 'account_id' => 2,
// 'user_id' => 2,
// 'client_id' => 2,
// 'is_primary' => 0,
// 'send_invoice' => 0,
// 'first_name' => $firstName_2,
// 'last_name' => $lastName_2,
// 'email' => 'the@email.com',
// 'phone' => '012-345678',
// 'public_id' => 2
// ));
//
// $invoice = Invoice::create(array(
// 'client_id' => 1,
// 'user_id' => 1,
// 'account_id' => 1,
// 'invoice_number' => 1,
// 'discount' => 0.4,
// 'po_number' => $firstName,
// 'terms' => 'bla bla bla bla bla bla bla',
// 'public_notes' => 'bla bla bla bla bla bla bla',
// 'is_deleted' => 0,
// 'is_recurring' => 0,
// 'frequency_id' => 1,
// 'tax_name' => 'moms',
// 'tax_rate' => 33.0,
// 'amount' => 10.0,
// 'balance' => 8.0,
// 'public_id' => 1,
// 'is_quote' => 0
// ));
//
// $invoice2 = Invoice::create(array(
// 'client_id' => 2,
// 'user_id' => 2,
// 'account_id' => 2,
// 'invoice_number' => 2,
// 'discount' => 0.4,
// 'po_number' => $firstName_2,
// 'terms' => 'bla bla bla bla bla bla bla',
// 'public_notes' => 'bla bla bla bla bla bla bla',
// 'is_deleted' => 0,
// 'is_recurring' => 0,
// 'frequency_id' => 1,
// 'tax_name' => 'moms',
// 'tax_rate' => 33.0,
// 'amount' => 10.0,
// 'balance' => 8.0,
// 'public_id' => 2,
// 'is_quote' => 0
// ));
//
// $invitation = Invitation::create(array(
// 'account_id' => 1,
// 'user_id' => 1,
// 'contact_id' => 1,
// 'invoice_id' => 1,
// 'invitation_key' => 'aaabbb',
// 'transaction_reference' => 'bla bla bla bla bla bla bla',
// 'public_id' => 1
// ));
//
// $invitation2 = Invitation::create(array(
// 'account_id' => 2,
// 'user_id' => 2,
// 'contact_id' => 2,
// 'invoice_id' => 2,
// 'invitation_key' => 'cccddd',
// 'transaction_reference' => 'bla bla bla bla bla bla bla',
// 'public_id' => 2
// ));
}
}

File diff suppressed because one or more lines are too long

View File

@ -3372,6 +3372,10 @@ ul.user-accounts a:hover div.remove {
font-size: .9em;
}
td.right {
text-align: right;
}
/* Show selected section in settings nav */
.list-group-item.selected:before {
position: absolute;

View File

@ -1021,6 +1021,10 @@ ul.user-accounts a:hover div.remove {
font-size: .9em;
}
td.right {
text-align: right;
}
/* Show selected section in settings nav */
.list-group-item.selected:before {
position: absolute;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -54,15 +54,16 @@ function GetPdfMake(invoice, javascript, callback) {
}
}
// only show the header on the first page
// and the footer on the last page
// determine whether or not to show the header/footer
if (invoice.is_pro) {
if (key === 'header') {
return function(page, pages) {
return page === 1 ? val : '';
return page === 1 || account.all_pages_header ? val : '';
}
} else if (invoice.is_pro && key === 'footer') {
} else if (key === 'footer') {
return function(page, pages) {
return page === pages ? val : '';
return page === pages || account.all_pages_footer ? val : '';
}
}
}
@ -190,8 +191,8 @@ NINJA.decodeJavascript = function(invoice, javascript)
if (match.indexOf('?') < 0 || value) {
if (invoice.partial && field == 'balance_due') {
field = 'amount_due';
} else if (invoice.is_quote && field == 'your_invoice') {
field = 'your_quote';
} else if (invoice.is_quote) {
field = field.replace('invoice', 'quote');
}
var label = invoiceLabels[field];
if (match.indexOf('UC') >= 0) {
@ -334,9 +335,6 @@ NINJA.invoiceLines = function(invoice) {
}
var lineTotal = roundToTwo(NINJA.parseFloat(item.cost)) * roundToTwo(NINJA.parseFloat(item.qty));
if (showItemTaxes && tax) {
lineTotal += lineTotal * tax / 100;
}
lineTotal = formatMoneyInvoice(lineTotal, invoice);
rowStyle = (i % 2 == 0) ? 'odd' : 'even';

View File

@ -5,6 +5,8 @@ var isSafari = Object.prototype.toString.call(window.HTMLElement).indexOf('Const
var isEdge = navigator.userAgent.indexOf('Edge/') >= 0;
var isChrome = !!window.chrome && !isOpera && !isEdge; // Chrome 1+
var isChromium = isChrome && navigator.userAgent.indexOf('Chromium') >= 0;
// https://code.google.com/p/chromium/issues/detail?id=574648
var isChrome48 = isChrome && navigator.userAgent.indexOf('Chrome/48') >= 0;
var isIE = /*@cc_on!@*/false || !!document.documentMode; // At least IE6
var refreshTimer;

View File

@ -13,7 +13,7 @@
### Reseller Program
There are two options:
* 10% of revenue
* $1,000 for a site limited to 1,000 accounts
* $1,000 for a site limited to 1,000 users
### Installation Options
* [Self-Host Zip](https://www.invoiceninja.com/knowledgebase/self-host/) - Free
@ -95,3 +95,5 @@ There are two options:
* [thephpleague/fractal](https://github.com/thephpleague/fractal) - Output complex, flexible, AJAX/RESTful data structures
* [ezyang/htmlpurifier](https://github.com/ezyang/htmlpurifier) - Standards compliant HTML filter written in PHP
* [cerdic/css-tidy](https://github.com/Cerdic/CSSTidy) - CSSTidy is a CSS minifier
* [asgrim/ofxparser](https://github.com/asgrim/ofxparser) - OFX File Parser
* [stacktracejs/stacktrace.js](https://github.com/stacktracejs/stacktrace.js) - Framework-agnostic, micro-library for getting stack traces in all web browsers

View File

@ -989,5 +989,126 @@
'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',
// Expense / vendor
'expense' => 'Expense',
'expenses' => 'Expenses',
'new_expense' => 'Enter Expense',
'enter_expense' => 'Enter Expense',
'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',
// 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' => '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',
// 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',
// recurring due dates
'recurring_due_dates' => 'Recurring Invoice Due Dates',
'recurring_due_date_help' => '<p>Automatically sets a due date for the invoice.</p>
<p>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.</p>
<p>Invoices on a weekly cycle set to be due on the day of the week they are created will be due the next week.</p>
<p>For example:</p>
<ul>
<li>Today is the 15th, due date is 1st of the month. The due date should likely be the 1st of the next month.</li>
<li>Today is the 15th, due date is the last day of the month. The due date will be the last day of the this month.
</li>
<li>Today is the 15th, due date is the 15th day of the month. The due date will be the 15th day of <strong>next</strong> month.
</li>
<li>Today is the Friday, due date is the 1st Friday after. The due date will be next Friday, not today.
</li>
</ul>',
'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',
// 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.',
'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',
'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',
'bank_accounts_help' => 'Connect a bank account to automatically import expenses and create vendors. Supports American Express and <a href="'.OFX_HOME_URL.'" target="_blank">400+ US banks.</a>',
'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_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',
'info' => 'Info',
'imported_expenses' => 'Successfully created :count_vendors vendor(s) and :count_expenses expense(s)',
);

View File

@ -25,7 +25,7 @@ return array(
"numeric" => ":attribute skal være imellem :min - :max.",
"file" => ":attribute skal være imellem :min - :max kilobytes.",
"string" => ":attribute skal være imellem :min - :max tegn.",
"array" => ":attribute skal indeholde mellem :min - :max elementer."
"array" => ":attribute skal indeholde mellem :min - :max elementer.",
),
"boolean" => ":attribute skal være sandt eller falsk",
"confirmed" => ":attribute er ikke det samme som bekræftelsesfeltet.",
@ -44,14 +44,14 @@ return array(
"numeric" => ":attribute skal være højest :max.",
"file" => ":attribute skal være højest :max kilobytes.",
"string" => ":attribute skal være højest :max tegn.",
"array" => ":attribute må ikke indeholde mere end :max elementer."
"array" => ":attribute må ikke indeholde mere end :max elementer.",
),
"mimes" => ":attribute skal være en fil af typen: :values.",
"min" => array(
"numeric" => ":attribute skal være mindst :min.",
"file" => ":attribute skal være mindst :min kilobytes.",
"string" => ":attribute skal være mindst :min tegn.",
"array" => ":attribute skal indeholde mindst :min elementer."
"array" => ":attribute skal indeholde mindst :min elementer.",
),
"not_in" => "Den valgte :attribute er ugyldig.",
"numeric" => ":attribute skal være et tal.",
@ -67,7 +67,7 @@ return array(
"numeric" => ":attribute skal være :size.",
"file" => ":attribute skal være :size kilobytes.",
"string" => ":attribute skal være :size tegn lang.",
"array" => ":attribute skal indeholde :size elementer."
"array" => ":attribute skal indeholde :size elementer.",
),
"timezone" => "The :attribute must be a valid zone.",
"unique" => ":attribute er allerede taget.",

View File

@ -59,7 +59,7 @@ return array(
'learn_more' => 'Mehr erfahren',
'manage_rates' => 'Steuersätze verwalten',
'note_to_client' => 'Notiz an den Kunden',
'invoice_terms' => 'Zahlungsbedingungen',
'invoice_terms' => 'Rechnungsbedingungen',
'save_as_default_terms' => 'Als Standardbedingungen speichern',
'download_pdf' => 'PDF herunterladen',
'pay_now' => 'Jetzt bezahlen',
@ -273,7 +273,6 @@ return array(
'reset_password' => 'Du kannst dein Passwort zurücksetzen, indem du auf den folgenden Link klickst:',
'reset_password_footer' => 'Wenn du das Zurücksetzen des Passworts nicht beantragt hast, benachrichtige bitte unseren Support: '.CONTACT_EMAIL,
// Payment page
'secure_payment' => 'Sichere Zahlung',
'card_number' => 'Kartennummer',
@ -991,4 +990,126 @@ return array(
'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',
// Expense / vendor
'expense' => 'Expense',
'expenses' => 'Expenses',
'new_expense' => 'Enter Expense',
'enter_expense' => 'Enter Expense',
'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',
// 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' => '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',
// 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',
// recurring due dates
'recurring_due_dates' => 'Recurring Invoice Due Dates',
'recurring_due_date_help' => '<p>Automatically sets a due date for the invoice.</p>
<p>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.</p>
<p>Invoices on a weekly cycle set to be due on the day of the week they are created will be due the next week.</p>
<p>For example:</p>
<ul>
<li>Today is the 15th, due date is 1st of the month. The due date should likely be the 1st of the next month.</li>
<li>Today is the 15th, due date is the last day of the month. The due date will be the last day of the this month.
</li>
<li>Today is the 15th, due date is the 15th day of the month. The due date will be the 15th day of <strong>next</strong> month.
</li>
<li>Today is the Friday, due date is the 1st Friday after. The due date will be next Friday, not today.
</li>
</ul>',
'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',
// 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.',
'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',
'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',
'bank_accounts_help' => 'Connect a bank account to automatically import expenses and create vendors. Supports American Express and <a href="'.OFX_HOME_URL.'" target="_blank">400+ US banks.</a>',
'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_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',
'info' => 'Info',
'imported_expenses' => 'Successfully created :count_vendors vendor(s) and :count_expenses expense(s)',
);

View File

@ -25,7 +25,7 @@ return array(
"numeric" => ":attribute muss zwischen :min & :max liegen.",
"file" => ":attribute muss zwischen :min & :max Kilobytes groß sein.",
"string" => ":attribute muss zwischen :min & :max Zeichen lang sein.",
"array" => ":attribute muss zwischen :min & :max Elemente haben."
"array" => ":attribute muss zwischen :min & :max Elemente haben.",
),
"confirmed" => ":attribute stimmt nicht mit der Bestätigung überein.",
"date" => ":attribute muss ein gültiges Datum sein.",
@ -43,14 +43,14 @@ return array(
"numeric" => ":attribute darf maximal :max sein.",
"file" => ":attribute darf maximal :max Kilobytes groß sein.",
"string" => ":attribute darf maximal :max Zeichen haben.",
"array" => ":attribute darf nicht mehr als :max Elemente haben."
"array" => ":attribute darf nicht mehr als :max Elemente haben.",
),
"mimes" => ":attribute muss den Dateityp :values haben.",
"min" => array(
"numeric" => ":attribute muss mindestens :min sein.",
"file" => ":attribute muss mindestens :min Kilobytes groß sein.",
"string" => ":attribute muss mindestens :min Zeichen lang sein.",
"array" => ":attribute muss mindestens :min Elemente haben."
"array" => ":attribute muss mindestens :min Elemente haben.",
),
"not_in" => "Der gewählte Wert für :attribute ist ungültig.",
"numeric" => ":attribute muss eine Zahl sein.",
@ -66,7 +66,7 @@ return array(
"numeric" => ":attribute muss gleich :size sein.",
"file" => ":attribute muss :size Kilobyte groß sein.",
"string" => ":attribute muss :size Zeichen lang sein.",
"array" => ":attribute muss genau :size Elemente haben."
"array" => ":attribute muss genau :size Elemente haben.",
),
"unique" => ":attribute ist schon vergeben.",
"url" => "Das Format von :attribute ist ungültig.",

View File

@ -263,7 +263,6 @@ return array(
'deleted_vendor' => 'Successfully deleted vendor',
'deleted_vendors' => 'Successfully deleted :count vendors',
// Emails
'confirmation_subject' => 'Invoice Ninja Account Confirmation',
'confirmation_header' => 'Account Confirmation',
@ -286,7 +285,6 @@ return array(
'reset_password' => 'You can reset your account password by clicking the following button:',
'reset_password_footer' => 'If you did not request this password reset please email our support: '.CONTACT_EMAIL,
// Payment page
'secure_payment' => 'Secure Payment',
'card_number' => 'Card Number',
@ -1019,7 +1017,7 @@ return array(
'new_expense' => 'Enter Expense',
'enter_expense' => 'Enter Expense',
'vendors' => 'Vendors',
'new_vendor' => 'Create Vendor',
'new_vendor' => 'New Vendor',
'payment_terms_net' => 'Net',
'vendor' => 'Vendor',
'edit_vendor' => 'Edit Vendor',
@ -1037,7 +1035,7 @@ return array(
'expense_date' => 'Expense Date',
'expense_should_be_invoiced' => 'Should this expense be invoiced?',
'public_notes' => 'Public Notes',
'converted_amount' => 'Converted Amount',
'invoice_amount' => 'Invoice Amount',
'exchange_rate' => 'Exchange Rate',
'yes' => 'Yes',
'no' => 'No',
@ -1054,7 +1052,7 @@ return array(
'restore_expense' => 'Restore Expense',
'invoice_expense' => 'Invoice Expense',
'expense_error_multiple_clients' => 'The expenses can\'t belong to different clients',
'expense_error_invoiced' => 'Expense have already been invoiced',
'expense_error_invoiced' => 'Expense has already been invoiced',
'convert_currency' => 'Convert currency',
// Payment terms
@ -1107,6 +1105,8 @@ return array(
'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',
'integration_type' => 'Integration Type',
'updated_bank_account' => 'Successfully updated bank account',
@ -1114,17 +1114,34 @@ return array(
'archive_bank_account' => 'Archive Bank Account',
'archived_bank_account' => 'Successfully archived bank account',
'created_bank_account' => 'Successfully created bank account',
'test' => 'Test',
'test_bank_account' => 'Test Bank Account',
'validate_bank_account' => 'Validate Bank Account',
'bank_accounts_help' => 'Connect a bank account to automatically import expenses and create vendors. Supports American Express and <a href="'.OFX_HOME_URL.'" target="_blank">400+ US banks.</a>',
'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_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',
'info' => 'Info',
'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' => 'first page',
'all_pages' => 'all pages',
'last_page' => 'last 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',
);

View File

@ -966,5 +966,126 @@ return array(
'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',
// Expense / vendor
'expense' => 'Expense',
'expenses' => 'Expenses',
'new_expense' => 'Enter Expense',
'enter_expense' => 'Enter Expense',
'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',
// 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' => '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',
// 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',
// recurring due dates
'recurring_due_dates' => 'Recurring Invoice Due Dates',
'recurring_due_date_help' => '<p>Automatically sets a due date for the invoice.</p>
<p>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.</p>
<p>Invoices on a weekly cycle set to be due on the day of the week they are created will be due the next week.</p>
<p>For example:</p>
<ul>
<li>Today is the 15th, due date is 1st of the month. The due date should likely be the 1st of the next month.</li>
<li>Today is the 15th, due date is the last day of the month. The due date will be the last day of the this month.
</li>
<li>Today is the 15th, due date is the 15th day of the month. The due date will be the 15th day of <strong>next</strong> month.
</li>
<li>Today is the Friday, due date is the 1st Friday after. The due date will be next Friday, not today.
</li>
</ul>',
'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',
// 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.',
'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',
'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',
'bank_accounts_help' => 'Connect a bank account to automatically import expenses and create vendors. Supports American Express and <a href="'.OFX_HOME_URL.'" target="_blank">400+ US banks.</a>',
'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_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',
'info' => 'Info',
'imported_expenses' => 'Successfully created :count_vendors vendor(s) and :count_expenses expense(s)',
);

View File

@ -4,7 +4,7 @@ return array(
// client
'organization' => 'Empresa',
'name' => 'Nombre de Empresa',
'name' => 'Nombre',
'website' => 'Sitio Web',
'work_phone' => 'Teléfono',
'address' => 'Dirección',
@ -38,7 +38,7 @@ return array(
'frequency_id' => 'Frecuencia',
'discount' => 'Descuento',
'taxes' => 'Impuestos',
'tax' => 'IVA',
'tax' => 'Impuesto',
'item' => 'Concepto',
'description' => 'Descripción',
'unit_cost' => 'Coste unitario',
@ -68,11 +68,11 @@ return array(
'delete_invoice' => 'Eliminar factura',
'email_invoice' => 'Enviar factura por email',
'enter_payment' => 'Agregar pago',
'tax_rates' => 'Tasas de impuesto',
'rate' => 'Tasas',
'tax_rates' => 'Tipos impositivos',
'rate' => 'Tipos',
'settings' => 'Configuración',
'enable_invoice_tax' => 'Activar impuesto <b>para la factura</b>',
'enable_line_item_tax' => 'Activar impuesto <b>por concepto</b>',
'enable_invoice_tax' => 'Activar Impuesto para <b>el Total de la Factura</b>',
'enable_line_item_tax' => 'Activar Impuesto para <b>cada Concepto de la Factura</b>',
// navigation
'dashboard' => 'Inicio',
@ -85,7 +85,7 @@ return array(
'sign_up' => 'Registrarse',
'guest' => 'Invitado',
'company_details' => 'Detalles de la Empresa',
'online_payments' => 'Pagos en Linea',
'online_payments' => 'Pagos Online',
'notifications' => 'Notificaciones',
'import_export' => 'Importar/Exportar',
'done' => 'Hecho',
@ -101,14 +101,14 @@ return array(
'no_items' => 'No hay datos',
// recurring invoices
'recurring_invoices' => 'Facturas recurrentes',
'recurring_invoices' => 'Facturas periódicas',
'recurring_help' => '<p>Enviar facturas automáticamente a clientes semanalmente, bi-mensualmente, mensualmente, trimestral o anualmente. </p>
<p>Uso :MONTH, :QUARTER or :YEAR para fechas dinámicas. Matemáticas básicas también funcionan bien. Por ejemplo: :MONTH-1.</p>
<p>Usando :MONTH, :QUARTER or :YEAR para fechas dinámicas. Y utilizando tambien Matemáticas básicas. Por ejemplo: :MONTH-1.</p>
<p>Ejemplos de variables dinámicas de factura:</p>
<ul>
<li>"Afiliación de gimnasio para el mes de :MONTH" => Afiliación de gimnasio para el mes de julio"</li>
<li>":YEAR+1 suscripción anual" => "2015 suscripción anual"</li>
<li>"Retainer payment for :QUARTER+1" => "Pago anticipo de pagos para T2"</li>
<li>"Pago retenido para :QUARTER+1" => "Pago retenido para T2"</li>
</ul>',
// dashboard
@ -139,7 +139,7 @@ return array(
'contact' => 'Contacto',
'date_created' => 'Fecha de Creación',
'last_login' => 'Último Acceso',
'balance' => 'Balance',
'balance' => 'Saldo',
'action' => 'Acción',
'status' => 'Estado',
'invoice_total' => 'Total Facturado',
@ -151,7 +151,7 @@ return array(
'payment_amount' => 'Valor del Pago',
'payment_date' => 'Fecha de Pago',
'credit_amount' => 'Cantidad de Crédito',
'credit_balance' => 'Balance de Crédito',
'credit_balance' => 'Saldo de Crédito',
'credit_date' => 'Fecha de Crédito',
'empty_table' => 'Tabla vacía',
'select' => 'Seleccionar',
@ -199,10 +199,10 @@ return array(
'logo_help' => 'Formatos aceptados: JPEG, GIF y PNG',
'payment_gateway' => 'Pasarela de pago',
'gateway_id' => 'Proveedor',
'email_notifications' => 'Notificaciones de email',
'email_sent' => 'Avísame por email cuando una factura <b>se envía</b>',
'email_viewed' => 'Avísame por email cuando una factura <b>se visualiza</b>',
'email_paid' => 'Avísame por email cuando una factura <b>se paga</b>',
'email_notifications' => 'Notificaciones de correo',
'email_sent' => 'Avísame por correo cuando una factura <b>se envía</b>',
'email_viewed' => 'Avísame por correo cuando una factura <b>se visualiza</b>',
'email_paid' => 'Avísame por correo cuando una factura <b>se paga</b>',
'site_updates' => 'Actualizaciones del sitio',
'custom_messages' => 'Mensajes a medida',
'default_invoice_terms' => 'Configurar términos de factura por defecto',
@ -320,7 +320,7 @@ return array(
'pro_plan_description' => 'Un año de inscripción al Plan Pro de Invoice Ninja.',
'pro_plan_success' => '¡Gracias por unirte a Invoice Ninja! Al realizar el pago de tu factura, se iniciara tu PLAN PRO.',
'unsaved_changes' => 'Tienes cambios no guardados',
'custom_fields' => 'Campos a medida',
'custom_fields' => 'Campos personalizados',
'company_fields' => 'Campos de la empresa',
'client_fields' => 'Campos del cliente',
'field_label' => 'Etiqueta del campo',
@ -401,7 +401,7 @@ return array(
'hide_paid_to_date' => 'Ocultar valor pagado a la fecha',
'hide_paid_to_date_help' => 'Solo mostrar la opción “Pagado a la fecha” en sus facturas cuando se ha recibido un pago.',
'charge_taxes' => 'Cargar Impuestos',
'user_management' => 'Gestión de Usuario',
'user_management' => 'Gestión de Usuarios',
'add_user' => 'Añadir Usuario',
'send_invite' => 'Enviar Invitación', //Context for its use
'sent_invite' => 'Invitación enviada con éxito',
@ -511,7 +511,7 @@ return array(
'token_billing_4' => 'Siempre',
'token_billing_checkbox' => 'Almacenar datos de la tarjeta de crédito',
'view_in_stripe' => 'Ver en Stripe',
'use_card_on_file' => 'Use card on file', //??
'use_card_on_file' => 'Usar tarjeta en fichero', //??
'edit_payment_details' => 'Editar detalles de pago',
'token_billing' => 'Guardar datos de la tarjeta',
'token_billing_secure' => 'Los datos serán almacenados de forma segura por :stripe_link',
@ -571,108 +571,107 @@ return array(
'converted' => 'Modificada',
//------Texto extraido -----------------------------------------------------------------------------------------
'<i>Manual entry</i>' => '<i>Entrada Manual</i>',
// Error
'Whoops, looks like something went wrong.' => 'Vaya, parece que algo salió mal',
'Sorry, the page you are looking for could not be found.' => 'Lo sentimos, la página que está buscando no se pudo encontrar.',
'email_approved' => 'Email me when a quote is <b>approved</b>',
'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',
'email_approved' => 'Avísame por correo cuando un presupuesto sea <b>aprobado</b>',
'notification_quote_approved_subject' => 'Presupuesto :invoice fue aprobado por :client',
'notification_quote_approved' => 'El cliente :client aprovó el presupuesto :invoice por :amount.',
'resend_confirmation' => 'Reenviar correo de confirmacion',
'confirmation_resent' => 'El correo de confirmación fué reenviado',
'gateway_help_42' => ':link to sign up for BitPay.<br/>Note: use a Legacy API Key, not an API token.',
'payment_type_credit_card' => 'Credit card',
'payment_type_credit_card' => 'Tarjeta de crédito',
'payment_type_paypal' => 'PayPal',
'payment_type_bitcoin' => 'Bitcoin',
'knowledge_base' => 'Knowledge Base',
'partial' => 'Partial',
'partial_remaining' => ':partial of :balance',
'knowledge_base' => 'Ayuda',
'partial' => 'Parcial',
'partial_remaining' => ':partial de :balance',
'more_fields' => 'More Fields',
'less_fields' => 'Less Fields',
'client_name' => 'Client Name',
'pdf_settings' => 'PDF Settings',
'product_settings' => 'Product Settings',
'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',
'more_fields' => 'Mas campos',
'less_fields' => 'Menos campos',
'client_name' => 'Nombre del cliente',
'pdf_settings' => 'Configuracion PDF',
'product_settings' => 'Configuracion de Producto',
'auto_wrap' => 'Auto ajuste de línea',
'duplicate_post' => 'Atencion: la pagina anterior se ha enviado dos veces. El segundo envío será ignorado.',
'view_documentation' => 'Ver documentación',
'app_title' => 'Facturacion Online Open-Source gratuita',
'app_description' => 'Invoice Ninja is a free, open-source solution for invoicing and billing customers. With Invoice Ninja, you can easily build and send beautiful invoices from any device that has access to the web. Your clients can print your invoices, download them as pdf files, and even pay you online from within the system.',
'rows' => 'rows',
'rows' => 'filas',
'www' => 'www',
'logo' => 'Logo',
'subdomain' => 'Subdomain',
'provide_name_or_email' => 'Please provide a contact name or email',
'charts_and_reports' => 'Charts & Reports',
'chart' => 'Chart',
'report' => 'Report',
'group_by' => 'Group by',
'subdomain' => 'Subdominio',
'provide_name_or_email' => 'Por favor, indique un nombre o correo de contacto',
'charts_and_reports' => 'Gráficos e Informes',
'chart' => 'gráfico',
'report' => 'informe',
'group_by' => 'Agrupar por',
'paid' => 'Paid',
'enable_report' => 'Report',
'enable_chart' => 'Chart',
'totals' => 'Totals',
'run' => 'Run',
'export' => 'Export',
'documentation' => 'Documentation',
'enable_report' => 'Informe',
'enable_chart' => 'Gráfico',
'totals' => 'Totales',
'run' => 'Ejecutar',
'export' => 'Exportar',
'documentation' => 'Documentación',
'zapier' => 'Zapier',
'recurring' => 'Recurring',
'last_invoice_sent' => 'Last invoice sent :date',
'recurring' => 'Periódico',
'last_invoice_sent' => 'Última factura enviada el :date',
'processed_updates' => 'Successfully completed update',
'tasks' => 'Tasks',
'new_task' => 'New Task',
'start_time' => 'Start Time',
'created_task' => 'Successfully created task',
'updated_task' => 'Successfully updated task',
'edit_task' => 'Edit Task',
'archive_task' => 'Archive Task',
'restore_task' => 'Restore Task',
'delete_task' => 'Delete Task',
'stop_task' => 'Stop Task',
'time' => 'Time',
'start' => 'Start',
'stop' => 'Stop',
'now' => 'Now',
'timer' => 'Timer',
'processed_updates' => 'Actualización correcta',
'tasks' => 'Tareas',
'new_task' => 'Nueva tarea',
'start_time' => 'Hora de Inicio',
'created_task' => 'Tarea creada correctamente',
'updated_task' => 'Tarea actualizada correctamente',
'edit_task' => 'Editar Tarea',
'archive_task' => 'Archivar Tarea',
'restore_task' => 'Restaurar Tarea',
'delete_task' => 'Borrar Tarea',
'stop_task' => 'Parar Tarea',
'time' => 'Hora',
'start' => 'Iniciar',
'stop' => 'Parar',
'now' => 'Ahora',
'timer' => 'Temporizador',
'manual' => 'Manual',
'date_and_time' => 'Date & Time',
'second' => 'second',
'seconds' => 'seconds',
'minute' => 'minute',
'minutes' => 'minutes',
'hour' => 'hour',
'hours' => 'hours',
'task_details' => 'Task Details',
'duration' => 'Duration',
'end_time' => 'End Time',
'end' => 'End',
'invoiced' => 'Invoiced',
'logged' => 'Logged',
'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',
'date_and_time' => 'Día y hora',
'second' => 'segundo',
'seconds' => 'segundos',
'minute' => 'minuto',
'minutes' => 'minutos',
'hour' => 'hora',
'hours' => 'horas',
'task_details' => 'Detalles de Tarea',
'duration' => 'Duración',
'end_time' => 'Hora de Fin',
'end' => 'Fin',
'invoiced' => 'Facturado',
'logged' => 'Conectado',
'running' => 'Ejecutando',
'task_error_multiple_clients' => 'Las tareas no pueden pertenecer a diferentes clientes',
'task_error_running' => 'Por favor, para las tareas ejecutandose primero',
'task_error_invoiced' => 'Las tareas ya se han facturado',
'restored_task' => 'Tarea restaurada correctamente',
'archived_task' => 'Tarea archivada correctamente',
'archived_tasks' => ':count tareas archivadas correctamente',
'deleted_task' => 'Tarea borrada correctamente',
'deleted_tasks' => ':count tareas borradas correctamente',
'create_task' => 'Crear Tarea',
'stopped_task' => 'Tarea parada correctamente',
'invoice_task' => 'Tarea de factura',
'invoice_labels' => 'Etiquetas de factura',
'prefix' => 'Prefijo',
'counter' => 'Contador',
'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',
'partial_value' => 'Debe ser mayor que 0 y menos que el total',
'more_actions' => 'Mas Acciones',
'pro_plan_title' => 'NINJA PRO',
'pro_plan_call_to_action' => 'Upgrade Now!',
@ -685,307 +684,430 @@ return array(
'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',
'resume' => 'Reanudar',
'break_duration' => 'Interrumpir',
'edit_details' => 'Editar detalles',
'work' => 'Trabajo',
'timezone_unset' => 'Pro favor :link para especificar su timezone',
'click_here' => 'pulse aqui',
'email_receipt' => 'Email payment receipt to the client',
'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',
'email_receipt' => 'Enviar recibo de pago al cliente',
'created_payment_emailed_client' => 'Pago creado y enviado al cliente correctamente',
'add_company' => 'Añadir compañía',
'untitled' => 'Sin Título',
'new_company' => 'Nueva compañía',
'associated_accounts' => 'Vínculo de cuentas correcta',
'unlinked_account' => 'Desvínculo de cuentas correcta',
'login' => 'Login',
'or' => 'or',
'or' => 'o',
'email_error' => 'There was a problem sending the email',
'confirm_recurring_timing' => 'Note: emails are sent at the start of the hour.',
'old_browser' => 'Please use a <a href="'.OUTDATE_BROWSER_URL.'" target="_blank">newer browser</a>',
'payment_terms_help' => 'Sets the default invoice due date',
'unlink_account' => 'Unlink Account',
'unlink' => 'Unlink',
'show_address' => 'Show Address',
'show_address_help' => 'Require client to provide their billing address',
'update_address' => 'Update Address',
'update_address_help' => 'Update client\'s address with provided details',
'times' => 'Times',
'set_now' => 'Set now',
'dark_mode' => 'Dark Mode',
'dark_mode_help' => 'Show white text on black background',
'add_to_invoice' => 'Add to invoice :invoice',
'create_new_invoice' => 'Create new invoice',
'task_errors' => 'Please correct any overlapping times',
'from' => 'From',
'to' => 'To',
'font_size' => 'Font Size',
'primary_color' => 'Primary Color',
'secondary_color' => 'Secondary Color',
'customize_design' => 'Customize Design',
'email_error' => 'Ocurrió un problema enviando el correo',
'confirm_recurring_timing' => 'Nota: correos enviados cada hora en punto.',
'old_browser' => 'Por favor use un <a href="'.OUTDATE_BROWSER_URL.'" target="_blank">navegador mas actual</a>',
'payment_terms_help' => 'Establezca la fecha de pago de factura por defecto',
'unlink_account' => 'Cuenta desvinculada',
'unlink' => 'Desvincular',
'show_address' => 'Mostrar Dirección',
'show_address_help' => 'Cliente obligatorio para utilizar su dirección de facturación',
'update_address' => 'Actualizar dirección',
'update_address_help' => 'Actualizar la direccion del cliente con los datos provistos',
'times' => 'Tiempos',
'set_now' => 'Establecer ahora',
'dark_mode' => 'Modo Oscuro',
'dark_mode_help' => 'Muestra el texto blanco sobre fondo negro',
'add_to_invoice' => 'Añadir a la factura :invoice',
'create_new_invoice' => 'Crear una nueva Factura',
'task_errors' => 'Por favor corrija los tiempos solapados',
'from' => 'De',
'to' => 'Para',
'font_size' => 'Tamaño de Letra',
'primary_color' => 'Color Primario',
'secondary_color' => 'Color Secundario',
'customize_design' => 'Personalizar diseño',
'content' => 'Content',
'styles' => 'Styles',
'defaults' => 'Defaults',
'margins' => 'Margins',
'header' => 'Header',
'footer' => 'Footer',
'custom' => 'Custom',
'invoice_to' => 'Invoice to',
'invoice_no' => 'Invoice No.',
'recent_payments' => 'Recent Payments',
'outstanding' => 'Outstanding',
'manage_companies' => 'Manage Companies',
'total_revenue' => 'Total Revenue',
'content' => 'Contenido',
'styles' => 'Estilos',
'defaults' => 'Por defectos',
'margins' => 'Margenes',
'header' => 'Cabecera',
'footer' => 'Pie',
'custom' => 'Personalizado',
'invoice_to' => 'Factura para',
'invoice_no' => 'N.º Factura',
'recent_payments' => 'Pagos recientes',
'outstanding' => 'Pendiente de Cobro',
'manage_companies' => 'Gestionar compañías',
'total_revenue' => 'Ingresos Totales',
'current_user' => 'Current User',
'new_recurring_invoice' => 'New Recurring Invoice',
'recurring_invoice' => 'Recurring Invoice',
'recurring_too_soon' => 'It\'s too soon to create the next recurring invoice, it\'s scheduled for :date',
'created_by_invoice' => 'Created by :invoice',
'primary_user' => 'Primary User',
'help' => 'Help',
'current_user' => 'Usuario Actual',
'new_recurring_invoice' => 'Nueva Factura Periódica',
'recurring_invoice' => 'Factura Periódica',
'recurring_too_soon' => 'Es demasiado pronto para crear la siguiente factura periodica, esta programada para :date',
'created_by_invoice' => 'Creado por :invoice',
'primary_user' => 'Usuario Principal',
'help' => 'Ayuda',
'customize_help' => '<p>We use <a href="http://pdfmake.org/" target="_blank">pdfmake</a> to define the invoice designs declaratively. The pdfmake <a href="http://pdfmake.org/playground.html" target="_blank">playground</a> provide\'s a great way to see the library in action.</p>
<p>You can access any invoice field by adding <code>Value</code> to the end. For example <code>$invoiceNumberValue</code> displays the invoice number.</p>
<p>To access a child property using dot notation. For example to show the client name you could use <code>$client.nameValue</code>.</p>
<p>If you need help figuring something out post a question to our <a href="https://www.invoiceninja.com/forums/forum/support/" target="_blank">support forum</a>.</p>',
'invoice_due_date' => 'Due Date',
'quote_due_date' => 'Valid Until',
'valid_until' => 'Valid Until',
'reset_terms' => 'Reset terms',
'reset_footer' => 'Reset footer',
'invoices_sent' => ':count invoice sent|:count invoices sent',
'status_draft' => 'Draft',
'status_sent' => 'Sent',
'status_viewed' => 'Viewed',
'status_partial' => 'Partial',
'status_paid' => 'Paid',
'show_line_item_tax' => 'Display <b>line item taxes</b> inline',
'invoice_due_date' => 'Fecha de Pago',
'quote_due_date' => 'Válido hasta',
'valid_until' => 'Válido hasta',
'reset_terms' => 'Reiniciar terminos',
'reset_footer' => 'Reiniciar pie',
'invoices_sent' => ':count factura enviada|:count facturas enviadas',
'status_draft' => 'Borrador',
'status_sent' => 'Enviada',
'status_viewed' => 'Vista',
'status_partial' => 'Parcial',
'status_paid' => 'Pagada',
'show_line_item_tax' => 'Mostrar <b>importe por concepto</b> en la linea de concepto',
'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.',
'iframe_url_help1' => 'Copia el siguiente codigo para una pagina de si sitio web.',
'iframe_url_help2' => 'Puedes probar esta caracteristica pulsando \'Ver como destinatario\' para una Factura.',
'auto_bill' => 'Auto Bill',
'military_time' => '24 Hour Time',
'last_sent' => 'Last Sent',
'auto_bill' => 'Facturación automática',
'military_time' => '24 Horas',
'last_sent' => 'Última enviada',
'reminder_emails' => 'Reminder Emails',
'templates_and_reminders' => 'Templates & Reminders',
'subject' => 'Subject',
'body' => 'Body',
'first_reminder' => 'First Reminder',
'second_reminder' => 'Second Reminder',
'third_reminder' => 'Third Reminder',
'num_days_reminder' => 'Days after due date',
'reminder_subject' => 'Reminder: Invoice :invoice from :account',
'reset' => 'Reset',
'invoice_not_found' => 'The requested invoice is not available',
'reminder_emails' => 'Correos recordatorio',
'templates_and_reminders' => 'Plantillas & Recordatorios',
'subject' => 'Asunto',
'body' => 'Cuerpo',
'first_reminder' => 'Primer Recordatorio',
'second_reminder' => 'Segundo Recordatorio',
'third_reminder' => 'Tercer Recordatorio',
'num_days_reminder' => 'Dias despues de la fecha de pago',
'reminder_subject' => 'Recordatorio: Factura :invoice de :account',
'reset' => 'Reiniciar',
'invoice_not_found' => 'La Factura solicitara no está disponible',
'referral_program' => 'Referral Program',
'referral_code' => 'Referral Code',
'last_sent_on' => 'Last sent on :date',
'referral_program' => 'Programa de Recomendaciones',
'referral_code' => 'Codigo de Recomendacion',
'last_sent_on' => 'Enviado la ultima vez en :date',
'page_expire' => 'This page will expire soon, :click_here to keep working',
'upcoming_quotes' => 'Upcoming Quotes',
'expired_quotes' => 'Expired Quotes',
'page_expire' => 'Esta página expirará en breve, :click_here para seguir trabajando',
'upcoming_quotes' => 'Próximos Presupuestos',
'expired_quotes' => 'Presupuestos Expirados',
'sign_up_using' => 'Sign up using',
'invalid_credentials' => 'These credentials do not match our records',
'show_all_options' => 'Show all options',
'user_details' => 'User Details',
'sign_up_using' => 'Registrarse usando',
'invalid_credentials' => 'Las credenciales no coinciden con nuestros registros',
'show_all_options' => 'Mostrar todas las opciones',
'user_details' => 'Detalles de Usuario',
'oneclick_login' => 'One-Click Login',
'disable' => 'Disable',
'invoice_quote_number' => 'Invoice and Quote Numbers',
'invoice_charges' => 'Invoice Charges',
'disable' => 'Deshabilitado',
'invoice_quote_number' => 'Números de Factura y Presupuesto',
'invoice_charges' => 'Cargos de factura',
'invitation_status' => [
'sent' => 'Email Sent',
'opened' => 'Email Openend',
'viewed' => 'Invoice Viewed',
'sent' => 'Correo Enviado',
'opened' => 'Correo abierto',
'viewed' => 'Factura vista',
],
'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',
'notification_invoice_bounced' => 'No podemos entregar la factura :invoice a :contact.',
'notification_invoice_bounced_subject' => 'No se puede entregar la factura :invoice',
'notification_quote_bounced' => 'No podemos entregar el presupuesto :invoice a :contact.',
'notification_quote_bounced_subject' => 'No se puede entregar el presupuesto :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' => 'Basic Settings',
'custom_invoice_link' => 'Enlace a factura personalizado',
'total_invoiced' => 'Total facturado',
'open_balance' => 'Saldo abierto',
'verify_email' => 'Por favor, visite el enlace en el correo de confirmación de cuentapara verificar su correo.',
'basic_settings' => 'Configuraciones Básicas',
'pro' => 'Pro',
'gateways' => 'Payment Gateways',
'gateways' => 'Pasarelas de Pago',
'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',
'next_send_on' => 'Próximo envío: :date',
'no_longer_running' => 'Esta factura no esta programada para su ejecución',
'general_settings' => 'Configuraciones Generales',
'customize' => 'Personalizar',
'oneclick_login_help' => 'Conectar una cuenta a un login sin password',
'referral_code_help' => 'Gana dinero compartiendo nuestras aplicacion online',
'enable_with_stripe' => 'Enable | Requires Stripe',
'tax_settings' => 'Tax Settings',
'create_tax_rate' => 'Add Tax Rate',
'updated_tax_rate' => 'Successfully updated tax rate',
'created_tax_rate' => 'Successfully created tax rate',
'edit_tax_rate' => 'Edit tax rate',
'archive_tax_rate' => 'Archive tax rate',
'archived_tax_rate' => 'Successfully archived the tax rate',
'default_tax_rate_id' => 'Default Tax Rate',
'tax_rate' => 'Tax Rate',
'recurring_hour' => 'Recurring Hour',
'pattern' => 'Pattern',
'pattern_help_title' => 'Pattern Help',
'pattern_help_1' => 'Create custom invoice and quote numbers by specifying a pattern',
'pattern_help_2' => 'Available variables:',
'pattern_help_3' => 'For example, :example would be converted to :value',
'see_options' => 'See options',
'invoice_counter' => 'Invoice Counter',
'quote_counter' => 'Quote Counter',
'type' => 'Type',
'enable_with_stripe' => 'Habilitado | Requiere Stripe',
'tax_settings' => 'Configuraciones de Impuestos',
'create_tax_rate' => 'Añadir Tipo Impositivo',
'updated_tax_rate' => 'Tipo Impositivo actualizado',
'created_tax_rate' => 'Tipo Impositivo creado',
'edit_tax_rate' => 'Editar Tipo Impositivo',
'archive_tax_rate' => 'Archivar Tipo Impositivo',
'archived_tax_rate' => 'Tipo Impositivo archivado',
'default_tax_rate_id' => 'Tipo Impositivo por defecto',
'tax_rate' => 'Tipo Impositivo',
'recurring_hour' => 'Hora Periodica',
'pattern' => 'Patrn',
'pattern_help_title' => 'Ayuda',
'pattern_help_1' => 'Crear números de factura y de presupuesto personalizados mediante un patron',
'pattern_help_2' => 'Variables disponibles:',
'pattern_help_3' => 'Por ejemplo, :example será convertido a :value',
'see_options' => 'Ver opciones',
'invoice_counter' => 'Contador de Factura',
'quote_counter' => 'Contador de Presupuesto',
'type' => 'Tipo',
'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_1' => ':user creó el cliente :client',
'activity_2' => ':user archivó el cliente :client',
'activity_3' => ':user borró el cliente :client',
'activity_4' => ':user archivó la factura :invoice',
'activity_5' => ':user actualizó la factura :invoice',
'activity_6' => ':user envió la factura :invoice to :contact',
'activity_7' => ':contact vió la factura :invoice',
'activity_8' => ':user archivó la factura :invoice',
'activity_9' => ':user borró la factura :invoice',
'activity_10' => ':contact introdujo el pago :payment para :invoice',
'activity_11' => ':user actualizó el pago :payment',
'activity_12' => ':user archivó el pago :payment',
'activity_13' => ':user borró el pago :payment',
'activity_14' => ':user introdujo :credit credito',
'activity_15' => ':user actualizó :credit credito',
'activity_16' => ':user archivó :credit credito',
'activity_17' => ':user deleted :credit credito',
'activity_18' => ':user borró presupuesto :quote',
'activity_19' => ':user actualizó presupuesto :quote',
'activity_20' => ':user envió presupuesto :quote to :contact',
'activity_21' => ':contact vió presupuesto :quote',
'activity_22' => ':user archivó presupuesto :quote',
'activity_23' => ':user borró presupuesto :quote',
'activity_24' => ':user restauró presupuesto :quote',
'activity_25' => ':user restauró la factura :invoice',
'activity_26' => ':user restauró el cliente :client',
'activity_27' => ':user restauró el pago :payment',
'activity_28' => ':user restauró :credit credito',
'activity_29' => ':contact approved presupuesto :quote',
'payment' => 'Payment',
'system' => 'System',
'signature' => 'Email Signature',
'default_messages' => 'Default Messages',
'quote_terms' => 'Quote Terms',
'default_quote_terms' => 'Default Quote Terms',
'default_invoice_terms' => 'Default Invoice Terms',
'default_invoice_footer' => 'Default Invoice Footer',
'quote_footer' => 'Quote Footer',
'free' => 'Free',
'payment' => 'Pago',
'system' => 'Sistema',
'signature' => 'Firma del correo',
'default_messages' => 'Mensajes por defecto',
'quote_terms' => 'Terminos del presupuesto',
'default_quote_terms' => 'Terminos del presupuesto por defecto',
'default_invoice_terms' => 'Terminos de la factura por defecto',
'default_invoice_footer' => 'Pie de la factura por defecto',
'quote_footer' => 'Pie del presupuesto',
'free' => 'Gratis',
'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' => 'Archive Recurring Invoice',
'archived_recurring_invoice' => 'Successfully archived recurring invoice',
'delete_recurring_invoice' => 'Delete Recurring Invoice',
'deleted_recurring_invoice' => 'Successfully deleted recurring invoice',
'restore_recurring_invoice' => 'Restore Recurring Invoice',
'restored_recurring_invoice' => 'Successfully restored recurring invoice',
'archived' => 'Archived',
'untitled_account' => 'Untitled Company',
'quote_is_approved' => 'Este presupuesto esta aprovado',
'apply_credit' => 'Aplicar Crédito',
'system_settings' => 'Configuración del Sistema',
'archive_token' => 'Archivar Token',
'archived_token' => 'Token archivado',
'archive_user' => 'Archivar Usuario',
'archived_user' => 'Usuario archivado',
'archive_account_gateway' => 'Archivar Pasarela',
'archived_account_gateway' => 'Pasarela archivada',
'archive_recurring_invoice' => 'Archivar Factura periódica',
'archived_recurring_invoice' => 'Factura periódica archivada',
'delete_recurring_invoice' => 'Borrar Factura periódica',
'deleted_recurring_invoice' => 'Factura periódica borrada',
'restore_recurring_invoice' => 'Restaurar Factura periódica ',
'restored_recurring_invoice' => 'Factura periódica restaurada',
'archived' => 'Archivado',
'untitled_account' => 'Compañía sin Nombre',
'before' => 'Before',
'after' => 'After',
'before' => 'Antes',
'after' => 'Despues',
'reset_terms_help' => 'Reset to the default account terms',
'reset_footer_help' => 'Reset to the default account footer',
'export_data' => 'Export Data',
'user' => 'User',
'country' => 'Country',
'include' => 'Include',
'export_data' => 'Exportar',
'user' => 'Usuario',
'country' => 'Pais',
'include' => 'Incluir',
'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' => 'El logo es :size, para un buen rendimiento del PDF, sugerimos cargar una imagen de menos de 200KB',
'import_freshbooks' => 'Importar desde FreshBooks',
'import_data' => 'Importar datos',
'source' => 'Origen',
'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' => 'Fichero de Clientes',
'invoice_file' => 'Fichero de Facturas',
'task_file' => 'Fichero de Tareas',
'no_mapper' => 'Mapeo no válido para el fichero',
'invalid_csv_header' => 'Cabecera CSV no Válida',
'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' => 'No se pueden enviar correos a Clientes inactivos',
'inactive_contact' => 'No se pueden enviar correos a Contactos inactivos',
'inactive_invoice' => 'No se pueden enviar correos de Facturas inactivas',
'user_unregistered' => 'Por favor registra tu cuenta para enviar correos',
'user_unconfirmed' => 'Por favor confirma tu cuenta para enviar correos',
'invalid_contact_email' => 'Correo de contacto no válido',
],
'client_portal' => 'Client Portal',
'admin' => 'Admin',
'disabled' => 'Disabled',
'show_archived_users' => 'Show archived users',
'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',
'client_portal' => 'Portal Cliente',
'admin' => 'Admin.',
'disabled' => 'Deshabilitado',
'show_archived_users' => 'Mostrar usuarios archivados',
'notes' => 'Notas',
'invoice_will_create' => 'cliente será creado',
'invoices_will_create' => 'facturas seran creadas',
'failed_to_import' => 'Los siguientes registros fallaron al importar',
'publishable_key' => 'Publishable Key',
'secret_key' => 'Secret Key',
'missing_publishable_key' => 'Set your Stripe publishable key for an improved checkout process',
'publishable_key' => 'Clave pública',
'secret_key' => 'Clave Privada',
'missing_publishable_key' => 'Establece tu clacve publica de Stripe para mejorar el proceso de checkout',
'email_design' => 'Email Design',
'due_by' => 'Due by :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',
'email_design' => 'Diseño de Correo',
'due_by' => 'Pagado en :date',
'enable_email_markup' => 'Habilitar Markup',
'enable_email_markup_help' => 'Haga que sea fácil para sus clientes que paguen mediante la adición de marcas "schema.org" a sus correos electrónicos.',
'template_help_title' => 'Ayuda',
'template_help_1' => 'Variables disponibles:',
'email_design_id' => 'Estilo de Correo',
'email_design_help' => 'Haga que sus mensajes de correo electrónico tengan un aspecto más profesional con diseños HTML',
'plain' => 'Plano',
'light' => 'Claro',
'dark' => 'Oscuro',
'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 text input to the client create/edit page 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 text input to the invoice create/edit page and display the label and value on the PDF.',
'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.',
'industry_help' => 'Usado para proporcionar comparaciones de las medias de las empresas de tamaño y industria similar.',
'subdomain_help' => 'Personaliza el subdominio para el enlace de la factura o mostrar la factura en su propio sitio web.',
'invoice_number_help' => 'Especifique un prefijo o utilice un patrón a medida para establecer dinámicamente el número de factura.',
'quote_number_help' => 'Especifique un prefijo o utilice un patrón a medida para establecer dinámicamente el número de presupuesto.',
'custom_client_fields_helps' => 'Agregar una entrada de texto a la pagina de crear/editar cliente y mostrar la etiqueta y el valor en el PDF.',
'custom_account_fields_helps' => 'Añadir una etiqueta y valor a la sección de detalles de la empresa del PDF.',
'custom_invoice_fields_helps' => 'Agregar una entrada de texto a la pagina de crear/editar factura y mostrar la etiqueta y el valor en el PDF.',
'custom_invoice_charges_helps' => 'gregar una entrada de texto a la pagina de crear/editar factura y mostrar la carga en los subtotales de la factura.',
'color_help' => 'Nota: el color primario también se utiliza en el portal del cliente y los diseños de correo electrónico personalizado.',
'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' => 'Token de validación ha caducado. Por favor, vuelva a intentarlo.',
'invoice_link' => 'Enlace a Factura',
'button_confirmation_message' => 'Pulse aqui para confirmar su dirección de correo.',
'confirm' => 'Confirmar',
'email_preferences' => 'Preferencias de Correo',
'created_invoices' => ':count factura(s) creada(s) correctamente',
'next_invoice_number' => 'El próximo número de factura es :number.',
'next_quote_number' => 'El próximo número de presupuesto es :number.',
'days_before' => 'days before',
'days_after' => 'days after',
'field_due_date' => 'due date',
'field_invoice_date' => 'invoice date',
'schedule' => 'Schedule',
'email_designs' => 'Email Designs',
'assigned_when_sent' => 'Assigned when sent',
'days_before' => 'dias antes',
'days_after' => 'dias despues',
'field_due_date' => 'fecha de pago',
'field_invoice_date' => 'fecha de factura',
'schedule' => 'Programar',
'email_designs' => 'Diseños de correo',
'assigned_when_sent' => 'Asignado al enviar',
'white_label_custom_css' => ':link para $'.WHITE_LABEL_PRICE.' to enable custom styling and help support our project.',
'white_label_purchase_link' => 'Comprar licencia de marca blanca',
// Expense / vendor
'expense' => 'Gasto',
'expenses' => 'Gastos',
'new_expense' => 'Nuevo Gasto',
'enter_expense' => 'Introducir Gasto',
'vendors' => 'Proveedores',
'new_vendor' => 'Nuevo Proveedor',
'payment_terms_net' => 'Net',
'vendor' => 'Proveedor',
'edit_vendor' => 'Editar Proveedor',
'archive_vendor' => 'Archivar Proveedor',
'delete_vendor' => 'Borrar Proveedor',
'view_vendor' => 'Ver Proveedor',
'deleted_expense' => 'Gasto borrado correctamente',
'archived_expense' => 'Gasto archivado correctamente',
'deleted_expenses' => 'Gastos borrados correctamente',
'archived_expenses' => 'Gastos archivados correctamente',
// Expenses
'expense_amount' => 'Importe del Gasto',
'expense_balance' => 'Saldo del Gasto',
'expense_date' => 'Fecha del Gasto',
'expense_should_be_invoiced' => '¿Este gasto debe ser facturado?',
'public_notes' => 'Notas',
'invoice_amount' => 'Importe de Factura',
'exchange_rate' => 'Tipo de Cambio',
'yes' => 'Si',
'no' => 'No',
'should_be_invoiced' => 'Debe ser Facturado',
'view_expense' => 'Ver gasto # :expense',
'edit_expense' => 'Editar Gasto',
'archive_expense' => 'Archivar Gasto',
'delete_expense' => 'Borrar Gasto',
'view_expense_num' => 'Gasto # :expense',
'updated_expense' => 'Gasto actualizado correctamente',
'created_expense' => 'Gasto creado correctamente',
'enter_expense' => 'Introducir Gasto',
'view' => 'Ver',
'restore_expense' => 'Restaurar Gasto',
'invoice_expense' => 'Facturar Gasto',
'expense_error_multiple_clients' => 'Los gastos no pertenecen a diferentes clientes',
'expense_error_invoiced' => 'El gasto ya ha sido Facturado',
'convert_currency' => 'Convertir moneda',
// Payment terms
'num_days' => 'Número de días',
'create_payment_term' => 'Crear Término de Pago',
'edit_payment_terms' => 'Editar los Términos de Pago',
'edit_payment_term' => 'Editar el Término de Pago',
'archive_payment_term' => 'Archivar Término de Pago',
// recurring due dates
'recurring_due_dates' => 'Fecha de Vencimiento de Factura Periodica',
'recurring_due_date_help' => '<p>Define automáticamente una fecha de vencimiento de la factura..</p>
<p>Facturas en un ciclo mensual o anual preparadas para vencer el día que se crearon o antes, vencerán al siguiente mes. Facturas preparadas para vencer el 29 o el 30, en los meses que no tienen ese día, vencerán el último día del mes</p>
<p>(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.)</p>
<p>Facturas en un ciclo semanal preparadas para vencer el día de la semana que se crearon, vencerán a la siguiente semana.</p>
<p>(Invoices on a weekly cycle set to be due on the day of the week they are created will be due the next week.)</p>
<p>Por ejemplo:</p>
<ul>
<li>Hoy es día 15, la fecha de vencimiento es el día 1 de cada mes. La fecha de vencimiento deberá ser el día 1 del siguiente mes.</li>
<li>Hoy es día 15, la fecha de vencimiento es el último día de cada mes. La fecha de vencimiento deberá ser el último día de este mes.</li>
<li>Hoy es día 15, la fecha de vencimiento es el día 15 de cada mes. La fecha de vencimiento deberá ser el día 15 del <strong>siguiente</strong> mes.</li>
<li>Hoy es Viernes, la fecha de vencimiento es Viernes. La fecha de vencimiento deberá ser el siguiente Viernes, no hoy.</li>
</ul>',
'due' => 'Vencimiento',
'next_due_on' => 'Próximo Vencimiento: :date',
'use_client_terms' => 'Utilizar términos del cliente',
'day_of_month' => ':ordinal día del mes',
'last_day_of_month' => 'Último día del mes',
'day_of_week_after' => ':ordinal :day despues',
'sunday' => 'Domingo',
'monday' => 'Lunes',
'tuesday' => 'Martes',
'wednesday' => 'Miercoles',
'thursday' => 'Jueves',
'friday' => 'Viernes',
'saturday' => 'Sabado',
// Fonts
'header_font_id' => 'Tipo de letra de la cabecera',
'body_font_id' => 'Tipo de letra del cuerpo',
'color_font_help' => 'Nota: el color primario y las fuentes también se utilizan en el portal del cliente y los diseños de correo electrónico personalizados.',
'live_preview' => 'Live Preview',
'invalid_mail_config' => 'Imposible enviar el correo, por favor, comprobar que la configuración de correo es correcta..',
'invoice_message_button' => 'Para ver su factura con importe :amount, pulse el siguiente botón.',
'quote_message_button' => 'Para ver su presupuesto con importe :amount, pulse el siguiente botón.',
'payment_message_button' => 'Gracias por su pago de :amount.',
'payment_type_direct_debit' => 'Débito directo',
'bank_accounts' => 'Cuentas Bancarias',
'add_bank_account' => 'Añadir Cuenta Bancaria',
'setup_account' => 'Configurar Cuenta',
'import_expenses' => 'Importar Gastos',
'bank_id' => 'banco',
'integration_type' => 'Tipo de Integración',
'updated_bank_account' => 'Cuenta Bancaria actualizada correctamente',
'edit_bank_account' => 'Editar Cuenta Bancaria',
'archive_bank_account' => 'Archivar Cuenta Bancaria',
'archived_bank_account' => 'Cuenta Bancaria archivada correctamente',
'created_bank_account' => 'Cuenta Bancaria creada correctamente',
'validate_bank_account' => 'Validar Cuenta Bancaria',
'bank_accounts_help' => 'Connect a bank account to automatically import expenses and create vendors. Supports American Express and <a href="'.OFX_HOME_URL.'" target="_blank">400+ US banks.</a>',
'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' => 'Usuario',
'account_number' => 'Número de Cuenta',
'account_name' => 'Nombre de Cuenta',
'bank_account_error' => 'Error al recuperar los detalles de la cuenta, por favor revisa tus credenciales.',
'status_approved' => 'Aprobado',
'quote_settings' => 'Configuración de Presupuestos',
'auto_convert_quote' => 'Auto convertir presupuesto',
'auto_convert_quote_help' => 'Convierte un presupuesto en factura automaticamente cuando los aprueba el cliente.',
'validate' => 'Validar',
'info' => 'Info',
'imported_expenses' => ' :count_vendors proveedor(es) and :count_expenses gasto(s) importados correctamente',
'iframe_url_help3' => 'Nota: Si piensas aceptar tarjetas de credito recomendamos tener habilitado HTTPS.',
);

View File

@ -427,7 +427,6 @@ return array(
'hide' => 'Cacher',
'new_version_available' => 'Une nouvelle version de :releases_link est disponible. Vous utilisez v:user_version, la plus récente est v:latest_version',
'invoice_settings' => 'Paramètres des factures',
'invoice_number_prefix' => 'Préfixe du numéro de facture',
'invoice_number_counter' => 'Compteur du numéro de facture',
@ -982,4 +981,126 @@ return array(
'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',
// Expense / vendor
'expense' => 'Expense',
'expenses' => 'Expenses',
'new_expense' => 'Enter Expense',
'enter_expense' => 'Enter Expense',
'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',
// 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' => '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',
// 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',
// recurring due dates
'recurring_due_dates' => 'Recurring Invoice Due Dates',
'recurring_due_date_help' => '<p>Automatically sets a due date for the invoice.</p>
<p>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.</p>
<p>Invoices on a weekly cycle set to be due on the day of the week they are created will be due the next week.</p>
<p>For example:</p>
<ul>
<li>Today is the 15th, due date is 1st of the month. The due date should likely be the 1st of the next month.</li>
<li>Today is the 15th, due date is the last day of the month. The due date will be the last day of the this month.
</li>
<li>Today is the 15th, due date is the 15th day of the month. The due date will be the 15th day of <strong>next</strong> month.
</li>
<li>Today is the Friday, due date is the 1st Friday after. The due date will be next Friday, not today.
</li>
</ul>',
'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',
// 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.',
'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',
'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',
'bank_accounts_help' => 'Connect a bank account to automatically import expenses and create vendors. Supports American Express and <a href="'.OFX_HOME_URL.'" target="_blank">400+ US banks.</a>',
'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_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',
'info' => 'Info',
'imported_expenses' => 'Successfully created :count_vendors vendor(s) and :count_expenses expense(s)',
);

View File

@ -25,7 +25,7 @@ return array(
"numeric" => "La valeur de :attribute doit être comprise entre :min et :max.",
"file" => "Le fichier :attribute doit avoir une taille entre :min et :max kilobytes.",
"string" => "Le texte :attribute doit avoir entre :min et :max caractères.",
"array" => "Le champ :attribute doit avoir entre :min et :max éléments."
"array" => "Le champ :attribute doit avoir entre :min et :max éléments.",
),
"confirmed" => "Le champ de confirmation :attribute ne correspond pas.",
"date" => "Le champ :attribute n'est pas une date valide.",
@ -50,7 +50,7 @@ return array(
"numeric" => "La valeur de :attribute doit être supérieure à :min.",
"file" => "Le fichier :attribute doit être plus que gros que :min kilobytes.",
"string" => "Le texte :attribute doit contenir au moins :min caractères.",
"array" => "Le champ :attribute doit avoir au moins :min éléments."
"array" => "Le champ :attribute doit avoir au moins :min éléments.",
),
"not_in" => "Le champ :attribute sélectionné n'est pas valide.",
"numeric" => "Le champ :attribute doit contenir un nombre.",
@ -66,7 +66,7 @@ return array(
"numeric" => "La valeur de :attribute doit être :size.",
"file" => "La taille du fichier de :attribute doit être de :size kilobytes.",
"string" => "Le texte de :attribute doit contenir :size caractères.",
"array" => "Le champ :attribute doit contenir :size éléments."
"array" => "Le champ :attribute doit contenir :size éléments.",
),
"unique" => "La valeur du champ :attribute est déjà utilisée.",
"url" => "Le format de l'URL de :attribute n'est pas valide.",
@ -136,7 +136,7 @@ return array(
"date" => "Date",
"time" => "Heure",
"available" => "Disponible",
"size" => "Taille"
"size" => "Taille",
),
);

View File

@ -345,7 +345,7 @@ return array(
'chart_builder' => 'Concepteur de graphiques',
'ninja_email_footer' => 'Utilisez :site pour facturer vos clients et être payés en ligne gratuitement!',
'go_pro' => 'Passez au Plan Pro',
'go_pro' => 'Devenez Pro',
// Quotes
'quote' => 'Devis',
@ -427,7 +427,6 @@ return array(
'hide' => 'Cacher',
'new_version_available' => 'Une nouvelle version de :releases_link est disponible. Vous utilisez v:user_version, la plus récente est v:latest_version',
'invoice_settings' => 'Paramètres des factures',
'invoice_number_prefix' => 'Préfixe du numéro de facture',
'invoice_number_counter' => 'Compteur du numéro de facture',
@ -667,7 +666,6 @@ return array(
'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',
@ -982,4 +980,126 @@ return array(
'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',
// Expense / vendor
'expense' => 'Expense',
'expenses' => 'Expenses',
'new_expense' => 'Enter Expense',
'enter_expense' => 'Enter Expense',
'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',
// 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' => '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',
// 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',
// recurring due dates
'recurring_due_dates' => 'Recurring Invoice Due Dates',
'recurring_due_date_help' => '<p>Automatically sets a due date for the invoice.</p>
<p>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.</p>
<p>Invoices on a weekly cycle set to be due on the day of the week they are created will be due the next week.</p>
<p>For example:</p>
<ul>
<li>Today is the 15th, due date is 1st of the month. The due date should likely be the 1st of the next month.</li>
<li>Today is the 15th, due date is the last day of the month. The due date will be the last day of the this month.
</li>
<li>Today is the 15th, due date is the 15th day of the month. The due date will be the 15th day of <strong>next</strong> month.
</li>
<li>Today is the Friday, due date is the 1st Friday after. The due date will be next Friday, not today.
</li>
</ul>',
'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',
// 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.',
'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',
'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',
'bank_accounts_help' => 'Connect a bank account to automatically import expenses and create vendors. Supports American Express and <a href="'.OFX_HOME_URL.'" target="_blank">400+ US banks.</a>',
'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_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',
'info' => 'Info',
'imported_expenses' => 'Successfully created :count_vendors vendor(s) and :count_expenses expense(s)',
);

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