mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Working on d3.js vizualizer
This commit is contained in:
parent
b3a0b6f83d
commit
020158a0ae
88
app/commands/CreateRandomData.php
Normal file
88
app/commands/CreateRandomData.php
Normal file
@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
|
||||
class CreateRandomData extends Command {
|
||||
|
||||
protected $name = 'ninja:create-data';
|
||||
protected $description = 'Create random data';
|
||||
|
||||
public function fire()
|
||||
{
|
||||
$this->info(date('Y-m-d') . ' Running CreateRandomData...');
|
||||
|
||||
$user = User::first();
|
||||
|
||||
if (!$user) {
|
||||
$this->error("Error: please create user account by logging in");
|
||||
return;
|
||||
}
|
||||
|
||||
$productNames = ['Arkansas', 'New York', 'Arizona', 'California', 'Colorado', 'Alabama', 'Connecticut', 'Delaware', 'Florida', 'Georgia', 'Hawaii', 'Idaho', 'Illinois', 'Indiana', 'Iowa', 'Kansas', 'Kentucky', 'Louisiana', 'Maine', 'Maryland', 'Massachusetts', 'Michigan', 'Minnesota', 'Mississippi', 'Missouri', 'Montana', 'Nebraska', 'Nevada', 'New Hampshire', 'New Jersey', 'New Mexico', 'Alaska', 'North Carolina', 'North Dakota', 'Ohio', 'Oklahoma', 'Oregon', 'Pennsylvania', 'Rhode Island', 'South Carolina', 'South Dakota', 'Tennessee', 'Texas', 'Utah', 'Vermont', 'Virginia', 'Washington', 'West Virginia', 'Wisconsin', 'Wyoming'];
|
||||
$clientNames = ['IBM', 'Nestle', 'Mitsubishi UFJ Financial', 'Vodafone', 'Eni', 'Procter & Gamble', 'Johnson & Johnson', 'American International Group', 'Banco Santander', 'BHP Billiton', 'Pfizer', 'Itaú Unibanco Holding', 'Ford Motor', 'BMW Group', 'Commonwealth Bank', 'EDF', 'Statoil', 'Google', 'Siemens', 'Novartis', 'Royal Bank of Canada', 'Sumitomo Mitsui Financial', 'Comcast', 'Sberbank', 'Goldman Sachs Group', 'Westpac Banking Group', 'Nippon Telegraph & Tel', 'Ping An Insurance Group', 'Banco Bradesco', 'Anheuser-Busch InBev', 'Bank of Communications', 'China Life Insurance', 'General Motors', 'Telefónica', 'MetLife', 'Honda Motor', 'Enel', 'BASF', 'Softbank', 'National Australia Bank', 'ANZ', 'ConocoPhillips', 'TD Bank Group', 'Intel', 'UBS', 'Hewlett-Packard', 'Coca-Cola', 'Cisco Systems', 'UnitedHealth Group', 'Boeing', 'Zurich Insurance Group', 'Hyundai Motor', 'Sanofi', 'Credit Agricole', 'United Technologies', 'Roche Holding', 'Munich Re', 'PepsiCo', 'Oracle', 'Bank of Nova Scotia'];
|
||||
|
||||
for ($i=1; $i<=40; $i++) {
|
||||
$product = Product::createNew($user);
|
||||
$product->id = $i;
|
||||
$product->product_key = $productNames[$i-1];
|
||||
$product->save();
|
||||
}
|
||||
|
||||
for ($i=0; $i<60; $i++) {
|
||||
$client = Client::createNew($user);
|
||||
$client->name = $clientNames[$i];
|
||||
$client->save();
|
||||
|
||||
$contact = Contact::createNew($user);
|
||||
$contact->email = "client@aol.com";
|
||||
$contact->is_primary = 1;
|
||||
$client->contacts()->save($contact);
|
||||
|
||||
$numInvoices = rand(1, 25);
|
||||
if ($numInvoices == 4 || $numInvoices == 10 || $numInvoices == 25) {
|
||||
// leave these
|
||||
} else if ($numInvoices % 3 == 0) {
|
||||
$numInvoices = 1;
|
||||
} else if ($numInvoices > 10) {
|
||||
$numInvoices = $numInvoices % 2;
|
||||
}
|
||||
|
||||
$paidUp = rand(0, 1) == 1;
|
||||
|
||||
for ($j=1; $j<=$numInvoices; $j++) {
|
||||
|
||||
$price = rand(10, 1000);
|
||||
if ($price < 900) {
|
||||
$price = rand(10, 150);
|
||||
}
|
||||
|
||||
$invoice = Invoice::createNew($user);
|
||||
$invoice->invoice_number = $user->account->getNextInvoiceNumber();
|
||||
$invoice->amount = $invoice->balance = $price;
|
||||
$invoice->created_at = date('Y-m-d', strtotime(date("Y-m-d") . ' - ' . rand(1, 100) . ' days'));
|
||||
$client->invoices()->save($invoice);
|
||||
|
||||
$productId = rand(0, 40);
|
||||
if ($productId > 20) {
|
||||
$productId = ($productId % 2) + rand(0, 2);
|
||||
}
|
||||
|
||||
$invoiceItem = InvoiceItem::createNew($user);
|
||||
$invoiceItem->product_id = $productId+1;
|
||||
$invoiceItem->product_key = $productNames[$invoiceItem->product_id];
|
||||
$invoiceItem->cost = $invoice->amount;
|
||||
$invoiceItem->qty = 1;
|
||||
$invoice->invoice_items()->save($invoiceItem);
|
||||
|
||||
if ($paidUp || rand(0,2) > 1) {
|
||||
$payment = Payment::createNew($user);
|
||||
$payment->invoice_id = $invoice->id;
|
||||
$payment->amount = $invoice->amount;
|
||||
$client->payments()->save($payment);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
24
app/commands/ResetData.php
Normal file
24
app/commands/ResetData.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
|
||||
class ResetData extends Command {
|
||||
|
||||
protected $name = 'ninja:reset-data';
|
||||
protected $description = 'Reset data';
|
||||
|
||||
public function fire()
|
||||
{
|
||||
$this->info(date('Y-m-d') . ' Running ResetData...');
|
||||
|
||||
if (!Utils::isNinjaDev()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Artisan::call('migrate:reset');
|
||||
Artisan::call('migrate');
|
||||
Artisan::call('db:seed');
|
||||
}
|
||||
}
|
@ -46,9 +46,9 @@ class AccountController extends \BaseController {
|
||||
return Redirect::to('/');
|
||||
}
|
||||
|
||||
/*
|
||||
public function reset()
|
||||
{
|
||||
/*
|
||||
if (Utils::isNinjaDev()) {
|
||||
Confide::logout();
|
||||
try {
|
||||
@ -59,10 +59,11 @@ class AccountController extends \BaseController {
|
||||
Response::make($e->getMessage(), 500);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
return Redirect::to('/');
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
public function getStarted()
|
||||
{
|
||||
if (Auth::check())
|
||||
|
@ -2,6 +2,23 @@
|
||||
|
||||
class ReportController extends \BaseController {
|
||||
|
||||
public function d3()
|
||||
{
|
||||
$account = Auth::user()->account;
|
||||
$account = $account->with(['clients.invoices.invoice_items', 'clients.contacts'])->first();
|
||||
|
||||
$account = $account->hideFieldsForViz();
|
||||
$clients = $account->clients;
|
||||
//dd($clients->toJson());
|
||||
|
||||
$data = [
|
||||
'feature' => ACCOUNT_DATA_VISUALIZER,
|
||||
'clients' => $clients
|
||||
];
|
||||
|
||||
return View::make('reports.d3', $data);
|
||||
}
|
||||
|
||||
public function report()
|
||||
{
|
||||
if (Input::all())
|
||||
|
@ -333,6 +333,13 @@ return array(
|
||||
'created_product' => 'Produkt erfolgreich erstellt',
|
||||
'archived_product' => 'Produkt erfolgreich archiviert',
|
||||
'product_library' => 'Produktbibliothek',
|
||||
'pro_plan_custom_fields' => ':link to enable custom fields by joining the Pro Plan',
|
||||
|
||||
'advanced_settings' => 'Advanced Settings',
|
||||
'pro_plan_advanced_settings' => ':link to enable the advanced settings by joining the Pro Plan',
|
||||
'invoice_design' => 'Invoice Design',
|
||||
'specify_colors' => 'Specify colors',
|
||||
'specify_colors_label' => 'Select the colors used in the invoice',
|
||||
|
||||
'chart_builder' => 'Diagrammersteller',
|
||||
'ninja_email_footer' => 'Nutze :site um Kunden eine Rechnung zu stellen und online bezahlt zu werden, kostenlos!',
|
||||
@ -407,10 +414,12 @@ return array(
|
||||
|
||||
'confirm_email_invoice' => 'Are you sure you want to email this invoice?',
|
||||
'confirm_email_quote' => 'Are you sure you want to email this quote?',
|
||||
'confirm_recurring_email_invoice' => 'Are you sure you want this invoice emailed?',
|
||||
'confirm_recurring_email_invoice' => 'Recurring is enabled, are you sure you want this invoice emailed?',
|
||||
|
||||
'cancel_account' => 'Cancel Account',
|
||||
'cancel_account_message' => 'Warning: This will permanently erase all of your data, there is no undo.',
|
||||
'go_back' => 'Go Back',
|
||||
|
||||
'data_visualizer' => 'Data Visualizer',
|
||||
|
||||
);
|
||||
|
@ -422,10 +422,12 @@ return array(
|
||||
|
||||
'confirm_email_invoice' => 'Are you sure you want to email this invoice?',
|
||||
'confirm_email_quote' => 'Are you sure you want to email this quote?',
|
||||
'confirm_recurring_email_invoice' => 'Are you sure you want this invoice emailed?',
|
||||
'confirm_recurring_email_invoice' => 'Recurring is enabled,re you sure you want this invoice emailed?',
|
||||
|
||||
'cancel_account' => 'Cancel Account',
|
||||
'cancel_account_message' => 'Warning: This will permanently erase all of your data, there is no undo.',
|
||||
'go_back' => 'Go Back',
|
||||
|
||||
'data_visualizer' => 'Data Visualizer',
|
||||
|
||||
);
|
@ -332,6 +332,13 @@ return array(
|
||||
'updated_product' => 'Successfully updated product',
|
||||
'created_product' => 'Successfully created product',
|
||||
'archived_product' => 'Successfully archived product',
|
||||
'pro_plan_custom_fields' => ':link to enable custom fields by joining the Pro Plan',
|
||||
|
||||
'advanced_settings' => 'Advanced Settings',
|
||||
'pro_plan_advanced_settings' => ':link to enable the advanced settings by joining the Pro Plan',
|
||||
'invoice_design' => 'Invoice Design',
|
||||
'specify_colors' => 'Specify colors',
|
||||
'specify_colors_label' => 'Select the colors used in the invoice',
|
||||
|
||||
'chart_builder' => 'Chart Builder',
|
||||
'ninja_email_footer' => 'Use :site to invoice your clients and get paid online for free!',
|
||||
@ -405,10 +412,12 @@ return array(
|
||||
|
||||
'confirm_email_invoice' => 'Are you sure you want to email this invoice?',
|
||||
'confirm_email_quote' => 'Are you sure you want to email this quote?',
|
||||
'confirm_recurring_email_invoice' => 'Are you sure you want this invoice emailed?',
|
||||
'confirm_recurring_email_invoice' => 'Recurring is enabled, are you sure you want this invoice emailed?',
|
||||
|
||||
'cancel_account' => 'Cancel Account',
|
||||
'cancel_account_message' => 'Warning: This will permanently erase all of your data, there is no undo.',
|
||||
'go_back' => 'Go Back',
|
||||
|
||||
'data_visualizer' => 'Data Visualizer',
|
||||
|
||||
);
|
||||
|
@ -333,6 +333,13 @@ return array(
|
||||
'updated_product' => 'Successfully updated product',
|
||||
'created_product' => 'Successfully created product',
|
||||
'archived_product' => 'Successfully archived product',
|
||||
'pro_plan_custom_fields' => ':link to enable custom fields by joining the Pro Plan',
|
||||
|
||||
'advanced_settings' => 'Advanced Settings',
|
||||
'pro_plan_advanced_settings' => ':link to enable the advanced settings by joining the Pro Plan',
|
||||
'invoice_design' => 'Invoice Design',
|
||||
'specify_colors' => 'Specify colors',
|
||||
'specify_colors_label' => 'Select the colors used in the invoice',
|
||||
|
||||
'chart_builder' => 'Chart Builder',
|
||||
'ninja_email_footer' => 'Use :site to invoice your clients and get paid online for free!',
|
||||
@ -407,10 +414,12 @@ return array(
|
||||
|
||||
'confirm_email_invoice' => 'Are you sure you want to email this invoice?',
|
||||
'confirm_email_quote' => 'Are you sure you want to email this quote?',
|
||||
'confirm_recurring_email_invoice' => 'Are you sure you want this invoice emailed?',
|
||||
'confirm_recurring_email_invoice' => 'Recurring is enabled, are you sure you want this invoice emailed?',
|
||||
|
||||
'cancel_account' => 'Cancel Account',
|
||||
'cancel_account_message' => 'Warning: This will permanently erase all of your data, there is no undo.',
|
||||
'go_back' => 'Go Back',
|
||||
|
||||
'data_visualizer' => 'Data Visualizer',
|
||||
|
||||
);
|
||||
|
@ -333,6 +333,13 @@ return array(
|
||||
'updated_product' => 'Prodotto aggiornato con successo',
|
||||
'created_product' => 'Prodotto creato con successo',
|
||||
'archived_product' => 'Prodotto archiviato con successo',
|
||||
'pro_plan_custom_fields' => ':link to enable custom fields by joining the Pro Plan',
|
||||
|
||||
'advanced_settings' => 'Advanced Settings',
|
||||
'pro_plan_advanced_settings' => ':link to enable the advanced settings by joining the Pro Plan',
|
||||
'invoice_design' => 'Invoice Design',
|
||||
'specify_colors' => 'Specify colors',
|
||||
'specify_colors_label' => 'Select the colors used in the invoice',
|
||||
|
||||
'chart_builder' => 'Creatore grafico',
|
||||
'ninja_email_footer' => 'Usa :site per fatturare ai tuoi clienti e venire pagato online gratis!',
|
||||
@ -407,10 +414,11 @@ return array(
|
||||
|
||||
'confirm_email_invoice' => 'Are you sure you want to email this invoice?',
|
||||
'confirm_email_quote' => 'Are you sure you want to email this quote?',
|
||||
'confirm_recurring_email_invoice' => 'Are you sure you want this invoice emailed?',
|
||||
'confirm_recurring_email_invoice' => 'Recurring is enabled, are you sure you want this invoice emailed?',
|
||||
|
||||
'cancel_account' => 'Cancel Account',
|
||||
'cancel_account_message' => 'Warning: This will permanently erase all of your data, there is no undo.',
|
||||
'go_back' => 'Go Back',
|
||||
|
||||
|
||||
'data_visualizer' => 'Data Visualizer',
|
||||
);
|
||||
|
@ -422,11 +422,13 @@ return array(
|
||||
|
||||
'confirm_email_invoice' => 'Are you sure you want to email this invoice?',
|
||||
'confirm_email_quote' => 'Are you sure you want to email this quote?',
|
||||
'confirm_recurring_email_invoice' => 'Are you sure you want this invoice emailed?',
|
||||
'confirm_recurring_email_invoice' => 'Recurring is enabled, are you sure you want this invoice emailed?',
|
||||
|
||||
'cancel_account' => 'Cancel Account',
|
||||
'cancel_account_message' => 'Warning: This will permanently erase all of your data, there is no undo.',
|
||||
'go_back' => 'Go Back',
|
||||
|
||||
'data_visualizer' => 'Data Visualizer',
|
||||
|
||||
);
|
||||
|
||||
|
@ -422,10 +422,12 @@ return array(
|
||||
|
||||
'confirm_email_invoice' => 'Are you sure you want to email this invoice?',
|
||||
'confirm_email_quote' => 'Are you sure you want to email this quote?',
|
||||
'confirm_recurring_email_invoice' => 'Are you sure you want this invoice emailed?',
|
||||
'confirm_recurring_email_invoice' => 'Recurring is enabled, are you sure you want this invoice emailed?',
|
||||
|
||||
'cancel_account' => 'Cancel Account',
|
||||
'cancel_account_message' => 'Warning: This will permanently erase all of your data, there is no undo.',
|
||||
'go_back' => 'Go Back',
|
||||
|
||||
'data_visualizer' => 'Data Visualizer',
|
||||
|
||||
);
|
@ -334,6 +334,13 @@ return array(
|
||||
'updated_product' => 'Successfully updated product',
|
||||
'created_product' => 'Successfully created product',
|
||||
'archived_product' => 'Successfully archived product',
|
||||
'pro_plan_custom_fields' => ':link to enable custom fields by joining the Pro Plan',
|
||||
|
||||
'advanced_settings' => 'Advanced Settings',
|
||||
'pro_plan_advanced_settings' => ':link to enable the advanced settings by joining the Pro Plan',
|
||||
'invoice_design' => 'Invoice Design',
|
||||
'specify_colors' => 'Specify colors',
|
||||
'specify_colors_label' => 'Select the colors used in the invoice',
|
||||
|
||||
'chart_builder' => 'Chart Builder',
|
||||
'ninja_email_footer' => 'Use :site to invoice your clients and get paid online for free!',
|
||||
@ -408,10 +415,12 @@ return array(
|
||||
|
||||
'confirm_email_invoice' => 'Are you sure you want to email this invoice?',
|
||||
'confirm_email_quote' => 'Are you sure you want to email this quote?',
|
||||
'confirm_recurring_email_invoice' => 'Are you sure you want this invoice emailed?',
|
||||
|
||||
'confirm_recurring_email_invoice' => 'Recurring is enabled, are you sure you want this invoice emailed?',
|
||||
|
||||
'cancel_account' => 'Cancel Account',
|
||||
'cancel_account_message' => 'Warning: This will permanently erase all of your data, there is no undo.',
|
||||
'go_back' => 'Go Back',
|
||||
|
||||
'data_visualizer' => 'Data Visualizer',
|
||||
|
||||
);
|
||||
|
@ -322,6 +322,13 @@ return array(
|
||||
'updated_product' => 'Successfully updated product',
|
||||
'created_product' => 'Successfully created product',
|
||||
'archived_product' => 'Successfully archived product',
|
||||
'pro_plan_custom_fields' => ':link to enable custom fields by joining the Pro Plan',
|
||||
|
||||
'advanced_settings' => 'Advanced Settings',
|
||||
'pro_plan_advanced_settings' => ':link to enable the advanced settings by joining the Pro Plan',
|
||||
'invoice_design' => 'Invoice Design',
|
||||
'specify_colors' => 'Specify colors',
|
||||
'specify_colors_label' => 'Select the colors used in the invoice',
|
||||
|
||||
'chart_builder' => 'Chart Builder',
|
||||
'ninja_email_footer' => 'Use :site to invoice your clients and get paid online for free!',
|
||||
@ -396,10 +403,12 @@ return array(
|
||||
|
||||
'confirm_email_invoice' => 'Are you sure you want to email this invoice?',
|
||||
'confirm_email_quote' => 'Are you sure you want to email this quote?',
|
||||
'confirm_recurring_email_invoice' => 'Are you sure you want this invoice emailed?',
|
||||
|
||||
'confirm_recurring_email_invoice' => 'Recurring is enabled, are you sure you want this invoice emailed?',
|
||||
|
||||
'cancel_account' => 'Cancel Account',
|
||||
'cancel_account_message' => 'Warning: This will permanently erase all of your data, there is no undo.',
|
||||
'go_back' => 'Go Back',
|
||||
|
||||
'data_visualizer' => 'Data Visualizer',
|
||||
|
||||
);
|
||||
|
@ -252,4 +252,47 @@ class Account extends Eloquent
|
||||
{
|
||||
return Subscription::where('account_id', '=', $this->id)->where('event_id', '=', $eventId)->first();
|
||||
}
|
||||
|
||||
public function hideFieldsForViz()
|
||||
{
|
||||
foreach ($this->clients as $client)
|
||||
{
|
||||
$client->setVisible([
|
||||
'name',
|
||||
'balance',
|
||||
'paid_to_date',
|
||||
'invoices',
|
||||
'contacts',
|
||||
]);
|
||||
|
||||
foreach ($client->invoices as $invoice)
|
||||
{
|
||||
$invoice->setVisible([
|
||||
'invoice_number',
|
||||
'amount',
|
||||
'balance',
|
||||
'invoice_status_id',
|
||||
'invoice_items',
|
||||
'created_at',
|
||||
]);
|
||||
|
||||
foreach ($invoice->invoice_items as $invoiceItem)
|
||||
{
|
||||
$invoiceItem->setVisible([
|
||||
'product_key',
|
||||
'cost',
|
||||
'qty',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($client->contacts as $contact)
|
||||
{
|
||||
$contact->setVisible(['public_id']);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
@ -51,7 +51,7 @@ class Activity extends Eloquent
|
||||
|
||||
if ($entity)
|
||||
{
|
||||
$activity->user_id = $entity->user_id;
|
||||
$activity->user_id = $entity instanceof User ? $entity->id : $entity->user_id;
|
||||
$activity->account_id = $entity->account_id;
|
||||
}
|
||||
else if (Auth::check())
|
||||
@ -272,7 +272,7 @@ class Activity extends Eloquent
|
||||
}
|
||||
else
|
||||
{
|
||||
$activity = Activity::getBlank();
|
||||
$activity = Activity::getBlank($client);
|
||||
$message = $payment->payment_type_id == PAYMENT_TYPE_CREDIT ? 'applied credit for ' : 'entered ' . $payment->getName() . ' for ';
|
||||
$activity->message = Utils::encodeActivity(Auth::user(), $message, $payment->invoice);
|
||||
}
|
||||
|
@ -204,6 +204,7 @@ class Client extends EntityModel
|
||||
return $this->created_at->format('m/d/y h:i a');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -5,7 +5,7 @@ class EntityModel extends Eloquent
|
||||
protected $softDelete = true;
|
||||
public $timestamps = true;
|
||||
|
||||
protected $hidden = ['id', 'created_at', 'deleted_at', 'updated_at'];
|
||||
protected $hidden = ['id'];
|
||||
|
||||
public static function createNew($parent = false)
|
||||
{
|
||||
@ -14,7 +14,7 @@ class EntityModel extends Eloquent
|
||||
|
||||
if ($parent)
|
||||
{
|
||||
$entity->user_id = $parent->user_id;
|
||||
$entity->user_id = $parent instanceof User ? $parent->id : $parent->user_id;
|
||||
$entity->account_id = $parent->account_id;
|
||||
}
|
||||
else if (Auth::check())
|
||||
|
@ -80,6 +80,7 @@ Route::group(array('before' => 'auth'), function()
|
||||
Route::resource('products', 'ProductController');
|
||||
Route::get('products/{product_id}/archive', 'ProductController@archive');
|
||||
|
||||
Route::get('company/advanced_settings/data_visualizer', 'ReportController@d3');
|
||||
Route::get('company/advanced_settings/chart_builder', 'ReportController@report');
|
||||
Route::post('company/advanced_settings/chart_builder', 'ReportController@report');
|
||||
|
||||
@ -170,7 +171,7 @@ define('ACCOUNT_CUSTOM_FIELDS', 'custom_fields');
|
||||
define('ACCOUNT_INVOICE_DESIGN', 'invoice_design');
|
||||
define('ACCOUNT_CHART_BUILDER', 'chart_builder');
|
||||
define('ACCOUNT_USER_MANAGEMENT', 'user_management');
|
||||
|
||||
define('ACCOUNT_DATA_VISUALIZER', 'data_visualizer');
|
||||
|
||||
define('DEFAULT_INVOICE_NUMBER', '0001');
|
||||
define('RECENTLY_VIEWED_LIMIT', 8);
|
||||
|
@ -11,4 +11,6 @@
|
||||
|
|
||||
*/
|
||||
|
||||
Artisan::resolve('SendRecurringInvoices');
|
||||
Artisan::resolve('SendRecurringInvoices');
|
||||
Artisan::resolve('CreateRandomData');
|
||||
Artisan::resolve('ResetData');
|
||||
|
@ -1,6 +1,7 @@
|
||||
<ul class="nav nav-tabs nav nav-justified">
|
||||
{{ HTML::nav_link('company/advanced_settings/custom_fields', 'custom_fields') }}
|
||||
{{ HTML::nav_link('company/advanced_settings/invoice_design', 'invoice_design') }}
|
||||
{{ HTML::nav_link('company/advanced_settings/data_visualizer', 'data_visualizer') }}
|
||||
{{ HTML::nav_link('company/advanced_settings/chart_builder', 'chart_builder') }}
|
||||
{{ HTML::nav_link('company/advanced_settings/user_management', 'user_management') }}
|
||||
</ul>
|
||||
|
300
app/views/reports/d3.blade.php
Normal file
300
app/views/reports/d3.blade.php
Normal file
@ -0,0 +1,300 @@
|
||||
@extends('accounts.nav')
|
||||
|
||||
@section('head')
|
||||
@parent
|
||||
|
||||
<script src="{{ asset('vendor/d3/d3.min.js') }}" type="text/javascript"></script>
|
||||
|
||||
<style type="text/css">
|
||||
|
||||
#tooltip {
|
||||
position: absolute;
|
||||
width: 200px;
|
||||
height: auto;
|
||||
padding: 10px 10px 2px 10px;
|
||||
background-color: #F6F6F6;
|
||||
-webkit-border-radius: 10px;
|
||||
-moz-border-radius: 10px;
|
||||
border-radius: 10px;
|
||||
-webkit-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
|
||||
-moz-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
|
||||
box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.no-pointer-events {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
</style>
|
||||
@stop
|
||||
|
||||
@section('content')
|
||||
@parent
|
||||
@include('accounts.nav_advanced')
|
||||
|
||||
<div id="tooltip" class="hidden">
|
||||
<p>
|
||||
<strong><span id="tooltipTitle"></span></strong>
|
||||
<a class="pull-right" href="#" target="_blank">View</a>
|
||||
</p>
|
||||
<p>Total <span id="tooltipTotal" class="pull-right"></span></p>
|
||||
<p>Balance <span id="tooltipBalance" class="pull-right"></span></p>
|
||||
<p>Age <span id="tooltipAge" class="pull-right"></span></p>
|
||||
</div>
|
||||
|
||||
<form class="form-inline" role="form">
|
||||
Group By
|
||||
<select id="groupBySelect" class="form-control" onchange="update()">
|
||||
<option>Clients</option>
|
||||
<option>Invoices</option>
|
||||
<option>Products</option>
|
||||
</select>
|
||||
|
||||
</form>
|
||||
|
||||
<p> </p>
|
||||
|
||||
<div class="svg-div"/>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
// store data as JSON
|
||||
var data = {{ $clients->toJson() }};
|
||||
|
||||
_.each(data, function(client) {
|
||||
_.each(client.invoices, function(invoice) {
|
||||
_.each(invoice.invoice_items, function(invoice_item) {
|
||||
invoice_item.invoice = invoice;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// pre-process the possible groupings (clients, invoices and products)
|
||||
var clients = data.concat();
|
||||
var invoices = _.flatten(_.pluck(clients, 'invoices'));
|
||||
|
||||
/*
|
||||
// remove quotes and recurring invoices
|
||||
invoices = _.filter(invoices, function(invoice) {
|
||||
return !parseInt(invoice.is_quote) && !parseInt(invoice.is_recurring);
|
||||
});
|
||||
*/
|
||||
|
||||
var products = _.flatten(_.pluck(invoices, 'invoice_items'));
|
||||
products = d3.nest()
|
||||
.key(function(d) { return d.product_key; })
|
||||
.sortKeys(d3.ascending)
|
||||
.rollup(function(d) { return {
|
||||
amount: d3.sum(d, function(g) {
|
||||
return g.qty * g.cost;
|
||||
}),
|
||||
paid: d3.sum(d, function(g) {
|
||||
return g.invoice && g.invoice.invoice_status_id == 5 ? (g.qty * g.cost) : 0;
|
||||
}),
|
||||
age: d3.median(d, function(g) {
|
||||
return calculateInvoiceAge(g.invoice) || null;
|
||||
}),
|
||||
}})
|
||||
.entries(products);
|
||||
|
||||
// create standardized display properties
|
||||
_.each(clients, function(client) {
|
||||
client.displayName = getClientDisplayName(client);
|
||||
client.displayTotal = +client.paid_to_date + +client.balance;
|
||||
client.displayBalance = +client.balance;
|
||||
client.displayPercent = (+client.paid_to_date / (+client.paid_to_date + +client.balance)).toFixed(2);
|
||||
var oldestInvoice = _.max(client.invoices, function(invoice) { return calculateInvoiceAge(invoice) });
|
||||
client.displayAge = oldestInvoice ? calculateInvoiceAge(oldestInvoice) : 0;
|
||||
})
|
||||
|
||||
_.each(invoices, function(invoice) {
|
||||
invoice.displayName = invoice.invoice_number;
|
||||
invoice.displayTotal = +invoice.amount;
|
||||
invoice.displayBalance = +invoice.balance;
|
||||
invoice.displayPercent = parseInt((+invoice.amount - +invoice.balance) / +invoice.amount);
|
||||
invoice.displayAge = calculateInvoiceAge(invoice);
|
||||
})
|
||||
|
||||
_.each(products, function(product) {
|
||||
product.displayName = product.key;
|
||||
product.displayTotal = product.values.amount;
|
||||
product.displayBalance = product.values.amount - product.values.paid;
|
||||
product.displayPercent = (product.values.paid / product.values.amount).toFixed(2);
|
||||
product.displayAge = product.values.age;
|
||||
})
|
||||
|
||||
/*
|
||||
_.each(clients, function(client) {
|
||||
_.each(client.invoices, function(invoice) {
|
||||
_.each(invoice.invoice_items, function(invoice_item) {
|
||||
delete invoice_item.invoice;
|
||||
});
|
||||
});
|
||||
});
|
||||
*/
|
||||
|
||||
//console.log(JSON.stringify(clients));
|
||||
//console.log(JSON.stringify(invoices));
|
||||
//console.log(JSON.stringify(products));
|
||||
|
||||
|
||||
|
||||
var arc = d3.svg.arc()
|
||||
.innerRadius(function(d) { return d.r - 2 })
|
||||
.outerRadius(function(d) { return d.r - 8 })
|
||||
.startAngle(0);
|
||||
|
||||
var fullArc = d3.svg.arc()
|
||||
.innerRadius(function(d) { return d.r - 3 })
|
||||
.outerRadius(function(d) { return d.r - 7 })
|
||||
.startAngle(0)
|
||||
.endAngle(2 * Math.PI);
|
||||
|
||||
|
||||
var diameter = 1050,
|
||||
format = d3.format(",d");
|
||||
//color = d3.scale.category10();
|
||||
|
||||
var color = d3.scale.linear()
|
||||
.domain([0, 100])
|
||||
.range(["yellow", "red"]);
|
||||
|
||||
var bubble = d3.layout.pack()
|
||||
.sort(null)
|
||||
.size([diameter, diameter])
|
||||
.value(function(d) { return Math.max(30, d.displayTotal) })
|
||||
.padding(12);
|
||||
|
||||
var svg = d3.select(".svg-div").append("svg")
|
||||
.attr("width", "100%")
|
||||
.attr("height", "100%")
|
||||
.attr("class", "bubble");
|
||||
|
||||
svg.append("rect")
|
||||
.attr("stroke-width", "1")
|
||||
.attr("stroke", "rgb(150,150,150)")
|
||||
.attr("width", "100%")
|
||||
.attr("height", "100%")
|
||||
.attr("fill", "white");
|
||||
|
||||
function update() {
|
||||
|
||||
var data = {};
|
||||
var groupBy = $('#groupBySelect').val().toLowerCase();
|
||||
data.children = window[groupBy];
|
||||
|
||||
data = bubble.nodes(data).filter(function(d) {
|
||||
return !d.children && d.displayTotal && d.displayName;
|
||||
});
|
||||
|
||||
var selection = svg.selectAll(".node")
|
||||
.data(data, function(d) { return d.displayName; });
|
||||
//.data(data);
|
||||
|
||||
var node = selection.enter().append("g")
|
||||
.attr("class", "node")
|
||||
.attr("transform", function(d) { return "translate(" + (d.x+20) + "," + (d.y+20) + ")"; });
|
||||
|
||||
var visibleTooltip = false;
|
||||
node.on("mousemove", function(d) {
|
||||
if (!visibleTooltip || visibleTooltip != d.displayName) {
|
||||
d3.select("#tooltip")
|
||||
.classed("hidden", false)
|
||||
.style("left", d3.event.pageX + "px")
|
||||
.style("top", d3.event.pageY + "px");
|
||||
visibleTooltip = d.displayName;
|
||||
}
|
||||
|
||||
d3.select("#tooltipTitle").text(d.displayName);
|
||||
d3.select("#tooltipTotal").text(formatMoney(d.displayTotal));
|
||||
d3.select("#tooltipBalance").text(formatMoney(d.displayBalance));
|
||||
d3.select("#tooltipAge").text(pluralize('? day', parseInt(d.displayAge)));
|
||||
|
||||
if (groupBy == "products") {
|
||||
d3.select("#tooltip a").classed("hidden", true);
|
||||
} else {
|
||||
d3.select("#tooltip a").classed("hidden", false);
|
||||
d3.select("#tooltip a").attr("href", "/" + groupBy + "/" + d.public_id);
|
||||
}
|
||||
});
|
||||
|
||||
svg.on("click", function() {
|
||||
visibleTooltip = false;
|
||||
d3.select("#tooltip")
|
||||
.classed("hidden", true);
|
||||
});
|
||||
|
||||
node.append("circle")
|
||||
.attr("fill", "#ffffff")
|
||||
.attr("r", function(d) { return d.r });
|
||||
|
||||
node.append("path")
|
||||
.each(function(d) { d.endAngle = 0; })
|
||||
.attr("class", "no-pointer-events")
|
||||
.attr("class", "animate-fade")
|
||||
.attr("d", fullArc)
|
||||
.style("fill", function(d, i) { return 'white'; });
|
||||
|
||||
node.append("text")
|
||||
.attr("dy", ".3em")
|
||||
.attr("class", "no-pointer-events")
|
||||
.style("text-anchor", "middle")
|
||||
.text(function(d) { return d.displayName; });
|
||||
|
||||
node.append("path")
|
||||
.each(function(d) { d.endAngle = 0; })
|
||||
.attr("class", "no-pointer-events")
|
||||
.attr("class", "animate-grow")
|
||||
.attr("d", arc)
|
||||
.style("fill", function(d, i) { return 'grey'; });
|
||||
|
||||
d3.selectAll("path.animate-grow")
|
||||
.transition()
|
||||
.delay(function(d, i) { return (Math.random() * 500) })
|
||||
.duration(1000)
|
||||
.call(arcTween, 5);
|
||||
|
||||
d3.selectAll("path.animate-fade")
|
||||
.transition()
|
||||
.duration(1000)
|
||||
.style("fill", function(d, i) {
|
||||
return d.displayAge ? color(d.displayAge) : 'grey';
|
||||
//return 'red';
|
||||
});
|
||||
|
||||
selection.exit().remove();
|
||||
}
|
||||
|
||||
update();
|
||||
|
||||
// http://bl.ocks.org/mbostock/5100636
|
||||
function arcTween(transition, newAngle) {
|
||||
transition.attrTween("d", function(d) {
|
||||
var interpolate = d3.interpolate( 0, 360 * d.displayPercent * Math.PI/180 );
|
||||
return function(t) {
|
||||
d.endAngle = interpolate(t);
|
||||
return arc(d);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function calculateInvoiceAge(invoice) {
|
||||
if (!invoice || invoice.invoice_status_id == 5) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return parseInt((new Date().getTime() - Date.parse(invoice.created_at)) / (1000*60*60*24));
|
||||
}
|
||||
|
||||
|
||||
function pluralize(string, count) {
|
||||
string = string.replace('?', count);
|
||||
if (count !== 1) {
|
||||
string += 's';
|
||||
}
|
||||
return string;
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
@stop
|
@ -17,10 +17,10 @@
|
||||
"typeahead.js": "~0.9.3",
|
||||
"accounting": "~0.*",
|
||||
"pdfjs": "*",
|
||||
"spectrum": "~1.3.4"
|
||||
"spectrum": "~1.3.4",
|
||||
"d3": "~3.4.11"
|
||||
},
|
||||
"resolutions": {
|
||||
"datatables": "~1.*",
|
||||
"jquery": "~1.11"
|
||||
}
|
||||
}
|
||||
|
1273
public/built.js
1273
public/built.js
File diff suppressed because it is too large
Load Diff
1272
public/js/script.js
1272
public/js/script.js
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user