mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Merge branch 'master' of https://github.com/hillelcoren/invoice-ninja
This commit is contained in:
commit
20bf88b028
@ -9,13 +9,15 @@ If you'd like to use our code to sell your own invoicing app get in touch.
|
||||
|
||||
Most online invoicing sites are expensive. They shouldn't be. The aim of this project is to provide a free, open-source alternative. Additionally, the hope is the codebase will serve as a sample site for Laravel as well as other JavaScript technologies.
|
||||
|
||||
To setup the site you can either use this [zip file](http://hillelcoren.com/invoice-ninja/self-hosting/) (easier to setup) or checkout the code from GitHub following the instructions below (easier to stay up to date). There's also a more detailed setup guide [available here](http://hillelcoren.com/invoice-ninja/laravel-ubuntu-virtualbox/). To deploy the app with [Docker](http://www.docker.com/) you can use [this project](https://github.com/rollbrettler/Dockerfiles/tree/master/invoice-ninja).
|
||||
To setup the site you can either use this [zip file](http://hillelcoren.com/invoice-ninja/self-hosting/) (easier to setup) or checkout the code from GitHub following the instructions below (easier to stay up to date). There's also a more detailed setup guide [available here](http://hillelcoren.com/invoice-ninja/laravel-ubuntu-virtualbox/).
|
||||
|
||||
For a WAMP/MAMP/LAMP setup you can one-click install using Softaculous's [AMPPS](http://www.ampps.com/). To deploy the app with [Docker](http://www.docker.com/) you can use [this project](https://github.com/rollbrettler/Dockerfiles/tree/master/invoice-ninja).
|
||||
|
||||
To connect follow [@invoiceninja](https://twitter.com/invoiceninja) or join the [Facebook Group](https://www.facebook.com/invoiceninja). For discussion of the code please use the [Google Group](https://groups.google.com/d/forum/invoiceninja).
|
||||
|
||||
If you'd like to translate the site please use [caouecs/Laravel4-long](https://github.com/caouecs/Laravel4-lang) for the starter files.
|
||||
|
||||
Site design by [kantorp-wegl.in](http://kantorp-wegl.in/)
|
||||
Developed by [@hillelcoren](https://twitter.com/hillelcoren) | Designed by [kantorp-wegl.in](http://kantorp-wegl.in/).
|
||||
|
||||
### Features
|
||||
|
||||
@ -25,6 +27,8 @@ Site design by [kantorp-wegl.in](http://kantorp-wegl.in/)
|
||||
* Recurring invoices
|
||||
* Tax rates and payment terms
|
||||
* Multi-user support
|
||||
* [Zapier](https://zapier.com/) integration
|
||||
* [D3.js](http://d3js.org/) visualizations
|
||||
|
||||
### Steps to setup
|
||||
|
||||
|
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');
|
||||
}
|
||||
}
|
@ -36,6 +36,18 @@ class AccountController extends \BaseController {
|
||||
public function update()
|
||||
{
|
||||
if (!Utils::isNinja()) {
|
||||
// populate migrations if the application was initially setup using database.sql
|
||||
$migrations = DB::table('migrations')->get();
|
||||
if (Schema::hasTable('accounts') && count($migrations) == 0) {
|
||||
$migrations = ['2013_11_05_180133_confide_setup_users_table', '2013_11_28_195703_setup_countries_table', '2014_02_13_151500_add_cascase_drops', '2014_02_19_151817_add_support_for_invoice_designs', '2014_03_03_155556_add_phone_to_account', '2014_03_19_201454_add_language_support', '2014_03_20_200300_create_payment_libraries', '2014_03_23_051736_enable_forcing_jspdf', '2014_03_25_102200_add_sort_and_recommended_to_gateways', '2014_04_03_191105_add_pro_plan', '2014_04_17_100523_add_remember_token', '2014_04_17_145108_add_custom_fields', '2014_04_23_170909_add_products_settings', '2014_04_29_174315_add_advanced_settings', '2014_05_17_175626_add_quotes', '2014_06_17_131940_add_accepted_credit_cards_to_account_gateways', '2014_07_13_142654_one_click_install', '2014_07_17_205900_support_hiding_quantity', '2014_07_24_171214_add_zapier_support'];
|
||||
foreach ($migrations as $migration) {
|
||||
DB::table('migrations')->insert([
|
||||
'migration' => $migration,
|
||||
'batch' => 1
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Artisan::call('migrate');
|
||||
} catch (Exception $e) {
|
||||
@ -46,6 +58,7 @@ class AccountController extends \BaseController {
|
||||
return Redirect::to('/');
|
||||
}
|
||||
|
||||
/*
|
||||
public function reset()
|
||||
{
|
||||
if (Utils::isNinjaDev()) {
|
||||
@ -61,6 +74,7 @@ class AccountController extends \BaseController {
|
||||
|
||||
return Redirect::to('/');
|
||||
}
|
||||
*/
|
||||
|
||||
public function getStarted()
|
||||
{
|
||||
@ -160,6 +174,9 @@ class AccountController extends \BaseController {
|
||||
{
|
||||
$configFields->$configField = str_repeat('*', strlen($value));
|
||||
}
|
||||
} else {
|
||||
$accountGateway = AccountGateway::createNew();
|
||||
$accountGateway->gateway_id = GATEWAY_MOOLAH;
|
||||
}
|
||||
|
||||
$recommendedGateways = Gateway::remember(DEFAULT_QUERY_CACHE)
|
||||
@ -197,22 +214,9 @@ class AccountController extends \BaseController {
|
||||
);
|
||||
$recommendedGatewayArray['Other Options'] = $otherItem;
|
||||
|
||||
$data = [
|
||||
'account' => $account,
|
||||
'accountGateway' => $accountGateway,
|
||||
'config' => $configFields,
|
||||
'gateways' => Gateway::remember(DEFAULT_QUERY_CACHE)
|
||||
->orderBy('name')
|
||||
->get(),
|
||||
'dropdownGateways' => Gateway::remember(DEFAULT_QUERY_CACHE)
|
||||
->where('recommended', '=', '0')
|
||||
->orderBy('name')
|
||||
->get(),
|
||||
'recommendedGateways' => $recommendedGatewayArray,
|
||||
'creditCardTypes' => $creditCards,
|
||||
];
|
||||
$gateways = Gateway::remember(DEFAULT_QUERY_CACHE)->orderBy('name')->get();
|
||||
|
||||
foreach ($data['gateways'] as $gateway)
|
||||
foreach ($gateways as $gateway)
|
||||
{
|
||||
$paymentLibrary = $gateway->paymentlibrary;
|
||||
|
||||
@ -224,6 +228,19 @@ class AccountController extends \BaseController {
|
||||
}
|
||||
}
|
||||
|
||||
$data = [
|
||||
'account' => $account,
|
||||
'accountGateway' => $accountGateway,
|
||||
'config' => $configFields,
|
||||
'gateways' => $gateways,
|
||||
'dropdownGateways' => Gateway::remember(DEFAULT_QUERY_CACHE)
|
||||
->where('recommended', '=', '0')
|
||||
->orderBy('name')
|
||||
->get(),
|
||||
'recommendedGateways' => $recommendedGatewayArray,
|
||||
'creditCardTypes' => $creditCards,
|
||||
];
|
||||
|
||||
return View::make('accounts.payments', $data);
|
||||
}
|
||||
else if ($section == ACCOUNT_NOTIFICATIONS)
|
||||
|
@ -3,18 +3,20 @@
|
||||
use ninja\repositories\PaymentRepository;
|
||||
use ninja\repositories\InvoiceRepository;
|
||||
use ninja\repositories\AccountRepository;
|
||||
use ninja\mailers\ContactMailer;
|
||||
|
||||
class PaymentController extends \BaseController
|
||||
{
|
||||
protected $creditRepo;
|
||||
|
||||
public function __construct(PaymentRepository $paymentRepo, InvoiceRepository $invoiceRepo, AccountRepository $accountRepo)
|
||||
public function __construct(PaymentRepository $paymentRepo, InvoiceRepository $invoiceRepo, AccountRepository $accountRepo, ContactMailer $contactMailer)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->paymentRepo = $paymentRepo;
|
||||
$this->invoiceRepo = $invoiceRepo;
|
||||
$this->accountRepo = $accountRepo;
|
||||
$this->contactMailer = $contactMailer;
|
||||
}
|
||||
|
||||
public function index()
|
||||
@ -423,6 +425,9 @@ class PaymentController extends \BaseController
|
||||
'hideHeader' => true
|
||||
];
|
||||
|
||||
$name = "{$license->first_name} {$license->last_name}";
|
||||
$this->contactMailer->sendLicensePaymentConfirmation($name, $license->email, LICENSE_PRICE, $license->license_key);
|
||||
|
||||
return View::make('public.license', $data);
|
||||
|
||||
//return Redirect::away(Session::get('return_url') . "?license_key={$license->license_key}");
|
||||
|
@ -2,6 +2,31 @@
|
||||
|
||||
class ReportController extends \BaseController {
|
||||
|
||||
public function d3()
|
||||
{
|
||||
$message = '';
|
||||
|
||||
if (Auth::user()->account->isPro()) {
|
||||
$account = Auth::user()->account;
|
||||
$account = $account->with(['clients.invoices.invoice_items', 'clients.contacts'])->first();
|
||||
$account = $account->hideFieldsForViz();
|
||||
$clients = $account->clients->toJson();
|
||||
} else if (isset($_ENV['DATA_VIZ_SAMPLE'])) {
|
||||
$clients = $_ENV['DATA_VIZ_SAMPLE'];
|
||||
$message = trans('texts.sample_data');
|
||||
} else {
|
||||
$clients = '[]';
|
||||
}
|
||||
|
||||
$data = [
|
||||
'feature' => ACCOUNT_DATA_VISUALIZATIONS,
|
||||
'clients' => $clients,
|
||||
'message' => $message
|
||||
];
|
||||
|
||||
return View::make('reports.d3', $data);
|
||||
}
|
||||
|
||||
public function report()
|
||||
{
|
||||
if (Input::all())
|
||||
|
@ -12,6 +12,20 @@ class PaymentLibrariesSeeder extends Seeder
|
||||
array('name'=>'Psigate', 'provider'=>'Psigate', 'payment_library_id' => 2)
|
||||
];
|
||||
|
||||
foreach ($gateways as $gateway)
|
||||
{
|
||||
Gateway::create($gateway);
|
||||
}
|
||||
|
||||
Gateway::create([
|
||||
'name' => 'moolah',
|
||||
'provider' => 'AuthorizeNet_AIM',
|
||||
'sort_order' => 1,
|
||||
'recommended' => 1,
|
||||
'site_url' => 'https://invoiceninja.mymoolah.com/',
|
||||
]);
|
||||
|
||||
/*
|
||||
$updateProviders = array(
|
||||
0 => 'AuthorizeNet_AIM',
|
||||
//1 => 'BeanStream',
|
||||
@ -21,11 +35,6 @@ class PaymentLibrariesSeeder extends Seeder
|
||||
5 => 'TwoCheckout'
|
||||
);
|
||||
|
||||
foreach ($gateways as $gateway)
|
||||
{
|
||||
Gateway::create($gateway);
|
||||
}
|
||||
|
||||
Gateway::whereIn('provider', $updateProviders)->update(array('recommended' => 1));
|
||||
|
||||
Gateway::where('provider', '=', 'AuthorizeNet_AIM')->update(array('sort_order' => 5, 'site_url' => 'http://reseller.authorize.net/application/?id=5560364'));
|
||||
@ -33,5 +42,7 @@ class PaymentLibrariesSeeder extends Seeder
|
||||
//Gateway::where('provider', '=', 'FirstData_Connect')->update(array('sort_order' => 20, 'site_url' => 'https://www.firstdata.com/'));
|
||||
Gateway::where('provider', '=', 'PayPal_Pro')->update(array('sort_order' => 25, 'site_url' => 'https://www.paypal.com/'));
|
||||
Gateway::where('provider', '=', 'TwoCheckout')->update(array('sort_order' => 30, 'site_url' => 'https://www.2checkout.com/referral?r=2c37ac2298'));
|
||||
*/
|
||||
|
||||
}
|
||||
}
|
@ -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,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_visualizations' => 'Data Visualizations',
|
||||
'sample_data' => 'Sample data shown',
|
||||
|
||||
);
|
||||
|
@ -422,10 +422,14 @@ 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_visualizations' => 'Data Visualizations',
|
||||
'sample_data' => 'Sample data shown',
|
||||
|
||||
|
||||
);
|
@ -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,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_visualizations' => 'Data Visualizations',
|
||||
'sample_data' => 'Sample data shown',
|
||||
|
||||
);
|
||||
|
@ -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,14 @@ 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_visualizations' => 'Data Visualizations',
|
||||
'sample_data' => 'Sample data shown',
|
||||
|
||||
|
||||
);
|
||||
|
@ -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,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_visualizations' => 'Data Visualizations',
|
||||
'sample_data' => 'Sample data shown',
|
||||
|
||||
);
|
||||
|
@ -422,11 +422,15 @@ 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_visualizations' => 'Data Visualizations',
|
||||
'sample_data' => 'Sample data shown',
|
||||
|
||||
|
||||
);
|
||||
|
||||
|
@ -422,10 +422,14 @@ 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_visualizations' => 'Data Visualizations',
|
||||
'sample_data' => 'Sample data shown',
|
||||
|
||||
|
||||
);
|
@ -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,14 @@ 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_visualizations' => 'Data Visualizations',
|
||||
'sample_data' => 'Sample data shown',
|
||||
|
||||
|
||||
);
|
||||
|
@ -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,14 @@ 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_visualizations' => 'Data Visualizations',
|
||||
'sample_data' => 'Sample data shown',
|
||||
|
||||
|
||||
);
|
||||
|
@ -252,4 +252,49 @@ 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([
|
||||
'public_id',
|
||||
'name',
|
||||
'balance',
|
||||
'paid_to_date',
|
||||
'invoices',
|
||||
'contacts',
|
||||
]);
|
||||
|
||||
foreach ($client->invoices as $invoice)
|
||||
{
|
||||
$invoice->setVisible([
|
||||
'public_id',
|
||||
'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())
|
||||
|
@ -73,4 +73,21 @@ class ContactMailer extends Mailer {
|
||||
$user = $payment->invitation->user;
|
||||
$this->sendTo($payment->contact->email, $user->email, $user->getDisplayName(), $subject, $view, $data);
|
||||
}
|
||||
|
||||
public function sendLicensePaymentConfirmation($name, $email, $amount, $license)
|
||||
{
|
||||
$view = 'payment_confirmation';
|
||||
$subject = trans('texts.payment_subject');
|
||||
|
||||
$data = [
|
||||
'accountName' => trans('texts.email_from'),
|
||||
'clientName' => $name,
|
||||
'emailFooter' => false,
|
||||
'paymentAmount' => Utils::formatMoney($amount, 1),
|
||||
'showNinjaFooter' => false,
|
||||
'emailMessage' => "Your license: $license",
|
||||
];
|
||||
|
||||
$this->sendTo($email, CONTACT_EMAIL, CONTACT_NAME, $subject, $view, $data);
|
||||
}
|
||||
}
|
@ -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_visualizations', '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_VISUALIZATIONS', 'data_visualizations');
|
||||
|
||||
define('DEFAULT_INVOICE_NUMBER', '0001');
|
||||
define('RECENTLY_VIEWED_LIMIT', 8);
|
||||
@ -223,6 +224,7 @@ define('GATEWAY_AUTHORIZE_NET', 1);
|
||||
define('GATEWAY_PAYPAL_EXPRESS', 17);
|
||||
define('GATEWAY_BEANSTREAM', 29);
|
||||
define('GATEWAY_PSIGATE', 30);
|
||||
define('GATEWAY_MOOLAH', 31);
|
||||
|
||||
define('EVENT_CREATE_CLIENT', 1);
|
||||
define('EVENT_CREATE_INVOICE', 2);
|
||||
|
@ -12,3 +12,5 @@
|
||||
*/
|
||||
|
||||
Artisan::resolve('SendRecurringInvoices');
|
||||
Artisan::resolve('CreateRandomData');
|
||||
Artisan::resolve('ResetData');
|
||||
|
5
app/tests/data/Demo/demo.response.json
Normal file
5
app/tests/data/Demo/demo.response.json
Normal file
File diff suppressed because one or more lines are too long
@ -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_visualizations', 'data_visualizations') }}
|
||||
{{ HTML::nav_link('company/advanced_settings/chart_builder', 'chart_builder') }}
|
||||
{{ HTML::nav_link('company/advanced_settings/user_management', 'user_management') }}
|
||||
</ul>
|
||||
|
@ -11,6 +11,7 @@
|
||||
@if ($accountGateway)
|
||||
{{ Former::populateField('gateway_id', $accountGateway->gateway_id) }}
|
||||
{{ Former::populateField('recommendedGateway_id', $accountGateway->gateway_id) }}
|
||||
@if ($config)
|
||||
@foreach ($accountGateway->fields as $field => $junk)
|
||||
@if (in_array($field, ['solutionType', 'landingPage', 'headerImageUrl', 'brandName']))
|
||||
{{-- do nothing --}}
|
||||
@ -19,6 +20,7 @@
|
||||
@endif
|
||||
@endforeach
|
||||
@endif
|
||||
@endif
|
||||
|
||||
<div class="two-column">
|
||||
{{ Former::checkboxes('creditCardTypes[]')->label('Accepted Credit Cards')
|
||||
@ -26,13 +28,15 @@
|
||||
}}
|
||||
</div>
|
||||
|
||||
<p/> <p/>
|
||||
|
||||
<div class="two-column">
|
||||
{{ Former::radios('recommendedGateway_id')->label('Recommended Gateways')
|
||||
{{ Former::radios('recommendedGateway_id')->label('Recommended Gateway')
|
||||
->radios($recommendedGateways)->class('recommended-gateway')
|
||||
}}
|
||||
</div>
|
||||
|
||||
{{ Former::select('gateway_id')->label('PayPal & Other Gateways')->addOption('', '')
|
||||
{{ Former::select('gateway_id')->label('Select Gateway')->addOption('', '')
|
||||
->dataClass('gateway-dropdown')
|
||||
->fromQuery($dropdownGateways, 'name', 'id')
|
||||
->onchange('setFieldsShown()'); }}
|
||||
@ -57,6 +61,8 @@
|
||||
|
||||
@endforeach
|
||||
|
||||
<p/> <p/>
|
||||
|
||||
{{ Former::actions( Button::lg_success_submit('Save')->append_with_icon('floppy-disk') ) }}
|
||||
{{ Former::close() }}
|
||||
|
||||
|
@ -9,6 +9,10 @@
|
||||
|
||||
{{ trans('texts.payment_message', ['amount' => $paymentAmount]) }}<p/>
|
||||
|
||||
@if (isset($emailMessage) && $emailMessage)
|
||||
{{ $emailMessage }}<p/>
|
||||
@endif
|
||||
|
||||
@if ($emailFooter)
|
||||
{{ nl2br($emailFooter) }}
|
||||
@else
|
||||
|
@ -2,6 +2,10 @@
|
||||
|
||||
{{ trans('texts.payment_message', ['amount' => $paymentAmount]) }}
|
||||
|
||||
@if (isset($emailMessage) && $emailMessage)
|
||||
{{ $emailMessage }}
|
||||
@endif
|
||||
|
||||
@if ($emailFooter)
|
||||
{{ $emailFooter }}
|
||||
@else
|
||||
|
@ -11,7 +11,7 @@
|
||||
<div class="cell">Multi-user support</div>
|
||||
<div class="cell">Quotes/pro-forma invoices</div>
|
||||
<div class="cell">Custom invoice fields and colors</div>
|
||||
<div class="cell">Dynamic chart builder</div>
|
||||
<div class="cell">Dynamic data vizualizations</div>
|
||||
<div class="cell">Priority email support</div>
|
||||
<div class="cell">Remove "Created by Invoice Ninja"</div>
|
||||
<div class="cell">Latest and greatest features</div>
|
||||
@ -29,7 +29,7 @@
|
||||
<div class="cell"><div class="hide-desktop">Multi-user support</div><span class="glyphicon glyphicon-remove"></div>
|
||||
<div class="cell"><div class="hide-desktop">Quotes/pro-forma invoices</div><span class="glyphicon glyphicon-remove"></div>
|
||||
<div class="cell"><div class="hide-desktop">Custom fields and invoice colors</div><span class="glyphicon glyphicon-remove"></div>
|
||||
<div class="cell"><div class="hide-desktop">Dynamic chart builder</div><span class="glyphicon glyphicon-remove"></div>
|
||||
<div class="cell"><div class="hide-desktop">Dynamic data vizualizations</div><span class="glyphicon glyphicon-remove"></div>
|
||||
<div class="cell"><div class="hide-desktop">Priority email support</div><span class="glyphicon glyphicon-remove"></div>
|
||||
<div class="cell"><div class="hide-desktop">Remove "Created by Invoice Ninja"</div><span class="glyphicon glyphicon-remove"></div>
|
||||
<div class="cell"><div class="hide-desktop">Latest and greatest features</div><span class="glyphicon glyphicon-remove"></div>
|
||||
@ -47,7 +47,7 @@
|
||||
<div class="cell"><div class="hide-desktop">Multi-user support</div><span class="glyphicon glyphicon-ok"></div>
|
||||
<div class="cell"><div class="hide-desktop">Quotes/pro-forma invoices</div><span class="glyphicon glyphicon-ok"></div>
|
||||
<div class="cell"><div class="hide-desktop">Custom invoice fields and colors</div><span class="glyphicon glyphicon-ok"></div>
|
||||
<div class="cell"><div class="hide-desktop">Dynamic chart builder</div><span class="glyphicon glyphicon-ok"></div>
|
||||
<div class="cell"><div class="hide-desktop">Dynamic data vizualizations</div><span class="glyphicon glyphicon-ok"></div>
|
||||
<div class="cell"><div class="hide-desktop">Priority email support</div><span class="glyphicon glyphicon-ok"></div>
|
||||
<div class="cell"><div class="hide-desktop">Remove "Created by Invoice Ninja"</div><span class="glyphicon glyphicon-ok"></div>
|
||||
<div class="cell"><div class="hide-desktop">Latest and greatest features</div><span class="glyphicon glyphicon-ok"></div>
|
||||
|
301
app/views/reports/d3.blade.php
Normal file
301
app/views/reports/d3.blade.php
Normal file
@ -0,0 +1,301 @@
|
||||
@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')
|
||||
|
||||
{{ Former::open() }}
|
||||
{{ Former::legend('data_visualizations') }}
|
||||
{{ Former::close() }}
|
||||
|
||||
<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>
|
||||
{{ $message }}
|
||||
</form>
|
||||
|
||||
<p> </p>
|
||||
|
||||
<div class="svg-div"/>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
// store data as JSON
|
||||
var data = {{ $clients }};
|
||||
|
||||
_.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(truncate(d.displayName, 18));
|
||||
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" || !d.public_id) {
|
||||
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';
|
||||
});
|
||||
|
||||
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
|
@ -10,6 +10,10 @@
|
||||
@parent
|
||||
@include('accounts.nav_advanced')
|
||||
|
||||
{{ Former::open() }}
|
||||
{{ Former::legend('chart_builder') }}
|
||||
{{ Former::close() }}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-4">
|
||||
|
||||
|
@ -18,10 +18,10 @@
|
||||
"accounting": "~0.*",
|
||||
"pdfjs": "*",
|
||||
"spectrum": "~1.3.4",
|
||||
"handsontable": "*"
|
||||
"handsontable": "*",
|
||||
"d3": "~3.4.11"
|
||||
},
|
||||
"resolutions": {
|
||||
"datatables": "~1.*",
|
||||
"jquery": "~1.11"
|
||||
}
|
||||
}
|
||||
|
1276
public/built.js
1276
public/built.js
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 6.6 KiB |
1276
public/js/script.js
1276
public/js/script.js
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user