bug fixes

This commit is contained in:
Hillel Coren 2014-01-02 01:12:33 +02:00
parent 689d1938e1
commit 0b8ad35d66
14 changed files with 161 additions and 101 deletions

View File

@ -59,3 +59,4 @@ Configure config/database.php and then initialize the database
* [mozilla/pdf.js](https://github.com/mozilla/pdf.js) - PDF Reader in JavaScript
* [nnnick/Chart.js](https://github.com/nnnick/Chart.js) - Simple HTML5 Charts using the <canvas> tag
* [josscrowcroft/accounting.js](https://github.com/josscrowcroft/accounting.js) - A lightweight JavaScript library for number, money and currency formatting
* [jashkenas/underscore](https://github.com/jashkenas/underscore) - JavaScript's utility _ belt

View File

@ -112,8 +112,9 @@ class CreditController extends \BaseController {
{
$rules = array(
'client' => 'required',
'amount' => 'required'
'amount' => 'required',
);
$validator = Validator::make(Input::all(), $rules);
if ($validator->fails()) {

View File

@ -216,8 +216,7 @@ class InvoiceController extends \BaseController {
if (!$ref)
{
var_dump($response);
exit('Sorry, there was an error processing your payment. Please try again later.');
Utils::fatalError('Sorry, there was an error processing your payment. Please try again later.<p>');
}
$payment = Payment::createNew();
@ -225,7 +224,7 @@ class InvoiceController extends \BaseController {
$payment->invoice_id = $invoice->id;
$payment->amount = $invoice->amount;
$payment->client_id = $invoice->client_id;
//$payment->contact_id = 0; // TODO_FIX
$payment->contact_id = $invitation->contact_id;
$payment->transaction_reference = $ref;
$payment->save();
@ -299,7 +298,7 @@ class InvoiceController extends \BaseController {
public function edit($publicId)
{
$invoice = Invoice::scope($publicId)->with('account.country', 'client', 'invoice_items')->firstOrFail();
$invoice = Invoice::scope($publicId)->with('account.country', 'client.contacts', 'invoice_items')->firstOrFail();
Utils::trackViewed($invoice->invoice_number . ' - ' . $invoice->client->getDisplayName(), ENTITY_INVOICE);
$invoice->invoice_date = Utils::fromSqlDate($invoice->invoice_date);
@ -420,12 +419,6 @@ class InvoiceController extends \BaseController {
$account->save();
}
if ($action == 'email' && $invoice->invoice_status_id == INVOICE_STATUS_DRAFT)
{
$client->balance = $client->balance + $invoice->amount;
$client->save();
}
$client->load('contacts');
$sendInvoiceIds = [];

View File

@ -75,7 +75,7 @@ class PaymentController extends \BaseController
$data = array(
'clientPublicId' => $clientPublicId,
'invoice' => null,
'invoices' => Invoice::scope()->with('client')->orderBy('invoice_number')->get(),
'invoices' => Invoice::scope()->with('client')->where('balance','>',0)->orderBy('invoice_number')->get(),
'payment' => null,
'method' => 'POST',
'url' => 'payments',

View File

@ -2,9 +2,15 @@
class Utils
{
public static function fatalError($error)
public static function fatalError($error = false)
{
if (!$error)
{
$error = "An error occurred, please try again later";
}
Log::error($error);
return View::make('error')->with('error', $error);
}

View File

@ -42,7 +42,7 @@ class Activity extends Eloquent
$activity->user_id = $entity->user_id;
$activity->account_id = $entity->account_id;
} else {
exit; // TODO_FIX log error
Utils::fatalError();
}
return $activity;
@ -104,11 +104,11 @@ class Activity extends Eloquent
public static function emailInvoice($invitation)
{
$adjustment = 0;
$client = $invitation->invoice->client;
if (!$invitation->invoice->isSent())
{
$adjustment = $invitation->invoice->amount;
$client = $invitation->invoice->client;
$client->balance = $client->balance + $adjustment;
$client->save();
}
@ -120,7 +120,7 @@ class Activity extends Eloquent
$activity->contact_id = $invitation->contact_id;
$activity->activity_type_id = ACTIVITY_TYPE_EMAIL_INVOICE;
$activity->message = $userName . ' emailed invoice ' . link_to('invoices/'.$invitation->invoice->public_id, $invitation->invoice->invoice_number) . ' to ' . $invitation->contact->getFullName() . ' - ' . $invitation->contact->email;
$activity->balance = $invitation->invoice->client->balance;
$activity->balance = $client->balance;
$activity->adjustment = $adjustment;
$activity->save();
}
@ -176,9 +176,16 @@ class Activity extends Eloquent
}
$activity->payment_id = $payment->id;
if ($payment->invoice_id) {
if ($payment->invoice_id)
{
$activity->invoice_id = $payment->invoice_id;
$invoice = $payment->invoice;
$invoice->balance = $invoice->balance - $payment->amount;
$invoice->save();
}
$activity->client_id = $payment->client_id;
$activity->currency_id = $payment->currency_id;
$activity->activity_type_id = ACTIVITY_TYPE_CREATE_PAYMENT;
@ -197,6 +204,16 @@ class Activity extends Eloquent
$activity->message = Auth::user()->getFullName() . ' created credit';
$activity->credit_id = $credit->id;
$activity->client_id = $credit->client_id;
if ($credit->invoice_id)
{
$activity->invoice_id = $payment->invoice_id;
$invoice = $credit->invoice;
$invoice->balance = $invoice->amount - $credit->amount;
$invoice->save();
}
$activity->currency_id = $credit->currency_id;
$activity->activity_type_id = ACTIVITY_TYPE_CREATE_CREDIT;
$activity->balance = $client->balance;

View File

@ -15,7 +15,7 @@ class EntityModel extends Eloquent
$entity->user_id = $parent->user_id;
$entity->account_id = $parent->account_id;
} else {
exit; // TODO_FIX
Utils::fatalError();
}
$lastEntity = $className::withTrashed()->scope(false, $entity->account_id)->orderBy('public_id', 'DESC')->first();

View File

@ -106,7 +106,7 @@ class Invoice extends EntityModel
case FREQUENCY_ANNUALLY:
return ($dayOfMonthStart == $dayOfMonthToday && (!$daysSinceLastSent || $monthsSinceLastSent == 12)) || $daysSinceLastSent > (12 *31);
default:
echo "Error: invalid frequency_id - ".$this->frequency_id; exit; //TODO_FIX
Utils::fatalError("Invalid frequency supplied: " . $this->frequency_id);
break;
}

View File

@ -71,6 +71,8 @@ class ClientRepository
}
}
$client->save();
return $client;
}
}

View File

@ -22,6 +22,7 @@
<script type="text/javascript" src="{{ asset('js/knockout.mapping-latest.js') }}"></script>
<script src="{{ asset('js/knockout-sortable.js') }}" type="text/javascript"></script>
<link rel="stylesheet" type="text/css" href="{{ asset('css/font-awesome.css') }}"/>
<script src="{{ asset('js/underscore-min.js') }}" type="text/javascript"></script>
<script src="{{ asset('js/jspdf.source.js') }}" type="text/javascript"></script>
<script src="{{ asset('js/jspdf.plugin.split_text_to_size.js') }}" type="text/javascript"></script>
@ -91,15 +92,18 @@
</ul>
<div class="navbar-form navbar-right">
@if (Auth::check() && Auth::user()->registered)
{{ Auth::user()->email }} &nbsp;
@else
@if (!Auth::check() || !Auth::user()->registered)
{{ Button::sm_primary('Sign up', array('data-toggle'=>'modal', 'data-target'=>'#signUpModal')) }}
@endif
<div class="btn-group">
<button type="button" class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown">
My Account <span class="caret"></span>
@if (Auth::check() && Auth::user()->registered)
{{ Auth::user()->getFullName() }}
@else
My Account
@endif
<span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu">
<li>{{ link_to('account/details', 'Details') }}</li>
@ -359,7 +363,7 @@
$('#signUpModal').on('shown.bs.modal', function () {
$(['first_name','last_name','email','password']).each(function(i, field) {
var $input = $('form.signUpForm #'+field);
var $input = $('form.signUpForm #new_'+field);
if (!$input.val()) {
$input.focus();
return false;

View File

@ -36,7 +36,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 href="#" data-bind="click: $root.showClientForm, text: client.linkText"></a>
<a href="#" data-bind="click: $root.showClientForm, text: $root.clientLinkText"></a>
</div>
</div>
@ -204,12 +204,12 @@
array('Delete Invoice', "javascript:onDeleteClick()"),
)
)
, array('id'=>'actionDropDown', 'style'=>'text-align:left', 'data-bind'=>'css: enable.save'))->split(); }}
, array('id'=>'actionDropDown', 'style'=>'text-align:left', 'data-bind'=>'css: $root.enable.save'))->split(); }}
@else
{{ Button::primary_submit('Save Invoice', array('data-bind'=>'css: enable.save')) }}
{{ Button::primary_submit('Save Invoice', array('data-bind'=>'css: $root.enable.save')) }}
@endif
{{ Button::primary('Send Email', array('id' => 'email_button', 'onclick' => 'onEmailClick()', 'data-bind' => 'css: enable.email')) }}
{{ Button::primary('Send Email', array('id' => 'email_button', 'onclick' => 'onEmailClick()', 'data-bind' => 'css: $root.enable.email')) }}
</div>
<p>&nbsp;</p>
@ -326,7 +326,7 @@
<input onkeyup="onTaxRateChange()" data-bind="value: prettyRate, valueUpdate: 'afterkeydown'" style="text-align: right" class="form-control" onchange="refreshPDF()"//>
</td>
<td style="width:10px; cursor:pointer" class="hide-border td-icon">
&nbsp;<i data-bind="click: $root.removeTaxRate, visible: actionsVisible() &amp;&amp; $root.tax_rates().length > 1" class="fa fa-minus-circle" title="Remove item"/>
&nbsp;<i data-bind="click: $root.removeTaxRate, visible: actionsVisible() &amp;&amp; !isEmpty()" class="fa fa-minus-circle" title="Remove item"/>
</td>
</tr>
</tbody>
@ -380,7 +380,7 @@
model.loadClient($.parseJSON(ko.toJSON(new ClientModel())));
}
refreshPDF();
}).trigger('change');
}); //.trigger('change');
$('#terms, #public_notes, #invoice_number, #invoice_date, #due_date, #po_number, #discout, #currency_id').change(function() {
refreshPDF();
@ -410,7 +410,9 @@
$('#taxModal').on('shown.bs.modal', function () {
$('#taxModal input:first').focus();
}).on('hidden.bs.modal', function () {
// if the user changed the tax rates we need to trigger the
// change event on the selects for the model to get updated
$('table.invoice-table select').trigger('change');
})
$('#actionDropDown > button:first').click(function() {
@ -419,7 +421,13 @@
$('label.radio').addClass('radio-inline');
var client = model.invoice().client();
setComboboxValue($('.client_select'),
client.public_id(),
client.name.display());
applyComboboxListeners();
refreshPDF();
});
function applyComboboxListeners() {
@ -534,6 +542,11 @@
return;
}
event.preventDefault();
if (model.enable.save() != 'enabled') {
return;
}
$('.main_form').submit();
return false;
}
@ -609,7 +622,18 @@
});
self.tax_rates.filtered = ko.computed(function() {
return self.tax_rates().slice(1, self.tax_rates().length);
var i = 0;
for (i; i<self.tax_rates().length; i++) {
var taxRate = self.tax_rates()[i];
if (taxRate.isEmpty()) {
break;
}
}
//console.log('i is %s', i);
var rates = self.tax_rates().concat();
rates.splice(i, 1);
return rates;
});
@ -624,6 +648,7 @@
applyComboboxListeners();
}
/*
self.getBlankTaxRate = function() {
for (var i=0; i<self.tax_rates().length; i++) {
var taxRate = self.tax_rates()[i];
@ -632,6 +657,7 @@
}
}
}
*/
self.getTaxRate = function(name, rate) {
for (var i=0; i<self.tax_rates().length; i++) {
@ -640,10 +666,11 @@
return taxRate;
}
}
var taxRate = new TaxRateModel();
taxRate.name(name);
taxRate.rate(parseFloat(rate));
taxRate.is_deleted(true);
if (parseFloat(rate) > 0) taxRate.is_deleted(true);
self.tax_rates.push(taxRate);
return taxRate;
}
@ -688,9 +715,11 @@
name = email;
}
$('.client_select select').combobox('setSelected');
$('.client_select input.form-control').val(name);
$('.client_select .combobox-container').addClass('combobox-selected');
setComboboxValue($('.client_select'), -1, name);
//$('.client_select select').combobox('setSelected');
//$('.client_select input.form-control').val(name);
//$('.client_select .combobox-container').addClass('combobox-selected');
$('#emailError').css( "display", "none" );
//$('.client_select input.form-control').focus();
@ -700,6 +729,44 @@
model.clientBackup = false;
$('#clientModal').modal('hide');
}
self.enable = {};
self.enable.save = ko.computed(function() {
var isValid = false;
for (var i=0; i<self.invoice().client().contacts().length; i++) {
var contact = self.invoice().client().contacts()[i];
if (isValidEmailAddress(contact.email())) {
isValid = true;
} else {
isValid = false;
break;
}
}
return isValid ? "enabled" : "disabled";
});
self.enable.email = ko.computed(function() {
var isValid = false;
var sendTo = false;
for (var i=0; i<self.invoice().client().contacts().length; i++) {
var contact = self.invoice().client().contacts()[i];
if (isValidEmailAddress(contact.email())) {
isValid = true;
if (contact.send_invoice()) {
sendTo = true;
}
} else {
isValid = false;
break;
}
}
return isValid && sendTo ? "enabled" : "disabled";
});
self.clientLinkText = ko.computed(function() {
return self.invoice().client().public_id() ? 'Edit client details' : 'Create new client';
});
}
function InvoiceModel(data) {
@ -708,7 +775,7 @@
this.id = ko.observable('');
self.discount = ko.observable('');
self.frequency_id = ko.observable('');
self.currency_id = ko.observable({{ Session::get(SESSION_CURRENCY, DEFAULT_CURRENCY) }});
self.currency_id = ko.observable({{ Session::get(SESSION_CURRENCY) }});
self.terms = ko.observable(wordWrapText('{{ $account->invoice_terms }}', 340));
self.public_notes = ko.observable('');
self.po_number = ko.observable('');
@ -799,43 +866,6 @@
owner: this
});
self.client.linkText = ko.computed(function() {
return self.client().public_id() ? 'Edit client details' : 'Create new client';
});
self.enable = {};
self.enable.save = ko.computed(function() {
var isValid = false;
for (var i=0; i<self.client().contacts().length; i++) {
var contact = self.client().contacts()[i];
if (isValidEmailAddress(contact.email())) {
isValid = true;
} else {
isValid = false;
break;
}
}
return isValid ? "enabled" : "disabled";
});
self.enable.email = ko.computed(function() {
var isValid = false;
var sendTo = false;
for (var i=0; i<self.client().contacts().length; i++) {
var contact = self.client().contacts()[i];
if (isValidEmailAddress(contact.email())) {
isValid = true;
if (contact.send_invoice()) {
sendTo = true;
}
} else {
isValid = false;
break;
}
}
return isValid && sendTo ? "enabled" : "disabled";
});
self.removeItem = function(item) {
self.invoice_items.remove(item);
@ -933,6 +963,7 @@
}
}
self.showContact = function(elem) { if (elem.nodeType === 1) $(elem).hide().slideDown() }
self.hideContact = function(elem) { if (elem.nodeType === 1) $(elem).slideUp(function() { $(elem).remove(); }) }
@ -949,17 +980,19 @@
self.contacts.remove(this);
}
/*
self.placeholderName = ko.computed(function() {
self.name.display = ko.computed(function() {
if (self.name()) {
return self.name();
}
if (self.contacts().length == 0) return;
var contact = self.contacts()[0];
if (contact.first_name() || contact.last_name()) {
return contact.first_name() + ' ' + contact.last_name();
} else {
return '';
return contact.email();
}
});
*/
if (data) {
ko.mapping.fromJS(data, {}, this);
@ -992,6 +1025,7 @@
self.rate = ko.observable(0);
self.name = ko.observable('');
self.is_deleted = ko.observable(false);
self.is_blank = ko.observable(false);
self.actionsVisible = ko.observable(false);
if (data) {
@ -1012,7 +1046,7 @@
self.displayName = ko.computed({
read: function () {
var name = self.name() ? self.name() : '';
var rate = self.rate() ? parseFloat(self.rate()) + '% -' : '';
var rate = self.rate() ? parseFloat(self.rate()) + '% ' : '';
return rate + name;
},
write: function (value) {
@ -1086,8 +1120,7 @@
if (data) {
ko.mapping.fromJS(data, self.mapping, this);
//if (this.cost()) this.cost(formatMoney(this.cost(), parent.invoice.currency_id(), true)); // TODO_FIX
if (this.cost()) this.cost(formatMoney(this.cost(), 1, true));
//if (this.cost()) this.cost(formatMoney(this.cost(), model ? model.invoice().currency_id() : 1, true));
}
self.wrapped_notes = ko.computed({
@ -1159,6 +1192,7 @@
function onTaxRateChange()
{
var emptyCount = 0;
for(var i=0; i<model.tax_rates().length; i++) {
var taxRate = model.tax_rates()[i];
if (taxRate.isEmpty()) {
@ -1166,7 +1200,7 @@
}
}
if (emptyCount < 2) {
for(var i=0; i<2-emptyCount; i++) {
model.addTaxRate();
}
}
@ -1203,16 +1237,18 @@
var contact = client.contacts[i];
contact.send_invoice = invitationContactIds.indexOf(contact.public_id) >= 0;
}
@endif
model.invoice().addItem();
model.addTaxRate();
//model.addTaxRate();
@endif
@endif
model.invoice().tax(model.getTaxRate(model.invoice().tax_name(), model.invoice().tax_rate()));
for (var i=0; i<model.invoice().invoice_items().length; i++) {
var item = model.invoice().invoice_items()[i];
item.tax(model.getTaxRate(item.tax_name(), item.tax_rate()));
item.cost(parseFloat(item.cost()) > 0 ? formatMoney(item.cost(), model.invoice().currency_id(), true) : '');
}
onTaxRateChange();
if (!model.invoice().discount()) model.invoice().discount('');

View File

@ -120,7 +120,7 @@
for (var i=0; i<list.length; i++) {
var invoice = list[i];
var client = clientMap[invoice.client.public_id];
$invoiceCombobox.append(new Option(invoice.invoice_number + ' - ' + getClientDisplayName(client), invoice.public_id));
$invoiceCombobox.append(new Option(invoice.invoice_number + ' - ' + getClientDisplayName(client) + ' - ' + formatMoney(invoice.balance, invoice.currency_id), invoice.public_id));
}
$('select#invoice').combobox('refresh');
}).trigger('change');

View File

@ -326,6 +326,7 @@
}
, keyup: function (e) {
console.log('keyCode: %s', e.keyCode);
switch(e.keyCode) {
case 40: // down arrow
case 39: // right arrow
@ -353,7 +354,6 @@
break;
default:
console.log('keyCode: %s', e.keyCode);
this.clearTarget();
this.lookup();
}

View File

@ -237,7 +237,7 @@ function generatePDF(invoice) {
x += 16;
doc.text(footerLeft, x, 'Paid to Date');
var paid = formatMoney(0, currencyId, true);
var paid = formatMoney(invoice.amount - invoice.balance, currencyId, true);
var paidX = headerRight - (doc.getStringUnitWidth(paid) * doc.internal.getFontSize());
doc.text(paidX, x, paid);
@ -246,7 +246,7 @@ function generatePDF(invoice) {
doc.setFontType("bold");
doc.text(footerLeft, x, 'Balance Due');
var total = formatMoney(total, currencyId);
var total = formatMoney(invoice.balance, currencyId);
var totalX = headerRight - (doc.getStringUnitWidth(total) * doc.internal.getFontSize());
doc.text(totalX, x, total);