1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-08 20:22:42 +01:00

Added support for additional invoice designs

This commit is contained in:
Hillel Coren 2014-10-24 13:48:59 +03:00
parent ea429368a8
commit cc6c6194c6
35 changed files with 614 additions and 674 deletions

View File

@ -31,8 +31,7 @@ module.exports = function(grunt) {
'public/vendor/spectrum/spectrum.js', 'public/vendor/spectrum/spectrum.js',
'public/js/bootstrap-combobox.js', 'public/js/bootstrap-combobox.js',
'public/vendor/jspdf/dist/jspdf.min.js', 'public/vendor/jspdf/dist/jspdf.min.js',
//'public/js/jspdf.source.js', 'public/vendor/lightbox2/js/lightbox.min.js',
//'public/js/jspdf.plugin.split_text_to_size.js',
'public/js/script.js', 'public/js/script.js',
], ],
dest: 'public/built.js', dest: 'public/built.js',
@ -58,6 +57,7 @@ module.exports = function(grunt) {
'public/vendor/spectrum/spectrum.css', 'public/vendor/spectrum/spectrum.css',
'public/css/bootstrap-combobox.css', 'public/css/bootstrap-combobox.css',
'public/css/typeahead.js-bootstrap.css', 'public/css/typeahead.js-bootstrap.css',
'public/vendor/lightbox2/css/lightbox.css',
'public/css/style.css', 'public/css/style.css',
], ],
dest: 'public/built.css', dest: 'public/built.css',

View File

@ -283,7 +283,7 @@ class AccountController extends \BaseController {
$invoice->invoice_number = Auth::user()->account->getNextInvoiceNumber(); $invoice->invoice_number = Auth::user()->account->getNextInvoiceNumber();
$invoice->invoice_date = date_create()->format('Y-m-d'); $invoice->invoice_date = date_create()->format('Y-m-d');
$invoice->account = Auth::user()->account; $invoice->account = json_decode(Auth::user()->account->toJson());
$invoice->amount = $invoice->balance = 100; $invoice->amount = $invoice->balance = 100;
$invoiceItem->cost = 100; $invoiceItem->cost = 100;
@ -295,7 +295,7 @@ class AccountController extends \BaseController {
$invoice->invoice_items = [$invoiceItem]; $invoice->invoice_items = [$invoiceItem];
$data['invoice'] = $invoice; $data['invoice'] = $invoice;
$data['invoiceDesigns'] = InvoiceDesign::remember(DEFAULT_QUERY_CACHE)->orderBy('id')->get(); $data['invoiceDesigns'] = InvoiceDesign::remember(DEFAULT_QUERY_CACHE, 'invoice_designs_cache')->where('id', '<=', Auth::user()->maxInvoiceDesignId())->orderBy('id')->get();
} }
return View::make("accounts.{$subSection}", $data); return View::make("accounts.{$subSection}", $data);

View File

@ -243,7 +243,7 @@ class InvoiceController extends \BaseController {
'sizes' => Size::remember(DEFAULT_QUERY_CACHE)->orderBy('id')->get(), 'sizes' => Size::remember(DEFAULT_QUERY_CACHE)->orderBy('id')->get(),
'paymentTerms' => PaymentTerm::remember(DEFAULT_QUERY_CACHE)->orderBy('num_days')->get(['name', 'num_days']), 'paymentTerms' => PaymentTerm::remember(DEFAULT_QUERY_CACHE)->orderBy('num_days')->get(['name', 'num_days']),
'industries' => Industry::remember(DEFAULT_QUERY_CACHE)->orderBy('name')->get(), 'industries' => Industry::remember(DEFAULT_QUERY_CACHE)->orderBy('name')->get(),
'invoiceDesigns' => InvoiceDesign::remember(DEFAULT_QUERY_CACHE)->orderBy('id')->get(), 'invoiceDesigns' => InvoiceDesign::remember(DEFAULT_QUERY_CACHE, 'invoice_designs_cache')->where('id', '<=', Auth::user()->maxInvoiceDesignId())->orderBy('id')->get(),
'frequencies' => array( 'frequencies' => array(
1 => 'Weekly', 1 => 'Weekly',
2 => 'Two weeks', 2 => 'Two weeks',

View File

@ -132,13 +132,13 @@ class PaymentController extends \BaseController
return $gateway; return $gateway;
} }
private function getLicensePaymentDetails($input) private function getLicensePaymentDetails($input, $affiliate)
{ {
$data = self::convertInputForOmnipay($input); $data = self::convertInputForOmnipay($input);
$card = new CreditCard($data); $card = new CreditCard($data);
return [ return [
'amount' => LICENSE_PRICE, 'amount' => $affiliate->price,
'card' => $card, 'card' => $card,
'currency' => 'USD', 'currency' => 'USD',
'returnUrl' => URL::to('license_complete'), 'returnUrl' => URL::to('license_complete'),
@ -310,12 +310,14 @@ class PaymentController extends \BaseController
} }
} }
Session::set('product_id', Input::get('product_id', PRODUCT_ONE_CLICK_INSTALL));
if (!Session::get('affiliate_id')) if (!Session::get('affiliate_id'))
{ {
return Utils::fatalError(); return Utils::fatalError();
} }
if (Input::has('test_mode')) if (Utils::isNinjaDev() && Input::has('test_mode'))
{ {
Session::set('test_mode', Input::get('test_mode')); Session::set('test_mode', Input::get('test_mode'));
} }
@ -334,7 +336,7 @@ class PaymentController extends \BaseController
'showBreadcrumbs' => false, 'showBreadcrumbs' => false,
'hideHeader' => true, 'hideHeader' => true,
'url' => 'license', 'url' => 'license',
'amount' => LICENSE_PRICE, 'amount' => $affiliate->price,
'client' => false, 'client' => false,
'contact' => false, 'contact' => false,
'paymentLibrary' => $paymentLibrary, 'paymentLibrary' => $paymentLibrary,
@ -380,6 +382,8 @@ class PaymentController extends \BaseController
try try
{ {
$affiliate = Affiliate::find(Session::get('affiliate_id'));
if ($testMode) if ($testMode)
{ {
$ref = 'TEST_MODE'; $ref = 'TEST_MODE';
@ -387,7 +391,7 @@ class PaymentController extends \BaseController
else else
{ {
$gateway = self::createGateway($accountGateway); $gateway = self::createGateway($accountGateway);
$details = self::getLicensePaymentDetails(Input::all()); $details = self::getLicensePaymentDetails(Input::all(), $affiliate);
$response = $gateway->purchase($details)->send(); $response = $gateway->purchase($details)->send();
$ref = $response->getTransactionReference(); $ref = $response->getTransactionReference();
@ -415,10 +419,9 @@ class PaymentController extends \BaseController
$license->transaction_reference = $ref; $license->transaction_reference = $ref;
$license->license_key = $licenseKey; $license->license_key = $licenseKey;
$license->affiliate_id = Session::get('affiliate_id'); $license->affiliate_id = Session::get('affiliate_id');
$license->product_id = Session::get('product_id');
$license->save(); $license->save();
$affiliate = Affiliate::find(Session::get('affiliate_id'));
$data = [ $data = [
'message' => $affiliate->payment_subtitle, 'message' => $affiliate->payment_subtitle,
'license' => $licenseKey, 'license' => $licenseKey,
@ -426,11 +429,16 @@ class PaymentController extends \BaseController
]; ];
$name = "{$license->first_name} {$license->last_name}"; $name = "{$license->first_name} {$license->last_name}";
$this->contactMailer->sendLicensePaymentConfirmation($name, $license->email, LICENSE_PRICE, $license->license_key); $this->contactMailer->sendLicensePaymentConfirmation($name, $license->email, $affiliate->price, $license->license_key, $license->product_id);
if (Session::has('return_url'))
{
return Redirect::away(Session::get('return_url') . "?license_key={$license->license_key}&product_id=" . Session::get('product_id'));
}
else
{
return View::make('public.license', $data); return View::make('public.license', $data);
}
//return Redirect::away(Session::get('return_url') . "?license_key={$license->license_key}");
} }
catch (\Exception $e) catch (\Exception $e)
{ {
@ -443,8 +451,13 @@ class PaymentController extends \BaseController
public function claim_license() public function claim_license()
{ {
$license = License::where('license_key', '=', Input::get('license_key')) $licenseKey = Input::get('license_key');
->where('is_claimed', '=', false)->first(); $productId = Input::get('product_id', PRODUCT_ONE_CLICK_INSTALL);
$license = License::where('license_key', '=', $licenseKey)
->where('is_claimed', '=', false)
->where('product_id', '=', $productId)
->first();
if ($license) if ($license)
{ {
@ -454,7 +467,7 @@ class PaymentController extends \BaseController
$license->save(); $license->save();
} }
return 'valid'; return $productId == PRODUCT_ONE_CLICK_INSTALL ? 'valid' : $_ENV['INVOICE_DESIGNS'];
} }
else else
{ {

View File

@ -95,7 +95,7 @@ class QuoteController extends \BaseController {
'sizes' => Size::remember(DEFAULT_QUERY_CACHE)->orderBy('id')->get(), 'sizes' => Size::remember(DEFAULT_QUERY_CACHE)->orderBy('id')->get(),
'paymentTerms' => PaymentTerm::remember(DEFAULT_QUERY_CACHE)->orderBy('num_days')->get(['name', 'num_days']), 'paymentTerms' => PaymentTerm::remember(DEFAULT_QUERY_CACHE)->orderBy('num_days')->get(['name', 'num_days']),
'industries' => Industry::remember(DEFAULT_QUERY_CACHE)->orderBy('name')->get(), 'industries' => Industry::remember(DEFAULT_QUERY_CACHE)->orderBy('name')->get(),
'invoiceDesigns' => InvoiceDesign::remember(DEFAULT_QUERY_CACHE)->orderBy('id')->get(), 'invoiceDesigns' => InvoiceDesign::remember(DEFAULT_QUERY_CACHE, 'invoice_designs_cache')->where('id', '<=', Auth::user()->maxInvoiceDesignId())->orderBy('id')->get(),
'invoiceLabels' => Auth::user()->account->getInvoiceLabels() 'invoiceLabels' => Auth::user()->account->getInvoiceLabels()
]; ];
} }

View File

@ -0,0 +1,44 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddAffiliatePrice extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('affiliates', function($table)
{
$table->decimal('price', 7, 2)->nullable();
});
Schema::table('licenses', function($table)
{
$table->unsignedInteger('product_id')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('affiliates', function($table)
{
$table->dropColumn('price');
});
Schema::table('licenses', function($table)
{
$table->dropColumn('product_id');
});
}
}

View File

@ -77,6 +77,34 @@ App::before(function($request)
$locale = Session::get(SESSION_LOCALE, DEFAULT_LOCALE); $locale = Session::get(SESSION_LOCALE, DEFAULT_LOCALE);
App::setLocale($locale); App::setLocale($locale);
} }
$claimingLicense = Utils::startsWith($_SERVER['REQUEST_URI'], '/claim_license');
if (!$claimingLicense && Input::has('license_key') && Input::has('product_id'))
{
$licenseKey = Input::get('license_key');
$productId = Input::get('product_id');
if ($productId == PRODUCT_INVOICE_DESIGNS)
{
//$data = file_get_contents("http://ninja.dev/claim_license?license_key={$licenseKey}&product_id={$productId}");
$data = file_get_contents(NINJA_URL . "/claim_license?license_key={$licenseKey}&product_id={$productId}");
if ($data = json_decode($data))
{
foreach ($data as $item)
{
$design = new InvoiceDesign();
$design->id = $item->id;
$design->name = $item->name;
$design->javascript = $item->javascript;
$design->save();
}
Cache::forget('invoice_designs_cache');
Session::flash('message', trans('texts.bought_designs'));
}
}
}
}); });

View File

@ -449,4 +449,14 @@ return array(
'gateway_help_23' => 'Note: use your secret API key, not your publishable API key.', 'gateway_help_23' => 'Note: use your secret API key, not your publishable API key.',
'gateway_help_27' => ':link to sign up for TwoCheckout.', 'gateway_help_27' => ':link to sign up for TwoCheckout.',
'more_designs' => 'More designs',
'more_designs_title' => 'Additional Invoice Designs',
'more_designs_cloud_header' => 'Go Pro for more invoice designs',
'more_designs_cloud_text' => '',
'more_designs_self_host_header' => 'Get 6 more invoice designs for just $20',
'more_designs_self_host_text' => '',
'buy' => 'Buy',
'bought_designs' => 'Successfully added additional invoice designs',
); );

View File

@ -85,6 +85,11 @@ class User extends ConfideUser implements UserInterface, RemindableInterface
return $this->account->id == Utils::getDemoAccountId(); return $this->account->id == Utils::getDemoAccountId();
} }
public function maxInvoiceDesignId()
{
return $this->isPro() ? 10 : COUNT_FREE_DESIGNS;
}
public function getDisplayName() public function getDisplayName()
{ {
if ($this->getFullName()) if ($this->getFullName())

View File

@ -37,7 +37,7 @@ class ContactMailer extends Mailer {
'contactName' => $invitation->contact->getDisplayName(), 'contactName' => $invitation->contact->getDisplayName(),
'invoiceAmount' => Utils::formatMoney($invoice->amount, $invoice->client->currency_id), 'invoiceAmount' => Utils::formatMoney($invoice->amount, $invoice->client->currency_id),
'emailFooter' => $invoice->account->email_footer, 'emailFooter' => $invoice->account->email_footer,
'showNinjaFooter' => !$invoice->account->isPro() || !Utils::isNinjaProd() 'showNinjaFooter' => !$invoice->account->isPro()
]; ];
$fromEmail = $invitation->user->email; $fromEmail = $invitation->user->email;
@ -67,25 +67,31 @@ class ContactMailer extends Mailer {
'clientName' => $payment->client->getDisplayName(), 'clientName' => $payment->client->getDisplayName(),
'emailFooter' => $payment->account->email_footer, 'emailFooter' => $payment->account->email_footer,
'paymentAmount' => Utils::formatMoney($payment->amount, $payment->client->currency_id), 'paymentAmount' => Utils::formatMoney($payment->amount, $payment->client->currency_id),
'showNinjaFooter' => !$payment->account->isPro() || !Utils::isNinjaProd() 'showNinjaFooter' => !$payment->account->isPro()
]; ];
$user = $payment->invitation->user; $user = $payment->invitation->user;
$this->sendTo($payment->contact->email, $user->email, $user->getDisplayName(), $subject, $view, $data); $this->sendTo($payment->contact->email, $user->email, $user->getDisplayName(), $subject, $view, $data);
} }
public function sendLicensePaymentConfirmation($name, $email, $amount, $license) public function sendLicensePaymentConfirmation($name, $email, $amount, $license, $productId)
{ {
$view = 'payment_confirmation'; $view = 'payment_confirmation';
$subject = trans('texts.payment_subject'); $subject = trans('texts.payment_subject');
if ($productId == PRODUCT_ONE_CLICK_INSTALL) {
$message = "Softaculous install license: $license";
} else if ($productId == PRODUCT_INVOICE_DESIGNS) {
$message = "Invoice designs license: $license";
}
$data = [ $data = [
'accountName' => trans('texts.email_from'), 'accountName' => trans('texts.email_from'),
'clientName' => $name, 'clientName' => $name,
'emailFooter' => false, 'emailFooter' => false,
'paymentAmount' => Utils::formatMoney($amount, 1), 'paymentAmount' => Utils::formatMoney($amount, 1),
'showNinjaFooter' => false, 'showNinjaFooter' => false,
'emailMessage' => "Your license: $license", 'emailMessage' => $message,
]; ];
$this->sendTo($email, CONTACT_EMAIL, CONTACT_NAME, $subject, $view, $data); $this->sendTo($email, CONTACT_EMAIL, CONTACT_NAME, $subject, $view, $data);

View File

@ -13,7 +13,7 @@
*/ */
//apc_clear_cache(); //apc_clear_cache();
//Cache::flush(); Cache::flush();
//dd(DB::getQueryLog()); //dd(DB::getQueryLog());
//dd(Client::getPrivateId(1)); //dd(Client::getPrivateId(1));
@ -248,8 +248,11 @@ define('NINJA_URL', 'https://www.invoiceninja.com');
define('NINJA_VERSION', '1.4.0'); define('NINJA_VERSION', '1.4.0');
define('RELEASES_URL', 'https://github.com/hillelcoren/invoice-ninja/releases/'); define('RELEASES_URL', 'https://github.com/hillelcoren/invoice-ninja/releases/');
define('COUNT_FREE_DESIGNS', 4);
define('PRO_PLAN_PRICE', 50); define('PRO_PLAN_PRICE', 50);
define('LICENSE_PRICE', 30.00); define('PRODUCT_ONE_CLICK_INSTALL', 1);
define('PRODUCT_INVOICE_DESIGNS', 2);
define('DESIGNS_AFFILIATE_KEY', 'T3RS74');
define('USER_TYPE_SELF_HOST', 'SELF_HOST'); define('USER_TYPE_SELF_HOST', 'SELF_HOST');
define('USER_TYPE_CLOUD_HOST', 'CLOUD_HOST'); define('USER_TYPE_CLOUD_HOST', 'CLOUD_HOST');

View File

@ -17,8 +17,14 @@
function getDesignJavascript() { function getDesignJavascript() {
var id = $('#invoice_design_id').val(); var id = $('#invoice_design_id').val();
if (id == '-1') {
showMoreDesigns();
$('#invoice_design_id').val(1);
return invoiceDesigns[0].javascript;
} else {
return invoiceDesigns[id-1].javascript; return invoiceDesigns[id-1].javascript;
} }
}
function getPDFString() { function getPDFString() {
invoice.is_pro = {{ Auth::user()->isPro() ? 'true' : 'false' }}; invoice.is_pro = {{ Auth::user()->isPro() ? 'true' : 'false' }};
@ -64,8 +70,15 @@
{{ Former::populateField('hide_paid_to_date', intval($account->hide_paid_to_date)) }} {{ Former::populateField('hide_paid_to_date', intval($account->hide_paid_to_date)) }}
{{ Former::legend('invoice_design') }} {{ Former::legend('invoice_design') }}
{{ Former::select('invoice_design_id')->style('display:inline;width:120px')
->fromQuery($invoiceDesigns, 'name', 'id') }}
@if (InvoiceDesign::count() == COUNT_FREE_DESIGNS)
{{ Former::select('invoice_design_id')->style('display:inline;width:120px')->fromQuery($invoiceDesigns, 'name', 'id')->addOption(trans('texts.more_designs') . '...', '-1') }}
@else
{{ Former::select('invoice_design_id')->style('display:inline;width:120px')->fromQuery($invoiceDesigns, 'name', 'id') }}
@endif
{{ Former::text('primary_color') }} {{ Former::text('primary_color') }}
{{ Former::text('secondary_color') }} {{ Former::text('secondary_color') }}

View File

@ -265,10 +265,11 @@
</div> </div>
@if (InvoiceDesign::count() == COUNT_FREE_DESIGNS)
{{ Former::select('invoice_design_id')->style('display:inline;width:120px')->raw() {{ Former::select('invoice_design_id')->style('display:inline;width:150px')->raw()->fromQuery($invoiceDesigns, 'name', 'id')->data_bind("value: invoice_design_id")->addOption(trans('texts.more_designs') . '...', '-1') }}
->fromQuery($invoiceDesigns, 'name', 'id')->data_bind("value: invoice_design_id") }} @else
{{ Former::select('invoice_design_id')->style('display:inline;width:150px')->raw()->fromQuery($invoiceDesigns, 'name', 'id')->data_bind("value: invoice_design_id") }}
@endif
{{ Button::primary(trans('texts.download_pdf'), array('onclick' => 'onDownloadClick()'))->append_with_icon('download-alt'); }} {{ Button::primary(trans('texts.download_pdf'), array('onclick' => 'onDownloadClick()'))->append_with_icon('download-alt'); }}
@ -646,6 +647,7 @@
function getPDFString() { function getPDFString() {
var invoice = createInvoiceModel(); var invoice = createInvoiceModel();
var design = getDesignJavascript(); var design = getDesignJavascript();
if (!design) return;
var doc = generatePDF(invoice, design); var doc = generatePDF(invoice, design);
if (!doc) return; if (!doc) return;
return doc.output('datauristring'); return doc.output('datauristring');
@ -653,13 +655,21 @@
function getDesignJavascript() { function getDesignJavascript() {
var id = $('#invoice_design_id').val(); var id = $('#invoice_design_id').val();
if (id == '-1') {
showMoreDesigns();
$('#invoice_design_id').val(1);
return invoiceDesigns[0].javascript;
} else {
return invoiceDesigns[id-1].javascript; return invoiceDesigns[id-1].javascript;
} }
}
function onDownloadClick() { function onDownloadClick() {
trackUrl('/download_pdf'); trackUrl('/download_pdf');
var invoice = createInvoiceModel(); var invoice = createInvoiceModel();
var doc = generatePDF(invoice, true); var design = getDesignJavascript();
if (!design) return;
var doc = generatePDF(invoice, design, true);
doc.save('Invoice-' + $('#invoice_number').val() + '.pdf'); doc.save('Invoice-' + $('#invoice_number').val() + '.pdf');
} }

View File

@ -1,6 +1,54 @@
<iframe id="theFrame" style="display:none" frameborder="1" width="100%" height="{{ isset($pdfHeight) ? $pdfHeight : 1180 }}px"></iframe> <iframe id="theFrame" style="display:none" frameborder="1" width="100%" height="{{ isset($pdfHeight) ? $pdfHeight : 1180 }}px"></iframe>
<canvas id="theCanvas" style="display:none;width:100%;border:solid 1px #CCCCCC;"></canvas> <canvas id="theCanvas" style="display:none;width:100%;border:solid 1px #CCCCCC;"></canvas>
<div class="modal fade" id="moreDesignsModal" tabindex="-1" role="dialog" aria-labelledby="moreDesignsModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">{{ trans('texts.more_designs_title') }}</h4>
</div>
<div class="container">
@if (Utils::isNinja())
<h3>{{ trans('texts.more_designs_cloud_header') }}</h3>
<p>{{ trans('texts.more_designs_cloud_text') }}</p>
@else
<h3>{{ trans('texts.more_designs_self_host_header') }}</h3>
<p>{{ trans('texts.more_designs_self_host_text') }}</p>
@endif
</div>
<center id="designThumbs">
<p>&nbsp;</p>
<a href="/images/designs/business.png" data-lightbox="more-designs" data-title="Business"><img src="/images/designs/business_thumb.png"/></a>&nbsp;&nbsp;&nbsp;&nbsp;
<a href="/images/designs/creative.png" data-lightbox="more-designs" data-title="Creative"><img src="/images/designs/creative_thumb.png"/></a>&nbsp;&nbsp;&nbsp;&nbsp;
<a href="/images/designs/elegant.png" data-lightbox="more-designs" data-title="Elegant"><img src="/images/designs/elegant_thumb.png"/></a>
<p>&nbsp;</p>
<a href="/images/designs/hipster.png" data-lightbox="more-designs" data-title="Hipster"><img src="/images/designs/hipster_thumb.png"/></a>&nbsp;&nbsp;&nbsp;&nbsp;
<a href="/images/designs/playful.png" data-lightbox="more-designs" data-title="Playful"><img src="/images/designs/playful_thumb.png"/></a>&nbsp;&nbsp;&nbsp;&nbsp;
<a href="/images/designs/photo.png" data-lightbox="more-designs" data-title="Photo"><img src="/images/designs/photo_thumb.png"/></a>
<p>&nbsp;</p>
</center>
<div class="modal-footer" id="signUpFooter">
<button type="button" class="btn btn-default" data-dismiss="modal">{{ trans('texts.cancel') }}</button>
&nbsp;&nbsp;&nbsp;Coming soon
<!--
@if (Utils::isNinja())
<button type="button" class="btn btn-primary" onclick="submitProPlan()">{{ trans('texts.go_pro') }}</button>
@else
<button type="button" class="btn btn-primary" onclick="buyDesigns()">{{ trans('texts.buy') }}</button>
@endif
-->
</div>
</div>
</div>
</div>
<script type="text/javascript"> <script type="text/javascript">
window.logoImages = {}; window.logoImages = {};
@ -70,4 +118,12 @@
} }
} }
function showMoreDesigns() {
$('#moreDesignsModal').modal('show');
}
function buyDesigns() {
window.open('{{ NINJA_URL }}/license?return_url=' + window.location + '&affiliate_key={{ DESIGNS_AFFILIATE_KEY }}&product_id={{ PRODUCT_INVOICE_DESIGNS }}');
}
</script> </script>

View File

@ -17,7 +17,8 @@
"typeahead.js": "~0.9.3", "typeahead.js": "~0.9.3",
"accounting": "~0.*", "accounting": "~0.*",
"spectrum": "~1.3.4", "spectrum": "~1.3.4",
"d3": "~3.4.11" "d3": "~3.4.11",
"lightbox2": "~2.7.1"
}, },
"resolutions": { "resolutions": {
"jquery": "~1.11" "jquery": "~1.11"

View File

@ -1773,6 +1773,217 @@ See http://bgrins.github.io/spectrum/themes/ for instructions.
border-radius: 6px; border-radius: 6px;
line-height: 1.33; line-height: 1.33;
} }
/* Preload images */
body:after {
content: url(images/lightbox/close.png) url(images/lightbox/loading.gif) url(images/lightbox/prev.png) url(images/lightbox/next.png);
display: none;
}
.lightboxOverlay {
position: absolute;
top: 0;
left: 0;
z-index: 9999;
background-color: black;
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=80);
opacity: 0.8;
display: none;
}
.lightbox {
position: absolute;
left: 0;
width: 100%;
z-index: 10000;
text-align: center;
line-height: 0;
font-weight: normal;
}
.lightbox .lb-image {
display: block;
height: auto;
max-width: inherit;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
-ms-border-radius: 3px;
-o-border-radius: 3px;
border-radius: 3px;
}
.lightbox a img {
border: none;
}
.lb-outerContainer {
position: relative;
background-color: white;
*zoom: 1;
width: 250px;
height: 250px;
margin: 0 auto;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
-ms-border-radius: 4px;
-o-border-radius: 4px;
border-radius: 4px;
}
.lb-outerContainer:after {
content: "";
display: table;
clear: both;
}
.lb-container {
padding: 4px;
}
.lb-loader {
position: absolute;
top: 43%;
left: 0;
height: 25%;
width: 100%;
text-align: center;
line-height: 0;
}
.lb-cancel {
display: block;
width: 32px;
height: 32px;
margin: 0 auto;
background: url(images/lightbox/loading.gif) no-repeat;
}
.lb-nav {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
z-index: 10;
}
.lb-container > .nav {
left: 0;
}
.lb-nav a {
outline: none;
background-image: url('data:image/gif;base64,R0lGODlhAQABAPAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==');
}
.lb-prev, .lb-next {
height: 100%;
cursor: pointer;
display: block;
}
.lb-nav a.lb-prev {
width: 34%;
left: 0;
float: left;
background: url(images/lightbox/prev.png) left 48% no-repeat;
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0);
opacity: 0;
-webkit-transition: opacity 0.6s;
-moz-transition: opacity 0.6s;
-o-transition: opacity 0.6s;
transition: opacity 0.6s;
}
.lb-nav a.lb-prev:hover {
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100);
opacity: 1;
}
.lb-nav a.lb-next {
width: 64%;
right: 0;
float: right;
background: url(images/lightbox/next.png) right 48% no-repeat;
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0);
opacity: 0;
-webkit-transition: opacity 0.6s;
-moz-transition: opacity 0.6s;
-o-transition: opacity 0.6s;
transition: opacity 0.6s;
}
.lb-nav a.lb-next:hover {
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100);
opacity: 1;
}
.lb-dataContainer {
margin: 0 auto;
padding-top: 5px;
*zoom: 1;
width: 100%;
-moz-border-radius-bottomleft: 4px;
-webkit-border-bottom-left-radius: 4px;
border-bottom-left-radius: 4px;
-moz-border-radius-bottomright: 4px;
-webkit-border-bottom-right-radius: 4px;
border-bottom-right-radius: 4px;
}
.lb-dataContainer:after {
content: "";
display: table;
clear: both;
}
.lb-data {
padding: 0 4px;
color: #ccc;
}
.lb-data .lb-details {
width: 85%;
float: left;
text-align: left;
line-height: 1.1em;
}
.lb-data .lb-caption {
font-size: 13px;
font-weight: bold;
line-height: 1em;
}
.lb-data .lb-number {
display: block;
clear: left;
padding-bottom: 1em;
font-size: 12px;
color: #999999;
}
.lb-data .lb-close {
display: block;
float: right;
width: 30px;
height: 30px;
background: url(images/lightbox/close.png) top right no-repeat;
text-align: right;
outline: none;
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=70);
opacity: 0.7;
-webkit-transition: opacity 0.2s;
-moz-transition: opacity 0.2s;
-o-transition: opacity 0.2s;
transition: opacity 0.2s;
}
.lb-data .lb-close:hover {
cursor: pointer;
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100);
opacity: 1;
}
body { background: #f8f8f8 !important; body { background: #f8f8f8 !important;
font-family: 'Roboto', sans-serif; font-family: 'Roboto', sans-serif;
} }
@ -2482,6 +2693,10 @@ box-shadow: 0px 0px 15px 0px rgba(0, 5, 5, 0.2);
.plans-table a .cta h2 span {background: #1e84a5;} .plans-table a .cta h2 span {background: #1e84a5;}
#designThumbs img {
border: 1px solid #CCCCCC;
}
@media screen and (min-width: 992px) { @media screen and (min-width: 992px) {
.hide-desktop {display: none;} .hide-desktop {display: none;}
} }

File diff suppressed because one or more lines are too long

View File

@ -707,6 +707,10 @@ box-shadow: 0px 0px 15px 0px rgba(0, 5, 5, 0.2);
.plans-table a .cta h2 span {background: #1e84a5;} .plans-table a .cta h2 span {background: #1e84a5;}
#designThumbs img {
border: 1px solid #CCCCCC;
}
@media screen and (min-width: 992px) { @media screen and (min-width: 992px) {
.hide-desktop {display: none;} .hide-desktop {display: none;}
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 280 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -958,7 +958,6 @@ function getInvoiceTaxRate(invoice) {
return tax; return tax;
} }
/*
function displayInvoiceHeader(doc, invoice, layout) { function displayInvoiceHeader(doc, invoice, layout) {
var costX = layout.unitCostRight - (doc.getStringUnitWidth(invoiceLabels.unit_cost) * doc.internal.getFontSize()); var costX = layout.unitCostRight - (doc.getStringUnitWidth(invoiceLabels.unit_cost) * doc.internal.getFontSize());
@ -966,6 +965,15 @@ function displayInvoiceHeader(doc, invoice, layout) {
var taxX = layout.taxRight - (doc.getStringUnitWidth(invoiceLabels.tax) * doc.internal.getFontSize()); var taxX = layout.taxRight - (doc.getStringUnitWidth(invoiceLabels.tax) * doc.internal.getFontSize());
var totalX = layout.lineTotalRight - (doc.getStringUnitWidth(invoiceLabels.line_total) * doc.internal.getFontSize()); var totalX = layout.lineTotalRight - (doc.getStringUnitWidth(invoiceLabels.line_total) * doc.internal.getFontSize());
if (invoice.invoice_design_id == 6 || invoice.invoice_design_id == 8 || invoice.invoice_design_id == 10) {
invoiceLabels.item = invoiceLabels.item.toUpperCase();
invoiceLabels.description = invoiceLabels.description.toUpperCase();
invoiceLabels.unit_cost = invoiceLabels.unit_cost.toUpperCase();
invoiceLabels.quantity = invoiceLabels.quantity.toUpperCase();
invoiceLabels.line_total = invoiceLabels.total.toUpperCase();
invoiceLabels.tax = invoiceLabels.tax.toUpperCase();
}
doc.text(layout.marginLeft, layout.tableTop, invoiceLabels.item); doc.text(layout.marginLeft, layout.tableTop, invoiceLabels.item);
doc.text(layout.descriptionLeft, layout.tableTop, invoiceLabels.description); doc.text(layout.descriptionLeft, layout.tableTop, invoiceLabels.description);
doc.text(costX, layout.tableTop, invoiceLabels.unit_cost); doc.text(costX, layout.tableTop, invoiceLabels.unit_cost);
@ -978,257 +986,6 @@ function displayInvoiceHeader(doc, invoice, layout) {
{ {
doc.text(taxX, layout.tableTop, invoiceLabels.tax); doc.text(taxX, layout.tableTop, invoiceLabels.tax);
} }
}
function displayInvoiceItems(doc, invoice, layout) {
doc.setFontType("normal");
var line = 1;
var total = 0;
var shownItem = false;
var currencyId = invoice && invoice.client ? invoice.client.currency_id : 1;
var tableTop = layout.tableTop;
var hideQuantity = invoice.account.hide_quantity == '1';
doc.setFontSize(8);
for (var i=0; i<invoice.invoice_items.length; i++) {
var item = invoice.invoice_items[i];
var numLines = doc.splitTextToSize(item.notes, 200).length + 2;
//console.log('num lines %s', numLines);
var y = tableTop + (line * layout.tableRowHeight) + (2 * layout.tablePadding);
var top = y - layout.tablePadding;
var newTop = top + (numLines * layout.tableRowHeight);
if (newTop > 770) {
line = 0;
tableTop = layout.accountTop + layout.tablePadding;
y = tableTop;
top = y - layout.tablePadding;
newTop = top + (numLines * layout.tableRowHeight);
doc.addPage();
}
var left = layout.marginLeft - layout.tablePadding;
var width = layout.marginRight + layout.tablePadding;
var cost = formatMoney(item.cost, currencyId, true);
var qty = NINJA.parseFloat(item.qty) ? NINJA.parseFloat(item.qty) + '' : '';
var notes = item.notes;
var productKey = item.product_key;
var tax = 0;
if (item.tax && parseFloat(item.tax.rate)) {
tax = parseFloat(item.tax.rate);
} else if (item.tax_rate && parseFloat(item.tax_rate)) {
tax = parseFloat(item.tax_rate);
}
// show at most one blank line
if (shownItem && (!cost || cost == '0.00') && !notes && !productKey) {
continue;
}
shownItem = true;
// process date variables
notes = processVariables(notes);
productKey = processVariables(productKey);
var lineTotal = NINJA.parseFloat(item.cost) * NINJA.parseFloat(item.qty);
if (tax) {
lineTotal += lineTotal * tax / 100;
}
if (lineTotal) {
total += lineTotal;
}
lineTotal = formatMoney(lineTotal, currencyId);
var costX = layout.unitCostRight - (doc.getStringUnitWidth(cost) * doc.internal.getFontSize());
var qtyX = layout.qtyRight - (doc.getStringUnitWidth(qty) * doc.internal.getFontSize());
var taxX = layout.taxRight - (doc.getStringUnitWidth(tax+'%') * doc.internal.getFontSize());
var totalX = layout.lineTotalRight - (doc.getStringUnitWidth(lineTotal) * doc.internal.getFontSize());
line += numLines;
if (invoice.invoice_design_id == 1) {
if (i%2 == 0) {
doc.setDrawColor(255,255,255);
doc.setFillColor(246,246,246);
doc.rect(left, top, width-left, newTop-top, 'FD');
doc.setLineWidth(0.3);
doc.setDrawColor(200,200,200);
doc.line(left, top, width, top);
doc.line(left, newTop, width, newTop);
}
} else if (invoice.invoice_design_id == 2) {
if (i%2 == 0) {
left = 0;
width = 1000;
doc.setDrawColor(255,255,255);
doc.setFillColor(235,235,235);
doc.rect(left, top, width-left, newTop-top, 'FD');
}
} else if (invoice.invoice_design_id == 5) {
if (i%2 == 0) {
doc.setDrawColor(255,255,255);
doc.setFillColor(247,247,247);
doc.rect(left, top, width-left+17, newTop-top, 'FD');
doc.setLineWidth(0.3);
doc.setDrawColor(255,255,255);
} else {
doc.setDrawColor(255,255,255);
doc.setFillColor(232,232,232);
doc.rect(left, top, width-left+17, newTop-top, 'FD');
doc.setLineWidth(0.3);
doc.setDrawColor(255,255,255);
}
} else {
doc.setLineWidth(0.3);
doc.setDrawColor(150,150,150);
doc.line(left, newTop, width, newTop);
}
y += 4;
if (invoice.invoice_design_id == 1) {
SetPdfColor('LightBlue', doc, 'primary');
} else if (invoice.invoice_design_id == 2) {
SetPdfColor('SomeGreen', doc, 'primary');
} else if (invoice.invoice_design_id == 3) {
doc.setFontType('bold');
} else if (invoice.invoice_design_id == 4) {
SetPdfColor('Black', doc);
} else if (invoice.invoice_design_id == 5) {
SetPdfColor('Black', doc);
}
var splitTitle = doc.splitTextToSize(productKey, 60);
doc.text(layout.marginLeft, y+2, splitTitle);
if (invoice.invoice_design_id == 5) {
doc.setDrawColor(255, 255, 255);
doc.setLineWidth(1);
doc.line(layout.descriptionLeft-8, y-16,layout.descriptionLeft-8, y+55);
doc.setDrawColor(255, 255, 255);
doc.setLineWidth(1);
doc.line(costX-30, y-16,costX-30, y+55);
doc.setDrawColor(255, 255, 255);
doc.setLineWidth(1);
doc.line(qtyX-45, y-16,qtyX-45, y+55);
if (invoice.has_taxes) {
doc.setDrawColor(255, 255, 255);
doc.setLineWidth(1);
doc.line(taxX-15, y-16,taxX-15, y+55);
}
doc.setDrawColor(255, 255, 255);
doc.setLineWidth(1);
doc.line(totalX-27, y-16,totalX-27, y+55);
}
SetPdfColor('Black', doc);
doc.setFontType('normal');
doc.text(layout.descriptionLeft, y+2, notes);
doc.text(costX, y+2, cost);
if (!hideQuantity) {
doc.text(qtyX, y+2, qty);
}
doc.text(totalX, y+2, lineTotal);
if (tax) {
doc.text(taxX, y+2, tax+'%');
}
}
y = tableTop + (line * layout.tableRowHeight) + (3 * layout.tablePadding);
var cutoff = 700;
if (invoice.terms) {
cutoff -= 50;
}
if (invoice.public_notes) {
cutoff -= 50;
}
if (y > cutoff) {
doc.addPage();
return layout.marginLeft;
}
return y;
}
*/
function displayInvoiceHeader(doc, invoice, layout) {
var costX = layout.unitCostRight - (doc.getStringUnitWidth(invoiceLabels.unit_cost) * doc.internal.getFontSize());
var qtyX = layout.qtyRight - (doc.getStringUnitWidth(invoiceLabels.quantity) * doc.internal.getFontSize());
var taxX = layout.taxRight - (doc.getStringUnitWidth(invoiceLabels.tax) * doc.internal.getFontSize());
var totalX = layout.lineTotalRight - (doc.getStringUnitWidth(invoiceLabels.line_total) * doc.internal.getFontSize());
if(invoice.invoice_design_id == 6) {
doc.setFontType('normal');
doc.text(layout.marginLeft, layout.tableTop, invoiceLabels.item.toUpperCase());
doc.text(layout.descriptionLeft, layout.tableTop, invoiceLabels.description.toUpperCase());
doc.text(costX, layout.tableTop, ' UNIT');
if (invoice.account.hide_quantity != '1') {
doc.text(qtyX, layout.tableTop, invoiceLabels.quantity.toUpperCase());
}
doc.text(totalX, layout.tableTop, ' TOTAL');
if (invoice.has_taxes)
{
doc.text(taxX, layout.tableTop, invoiceLabels.tax.toUpperCase());
}
} else if(invoice.invoice_design_id == 8) {
doc.setFontType('bold');
doc.text(layout.marginLeft, layout.tableTop, invoiceLabels.item.toUpperCase());
doc.text(layout.descriptionLeft, layout.tableTop, invoiceLabels.description.toUpperCase());
doc.text(costX, layout.tableTop, ' UNIT');
if (invoice.account.hide_quantity != '1') {
doc.text(qtyX, layout.tableTop, invoiceLabels.quantity.toUpperCase());
}
doc.text(totalX, layout.tableTop, ' TOTAL');
if (invoice.has_taxes)
{
doc.text(taxX, layout.tableTop, invoiceLabels.tax.toUpperCase());
}
} else if(invoice.invoice_design_id == 10) {
doc.setFontType('bold');
doc.setTextColor(63,60,60);
doc.text(layout.marginLeft, layout.tableTop, invoiceLabels.item.toUpperCase());
doc.text(layout.descriptionLeft, layout.tableTop, invoiceLabels.description.toUpperCase());
doc.text(costX, layout.tableTop, invoiceLabels.unit_cost.toUpperCase());
if (invoice.account.hide_quantity != '1') {
doc.text(qtyX, layout.tableTop, invoiceLabels.quantity.toUpperCase());
}
doc.text(totalX, layout.tableTop, invoiceLabels.line_total.toUpperCase());
if (invoice.has_taxes)
{
doc.text(taxX, layout.tableTop, invoiceLabels.tax.toUpperCase());
}
} else {
doc.text(layout.marginLeft, layout.tableTop, invoiceLabels.item);
doc.text(layout.descriptionLeft, layout.tableTop, invoiceLabels.description);
doc.text(costX, layout.tableTop, invoiceLabels.unit_cost);
if (invoice.account.hide_quantity != '1') {
doc.text(qtyX, layout.tableTop, invoiceLabels.quantity);
}
doc.text(totalX, layout.tableTop, invoiceLabels.line_total);
if (invoice.has_taxes)
{
doc.text(taxX, layout.tableTop, invoiceLabels.tax);
}
}
} }
@ -1411,7 +1168,9 @@ function displayInvoiceItems(doc, invoice, layout) {
doc.line(qtyX-45, y-16,qtyX-45, y+55); doc.line(qtyX-45, y-16,qtyX-45, y+55);
if (invoice.has_taxes) {
doc.line(taxX-15, y-16,taxX-15, y+55); doc.line(taxX-15, y-16,taxX-15, y+55);
}
doc.line(totalX-27, y-16,totalX-27, y+55); doc.line(totalX-27, y-16,totalX-27, y+55);
@ -1428,7 +1187,9 @@ function displayInvoiceItems(doc, invoice, layout) {
doc.line(qtyX-45, y-60,qtyX-45, y+20); doc.line(qtyX-45, y-60,qtyX-45, y+20);
doc.line(taxX-15, y-60,taxX-15, y+20); if (invoice.has_taxes) {
doc.line(taxX-10, y-60,taxX-10, y+20);
}
doc.line(totalX-27, y-60,totalX-27, y+120); doc.line(totalX-27, y-60,totalX-27, y+120);