Bug fixes

This commit is contained in:
Hillel Coren 2015-05-05 12:48:23 +03:00
parent 5a28ff2612
commit b368e5589c
20 changed files with 484 additions and 336 deletions

View File

@ -1,5 +1,8 @@
<?php namespace App\Console\Commands; <?php namespace App\Console\Commands;
use DB;
use DateTime;
use Carbon;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputArgument;
@ -10,7 +13,7 @@ use Symfony\Component\Console\Input\InputArgument;
WARNING: Please backup your database before running this script 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 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 balance, paid to date and/or activity records to become inaccurate. This script will
check for errors and correct the data. 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') $clients = $clients->groupBy('clients.id', 'clients.balance', 'clients.created_at')
->orderBy('clients.id', 'DESC') ->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'); $this->info(count($clients) . ' clients with incorrect balance/activities');
foreach ($clients as $client) { 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; $foundProblem = false;
$lastBalance = 0; $lastBalance = 0;
$lastAdjustment = 0;
$lastCreatedAt = null;
$clientFix = false; $clientFix = false;
$activities = DB::table('activities') $activities = DB::table('activities')
->where('client_id', '=', $client->id) ->where('client_id', '=', $client->id)
@ -195,6 +200,11 @@ class CheckData extends Command {
$foundProblem = true; $foundProblem = true;
$clientFix -= $activity->adjustment; $clientFix -= $activity->adjustment;
$activityFix = 0; $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) { } elseif ($activity->activity_type_id == ACTIVITY_TYPE_UPDATE_QUOTE) {
// **Fix for updating balance when updating a quote** // **Fix for updating balance when updating a quote**
@ -231,19 +241,33 @@ class CheckData extends Command {
} }
$lastBalance = $activity->balance; $lastBalance = $activity->balance;
$lastAdjustment = $activity->adjustment;
$lastCreatedAt = $activity->created_at;
} }
if ($clientFix !== false) { if ($activity->balance + $clientFix != $client->actual_balance) {
$balance = $activity->balance + $clientFix; $this->info("** Creating 'recovered update' activity **");
$data = ['balance' => $balance]; if ($this->option('fix') == 'true') {
$this->info("Corrected balance:{$balance}"); 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') { if ($this->option('fix') == 'true') {
DB::table('clients') DB::table('clients')
->where('id', $client->id) ->where('id', $client->id)
->update($data); ->update($data);
} }
} }
}
$this->info('Done'); $this->info('Done');
} }

View File

@ -51,6 +51,7 @@ class DashboardController extends BaseController
$activities = Activity::where('activities.account_id', '=', Auth::user()->account_id) $activities = Activity::where('activities.account_id', '=', Auth::user()->account_id)
->where('activity_type_id', '>', 0)
->orderBy('created_at', 'desc')->take(6)->get(); ->orderBy('created_at', 'desc')->take(6)->get();
$pastDue = Invoice::scope() $pastDue = Invoice::scope()

View File

@ -59,8 +59,6 @@ class ReportController extends BaseController
$enableChart = true; $enableChart = true;
} }
$padding = $groupBy == 'DAYOFYEAR' ? 'day' : ($groupBy == 'WEEK' ? 'week' : 'month');
$endDate->modify('+1 '.$padding);
$datasets = []; $datasets = [];
$labels = []; $labels = [];
$maxTotals = 0; $maxTotals = 0;
@ -155,7 +153,7 @@ class ReportController extends BaseController
if ($enableChart) { if ($enableChart) {
foreach ([ENTITY_INVOICE, ENTITY_PAYMENT, ENTITY_CREDIT] as $entityType) { foreach ([ENTITY_INVOICE, ENTITY_PAYMENT, ENTITY_CREDIT] as $entityType) {
$records = DB::table($entityType.'s') $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('account_id', '=', Auth::user()->account_id)
->where($entityType.'s.is_deleted', '=', false) ->where($entityType.'s.is_deleted', '=', false)
->where($entityType.'s.'.$entityType.'_date', '>=', $startDate->format('Y-m-d')) ->where($entityType.'s.'.$entityType.'_date', '>=', $startDate->format('Y-m-d'))
@ -171,14 +169,17 @@ class ReportController extends BaseController
$dates = $records->lists($groupBy); $dates = $records->lists($groupBy);
$data = array_combine($dates, $totals); $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)); $interval = new DateInterval('P1'.substr($groupBy, 0, 1));
$period = new DatePeriod($startDate, $interval, $endDate); $period = new DatePeriod($startDate, $interval, $endDate);
$endDate->modify('-1 '.$padding);
$totals = []; $totals = [];
foreach ($period as $d) { foreach ($period as $d) {
$dateFormat = $groupBy == 'DAYOFYEAR' ? 'z' : ($groupBy == 'WEEK' ? 'W' : 'n'); $dateFormat = $groupBy == 'DAYOFYEAR' ? 'z' : ($groupBy == 'WEEK' ? 'W' : 'n');
$date = $d->format($dateFormat); $date = $d->format('Y'.$dateFormat);
$totals[] = isset($data[$date]) ? $data[$date] : 0; $totals[] = isset($data[$date]) ? $data[$date] : 0;
if ($entityType == ENTITY_INVOICE) { if ($entityType == ENTITY_INVOICE) {
@ -228,7 +229,7 @@ class ReportController extends BaseController
'chartTypes' => $chartTypes, 'chartTypes' => $chartTypes,
'chartType' => $chartType, 'chartType' => $chartType,
'startDate' => $startDate->format(Session::get(SESSION_DATE_FORMAT)), '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, 'groupBy' => $groupBy,
'feature' => ACCOUNT_CHART_BUILDER, 'feature' => ACCOUNT_CHART_BUILDER,
'displayData' => $displayData, 'displayData' => $displayData,

View File

@ -66,8 +66,7 @@ class StartupCheck
$count = Session::get(SESSION_COUNTER, 0); $count = Session::get(SESSION_COUNTER, 0);
Session::put(SESSION_COUNTER, ++$count); Session::put(SESSION_COUNTER, ++$count);
//if (!Utils::startsWith($_SERVER['REQUEST_URI'], '/news_feed') && !Session::has('news_feed_id')) { if (!Utils::startsWith($_SERVER['REQUEST_URI'], '/news_feed') && !Session::has('news_feed_id')) {
if (true) {
$data = false; $data = false;
if (Utils::isNinja()) { if (Utils::isNinja()) {
$data = Utils::getNewsFeedResponse(); $data = Utils::getNewsFeedResponse();

View File

@ -236,7 +236,11 @@ class Utils
$currencyId = Session::get(SESSION_CURRENCY); $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); $currency = Currency::find(1);
@ -486,7 +490,7 @@ class Utils
public static function encodeActivity($person = null, $action, $entity = null, $otherPerson = null) public static function encodeActivity($person = null, $action, $entity = null, $otherPerson = null)
{ {
$person = $person ? $person->getDisplayName() : '<i>System</i>'; $person = $person ? $person->getDisplayName() : '<i>System</i>';
$entity = $entity ? '['.$entity->getActivityKey().']' : ''; $entity = $entity ? $entity->getActivityKey() : '';
$otherPerson = $otherPerson ? 'to '.$otherPerson->getDisplayName() : ''; $otherPerson = $otherPerson ? 'to '.$otherPerson->getDisplayName() : '';
$token = Session::get('token_id') ? ' ('.trans('texts.token').')' : ''; $token = Session::get('token_id') ? ' ('.trans('texts.token').')' : '';

View File

@ -44,7 +44,7 @@ class EntityModel extends Eloquent
public function getActivityKey() public function getActivityKey()
{ {
return $this->getEntityType().':'.$this->public_id.':'.$this->getName(); return '[' . $this->getEntityType().':'.$this->public_id.':'.$this->getName() . ']';
} }
/* /*

View File

@ -44,7 +44,7 @@ class AccountRepository
} }
$user->confirmed = !Utils::isNinja(); $user->confirmed = !Utils::isNinja();
$user->registered = !Utils::isNinja(); $user->registered = !Utils::isNinja() && $user->email;
if (!$user->confirmed) { if (!$user->confirmed) {
$user->confirmation_code = str_random(RANDOM_KEY_LENGTH); $user->confirmation_code = str_random(RANDOM_KEY_LENGTH);

View File

@ -270,9 +270,12 @@ class InvoiceRepository
$invoice->is_amount_discount = $data['is_amount_discount'] ? true : false; $invoice->is_amount_discount = $data['is_amount_discount'] ? true : false;
$invoice->invoice_number = trim($data['invoice_number']); $invoice->invoice_number = trim($data['invoice_number']);
$invoice->partial = round(Utils::parseFloat($data['partial']), 2); $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']); $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) { if ($invoice->is_recurring) {
$invoice->frequency_id = $data['frequency_id'] ? $data['frequency_id'] : 0; $invoice->frequency_id = $data['frequency_id'] ? $data['frequency_id'] : 0;
$invoice->start_date = Utils::toSqlDate($data['start_date']); $invoice->start_date = Utils::toSqlDate($data['start_date']);

View File

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

View File

@ -2421,7 +2421,7 @@ display: block;
width: 100%; width: 100%;
height: 40px; height: 40px;
padding: 9px 12px; padding: 9px 12px;
font-size: 14px; font-size: 16px;
line-height: 1.42857143; line-height: 1.42857143;
color: #000 !important; color: #000 !important;
background: #f9f9f9 !important; background: #f9f9f9 !important;

View File

@ -37,7 +37,7 @@ display: block;
width: 100%; width: 100%;
height: 40px; height: 40px;
padding: 9px 12px; padding: 9px 12px;
font-size: 14px; font-size: 16px;
line-height: 1.42857143; line-height: 1.42857143;
color: #000 !important; color: #000 !important;
background: #f9f9f9 !important; background: #f9f9f9 !important;

View File

@ -33231,7 +33231,7 @@ function subtotals(invoice)
function accountDetails(account) { function accountDetails(account) {
var data = []; var data = [];
if(account.name) data.push({text:account.name, style:'accountDetails'}); if(account.name) data.push({text:account.name, style:'accountName'});
if(account.id_number) data.push({text:account.id_number, 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.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_email) data.push({text:account.work_email, style:'accountDetails'});
@ -33240,15 +33240,66 @@ function subtotals(invoice)
} }
function accountAddress(account) { function accountAddress(account) {
var address = '';
if (account.city || account.state || account.postal_code) {
address = ((account.city ? account.city + ', ' : '') + account.state + ' ' + account.postal_code).trim();
}
var data = []; var data = [];
if(account.address1) data.push({text:account.address1, style:'accountDetails'}); if(account.address1) data.push({text:account.address1, style:'accountDetails'});
if(account.address2) data.push({text:account.address2, style:'accountDetails'}); if(account.address2) data.push({text:account.address2, style:'accountDetails'});
if(account.city) data.push({text:account.city, style:'accountDetails'}); if(address) data.push({text:address, style:'accountDetails'});
if(account.state) data.push({text:account.state, style:'accountDetails'}); if(account.country) data.push({text:account.country.name, style: 'accountDetails'});
if(account.postal_code) data.push({text:account.postal_code, style:'accountDetails'});
return data; 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) { function primaryColor( defaultColor) {
return NINJA.primaryColor?NINJA.primaryColor:defaultColor; return NINJA.primaryColor?NINJA.primaryColor:defaultColor;
} }

View File

@ -139,7 +139,7 @@ function subtotals(invoice)
function accountDetails(account) { function accountDetails(account) {
var data = []; var data = [];
if(account.name) data.push({text:account.name, style:'accountDetails'}); if(account.name) data.push({text:account.name, style:'accountName'});
if(account.id_number) data.push({text:account.id_number, 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.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_email) data.push({text:account.work_email, style:'accountDetails'});
@ -148,15 +148,66 @@ function subtotals(invoice)
} }
function accountAddress(account) { function accountAddress(account) {
var address = '';
if (account.city || account.state || account.postal_code) {
address = ((account.city ? account.city + ', ' : '') + account.state + ' ' + account.postal_code).trim();
}
var data = []; var data = [];
if(account.address1) data.push({text:account.address1, style:'accountDetails'}); if(account.address1) data.push({text:account.address1, style:'accountDetails'});
if(account.address2) data.push({text:account.address2, style:'accountDetails'}); if(account.address2) data.push({text:account.address2, style:'accountDetails'});
if(account.city) data.push({text:account.city, style:'accountDetails'}); if(address) data.push({text:address, style:'accountDetails'});
if(account.state) data.push({text:account.state, style:'accountDetails'}); if(account.country) data.push({text:account.country.name, style: 'accountDetails'});
if(account.postal_code) data.push({text:account.postal_code, style:'accountDetails'});
return data; 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) { function primaryColor( defaultColor) {
return NINJA.primaryColor?NINJA.primaryColor:defaultColor; return NINJA.primaryColor?NINJA.primaryColor:defaultColor;
} }

View File

@ -20,23 +20,29 @@ var dd = {
}, },
{ {
text:(invoice.is_quote ? invoiceLabels.quote : invoiceLabels.invoice).toUpperCase(), text:(invoice.is_quote ? invoiceLabels.quote : invoiceLabels.invoice).toUpperCase(),
margin: [8, 16, 8, 16], margin: [8, 70, 8, 16],
style: 'primaryColor' style: 'primaryColor',
fontSize: 11
}, },
{ {
style: 'tableExample', style: 'tableExample',
table: { table: {
headerRows: 1, headerRows: 1,
widths: ['auto', 'auto', '*'], widths: ['auto', 'auto', '*'],
body: [ 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, ""], table: {
[invoice.is_quote ? invoiceLabels.total : invoiceLabels.balance_due, formatMoney(invoice.balance_amount, invoice.client.currency_id), ""], body: invoiceDetails(invoice),
] },
layout: 'noBorders',
},
clientDetails(invoice),
''
]]
}, },
layout: { layout: {
hLineWidth: function (i, node) { 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) { vLineWidth: function (i, node) {
return 0;//(i === 0 || i === node.table.widths.length) ? 2 : 1; return 0;//(i === 0 || i === node.table.widths.length) ? 2 : 1;
@ -57,12 +63,12 @@ var dd = {
{ {
table: { table: {
headerRows: 1, headerRows: 1,
widths: ['auto', '*', 'auto', 'auto', 'auto', 'auto'], widths: ['15%', '*', 'auto', 'auto', 'auto', 'auto'],
body:invoiceLines(invoice), body:invoiceLines(invoice),
}, },
layout: { layout: {
hLineWidth: function (i, node) { hLineWidth: function (i, node) {
return i === 0 ? 0 : 1; return i === 0 ? 0 : .5;
}, },
vLineWidth: function (i, node) { vLineWidth: function (i, node) {
return 0; return 0;
@ -104,12 +110,12 @@ var dd = {
], ],
footer: function(){ 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) { if (!invoice.is_pro && logoImages.imageLogo1) {
f.push({ f.push({
image: logoImages.imageLogo1, image: logoImages.imageLogo1,
width: 150, width: 150,
margin: [72,0] margin: [40,0]
}); });
} }
return f; return f;
@ -124,6 +130,10 @@ var dd = {
primaryColor:{ primaryColor:{
color: primaryColor('#299CC2') color: primaryColor('#299CC2')
}, },
accountName: {
margin: [4, 2, 4, 2],
color:primaryColor('#299CC2')
},
accountDetails: { accountDetails: {
margin: [4, 2, 4, 2], margin: [4, 2, 4, 2],
color: '#AAA9A9' color: '#AAA9A9'
@ -178,5 +188,5 @@ var dd = {
margin: [0, 10, 0, 4] margin: [0, 10, 0, 4]
} }
}, },
pageMargins: [72, 40, 40, 80] pageMargins: [40, 40, 40, 40]
}; };

View File

@ -650,5 +650,9 @@ return array(
'export' => 'Export', 'export' => 'Export',
'documentation' => 'Documentation', 'documentation' => 'Documentation',
'zapier' => 'Zapier <sup>Beta</sup>', 'zapier' => 'Zapier <sup>Beta</sup>',
'recurring' => 'Recurring',
'last_invoice_sent' => 'Last invoice sent :date',
); );

View File

@ -98,8 +98,10 @@
</div> </div>
<div class="panel-body"> <div class="panel-body">
{!! Former::checkbox('pdf_email_attachment')->text(trans('texts.enable')) !!} {!! Former::checkbox('pdf_email_attachment')->text(trans('texts.enable')) !!}
{!! Former::checkbox('auto_wrap')->text(trans('texts.enable')) !!}
{!! Former::checkbox('utf8_invoices')->text(trans('texts.enable')) !!} {!! Former::checkbox('utf8_invoices')->text(trans('texts.enable')) !!}
<div style="display:none">
{!! Former::checkbox('auto_wrap')->text(trans('texts.enable')) !!}
</div>
</div> </div>
</div> </div>
</div> </div>

View File

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

View File

@ -90,7 +90,7 @@
@foreach ($pastDue as $invoice) @foreach ($pastDue as $invoice)
@if (!$invoice->client->trashed()) @if (!$invoice->client->trashed())
<tr> <tr>
<td>{{ $invoice->getLink() }}</td> <td>{!! $invoice->getLink() !!}</td>
<td>{{ $invoice->client->getDisplayName() }}</td> <td>{{ $invoice->client->getDisplayName() }}</td>
<td>{{ Utils::fromSqlDate($invoice->due_date) }}</td> <td>{{ Utils::fromSqlDate($invoice->due_date) }}</td>
<td>{{ Utils::formatMoney($invoice->balance, $invoice->client->currency_id) }}</td> <td>{{ Utils::formatMoney($invoice->balance, $invoice->client->currency_id) }}</td>

View File

@ -19,6 +19,12 @@
} }
} }
@media screen and (max-width: 768px) {
body {
padding-top: 56px;
}
}
</style> </style>
@include('script') @include('script')

View File

@ -11,14 +11,6 @@
<script src="{{ asset('js/vfs_fonts.js') }}" type="text/javascript"></script> <script src="{{ asset('js/vfs_fonts.js') }}" type="text/javascript"></script>
@endif @endif
<style type="text/css">
.partial div.checkbox {
display: inline;
}
.partial span.input-group-addon {
padding-right: 30px;
}
</style>
@stop @stop
@section('content') @section('content')
@ -86,9 +78,7 @@
{!! Former::text('due_date')->data_bind("datePicker: due_date, valueUpdate: 'afterkeydown'") {!! 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))->append('<i class="glyphicon glyphicon-calendar" onclick="toggleDatePicker(\'due_date\')"></i>') !!}
{!! Former::text('partial')->data_bind("value: partial, valueUpdate: 'afterkeydown', enable: is_partial") {!! Former::text('partial')->data_bind("value: partial, valueUpdate: 'afterkeydown'")->onchange('onPartialChange()') !!}
->onchange('onPartialChange()')->addGroupClass('partial')->append(Former::checkbox('is_partial')->raw()
->data_bind('checked: is_partial')->onclick('onPartialEnabled()') . '&nbsp;' . (trans('texts.enable'))) !!}
</div> </div>
@if ($entityType == ENTITY_INVOICE) @if ($entityType == ENTITY_INVOICE)
<div data-bind="visible: is_recurring" style="display: none"> <div data-bind="visible: is_recurring" style="display: none">
@ -104,8 +94,24 @@
</div> </div>
@else @else
<div data-bind="visible: invoice_status_id() === 0"> <div data-bind="visible: invoice_status_id() === 0">
{!! Former::checkbox('recurring')->onclick('onRecurringEnabled()')->text(trans('texts.enable').' &nbsp;&nbsp; <a href="#" onclick="showLearnMore()"><i class="glyphicon glyphicon-question-sign"></i> '.trans('texts.learn_more').'</a>')->data_bind("checked: is_recurring") <div class="form-group">
->inlineHelp($invoice && $invoice->last_sent_date ? 'Last invoice sent ' . Utils::dateToString($invoice->last_sent_date) : '') !!} <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>
</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> </div>
@endif @endif
@endif @endif
@ -1130,7 +1136,6 @@
self.balance = ko.observable(0); self.balance = ko.observable(0);
self.invoice_design_id = ko.observable({{ $account->utf8_invoices ? 1 : $account->invoice_design_id }}); self.invoice_design_id = ko.observable({{ $account->utf8_invoices ? 1 : $account->invoice_design_id }});
self.partial = ko.observable(0); self.partial = ko.observable(0);
self.is_partial = ko.observable(false);
self.custom_value1 = ko.observable(0); self.custom_value1 = ko.observable(0);
self.custom_value2 = ko.observable(0); self.custom_value2 = ko.observable(0);
@ -1336,7 +1341,7 @@
}); });
self.totals.total = ko.computed(function() { 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) { self.onDragged = function(item) {
@ -1647,19 +1652,7 @@
{ {
var val = NINJA.parseFloat($('#partial').val()); var val = NINJA.parseFloat($('#partial').val());
val = Math.max(Math.min(val, model.invoice().totals.rawTotal()), 0); val = Math.max(Math.min(val, model.invoice().totals.rawTotal()), 0);
$('#partial').val(val); $('#partial').val(val || '');
}
function onPartialEnabled()
{
model.invoice().partial('');
refreshPDF();
if ($('#is_partial').prop('checked')) {
setTimeout(function() {
$('#partial').focus();
}, 1);
}
} }
function onRecurringEnabled() function onRecurringEnabled()
@ -1702,9 +1695,6 @@
@if ($invoice) @if ($invoice)
var invoice = {!! $invoice !!}; var invoice = {!! $invoice !!};
ko.mapping.fromJS(invoice, model.invoice().mapping, model.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 invitationContactIds = {!! json_encode($invitationContactIds) !!};
var client = clientMap[invoice.client.public_id]; var client = clientMap[invoice.client.public_id];
if (client) { // in case it's deleted if (client) { // in case it's deleted