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

Added support for themes

This commit is contained in:
Hillel Coren 2013-12-05 17:23:24 +02:00
parent 4f099c1fa5
commit ebdfa54489
41 changed files with 618 additions and 179 deletions

View File

@ -30,8 +30,7 @@ Install packages using Composer
Configure config/database.php and then initialize the database
php artisan migrate
php artisan db:seed
php artisan migrate --seed
### Frameworks/Libraries
@ -55,4 +54,5 @@ Configure config/database.php and then initialize the database
* [omnipay/omnipay](https://github.com/omnipay/omnipay) - A framework agnostic, multi-gateway payment processing library for PHP 5.3+
* [Intervention/image](https://github.com/Intervention/image) - PHP Image Manipulation
* [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+
* [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

View File

@ -9,7 +9,7 @@ return array(
'host' => 'localhost',
'database' => 'ninja',
'username' => 'ninja',
'password' => '1234',
'password' => 'ninja',
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',

View File

@ -181,9 +181,8 @@ class AccountController extends \BaseController {
continue;
}
$client = new Client;
$client->account_id = Auth::user()->account_id;
$contact = new Contact;
$client = Client::createNew();
$contact = Contact::createNew();
$count++;
foreach ($row as $index => $value)

View File

@ -21,20 +21,20 @@ class ClientController extends \BaseController {
public function getDatatable()
{
$clients = Client::scope()->with('contacts')->get();
$query = DB::table('clients')->join('contacts', 'clients.id', '=','contacts.client_id')->where('contacts.is_primary', '=', true);
return Datatable::collection($clients)
return Datatable::query($query)
->addColumn('checkbox', function($model) { return '<input type="checkbox" name="ids[]" value="' . $model->public_id . '">'; })
->addColumn('name', function($model) { return link_to('clients/' . $model->public_id, $model->name); })
->addColumn('contact', function($model) { return $model->contacts[0]->getFullName(); })
->addColumn('first_name', function($model) { return $model->first_name . ' ' . $model->last_name; })
->addColumn('balance', function($model) { return '$' . $model->balance; })
->addColumn('last_login', function($model) { return $model->contacts[0]->getLastLogin(); })
->addColumn('date_created', function($model) { return $model->created_at->toFormattedDateString(); })
->addColumn('email', function($model) { return HTML::mailto($model->contacts[0]->email, $model->contacts[0]->email); })
->addColumn('phone', function($model) { return $model->contacts[0]->phone; })
->addColumn('clients.last_login', function($model) { return timestampToDateString($model->last_login); })
->addColumn('clients.created_at', function($model) { return timestampToDateString($model->created_at); })
->addColumn('email', function($model) { return HTML::mailto($model->email, $model->email); })
->addColumn('phone', function($model) { return $model->phone; })
->addColumn('dropdown', function($model)
{
return '<div class="btn-group tr-action" style="display:none">
return '<div class="btn-group tr-action" style="visibility:hidden;">
<button type="button" class="btn btn-xs btn-default dropdown-toggle" data-toggle="dropdown">
Select <span class="caret"></span>
</button>
@ -42,12 +42,12 @@ class ClientController extends \BaseController {
<li><a href="' . URL::to('invoices/create/'.$model->public_id) . '">New Invoice</a></li>
<li><a href="' . URL::to('clients/'.$model->public_id.'/edit') . '">Edit Client</a></li>
<li class="divider"></li>
<li><a href="' . URL::to('clients/'.$model->public_id.'/archive') . '">Archive Client</a></li>
<li><a href="javascript:archiveEntity(' . $model->public_id. ')">Archive Client</a></li>
<li><a href="javascript:deleteEntity(' . $model->public_id. ')">Delete Client</a></li>
</ul>
</div>';
})
->orderColumns('name')
->orderColumns('name','first_name','balance','clients.last_login','clients.created_at','email','phone')
->make();
}
@ -133,7 +133,8 @@ class ClientController extends \BaseController {
$validator = Validator::make(Input::all(), $rules);
if ($validator->fails()) {
return Redirect::to('clients/' . $publicId . '/edit')
$url = $publicId ? 'clients/' . $publicId . '/edit' : 'clients/create';
return Redirect::to($url)
->withErrors($validator)
->withInput(Input::except('password'));
} else {
@ -158,12 +159,13 @@ class ClientController extends \BaseController {
$data = json_decode(Input::get('data'));
$contactIds = [];
$isPrimary = true;
foreach ($data->contacts as $contact)
{
if (isset($contact->id) && $contact->id)
if (isset($contact->public_id) && $contact->public_id)
{
$record = Contact::scope($contact->id)->firstOrFail();
$record = Contact::scope($contact->public_id)->firstOrFail();
}
else
{
@ -174,14 +176,16 @@ class ClientController extends \BaseController {
$record->first_name = $contact->first_name;
$record->last_name = $contact->last_name;
$record->phone = $contact->phone;
$record->is_primary = $isPrimary;
$isPrimary = false;
$client->contacts()->save($record);
$contactIds[] = $record->id;
$contactIds[] = $record->public_id;
}
foreach ($client->contacts as $contact)
{
if (!in_array($contact->id, $contactIds))
if (!in_array($contact->public_id, $contactIds))
{
$contact->forceDelete();
}
@ -197,6 +201,7 @@ class ClientController extends \BaseController {
{
$action = Input::get('action');
$ids = Input::get('ids') ? Input::get('ids') : [Input::get('id')];
$clients = Client::scope($ids)->get();
foreach ($clients as $client) {
@ -212,27 +217,4 @@ class ClientController extends \BaseController {
return Redirect::to('clients');
}
public function archive($publicId)
{
$client = Client::scope($publicId)->firstOrFail();
$client->delete();
foreach ($client->invoices as $invoice)
{
$invoice->delete();
}
Session::flash('message', 'Successfully archived ' . $client->name);
return Redirect::to('clients');
}
public function delete($id)
{
$client = Client::scope($publicId)->firstOrFail();
$client->forceDelete();
Session::flash('message', 'Successfully deleted ' . $client->name);
return Redirect::to('clients');
}
}

View File

@ -12,7 +12,7 @@ class CreditController extends \BaseController {
return View::make('list', array(
'entityType'=>ENTITY_CREDIT,
'title' => '- Credits',
'columns'=>['checkbox', 'Credit Number', 'Client', 'Amount', 'Credit Date']
'columns'=>['checkbox', 'Client', 'Amount', 'Credit Date', 'Action']
));
}
@ -28,36 +28,115 @@ class CreditController extends \BaseController {
$table = Datatable::collection($collection->get());
if (!$clientPublicId) {
$table->addColumn('checkbox', function($model) { return '<input type="checkbox" name="ids[]" value="' . $model->public_id . '">'; });
}
$table->addColumn('credit_number', function($model) { return $model->credit_number; });
if (!$clientPublicId) {
$table->addColumn('client', function($model) { return link_to('clients/' . $model->client->public_id, $model->client->name); });
$table->addColumn('checkbox', function($model) { return '<input type="checkbox" name="ids[]" value="' . $model->public_id . '">'; })
->addColumn('client', function($model) { return link_to('clients/' . $model->client->public_id, $model->client->name); });
}
return $table->addColumn('amount', function($model){ return '$' . money_format('%i', $model->amount); })
->addColumn('credit_date', function($model) { return (new Carbon($model->credit_date))->toFormattedDateString(); })
->orderColumns('number')
->addColumn('dropdown', function($model)
{
return '<div class="btn-group tr-action" style="display:none">
<button type="button" class="btn btn-xs btn-default dropdown-toggle" data-toggle="dropdown">
Select <span class="caret"></span>
</button>
<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:deleteEntity(' . $model->public_id. ')">Delete Credit</a></li>
</ul>
</div>';
})
->orderColumns('number')
->make();
}
public function archive($publicId)
{
$credit = Credit::scope($publicId)->firstOrFail();
$creidt->delete();
Session::flash('message', 'Successfully archived credit ' . $credit->credit_number);
return Redirect::to('credits');
public function create()
{
$data = array(
'client' => null,
'credit' => null,
'method' => 'POST',
'url' => 'credits',
'title' => '- New Credit',
'clients' => Client::scope()->orderBy('name')->get());
return View::make('credits.edit', $data);
}
public function delete($publicId)
public function edit($publicId)
{
$credit = Credit::scope($publicId)->firstOrFail();
$credit->forceDelete();
$data = array(
'client' => null,
'credit' => $credit,
'method' => 'PUT',
'url' => 'credits/' . $publicId,
'title' => '- Edit Credit',
'clients' => Client::scope()->orderBy('name')->get());
return View::make('credit.edit', $data);
}
Session::flash('message', 'Successfully deleted credit ' . $credit->credit_number);
return Redirect::to('credits');
public function store()
{
return $this->save();
}
public function update($publicId)
{
return $this->save($publicId);
}
private function save($publicId = null)
{
$rules = array(
'client' => 'required',
'amount' => 'required'
);
$validator = Validator::make(Input::all(), $rules);
if ($validator->fails()) {
$url = $publicId ? 'credits/' . $publicId . '/edit' : 'credits/create';
return Redirect::to($url)
->withErrors($validator)
->withInput();
} else {
if ($publicId) {
$credit = Credit::scope($publicId)->firstOrFail();
} else {
$credit = Credit::createNew();
}
$credit->client_id = Input::get('client');
$credit->credit_date = toSqlDate(Input::get('credit_date'));
$credit->amount = Input::get('amount');
$credit->save();
$message = $publicId ? 'Successfully updated credit' : 'Successfully created credit';
Session::flash('message', $message);
return Redirect::to('clients/' . $credit->client_id);
}
}
public function bulk()
{
$action = Input::get('action');
$ids = Input::get('ids');
$credits = Credit::scope($ids)->get();
foreach ($credits as $credit) {
if ($action == 'archive') {
$credit->delete();
} else if ($action == 'delete') {
$credit->forceDelete();
}
}
$message = pluralize('Successfully '.$action.'d ? credit', count($ids));
Session::flash('message', $message);
return Redirect::to('credits');
}
}

View File

@ -18,14 +18,20 @@ class InvoiceController extends \BaseController {
public function getDatatable($clientPublicId = null)
{
$collection = Invoice::scope()->with('client','invoice_items','invoice_status');
$query = DB::table('invoices')
->join('clients', 'clients.id', '=','invoices.client_id')
->join('invoice_statuses', 'invoice_statuses.id', '=', 'invoices.invoice_status_id');
//->select('clients.public_id as client_public_id', 'invoice_number', 'clients.name', 'invoices.public_id', 'total', 'invoices.balance', 'invoice_date', 'due_date');
//dd($query->get());
if ($clientPublicId) {
$clientId = Client::getPrivateId($clientPublicId);
$collection->where('client_id','=',$clientId);
$query->where('clients.public_id', '=', $clientPublicId);
//$clientId = Client::getPrivateId($clientPublicId);
//$collection->where('client_id','=',$clientId);
}
$table = Datatable::collection($collection->get());
$table = Datatable::query($query);
if (!$clientPublicId) {
$table->addColumn('checkbox', function($model) { return '<input type="checkbox" name="ids[]" value="' . $model->public_id . '">'; });
@ -34,14 +40,14 @@ class InvoiceController extends \BaseController {
$table->addColumn('invoice_number', function($model) { return link_to('invoices/' . $model->public_id . '/edit', $model->invoice_number); });
if (!$clientPublicId) {
$table->addColumn('client', function($model) { return link_to('clients/' . $model->client->public_id, $model->client->name); });
//$table->addColumn('client', function($model) { dd($model); return link_to('clients/' . $model->client_public_id, $model->client_name); });
}
return $table->addColumn('total', function($model){ return '$' . money_format('%i', $model->getTotal()); })
->addColumn('amount_due', function($model) { return '$' . money_format('%i', $model->getTotal()); })
return $table->addColumn('total', function($model){ return '$' . money_format('%i', $model->total); })
->addColumn('invoices.balance', function($model) { return '$' . money_format('%i', $model->balance); })
->addColumn('invoice_date', function($model) { return fromSqlDate($model->invoice_date); })
->addColumn('due_date', function($model) { return fromSqlDate($model->due_date); })
->addColumn('status', function($model) { return $model->invoice_status->name; })
//->addColumn('status', function($model) { return $model->invoice_status->name; })
->addColumn('dropdown', function($model)
{
return '<div class="btn-group tr-action" style="display:none">
@ -442,22 +448,3 @@ class InvoiceController extends \BaseController {
return Redirect::to('invoices');
}
public function archive($publicId)
{
$invoice = Invoice::scope($publicId)->firstOrFail();
$invoice->delete();
Session::flash('message', 'Successfully archived invoice ' . $invoice->invoice_number);
return Redirect::to('invoices');
}
public function delete($publicId)
{
$invoice = Invoice::scope($publicId)->firstOrFail();
$invoice->forceDelete();
Session::flash('message', 'Successfully deleted invoice ' . $invoice->invoice_number);
return Redirect::to('invoices');
}
}

View File

@ -7,13 +7,13 @@ class PaymentController extends \BaseController
return View::make('list', array(
'entityType'=>ENTITY_PAYMENT,
'title' => '- Payments',
'columns'=>['checkbox', 'Transaction Reference', 'Client', 'Amount', 'Payment Date']
'columns'=>['checkbox', 'Transaction Reference', 'Client', 'Invoice', 'Amount', 'Payment Date', 'Action']
));
}
public function getDatatable($clientPublicId = null)
{
$collection = Payment::scope()->with('invoice.client');
$collection = Payment::scope()->with('invoice', 'client');
if ($clientPublicId) {
$clientId = Client::getPrivateId($clientPublicId);
@ -26,14 +26,29 @@ class PaymentController extends \BaseController
$table->addColumn('checkbox', function($model) { return '<input type="checkbox" name="ids[]" value="' . $model->public_id . '">'; });
}
$table->addColumn('transaction_reference', function($model) { return $model->transaction_reference; });
$table->addColumn('transaction_reference', function($model) { return $model->transaction_reference ? $model->transaction_reference : '<i>Manual entry</i>'; });
if (!$clientPublicId) {
$table->addColumn('client', function($model) { return link_to('clients/' . $model->client->public_id, $model->client->name); });
}
return $table->addColumn('amount', function($model) { return '$' . $model->amount; })
return $table->addColumn('invoice_number', function($model) { return $model->invoice ? link_to('invoices/' . $model->invoice->public_id . '/edit', $model->invoice->invoice_number) : ''; })
->addColumn('amount', function($model) { return '$' . $model->amount; })
->addColumn('date', function($model) { return timestampToDateTimeString($model->created_at); })
->addColumn('dropdown', function($model)
{
return '<div class="btn-group tr-action" style="display:none">
<button type="button" class="btn btn-xs btn-default dropdown-toggle" data-toggle="dropdown">
Select <span class="caret"></span>
</button>
<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:deleteEntity(' . $model->public_id. ')">Delete Payment</a></li>
</ul>
</div>';
})
->orderColumns('client')
->make();
}
@ -42,10 +57,14 @@ class PaymentController extends \BaseController
public function create()
{
$data = array(
'client' => null,
'invoice' => null,
'invoices' => Invoice::with('client')->scope()->orderBy('invoice_number')->get(),
'payment' => null,
'method' => 'POST',
'url' => 'payments',
'title' => '- New Payment');
'title' => '- New Payment',
'clients' => Client::scope()->orderBy('name')->get());
return View::make('payments.edit', $data);
}
@ -54,29 +73,79 @@ class PaymentController extends \BaseController
{
$payment = Payment::scope($publicId)->firstOrFail();
$data = array(
'client' => null,
'invoice' => null,
'invoices' => Invoice::scope()->orderBy('invoice_number')->get(array('public_id','invoice_number')),
'payment' => $payment,
'method' => 'PUT',
'url' => 'payments/' . $publicId,
'title' => '- Edit Payment');
'title' => '- Edit Payment',
'clients' => Client::scope()->orderBy('name')->get());
return View::make('payments.edit', $data);
}
public function archive($publicId)
public function store()
{
$payment = Payment::scope($publicId)->firstOrFail();
$payment->delete();
Session::flash('message', 'Successfully archived payment');
return Redirect::to('payments');
return $this->save();
}
public function delete($publicId)
public function update($publicId)
{
$payment = Payment::scope($publicId)->firstOrFail();
$payment->forceDelete();
Session::flash('message', 'Successfully deleted payment');
return Redirect::to('payments');
return $this->save($publicId);
}
private function save($publicId = null)
{
$rules = array(
'client' => 'required',
'amount' => 'required'
);
$validator = Validator::make(Input::all(), $rules);
if ($validator->fails()) {
$url = $publicId ? 'payments/' . $publicId . '/edit' : 'payments/create';
return Redirect::to($url)
->withErrors($validator)
->withInput();
} else {
if ($publicId) {
$payment = Payment::scope($publicId)->firstOrFail();
} else {
$payment = Payment::createNew();
}
$invoiceId = Input::get('invoice') && Input::get('invoice') != "-1" ? Input::get('invoice') : null;
$payment->client_id = Input::get('client');
$payment->invoice_id = $invoiceId;
$payment->payment_date = toSqlDate(Input::get('payment_date'));
$payment->amount = Input::get('amount');
$payment->save();
$message = $publicId ? 'Successfully updated payment' : 'Successfully created payment';
Session::flash('message', $message);
return Redirect::to('clients/' . $payment->client_id);
}
}
public function bulk()
{
$action = Input::get('action');
$ids = Input::get('ids');
$payments = Payment::scope($ids)->get();
foreach ($payments as $payment) {
if ($action == 'archive') {
$payment->delete();
} else if ($action == 'delete') {
$payment->forceDelete();
}
}
$message = pluralize('Successfully '.$action.'d ? payment', count($ids));
Session::flash('message', $message);
return Redirect::to('payments');
}
}

View File

@ -11,6 +11,15 @@
class UserController extends BaseController {
public function setTheme()
{
$user = User::find(Auth::user()->id);
$user->theme_id = Input::get('theme_id');
$user->save();
return Redirect::to(Input::get('path'));
}
/**
* Displays the form for account creation
*

View File

@ -10,6 +10,7 @@ class ConfideSetupUsersTable extends Migration {
*/
public function up()
{
Schema::dropIfExists('themes');
Schema::dropIfExists('credits');
Schema::dropIfExists('activities');
Schema::dropIfExists('invitations');
@ -47,6 +48,12 @@ class ConfideSetupUsersTable extends Migration {
$table->boolean('eea')->default(0);
});
Schema::create('themes', function($t)
{
$t->increments('id');
$t->string('name');
});
Schema::create('timezones', function($t)
{
$t->increments('id');
@ -121,7 +128,8 @@ class ConfideSetupUsersTable extends Migration {
$t->string('confirmation_code');
$t->boolean('registered')->default(false);
$t->boolean('confirmed')->default(false);
$t->integer('theme_id');
$t->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
$t->unsignedInteger('public_id');
@ -170,6 +178,7 @@ class ConfideSetupUsersTable extends Migration {
$t->timestamps();
$t->softDeletes();
$t->boolean('is_primary');
$t->string('first_name');
$t->string('last_name');
$t->string('email');
@ -204,6 +213,9 @@ class ConfideSetupUsersTable extends Migration {
$t->date('due_date');
$t->text('notes');
$t->decimal('total', 10, 2);
$t->decimal('balance', 10, 2);
$t->foreign('client_id')->references('id')->on('clients')->onDelete('cascade');
$t->foreign('account_id')->references('id')->on('accounts');
$t->foreign('invoice_status_id')->references('id')->on('invoice_statuses');
@ -277,7 +289,7 @@ class ConfideSetupUsersTable extends Migration {
Schema::create('payments', function($t)
{
$t->increments('id');
$t->unsignedInteger('invoice_id');
$t->unsignedInteger('invoice_id')->nullable();
$t->unsignedInteger('account_id');
$t->unsignedInteger('client_id');
$t->unsignedInteger('contact_id')->nullable();
@ -352,6 +364,7 @@ class ConfideSetupUsersTable extends Migration {
*/
public function down()
{
Schema::dropIfExists('themes');
Schema::dropIfExists('credits');
Schema::dropIfExists('activities');
Schema::dropIfExists('invitations');

View File

@ -31,6 +31,19 @@ class ConstantsSeeder extends Seeder
$client->invoices()->save($invoice);
*/
Theme::create(array('name' => 'amelia'));
Theme::create(array('name' => 'cerulean'));
Theme::create(array('name' => 'cosmo'));
Theme::create(array('name' => 'cyborg'));
Theme::create(array('name' => 'flatly'));
Theme::create(array('name' => 'journal'));
Theme::create(array('name' => 'readable'));
Theme::create(array('name' => 'simplex'));
Theme::create(array('name' => 'slate'));
Theme::create(array('name' => 'spacelab'));
Theme::create(array('name' => 'united'));
Theme::create(array('name' => 'yeti'));
InvoiceStatus::create(array('name' => 'Draft'));
InvoiceStatus::create(array('name' => 'Sent'));
InvoiceStatus::create(array('name' => 'Viewed'));

View File

@ -100,12 +100,27 @@ class Activity extends Eloquent
}
$activity->payment_id = $payment->id;
$activity->invoice_id = $payment->invoice_id;
$activity->client_id = $payment->invoice->client_id;
if ($payment->invoice_id) {
$activity->invoice_id = $payment->invoice_id;
}
$activity->client_id = $payment->client_id;
$activity->activity_type_id = ACTIVITY_TYPE_CREATE_PAYMENT;
$activity->save();
}
public static function createCredit($credit)
{
$activity = Activity::getBlank();
$activity->message = Auth::user()->getFullName() . ' created credit';
$activity->credit_id = $credit->id;
$activity->client_id = $credit->client_id;
$activity->activity_type_id = ACTIVITY_TYPE_CREATE_CREDIT;
$activity->save();
}
public static function archivePayment($payment)
{
$activity = Activity::getBlank();

View File

@ -2,7 +2,7 @@
class Client extends EntityModel
{
protected $hidden = array('id', 'created_at', 'updated_at', 'deleted_at', 'notes', 'last_login');
protected $hidden = array('id', 'account_id', 'created_at', 'updated_at', 'deleted_at', 'notes', 'last_login');
public static $fieldName = 'Client - Name';
public static $fieldPhone = 'Client - Phone';

View File

@ -19,6 +19,7 @@ class Contact extends EntityModel
return PERSON_CONTACT;
}
/*
public function getLastLogin()
{
if ($this->last_login == '0000-00-00 00:00:00')
@ -30,7 +31,8 @@ class Contact extends EntityModel
return $this->last_login->format('m/d/y h:i a');
}
}
*/
public function getFullName()
{
$fullName = $this->first_name . ' ' . $this->last_name;

View File

@ -7,6 +7,11 @@ class Credit extends EntityModel
return $this->belongsTo('Invoice');
}
public function client()
{
return $this->belongsTo('Client');
}
public function getName()
{
return $this->credit_number;
@ -20,5 +25,5 @@ class Credit extends EntityModel
Credit::created(function($credit)
{
Activity::creaateCredit($credit);
Activity::createCredit($credit);
});

View File

@ -2,7 +2,7 @@
class Invoice extends EntityModel
{
protected $hidden = array('id', 'created_at', 'updated_at', 'deleted_at', 'viewed_date');
protected $hidden = array('id', 'account_id', 'client_id', 'created_at', 'updated_at', 'deleted_at', 'viewed_date');
public function account()
{
@ -34,6 +34,7 @@ class Invoice extends EntityModel
return ENTITY_INVOICE;
}
/*
public function getTotal()
{
$total = 0;
@ -45,6 +46,7 @@ class Invoice extends EntityModel
return $total;
}
*/
}
Invoice::created(function($invoice)

View File

@ -7,6 +7,11 @@ class Payment extends EntityModel
return $this->belongsTo('Invoice');
}
public function client()
{
return $this->belongsTo('Client');
}
public function getName()
{
return '';

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

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

View File

@ -31,6 +31,11 @@ class User extends ConfideUser implements UserInterface, RemindableInterface, iP
return $this->belongsTo('Account');
}
public function theme()
{
return $this->belongsTo('Theme');
}
public function getPersonType()
{
return PERSON_USER;

View File

@ -36,31 +36,25 @@ Route::group(array('before' => 'auth'), function()
Route::get('home', function() { return View::make('header'); });
Route::get('account/{section?}', 'AccountController@showSection');
Route::post('account/{section?}', 'AccountController@doSection');
Route::post('user/setTheme', 'UserController@setTheme');
Route::resource('clients', 'ClientController');
Route::get('api/clients', array('as'=>'api.clients', 'uses'=>'ClientController@getDatatable'));
Route::get('api/activities/{client_id?}', array('as'=>'api.activities', 'uses'=>'ActivityController@getDatatable'));
Route::post('clients/bulk', 'ClientController@bulk');
Route::get('clients/{client_id}/archive', 'ClientController@archive');
Route::get('clients/{client_id}/delete', 'ClientController@delete');
Route::resource('invoices', 'InvoiceController');
Route::get('api/invoices/{client_id?}', array('as'=>'api.invoices', 'uses'=>'InvoiceController@getDatatable'));
Route::get('invoices/create/{client_id}', 'InvoiceController@create');
Route::post('invoices/bulk', 'InvoiceController@bulk');
Route::get('invoices/{client_id}/archive', 'InvoiceController@archive');
Route::get('invoices/{client_id}/delete', 'InvoiceController@delete');
Route::resource('payments', 'PaymentController');
Route::get('api/payments/{client_id?}', array('as'=>'api.payments', 'uses'=>'PaymentController@getDatatable'));
Route::post('payments/bulk', 'PaymentController@bulk');
Route::get('payments/{client_id}/archive', 'PaymentController@archive');
Route::get('payments/{client_id}/delete', 'PaymentController@delete');
Route::resource('credits', 'CreditController');
Route::get('api/credits/{client_id?}', array('as'=>'api.credits', 'uses'=>'CreditController@getDatatable'));
Route::get('credits/{client_id}/archive', 'CreditController@archive');
Route::get('credits/{client_id}/delete', 'CreditController@delete');
Route::post('credits/bulk', 'PaymentController@bulk');
Route::get('reports', function() { return View::make('header'); });
});
@ -132,9 +126,26 @@ function timestampToDateTimeString($timestamp) {
}
$date = new Carbon($timestamp);
$date->tz = $tz;
return $date->toDayDateTimeString();
if ($date->year < 1900) {
return '';
}
return $date->toFormattedDateTimeString();
}
function timestampToDateString($timestamp) {
$tz = Session::get('tz');
if (!$tz) {
$tz = 'US/Eastern';
}
$date = new Carbon($timestamp);
$date->tz = $tz;
if ($date->year < 1900) {
return '';
}
return $date->toFormattedDateString();
}
function toDateString($date)
{
if ($date->year < 1900) {
@ -174,6 +185,16 @@ function fromSqlDate($date)
return DateTime::createFromFormat('Y-m-d', $date)->format('m/d/Y');
}
function fromSqlTimestamp($date)
{
if (!$date || $date == '0000-00-00 00:00:00')
{
return '';
}
return DateTime::createFromFormat('Y-m-d H:i:s', $date)->format('m/d/Y h:ia');
}
function processedRequest($url)
{
//Session::put(Input::get('_token'), $url);

View File

@ -45,7 +45,7 @@
<div data-bind='template: { foreach: contacts,
beforeRemove: hideContact,
afterAdd: showContact }'>
{{ Former::hidden('id')->data_bind("value: id, valueUpdate: 'afterkeydown'") }}
{{ Former::hidden('public_id')->data_bind("value: public_id, valueUpdate: 'afterkeydown'") }}
{{ Former::text('first_name')->data_bind("value: first_name, valueUpdate: 'afterkeydown'") }}
{{ Former::text('last_name')->data_bind("value: last_name, valueUpdate: 'afterkeydown'") }}
{{ Former::text('email')->data_bind("value: email, valueUpdate: 'afterkeydown'") }}
@ -85,7 +85,7 @@
function ContactModel() {
var self = this;
self.id = ko.observable('');
self.public_id = ko.observable('');
self.first_name = ko.observable('');
self.last_name = ko.observable('');
self.email = ko.observable('');

View File

@ -90,7 +90,7 @@
<div class="tab-pane" id="payments">
{{ Datatable::table()
->addColumn('Invoice Number', 'Amount', 'Date')
->addColumn('Transaction Reference', 'Invoice', 'Amount', 'Payment Date')
->setUrl(url('api/payments/' . $client->public_id))
->setOptions('sPaginationType', 'bootstrap')
->setOptions('bFilter', false)
@ -100,7 +100,7 @@
<div class="tab-pane" id="credits">
{{ Datatable::table()
->addColumn('Credit Number', 'Amount', 'Credit Date')
->addColumn('Amount', 'Credit Date')
->setUrl(url('api/credits/' . $client->public_id))
->setOptions('sPaginationType', 'bootstrap')
->setOptions('bFilter', false)

View File

@ -0,0 +1,62 @@
@extends('header')
@section('onReady')
$('input#name').focus();
@stop
@section('content')
{{ Former::open($url)->addClass('col-md-10 col-md-offset-1 main_form')->method($method)->rules(array(
'amount' => 'required'
)); }}
@if ($credit)
{{ Former::populate($credit) }}
@endif
<div class="row">
<div class="col-md-8">
@if ($credit)
{{ Former::legend('Edit Credit') }}
@else
{{ Former::legend('New Credit') }}
@endif
{{ Former::select('client')->fromQuery($clients, 'name', 'public_id')->select($client ? $client->public_id : '')->addOption('', '')->addGroupClass('client-select') }}
{{ Former::text('amount') }}
{{ Former::text('credit_date') }}
</div>
<div class="col-md-6">
</div>
</div>
<center style="margin-top:16px">
{{ Button::lg_primary_submit('Save') }} &nbsp;|&nbsp;
{{ link_to('credits/' . ($credit ? $credit->public_id : ''), 'Cancel') }}
</center>
{{ Former::close() }}
<script type="text/javascript">
$(function() {
var $input = $('select#client');
$input.combobox();
$('#credit_date').datepicker({
autoclose: true,
todayHighlight: true
});
});
</script>
@stop

View File

@ -1,4 +1,4 @@
<table class="table {{ $class = str_random(8) }}">
<table class="table table-striped {{ $class = str_random(8) }}">
<colgroup>
@for ($i = 0; $i < count($columns); $i++)
<col class="con{{ $i }}" />

View File

@ -23,8 +23,11 @@
<script src="{{ asset('js/typeahead.js') }}" type="text/javascript"></script>
<link rel="stylesheet" type="text/css" href="{{ asset('css/typeahead.js-bootstrap.css') }}"/>
-->
<link rel="stylesheet" type="text/css" href="{{ asset('css/bootstrap.css') }}"/>
@if (Auth::check() && Auth::user()->theme_id)
<link rel="stylesheet" type="text/css" href="{{ asset('css/themes/'.Auth::user()->theme->name.'.min.css') }}"/>
@else
<link rel="stylesheet" type="text/css" href="{{ asset('css/bootstrap.css') }}"/>
@endif
<script src="{{ asset('js/bootstrap.js') }}" type="text/javascript"></script>
<!-- <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap-theme.min.css"> -->
@ -107,6 +110,7 @@
margin-top: -30px;
}
/*
table.table tbody tr.odd {
background-color: #f9f9f9;
}
@ -114,9 +118,9 @@
table.table tbody tr:hover {
background-color: #f0f0f0 !important;
}
*/
/*
/* table sorting indicators */
table.table thead .sorting,
table.table thead .sorting_asc,
table.table thead .sorting_desc,
@ -126,14 +130,15 @@
*cursor: hand;
}
table.table thead .sorting { background: url('images/sort_both.png') no-repeat center right; }
/*table.table thead .sorting { background: url('images/sort_both.png') no-repeat center right; }*/
table.table thead .sorting_asc { background: url('images/sort_asc.png') no-repeat center right; }
table.table thead .sorting_desc { background: url('images/sort_desc.png') no-repeat center right; }
table.table thead .sorting_asc_disabled { background: url('images/sort_asc_disabled.png') no-repeat center right; }
table.table thead .sorting_desc_disabled { background: url('images/sort_desc_disabled.png') no-repeat center right; }
*/
/* Hover nav */
.sidebar-nav {
@ -310,9 +315,30 @@
</div>
<div class="container">
<div class="footer">
Powered by {{ link_to('https://github.com/hillelcoren/invoice-ninja', 'InvoiceNinja', array('target'=>'_blank')) }}
<div class="pull-right">
{{ Former::open('user/setTheme')->addClass('themeForm') }}
<div style="display:none">
{{ Former::text('theme_id') }}
{{ Former::text('path')->value(Request::url()) }}
</div>
<div class="btn-group tr-action dropup">
<button type="button" class="btn btn-xs btn-default dropdown-toggle" data-toggle="dropdown">
Theme <span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu">
<li><a href="#" onclick="setTheme(0)">Default</a></li>
@foreach (Theme::all() as $theme)
<li><a href="#" onclick="setTheme({{ $theme->id }})">{{ ucwords($theme->name) }}</a></li>
@endforeach
</ul>
</div>
{{ Former::close() }}
</div>
Powered by {{ link_to('https://github.com/hillelcoren/invoice-ninja', 'InvoiceNinja', array('target'=>'_blank')) }}
<p class="text-danger">This is a demo site, the data is erased.</p>
</div>
</div>
</div>
</div>
@ -384,6 +410,12 @@
<script type="text/javascript">
function setTheme(id)
{
$('#theme_id').val(id);
$('form.themeForm').submit();
}
@if (!Auth::check() || !Auth::user()->registered)
function validateSignUp(showError)
{

View File

@ -207,30 +207,8 @@
$input.combobox();
$('.combobox-container input.form-control').attr('name', 'client_combobox').on('change', function(e) {
refreshPDF();
}).mouseleave(function() {
$(this).css('text-decoration','none');
}).on('change keyup mouseenter', function(e) {
if ($(this).closest('.combobox-container').hasClass('combobox-selected')) {
$(this).css('text-decoration','underline');
$(this).css('cursor','pointer');
} else {
$(this).css('text-decoration','none');
$(this).css('cursor','text');
}
}).on('focusout mouseleave', function(e) {
$(this).css('text-decoration','none');
$(this).css('cursor','text');
}).on('click', function() {
var clientId = $('.combobox-container input[name=client]').val();
if ($(this).closest('.combobox-container').hasClass('combobox-selected')) {
if (parseInt(clientId) > 0) {
window.open('{{ URL::to('clients') }}' + '/' + clientId, '_blank');
} else {
$('#myModal').modal('show');
}
};
});
enableHoverClick($('.combobox-container input.form-control'), $('.combobox-container input[name=client]'), '{{ URL::to('clients') }}');
@if ($client)
$('input#invoice_number').focus();

View File

@ -3,7 +3,10 @@
@section('content')
{{ Former::open($entityType . 's/bulk')->addClass('listForm') }}
<div style="display:none">{{ Former::text('action') }}</div>
<div style="display:none">
{{ Former::text('action') }}
{{ Former::text('id') }}
</div>
{{ DropdownButton::normal('Archive',
Navigation::links(
@ -35,17 +38,23 @@
if (!confirm('Are you sure')) {
return;
}
}
}
$('#action').val(action);
$('form.listForm').submit();
}
function deleteEntity(id) {
if (confirm("Are you sure?")) {
window.location = "{{ URL::to($entityType . 's') }}/" + id + "/delete";
$('#id').val(id);
submitForm('delete');
}
}
function archiveEntity(id) {
$('#id').val(id);
submitForm('archive');
}
</script>
@stop
@ -67,12 +76,12 @@
});
$('tbody tr').mouseover(function() {
$(this).closest('tr').find('.tr-action').show();
$(this).closest('tr').find('.tr-action').css('visibility','visible');
}).mouseout(function() {
$dropdown = $(this).closest('tr').find('.tr-action');
if (!$dropdown.hasClass('open')) {
$dropdown.hide();
}
$dropdown.css('visibility','hidden');
}
});
}

View File

@ -9,17 +9,16 @@
{{ Former::open($url)->addClass('col-md-10 col-md-offset-1 main_form')->method($method)->rules(array(
'name' => 'required',
'email' => 'email'
'amount' => 'required'
)); }}
@if ($payment)
{{ Former::populate($payment) }}
{{-- Former::populate($payment) --}}
@endif
<div class="row">
<div class="col-md-6">
<div class="col-md-8">
@if ($payment)
{{ Former::legend('Edit Payment') }}
@ -27,9 +26,10 @@
{{ Former::legend('New Payment') }}
@endif
{{ Former::text('name') }}
{{ Former::text('work_phone')->label('Phone') }}
{{ Former::textarea('notes') }}
{{ Former::select('client')->addOption('', '')->addGroupClass('client-select') }}
{{ Former::select('invoice')->addOption('', '')->addGroupClass('invoice-select') }}
{{ Former::text('amount') }}
{{ Former::text('payment_date') }}
</div>
<div class="col-md-6">
@ -44,4 +44,90 @@
{{ Former::close() }}
@stop
<script type="text/javascript">
var invoices = {{ $invoices }};
var clients = {{ $clients }};
var clientMap = {};
var invoiceMap = {};
/*
function compareClient(a,b) {
if (a.name < b.name)
return -1;
if (a.name> b.name)
return 1;
return 0;
}
*/
$(function() {
var $input = $('select#client');
for (var i=0; i<invoices.length; i++) {
var invoice = invoices[i];
var client = invoice.client;
if (!invoiceMap.hasOwnProperty(client.public_id)) {
invoiceMap[client.public_id] = [];
}
invoiceMap[client.public_id].push(invoice);
clientMap[invoice.public_id] = invoice.client;
}
//clients.sort(compareClient);
$input.append(new Option('', ''));
for (var i=0; i<clients.length; i++) {
var client = clients[i];
$input.append(new Option(client.name, client.public_id));
}
$input.on('change', function(e) {
console.log('client change');
var clientId = $('input[name=client]').val();
var invoiceId = $('input[name=invoice]').val();
if (clientMap.hasOwnProperty(invoiceId) && clientMap[invoiceId].public_id == clientId) {
console.log('values the same:' + $('select#client').prop('selected'))
e.preventDefault();
return;
}
setComboboxValue($('.invoice-select'), '', '');
$invoiceCombobox = $('select#invoice');
$invoiceCombobox.find('option').remove().end().combobox('refresh');
$invoiceCombobox.append(new Option('', ''));
var list = clientId ? (invoiceMap.hasOwnProperty(clientId) ? invoiceMap[clientId] : []) : invoices;
for (var i=0; i<list.length; i++) {
var invoice = list[i];
$invoiceCombobox.append(new Option(invoice.invoice_number + ' - ' + invoice.invoice_date + ' - ' + invoice.client.name, invoice.public_id));
}
$('select#invoice').combobox('refresh');
}).trigger('change');
//enableHoverClick($('.client-select input.form-control'), $('input[name=client]'), '{{ URL::to('clients') }}');
$input.combobox();
var $input = $('select#invoice').on('change', function(e) {
console.log('invoice change');
$clientCombobox = $('select#client');
var invoiceId = $('input[name=invoice]').val();
if (invoiceId) {
var client = clientMap[invoiceId];
setComboboxValue($('.client-select'), client.public_id, client.name);
//setComboboxValue($('select#client'), client.public_id, client.name);
}
});
//enableHoverClick($('.invoice-select input.form-control'), $('input[name=invoice]'), '{{ URL::to('invoices') }}');
$input.combobox();
$('#payment_date').datepicker({
autoclose: true,
todayHighlight: true
});
});
</script>
@stop

1
public/css/bootstrap.space.css vendored Executable file

File diff suppressed because one or more lines are too long

1
public/css/themes/amelia.min.css vendored Executable file

File diff suppressed because one or more lines are too long

1
public/css/themes/cerulean.min.css vendored Executable file

File diff suppressed because one or more lines are too long

1
public/css/themes/cosmo.min.css vendored Executable file

File diff suppressed because one or more lines are too long

1
public/css/themes/cyborg.min.css vendored Executable file

File diff suppressed because one or more lines are too long

1
public/css/themes/flatly.min.css vendored Executable file

File diff suppressed because one or more lines are too long

1
public/css/themes/journal.min.css vendored Executable file

File diff suppressed because one or more lines are too long

1
public/css/themes/readable.min.css vendored Executable file

File diff suppressed because one or more lines are too long

1
public/css/themes/simplex.min.css vendored Executable file

File diff suppressed because one or more lines are too long

1
public/css/themes/slate.min.css vendored Executable file

File diff suppressed because one or more lines are too long

1
public/css/themes/spacelab.min.css vendored Executable file

File diff suppressed because one or more lines are too long

1
public/css/themes/united.min.css vendored Executable file

File diff suppressed because one or more lines are too long

1
public/css/themes/yeti.min.css vendored Executable file

File diff suppressed because one or more lines are too long

View File

@ -379,3 +379,49 @@ $(function() {
});
});
function enableHoverClick($combobox, $entityId, url) {
/*
$combobox.mouseleave(function() {
$combobox.css('text-decoration','none');
}).on('mouseenter', function(e) {
setAsLink($combobox, $combobox.closest('.combobox-container').hasClass('combobox-selected'));
}).on('focusout mouseleave', function(e) {
setAsLink($combobox, false);
}).on('click', function() {
var clientId = $entityId.val();
if ($(combobox).closest('.combobox-container').hasClass('combobox-selected')) {
if (parseInt(clientId) > 0) {
window.open(url + '/' + clientId, '_blank');
} else {
$('#myModal').modal('show');
}
};
});
*/
}
function setAsLink($input, enable) {
if (enable) {
$input.css('text-decoration','underline');
$input.css('cursor','pointer');
} else {
$input.css('text-decoration','none');
$input.css('cursor','text');
}
}
function setComboboxValue($combobox, id, name) {
console.log('id: ' + id);
$combobox.find('input').val(id);
$combobox.find('input.form-control').val(name);
if (id && name) {
//console.log('%s %s', $combobox.find('select')[0], id);
//$combobox.find('select').val(id).prop('selected', 'selected');
$combobox.find('.combobox-container').addClass('combobox-selected');
} else {
//$combobox.find('select').val('');
$combobox.find('.combobox-container').removeClass('combobox-selected');
}
//console.log($combobox).combobox('');
}