1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-10 05:02:36 +01:00

Merge pull request #10 from hillelcoren/master

Update from master
This commit is contained in:
Paul-Vincent Roll 2015-08-12 16:26:41 +02:00
commit 4753b47c22
73 changed files with 1048 additions and 620 deletions

View File

@ -18,6 +18,4 @@ MAIL_HOST
MAIL_USERNAME
MAIL_FROM_ADDRESS
MAIL_FROM_NAME
MAIL_PASSWORD
ALLOW_NEW_ACCOUNTS
MAIL_PASSWORD

View File

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

View File

@ -75,18 +75,18 @@ class AccountController extends BaseController
public function getStarted()
{
if (Auth::check()) {
return Redirect::to('invoices/create');
}
if (!Utils::isNinja() && !Utils::allowNewAccounts() && Account::count() > 0) {
return Redirect::to('/login');
}
$user = false;
$guestKey = Input::get('guest_key'); // local storage key to login until registered
$prevUserId = Session::pull(PREV_USER_ID); // last user id used to link to new account
if (Auth::check()) {
return Redirect::to('invoices/create');
}
if (!Utils::isNinja() && (Account::count() > 0 && !$prevUserId)) {
return Redirect::to('/login');
}
if ($guestKey && !$prevUserId) {
$user = User::where('password', '=', $guestKey)->first();
@ -149,6 +149,7 @@ class AccountController extends BaseController
public function showSection($section = ACCOUNT_DETAILS, $subSection = false)
{
if ($section == ACCOUNT_DETAILS) {
$primaryUser = Auth::user()->account->users()->orderBy('id')->first();
$data = [
'account' => Account::with('users')->findOrFail(Auth::user()->account_id),
'countries' => Cache::get('countries'),
@ -159,8 +160,9 @@ class AccountController extends BaseController
'datetimeFormats' => Cache::get('datetimeFormats'),
'currencies' => Cache::get('currencies'),
'languages' => Cache::get('languages'),
'showUser' => Auth::user()->id === Auth::user()->account->users()->first()->id,
'showUser' => Auth::user()->id === $primaryUser->id,
'title' => trans('texts.company_details'),
'primaryUser' => $primaryUser,
];
return View::make('accounts.details', $data);
@ -211,7 +213,7 @@ class AccountController extends BaseController
$client->work_email = '';
$invoice->invoice_number = $account->getNextInvoiceNumber();
$invoice->invoice_date = date_create()->format('Y-m-d');
$invoice->invoice_date = Utils::fromSqlDate(date('Y-m-d'));
$invoice->account = json_decode($account->toJson());
$invoice->amount = $invoice->balance = 100;
@ -637,9 +639,10 @@ class AccountController extends BaseController
{
$rules = array(
'name' => 'required',
'logo' => 'sometimes|max:1024|mimes:jpeg,gif,png',
);
$user = Auth::user()->account->users()->first();
$user = Auth::user()->account->users()->orderBy('id')->first();
if (Auth::user()->id === $user->id) {
$rules['email'] = 'email|required|unique:users,email,'.$user->id.',id';

View File

@ -88,7 +88,7 @@ class AppController extends BaseController
"MAIL_HOST={$mail['host']}\n".
"MAIL_USERNAME={$mail['username']}\n".
"MAIL_FROM_NAME={$mail['from']['name']}\n".
"MAIL_PASSWORD={$mail['password']}\n";
"MAIL_PASSWORD={$mail['password']}";
// Write Config Settings
$fp = fopen(base_path()."/.env", 'w');

View File

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

View File

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

View File

@ -43,7 +43,7 @@ class HomeController extends BaseController
public function invoiceNow()
{
if (Auth::check() && Input::get('new_account')) {
if (Auth::check() && Input::get('new_company')) {
Session::put(PREV_USER_ID, Auth::user()->id);
Auth::user()->clearSession();
Auth::logout();
@ -72,9 +72,9 @@ class HomeController extends BaseController
$user->news_feed_id = $newsFeedId;
$user->save();
}
Session::forget('news_feed_message');
}
Session::forget('news_feed_message');
return 'success';
}

View File

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

View File

@ -60,8 +60,7 @@ class InvoiceController extends BaseController
'columns' => Utils::trans(['checkbox', 'invoice_number', 'client', 'invoice_date', 'invoice_total', 'balance_due', 'due_date', 'status', 'action']),
];
$recurringInvoices = Invoice::scope()
->where('is_recurring', '=', true);
$recurringInvoices = Invoice::scope()->where('is_recurring', '=', true);
if (Session::get('show_trash:invoice')) {
$recurringInvoices->withTrashed();
@ -86,11 +85,12 @@ class InvoiceController extends BaseController
}
$invitation = Invitation::with('account')->where('invitation_key', '=', $invitationKey)->first();
$color = $invitation->account->primary_color ? $invitation->account->primary_color : '#0b4d78';
$account = $invitation->account;
$color = $account->primary_color ? $account->primary_color : '#0b4d78';
$data = [
'color' => $color,
'hideLogo' => Session::get('white_label'),
'hideLogo' => $account->isWhiteLabel(),
'title' => trans('texts.invoices'),
'entityType' => ENTITY_INVOICE,
'columns' => Utils::trans(['invoice_number', 'invoice_date', 'invoice_total', 'balance_due', 'due_date']),
@ -205,7 +205,6 @@ class InvoiceController extends BaseController
Session::set($invitationKey, true);
Session::set('invitation_key', $invitationKey);
Session::set('white_label', $account->isWhiteLabel());
$account->loadLocalizationSettings();
@ -215,6 +214,8 @@ class InvoiceController extends BaseController
if ($invoice->invoice_design_id == CUSTOM_DESIGN) {
$invoice->invoice_design->javascript = $account->custom_design;
} elseif ($account->utf8_invoices) {
$invoice->invoice_design->javascript = $invoice->invoice_design->pdfmake;
}
$contact = $invitation->contact;
@ -254,7 +255,7 @@ class InvoiceController extends BaseController
'invoiceLabels' => $account->getInvoiceLabels(),
'contact' => $contact,
'paymentTypes' => $paymentTypes,
'paymentURL' => $paymentURL
'paymentURL' => $paymentURL,
);
return View::make('invoices.view', $data);
@ -281,7 +282,7 @@ class InvoiceController extends BaseController
$method = 'POST';
$url = "{$entityType}s";
} else {
Utils::trackViewed($invoice->invoice_number.' - '.$invoice->client->getDisplayName(), $invoice->getEntityType());
Utils::trackViewed($invoice->getDisplayName().' - '.$invoice->client->getDisplayName(), $invoice->getEntityType());
$method = 'PUT';
$url = "{$entityType}s/{$publicId}";
}
@ -336,6 +337,7 @@ class InvoiceController extends BaseController
'url' => $url,
'title' => trans("texts.edit_{$entityType}"),
'client' => $invoice->client,
'isRecurring' => $invoice->is_recurring,
'actions' => $actions);
$data = array_merge($data, self::getViewModel());
@ -359,10 +361,10 @@ class InvoiceController extends BaseController
return View::make('invoices.edit', $data);
}
public function create($clientPublicId = 0)
public function create($clientPublicId = 0, $isRecurring = false)
{
$client = null;
$invoiceNumber = Auth::user()->account->getNextInvoiceNumber();
$invoiceNumber = $isRecurring ? microtime(true) : Auth::user()->account->getNextInvoiceNumber();
if ($clientPublicId) {
$client = Client::scope($clientPublicId)->firstOrFail();
@ -376,12 +378,18 @@ class InvoiceController extends BaseController
'method' => 'POST',
'url' => 'invoices',
'title' => trans('texts.new_invoice'),
'isRecurring' => $isRecurring,
'client' => $client);
$data = array_merge($data, self::getViewModel());
return View::make('invoices.edit', $data);
}
public function createRecurring($clientPublicId = 0)
{
return self::create($clientPublicId, true);
}
private static function getViewModel()
{
$recurringHelp = '';
@ -511,7 +519,16 @@ class InvoiceController extends BaseController
return $this->convertQuote($publicId);
} elseif ($action == 'email') {
if (Auth::user()->confirmed && !Auth::user()->isDemo()) {
$response = $this->mailer->sendInvoice($invoice);
if ($invoice->is_recurring) {
if ($invoice->shouldSendToday()) {
$invoice = $this->invoiceRepo->createRecurringInvoice($invoice);
$response = $this->mailer->sendInvoice($invoice);
} else {
$response = trans('texts.recurring_too_soon');
}
} else {
$response = $this->mailer->sendInvoice($invoice);
}
if ($response === true) {
$message = trans("texts.emailed_{$entityType}");
Session::flash('message', $message);

View File

@ -61,11 +61,12 @@ class PaymentController extends BaseController
}
$invitation = Invitation::with('account')->where('invitation_key', '=', $invitationKey)->first();
$color = $invitation->account->primary_color ? $invitation->account->primary_color : '#0b4d78';
$account = $invitation->account;
$color = $account->primary_color ? $account->primary_color : '#0b4d78';
$data = [
'color' => $color,
'hideLogo' => Session::get('white_label'),
'hideLogo' => $account->isWhiteLabel(),
'entityType' => ENTITY_PAYMENT,
'title' => trans('texts.payments'),
'columns' => Utils::trans(['invoice', 'transaction_reference', 'method', 'payment_amount', 'payment_date'])
@ -336,6 +337,7 @@ class PaymentController extends BaseController
'acceptedCreditCardTypes' => $acceptedCreditCardTypes,
'countries' => Cache::get('countries'),
'currencyId' => $client->getCurrencyId(),
'currencyCode' => $client->currency ? $client->currency->code : ($account->currency ? $account->currency->code : 'USD'),
'account' => $client->account,
'hideLogo' => $account->isWhiteLabel(),
'showAddress' => $accountGateway->show_address,
@ -387,6 +389,7 @@ class PaymentController extends BaseController
'currencyId' => 1,
'paymentTitle' => $affiliate->payment_title,
'paymentSubtitle' => $affiliate->payment_subtitle,
'showAddress' => true,
];
return View::make('payments.payment', $data);
@ -541,18 +544,19 @@ class PaymentController extends BaseController
->withErrors($validator)
->withInput();
}
if ($accountGateway->update_address) {
$client->address1 = trim(Input::get('address1'));
$client->address2 = trim(Input::get('address2'));
$client->city = trim(Input::get('city'));
$client->state = trim(Input::get('state'));
$client->postal_code = trim(Input::get('postal_code'));
$client->country_id = Input::get('country_id');
$client->save();
}
}
if ($onSite && $accountGateway->update_address) {
$client->address1 = trim(Input::get('address1'));
$client->address2 = trim(Input::get('address2'));
$client->city = trim(Input::get('city'));
$client->state = trim(Input::get('state'));
$client->postal_code = trim(Input::get('postal_code'));
$client->country_id = Input::get('country_id');
$client->save();
}
try {
$gateway = self::createGateway($accountGateway);
$details = self::getPaymentDetails($invitation, ($useToken || !$onSite) ? false : Input::all());

View File

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

View File

@ -75,11 +75,12 @@ class QuoteController extends BaseController
}
$invitation = Invitation::with('account')->where('invitation_key', '=', $invitationKey)->first();
$color = $invitation->account->primary_color ? $invitation->account->primary_color : '#0b4d78';
$account = $invitation->account;
$color = $account->primary_color ? $account->primary_color : '#0b4d78';
$data = [
'color' => $color,
'hideLogo' => Session::get('white_label'),
'hideLogo' => $account->isWhiteLabel(),
'title' => trans('texts.quotes'),
'entityType' => ENTITY_QUOTE,
'columns' => Utils::trans(['quote_number', 'quote_date', 'quote_total', 'due_date']),
@ -157,7 +158,8 @@ class QuoteController extends BaseController
'paymentTerms' => Cache::get('paymentTerms'),
'industries' => Cache::get('industries'),
'invoiceDesigns' => InvoiceDesign::getDesigns(),
'invoiceLabels' => Auth::user()->account->getInvoiceLabels()
'invoiceLabels' => Auth::user()->account->getInvoiceLabels(),
'isRecurring' => false,
];
}

View File

@ -20,7 +20,9 @@ class ReportController extends BaseController
$fileName = storage_path() . '/dataviz_sample.txt';
if (Auth::user()->account->isPro()) {
$account = Account::where('id', '=', Auth::user()->account->id)->with(['clients.invoices.invoice_items', 'clients.contacts'])->first();
$account = Account::where('id', '=', Auth::user()->account->id)
->with(['clients.invoices.invoice_items', 'clients.contacts'])
->first();
$account = $account->hideFieldsForViz();
$clients = $account->clients->toJson();
} elseif (file_exists($fileName)) {

View File

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

View File

@ -158,7 +158,7 @@ class StartupCheck
}
}
if (preg_match('/(?i)msie [2-8]/', $_SERVER['HTTP_USER_AGENT'])) {
if (isset($_SERVER['HTTP_USER_AGENT']) && preg_match('/(?i)msie [2-8]/', $_SERVER['HTTP_USER_AGENT'])) {
Session::flash('error', trans('texts.old_browser'));
}

View File

@ -95,6 +95,7 @@ Route::group(['middleware' => 'auth'], function() {
Route::post('users/change_password', 'UserController@changePassword');
Route::get('/switch_account/{user_id}', 'UserController@switchAccount');
Route::get('/unlink_account/{user_account_id}/{user_id}', 'UserController@unlinkAccount');
Route::get('/manage_companies', 'UserController@manageCompanies');
Route::get('api/tokens', array('as'=>'api.tokens', 'uses'=>'TokenController@getDatatable'));
Route::resource('tokens', 'TokenController');
@ -130,7 +131,6 @@ Route::group(['middleware' => 'auth'], function() {
Route::get('tasks/create/{client_id?}', 'TaskController@create');
Route::post('tasks/bulk', 'TaskController@bulk');
Route::get('recurring_invoices', 'InvoiceController@recurringIndex');
Route::get('api/recurring_invoices/{client_id?}', array('as'=>'api.recurring_invoices', 'uses'=>'InvoiceController@getRecurringDatatable'));
Route::get('invoices/invoice_history/{invoice_id}', 'InvoiceController@invoiceHistory');
@ -139,6 +139,7 @@ Route::group(['middleware' => 'auth'], function() {
Route::resource('invoices', 'InvoiceController');
Route::get('api/invoices/{client_id?}', array('as'=>'api.invoices', 'uses'=>'InvoiceController@getDatatable'));
Route::get('invoices/create/{client_id?}', 'InvoiceController@create');
Route::get('recurring_invoices/create/{client_id?}', 'InvoiceController@createRecurring');
Route::get('invoices/{public_id}/clone', 'InvoiceController@cloneInvoice');
Route::post('invoices/bulk', 'InvoiceController@bulk');
@ -325,6 +326,7 @@ define('SESSION_LAST_REQUEST_TIME', 'SESSION_LAST_REQUEST_TIME');
define('DEFAULT_TIMEZONE', 'US/Eastern');
define('DEFAULT_CURRENCY', 1); // US Dollar
define('DEFAULT_LANGUAGE', 1); // English
define('DEFAULT_DATE_FORMAT', 'M j, Y');
define('DEFAULT_DATE_PICKER_FORMAT', 'M d, yyyy');
define('DEFAULT_DATETIME_FORMAT', 'F j, Y, g:i a');
@ -363,7 +365,7 @@ define('NINJA_GATEWAY_ID', GATEWAY_STRIPE);
define('NINJA_GATEWAY_CONFIG', '');
define('NINJA_WEB_URL', 'https://www.invoiceninja.com');
define('NINJA_APP_URL', 'https://app.invoiceninja.com');
define('NINJA_VERSION', '2.2.2');
define('NINJA_VERSION', '2.3.0');
define('NINJA_DATE', '2000-01-01');
define('NINJA_FROM_EMAIL', 'maildelivery@invoiceninja.com');
@ -472,4 +474,4 @@ if (Auth::check() && Auth::user()->id === 1)
{
Auth::loginUsingId(1);
}
*/
*/

View File

@ -3,6 +3,7 @@
use Auth;
use Cache;
use DB;
use App;
use Schema;
use Session;
use Request;
@ -61,7 +62,7 @@ class Utils
public static function allowNewAccounts()
{
return Utils::isNinja() || (isset($_ENV['ALLOW_NEW_ACCOUNTS']) && $_ENV['ALLOW_NEW_ACCOUNTS'] == 'true');
return Utils::isNinja() || Auth::check();
}
public static function isPro()
@ -69,6 +70,11 @@ class Utils
return Auth::check() && Auth::user()->isPro();
}
public static function isEnglish()
{
return App::getLocale() == 'en';
}
public static function getUserType()
{
if (Utils::isNinja()) {
@ -335,10 +341,11 @@ class Utils
return;
}
$timezone = Session::get(SESSION_TIMEZONE, DEFAULT_TIMEZONE);
//$timezone = Session::get(SESSION_TIMEZONE, DEFAULT_TIMEZONE);
$format = Session::get(SESSION_DATE_FORMAT, DEFAULT_DATE_FORMAT);
$dateTime = DateTime::createFromFormat($format, $date, new DateTimeZone($timezone));
//$dateTime = DateTime::createFromFormat($format, $date, new DateTimeZone($timezone));
$dateTime = DateTime::createFromFormat($format, $date);
return $formatResult ? $dateTime->format('Y-m-d') : $dateTime;
}
@ -349,11 +356,11 @@ class Utils
return '';
}
$timezone = Session::get(SESSION_TIMEZONE, DEFAULT_TIMEZONE);
//$timezone = Session::get(SESSION_TIMEZONE, DEFAULT_TIMEZONE);
$format = Session::get(SESSION_DATE_FORMAT, DEFAULT_DATE_FORMAT);
$dateTime = DateTime::createFromFormat('Y-m-d', $date);
$dateTime->setTimeZone(new DateTimeZone($timezone));
//$dateTime->setTimeZone(new DateTimeZone($timezone));
return $formatResult ? $dateTime->format($format) : $dateTime;
}
@ -400,10 +407,12 @@ class Utils
}
$object = new stdClass();
$object->accountId = Auth::user()->account_id;
$object->url = $url;
$object->name = ucwords($type).': '.$name;
$data = [];
$counts = [];
for ($i = 0; $i<count($viewed); $i++) {
$item = $viewed[$i];
@ -412,12 +421,22 @@ class Utils
continue;
}
// temporary fix to check for new property in session
if (!property_exists($item, 'accountId')) {
continue;
}
array_unshift($data, $item);
if (isset($counts[$item->accountId])) {
$counts[$item->accountId]++;
} else {
$counts[$item->accountId] = 1;
}
}
array_unshift($data, $object);
if (count($data) > RECENTLY_VIEWED_LIMIT) {
if (isset($counts[Auth::user()->account_id]) && $counts[Auth::user()->account_id] > RECENTLY_VIEWED_LIMIT) {
array_pop($data);
}

View File

@ -4,7 +4,7 @@ use Eloquent;
use Utils;
use Session;
use DateTime;
use App;
use Illuminate\Database\Eloquent\SoftDeletes;
class Account extends Eloquent
@ -92,6 +92,11 @@ class Account extends Eloquent
}
}
public function isEnglish()
{
return !$this->language_id || $this->language_id == DEFAULT_LANGUAGE;
}
public function getDisplayName()
{
if ($this->name) {
@ -147,7 +152,8 @@ class Account extends Eloquent
public function getLogoPath()
{
$fileName = 'logo/' . $this->account_key;
return file_exists($fileName.'.png') ? $fileName.'.png' : $fileName.'.jpg';
return file_exists($fileName.'.png') && $this->utf8_invoices ? $fileName.'.png' : $fileName.'.jpg';
}
public function getLogoWidth()
@ -176,37 +182,36 @@ class Account extends Eloquent
{
$counter = $isQuote && !$this->share_counter ? $this->quote_number_counter : $this->invoice_number_counter;
$prefix .= $isQuote ? $this->quote_number_prefix : $this->invoice_number_prefix;
$counterOffset = 0;
// confirm the invoice number isn't already taken
do {
$number = $prefix.str_pad($counter, 4, "0", STR_PAD_LEFT);
$check = Invoice::scope(false, $this->id)->whereInvoiceNumber($number)->withTrashed()->first();
$counter++;
$counterOffset++;
} while ($check);
// update the invoice counter to be caught up
if ($counterOffset > 1) {
if ($isQuote && !$this->share_counter) {
$this->quote_number_counter += $counterOffset - 1;
} else {
$this->invoice_number_counter += $counterOffset - 1;
}
$this->save();
}
return $number;
}
public function incrementCounter($invoiceNumber, $isQuote = false, $isRecurring)
public function incrementCounter($isQuote = false)
{
// check if the user modified the invoice number
if (!$isRecurring && $invoiceNumber != $this->getNextInvoiceNumber($isQuote)) {
// remove the prefix
$prefix = $isQuote ? $this->quote_number_prefix : $this->invoice_number_prefix;
$invoiceNumber = preg_replace('/^'.$prefix.'/', '', $invoiceNumber);
$invoiceNumber = intval(preg_replace('/[^0-9]/', '', $invoiceNumber));
if ($isQuote && !$this->share_counter) {
$this->quote_number_counter = $invoiceNumber + 1;
} else {
$this->invoice_number_counter = $invoiceNumber + 1;
}
// otherwise, just increment the counter
if ($isQuote && !$this->share_counter) {
$this->quote_number_counter += 1;
} else {
if ($isQuote && !$this->share_counter) {
$this->quote_number_counter += 1;
} else {
$this->invoice_number_counter += 1;
}
$this->invoice_number_counter += 1;
}
$this->save();
@ -229,6 +234,8 @@ class Account extends Eloquent
Session::put(SESSION_DATETIME_FORMAT, $this->datetime_format ? $this->datetime_format->format : DEFAULT_DATETIME_FORMAT);
Session::put(SESSION_CURRENCY, $this->currency_id ? $this->currency_id : DEFAULT_CURRENCY);
Session::put(SESSION_LOCALE, $this->language_id ? $this->language->locale : DEFAULT_LOCALE);
App::setLocale(session(SESSION_LOCALE));
}
public function getInvoiceLabels()
@ -277,7 +284,7 @@ class Account extends Eloquent
if (isset($custom[$field]) && $custom[$field]) {
$data[$field] = $custom[$field];
} else {
$data[$field] = uctrans("texts.$field");
$data[$field] = $this->isEnglish() ? uctrans("texts.$field") : trans("texts.$field");
}
}
@ -315,11 +322,11 @@ class Account extends Eloquent
public function isWhiteLabel()
{
if (Utils::isNinjaProd()) {
return false;
if (Utils::isNinja()) {
return self::isPro() && $this->pro_plan_paid != NINJA_DATE;
} else {
return $this->pro_plan_paid == NINJA_DATE;
}
return $this->pro_plan_paid == NINJA_DATE;
}
public function getSubscription($eventId)
@ -348,6 +355,8 @@ class Account extends Eloquent
'invoice_status_id',
'invoice_items',
'created_at',
'is_recurring',
'is_quote',
]);
foreach ($invoice->invoice_items as $invoiceItem) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,11 +1,12 @@
<?php namespace App\Ninja\Repositories;
use Carbon;
use Utils;
use App\Models\Invoice;
use App\Models\InvoiceItem;
use App\Models\Invitation;
use App\Models\Product;
use App\Models\Task;
use Utils;
class InvoiceRepository
{
@ -266,15 +267,18 @@ class InvoiceRepository
$account->save();
}
$invoice->client_id = $data['client_id'];
if (isset($data['invoice_number'])) {
$invoice->invoice_number = trim($data['invoice_number']);
}
$invoice->discount = round(Utils::parseFloat($data['discount']), 2);
$invoice->is_amount_discount = $data['is_amount_discount'] ? true : false;
$invoice->invoice_number = trim($data['invoice_number']);
$invoice->partial = round(Utils::parseFloat($data['partial']), 2);
$invoice->invoice_date = isset($data['invoice_date_sql']) ? $data['invoice_date_sql'] : Utils::toSqlDate($data['invoice_date']);
$invoice->has_tasks = isset($data['has_tasks']) ? $data['has_tasks'] : false;
if (!$publicId) {
$invoice->client_id = $data['client_id'];
$invoice->is_recurring = $data['is_recurring'] && !Utils::isDemo() ? true : false;
}
@ -387,7 +391,7 @@ class InvoiceRepository
$task->invoice_id = $invoice->id;
$task->client_id = $invoice->client_id;
$task->save();
} else if ($item['product_key']) {
} else if ($item['product_key'] && !$invoice->has_tasks) {
$product = Product::findProductByKey(trim($item['product_key']));
if (!$product) {
@ -543,9 +547,82 @@ class InvoiceRepository
->whereClientId($clientId)
->whereIsQuote(false)
->whereIsRecurring(false)
->whereDeletedAt(null)
->whereHasTasks(true)
->where('balance', '>', 0)
->where('invoice_status_id', '<', 5)
->select(['public_id', 'invoice_number'])
->get();
}
public function createRecurringInvoice($recurInvoice)
{
$recurInvoice->load('account.timezone', 'invoice_items', 'client', 'user');
if ($recurInvoice->client->deleted_at) {
return false;
}
if (!$recurInvoice->user->confirmed) {
return false;
}
if (!$recurInvoice->shouldSendToday()) {
return false;
}
$invoice = Invoice::createNew($recurInvoice);
$invoice->client_id = $recurInvoice->client_id;
$invoice->recurring_invoice_id = $recurInvoice->id;
$invoice->invoice_number = $recurInvoice->account->getNextInvoiceNumber(false, 'R');
$invoice->amount = $recurInvoice->amount;
$invoice->balance = $recurInvoice->amount;
$invoice->invoice_date = date_create()->format('Y-m-d');
$invoice->discount = $recurInvoice->discount;
$invoice->po_number = $recurInvoice->po_number;
$invoice->public_notes = Utils::processVariables($recurInvoice->public_notes);
$invoice->terms = Utils::processVariables($recurInvoice->terms);
$invoice->invoice_footer = Utils::processVariables($recurInvoice->invoice_footer);
$invoice->tax_name = $recurInvoice->tax_name;
$invoice->tax_rate = $recurInvoice->tax_rate;
$invoice->invoice_design_id = $recurInvoice->invoice_design_id;
$invoice->custom_value1 = $recurInvoice->custom_value1;
$invoice->custom_value2 = $recurInvoice->custom_value2;
$invoice->custom_taxes1 = $recurInvoice->custom_taxes1;
$invoice->custom_taxes2 = $recurInvoice->custom_taxes2;
$invoice->is_amount_discount = $recurInvoice->is_amount_discount;
if ($invoice->client->payment_terms != 0) {
$days = $invoice->client->payment_terms;
if ($days == -1) {
$days = 0;
}
$invoice->due_date = date_create()->modify($days.' day')->format('Y-m-d');
}
$invoice->save();
foreach ($recurInvoice->invoice_items as $recurItem) {
$item = InvoiceItem::createNew($recurItem);
$item->product_id = $recurItem->product_id;
$item->qty = $recurItem->qty;
$item->cost = $recurItem->cost;
$item->notes = Utils::processVariables($recurItem->notes);
$item->product_key = Utils::processVariables($recurItem->product_key);
$item->tax_name = $recurItem->tax_name;
$item->tax_rate = $recurItem->tax_rate;
$invoice->invoice_items()->save($item);
}
foreach ($recurInvoice->invitations as $recurInvitation) {
$invitation = Invitation::createNew($recurInvitation);
$invitation->contact_id = $recurInvitation->contact_id;
$invitation->invitation_key = str_random(RANDOM_KEY_LENGTH);
$invoice->invitations()->save($invitation);
}
$recurInvoice->last_sent_date = Carbon::now()->toDateTimeString();
$recurInvoice->save();
return $invoice;
}
}

View File

@ -46,7 +46,7 @@ class TaskRepository
}
public function save($publicId, $data)
{
{
if ($publicId) {
$task = Task::scope($publicId)->firstOrFail();
} else {
@ -60,8 +60,14 @@ class TaskRepository
$task->description = trim($data['description']);
}
//$timeLog = $task->time_log ? json_decode($task->time_log, true) : [];
$timeLog = isset($data['time_log']) ? json_decode($data['time_log']) : [];
if (isset($data['time_log'])) {
$timeLog = json_decode($data['time_log']);
} elseif ($task->time_log) {
$timeLog = json_decode($task->time_log);
} else {
$timeLog = [];
}
if ($data['action'] == 'start') {
$task->is_running = true;
$timeLog[] = [strtotime('now'), false];

View File

@ -40,10 +40,13 @@ class AppServiceProvider extends ServiceProvider {
<ul class="dropdown-menu" id="menu1">
<li><a href="'.URL::to($types.'/create').'">'.trans("texts.new_$type").'</a></li>';
if ($type == ENTITY_INVOICE && Auth::user()->isPro()) {
$str .= '<li class="divider"></li>
if ($type == ENTITY_INVOICE) {
$str .= '<li><a href="'.URL::to('recurring_invoices/create').'">'.trans("texts.new_recurring_invoice").'</a></li>';
if (Auth::user()->isPro()) {
$str .= '<li class="divider"></li>
<li><a href="'.URL::to('quotes').'">'.trans("texts.quotes").'</a></li>
<li><a href="'.URL::to('quotes/create').'">'.trans("texts.new_quote").'</a></li>';
}
} else if ($type == ENTITY_CLIENT) {
$str .= '<li class="divider"></li>
<li><a href="'.URL::to('credits').'">'.trans("texts.credits").'</a></li>

View File

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

View File

@ -64,7 +64,7 @@ class PaymentLibrariesSeeder extends Seeder
['name' => 'US Dollar', 'code' => 'USD', 'symbol' => '$', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['name' => 'Pound Sterling', 'code' => 'GBP', 'symbol' => '£', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['name' => 'Euro', 'code' => 'EUR', 'symbol' => '€', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['name' => 'Rand', 'code' => 'ZAR', 'symbol' => 'R', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['name' => 'South African Rand', 'code' => 'ZAR', 'symbol' => 'R', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['name' => 'Danish Krone', 'code' => 'DKK', 'symbol' => 'kr ', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['name' => 'Israeli Shekel', 'code' => 'ILS', 'symbol' => 'NIS ', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['name' => 'Swedish Krona', 'code' => 'SEK', 'symbol' => 'kr ', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
@ -82,10 +82,16 @@ class PaymentLibrariesSeeder extends Seeder
['name' => 'Malaysian Ringgit', 'code' => 'MYR', 'symbol' => 'RM', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['name' => 'Brazilian Real', 'code' => 'BRL', 'symbol' => 'R$', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['name' => 'Thai baht', 'code' => 'THB', 'symbol' => 'THB ', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['name' => 'Nigerian Naira', 'code' => 'NGN', 'symbol' => 'NGN ', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['name' => 'Argentine Peso', 'code' => 'ARS', 'symbol' => '$', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
];
foreach ($currencies as $currency) {
if (!DB::table('currencies')->whereName($currency['name'])->get()) {
$record = Currency::whereCode($currency['code'])->first();
if ($record) {
$record->name = $currency['name'];
$record->save();
} else {
Currency::create($currency);
}
}
@ -147,17 +153,21 @@ class PaymentLibrariesSeeder extends Seeder
'Photo',
];
foreach ($designs as $design) {
for ($i=0; $i<count($designs); $i++) {
$design = $designs[$i];
$fileName = storage_path() . '/templates/' . strtolower($design) . '.js';
$pdfmake = file_get_contents($fileName);
if ($pdfmake) {
$record = InvoiceDesign::whereName($design)->first();
if (!$record) {
$record = new InvoiceDesign;
$record->name = $design;
if (file_exists($fileName)) {
$pdfmake = file_get_contents($fileName);
if ($pdfmake) {
$record = InvoiceDesign::whereName($design)->first();
if (!$record) {
$record = new InvoiceDesign;
$record->id = $i + 1;
$record->name = $design;
}
$record->pdfmake = $pdfmake;
$record->save();
}
$record->pdfmake = $pdfmake;
$record->save();
}
}
}

View File

@ -2839,15 +2839,12 @@ background-clip: padding-box;
.dashboard .panel-body {padding: 0;}
.dashboard .table-striped>tbody>tr>td, .table-striped>tbody>tr>th { background-color: #fbfbfb;}
.dashboard .table-striped>tbody>tr:nth-child(odd)>tr, .table-striped>tbody>tr:nth-child(odd)>th {
background-color: #fff;
}
.dashboard th {
border-left: none;
background-color: #fbfbfb;
border-bottom: 1px solid #dfe0e1;
}
.dashboard table.table thead > tr > th {
border-bottom-width: 1px;
}

View File

@ -489,15 +489,12 @@ background-clip: padding-box;
.dashboard .panel-body {padding: 0;}
.dashboard .table-striped>tbody>tr>td, .table-striped>tbody>tr>th { background-color: #fbfbfb;}
.dashboard .table-striped>tbody>tr:nth-child(odd)>tr, .table-striped>tbody>tr:nth-child(odd)>th {
background-color: #fff;
}
.dashboard th {
border-left: none;
background-color: #fbfbfb;
border-bottom: 1px solid #dfe0e1;
}
.dashboard table.table thead > tr > th {
border-bottom-width: 1px;
}

File diff suppressed because one or more lines are too long

View File

@ -122,7 +122,10 @@ NINJA.decodeJavascript = function(invoice, javascript)
field = match.substring(2, match.indexOf('Label'));
field = toSnakeCase(field);
var value = getDescendantProp(invoice, field);
if (match.indexOf('?') < 0) {
if (match.indexOf('?') < 0 || value) {
if (invoice.partial && field == 'balance_due') {
field = 'amount_due';
}
var label = invoiceLabels[field];
if (match.indexOf('UC') >= 0) {
if (!label) console.log('match: ' + field);
@ -139,7 +142,7 @@ NINJA.decodeJavascript = function(invoice, javascript)
}
// search/replace values
var regExp = new RegExp('"\\$\\\w*?Value"', 'g');
var regExp = new RegExp('"\\$[\\\w\\\.]*?Value"', 'g');
var matches = javascript.match(regExp);
if (matches) {
@ -148,6 +151,7 @@ NINJA.decodeJavascript = function(invoice, javascript)
field = match.substring(2, match.indexOf('Value'));
field = toSnakeCase(field);
var value = getDescendantProp(invoice, field) || ' ';
if (field.toLowerCase().indexOf('date') >= 0 && value != ' ') {
value = moment(value, 'YYYY-MM-DD').format('MMM D YYYY');
}
@ -186,14 +190,14 @@ NINJA.invoiceColumns = function(invoice)
NINJA.invoiceLines = function(invoice) {
var grid = [
[
{text: invoiceLabels.item, style: ['tableHeader', 'itemTableHeader']},
{text: invoiceLabels.description, style: ['tableHeader', 'descriptionTableHeader']},
{text: invoiceLabels.unit_cost, style: ['tableHeader', 'costTableHeader']},
{text: invoiceLabels.quantity, style: ['tableHeader', 'qtyTableHeader']},
{text: invoice.has_taxes ? invoiceLabels.tax : '', style: ['tableHeader', 'taxTableHeader']},
{text: invoiceLabels.line_total, style: ['tableHeader', 'lineTotalTableHeader']}
]
[
{text: invoiceLabels.item, style: ['tableHeader', 'itemTableHeader']},
{text: invoiceLabels.description, style: ['tableHeader', 'descriptionTableHeader']},
{text: invoiceLabels.unit_cost, style: ['tableHeader', 'costTableHeader']},
{text: invoiceLabels.quantity, style: ['tableHeader', 'qtyTableHeader']},
{text: invoice.has_taxes ? invoiceLabels.tax : '', style: ['tableHeader', 'taxTableHeader']},
{text: invoiceLabels.line_total, style: ['tableHeader', 'lineTotalTableHeader']}
]
];
var total = 0;
@ -202,7 +206,7 @@ NINJA.invoiceLines = function(invoice) {
var hideQuantity = invoice.account.hide_quantity == '1';
for (var i = 0; i < invoice.invoice_items.length; i++) {
var row = [];
var item = invoice.invoice_items[i];
var cost = formatMoney(item.cost, currencyId, true);
@ -258,7 +262,7 @@ NINJA.invoiceLines = function(invoice) {
return NINJA.prepareDataTable(grid, 'invoiceItems');
}
NINJA.subtotals = function(invoice, removeBalance)
NINJA.subtotals = function(invoice, hideBalance)
{
if (!invoice) {
return;
@ -280,7 +284,8 @@ NINJA.subtotals = function(invoice, removeBalance)
}
if (invoice.tax && invoice.tax.name || invoice.tax_name) {
data.push([{text: invoiceLabels.tax}, {text: formatMoney(invoice.tax_amount, invoice.client.currency_id)}]);
var taxStr = invoice.tax_name + ' ' + (invoice.tax_rate*1).toString() + '%';
data.push([{text: taxStr}, {text: formatMoney(invoice.tax_amount, invoice.client.currency_id)}]);
}
if (NINJA.parseFloat(invoice.custom_value1) && invoice.custom_taxes1 != '1') {
@ -290,16 +295,17 @@ NINJA.subtotals = function(invoice, removeBalance)
data.push([{text: account.custom_invoice_label2}, {text: formatMoney(invoice.custom_value2, invoice.client.currency_id)}]);
}
var paid = invoice.amount - invoice.balance;
var paid = invoice.amount - invoice.balance;
if (invoice.account.hide_paid_to_date != '1' || paid) {
data.push([{text:invoiceLabels.paid_to_date}, {text:formatMoney(paid, invoice.client.currency_id)}]);
}
if (!removeBalance) {
if (!hideBalance) {
var isPartial = NINJA.parseFloat(invoice.partial);
data.push([
{text:invoice.is_quote ? invoiceLabels.balance_due : invoiceLabels.balance_due, style:['balanceDueLabel']},
{text:formatMoney(invoice.balance_amount, invoice.client.currency_id), style:['balanceDue']}
]);
{text: isPartial ? invoiceLabels.amount_due : invoiceLabels.balance_due, style:['balanceDueLabel']},
{text: formatMoney(invoice.balance_amount, invoice.client.currency_id), style:['balanceDue']}
]);
}
return NINJA.prepareDataPairs(data, 'subtotals');
@ -308,11 +314,11 @@ NINJA.subtotals = function(invoice, removeBalance)
NINJA.accountDetails = function(invoice) {
var account = invoice.account;
var data = [
{text:account.name, style: ['accountName']},
{text:account.id_number},
{text:account.vat_number},
{text:account.work_email},
{text:account.work_phone}
{text:account.name, style: ['accountName']},
{text:account.id_number},
{text:account.vat_number},
{text:account.work_email},
{text:account.work_phone}
];
return NINJA.prepareDataList(data, 'accountDetails');
}
@ -324,11 +330,11 @@ NINJA.accountAddress = function(invoice) {
cityStatePostal = ((account.city ? account.city + ', ' : '') + account.state + ' ' + account.postal_code).trim();
}
var data = [
{text: account.address1},
{text: account.address2},
{text: cityStatePostal},
{text: account.country ? account.country.name : ''}
var data = [
{text: account.address1},
{text: account.address2},
{text: cityStatePostal},
{text: account.country ? account.country.name : ''}
];
return NINJA.prepareDataList(data, 'accountAddress');
@ -337,42 +343,43 @@ NINJA.accountAddress = function(invoice) {
NINJA.invoiceDetails = function(invoice) {
var data = [
[
{text: (invoice.is_quote ? invoiceLabels.quote_number : invoiceLabels.invoice_number), style: ['invoiceNumberLabel']},
{text: invoice.invoice_number, style: ['invoiceNumber']}
],
[
{text: invoiceLabels.po_number},
{text: invoice.po_number}
],
[
{text: invoiceLabels.invoice_date},
{text: invoice.invoice_date}
],
[
{text: invoiceLabels.due_date},
{text: invoice.due_date}
]
[
{text: (invoice.is_quote ? invoiceLabels.quote_number : invoiceLabels.invoice_number), style: ['invoiceNumberLabel']},
{text: invoice.invoice_number, style: ['invoiceNumber']}
],
[
{text: invoiceLabels.po_number},
{text: invoice.po_number}
],
[
{text: (invoice.is_quote ? invoiceLabels.quote_date : invoiceLabels.invoice_date)},
{text: invoice.invoice_date}
],
[
{text: invoiceLabels.due_date},
{text: invoice.due_date}
]
];
var isPartial = NINJA.parseFloat(invoice.partial);
if (NINJA.parseFloat(invoice.balance) < NINJA.parseFloat(invoice.amount)) {
data.push([
{text: invoiceLabels.total},
{text: formatMoney(invoice.amount, invoice.client.currency_id)}
]);
}
if (NINJA.parseFloat(invoice.partial)) {
]);
} else if (isPartial) {
data.push([
{text: invoiceLabels.balance},
{text: invoiceLabels.total},
{text: formatMoney(invoice.total_amount, invoice.client.currency_id)}
]);
]);
}
data.push([
{text: invoiceLabels.balance_due, style: ['invoiceDetailBalanceDueLabel']},
{text: isPartial ? invoiceLabels.amount_due : invoiceLabels.balance_due, style: ['invoiceDetailBalanceDueLabel']},
{text: formatMoney(invoice.balance_amount, invoice.client.currency_id), style: ['invoiceDetailBalanceDue']}
])
])
return NINJA.prepareDataPairs(data, 'invoiceDetails');
}
@ -388,11 +395,11 @@ NINJA.clientDetails = function(invoice) {
var clientEmail = client.contacts[0].email == clientName ? '' : client.contacts[0].email;
data = [
{text:clientName || ' ', style: ['clientName']},
{text:client.address1},
{text:concatStrings(client.city, client.state, client.postal_code)},
{text:client.country ? client.country.name : ''},
{text:clientEmail}
{text:clientName || ' ', style: ['clientName']},
{text:client.address1},
{text:concatStrings(client.city, client.state, client.postal_code)},
{text:client.country ? client.country.name : ''},
{text:clientEmail}
];
return NINJA.prepareDataList(data, 'clientDetails');

View File

@ -13,7 +13,7 @@ function generatePDF(invoice, javascript, force, cb) {
if (!invoice || !javascript) {
return;
}
console.log('== generatePDF - force: %s', force);
//console.log('== generatePDF - force: %s', force);
if (force || !invoiceOld) {
refreshTimer = null;
} else {
@ -877,9 +877,6 @@ function displayGrid(doc, invoice, data, x, y, layout, options) {
key = invoice.account[key];
} else if (key === 'tax' && invoice.tax_name) {
key = invoice.tax_name + ' ' + (invoice.tax_rate*1).toString() + '%';
if (invoice.tax_name.toLowerCase().indexOf(invoiceLabels['tax'].toLowerCase()) == -1) {
key = invoiceLabels['tax'] + ': ' + key;
}
} else if (key === 'discount' && NINJA.parseFloat(invoice.discount) && !parseInt(invoice.is_amount_discount)) {
key = invoiceLabels[key] + ' ' + parseFloat(invoice.discount) + '%';
} else {

View File

@ -698,16 +698,15 @@ return array(
'email_receipt' => 'Email payment receipt to the client',
'created_payment_emailed_client' => 'Successfully created payment and emailed client',
'add_account' => 'Add Account',
'add_company' => 'Add Company',
'untitled' => 'Untitled',
'new_account' => 'New Account',
'new_company' => 'New Company',
'associated_accounts' => 'Successfully linked accounts',
'unlinked_account' => 'Successfully unlinked accounts',
'login' => 'Login',
'or' => 'or',
'email_error' => 'There was a problem sending the email',
'created_by_recurring' => 'Created by recurring invoice :invoice',
'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',
@ -738,6 +737,18 @@ return array(
'header' => 'Header',
'footer' => 'Footer',
'custom' => 'Custom',
'invoice_to' => 'Invoice to',
'invoice_no' => 'Invoice No.',
'recent_payments' => 'Recent Payments',
'outstanding' => 'Outstanding',
'manage_companies' => 'Manage Companies',
'total_revenue' => 'Total Revenue',
'current_user' => 'Current User',
'new_recurring_invoice' => 'New Recurring Invoice',
'recurring_invoice' => 'Recurring Invoice',
'recurring_too_soon' => 'It\'s too soon to create the next recurring invoice',
'created_by_invoice' => 'Created by :invoice',

View File

@ -688,48 +688,57 @@ return array(
'email_receipt' => 'Zahlungsbestätigung an Kunden per E-Mail senden',
'created_payment_emailed_client' => 'Zahlung erfolgreich erstellt und Kunde per E-Mail benachrichtigt',
'add_account' => 'Konto hinzufügen',
'add_company' => 'Konto hinzufügen',
'untitled' => 'Unbenannt',
'new_account' => 'Neues Konto',
'new_company' => 'Neues Konto',
'associated_accounts' => 'Konten erfolgreich verlinkt',
'unlinked_account' => 'Konten erfolgreich getrennt',
'login' => 'Login',
'or' => 'oder',
'email_error' => 'There was a problem sending the email',
'created_by_recurring' => 'Created by recurring invoice :invoice',
'confirm_recurring_timing' => 'Note: emails are sent at the start of the hour.',
'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',
'utf8_invoices' => 'New PDF Engine <sup>Beta</sup>',
'from' => 'From',
'to' => 'To',
'font_size' => 'Font Size',
'primary_color' => 'Primary Color',
'secondary_color' => 'Secondary Color',
'customize_design' => 'Customize Design',
'email_error' => 'Es gab ein Problem beim Senden dieses E-Mails.',
'confirm_recurring_timing' => 'Beachten Sie: E-Mails werden zu Beginn der Stunde gesendet.',
'old_browser' => 'Bitte verwenden Sie einen <a href="'.OUTDATE_BROWSER_URL.'" target="_blank">neueren Browser</a>',
'payment_terms_help' => 'Setzt das Standardfälligkeitssdatum',
'unlink_account' => 'Konten Trennen',
'unlink' => 'Trennen',
'show_address' => 'Adresse Anzeigen',
'show_address_help' => 'Verlange von Kunden ihre Rechnungsadresse anzugeben',
'update_address' => 'Adresse Aktualisieren',
'update_address_help' => 'Kundenadresse mit den gemachten Angaben aktualisieren',
'times' => 'Zeiten',
'set_now' => 'Jetzt setzen',
'dark_mode' => 'Dunkler Modus',
'dark_mode_help' => 'Weisser Text auf schwarzem Hintergrund anzeigen',
'add_to_invoice' => 'Zur Rechnung :invoice hinzufügen',
'create_new_invoice' => 'Neue Rechnung erstellen',
'task_errors' => 'Bitte korrigieren Sie alle überlappenden Zeiten',
'utf8_invoices' => 'Neue PDF Engine <sup>Beta</sup>',
'from' => 'Von',
'to' => 'An',
'font_size' => 'Schriftgrösse',
'primary_color' => 'Primäre Farbe',
'secondary_color' => 'Sekundäre Farbe',
'customize_design' => 'Design Anpassen',
'content' => 'Content',
'styles' => 'Styles',
'defaults' => 'Defaults',
'margins' => 'Margins',
'header' => 'Header',
'footer' => 'Footer',
'custom' => 'Custom',
'content' => 'Inhalt',
'styles' => 'Stile',
'defaults' => 'Standards',
'margins' => 'Aussenabstände',
'header' => 'Kopfzeile',
'footer' => 'Fusszeile',
'custom' => 'Benutzerdefiniert',
'invoice_to' => 'Rechnunge an',
'invoice_no' => 'Rechnung Nr.',
'recent_payments' => 'Kürzliche Zahlungen',
'manage_companies' => 'Manage Companies',
'total_revenue' => 'Total Revenue',
'current_user' => 'Current User',
'new_recurring_invoice' => 'New Recurring Invoice',
'recurring_invoice' => 'Recurring Invoice',
'recurring_too_soon' => 'It\'s too soon to create the next recurring invoice',
'created_by_invoice' => 'Created by :invoice',
);

View File

@ -119,8 +119,8 @@ return array(
'active_client' => 'active client',
'active_clients' => 'active clients',
'invoices_past_due' => 'Invoices Past Due',
'upcoming_invoices' => 'Upcoming invoices',
'average_invoice' => 'Average invoice',
'upcoming_invoices' => 'Upcoming Invoices',
'average_invoice' => 'Average Invoice',
// list pages
'archive' => 'Archive',
@ -696,16 +696,15 @@ return array(
'email_receipt' => 'Email payment receipt to the client',
'created_payment_emailed_client' => 'Successfully created payment and emailed client',
'add_account' => 'Add Account',
'add_company' => 'Add Company',
'untitled' => 'Untitled',
'new_account' => 'New Account',
'new_company' => 'New Company',
'associated_accounts' => 'Successfully linked accounts',
'unlinked_account' => 'Successfully unlinked accounts',
'login' => 'Login',
'or' => 'or',
'email_error' => 'There was a problem sending the email',
'created_by_recurring' => 'Created by recurring invoice :invoice',
'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',
@ -738,6 +737,17 @@ return array(
'custom' => 'Custom',
'invoice_to' => 'Invoice to',
'invoice_no' => 'Invoice No.',
'recent_payments' => 'Recent Payments',
'outstanding' => 'Outstanding',
'manage_companies' => 'Manage Companies',
'total_revenue' => 'Total Revenue',
'current_user' => 'Current User',
'new_recurring_invoice' => 'New Recurring Invoice',
'recurring_invoice' => 'Recurring Invoice',
'recurring_too_soon' => 'It\'s too soon to create the next recurring invoice',
'created_by_invoice' => 'Created by :invoice',
);

View File

@ -668,16 +668,15 @@ return array(
'email_receipt' => 'Email payment receipt to the client',
'created_payment_emailed_client' => 'Successfully created payment and emailed client',
'add_account' => 'Add Account',
'add_company' => 'Add Company',
'untitled' => 'Untitled',
'new_account' => 'New Account',
'new_company' => 'New Company',
'associated_accounts' => 'Successfully linked accounts',
'unlinked_account' => 'Successfully unlinked accounts',
'login' => 'Login',
'or' => 'or',
'email_error' => 'There was a problem sending the email',
'created_by_recurring' => 'Created by recurring invoice :invoice',
'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',
@ -708,6 +707,18 @@ return array(
'header' => 'Header',
'footer' => 'Footer',
'custom' => 'Custom',
'invoice_to' => 'Invoice to',
'invoice_no' => 'Invoice No.',
'recent_payments' => 'Recent Payments',
'outstanding' => 'Outstanding',
'manage_companies' => 'Manage Companies',
'total_revenue' => 'Total Revenue',
'current_user' => 'Current User',
'new_recurring_invoice' => 'New Recurring Invoice',
'recurring_invoice' => 'Recurring Invoice',
'recurring_too_soon' => 'It\'s too soon to create the next recurring invoice',
'created_by_invoice' => 'Created by :invoice',
);

View File

@ -697,16 +697,15 @@ return array(
'email_receipt' => 'Email payment receipt to the client',
'created_payment_emailed_client' => 'Successfully created payment and emailed client',
'add_account' => 'Add Account',
'add_company' => 'Add Company',
'untitled' => 'Untitled',
'new_account' => 'New Account',
'new_company' => 'New Company',
'associated_accounts' => 'Successfully linked accounts',
'unlinked_account' => 'Successfully unlinked accounts',
'login' => 'Login',
'or' => 'or',
'email_error' => 'There was a problem sending the email',
'created_by_recurring' => 'Created by recurring invoice :invoice',
'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',
@ -737,6 +736,18 @@ return array(
'header' => 'Header',
'footer' => 'Footer',
'custom' => 'Custom',
'invoice_to' => 'Invoice to',
'invoice_no' => 'Invoice No.',
'recent_payments' => 'Recent Payments',
'outstanding' => 'Outstanding',
'manage_companies' => 'Manage Companies',
'total_revenue' => 'Total Revenue',
'current_user' => 'Current User',
'new_recurring_invoice' => 'New Recurring Invoice',
'recurring_invoice' => 'Recurring Invoice',
'recurring_too_soon' => 'It\'s too soon to create the next recurring invoice',
'created_by_invoice' => 'Created by :invoice',

View File

@ -689,16 +689,15 @@ return array(
'email_receipt' => 'Email payment receipt to the client',
'created_payment_emailed_client' => 'Paiement crée avec succès et envoyé au client',
'add_account' => 'Ajouter compte',
'add_company' => 'Ajouter compte',
'untitled' => 'Sans titre',
'new_account' => 'Nouveau compte',
'new_company' => 'Nouveau compte',
'associated_accounts' => 'Successfully linked accounts',
'unlinked_account' => 'Successfully unlinked accounts',
'login' => 'Connexion',
'or' => 'ou',
'email_error' => 'There was a problem sending the email',
'created_by_recurring' => 'Created by recurring invoice :invoice',
'confirm_recurring_timing' => 'Note: emails are sent at the start of the hour.',
'old_browser' => 'Please use a <a href="'.OUTDATE_BROWSER_URL.'" target="_blank">newer browser</a>',
'payment_terms_help' => 'Sets the default invoice due date',
@ -729,6 +728,18 @@ return array(
'header' => 'Header',
'footer' => 'Footer',
'custom' => 'Custom',
'invoice_to' => 'Invoice to',
'invoice_no' => 'Invoice No.',
'recent_payments' => 'Recent Payments',
'outstanding' => 'Outstanding',
'manage_companies' => 'Manage Companies',
'total_revenue' => 'Total Revenue',
'current_user' => 'Current User',
'new_recurring_invoice' => 'New Recurring Invoice',
'recurring_invoice' => 'Recurring Invoice',
'recurring_too_soon' => 'It\'s too soon to create the next recurring invoice',
'created_by_invoice' => 'Created by :invoice',

View File

@ -690,16 +690,15 @@ return array(
'email_receipt' => 'Email payment receipt to the client',
'created_payment_emailed_client' => 'Successfully created payment and emailed client',
'add_account' => 'Add Account',
'add_company' => 'Add Company',
'untitled' => 'Untitled',
'new_account' => 'New Account',
'new_company' => 'New Company',
'associated_accounts' => 'Successfully linked accounts',
'unlinked_account' => 'Successfully unlinked accounts',
'login' => 'Login',
'or' => 'or',
'email_error' => 'There was a problem sending the email',
'created_by_recurring' => 'Created by recurring invoice :invoice',
'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',
@ -730,6 +729,18 @@ return array(
'header' => 'Header',
'footer' => 'Footer',
'custom' => 'Custom',
'invoice_to' => 'Invoice to',
'invoice_no' => 'Invoice No.',
'recent_payments' => 'Recent Payments',
'outstanding' => 'Outstanding',
'manage_companies' => 'Manage Companies',
'total_revenue' => 'Total Revenue',
'current_user' => 'Current User',
'new_recurring_invoice' => 'New Recurring Invoice',
'recurring_invoice' => 'Recurring Invoice',
'recurring_too_soon' => 'It\'s too soon to create the next recurring invoice',
'created_by_invoice' => 'Created by :invoice',
);

View File

@ -692,16 +692,15 @@ return array(
'email_receipt' => 'Email payment receipt to the client',
'created_payment_emailed_client' => 'Successfully created payment and emailed client',
'add_account' => 'Add Account',
'add_company' => 'Add Company',
'untitled' => 'Untitled',
'new_account' => 'New Account',
'new_company' => 'New Company',
'associated_accounts' => 'Successfully linked accounts',
'unlinked_account' => 'Successfully unlinked accounts',
'login' => 'Login',
'or' => 'or',
'email_error' => 'There was a problem sending the email',
'created_by_recurring' => 'Created by recurring invoice :invoice',
'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',
@ -732,6 +731,18 @@ return array(
'header' => 'Header',
'footer' => 'Footer',
'custom' => 'Custom',
'invoice_to' => 'Invoice to',
'invoice_no' => 'Invoice No.',
'recent_payments' => 'Recent Payments',
'outstanding' => 'Outstanding',
'manage_companies' => 'Manage Companies',
'total_revenue' => 'Total Revenue',
'current_user' => 'Current User',
'new_recurring_invoice' => 'New Recurring Invoice',
'recurring_invoice' => 'Recurring Invoice',
'recurring_too_soon' => 'It\'s too soon to create the next recurring invoice',
'created_by_invoice' => 'Created by :invoice',

View File

@ -699,16 +699,15 @@ return array(
'email_receipt' => 'Email payment receipt to the client',
'created_payment_emailed_client' => 'Successfully created payment and emailed client',
'add_account' => 'Add Account',
'add_company' => 'Add Company',
'untitled' => 'Untitled',
'new_account' => 'New Account',
'new_company' => 'New Company',
'associated_accounts' => 'Successfully linked accounts',
'unlinked_account' => 'Successfully unlinked accounts',
'login' => 'Login',
'or' => 'or',
'email_error' => 'There was a problem sending the email',
'created_by_recurring' => 'Created by recurring invoice :invoice',
'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',
@ -739,6 +738,18 @@ return array(
'header' => 'Header',
'footer' => 'Footer',
'custom' => 'Custom',
'invoice_to' => 'Invoice to',
'invoice_no' => 'Invoice No.',
'recent_payments' => 'Recent Payments',
'outstanding' => 'Outstanding',
'manage_companies' => 'Manage Companies',
'total_revenue' => 'Total Revenue',
'current_user' => 'Current User',
'new_recurring_invoice' => 'New Recurring Invoice',
'recurring_invoice' => 'Recurring Invoice',
'recurring_too_soon' => 'It\'s too soon to create the next recurring invoice',
'created_by_invoice' => 'Created by :invoice',

View File

@ -697,16 +697,15 @@ return array(
'email_receipt' => 'Email payment receipt to the client',
'created_payment_emailed_client' => 'Successfully created payment and emailed client',
'add_account' => 'Add Account',
'add_company' => 'Add Company',
'untitled' => 'Untitled',
'new_account' => 'New Account',
'new_company' => 'New Company',
'associated_accounts' => 'Successfully linked accounts',
'unlinked_account' => 'Successfully unlinked accounts',
'login' => 'Login',
'or' => 'or',
'email_error' => 'There was a problem sending the email',
'created_by_recurring' => 'Created by recurring invoice :invoice',
'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',
@ -737,6 +736,18 @@ return array(
'header' => 'Header',
'footer' => 'Footer',
'custom' => 'Custom',
'invoice_to' => 'Invoice to',
'invoice_no' => 'Invoice No.',
'recent_payments' => 'Recent Payments',
'outstanding' => 'Outstanding',
'manage_companies' => 'Manage Companies',
'total_revenue' => 'Total Revenue',
'current_user' => 'Current User',
'new_recurring_invoice' => 'New Recurring Invoice',
'recurring_invoice' => 'Recurring Invoice',
'recurring_too_soon' => 'It\'s too soon to create the next recurring invoice',
'created_by_invoice' => 'Created by :invoice',

View File

@ -692,16 +692,15 @@ return array(
'email_receipt' => 'Email payment receipt to the client',
'created_payment_emailed_client' => 'Successfully created payment and emailed client',
'add_account' => 'Add Account',
'add_company' => 'Add Company',
'untitled' => 'Untitled',
'new_account' => 'New Account',
'new_company' => 'New Company',
'associated_accounts' => 'Successfully linked accounts',
'unlinked_account' => 'Successfully unlinked accounts',
'login' => 'Login',
'or' => 'or',
'email_error' => 'There was a problem sending the email',
'created_by_recurring' => 'Created by recurring invoice :invoice',
'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',
@ -732,6 +731,18 @@ return array(
'header' => 'Header',
'footer' => 'Footer',
'custom' => 'Custom',
'invoice_to' => 'Invoice to',
'invoice_no' => 'Invoice No.',
'recent_payments' => 'Recent Payments',
'outstanding' => 'Outstanding',
'manage_companies' => 'Manage Companies',
'total_revenue' => 'Total Revenue',
'current_user' => 'Current User',
'new_recurring_invoice' => 'New Recurring Invoice',
'recurring_invoice' => 'Recurring Invoice',
'recurring_too_soon' => 'It\'s too soon to create the next recurring invoice',
'created_by_invoice' => 'Created by :invoice',

View File

@ -692,16 +692,15 @@ return array(
'email_receipt' => 'Email payment receipt to the client',
'created_payment_emailed_client' => 'Successfully created payment and emailed client',
'add_account' => 'Add Account',
'add_company' => 'Add Company',
'untitled' => 'Untitled',
'new_account' => 'New Account',
'new_company' => 'New Company',
'associated_accounts' => 'Successfully linked accounts',
'unlinked_account' => 'Successfully unlinked accounts',
'login' => 'Login',
'or' => 'or',
'email_error' => 'There was a problem sending the email',
'created_by_recurring' => 'Created by recurring invoice :invoice',
'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',
@ -732,6 +731,18 @@ return array(
'header' => 'Header',
'footer' => 'Footer',
'custom' => 'Custom',
'invoice_to' => 'Invoice to',
'invoice_no' => 'Invoice No.',
'recent_payments' => 'Recent Payments',
'outstanding' => 'Outstanding',
'manage_companies' => 'Manage Companies',
'total_revenue' => 'Total Revenue',
'current_user' => 'Current User',
'new_recurring_invoice' => 'New Recurring Invoice',
'recurring_invoice' => 'Recurring Invoice',
'recurring_too_soon' => 'It\'s too soon to create the next recurring invoice',
'created_by_invoice' => 'Created by :invoice',
);

View File

@ -695,16 +695,15 @@ return array(
'email_receipt' => 'Email payment receipt to the client',
'created_payment_emailed_client' => 'Successfully created payment and emailed client',
'add_account' => 'Add Account',
'add_company' => 'Add Company',
'untitled' => 'Untitled',
'new_account' => 'New Account',
'new_company' => 'New Company',
'associated_accounts' => 'Successfully linked accounts',
'unlinked_account' => 'Successfully unlinked accounts',
'login' => 'Login',
'or' => 'or',
'email_error' => 'There was a problem sending the email',
'created_by_recurring' => 'Created by recurring invoice :invoice',
'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',
@ -735,6 +734,18 @@ return array(
'header' => 'Header',
'footer' => 'Footer',
'custom' => 'Custom',
'invoice_to' => 'Invoice to',
'invoice_no' => 'Invoice No.',
'recent_payments' => 'Recent Payments',
'outstanding' => 'Outstanding',
'manage_companies' => 'Manage Companies',
'total_revenue' => 'Total Revenue',
'current_user' => 'Current User',
'new_recurring_invoice' => 'New Recurring Invoice',
'recurring_invoice' => 'Recurring Invoice',
'recurring_too_soon' => 'It\'s too soon to create the next recurring invoice',
'created_by_invoice' => 'Created by :invoice',

View File

@ -18,12 +18,12 @@
{{ Former::populate($account) }}
@if ($showUser)
{{ Former::populateField('first_name', $account->users()->first()->first_name) }}
{{ Former::populateField('last_name', $account->users()->first()->last_name) }}
{{ Former::populateField('email', $account->users()->first()->email) }}
{{ Former::populateField('phone', $account->users()->first()->phone) }}
{{ Former::populateField('first_name', $primaryUser->first_name) }}
{{ Former::populateField('last_name', $primaryUser->last_name) }}
{{ Former::populateField('email', $primaryUser->email) }}
{{ Former::populateField('phone', $primaryUser->phone) }}
@if (Utils::isNinja())
{{ Former::populateField('dark_mode', intval($account->users()->first()->dark_mode)) }}
{{ Former::populateField('dark_mode', intval($primaryUser->dark_mode)) }}
@endif
@endif
@ -51,7 +51,7 @@
@if (file_exists($account->getLogoPath()))
<center>
{!! HTML::image($account->getLogoPath().'?no_cache='.time(), "Logo") !!} &nbsp;
{!! HTML::image($account->getLogoPath().'?no_cache='.time(), 'Logo', ['width' => 200]) !!} &nbsp;
<a href="#" onclick="deleteLogo()">{{ trans('texts.remove_logo') }}</a>
</center><br/>
@endif

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,18 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<html lang="{{ App::getLocale() }}">
<head>
<meta charset="utf-8">
</head>
<body>
{{ $client }},<p/>
{{ $client }},<p/>
{{ trans('texts.payment_message', ['amount' => $amount]) }}<p/>
{{ trans('texts.payment_message', ['amount' => $amount]) }}<p/>
{{ $license }}<p/>
{{ $license }}<p/>
{{ trans('texts.email_signature') }}<br/>
{{ $account }}
</body>
{{ trans('texts.email_signature') }}<br/>
{{ $account }}
</body>
</html>

View File

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

View File

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

View File

@ -203,19 +203,6 @@
});
}
function showUnlink(userAccountId, userId) {
NINJA.unlink = {
'userAccountId': userAccountId,
'userId': userId
};
$('#unlinkModal').modal('show');
return false;
}
function unlinkAccount() {
window.location = '{{ URL::to('/unlink_account') }}' + '/' + NINJA.unlink.userAccountId + '/' + NINJA.unlink.userId;
}
function wordWrapText(value, width)
{
@if (Auth::user()->account->auto_wrap)
@ -266,12 +253,12 @@
}, 2000);
$('#search').blur(function(){
$('#search').css('width', '150px');
$('#search').css('width', '{{ Utils::isEnglish() ? 150 : 110 }}px');
$('ul.navbar-right').show();
});
$('#search').focus(function(){
$('#search').css('width', '256px');
$('#search').css('width', '{{ Utils::isEnglish() ? 256 : 216 }}px');
$('ul.navbar-right').hide();
if (!window.hasOwnProperty('searchData')) {
$.get('{{ URL::route('getSearchData') }}', function(data) {
@ -389,7 +376,7 @@
<span class="caret"></span>
</div>
</button>
<ul class="dropdown-menu user-accounts" role="menu">
<ul class="dropdown-menu user-accounts">
@if (session(SESSION_USER_ACCOUNTS))
@foreach (session(SESSION_USER_ACCOUNTS) as $item)
@if ($item->user_id == Auth::user()->id)
@ -398,9 +385,8 @@
'user_id' => $item->user_id,
'account_name' => $item->account_name,
'user_name' => $item->user_name,
'account_key' => $item->account_key,
'logo_path' => isset($item->logo_path) ? $item->logo_path : "",
'selected' => true,
'show_remove' => count(session(SESSION_USER_ACCOUNTS)) > 1,
])
@endif
@endforeach
@ -411,9 +397,8 @@
'user_id' => $item->user_id,
'account_name' => $item->account_name,
'user_name' => $item->user_name,
'account_key' => $item->account_key,
'logo_path' => isset($item->logo_path) ? $item->logo_path : "",
'selected' => false,
'show_remove' => count(session(SESSION_USER_ACCOUNTS)) > 1,
])
@endif
@endforeach
@ -421,13 +406,15 @@
@include('user_account', [
'account_name' => Auth::user()->account->name ?: trans('texts.untitled'),
'user_name' => Auth::user()->getDisplayName(),
'account_key' => Auth::user()->account->account_key,
'logo_path' => Auth::user()->account->getLogoPath(),
'selected' => true,
])
@endif
<li class="divider"></li>
@if (!session(SESSION_USER_ACCOUNTS) || count(session(SESSION_USER_ACCOUNTS)) < 5)
<li>{!! link_to('/login?new_account=true', trans('texts.add_account')) !!}</li>
@if (count(session(SESSION_USER_ACCOUNTS)) > 1)
<li>{!! link_to('/manage_companies', trans('texts.manage_companies')) !!}</li>
@elseif (!session(SESSION_USER_ACCOUNTS) || count(session(SESSION_USER_ACCOUNTS)) < 5)
<li>{!! link_to('/login?new_company=true', trans('texts.add_company')) !!}</li>
@endif
<li>{!! link_to('#', trans('texts.logout'), array('onclick'=>'logout()')) !!}</li>
</ul>
@ -462,7 +449,9 @@
<li><a href="#">{{ trans('texts.no_items') }}</a></li>
@else
@foreach (Session::get(RECENTLY_VIEWED) as $link)
<li><a href="{{ $link->url }}">{{ $link->name }}</a></li>
@if (property_exists($link, 'accountId') && $link->accountId == Auth::user()->account_id)
<li><a href="{{ $link->url }}">{{ $link->name }}</a></li>
@endif
@endforeach
@endif
</ul>
@ -471,7 +460,7 @@
<form class="navbar-form navbar-right" role="search">
<div class="form-group">
<input type="text" id="search" style="width: 150px"
<input type="text" id="search" style="width: {{ Utils::isEnglish() ? 150 : 110 }}px"
class="form-control" placeholder="{{ trans('texts.search') }}">
</div>
</form>
@ -601,28 +590,6 @@
</div>
@endif
@if (Auth::check() && session(SESSION_USER_ACCOUNTS) && count(session(SESSION_USER_ACCOUNTS)))
<div class="modal fade" id="unlinkModal" tabindex="-1" role="dialog" aria-labelledby="unlinkModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" id="myModalLabel">{{ trans('texts.unlink_account') }}</h4>
</div>
<div class="container">
<h3>{{ trans('texts.are_you_sure') }}</h3>
</div>
<div class="modal-footer" id="signUpFooter">
<button type="button" class="btn btn-default" data-dismiss="modal">{{ trans('texts.cancel') }}</button>
<button type="button" class="btn btn-primary" onclick="unlinkAccount()">{{ trans('texts.unlink') }}</button>
</div>
</div>
</div>
</div>
@endif
@if (Auth::check() && !Auth::user()->isPro())
<div class="modal fade" id="proPlanModal" tabindex="-1" role="dialog" aria-labelledby="proPlanModalLabel" aria-hidden="true">
<div class="modal-dialog large-dialog">

View File

@ -17,8 +17,12 @@
@if ($invoice && $invoice->id)
<ol class="breadcrumb">
<li>{!! link_to(($entityType == ENTITY_QUOTE ? 'quotes' : 'invoices'), trans('texts.' . ($entityType == ENTITY_QUOTE ? 'quotes' : 'invoices'))) !!}</li>
<li class='active'>{{ $invoice->invoice_number }}</li>
@if ($isRecurring)
<li>{!! link_to('invoices', trans('texts.recurring_invoice')) !!}</li>
@else
<li>{!! link_to(($entityType == ENTITY_QUOTE ? 'quotes' : 'invoices'), trans('texts.' . ($entityType == ENTITY_QUOTE ? 'quotes' : 'invoices'))) !!}</li>
<li class='active'>{{ $invoice->invoice_number }}</li>
@endif
</ol>
@endif
@ -53,7 +57,7 @@
<div class="form-group" style="margin-bottom: 8px">
<div class="col-lg-8 col-sm-8 col-lg-offset-4 col-sm-offset-4">
<a id="createClientLink" class="pointer" data-bind="click: $root.showClientForm, text: $root.clientLinkText"></a>
<a id="createClientLink" class="pointer" data-bind="click: $root.showClientForm, html: $root.clientLinkText"></a>
<span data-bind="visible: $root.invoice().client().public_id() > 0">|
<a data-bind="attr: {href: '{{ url('/clients') }}/' + $root.invoice().client().public_id()}" target="_blank">{{ trans('texts.view_client') }}</a>
</span>
@ -79,53 +83,41 @@
<div class="col-md-4" id="col_2">
<div data-bind="visible: !is_recurring()">
{!! Former::text('invoice_date')->data_bind("datePicker: invoice_date, valueUpdate: 'afterkeydown'")->label(trans("texts.{$entityType}_date"))
->data_date_format(Session::get(SESSION_DATE_PICKER_FORMAT, DEFAULT_DATE_PICKER_FORMAT))->append('<i class="glyphicon glyphicon-calendar" onclick="toggleDatePicker(\'invoice_date\')"></i>') !!}
->data_date_format(Session::get(SESSION_DATE_PICKER_FORMAT, DEFAULT_DATE_PICKER_FORMAT))->appendIcon('calendar')->addGroupClass('invoice_date') !!}
{!! Former::text('due_date')->data_bind("datePicker: due_date, valueUpdate: 'afterkeydown'")
->data_date_format(Session::get(SESSION_DATE_PICKER_FORMAT, DEFAULT_DATE_PICKER_FORMAT))->append('<i class="glyphicon glyphicon-calendar" onclick="toggleDatePicker(\'due_date\')"></i>') !!}
->data_date_format(Session::get(SESSION_DATE_PICKER_FORMAT, DEFAULT_DATE_PICKER_FORMAT))->appendIcon('calendar')->addGroupClass('due_date') !!}
{!! Former::text('partial')->data_bind("value: partial, valueUpdate: 'afterkeydown'")->onchange('onPartialChange()')
->rel('tooltip')->data_toggle('tooltip')->data_placement('bottom')->title(trans('texts.partial_value')) !!}
</div>
@if ($entityType == ENTITY_INVOICE)
<div data-bind="visible: is_recurring" style="display: none">
{!! Former::select('frequency_id')->options($frequencies)->data_bind("value: frequency_id") !!}
{!! Former::select('frequency_id')->options($frequencies)->data_bind("value: frequency_id")
->appendIcon('question-sign')->addGroupClass('frequency_id') !!}
{!! Former::text('start_date')->data_bind("datePicker: start_date, valueUpdate: 'afterkeydown'")
->data_date_format(Session::get(SESSION_DATE_PICKER_FORMAT, DEFAULT_DATE_PICKER_FORMAT))->append('<i class="glyphicon glyphicon-calendar" onclick="toggleDatePicker(\'start_date\')"></i>') !!}
->data_date_format(Session::get(SESSION_DATE_PICKER_FORMAT, DEFAULT_DATE_PICKER_FORMAT))->appendIcon('calendar')->addGroupClass('start_date') !!}
{!! Former::text('end_date')->data_bind("datePicker: end_date, valueUpdate: 'afterkeydown'")
->data_date_format(Session::get(SESSION_DATE_PICKER_FORMAT, DEFAULT_DATE_PICKER_FORMAT))->append('<i class="glyphicon glyphicon-calendar" onclick="toggleDatePicker(\'end_date\')"></i>') !!}
->data_date_format(Session::get(SESSION_DATE_PICKER_FORMAT, DEFAULT_DATE_PICKER_FORMAT))->appendIcon('calendar')->addGroupClass('end_date') !!}
</div>
@if ($invoice && $invoice->recurring_invoice_id)
@if ($invoice && $invoice->recurring_invoice)
<div class="pull-right" style="padding-top: 6px">
{!! trans('texts.created_by_recurring', ['invoice' => link_to('/invoices/'.$invoice->recurring_invoice->public_id, $invoice->recurring_invoice->invoice_number)]) !!}
{!! trans('texts.created_by_invoice', ['invoice' => link_to('/invoices/'.$invoice->recurring_invoice->public_id, trans('texts.recurring_invoice'))]) !!}
</div>
@else
<div data-bind="visible: invoice_status_id() === 0">
<div class="form-group">
<label for="" class="control-label col-lg-4 col-sm-4">
{{ trans('texts.recurring') }}
</label>
<div class="col-lg-8 col-sm-8">
<div class="checkbox">
<label for="recurring" class="">
<input onclick="onRecurringEnabled()" data-bind="checked: is_recurring" id="recurring" type="checkbox" name="recurring" value="1">{{ trans('texts.enable') }} &nbsp;&nbsp;
<a href="#" onclick="showLearnMore()"><i class="glyphicon glyphicon-question-sign"></i> {{ trans('texts.learn_more') }}</a>
</label>
</div>
</div>
@elseif ($invoice && $invoice->last_sent_date)
<div class="pull-right" style="padding-top: 6px">
{!! trans('texts.last_invoice_sent', [
'date' => link_to('/invoices/'.$invoice->recurring_invoices->last()->public_id, Utils::dateToString($invoice->last_sent_date))
]) !!}
</div>
@if ($invoice && $invoice->last_sent_date)
<div class="pull-right">
{{ trans('texts.last_invoice_sent', ['date' => Utils::dateToString($invoice->last_sent_date)]) }}
</div>
@endif
</div>
@endif
@endif
@endif
</div>
<div class="col-md-4" id="col_2">
{!! Former::text('invoice_number')->label(trans("texts.{$entityType}_number_short"))->data_bind("value: invoice_number, valueUpdate: 'afterkeydown'") !!}
@if (!$isRecurring)
{!! Former::text('invoice_number')->label(trans("texts.{$entityType}_number_short"))->data_bind("value: invoice_number, valueUpdate: 'afterkeydown'") !!}
@endif
{!! Former::text('po_number')->label(trans('texts.po_number_short'))->data_bind("value: po_number, valueUpdate: 'afterkeydown'") !!}
{!! Former::text('discount')->data_bind("value: discount, valueUpdate: 'afterkeydown'")
->addGroupClass('discount-group')->type('number')->min('0')->step('any')->append(
@ -334,10 +326,7 @@
@if (!$invoice || (!$invoice->trashed() && !$invoice->client->trashed()))
{!! Button::success(trans("texts.save_{$entityType}"))->withAttributes(array('id' => 'saveButton', 'onclick' => 'onSaveClick()'))->appendIcon(Icon::create('floppy-disk')) !!}
@if (!$invoice || ($invoice && !$invoice->is_recurring))
{!! Button::info(trans("texts.email_{$entityType}"))->withAttributes(array('id' => 'email_button', 'onclick' => 'onEmailClick()'))->appendIcon(Icon::create('send')) !!}
@endif
{!! Button::info(trans("texts.email_{$entityType}"))->withAttributes(array('id' => 'email_button', 'onclick' => 'onEmailClick()'))->appendIcon(Icon::create('send')) !!}
@if ($invoice && $invoice->id)
{!! DropdownButton::normal(trans('texts.more_actions'))
@ -604,6 +593,20 @@
}, 1);
});
$('.frequency_id .input-group-addon').click(function() {
showLearnMore();
});
var fields = ['invoice_date', 'due_date', 'start_date', 'end_date'];
for (var i=0; i<fields.length; i++) {
var field = fields[i];
(function (_field) {
$('.' + _field + ' .input-group-addon').click(function() {
toggleDatePicker(_field);
});
})(field);
}
@if ($client || $invoice || count($clients) == 0)
$('#invoice_number').focus();
@else
@ -670,9 +673,15 @@
var product = products[i];
if (product.product_key == key) {
var model = ko.dataFor(this);
model.notes(product.notes);
model.cost(accounting.toFixed(product.cost,2));
model.qty(1);
if (!model.notes()) {
model.notes(product.notes);
}
if (!model.cost()) {
model.cost(accounting.toFixed(product.cost,2));
}
if (!model.qty()) {
model.qty(1);
}
break;
}
}
@ -688,6 +697,10 @@
invoice.is_quote = {{ $entityType == ENTITY_QUOTE ? 'true' : 'false' }};
invoice.contact = _.findWhere(invoice.client.contacts, {send_invoice: true});
if (invoice.is_recurring) {
invoice.invoice_number = '0000';
}
@if (!$invoice)
if (!invoice.terms) {
invoice.terms = wordWrapText('{!! str_replace(["\r\n","\r","\n"], '\n', addslashes($account->invoice_terms)) !!}', 300);
@ -1115,10 +1128,10 @@
var self = this;
this.client = ko.observable(data ? false : new ClientModel());
self.account = {!! $account !!};
this.id = ko.observable('');
self.id = ko.observable('');
self.discount = ko.observable('');
self.is_amount_discount = ko.observable(0);
self.frequency_id = ko.observable('');
self.frequency_id = ko.observable(4); // default to monthly
self.terms = ko.observable('');
self.default_terms = ko.observable({{ !$invoice && $account->invoice_terms ? 'true' : 'false' }} ? wordWrapText('{!! str_replace(["\r\n","\r","\n"], '\n', addslashes($account->invoice_terms)) !!}', 300) : '');
self.set_default_terms = ko.observable(false);
@ -1134,7 +1147,7 @@
self.end_date = ko.observable('');
self.tax_name = ko.observable();
self.tax_rate = ko.observable();
self.is_recurring = ko.observable(false);
self.is_recurring = ko.observable({{ $isRecurring ? 'true' : 'false' }});
self.invoice_status_id = ko.observable(0);
self.invoice_items = ko.observableArray();
self.amount = ko.observable(0);

View File

@ -74,6 +74,7 @@
var NINJA = NINJA || {};
NINJA.primaryColor = "{{ $account->primary_color }}";
NINJA.secondaryColor = "{{ $account->secondary_color }}";
NINJA.fontSize = {{ $account->font_size }};
var invoiceLabels = {!! json_encode($account->getInvoiceLabels()) !!};

View File

@ -24,13 +24,16 @@
</label>
<div id="top_right_buttons" class="pull-right">
<input id="tableFilter" type="text" style="width:140px;margin-right:17px;background-color: white !important" class="form-control pull-left" placeholder="{{ trans('texts.filter') }}"/>
<input id="tableFilter" type="text" style="width:140px;margin-right:17px;background-color: white !important" class="form-control pull-left" placeholder="{{ trans('texts.filter') }}"/>
@if (Auth::user()->isPro() && $entityType == ENTITY_INVOICE)
{!! Button::normal(trans('texts.quotes'))->asLinkTo(URL::to('/quotes'))->appendIcon(Icon::create('list')) !!}
@elseif ($entityType == ENTITY_CLIENT)
{!! Button::normal(trans('texts.credits'))->asLinkTo(URL::to('/credits'))->appendIcon(Icon::create('list')) !!}
@endif
{!! Button::primary(trans("texts.new_$entityType"))->asLinkTo(URL::to("/{$entityType}s/create"))->appendIcon(Icon::create('plus-sign')) !!}
@if ($entityType != ENTITY_TASK || Auth::user()->account->timezone_id)
{!! Button::primary(trans("texts.new_$entityType"))->asLinkTo(URL::to("/{$entityType}s/create"))->appendIcon(Icon::create('plus-sign')) !!}
@endif
</div>
@if (isset($secEntityType))

View File

@ -53,10 +53,13 @@
'sSearch': ''
}
} );
/*
$.extend( true, $.fn.datepicker.defaults, {
language:'{{App::getLocale()}}'
});
*/
</script>
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->

View File

@ -169,7 +169,7 @@ header h3 em {
<header>
@if ($client)
<h2>{{ $client->getDisplayName() }}</h2>
<h3>{{ trans('texts.invoice') . ' ' . $invoiceNumber }}<span>|&nbsp; {{ trans('texts.amount_due') }}: <em>{{ Utils::formatMoney($amount, $currencyId) }}</em></span></h3>
<h3>{{ trans('texts.invoice') . ' ' . $invoiceNumber }}<span>|&nbsp; {{ trans('texts.amount_due') }}: <em>{{ Utils::formatMoney($amount, $currencyId) }} {{ $currencyCode }}</em></span></h3>
@elseif ($paymentTitle)
<h2>{{ $paymentTitle }}<br/><small>{{ $paymentSubtitle }}</small></h2>
@endif

View File

@ -75,7 +75,7 @@
// remove quotes and recurring invoices
invoices = _.filter(invoices, function(invoice) {
return !parseInt(invoice.is_quote) && !parseInt(invoice.is_recurring);
return !parseInt(invoice.is_quote) && !invoice.is_recurring;
});
var products = _.flatten(_.pluck(invoices, 'invoice_items'));

View File

@ -4,10 +4,9 @@
<style type="text/css">
.time-input input,
.time-input select {
float: left;
input.time-input {
width: 110px;
font-size: 14px !important;
}
</style>
@ -79,13 +78,13 @@
<td style="padding: 0px 12px 12px 0 !important">
<div data-bind="css: { 'has-error': !isStartValid() }">
<input type="text" data-bind="value: startTime.pretty, event:{ change: $root.refresh }"
class="form-control" placeholder="{{ trans('texts.start_time') }}"/>
class="form-control time-input" placeholder="{{ trans('texts.start_time') }}"/>
</div>
</td>
<td style="padding: 0px 12px 12px 0 !important">
<div data-bind="css: { 'has-error': !isEndValid() }">
<input type="text" data-bind="value: endTime.pretty, event:{ change: $root.refresh }"
class="form-control" placeholder="{{ trans('texts.end_time') }}"/>
class="form-control time-input" placeholder="{{ trans('texts.end_time') }}"/>
</div>
</td>
<td style="width:100px">
@ -202,9 +201,6 @@
}
function TimeModel(data) {
console.log('== TimeModel ==');
console.log(data);
var self = this;
self.startTime = ko.observable(0);
self.endTime = ko.observable(0);
@ -223,7 +219,7 @@
});
self.startTime.pretty = ko.computed({
read: function() {
read: function() {
return self.startTime() ? moment.unix(self.startTime()).utcOffset({{ $minuteOffset }}).format('MMM D YYYY h:mm:ss a') : '';
},
write: function(data) {
@ -341,13 +337,6 @@
$clientSelect.combobox();
@if ($task)
$('#date').datepicker('update', new Date('{{ Utils::fromSqlDateTime($task->start_time) }}'));
@else
var date = new Date();
$('#date').datepicker('update', date);
@endif
@if (!$task && !$clientPublicId)
$('.client-select input.form-control').focus();
@else

View File

@ -1,16 +1,14 @@
<li style="margin-top: 4px; margin-bottom: 4px; min-width: 220px; cursor: pointer">
@if (isset($user_id) && $show_remove)
<a href='{{ URL::to("/switch_account/{$user_id}") }}'>
@if (isset($user_id) && $user_id != Auth::user()->id)
<a href="{{ URL::to("/switch_account/{$user_id}") }}">
@else
<a href='#' onclick="return false;">
<a href="{{ URL::to("/company/details") }}">
@endif
@if (isset($show_remove) && $show_remove)
<div class="pull-right glyphicon glyphicon-remove remove" onclick="return showUnlink({{ $user_account_id }}, {{ $user_id }})"></div>
@endif
@if (file_exists('logo/'.$account_key.'.jpg'))
<img class="pull-left" style="width: 40px; min-height: 40px; margin-right: 16px" src="{{ asset('logo/'.$account_key.'.jpg') }}"/>
@if (file_exists($logo_path))
<div class="pull-left" style="height: 40px; margin-right: 16px;">
<img style="width: 40px; margin-top:6px" src="{{ asset($logo_path) }}"/>
</div>
@else
<div class="pull-left" style="width: 40px; min-height: 40px; margin-right: 16px">&nbsp;</div>
@endif

View File

@ -0,0 +1,83 @@
@extends('header')
@section('content')
<center>
{!! Button::success(trans('texts.add_company'))->asLinkTo('/login?new_company=true') !!}
</center>
<p>&nbsp;</p>
<div class="row">
<div class="col-md-6 col-md-offset-3">
</div>
</div>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<div class="panel panel-default">
<div class="panel-body">
<table class="table table-striped">
@foreach (Session::get(SESSION_USER_ACCOUNTS) as $account)
<tr>
<td>
@if (isset($account->logo_path))
{!! HTML::image($account->logo_path.'?no_cache='.time(), 'Logo', ['width' => 100]) !!}
@endif
</td>
<td>
<h3>{{ $account->account_name }}<br/>
<small>{{ $account->user_name }}
@if ($account->user_id == Auth::user()->id)
| {{ trans('texts.current_user')}}
@endif
</small></h3>
</td>
<td>{!! Button::primary(trans('texts.unlink'))->withAttributes(['onclick'=>"return showUnlink({$account->id}, {$account->user_id})"]) !!}</td>
</tr>
@endforeach
</table>
</div>
</div>
</div>
</div>
<div class="modal fade" id="unlinkModal" tabindex="-1" role="dialog" aria-labelledby="unlinkModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" id="myModalLabel">{{ trans('texts.unlink_account') }}</h4>
</div>
<div class="container">
<h3>{{ trans('texts.are_you_sure') }}</h3>
</div>
<div class="modal-footer" id="signUpFooter">
<button type="button" class="btn btn-default" data-dismiss="modal">{{ trans('texts.cancel') }}</button>
<button type="button" class="btn btn-primary" onclick="unlinkAccount()">{{ trans('texts.unlink') }}</button>
</div>
</div>
</div>
</div>
<script type="text/javascript">
function showUnlink(userAccountId, userId) {
NINJA.unlink = {
'userAccountId': userAccountId,
'userId': userId
};
$('#unlinkModal').modal('show');
return false;
}
function unlinkAccount() {
window.location = '{{ URL::to('/unlink_account') }}' + '/' + NINJA.unlink.userAccountId + '/' + NINJA.unlink.userId;
}
</script>
@stop

View File

@ -65,7 +65,7 @@
"$notesAndTerms",
{
"table": {
"widths": ["*", "*"],
"widths": ["*", "40%"],
"body": "$subtotals"
},
"layout": {

View File

@ -3,7 +3,7 @@
"columns": [
{
"image": "$accountLogo",
"width": 140
"fit": [120, 80]
},
{
"stack": "$accountDetails",
@ -16,7 +16,7 @@
},
{
"text": "$entityTypeUC",
"margin": [8, 50, 8, 5],
"margin": [8, 30, 8, 5],
"style": "entityTypeLabel"
},
@ -30,7 +30,7 @@
"table": {
"body": "$invoiceDetails"
},
"margin": [0, 4, 12, 4],
"margin": [0, 4, 12, 4],
"layout": "noBorders"
},
{
@ -70,17 +70,17 @@
}
},
{
"columns": [
"columns": [
"$notesAndTerms",
{
"table": {
"widths": ["*", "*"],
"widths": ["*", "40%"],
"body": "$subtotals"
},
"layout": {
"hLineWidth": "$none",
"vLineWidth": "$none",
"paddingLeft": "$amount:8",
"paddingLeft": "$amount:34",
"paddingRight": "$amount:8",
"paddingTop": "$amount:4",
"paddingBottom": "$amount:4"
@ -97,12 +97,10 @@
"columns": [
{
"text": "$invoiceFooter",
"alignment": "left",
"margin": [0, 0, 0, 12]
"alignment": "left"
}
],
"margin": [40, -20, 40, 40]
"margin": [40, -20, 40, 0]
},
"styles": {
"entityTypeLabel": {
@ -116,6 +114,9 @@
"color": "$primaryColor:#37a3c6",
"bold": true
},
"invoiceDetails": {
"margin": [0, 0, 8, 0]
},
"accountDetails": {
"margin": [0, 2, 0, 2]
},
@ -136,10 +137,10 @@
"bold": true
},
"balanceDueLabel": {
"fontSize": "$fontSizeLargest"
"fontSize": "$fontSizeLarger"
},
"balanceDue": {
"fontSize": "$fontSizeLargest",
"fontSize": "$fontSizeLarger",
"color": "$primaryColor:#37a3c6"
},
"invoiceNumber": {
@ -147,7 +148,7 @@
},
"tableHeader": {
"bold": true,
"fontSize": "$fontSizeLargest"
"fontSize": "$fontSizeLarger"
},
"invoiceLineItemsTable": {
"margin": [0, 16, 0, 16]
@ -171,8 +172,7 @@
"alignment": "right"
},
"termsLabel": {
"bold": true,
"margin": [0, 0, 0, 4]
"bold": true
}
},
"pageMargins": [40, 40, 40, 60]

View File

@ -4,7 +4,7 @@
"columns": [
{
"image": "$accountLogo",
"width": 80,
"fit": [120, 80],
"margin": [0, 60, 0, 30]
},
{
@ -37,13 +37,13 @@
"$notesAndTerms",
{
"table": {
"widths": ["*", "*"],
"widths": ["*", "40%"],
"body": "$subtotalsWithoutBalance"
},
"layout": {
"hLineWidth": "$none",
"vLineWidth": "$none",
"paddingLeft": "$amount:8",
"paddingLeft": "$amount:34",
"paddingRight": "$amount:8",
"paddingTop": "$amount:4",
"paddingBottom": "$amount:4"

View File

@ -11,7 +11,7 @@
[
{
"image": "$accountLogo",
"width": 100
"fit": [120, 80]
}
]
]},
@ -66,13 +66,13 @@
{
"style": "subtotals",
"table": {
"widths": ["*", "*"],
"widths": ["*", "40%"],
"body": "$subtotals"
},
"layout": {
"hLineWidth": "$none",
"vLineWidth": "$none",
"paddingLeft": "$amount:8",
"paddingLeft": "$amount:34",
"paddingRight": "$amount:8",
"paddingTop": "$amount:4",
"paddingBottom": "$amount:4"