1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-09 12:42:36 +01:00

Starting to work on reports

This commit is contained in:
Hillel Coren 2013-12-15 14:55:50 +02:00
parent 19abec6f09
commit 374e34e733
29 changed files with 1827 additions and 129 deletions

View File

@ -56,4 +56,5 @@ Configure config/database.php and then initialize the database
* [webpatser/laravel-countries](https://github.com/webpatser/laravel-countries) - Almost ISO 3166_2, 3166_3, currency, Capital and more for all countries
* [briannesbitt/Carbon](https://github.com/briannesbitt/Carbon) - A simple API extension for DateTime with PHP 5.3+
* [thomaspark/bootswatch](https://github.com/thomaspark/bootswatch) - Themes for Bootstrap
* [mozilla/pdf.js)](https://github.com/mozilla/pdf.js) - PDF Reader in JavaScript
* [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

View File

@ -40,7 +40,7 @@ class AccountController extends \BaseController {
}
Auth::login($user, true);
Session::put('tz', 'US/Eastern');
Event::fire('user.login');
return Redirect::to('invoices/create');
}
@ -51,9 +51,8 @@ class AccountController extends \BaseController {
{
$account = Account::with('users')->findOrFail(Auth::user()->account_id);
$countries = Country::orderBy('name')->get();
$timezones = Timezone::orderBy('location')->get();
return View::make('accounts.details', array('account'=>$account, 'countries'=>$countries, 'timezones'=>$timezones));
return View::make('accounts.details', array('account'=>$account, 'countries'=>$countries));
}
else if ($section == ACCOUNT_SETTINGS)
{
@ -71,7 +70,10 @@ class AccountController extends \BaseController {
'account' => $account,
'accountGateway' => $accountGateway,
'config' => json_decode($config),
'gateways' => Gateway::all()
'gateways' => Gateway::all(),
'timezones' => Timezone::orderBy('location')->get(),
'dateFormats' => DateFormat::all(),
'datetimeFormats' => DatetimeFormat::all(),
];
foreach ($data['gateways'] as $gateway)
@ -383,6 +385,12 @@ class AccountController extends \BaseController {
$account = Account::findOrFail(Auth::user()->account_id);
$account->account_gateways()->forceDelete();
$account->timezone_id = Input::get('timezone_id') ? Input::get('timezone_id') : null;
$account->date_format_id = Input::get('date_format_id') ? Input::get('date_format_id') : null;
$account->datetime_format_id = Input::get('datetime_format_id') ? Input::get('datetime_format_id') : null;
Event::fire('user.refresh');
$account->invoice_terms = Input::get('invoice_terms');
$account->save();
@ -431,7 +439,6 @@ class AccountController extends \BaseController {
$account->state = trim(Input::get('state'));
$account->postal_code = trim(Input::get('postal_code'));
$account->country_id = Input::get('country_id') ? Input::get('country_id') : null;
$account->timezone_id = Input::get('timezone_id') ? Input::get('timezone_id') : null;
$account->save();
$user = $account->users()->first();

View File

@ -179,11 +179,12 @@ class ClientController extends \BaseController {
$client->address2 = trim(Input::get('address2'));
$client->city = trim(Input::get('city'));
$client->state = trim(Input::get('state'));
$client->notes = trim(Input::get('notes'));
$client->postal_code = trim(Input::get('postal_code'));
$client->country_id = Input::get('country_id') ? Input::get('country_id') : null;
$client->notes = trim(Input::get('notes'));
$client->client_size_id = Input::get('client_size_id') ? Input::get('client_size_id') : null;
$client->client_industry_id = Input::get('client_industry_id') ? Input::get('client_industry_id') : null;
$client->website = trim(Input::get('website'));
$client->save();
@ -238,15 +239,15 @@ class ClientController extends \BaseController {
$ids = Input::get('id') ? Input::get('id') : Input::get('ids');
$clients = Client::scope($ids)->get();
foreach ($clients as $client) {
if ($action == 'archive') {
$client->delete();
} else if ($action == 'delete') {
$client->forceDelete();
foreach ($clients as $client) {
if ($action == 'delete') {
$client->is_deleted = true;
$client->save();
}
$client->delete();
}
$message = Utils::pluralize('Successfully '.$action.'d ? client', count($ids));
$message = Utils::pluralize('Successfully '.$action.'d ? client', count($clients));
Session::flash('message', $message);
return Redirect::to('clients');

View File

@ -22,6 +22,7 @@ class CreditController extends \BaseController {
->join('clients', 'clients.id', '=','credits.client_id')
->where('clients.account_id', '=', Auth::user()->account_id)
->where('clients.deleted_at', '=', null)
->where('credits.deleted_at', '=', null)
->select('credits.public_id', 'clients.name as client_name', 'clients.public_id as client_public_id', 'credits.amount', 'credits.credit_date');
if ($clientPublicId) {
@ -55,7 +56,7 @@ class CreditController extends \BaseController {
<ul class="dropdown-menu" role="menu">
<li><a href="' . URL::to('credits/'.$model->public_id.'/edit') . '">Edit Credit</a></li>
<li class="divider"></li>
<li><a href="' . URL::to('credits/'.$model->public_id.'/archive') . '">Archive Credit</a></li>
<li><a href="javascript:archiveEntity(' . $model->public_id. ')">Archive Credit</a></li>
<li><a href="javascript:deleteEntity(' . $model->public_id. ')">Delete Credit</a></li>
</ul>
</div>';
@ -65,7 +66,7 @@ class CreditController extends \BaseController {
}
public function create($clientPublicId)
public function create($clientPublicId = null)
{
$client = null;
if ($clientPublicId) {
@ -144,14 +145,14 @@ class CreditController extends \BaseController {
$credits = Credit::scope($ids)->get();
foreach ($credits as $credit) {
if ($action == 'archive') {
$credit->delete();
} else if ($action == 'delete') {
$credit->forceDelete();
if ($action == 'delete') {
$credit->is_deleted = true;
$credit->save();
}
$credit->delete();
}
$message = Utils::pluralize('Successfully '.$action.'d ? credit', count($ids));
$message = Utils::pluralize('Successfully '.$action.'d ? credit', count($credits));
Session::flash('message', $message);
return Redirect::to('credits');

View File

@ -37,6 +37,7 @@ class InvoiceController extends \BaseController {
->join('invoice_statuses', 'invoice_statuses.id', '=', 'invoices.invoice_status_id')
->where('invoices.account_id', '=', Auth::user()->account_id)
->where('invoices.deleted_at', '=', null)
->where('clients.deleted_at', '=', null)
->where('invoices.is_recurring', '=', false)
->select('clients.public_id as client_public_id', 'invoice_number', 'clients.name as client_name', 'invoices.public_id', 'total', 'invoices.balance', 'invoice_date', 'due_date', 'invoice_statuses.name as invoice_status_name');
@ -81,7 +82,7 @@ class InvoiceController extends \BaseController {
<ul class="dropdown-menu" role="menu">
<li><a href="' . URL::to('invoices/'.$model->public_id.'/edit') . '">Edit Invoice</a></li>
<li class="divider"></li>
<li><a href="' . URL::to('invoices/'.$model->public_id.'/archive') . '">Archive Invoice</a></li>
<li><a href="javascript:archiveEntity(' . $model->public_id . ')">Archive Invoice</a></li>
<li><a href="javascript:deleteEntity(' . $model->public_id . ')">Delete Invoice</a></li>
</ul>
</div>';
@ -138,7 +139,7 @@ class InvoiceController extends \BaseController {
<ul class="dropdown-menu" role="menu">
<li><a href="' . URL::to('invoices/'.$model->public_id.'/edit') . '">Edit Invoice</a></li>
<li class="divider"></li>
<li><a href="' . URL::to('invoices/'.$model->public_id.'/archive') . '">Archive Invoice</a></li>
<li><a href="javascript:archiveEntity(' . $model->public_id . ')">Archive Invoice</a></li>
<li><a href="javascript:deleteEntity(' . $model->public_id . ')">Delete Invoice</a></li>
</ul>
</div>';
@ -150,12 +151,22 @@ class InvoiceController extends \BaseController {
public function view($invitationKey)
{
$invitation = Invitation::with('user', 'invoice.account', 'invoice.invoice_items', 'invoice.client.account.account_gateways')
$invitation = Invitation::with('user', 'invoice.account', 'invoice.client', 'invoice.invoice_items', 'invoice.client.account.account_gateways')
->where('invitation_key', '=', $invitationKey)->firstOrFail();
$user = $invitation->user;
$invoice = $invitation->invoice;
if (!$invoice || $invoice->is_deleted) {
return View::make('invoices.deleted');
}
$client = $invoice->client;
if (!$client || $client->is_deleted) {
return View::make('invoices.deleted');
}
if ($invoice->invoice_status_id < INVOICE_STATUS_VIEWED) {
$invoice->invoice_status_id = INVOICE_STATUS_VIEWED;
$invoice->save();
@ -438,6 +449,10 @@ class InvoiceController extends \BaseController {
$client->state = trim($inputClient->state);
$client->postal_code = trim($inputClient->postal_code);
$client->country_id = $inputClient->country_id ? $inputClient->country_id : null;
$client->notes = trim($inputClient->notes);
$client->client_size_id = $inputClient->client_size_id ? $inputClient->client_size_id : null;
$client->client_industry_id = $inputClient->client_industry_id ? $inputClient->client_industry_id : null;
$client->website = trim($inputClient->website);
$client->save();
$isPrimary = true;
@ -498,7 +513,7 @@ class InvoiceController extends \BaseController {
$invoice->frequency_id = $input->frequency_id ? $input->frequency_id : 0;
$invoice->start_date = Utils::toSqlDate($input->start_date);
$invoice->end_date = Utils::toSqlDate($input->end_date);
$invoice->notes = $input->notes;
$invoice->terms = $input->terms;
$invoice->po_number = $input->po_number;
@ -587,6 +602,7 @@ class InvoiceController extends \BaseController {
/*
*/
$message = $clientPublicId == "-1" ? ' and created client' : '';
if ($action == 'clone')
{
return InvoiceController::cloneInvoice($publicId);
@ -595,9 +611,9 @@ class InvoiceController extends \BaseController {
{
$this->mailer->sendInvoice($invoice);
Session::flash('message', 'Successfully emailed invoice');
Session::flash('message', 'Successfully emailed invoice'.$message);
} else {
Session::flash('message', 'Successfully saved invoice');
Session::flash('message', 'Successfully saved invoice'.$message);
}
$url = 'invoices/' . $invoice->public_id . '/edit';
@ -640,14 +656,14 @@ class InvoiceController extends \BaseController {
$invoices = Invoice::scope($ids)->get();
foreach ($invoices as $invoice) {
if ($action == 'archive') {
$invoice->delete();
} else if ($action == 'delete') {
$invoice->forceDelete();
if ($action == 'delete') {
$invoice->is_deleted = true;
$invoice->save();
}
$invoice->delete();
}
$message = Utils::pluralize('Successfully '.$action.'d ? invoice', count($ids));
$message = Utils::pluralize('Successfully '.$action.'d ? invoice', count($invoices));
Session::flash('message', $message);
return Redirect::to('invoices');

View File

@ -18,6 +18,7 @@ class PaymentController extends \BaseController
->leftJoin('invoices', 'invoices.id', '=','payments.invoice_id')
->where('payments.account_id', '=', Auth::user()->account_id)
->where('payments.deleted_at', '=', null)
->where('clients.deleted_at', '=', null)
->select('payments.public_id', 'payments.transaction_reference', 'clients.name as client_name', 'clients.public_id as client_public_id', 'payments.amount', 'payments.payment_date', 'invoices.public_id as invoice_public_id', 'invoices.invoice_number');
if ($clientPublicId) {
@ -57,7 +58,7 @@ class PaymentController extends \BaseController
<ul class="dropdown-menu" role="menu">
<li><a href="' . URL::to('payments/'.$model->public_id.'/edit') . '">Edit Payment</a></li>
<li class="divider"></li>
<li><a href="' . URL::to('payments/'.$model->public_id.'/archive') . '">Archive Payment</a></li>
<li><a href="javascript:archiveEntity(' . $model->public_id. ')">Archive Payment</a></li>
<li><a href="javascript:deleteEntity(' . $model->public_id. ')">Delete Payment</a></li>
</ul>
</div>';
@ -152,15 +153,15 @@ class PaymentController extends \BaseController
$ids = Input::get('id') ? Input::get('id') : Input::get('ids');
$payments = Payment::scope($ids)->get();
foreach ($payments as $payment) {
if ($action == 'archive') {
$payment->delete();
} else if ($action == 'delete') {
$payment->forceDelete();
foreach ($payments as $payment) {
if ($action == 'delete') {
$payment->is_deleted = true;
$payment->save();
}
$payment->delete();
}
$message = Utils::pluralize('Successfully '.$action.'d ? payment', count($ids));
$message = Utils::pluralize('Successfully '.$action.'d ? payment', count($payments));
Session::flash('message', $message);
return Redirect::to('payments');

View File

@ -0,0 +1,46 @@
<?php
class ReportController extends \BaseController {
public function monthly()
{
$records = DB::table('invoices')
->select(DB::raw('sum(total) as total, month(invoice_date) as month'))
->where('invoices.deleted_at', '=', null)
->where('invoices.invoice_date', '>', 0)
->groupBy('month');
$totals = $records->lists('total');
$dates = $records->lists('month');
$data = array_combine($dates, $totals);
$startDate = date_create('2013-06-30');
$endDate = date_create('2013-12-30');
$endDate = $endDate->modify('+1 month');
$interval = new DateInterval('P1M');
$period = new DatePeriod($startDate, $interval, $endDate);
$totals = [];
$dates = [];
foreach ($period as $d)
{
$date = $d->format('Y-m-d');
$month = $d->format('n');
$dates[] = $date;
$totals[] = isset($data[$month]) ? $data[$month] : 0;
}
$width = (ceil( max($totals) / 100 ) * 100) / 10;
$params = [
'dates' => $dates,
'totals' => $totals,
'scaleStepWidth' => $width,
];
return View::make('reports.monthly', $params);
}
}

View File

@ -74,6 +74,7 @@ class UserController extends BaseController {
{
if( Confide::user() )
{
Event::fire('user.login');
return Redirect::to('/clients');
}
else
@ -101,10 +102,7 @@ class UserController extends BaseController {
// Get the value from the config file instead of changing the controller
if ( Confide::logAttempt( $input, Config::get('confide::signup_confirm') ) )
{
$account = Account::findOrFail(Auth::user()->account_id);
$account->last_login = Carbon::now()->toDateTimeString();
$account->save();
Event::fire('user.login');
// Redirect the user to the URL they were trying to access before
// caught by the authentication filter IE Redirect::guest('user/login').
// Otherwise fallback to '/'

View File

@ -31,6 +31,8 @@ class ConfideSetupUsersTable extends Migration {
Schema::dropIfExists('countries');
Schema::dropIfExists('timezones');
Schema::dropIfExists('frequencies');
Schema::dropIfExists('date_formats');
Schema::dropIfExists('datetime_formats');
Schema::create('countries', function($table)
{
@ -63,10 +65,26 @@ class ConfideSetupUsersTable extends Migration {
$t->string('location');
});
Schema::create('date_formats', function($t)
{
$t->increments('id');
$t->string('format');
$t->string('label');
});
Schema::create('datetime_formats', function($t)
{
$t->increments('id');
$t->string('format');
$t->string('label');
});
Schema::create('accounts', function($t)
{
$t->increments('id');
$t->unsignedInteger('timezone_id')->nullable();
$t->unsignedInteger('date_format_id')->nullable();
$t->unsignedInteger('datetime_format_id')->nullable();
$t->timestamps();
$t->softDeletes();
@ -85,19 +103,20 @@ class ConfideSetupUsersTable extends Migration {
$t->text('invoice_terms');
$t->foreign('timezone_id')->references('id')->on('timezones');
$t->foreign('date_format_id')->references('id')->on('date_formats');
$t->foreign('datetime_format_id')->references('id')->on('datetime_formats');
$t->foreign('country_id')->references('id')->on('countries');
});
Schema::create('gateways', function($t)
{
$t->increments('id');
$t->timestamps();
$t->softDeletes();
$t->timestamps();
$t->string('name');
$t->string('provider');
$t->boolean('visible')->default(true);
});
});
Schema::create('account_gateways', function($t)
{
@ -105,8 +124,7 @@ class ConfideSetupUsersTable extends Migration {
$t->unsignedInteger('account_id');
$t->unsignedInteger('gateway_id');
$t->timestamps();
$t->softDeletes();
$t->text('config');
$t->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
@ -175,10 +193,12 @@ class ConfideSetupUsersTable extends Migration {
$t->string('work_phone');
$t->text('notes');
$t->decimal('balance', 10, 2);
$t->decimal('paid_to_date', 10, 2);
$t->timestamp('last_login');
$t->string('website');
$t->unsignedInteger('client_industry_id')->nullable();
$t->unsignedInteger('client_size_id')->nullable();
$t->boolean('is_deleted');
$t->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
$t->foreign('user_id')->references('id')->on('users');
@ -240,8 +260,8 @@ class ConfideSetupUsersTable extends Migration {
$t->string('po_number');
$t->date('invoice_date')->nullable();
$t->date('due_date')->nullable();
$t->text('notes');
$t->text('terms');
$t->boolean('is_deleted');
$t->boolean('is_recurring');
$t->unsignedInteger('frequency_id');
$t->date('start_date')->nullable();
@ -341,7 +361,8 @@ class ConfideSetupUsersTable extends Migration {
$t->unsignedInteger('user_id')->nullable();
$t->timestamps();
$t->softDeletes();
$t->boolean('is_deleted');
$t->decimal('amount', 10, 2);
$t->date('payment_date');
$t->string('transaction_reference');
@ -367,8 +388,9 @@ class ConfideSetupUsersTable extends Migration {
$t->timestamps();
$t->softDeletes();
$t->boolean('is_deleted');
$t->decimal('amount', 10, 2);
$t->date('credit_date');
$t->date('credit_date')->nullable();
$t->string('credit_number');
$t->foreign('account_id')->references('id')->on('accounts');
@ -432,5 +454,7 @@ class ConfideSetupUsersTable extends Migration {
Schema::dropIfExists('countries');
Schema::dropIfExists('timezones');
Schema::dropIfExists('frequencies');
Schema::dropIfExists('date_formats');
Schema::dropIfExists('datetime_formats');
}
}

View File

@ -69,6 +69,13 @@ class ConstantsSeeder extends Seeder
ClientSize::create(array('name' => '101 - 500'));
ClientSize::create(array('name' => '500+'));
DatetimeFormat::create(array('format' => 'F j, Y, g:i a', 'label' => 'March 10, 2013, 6:15 pm'));
DatetimeFormat::create(array('format' => 'D M jS, Y g:ia', 'label' => 'Mon March 10th, 2013, 6:15 pm'));
DateFormat::create(array('format' => 'F j, Y', 'label' => 'March 10, 2013'));
DateFormat::create(array('format' => 'D M jS', 'label' => 'Mon March 10th, 2013'));
$gateways = [
array('name'=>'Authorize.Net AIM', 'provider'=>'AuthorizeNet_AIM'),
array('name'=>'Authorize.Net SIM', 'provider'=>'AuthorizeNet_SIM'),

View File

@ -0,0 +1,36 @@
<?php
class UserEventHandler
{
public function subscribe($events)
{
$events->listen('user.signup', 'UserEventHandler@onSignup');
$events->listen('user.login', 'UserEventHandler@onLogin');
$events->listen('user.refresh', 'UserEventHandler@onRefresh');
}
public function onSignup()
{
dd('user signed up');
}
public function onLogin()
{
$account = Account::findOrFail(Auth::user()->account_id);
$account->last_login = Carbon::now()->toDateTimeString();
$account->save();
Event::fire('user.refresh');
}
public function onRefresh()
{
$user = User::whereId(Auth::user()->id)->with('account', 'account.date_format', 'account.datetime_format', 'account.timezone')->firstOrFail();
$account = $user->account;
Session::put(SESSION_TIMEZONE, $account->timezone ? $account->timezone->name : DEFAULT_TIMEZONE);
Session::put(SESSION_DATE_FORMAT, $account->date_format ? $account->date_format->format : DEFAULT_DATE_FORMAT);
Session::put(SESSION_DATETIME_FORMAT, $account->datetime_format ? $account->datetime_format->format : DEFAULT_DATETIME_FORMAT);
}
}

View File

@ -52,30 +52,25 @@ class Utils
}
public static function timestampToDateTimeString($timestamp) {
$tz = Session::get('tz');
if (!$tz) {
$tz = 'US/Eastern';
}
$date = new Carbon($timestamp);
$date->tz = $tz;
if ($date->year < 1900) {
return '';
}
return $date->format('D M jS, Y g:ia');
$timezone = Session::get(SESSION_TIMEZONE, DEFAULT_TIMEZONE);
$format = Session::get(SESSION_DATETIME_FORMAT, DEFAULT_DATETIME_FORMAT);
return Utils::timestampToString($timestamp, $timezone, $format);
}
public static function timestampToDateString($timestamp) {
$tz = Session::get('tz');
if (!$tz) {
$tz = 'US/Eastern';
}
$timezone = Session::get(SESSION_TIMEZONE, DEFAULT_TIMEZONE);
$format = Session::get(SESSION_DATE_FORMAT, DEFAULT_DATE_FORMAT);
return Utils::timestampToString($timestamp, $timezone, $format);
}
public static function timestampToString($timestamp, $timzone, $format)
{
$date = new Carbon($timestamp);
$date->tz = $tz;
$date->tz = $timzone;
if ($date->year < 1900) {
return '';
}
return $date->toFormattedDateString();
return $date->format($format);
}
public static function toSqlDate($date)

View File

@ -35,6 +35,17 @@ class Account extends Eloquent
return $this->belongsTo('Timezone');
}
public function date_format()
{
return $this->belongsTo('DateFormat');
}
public function datetime_format()
{
return $this->belongsTo('DatetimeFormat');
}
public function isGatewayConfigured($gatewayId = 0)
{
if ($gatewayId)

View File

@ -131,6 +131,16 @@ class Client extends EntityModel
return $str;
}
public function getWebsite()
{
if (!$this->website)
{
return '';
}
return link_to($this->website, $this->website);
}
public function getDateCreated()
{
if ($this->created_at == '0000-00-00 00:00:00')

7
app/models/DateFormat.php Executable file
View File

@ -0,0 +1,7 @@
<?php
class DateFormat extends Eloquent
{
public $timestamps = false;
protected $softDelete = false;
}

7
app/models/DatetimeFormat.php Executable file
View File

@ -0,0 +1,7 @@
<?php
class DatetimeFormat extends Eloquent
{
public $timestamps = false;
protected $softDelete = false;
}

View File

@ -2,5 +2,5 @@
class Gateway extends Eloquent
{
protected $softDelete = true;
}

View File

@ -14,7 +14,7 @@
//dd(DB::getQueryLog());
//dd(Client::getPrivateId(1));
//dd(new DateTime());
//Event::fire('user.signup');
Route::get('/send_emails', function() {
Artisan::call('ninja:send-invoices');
@ -80,9 +80,9 @@ Route::group(array('before' => 'auth'), function()
Route::resource('credits', 'CreditController');
Route::get('credits/create/{client_id?}', 'CreditController@create');
Route::get('api/credits/{client_id?}', array('as'=>'api.credits', 'uses'=>'CreditController@getDatatable'));
Route::post('credits/bulk', 'PaymentController@bulk');
Route::post('credits/bulk', 'CreditController@bulk');
Route::get('reports', function() { return View::make('header'); });
Route::get('reports', 'ReportController@monthly');
});
@ -160,4 +160,12 @@ define('FREQUENCY_FOUR_WEEKS', 3);
define('FREQUENCY_MONTHLY', 4);
define('FREQUENCY_THREE_MONTHS', 5);
define('FREQUENCY_SIX_MONTHS', 6);
define('FREQUENCY_ANNUALLY', 7);
define('FREQUENCY_ANNUALLY', 7);
define('SESSION_TIMEZONE', 'timezone');
define('SESSION_DATE_FORMAT', 'dateFormat');
define('SESSION_DATETIME_FORMAT', 'datetimeFormat');
define('DEFAULT_TIMEZONE', 'US/Eastern');
define('DEFAULT_DATE_FORMAT', 'F j, Y');
define('DEFAULT_DATETIME_FORMAT', 'F j, Y, g:i a');

View File

@ -18,6 +18,7 @@ ClassLoader::addDirectories(array(
app_path().'/models',
app_path().'/database/seeds',
app_path().'/libraries',
app_path().'/handlers',
));
@ -70,6 +71,11 @@ App::down(function()
return Response::make("Be right back!", 503);
});
Event::subscribe('UserEventHandler');
/*
|--------------------------------------------------------------------------
| Require The Filters File

View File

@ -27,8 +27,6 @@
{{ Former::legend('Account') }}
{{ Former::text('name') }}
{{ Former::select('timezone_id')->addOption('','')->label('Timezone')
->fromQuery($timezones, 'location', 'id')->select($account->timezone_id) }}
{{ Former::file('logo')->max(2, 'MB')->accept('image')->wrap('test') }}
@if (file_exists($account->getLogoPath()))

View File

@ -3,7 +3,7 @@
@section('content')
@parent
{{ Former::open()->addClass('col-md-10 col-md-offset-1') }}
{{ Former::open()->addClass('col-md-8 col-md-offset-2') }}
{{ Former::populate($account) }}
{{ Former::legend('Payment Gateway') }}
@ -36,6 +36,15 @@
@endforeach
{{ Former::legend('Date and Time') }}
{{ Former::select('timezone_id')->addOption('','')->label('Timezone')
->fromQuery($timezones, 'location', 'id')->select($account->timezone_id) }}
{{ Former::select('date_format_id')->addOption('','')->label('Date Format')
->fromQuery($dateFormats, 'label', 'id')->select($account->date_format_id) }}
{{ Former::select('datetime_format_id')->addOption('','')->label('Date/Time Format')
->fromQuery($datetimeFormats, 'label', 'id')->select($account->datetime_format_id) }}
{{ Former::legend('Invoices') }}
{{ Former::textarea('invoice_terms') }}

View File

@ -24,6 +24,7 @@
{{ Former::legend('Organization') }}
{{ Former::text('name') }}
{{ Former::text('website') }}
{{ Former::text('work_phone')->label('Phone') }}

View File

@ -50,6 +50,7 @@
<p>{{ $client->getPhone() }}</p>
<p>{{ $client->getNotes() }}</p>
<p>{{ $client->getIndustry() }}</p>
<p>{{ $client->getWebsite() }}
</div>
<div class="col-md-3">

View File

@ -273,7 +273,7 @@
{{ HTML::menu_link('invoice') }}
{{ HTML::menu_link('payment') }}
{{ HTML::menu_link('credit') }}
{{-- HTML::nav_link('reports', 'Reports') --}}
{{ HTML::nav_link('reports', 'Reports') }}
</ul>
<ul class="nav navbar-nav navbar-right">
<li class="dropdown">

View File

@ -0,0 +1,7 @@
@extends('header')
@section('content')
The requested invoice is no longer available.
@stop

View File

@ -22,7 +22,7 @@
->addGroupClass('client_select closer-row') }}
<div class="form-group" style="margin-bottom: 8px">
<div class="col-lg-8 col-lg-offset-4">
<div class="col-lg-8 col-sm-8 col-lg-offset-4 col-sm-offset-4">
<a href="#" data-bind="click: showClientForm, text: showClientText"></a>
</div>
</div>
@ -38,7 +38,7 @@
</div>
</div>
{{ Former::text('discount')->data_bind("value: discount, valueUpdate: 'afterkeydown'") }}
{{ Former::textarea('notes')->data_bind("value: notes, valueUpdate: 'afterkeydown'") }}
{{ Former::textarea('terms')->data_bind("value: wrapped_terms, valueUpdate: 'afterkeydown'") }}
</div>
<div class="col-md-4" id="col_2">
@ -94,7 +94,7 @@
->raw()->data_bind("value: product_key, valueUpdate: 'afterkeydown'")->addClass('datalist') }}
</td>
<td style="width:300px">
<textarea onkeyup="checkWordWrap(event)" data-bind="value: notes, valueUpdate: 'afterkeydown'" rows="1" cols="60" style="resize: none;" class="form-control" onchange="refreshPDF()"></textarea>
<textarea data-bind="value: wrapped_notes, valueUpdate: 'afterkeydown'" rows="1" cols="60" style="resize: none;" class="form-control word-wrap" onchange="refreshPDF()"></textarea>
</td>
<td style="width:100px">
<input onkeyup="onChange()" data-bind="value: cost, valueUpdate: 'afterkeydown'" style="text-align: right" class="form-control" onchange="refreshPDF()"//>
@ -195,6 +195,7 @@
{{ Former::legend('Organization') }}
{{ Former::text('name')->data_bind("value: name, valueUpdate: 'afterkeydown'") }}
{{ Former::text('website')->data_bind("value: website, valueUpdate: 'afterkeydown'") }}
{{ Former::text('work_phone')->data_bind("value: work_phone, valueUpdate: 'afterkeydown'")->label('Phone') }}
@ -437,9 +438,10 @@
this.client = new ClientModel();
self.discount = ko.observable('');
self.frequency_id = ko.observable('');
self.notes = ko.observable('');
self.terms = ko.observable('');
self.po_number = ko.observable('');
self.invoice_date = ko.observable('');
self.invoice_number = ko.observable('');
self.due_date = ko.observable('');
self.start_date = ko.observable('');
self.end_date = ko.observable('');
@ -455,11 +457,22 @@
}
}
self.wrapped_terms = ko.computed({
read: function() {
return this.terms();
},
write: function(value) {
value = wordWrapText(value, 250);
self.terms(value);
$('#terms').height(value.split('\n').length * 22);
},
owner: this
});
self.showClientText = ko.computed(function() {
return self.client.public_id() ? 'Edit client details' : 'Create new client';
});
self.showClientForm = function() {
if (self.client.public_id() == 0) {
$('#myModal input').val('');
@ -552,6 +565,7 @@
self.country_id = ko.observable('');
self.client_size_id = ko.observable('');
self.client_industry_id = ko.observable('');
self.website = ko.observable('');
self.contacts = ko.observableArray();
self.mapping = {
@ -617,6 +631,18 @@
ko.mapping.fromJS(data, {}, this);
}
self.wrapped_notes = ko.computed({
read: function() {
return this.notes();
},
write: function(value) {
value = wordWrapText(value);
self.notes(value);
onChange();
},
owner: this
});
this.rawTotal = ko.computed(function() {
var cost = parseFloat(self.cost());
var qty = parseFloat(self.qty());
@ -646,36 +672,6 @@
}
}
function checkWordWrap(event)
{
var doc = new jsPDF('p', 'pt');
doc.setFont('Helvetica','');
doc.setFontSize(10);
var $textarea = $(event.target || event.srcElement);
var lines = $textarea.val().split("\n");
for (var i = 0; i < lines.length; i++) {
var numLines = doc.splitTextToSize(lines[i], 200).length;
if (numLines <= 1) continue;
var j = 0; space = lines[i].length;
while (j++ < lines[i].length) {
if (lines[i].charAt(j) === " ") space = j;
}
lines[i + 1] = lines[i].substring(space + 1) + (lines[i + 1] || "");
lines[i] = lines[i].substring(0, space);
}
var val = lines.slice(0, 6).join("\n");
if (val != $textarea.val())
{
var model = ko.dataFor($textarea[0]);
model.notes(val);
refreshPDF();
}
$textarea.height(val.split('\n').length * 22);
onChange();
}
function onChange()
{
var hasEmpty = false;
@ -688,6 +684,10 @@
if (!hasEmpty) {
model.addItem();
}
$('.word-wrap').each(function(index, input) {
$(input).height($(input).val().split('\n').length * 22);
});
}
var products = {{ $products }};
@ -696,8 +696,8 @@
for (var i=0; i<clients.length; i++) {
var client = clients[i];
for (var i=0; i<client.contacts.length; i++) {
var contact = client.contacts[i];
for (var j=0; j<client.contacts.length; j++) {
var contact = client.contacts[j];
contact.send_invoice = contact.is_primary;
}
clientMap[client.public_id] = client;
@ -716,7 +716,8 @@
contact.send_invoice = invitationContactIds.indexOf(contact.public_id) >= 0;
}
@else
model.invoice_number = '{{ $invoiceNumber }}';
model.invoice_number('{{ $invoiceNumber }}');
model.terms(wordWrapText('{{ $account->invoice_terms }}', 250));
@endif
model.invoice_items.push(new ItemModel());
ko.applyBindings(model);

View File

@ -0,0 +1,39 @@
@extends('header')
@section('head')
@parent
<script src="{{ asset('js/chart.js') }}" type="text/javascript"></script>
@stop
@section('content')
<center style="padding-top: 40px">
<canvas id="monthly-reports" width="800" height="300"></canvas>
</center>
<script type="text/javascript">
var ctx = document.getElementById('monthly-reports').getContext('2d');
var chart = {
labels: {{ json_encode($dates)}},
datasets: [{
data: {{ json_encode($totals) }},
fillColor : "rgba(151,187,205,0.5)",
strokeColor : "rgba(151,187,205,1)",
}]
}
var options = {
scaleOverride: true,
scaleSteps: 10,
scaleStepWidth: {{ $scaleStepWidth }},
scaleStartValue: 0,
scaleLabel : "<%=formatMoney(value)%>",
};
new Chart(ctx).Bar(chart, options);
</script>
@stop

1426
public/js/Chart.js vendored Executable file

File diff suppressed because it is too large Load Diff

View File

@ -139,12 +139,16 @@ function generatePDF(invoice) {
var x = tableTop + (line * rowHeight);
doc.lines([[0,0],[headerRight-tableLeft+5,0]],tableLeft - 8, x);
doc.text(tableLeft, x+16, invoice.terms);
x += 16;
doc.text(footerLeft, x, 'Subtotal');
var total = formatNumber(total);
var totalX = headerRight - (doc.getStringUnitWidth(total) * doc.internal.getFontSize());
doc.text(totalX, x, total);
if (invoice.discount > 0) {
x += 16;
doc.text(footerLeft, x, 'Subtotal');
var total = formatNumber(total);
var totalX = headerRight - (doc.getStringUnitWidth(total) * doc.internal.getFontSize());
doc.text(totalX, x, total);
x += 16;
doc.text(footerLeft, x, 'Discount');
@ -154,6 +158,13 @@ function generatePDF(invoice) {
doc.text(discountX, x, discount);
}
x += 16;
doc.text(footerLeft, x, 'Paid to Date');
var paid = formatNumber(0);
var paidX = headerRight - (doc.getStringUnitWidth(paid) * doc.internal.getFontSize());
doc.text(paidX, x, paid);
x += 16;
doc.setFontType("bold");
doc.text(footerLeft, x, 'Total');
@ -229,15 +240,6 @@ function generatePDF(invoice) {
return doc;
}
function formatNumber(num) {
num = parseFloat(num);
if (!num) return '';
var p = num.toFixed(2).split(".");
return p[0].split("").reverse().reduce(function(acc, num, i, orig) {
return num + (i && !(i % 3) ? "," : "") + acc;
}, "") + "." + p[1];
}
/* Handle converting variables in the invoices (ie, MONTH+1) */
function processVariables(str) {
@ -316,6 +318,16 @@ function formatMoney(num) {
}
function formatNumber(num) {
num = parseFloat(num);
if (!num) num = 0;
var p = num.toFixed(2).split(".");
return p[0].split("").reverse().reduce(function(acc, num, i, orig) {
return num + (i && !(i % 3) ? "," : "") + acc;
}, "") + "." + p[1];
}
/* Set the defaults for DataTables initialisation */
$.extend( true, $.fn.dataTable.defaults, {
"sDom": "t<'row-fluid'<'span6'i><'span6'p>>",
@ -590,6 +602,28 @@ ko.bindingHandlers.dropdown = {
}
};
function wordWrapText(value, width)
{
if (!width) width = 200;
var doc = new jsPDF('p', 'pt');
doc.setFont('Helvetica','');
doc.setFontSize(10);
var lines = value.split("\n");
for (var i = 0; i < lines.length; i++) {
var numLines = doc.splitTextToSize(lines[i], width).length;
if (numLines <= 1) continue;
var j = 0; space = lines[i].length;
while (j++ < lines[i].length) {
if (lines[i].charAt(j) === " ") space = j;
}
lines[i + 1] = lines[i].substring(space + 1) + (lines[i + 1] || "");
lines[i] = lines[i].substring(0, space);
}
return lines.slice(0, 6).join("\n");
}
var CONSTS = {};
CONSTS.INVOICE_STATUS_DRAFT = 1;