mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Bug fixes
This commit is contained in:
parent
5a28ff2612
commit
b368e5589c
@ -1,5 +1,8 @@
|
||||
<?php namespace App\Console\Commands;
|
||||
|
||||
use DB;
|
||||
use DateTime;
|
||||
use Carbon;
|
||||
use Illuminate\Console\Command;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
@ -10,7 +13,7 @@ use Symfony\Component\Console\Input\InputArgument;
|
||||
WARNING: Please backup your database before running this script
|
||||
##################################################################
|
||||
|
||||
Since the application was released a number of bugs have (inevitably) been found.
|
||||
Since the application was released a number of bugs have inevitably been found.
|
||||
Although the bugs have always been fixed in some cases they've caused the client's
|
||||
balance, paid to date and/or activity records to become inaccurate. This script will
|
||||
check for errors and correct the data.
|
||||
@ -100,13 +103,15 @@ class CheckData extends Command {
|
||||
|
||||
$clients = $clients->groupBy('clients.id', 'clients.balance', 'clients.created_at')
|
||||
->orderBy('clients.id', 'DESC')
|
||||
->get(['clients.id', 'clients.balance', 'clients.paid_to_date']);
|
||||
->get(['clients.account_id', 'clients.id', 'clients.balance', 'clients.paid_to_date', DB::raw('sum(invoices.balance) actual_balance')]);
|
||||
$this->info(count($clients) . ' clients with incorrect balance/activities');
|
||||
|
||||
foreach ($clients as $client) {
|
||||
$this->info("=== Client:{$client->id} Balance:{$client->balance} ===");
|
||||
$this->info("=== Client:{$client->id} Balance:{$client->balance} Actual Balance:{$client->actual_balance} ===");
|
||||
$foundProblem = false;
|
||||
$lastBalance = 0;
|
||||
$lastAdjustment = 0;
|
||||
$lastCreatedAt = null;
|
||||
$clientFix = false;
|
||||
$activities = DB::table('activities')
|
||||
->where('client_id', '=', $client->id)
|
||||
@ -195,6 +200,11 @@ class CheckData extends Command {
|
||||
$foundProblem = true;
|
||||
$clientFix -= $activity->adjustment;
|
||||
$activityFix = 0;
|
||||
} else if ((strtotime($activity->created_at) - strtotime($lastCreatedAt) <= 1) && $activity->adjustment > 0 && $activity->adjustment == $lastAdjustment) {
|
||||
$this->info("Duplicate adjustment for updated invoice adjustment:{$activity->adjustment}");
|
||||
$foundProblem = true;
|
||||
$clientFix -= $activity->adjustment;
|
||||
$activityFix = 0;
|
||||
}
|
||||
} elseif ($activity->activity_type_id == ACTIVITY_TYPE_UPDATE_QUOTE) {
|
||||
// **Fix for updating balance when updating a quote**
|
||||
@ -231,18 +241,32 @@ class CheckData extends Command {
|
||||
}
|
||||
|
||||
$lastBalance = $activity->balance;
|
||||
$lastAdjustment = $activity->adjustment;
|
||||
$lastCreatedAt = $activity->created_at;
|
||||
}
|
||||
|
||||
if ($clientFix !== false) {
|
||||
$balance = $activity->balance + $clientFix;
|
||||
$data = ['balance' => $balance];
|
||||
$this->info("Corrected balance:{$balance}");
|
||||
if ($activity->balance + $clientFix != $client->actual_balance) {
|
||||
$this->info("** Creating 'recovered update' activity **");
|
||||
if ($this->option('fix') == 'true') {
|
||||
DB::table('clients')
|
||||
->where('id', $client->id)
|
||||
->update($data);
|
||||
DB::table('activities')->insert([
|
||||
'created_at' => new Carbon,
|
||||
'updated_at' => new Carbon,
|
||||
'account_id' => $client->account_id,
|
||||
'client_id' => $client->id,
|
||||
'message' => 'Recovered update to invoice [<a href="https://github.com/hillelcoren/invoice-ninja/releases/tag/v1.7.1" target="_blank">details</a>]',
|
||||
'adjustment' => $client->actual_balance - $activity->balance,
|
||||
'balance' => $client->actual_balance,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$data = ['balance' => $client->actual_balance];
|
||||
$this->info("Corrected balance:{$client->actual_balance}");
|
||||
if ($this->option('fix') == 'true') {
|
||||
DB::table('clients')
|
||||
->where('id', $client->id)
|
||||
->update($data);
|
||||
}
|
||||
}
|
||||
|
||||
$this->info('Done');
|
||||
|
@ -51,6 +51,7 @@ class DashboardController extends BaseController
|
||||
|
||||
|
||||
$activities = Activity::where('activities.account_id', '=', Auth::user()->account_id)
|
||||
->where('activity_type_id', '>', 0)
|
||||
->orderBy('created_at', 'desc')->take(6)->get();
|
||||
|
||||
$pastDue = Invoice::scope()
|
||||
|
@ -59,8 +59,6 @@ class ReportController extends BaseController
|
||||
$enableChart = true;
|
||||
}
|
||||
|
||||
$padding = $groupBy == 'DAYOFYEAR' ? 'day' : ($groupBy == 'WEEK' ? 'week' : 'month');
|
||||
$endDate->modify('+1 '.$padding);
|
||||
$datasets = [];
|
||||
$labels = [];
|
||||
$maxTotals = 0;
|
||||
@ -155,7 +153,7 @@ class ReportController extends BaseController
|
||||
if ($enableChart) {
|
||||
foreach ([ENTITY_INVOICE, ENTITY_PAYMENT, ENTITY_CREDIT] as $entityType) {
|
||||
$records = DB::table($entityType.'s')
|
||||
->select(DB::raw('sum(amount) as total, '.$groupBy.'('.$entityType.'_date) as '.$groupBy))
|
||||
->select(DB::raw('sum(amount) as total, concat(YEAR('.$entityType.'_date), '.$groupBy.'('.$entityType.'_date)) as '.$groupBy))
|
||||
->where('account_id', '=', Auth::user()->account_id)
|
||||
->where($entityType.'s.is_deleted', '=', false)
|
||||
->where($entityType.'s.'.$entityType.'_date', '>=', $startDate->format('Y-m-d'))
|
||||
@ -171,14 +169,17 @@ class ReportController extends BaseController
|
||||
$dates = $records->lists($groupBy);
|
||||
$data = array_combine($dates, $totals);
|
||||
|
||||
$padding = $groupBy == 'DAYOFYEAR' ? 'day' : ($groupBy == 'WEEK' ? 'week' : 'month');
|
||||
$endDate->modify('+1 '.$padding);
|
||||
$interval = new DateInterval('P1'.substr($groupBy, 0, 1));
|
||||
$period = new DatePeriod($startDate, $interval, $endDate);
|
||||
$endDate->modify('-1 '.$padding);
|
||||
|
||||
$totals = [];
|
||||
|
||||
foreach ($period as $d) {
|
||||
$dateFormat = $groupBy == 'DAYOFYEAR' ? 'z' : ($groupBy == 'WEEK' ? 'W' : 'n');
|
||||
$date = $d->format($dateFormat);
|
||||
$date = $d->format('Y'.$dateFormat);
|
||||
$totals[] = isset($data[$date]) ? $data[$date] : 0;
|
||||
|
||||
if ($entityType == ENTITY_INVOICE) {
|
||||
@ -228,7 +229,7 @@ class ReportController extends BaseController
|
||||
'chartTypes' => $chartTypes,
|
||||
'chartType' => $chartType,
|
||||
'startDate' => $startDate->format(Session::get(SESSION_DATE_FORMAT)),
|
||||
'endDate' => $endDate->modify('-1'.$padding)->format(Session::get(SESSION_DATE_FORMAT)),
|
||||
'endDate' => $endDate->format(Session::get(SESSION_DATE_FORMAT)),
|
||||
'groupBy' => $groupBy,
|
||||
'feature' => ACCOUNT_CHART_BUILDER,
|
||||
'displayData' => $displayData,
|
||||
|
@ -66,8 +66,7 @@ class StartupCheck
|
||||
$count = Session::get(SESSION_COUNTER, 0);
|
||||
Session::put(SESSION_COUNTER, ++$count);
|
||||
|
||||
//if (!Utils::startsWith($_SERVER['REQUEST_URI'], '/news_feed') && !Session::has('news_feed_id')) {
|
||||
if (true) {
|
||||
if (!Utils::startsWith($_SERVER['REQUEST_URI'], '/news_feed') && !Session::has('news_feed_id')) {
|
||||
$data = false;
|
||||
if (Utils::isNinja()) {
|
||||
$data = Utils::getNewsFeedResponse();
|
||||
|
@ -236,9 +236,13 @@ class Utils
|
||||
$currencyId = Session::get(SESSION_CURRENCY);
|
||||
}
|
||||
|
||||
$currency = Currency::find($currencyId);
|
||||
foreach (Cache::get('currencies') as $currency) {
|
||||
if ($currency->id == $currencyId) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!$currency){
|
||||
if (!$currency) {
|
||||
$currency = Currency::find(1);
|
||||
}
|
||||
|
||||
@ -486,7 +490,7 @@ class Utils
|
||||
public static function encodeActivity($person = null, $action, $entity = null, $otherPerson = null)
|
||||
{
|
||||
$person = $person ? $person->getDisplayName() : '<i>System</i>';
|
||||
$entity = $entity ? '['.$entity->getActivityKey().']' : '';
|
||||
$entity = $entity ? $entity->getActivityKey() : '';
|
||||
$otherPerson = $otherPerson ? 'to '.$otherPerson->getDisplayName() : '';
|
||||
$token = Session::get('token_id') ? ' ('.trans('texts.token').')' : '';
|
||||
|
||||
|
@ -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->getName() . ']';
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -44,7 +44,7 @@ class AccountRepository
|
||||
}
|
||||
|
||||
$user->confirmed = !Utils::isNinja();
|
||||
$user->registered = !Utils::isNinja();
|
||||
$user->registered = !Utils::isNinja() && $user->email;
|
||||
|
||||
if (!$user->confirmed) {
|
||||
$user->confirmation_code = str_random(RANDOM_KEY_LENGTH);
|
||||
|
@ -270,9 +270,12 @@ class InvoiceRepository
|
||||
$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->is_recurring = $data['is_recurring'] && !Utils::isDemo() ? true : false;
|
||||
$invoice->invoice_date = isset($data['invoice_date_sql']) ? $data['invoice_date_sql'] : Utils::toSqlDate($data['invoice_date']);
|
||||
|
||||
if (!$publicId) {
|
||||
$invoice->is_recurring = $data['is_recurring'] && !Utils::isDemo() ? true : false;
|
||||
}
|
||||
|
||||
if ($invoice->is_recurring) {
|
||||
$invoice->frequency_id = $data['frequency_id'] ? $data['frequency_id'] : 0;
|
||||
$invoice->start_date = Utils::toSqlDate($data['start_date']);
|
||||
|
@ -20,7 +20,7 @@ class AddPartialAmountToInvoices extends Migration {
|
||||
Schema::table('accounts', function($table)
|
||||
{
|
||||
$table->boolean('utf8_invoices')->default(false);
|
||||
$table->boolean('auto_wrap')->default(true);
|
||||
$table->boolean('auto_wrap')->default(false);
|
||||
$table->string('subdomain')->nullable();
|
||||
});
|
||||
}
|
||||
|
2
public/css/built.css
vendored
2
public/css/built.css
vendored
@ -2421,7 +2421,7 @@ display: block;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
padding: 9px 12px;
|
||||
font-size: 14px;
|
||||
font-size: 16px;
|
||||
line-height: 1.42857143;
|
||||
color: #000 !important;
|
||||
background: #f9f9f9 !important;
|
||||
|
2
public/css/style.css
vendored
2
public/css/style.css
vendored
@ -37,7 +37,7 @@ display: block;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
padding: 9px 12px;
|
||||
font-size: 14px;
|
||||
font-size: 16px;
|
||||
line-height: 1.42857143;
|
||||
color: #000 !important;
|
||||
background: #f9f9f9 !important;
|
||||
|
@ -33091,168 +33091,219 @@ function truncate(str, length) {
|
||||
return (str && str.length > length) ? (str.substr(0, length-1) + '...') : str;
|
||||
}
|
||||
function GetPdfMake(invoice, javascript, callback) {
|
||||
var account = invoice.account;
|
||||
eval(javascript);
|
||||
var account = invoice.account;
|
||||
eval(javascript);
|
||||
|
||||
/*
|
||||
var fonts = {
|
||||
Roboto: {
|
||||
normal: 'Roboto-Regular.ttf',
|
||||
bold: 'Roboto-Medium.ttf',
|
||||
italics: 'Roboto-Italic.ttf',
|
||||
bolditalics: 'Roboto-Italic.ttf'
|
||||
},
|
||||
};
|
||||
*/
|
||||
/*
|
||||
var fonts = {
|
||||
Roboto: {
|
||||
normal: 'Roboto-Regular.ttf',
|
||||
bold: 'Roboto-Medium.ttf',
|
||||
italics: 'Roboto-Italic.ttf',
|
||||
bolditalics: 'Roboto-Italic.ttf'
|
||||
},
|
||||
};
|
||||
*/
|
||||
|
||||
doc = pdfMake.createPdf(dd);
|
||||
doc.save = function(fileName) {
|
||||
doc = pdfMake.createPdf(dd);
|
||||
doc.save = function(fileName) {
|
||||
this.download(fileName);
|
||||
};
|
||||
return doc;
|
||||
};
|
||||
return doc;
|
||||
}
|
||||
function notesAndTerms(invoice)
|
||||
{
|
||||
var text = [];
|
||||
if (invoice.public_notes) {
|
||||
text.push({text:invoice.public_notes, style:'notes'});
|
||||
}
|
||||
var text = [];
|
||||
if (invoice.public_notes) {
|
||||
text.push({text:invoice.public_notes, style:'notes'});
|
||||
}
|
||||
|
||||
if (invoice.terms) {
|
||||
text.push({text:invoiceLabels.terms, style:'termsLabel'});
|
||||
text.push({text:invoice.terms, style:'terms'});
|
||||
}
|
||||
if (invoice.terms) {
|
||||
text.push({text:invoiceLabels.terms, style:'termsLabel'});
|
||||
text.push({text:invoice.terms, style:'terms'});
|
||||
}
|
||||
|
||||
return text;
|
||||
return text;
|
||||
}
|
||||
|
||||
function invoiceLines(invoice) {
|
||||
var grid =
|
||||
[[{text: invoiceLabels.item, style: 'tableHeader'},
|
||||
{text: invoiceLabels.description, style: 'tableHeader'},
|
||||
{text: invoiceLabels.unit_cost, style: 'tableHeader'},
|
||||
{text: invoiceLabels.quantity, style: 'tableHeader'},
|
||||
{text: invoice.has_taxes?invoiceLabels.tax:'', style: 'tableHeader'},
|
||||
{text: invoiceLabels.line_total, style: 'tableHeader'}]];
|
||||
var total = 0;
|
||||
var shownItem = false;
|
||||
var currencyId = invoice && invoice.client ? invoice.client.currency_id : 1;
|
||||
var hideQuantity = invoice.account.hide_quantity == '1';
|
||||
var grid =
|
||||
[[{text: invoiceLabels.item, style: 'tableHeader'},
|
||||
{text: invoiceLabels.description, style: 'tableHeader'},
|
||||
{text: invoiceLabels.unit_cost, style: 'tableHeader'},
|
||||
{text: invoiceLabels.quantity, style: 'tableHeader'},
|
||||
{text: invoice.has_taxes?invoiceLabels.tax:'', style: 'tableHeader'},
|
||||
{text: invoiceLabels.line_total, style: 'tableHeader'}]];
|
||||
var total = 0;
|
||||
var shownItem = false;
|
||||
var currencyId = invoice && invoice.client ? invoice.client.currency_id : 1;
|
||||
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);
|
||||
var qty = NINJA.parseFloat(item.qty) ? roundToTwo(NINJA.parseFloat(item.qty)) + '' : '';
|
||||
var notes = item.notes;
|
||||
var productKey = item.product_key;
|
||||
var tax = "";
|
||||
if (item.tax && parseFloat(item.tax.rate)) {
|
||||
tax = parseFloat(item.tax.rate);
|
||||
} else if (item.tax_rate && parseFloat(item.tax_rate)) {
|
||||
tax = parseFloat(item.tax_rate);
|
||||
}
|
||||
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);
|
||||
var qty = NINJA.parseFloat(item.qty) ? roundToTwo(NINJA.parseFloat(item.qty)) + '' : '';
|
||||
var notes = item.notes;
|
||||
var productKey = item.product_key;
|
||||
var tax = "";
|
||||
if (item.tax && parseFloat(item.tax.rate)) {
|
||||
tax = parseFloat(item.tax.rate);
|
||||
} else if (item.tax_rate && parseFloat(item.tax_rate)) {
|
||||
tax = parseFloat(item.tax_rate);
|
||||
}
|
||||
|
||||
// show at most one blank line
|
||||
if (shownItem && (!cost || cost == '0.00') && !notes && !productKey) {
|
||||
continue;
|
||||
}
|
||||
shownItem = true;
|
||||
// show at most one blank line
|
||||
if (shownItem && (!cost || cost == '0.00') && !notes && !productKey) {
|
||||
continue;
|
||||
}
|
||||
shownItem = true;
|
||||
|
||||
// process date variables
|
||||
if (invoice.is_recurring) {
|
||||
notes = processVariables(notes);
|
||||
productKey = processVariables(productKey);
|
||||
}
|
||||
// process date variables
|
||||
if (invoice.is_recurring) {
|
||||
notes = processVariables(notes);
|
||||
productKey = processVariables(productKey);
|
||||
}
|
||||
|
||||
var lineTotal = roundToTwo(NINJA.parseFloat(item.cost)) * roundToTwo(NINJA.parseFloat(item.qty));
|
||||
if (tax) {
|
||||
lineTotal += lineTotal * tax / 100;
|
||||
}
|
||||
if (lineTotal) {
|
||||
total += lineTotal;
|
||||
}
|
||||
lineTotal = formatMoney(lineTotal, currencyId);
|
||||
var lineTotal = roundToTwo(NINJA.parseFloat(item.cost)) * roundToTwo(NINJA.parseFloat(item.qty));
|
||||
if (tax) {
|
||||
lineTotal += lineTotal * tax / 100;
|
||||
}
|
||||
if (lineTotal) {
|
||||
total += lineTotal;
|
||||
}
|
||||
lineTotal = formatMoney(lineTotal, currencyId);
|
||||
|
||||
rowStyle = i%2===0?'odd':'even';
|
||||
rowStyle = i%2===0?'odd':'even';
|
||||
|
||||
row[0] = {style:["productKey", rowStyle], text:productKey};
|
||||
row[1] = {style:["notes", rowStyle], text:notes};
|
||||
row[2] = {style:["cost", rowStyle], text:cost};
|
||||
row[3] = {style:["quantity", rowStyle], text:qty};
|
||||
row[4] = {style:["tax", rowStyle], text:""+tax};
|
||||
row[5] = {style:["lineTotal", rowStyle], text:lineTotal};
|
||||
row[0] = {style:["productKey", rowStyle], text:productKey};
|
||||
row[1] = {style:["notes", rowStyle], text:notes};
|
||||
row[2] = {style:["cost", rowStyle], text:cost};
|
||||
row[3] = {style:["quantity", rowStyle], text:qty};
|
||||
row[4] = {style:["tax", rowStyle], text:""+tax};
|
||||
row[5] = {style:["lineTotal", rowStyle], text:lineTotal};
|
||||
|
||||
grid.push(row);
|
||||
}
|
||||
return grid;
|
||||
grid.push(row);
|
||||
}
|
||||
return grid;
|
||||
}
|
||||
|
||||
function subtotals(invoice)
|
||||
{
|
||||
if (!invoice) {
|
||||
return;
|
||||
}
|
||||
if (!invoice) {
|
||||
return;
|
||||
}
|
||||
|
||||
var data = [
|
||||
var data = [
|
||||
[invoiceLabels.subtotal, formatMoney(invoice.subtotal_amount, invoice.client.currency_id)],
|
||||
];
|
||||
if(invoice.discount_amount != 0) {
|
||||
data.push([invoiceLabels.discount, formatMoney(invoice.discount_amount, invoice.client.currency_id)]);
|
||||
}
|
||||
];
|
||||
if(invoice.discount_amount != 0) {
|
||||
data.push([invoiceLabels.discount, formatMoney(invoice.discount_amount, invoice.client.currency_id)]);
|
||||
}
|
||||
|
||||
if (NINJA.parseFloat(invoice.custom_value1) && invoice.custom_taxes1 == '1') {
|
||||
data.push([invoiceLabels.custom_invoice_label1, formatMoney(invoice.custom_value1, invoice.client.currency_id)]);
|
||||
}
|
||||
if (NINJA.parseFloat(invoice.custom_value2) && invoice.custom_taxes2 == '1') {
|
||||
data.push([invoiceLabels.custom_invoice_label2, formatMoney(invoice.custom_value2, invoice.client.currency_id)]);
|
||||
}
|
||||
if (NINJA.parseFloat(invoice.custom_value1) && invoice.custom_taxes1 == '1') {
|
||||
data.push([invoiceLabels.custom_invoice_label1, formatMoney(invoice.custom_value1, invoice.client.currency_id)]);
|
||||
}
|
||||
if (NINJA.parseFloat(invoice.custom_value2) && invoice.custom_taxes2 == '1') {
|
||||
data.push([invoiceLabels.custom_invoice_label2, formatMoney(invoice.custom_value2, invoice.client.currency_id)]);
|
||||
}
|
||||
|
||||
if(invoice.tax && invoice.tax.name || invoice.tax_name) {
|
||||
data.push([invoiceLabels.tax, formatMoney(invoice.tax_amount, invoice.client.currency_id)]);
|
||||
}
|
||||
if(invoice.tax && invoice.tax.name || invoice.tax_name) {
|
||||
data.push([invoiceLabels.tax, formatMoney(invoice.tax_amount, invoice.client.currency_id)]);
|
||||
}
|
||||
|
||||
if (NINJA.parseFloat(invoice.custom_value1) && invoice.custom_taxes1 != '1') {
|
||||
data.push([invoiceLabels.custom_invoice_label1, formatMoney(invoice.custom_value1, invoice.client.currency_id)]);
|
||||
}
|
||||
if (NINJA.parseFloat(invoice.custom_value2) && invoice.custom_taxes2 != '1') {
|
||||
data.push([invoiceLabels.custom_invoice_label2, formatMoney(invoice.custom_value2, invoice.client.currency_id)]);
|
||||
}
|
||||
if (NINJA.parseFloat(invoice.custom_value1) && invoice.custom_taxes1 != '1') {
|
||||
data.push([invoiceLabels.custom_invoice_label1, formatMoney(invoice.custom_value1, invoice.client.currency_id)]);
|
||||
}
|
||||
if (NINJA.parseFloat(invoice.custom_value2) && invoice.custom_taxes2 != '1') {
|
||||
data.push([invoiceLabels.custom_invoice_label2, formatMoney(invoice.custom_value2, invoice.client.currency_id)]);
|
||||
}
|
||||
|
||||
var paid = invoice.amount - invoice.balance;
|
||||
if (invoice.account.hide_paid_to_date != '1' || paid) {
|
||||
data.push([invoiceLabels.paid_to_date, formatMoney(paid, invoice.client.currency_id)]);
|
||||
}
|
||||
var paid = invoice.amount - invoice.balance;
|
||||
if (invoice.account.hide_paid_to_date != '1' || paid) {
|
||||
data.push([invoiceLabels.paid_to_date, formatMoney(paid, invoice.client.currency_id)]);
|
||||
}
|
||||
|
||||
data.push([{text:invoice.is_quote ? invoiceLabels.total : invoiceLabels.balance_due, style:'balanceDueLabel'},
|
||||
{text:formatMoney(invoice.balance_amount, invoice.client.currency_id), style:'balanceDueValue'}]);
|
||||
return data;
|
||||
}
|
||||
data.push([{text:invoice.is_quote ? invoiceLabels.total : invoiceLabels.balance_due, style:'balanceDueLabel'},
|
||||
{text:formatMoney(invoice.balance_amount, invoice.client.currency_id), style:'balanceDueValue'}]);
|
||||
return data;
|
||||
}
|
||||
|
||||
function accountDetails(account) {
|
||||
var data = [];
|
||||
if(account.name) data.push({text:account.name, style:'accountDetails'});
|
||||
if(account.id_number) data.push({text:account.id_number, style:'accountDetails'});
|
||||
if(account.vat_number) data.push({text:account.vat_number, style:'accountDetails'});
|
||||
if(account.work_email) data.push({text:account.work_email, style:'accountDetails'});
|
||||
if(account.work_phone) data.push({text:account.work_phone, style:'accountDetails'});
|
||||
return data;
|
||||
function accountDetails(account) {
|
||||
var data = [];
|
||||
if(account.name) data.push({text:account.name, style:'accountName'});
|
||||
if(account.id_number) data.push({text:account.id_number, style:'accountDetails'});
|
||||
if(account.vat_number) data.push({text:account.vat_number, style:'accountDetails'});
|
||||
if(account.work_email) data.push({text:account.work_email, style:'accountDetails'});
|
||||
if(account.work_phone) data.push({text:account.work_phone, style:'accountDetails'});
|
||||
return data;
|
||||
}
|
||||
|
||||
function accountAddress(account) {
|
||||
var data = [];
|
||||
if(account.address1) data.push({text:account.address1, style:'accountDetails'});
|
||||
if(account.address2) data.push({text:account.address2, style:'accountDetails'});
|
||||
if(account.city) data.push({text:account.city, style:'accountDetails'});
|
||||
if(account.state) data.push({text:account.state, style:'accountDetails'});
|
||||
if(account.postal_code) data.push({text:account.postal_code, style:'accountDetails'});
|
||||
return data;
|
||||
var address = '';
|
||||
if (account.city || account.state || account.postal_code) {
|
||||
address = ((account.city ? account.city + ', ' : '') + account.state + ' ' + account.postal_code).trim();
|
||||
}
|
||||
var data = [];
|
||||
if(account.address1) data.push({text:account.address1, style:'accountDetails'});
|
||||
if(account.address2) data.push({text:account.address2, style:'accountDetails'});
|
||||
if(address) data.push({text:address, style:'accountDetails'});
|
||||
if(account.country) data.push({text:account.country.name, style: 'accountDetails'});
|
||||
return data;
|
||||
}
|
||||
|
||||
function invoiceDetails(invoice) {
|
||||
var data = [
|
||||
[
|
||||
invoice.is_quote ? invoiceLabels.quote_number : invoiceLabels.invoice_number,
|
||||
{style: 'bold', text: invoice.invoice_number},
|
||||
],
|
||||
[
|
||||
invoice.is_quote ? invoiceLabels.quote_date : invoiceLabels.invoice_date,
|
||||
invoice.invoice_date,
|
||||
],
|
||||
[
|
||||
invoice.is_quote ? invoiceLabels.total : invoiceLabels.balance_due,
|
||||
formatMoney(invoice.balance_amount, invoice.client.currency_id),
|
||||
],
|
||||
];
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
function clientDetails(invoice) {
|
||||
var client = invoice.client;
|
||||
if (!client) {
|
||||
return;
|
||||
}
|
||||
var fields = [
|
||||
getClientDisplayName(client),
|
||||
client.id_number,
|
||||
client.vat_number,
|
||||
concatStrings(client.address1, client.address2),
|
||||
concatStrings(client.city, client.state, client.postal_code),
|
||||
client.country ? client.country.name : false,
|
||||
invoice.contact && getClientDisplayName(client) != invoice.contact.email ? invoice.contact.email : false,
|
||||
invoice.client.custom_value1 ? invoice.account['custom_client_label1'] + ' ' + invoice.client.custom_value1 : false,
|
||||
invoice.client.custom_value2 ? invoice.account['custom_client_label2'] + ' ' + invoice.client.custom_value2 : false,
|
||||
];
|
||||
|
||||
var data = [];
|
||||
for (var i=0; i<fields.length; i++) {
|
||||
var field = fields[i];
|
||||
if (!field) {
|
||||
continue;
|
||||
}
|
||||
data.push(field);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
function primaryColor( defaultColor) {
|
||||
return NINJA.primaryColor?NINJA.primaryColor:defaultColor;
|
||||
return NINJA.primaryColor?NINJA.primaryColor:defaultColor;
|
||||
}
|
||||
|
||||
function secondaryColor( defaultColor) {
|
||||
return NINJA.primaryColor?NINJA.secondaryColor:defaultColor;
|
||||
return NINJA.primaryColor?NINJA.secondaryColor:defaultColor;
|
||||
}
|
@ -1,166 +1,217 @@
|
||||
function GetPdfMake(invoice, javascript, callback) {
|
||||
var account = invoice.account;
|
||||
eval(javascript);
|
||||
var account = invoice.account;
|
||||
eval(javascript);
|
||||
|
||||
/*
|
||||
var fonts = {
|
||||
Roboto: {
|
||||
normal: 'Roboto-Regular.ttf',
|
||||
bold: 'Roboto-Medium.ttf',
|
||||
italics: 'Roboto-Italic.ttf',
|
||||
bolditalics: 'Roboto-Italic.ttf'
|
||||
},
|
||||
};
|
||||
*/
|
||||
/*
|
||||
var fonts = {
|
||||
Roboto: {
|
||||
normal: 'Roboto-Regular.ttf',
|
||||
bold: 'Roboto-Medium.ttf',
|
||||
italics: 'Roboto-Italic.ttf',
|
||||
bolditalics: 'Roboto-Italic.ttf'
|
||||
},
|
||||
};
|
||||
*/
|
||||
|
||||
doc = pdfMake.createPdf(dd);
|
||||
doc.save = function(fileName) {
|
||||
doc = pdfMake.createPdf(dd);
|
||||
doc.save = function(fileName) {
|
||||
this.download(fileName);
|
||||
};
|
||||
return doc;
|
||||
};
|
||||
return doc;
|
||||
}
|
||||
function notesAndTerms(invoice)
|
||||
{
|
||||
var text = [];
|
||||
if (invoice.public_notes) {
|
||||
text.push({text:invoice.public_notes, style:'notes'});
|
||||
}
|
||||
var text = [];
|
||||
if (invoice.public_notes) {
|
||||
text.push({text:invoice.public_notes, style:'notes'});
|
||||
}
|
||||
|
||||
if (invoice.terms) {
|
||||
text.push({text:invoiceLabels.terms, style:'termsLabel'});
|
||||
text.push({text:invoice.terms, style:'terms'});
|
||||
}
|
||||
if (invoice.terms) {
|
||||
text.push({text:invoiceLabels.terms, style:'termsLabel'});
|
||||
text.push({text:invoice.terms, style:'terms'});
|
||||
}
|
||||
|
||||
return text;
|
||||
return text;
|
||||
}
|
||||
|
||||
function invoiceLines(invoice) {
|
||||
var grid =
|
||||
[[{text: invoiceLabels.item, style: 'tableHeader'},
|
||||
{text: invoiceLabels.description, style: 'tableHeader'},
|
||||
{text: invoiceLabels.unit_cost, style: 'tableHeader'},
|
||||
{text: invoiceLabels.quantity, style: 'tableHeader'},
|
||||
{text: invoice.has_taxes?invoiceLabels.tax:'', style: 'tableHeader'},
|
||||
{text: invoiceLabels.line_total, style: 'tableHeader'}]];
|
||||
var total = 0;
|
||||
var shownItem = false;
|
||||
var currencyId = invoice && invoice.client ? invoice.client.currency_id : 1;
|
||||
var hideQuantity = invoice.account.hide_quantity == '1';
|
||||
var grid =
|
||||
[[{text: invoiceLabels.item, style: 'tableHeader'},
|
||||
{text: invoiceLabels.description, style: 'tableHeader'},
|
||||
{text: invoiceLabels.unit_cost, style: 'tableHeader'},
|
||||
{text: invoiceLabels.quantity, style: 'tableHeader'},
|
||||
{text: invoice.has_taxes?invoiceLabels.tax:'', style: 'tableHeader'},
|
||||
{text: invoiceLabels.line_total, style: 'tableHeader'}]];
|
||||
var total = 0;
|
||||
var shownItem = false;
|
||||
var currencyId = invoice && invoice.client ? invoice.client.currency_id : 1;
|
||||
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);
|
||||
var qty = NINJA.parseFloat(item.qty) ? roundToTwo(NINJA.parseFloat(item.qty)) + '' : '';
|
||||
var notes = item.notes;
|
||||
var productKey = item.product_key;
|
||||
var tax = "";
|
||||
if (item.tax && parseFloat(item.tax.rate)) {
|
||||
tax = parseFloat(item.tax.rate);
|
||||
} else if (item.tax_rate && parseFloat(item.tax_rate)) {
|
||||
tax = parseFloat(item.tax_rate);
|
||||
}
|
||||
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);
|
||||
var qty = NINJA.parseFloat(item.qty) ? roundToTwo(NINJA.parseFloat(item.qty)) + '' : '';
|
||||
var notes = item.notes;
|
||||
var productKey = item.product_key;
|
||||
var tax = "";
|
||||
if (item.tax && parseFloat(item.tax.rate)) {
|
||||
tax = parseFloat(item.tax.rate);
|
||||
} else if (item.tax_rate && parseFloat(item.tax_rate)) {
|
||||
tax = parseFloat(item.tax_rate);
|
||||
}
|
||||
|
||||
// show at most one blank line
|
||||
if (shownItem && (!cost || cost == '0.00') && !notes && !productKey) {
|
||||
continue;
|
||||
}
|
||||
shownItem = true;
|
||||
// show at most one blank line
|
||||
if (shownItem && (!cost || cost == '0.00') && !notes && !productKey) {
|
||||
continue;
|
||||
}
|
||||
shownItem = true;
|
||||
|
||||
// process date variables
|
||||
if (invoice.is_recurring) {
|
||||
notes = processVariables(notes);
|
||||
productKey = processVariables(productKey);
|
||||
}
|
||||
// process date variables
|
||||
if (invoice.is_recurring) {
|
||||
notes = processVariables(notes);
|
||||
productKey = processVariables(productKey);
|
||||
}
|
||||
|
||||
var lineTotal = roundToTwo(NINJA.parseFloat(item.cost)) * roundToTwo(NINJA.parseFloat(item.qty));
|
||||
if (tax) {
|
||||
lineTotal += lineTotal * tax / 100;
|
||||
}
|
||||
if (lineTotal) {
|
||||
total += lineTotal;
|
||||
}
|
||||
lineTotal = formatMoney(lineTotal, currencyId);
|
||||
var lineTotal = roundToTwo(NINJA.parseFloat(item.cost)) * roundToTwo(NINJA.parseFloat(item.qty));
|
||||
if (tax) {
|
||||
lineTotal += lineTotal * tax / 100;
|
||||
}
|
||||
if (lineTotal) {
|
||||
total += lineTotal;
|
||||
}
|
||||
lineTotal = formatMoney(lineTotal, currencyId);
|
||||
|
||||
rowStyle = i%2===0?'odd':'even';
|
||||
rowStyle = i%2===0?'odd':'even';
|
||||
|
||||
row[0] = {style:["productKey", rowStyle], text:productKey};
|
||||
row[1] = {style:["notes", rowStyle], text:notes};
|
||||
row[2] = {style:["cost", rowStyle], text:cost};
|
||||
row[3] = {style:["quantity", rowStyle], text:qty};
|
||||
row[4] = {style:["tax", rowStyle], text:""+tax};
|
||||
row[5] = {style:["lineTotal", rowStyle], text:lineTotal};
|
||||
row[0] = {style:["productKey", rowStyle], text:productKey};
|
||||
row[1] = {style:["notes", rowStyle], text:notes};
|
||||
row[2] = {style:["cost", rowStyle], text:cost};
|
||||
row[3] = {style:["quantity", rowStyle], text:qty};
|
||||
row[4] = {style:["tax", rowStyle], text:""+tax};
|
||||
row[5] = {style:["lineTotal", rowStyle], text:lineTotal};
|
||||
|
||||
grid.push(row);
|
||||
}
|
||||
return grid;
|
||||
grid.push(row);
|
||||
}
|
||||
return grid;
|
||||
}
|
||||
|
||||
function subtotals(invoice)
|
||||
{
|
||||
if (!invoice) {
|
||||
return;
|
||||
}
|
||||
if (!invoice) {
|
||||
return;
|
||||
}
|
||||
|
||||
var data = [
|
||||
var data = [
|
||||
[invoiceLabels.subtotal, formatMoney(invoice.subtotal_amount, invoice.client.currency_id)],
|
||||
];
|
||||
if(invoice.discount_amount != 0) {
|
||||
data.push([invoiceLabels.discount, formatMoney(invoice.discount_amount, invoice.client.currency_id)]);
|
||||
}
|
||||
];
|
||||
if(invoice.discount_amount != 0) {
|
||||
data.push([invoiceLabels.discount, formatMoney(invoice.discount_amount, invoice.client.currency_id)]);
|
||||
}
|
||||
|
||||
if (NINJA.parseFloat(invoice.custom_value1) && invoice.custom_taxes1 == '1') {
|
||||
data.push([invoiceLabels.custom_invoice_label1, formatMoney(invoice.custom_value1, invoice.client.currency_id)]);
|
||||
}
|
||||
if (NINJA.parseFloat(invoice.custom_value2) && invoice.custom_taxes2 == '1') {
|
||||
data.push([invoiceLabels.custom_invoice_label2, formatMoney(invoice.custom_value2, invoice.client.currency_id)]);
|
||||
}
|
||||
if (NINJA.parseFloat(invoice.custom_value1) && invoice.custom_taxes1 == '1') {
|
||||
data.push([invoiceLabels.custom_invoice_label1, formatMoney(invoice.custom_value1, invoice.client.currency_id)]);
|
||||
}
|
||||
if (NINJA.parseFloat(invoice.custom_value2) && invoice.custom_taxes2 == '1') {
|
||||
data.push([invoiceLabels.custom_invoice_label2, formatMoney(invoice.custom_value2, invoice.client.currency_id)]);
|
||||
}
|
||||
|
||||
if(invoice.tax && invoice.tax.name || invoice.tax_name) {
|
||||
data.push([invoiceLabels.tax, formatMoney(invoice.tax_amount, invoice.client.currency_id)]);
|
||||
}
|
||||
if(invoice.tax && invoice.tax.name || invoice.tax_name) {
|
||||
data.push([invoiceLabels.tax, formatMoney(invoice.tax_amount, invoice.client.currency_id)]);
|
||||
}
|
||||
|
||||
if (NINJA.parseFloat(invoice.custom_value1) && invoice.custom_taxes1 != '1') {
|
||||
data.push([invoiceLabels.custom_invoice_label1, formatMoney(invoice.custom_value1, invoice.client.currency_id)]);
|
||||
}
|
||||
if (NINJA.parseFloat(invoice.custom_value2) && invoice.custom_taxes2 != '1') {
|
||||
data.push([invoiceLabels.custom_invoice_label2, formatMoney(invoice.custom_value2, invoice.client.currency_id)]);
|
||||
}
|
||||
if (NINJA.parseFloat(invoice.custom_value1) && invoice.custom_taxes1 != '1') {
|
||||
data.push([invoiceLabels.custom_invoice_label1, formatMoney(invoice.custom_value1, invoice.client.currency_id)]);
|
||||
}
|
||||
if (NINJA.parseFloat(invoice.custom_value2) && invoice.custom_taxes2 != '1') {
|
||||
data.push([invoiceLabels.custom_invoice_label2, formatMoney(invoice.custom_value2, invoice.client.currency_id)]);
|
||||
}
|
||||
|
||||
var paid = invoice.amount - invoice.balance;
|
||||
if (invoice.account.hide_paid_to_date != '1' || paid) {
|
||||
data.push([invoiceLabels.paid_to_date, formatMoney(paid, invoice.client.currency_id)]);
|
||||
}
|
||||
var paid = invoice.amount - invoice.balance;
|
||||
if (invoice.account.hide_paid_to_date != '1' || paid) {
|
||||
data.push([invoiceLabels.paid_to_date, formatMoney(paid, invoice.client.currency_id)]);
|
||||
}
|
||||
|
||||
data.push([{text:invoice.is_quote ? invoiceLabels.total : invoiceLabels.balance_due, style:'balanceDueLabel'},
|
||||
{text:formatMoney(invoice.balance_amount, invoice.client.currency_id), style:'balanceDueValue'}]);
|
||||
return data;
|
||||
}
|
||||
data.push([{text:invoice.is_quote ? invoiceLabels.total : invoiceLabels.balance_due, style:'balanceDueLabel'},
|
||||
{text:formatMoney(invoice.balance_amount, invoice.client.currency_id), style:'balanceDueValue'}]);
|
||||
return data;
|
||||
}
|
||||
|
||||
function accountDetails(account) {
|
||||
var data = [];
|
||||
if(account.name) data.push({text:account.name, style:'accountDetails'});
|
||||
if(account.id_number) data.push({text:account.id_number, style:'accountDetails'});
|
||||
if(account.vat_number) data.push({text:account.vat_number, style:'accountDetails'});
|
||||
if(account.work_email) data.push({text:account.work_email, style:'accountDetails'});
|
||||
if(account.work_phone) data.push({text:account.work_phone, style:'accountDetails'});
|
||||
return data;
|
||||
function accountDetails(account) {
|
||||
var data = [];
|
||||
if(account.name) data.push({text:account.name, style:'accountName'});
|
||||
if(account.id_number) data.push({text:account.id_number, style:'accountDetails'});
|
||||
if(account.vat_number) data.push({text:account.vat_number, style:'accountDetails'});
|
||||
if(account.work_email) data.push({text:account.work_email, style:'accountDetails'});
|
||||
if(account.work_phone) data.push({text:account.work_phone, style:'accountDetails'});
|
||||
return data;
|
||||
}
|
||||
|
||||
function accountAddress(account) {
|
||||
var data = [];
|
||||
if(account.address1) data.push({text:account.address1, style:'accountDetails'});
|
||||
if(account.address2) data.push({text:account.address2, style:'accountDetails'});
|
||||
if(account.city) data.push({text:account.city, style:'accountDetails'});
|
||||
if(account.state) data.push({text:account.state, style:'accountDetails'});
|
||||
if(account.postal_code) data.push({text:account.postal_code, style:'accountDetails'});
|
||||
return data;
|
||||
var address = '';
|
||||
if (account.city || account.state || account.postal_code) {
|
||||
address = ((account.city ? account.city + ', ' : '') + account.state + ' ' + account.postal_code).trim();
|
||||
}
|
||||
var data = [];
|
||||
if(account.address1) data.push({text:account.address1, style:'accountDetails'});
|
||||
if(account.address2) data.push({text:account.address2, style:'accountDetails'});
|
||||
if(address) data.push({text:address, style:'accountDetails'});
|
||||
if(account.country) data.push({text:account.country.name, style: 'accountDetails'});
|
||||
return data;
|
||||
}
|
||||
|
||||
function invoiceDetails(invoice) {
|
||||
var data = [
|
||||
[
|
||||
invoice.is_quote ? invoiceLabels.quote_number : invoiceLabels.invoice_number,
|
||||
{style: 'bold', text: invoice.invoice_number},
|
||||
],
|
||||
[
|
||||
invoice.is_quote ? invoiceLabels.quote_date : invoiceLabels.invoice_date,
|
||||
invoice.invoice_date,
|
||||
],
|
||||
[
|
||||
invoice.is_quote ? invoiceLabels.total : invoiceLabels.balance_due,
|
||||
formatMoney(invoice.balance_amount, invoice.client.currency_id),
|
||||
],
|
||||
];
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
function clientDetails(invoice) {
|
||||
var client = invoice.client;
|
||||
if (!client) {
|
||||
return;
|
||||
}
|
||||
var fields = [
|
||||
getClientDisplayName(client),
|
||||
client.id_number,
|
||||
client.vat_number,
|
||||
concatStrings(client.address1, client.address2),
|
||||
concatStrings(client.city, client.state, client.postal_code),
|
||||
client.country ? client.country.name : false,
|
||||
invoice.contact && getClientDisplayName(client) != invoice.contact.email ? invoice.contact.email : false,
|
||||
invoice.client.custom_value1 ? invoice.account['custom_client_label1'] + ' ' + invoice.client.custom_value1 : false,
|
||||
invoice.client.custom_value2 ? invoice.account['custom_client_label2'] + ' ' + invoice.client.custom_value2 : false,
|
||||
];
|
||||
|
||||
var data = [];
|
||||
for (var i=0; i<fields.length; i++) {
|
||||
var field = fields[i];
|
||||
if (!field) {
|
||||
continue;
|
||||
}
|
||||
data.push(field);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
function primaryColor( defaultColor) {
|
||||
return NINJA.primaryColor?NINJA.primaryColor:defaultColor;
|
||||
return NINJA.primaryColor?NINJA.primaryColor:defaultColor;
|
||||
}
|
||||
|
||||
function secondaryColor( defaultColor) {
|
||||
return NINJA.primaryColor?NINJA.secondaryColor:defaultColor;
|
||||
return NINJA.primaryColor?NINJA.secondaryColor:defaultColor;
|
||||
}
|
@ -20,23 +20,29 @@ var dd = {
|
||||
},
|
||||
{
|
||||
text:(invoice.is_quote ? invoiceLabels.quote : invoiceLabels.invoice).toUpperCase(),
|
||||
margin: [8, 16, 8, 16],
|
||||
style: 'primaryColor'
|
||||
margin: [8, 70, 8, 16],
|
||||
style: 'primaryColor',
|
||||
fontSize: 11
|
||||
},
|
||||
{
|
||||
style: 'tableExample',
|
||||
table: {
|
||||
headerRows: 1,
|
||||
widths: ['auto', 'auto', '*'],
|
||||
body: [
|
||||
[invoice.is_quote ? invoiceLabels.quote_number:invoiceLabels.invoice_number, {style: 'bold', text: invoice.invoice_number}, ""],
|
||||
[invoice.is_quote ? invoiceLabels.quote_date:invoiceLabels.invoice_date, invoice.invoice_date, ""],
|
||||
[invoice.is_quote ? invoiceLabels.total : invoiceLabels.balance_due, formatMoney(invoice.balance_amount, invoice.client.currency_id), ""],
|
||||
]
|
||||
body: [[
|
||||
{
|
||||
table: {
|
||||
body: invoiceDetails(invoice),
|
||||
},
|
||||
layout: 'noBorders',
|
||||
},
|
||||
clientDetails(invoice),
|
||||
''
|
||||
]]
|
||||
},
|
||||
layout: {
|
||||
hLineWidth: function (i, node) {
|
||||
return (i === 0 || i === node.table.body.length) ? 1 : 0;
|
||||
return (i === 0 || i === node.table.body.length) ? .5 : 0;
|
||||
},
|
||||
vLineWidth: function (i, node) {
|
||||
return 0;//(i === 0 || i === node.table.widths.length) ? 2 : 1;
|
||||
@ -57,12 +63,12 @@ var dd = {
|
||||
{
|
||||
table: {
|
||||
headerRows: 1,
|
||||
widths: ['auto', '*', 'auto', 'auto', 'auto', 'auto'],
|
||||
widths: ['15%', '*', 'auto', 'auto', 'auto', 'auto'],
|
||||
body:invoiceLines(invoice),
|
||||
},
|
||||
layout: {
|
||||
hLineWidth: function (i, node) {
|
||||
return i === 0 ? 0 : 1;
|
||||
return i === 0 ? 0 : .5;
|
||||
},
|
||||
vLineWidth: function (i, node) {
|
||||
return 0;
|
||||
@ -104,12 +110,12 @@ var dd = {
|
||||
],
|
||||
|
||||
footer: function(){
|
||||
f = [{ text:invoice.invoice_footer?invoice.invoice_footer:"", margin: [72, 0]}]
|
||||
f = [{ text:invoice.invoice_footer?invoice.invoice_footer:"", margin: [40, 0]}]
|
||||
if (!invoice.is_pro && logoImages.imageLogo1) {
|
||||
f.push({
|
||||
image: logoImages.imageLogo1,
|
||||
width: 150,
|
||||
margin: [72,0]
|
||||
margin: [40,0]
|
||||
});
|
||||
}
|
||||
return f;
|
||||
@ -124,6 +130,10 @@ var dd = {
|
||||
primaryColor:{
|
||||
color: primaryColor('#299CC2')
|
||||
},
|
||||
accountName: {
|
||||
margin: [4, 2, 4, 2],
|
||||
color:primaryColor('#299CC2')
|
||||
},
|
||||
accountDetails: {
|
||||
margin: [4, 2, 4, 2],
|
||||
color: '#AAA9A9'
|
||||
@ -178,5 +188,5 @@ var dd = {
|
||||
margin: [0, 10, 0, 4]
|
||||
}
|
||||
},
|
||||
pageMargins: [72, 40, 40, 80]
|
||||
pageMargins: [40, 40, 40, 40]
|
||||
};
|
@ -650,5 +650,9 @@ return array(
|
||||
'export' => 'Export',
|
||||
'documentation' => 'Documentation',
|
||||
'zapier' => 'Zapier <sup>Beta</sup>',
|
||||
'recurring' => 'Recurring',
|
||||
'last_invoice_sent' => 'Last invoice sent :date',
|
||||
|
||||
|
||||
|
||||
);
|
||||
|
@ -98,8 +98,10 @@
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
{!! Former::checkbox('pdf_email_attachment')->text(trans('texts.enable')) !!}
|
||||
{!! Former::checkbox('auto_wrap')->text(trans('texts.enable')) !!}
|
||||
{!! Former::checkbox('utf8_invoices')->text(trans('texts.enable')) !!}
|
||||
<div style="display:none">
|
||||
{!! Former::checkbox('auto_wrap')->text(trans('texts.enable')) !!}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -14,7 +14,9 @@
|
||||
|
||||
<div class="pull-right">
|
||||
{!! Button::normal(trans('texts.documentation'))->asLinkTo(NINJA_WEB_URL.'/knowledgebase/api-documentation/')->withAttributes(['target' => '_blank']) !!}
|
||||
{!! Button::normal(trans('texts.zapier'))->asLinkTo(ZAPIER_URL)->withAttributes(['target' => '_blank']) !!}
|
||||
@if (Utils::isNinja())
|
||||
{!! Button::normal(trans('texts.zapier'))->asLinkTo(ZAPIER_URL)->withAttributes(['target' => '_blank']) !!}
|
||||
@endif
|
||||
@if (Utils::isPro())
|
||||
{!! Button::primary(trans('texts.add_token'))->asLinkTo('/tokens/create')->appendIcon(Icon::create('plus-sign')) !!}
|
||||
@endif
|
||||
|
@ -90,7 +90,7 @@
|
||||
@foreach ($pastDue as $invoice)
|
||||
@if (!$invoice->client->trashed())
|
||||
<tr>
|
||||
<td>{{ $invoice->getLink() }}</td>
|
||||
<td>{!! $invoice->getLink() !!}</td>
|
||||
<td>{{ $invoice->client->getDisplayName() }}</td>
|
||||
<td>{{ Utils::fromSqlDate($invoice->due_date) }}</td>
|
||||
<td>{{ Utils::formatMoney($invoice->balance, $invoice->client->currency_id) }}</td>
|
||||
|
@ -19,6 +19,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
body {
|
||||
padding-top: 56px;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@include('script')
|
||||
|
@ -11,14 +11,6 @@
|
||||
<script src="{{ asset('js/vfs_fonts.js') }}" type="text/javascript"></script>
|
||||
@endif
|
||||
|
||||
<style type="text/css">
|
||||
.partial div.checkbox {
|
||||
display: inline;
|
||||
}
|
||||
.partial span.input-group-addon {
|
||||
padding-right: 30px;
|
||||
}
|
||||
</style>
|
||||
@stop
|
||||
|
||||
@section('content')
|
||||
@ -86,9 +78,7 @@
|
||||
{!! 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>') !!}
|
||||
|
||||
{!! Former::text('partial')->data_bind("value: partial, valueUpdate: 'afterkeydown', enable: is_partial")
|
||||
->onchange('onPartialChange()')->addGroupClass('partial')->append(Former::checkbox('is_partial')->raw()
|
||||
->data_bind('checked: is_partial')->onclick('onPartialEnabled()') . ' ' . (trans('texts.enable'))) !!}
|
||||
{!! Former::text('partial')->data_bind("value: partial, valueUpdate: 'afterkeydown'")->onchange('onPartialChange()') !!}
|
||||
</div>
|
||||
@if ($entityType == ENTITY_INVOICE)
|
||||
<div data-bind="visible: is_recurring" style="display: none">
|
||||
@ -104,8 +94,24 @@
|
||||
</div>
|
||||
@else
|
||||
<div data-bind="visible: invoice_status_id() === 0">
|
||||
{!! Former::checkbox('recurring')->onclick('onRecurringEnabled()')->text(trans('texts.enable').' <a href="#" onclick="showLearnMore()"><i class="glyphicon glyphicon-question-sign"></i> '.trans('texts.learn_more').'</a>')->data_bind("checked: is_recurring")
|
||||
->inlineHelp($invoice && $invoice->last_sent_date ? 'Last invoice sent ' . Utils::dateToString($invoice->last_sent_date) : '') !!}
|
||||
<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') }}
|
||||
<a href="#" onclick="showLearnMore()"><i class="glyphicon glyphicon-question-sign"></i> {{ trans('texts.learn_more') }}</a>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</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
|
||||
@ -1130,7 +1136,6 @@
|
||||
self.balance = ko.observable(0);
|
||||
self.invoice_design_id = ko.observable({{ $account->utf8_invoices ? 1 : $account->invoice_design_id }});
|
||||
self.partial = ko.observable(0);
|
||||
self.is_partial = ko.observable(false);
|
||||
|
||||
self.custom_value1 = ko.observable(0);
|
||||
self.custom_value2 = ko.observable(0);
|
||||
@ -1336,7 +1341,7 @@
|
||||
});
|
||||
|
||||
self.totals.total = ko.computed(function() {
|
||||
return formatMoney(self.is_partial() ? self.partial() : self.totals.rawTotal(), self.client().currency_id());
|
||||
return formatMoney(self.partial() ? self.partial() : self.totals.rawTotal(), self.client().currency_id());
|
||||
});
|
||||
|
||||
self.onDragged = function(item) {
|
||||
@ -1647,19 +1652,7 @@
|
||||
{
|
||||
var val = NINJA.parseFloat($('#partial').val());
|
||||
val = Math.max(Math.min(val, model.invoice().totals.rawTotal()), 0);
|
||||
$('#partial').val(val);
|
||||
}
|
||||
|
||||
function onPartialEnabled()
|
||||
{
|
||||
model.invoice().partial('');
|
||||
refreshPDF();
|
||||
|
||||
if ($('#is_partial').prop('checked')) {
|
||||
setTimeout(function() {
|
||||
$('#partial').focus();
|
||||
}, 1);
|
||||
}
|
||||
$('#partial').val(val || '');
|
||||
}
|
||||
|
||||
function onRecurringEnabled()
|
||||
@ -1702,9 +1695,6 @@
|
||||
@if ($invoice)
|
||||
var invoice = {!! $invoice !!};
|
||||
ko.mapping.fromJS(invoice, model.invoice().mapping, model.invoice);
|
||||
if (NINJA.parseFloat(model.invoice().partial())) {
|
||||
model.invoice().is_partial(true);
|
||||
}
|
||||
var invitationContactIds = {!! json_encode($invitationContactIds) !!};
|
||||
var client = clientMap[invoice.client.public_id];
|
||||
if (client) { // in case it's deleted
|
||||
|
Loading…
x
Reference in New Issue
Block a user