mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Merge remote-tracking branch 'upstream/develop' into wepay-integration
This commit is contained in:
commit
49d47bb9e3
@ -5,6 +5,7 @@ sudo: true
|
|||||||
php:
|
php:
|
||||||
- 5.5.9
|
- 5.5.9
|
||||||
# - 5.6
|
# - 5.6
|
||||||
|
# - 5.6
|
||||||
# - 7.0
|
# - 7.0
|
||||||
# - hhvm
|
# - hhvm
|
||||||
|
|
||||||
@ -27,6 +28,7 @@ before_install:
|
|||||||
# set GitHub token and update composer
|
# set GitHub token and update composer
|
||||||
- if [ -n "$GH_TOKEN" ]; then composer config github-oauth.github.com ${GH_TOKEN}; fi;
|
- if [ -n "$GH_TOKEN" ]; then composer config github-oauth.github.com ${GH_TOKEN}; fi;
|
||||||
- composer self-update && composer -V
|
- composer self-update && composer -V
|
||||||
|
# - export USE_ZEND_ALLOC=0
|
||||||
|
|
||||||
install:
|
install:
|
||||||
# install Composer dependencies
|
# install Composer dependencies
|
||||||
@ -66,6 +68,7 @@ before_script:
|
|||||||
script:
|
script:
|
||||||
- php ./vendor/codeception/codeception/codecept run --debug acceptance AllPagesCept.php
|
- php ./vendor/codeception/codeception/codecept run --debug acceptance AllPagesCept.php
|
||||||
- php ./vendor/codeception/codeception/codecept run --debug acceptance APICest.php
|
- php ./vendor/codeception/codeception/codecept run --debug acceptance APICest.php
|
||||||
|
- php ./vendor/codeception/codeception/codecept run --debug acceptance TaxRatesCest.php
|
||||||
- php ./vendor/codeception/codeception/codecept run --debug acceptance CheckBalanceCest.php
|
- php ./vendor/codeception/codeception/codecept run --debug acceptance CheckBalanceCest.php
|
||||||
- php ./vendor/codeception/codeception/codecept run --debug acceptance ClientCest.php
|
- php ./vendor/codeception/codeception/codecept run --debug acceptance ClientCest.php
|
||||||
- php ./vendor/codeception/codeception/codecept run --debug acceptance ExpenseCest.php
|
- php ./vendor/codeception/codeception/codecept run --debug acceptance ExpenseCest.php
|
||||||
@ -76,7 +79,6 @@ script:
|
|||||||
- php ./vendor/codeception/codeception/codecept run acceptance OnlinePaymentCest.php
|
- php ./vendor/codeception/codeception/codecept run acceptance OnlinePaymentCest.php
|
||||||
- php ./vendor/codeception/codeception/codecept run --debug acceptance PaymentCest.php
|
- php ./vendor/codeception/codeception/codecept run --debug acceptance PaymentCest.php
|
||||||
- php ./vendor/codeception/codeception/codecept run --debug acceptance TaskCest.php
|
- php ./vendor/codeception/codeception/codecept run --debug acceptance TaskCest.php
|
||||||
- php ./vendor/codeception/codeception/codecept run --debug acceptance TaxRatesCest.php
|
|
||||||
|
|
||||||
#- sed -i 's/NINJA_DEV=true/NINJA_PROD=true/g' .env
|
#- sed -i 's/NINJA_DEV=true/NINJA_PROD=true/g' .env
|
||||||
#- php ./vendor/codeception/codeception/codecept run acceptance GoProCest.php
|
#- php ./vendor/codeception/codeception/codecept run acceptance GoProCest.php
|
||||||
@ -96,4 +98,4 @@ after_script:
|
|||||||
notifications:
|
notifications:
|
||||||
email:
|
email:
|
||||||
on_success: never
|
on_success: never
|
||||||
on_failure: change
|
on_failure: change
|
||||||
|
@ -154,7 +154,7 @@ class CheckData extends Command {
|
|||||||
$clients->where('clients.id', '=', $this->option('client_id'));
|
$clients->where('clients.id', '=', $this->option('client_id'));
|
||||||
} else {
|
} else {
|
||||||
$clients->where('invoices.is_deleted', '=', 0)
|
$clients->where('invoices.is_deleted', '=', 0)
|
||||||
->where('invoices.is_quote', '=', 0)
|
->where('invoices.invoice_type_id', '=', INVOICE_TYPE_STANDARD)
|
||||||
->where('invoices.is_recurring', '=', 0)
|
->where('invoices.is_recurring', '=', 0)
|
||||||
->havingRaw('abs(clients.balance - sum(invoices.balance)) > .01 and clients.balance != 999999999.9999');
|
->havingRaw('abs(clients.balance - sum(invoices.balance)) > .01 and clients.balance != 999999999.9999');
|
||||||
}
|
}
|
||||||
@ -184,7 +184,7 @@ class CheckData extends Command {
|
|||||||
if ($activity->invoice_id) {
|
if ($activity->invoice_id) {
|
||||||
$invoice = DB::table('invoices')
|
$invoice = DB::table('invoices')
|
||||||
->where('id', '=', $activity->invoice_id)
|
->where('id', '=', $activity->invoice_id)
|
||||||
->first(['invoices.amount', 'invoices.is_recurring', 'invoices.is_quote', 'invoices.deleted_at', 'invoices.id', 'invoices.is_deleted']);
|
->first(['invoices.amount', 'invoices.is_recurring', 'invoices.invoice_type_id', 'invoices.deleted_at', 'invoices.id', 'invoices.is_deleted']);
|
||||||
|
|
||||||
// Check if this invoice was once set as recurring invoice
|
// Check if this invoice was once set as recurring invoice
|
||||||
if ($invoice && !$invoice->is_recurring && DB::table('invoices')
|
if ($invoice && !$invoice->is_recurring && DB::table('invoices')
|
||||||
@ -221,14 +221,14 @@ class CheckData extends Command {
|
|||||||
&& $invoice->amount > 0;
|
&& $invoice->amount > 0;
|
||||||
|
|
||||||
// **Fix for allowing converting a recurring invoice to a normal one without updating the balance**
|
// **Fix for allowing converting a recurring invoice to a normal one without updating the balance**
|
||||||
if ($noAdjustment && !$invoice->is_quote && !$invoice->is_recurring) {
|
if ($noAdjustment && $invoice->invoice_type_id == INVOICE_TYPE_STANDARD && !$invoice->is_recurring) {
|
||||||
$this->info("No adjustment for new invoice:{$activity->invoice_id} amount:{$invoice->amount} isQuote:{$invoice->is_quote} isRecurring:{$invoice->is_recurring}");
|
$this->info("No adjustment for new invoice:{$activity->invoice_id} amount:{$invoice->amount} invoiceTypeId:{$invoice->invoice_type_id} isRecurring:{$invoice->is_recurring}");
|
||||||
$foundProblem = true;
|
$foundProblem = true;
|
||||||
$clientFix += $invoice->amount;
|
$clientFix += $invoice->amount;
|
||||||
$activityFix = $invoice->amount;
|
$activityFix = $invoice->amount;
|
||||||
// **Fix for updating balance when creating a quote or recurring invoice**
|
// **Fix for updating balance when creating a quote or recurring invoice**
|
||||||
} elseif ($activity->adjustment != 0 && ($invoice->is_quote || $invoice->is_recurring)) {
|
} elseif ($activity->adjustment != 0 && ($invoice->invoice_type_id == INVOICE_TYPE_QUOTE || $invoice->is_recurring)) {
|
||||||
$this->info("Incorrect adjustment for new invoice:{$activity->invoice_id} adjustment:{$activity->adjustment} isQuote:{$invoice->is_quote} isRecurring:{$invoice->is_recurring}");
|
$this->info("Incorrect adjustment for new invoice:{$activity->invoice_id} adjustment:{$activity->adjustment} invoiceTypeId:{$invoice->invoice_type_id} isRecurring:{$invoice->is_recurring}");
|
||||||
$foundProblem = true;
|
$foundProblem = true;
|
||||||
$clientFix -= $activity->adjustment;
|
$clientFix -= $activity->adjustment;
|
||||||
$activityFix = 0;
|
$activityFix = 0;
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
<?php namespace App\Console\Commands;
|
<?php namespace App\Console\Commands;
|
||||||
|
|
||||||
|
|
||||||
|
use Utils;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use Symfony\Component\Console\Input\InputOption;
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
use Symfony\Component\Console\Input\InputArgument;
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
|
@ -597,8 +597,8 @@ class AccountController extends BaseController
|
|||||||
|
|
||||||
// sample invoice to help determine variables
|
// sample invoice to help determine variables
|
||||||
$invoice = Invoice::scope()
|
$invoice = Invoice::scope()
|
||||||
|
->invoiceType(INVOICE_TYPE_STANDARD)
|
||||||
->with('client', 'account')
|
->with('client', 'account')
|
||||||
->where('is_quote', '=', false)
|
|
||||||
->where('is_recurring', '=', false)
|
->where('is_recurring', '=', false)
|
||||||
->first();
|
->first();
|
||||||
|
|
||||||
@ -799,11 +799,7 @@ class AccountController extends BaseController
|
|||||||
private function saveTaxRates()
|
private function saveTaxRates()
|
||||||
{
|
{
|
||||||
$account = Auth::user()->account;
|
$account = Auth::user()->account;
|
||||||
|
$account->fill(Input::all());
|
||||||
$account->invoice_taxes = Input::get('invoice_taxes') ? true : false;
|
|
||||||
$account->invoice_item_taxes = Input::get('invoice_item_taxes') ? true : false;
|
|
||||||
$account->show_item_taxes = Input::get('show_item_taxes') ? true : false;
|
|
||||||
$account->default_tax_rate_id = Input::get('default_tax_rate_id');
|
|
||||||
$account->save();
|
$account->save();
|
||||||
|
|
||||||
Session::flash('message', trans('texts.updated_settings'));
|
Session::flash('message', trans('texts.updated_settings'));
|
||||||
@ -1268,6 +1264,10 @@ class AccountController extends BaseController
|
|||||||
$account = Auth::user()->account;
|
$account = Auth::user()->account;
|
||||||
\Log::info("Canceled Account: {$account->name} - {$user->email}");
|
\Log::info("Canceled Account: {$account->name} - {$user->email}");
|
||||||
|
|
||||||
|
Document::scope()->each(function($item, $key) {
|
||||||
|
$item->delete();
|
||||||
|
});
|
||||||
|
|
||||||
$this->accountRepo->unlinkAccount($account);
|
$this->accountRepo->unlinkAccount($account);
|
||||||
if ($account->company->accounts->count() == 1) {
|
if ($account->company->accounts->count() == 1) {
|
||||||
$account->company->forceDelete();
|
$account->company->forceDelete();
|
||||||
|
@ -262,6 +262,12 @@ class AppController extends BaseController
|
|||||||
if (!Utils::isNinjaProd()) {
|
if (!Utils::isNinjaProd()) {
|
||||||
try {
|
try {
|
||||||
set_time_limit(60 * 5);
|
set_time_limit(60 * 5);
|
||||||
|
Artisan::call('clear-compiled');
|
||||||
|
Artisan::call('cache:clear');
|
||||||
|
Artisan::call('debugbar:clear');
|
||||||
|
Artisan::call('route:clear');
|
||||||
|
Artisan::call('view:clear');
|
||||||
|
Artisan::call('config:clear');
|
||||||
Artisan::call('optimize', array('--force' => true));
|
Artisan::call('optimize', array('--force' => true));
|
||||||
Cache::flush();
|
Cache::flush();
|
||||||
Session::flush();
|
Session::flush();
|
||||||
|
@ -136,7 +136,7 @@ class ClientController extends BaseController
|
|||||||
'credit' => $client->getTotalCredit(),
|
'credit' => $client->getTotalCredit(),
|
||||||
'title' => trans('texts.view_client'),
|
'title' => trans('texts.view_client'),
|
||||||
'hasRecurringInvoices' => Invoice::scope()->where('is_recurring', '=', true)->whereClientId($client->id)->count() > 0,
|
'hasRecurringInvoices' => Invoice::scope()->where('is_recurring', '=', true)->whereClientId($client->id)->count() > 0,
|
||||||
'hasQuotes' => Invoice::scope()->where('is_quote', '=', true)->whereClientId($client->id)->count() > 0,
|
'hasQuotes' => Invoice::scope()->invoiceType(INVOICE_TYPE_QUOTE)->whereClientId($client->id)->count() > 0,
|
||||||
'hasTasks' => Task::scope()->whereClientId($client->id)->count() > 0,
|
'hasTasks' => Task::scope()->whereClientId($client->id)->count() > 0,
|
||||||
'gatewayLink' => $client->getGatewayLink($accountGateway),
|
'gatewayLink' => $client->getGatewayLink($accountGateway),
|
||||||
'gateway' => $accountGateway
|
'gateway' => $accountGateway
|
||||||
|
@ -63,7 +63,7 @@ class ClientPortalController extends BaseController
|
|||||||
|
|
||||||
if (!Input::has('phantomjs') && !Input::has('silent') && !Session::has($invitationKey)
|
if (!Input::has('phantomjs') && !Input::has('silent') && !Session::has($invitationKey)
|
||||||
&& (!Auth::check() || Auth::user()->account_id != $invoice->account_id)) {
|
&& (!Auth::check() || Auth::user()->account_id != $invoice->account_id)) {
|
||||||
if ($invoice->is_quote) {
|
if ($invoice->isType(INVOICE_TYPE_QUOTE)) {
|
||||||
event(new QuoteInvitationWasViewed($invoice, $invitation));
|
event(new QuoteInvitationWasViewed($invoice, $invitation));
|
||||||
} else {
|
} else {
|
||||||
event(new InvoiceInvitationWasViewed($invoice, $invitation));
|
event(new InvoiceInvitationWasViewed($invoice, $invitation));
|
||||||
|
@ -9,7 +9,7 @@ class DashboardApiController extends BaseAPIController
|
|||||||
{
|
{
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
$view_all = !Auth::user()->hasPermission('view_all');
|
$view_all = Auth::user()->hasPermission('view_all');
|
||||||
$user_id = Auth::user()->id;
|
$user_id = Auth::user()->id;
|
||||||
|
|
||||||
// total_income, billed_clients, invoice_sent and active_clients
|
// total_income, billed_clients, invoice_sent and active_clients
|
||||||
@ -24,7 +24,7 @@ class DashboardApiController extends BaseAPIController
|
|||||||
->where('clients.is_deleted', '=', false)
|
->where('clients.is_deleted', '=', false)
|
||||||
->where('invoices.is_deleted', '=', false)
|
->where('invoices.is_deleted', '=', false)
|
||||||
->where('invoices.is_recurring', '=', false)
|
->where('invoices.is_recurring', '=', false)
|
||||||
->where('invoices.is_quote', '=', false);
|
->where('invoices.invoice_type_id', '=', false);
|
||||||
|
|
||||||
if(!$view_all){
|
if(!$view_all){
|
||||||
$metrics = $metrics->where(function($query) use($user_id){
|
$metrics = $metrics->where(function($query) use($user_id){
|
||||||
@ -62,7 +62,7 @@ class DashboardApiController extends BaseAPIController
|
|||||||
->where('accounts.id', '=', Auth::user()->account_id)
|
->where('accounts.id', '=', Auth::user()->account_id)
|
||||||
->where('clients.is_deleted', '=', false)
|
->where('clients.is_deleted', '=', false)
|
||||||
->where('invoices.is_deleted', '=', false)
|
->where('invoices.is_deleted', '=', false)
|
||||||
->where('invoices.is_quote', '=', false)
|
->where('invoices.invoice_type_id', '=', INVOICE_TYPE_STANDARD)
|
||||||
->where('invoices.is_recurring', '=', false);
|
->where('invoices.is_recurring', '=', false);
|
||||||
|
|
||||||
if(!$view_all){
|
if(!$view_all){
|
||||||
@ -106,7 +106,7 @@ class DashboardApiController extends BaseAPIController
|
|||||||
$pastDue = $pastDue->where('invoices.user_id', '=', $user_id);
|
$pastDue = $pastDue->where('invoices.user_id', '=', $user_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
$pastDue = $pastDue->select(['invoices.due_date', 'invoices.balance', 'invoices.public_id', 'invoices.invoice_number', 'clients.name as client_name', 'contacts.email', 'contacts.first_name', 'contacts.last_name', 'clients.currency_id', 'clients.public_id as client_public_id', 'clients.user_id as client_user_id', 'is_quote'])
|
$pastDue = $pastDue->select(['invoices.due_date', 'invoices.balance', 'invoices.public_id', 'invoices.invoice_number', 'clients.name as client_name', 'contacts.email', 'contacts.first_name', 'contacts.last_name', 'clients.currency_id', 'clients.public_id as client_public_id', 'clients.user_id as client_user_id', 'invoice_type_id'])
|
||||||
->orderBy('invoices.due_date', 'asc')
|
->orderBy('invoices.due_date', 'asc')
|
||||||
->take(50)
|
->take(50)
|
||||||
->get();
|
->get();
|
||||||
@ -131,7 +131,7 @@ class DashboardApiController extends BaseAPIController
|
|||||||
}
|
}
|
||||||
|
|
||||||
$upcoming = $upcoming->take(50)
|
$upcoming = $upcoming->take(50)
|
||||||
->select(['invoices.due_date', 'invoices.balance', 'invoices.public_id', 'invoices.invoice_number', 'clients.name as client_name', 'contacts.email', 'contacts.first_name', 'contacts.last_name', 'clients.currency_id', 'clients.public_id as client_public_id', 'clients.user_id as client_user_id', 'is_quote'])
|
->select(['invoices.due_date', 'invoices.balance', 'invoices.public_id', 'invoices.invoice_number', 'clients.name as client_name', 'contacts.email', 'contacts.first_name', 'contacts.last_name', 'clients.currency_id', 'clients.public_id as client_public_id', 'clients.user_id as client_user_id', 'invoice_type_id'])
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
$payments = DB::table('payments')
|
$payments = DB::table('payments')
|
||||||
@ -157,13 +157,12 @@ class DashboardApiController extends BaseAPIController
|
|||||||
$hasQuotes = false;
|
$hasQuotes = false;
|
||||||
foreach ([$upcoming, $pastDue] as $data) {
|
foreach ([$upcoming, $pastDue] as $data) {
|
||||||
foreach ($data as $invoice) {
|
foreach ($data as $invoice) {
|
||||||
if ($invoice->is_quote) {
|
if ($invoice->invoice_type_id == INVOICE_TYPE_QUOTE) {
|
||||||
$hasQuotes = true;
|
$hasQuotes = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'id' => 1,
|
'id' => 1,
|
||||||
'paidToDate' => $paidToDate[0]->value ? $paidToDate[0]->value : 0,
|
'paidToDate' => $paidToDate[0]->value ? $paidToDate[0]->value : 0,
|
||||||
|
@ -26,7 +26,7 @@ class DashboardController extends BaseController
|
|||||||
->where('clients.is_deleted', '=', false)
|
->where('clients.is_deleted', '=', false)
|
||||||
->where('invoices.is_deleted', '=', false)
|
->where('invoices.is_deleted', '=', false)
|
||||||
->where('invoices.is_recurring', '=', false)
|
->where('invoices.is_recurring', '=', false)
|
||||||
->where('invoices.is_quote', '=', false);
|
->where('invoices.invoice_type_id', '=', INVOICE_TYPE_STANDARD);
|
||||||
|
|
||||||
if(!$view_all){
|
if(!$view_all){
|
||||||
$metrics = $metrics->where(function($query) use($user_id){
|
$metrics = $metrics->where(function($query) use($user_id){
|
||||||
@ -64,7 +64,7 @@ class DashboardController extends BaseController
|
|||||||
->where('accounts.id', '=', Auth::user()->account_id)
|
->where('accounts.id', '=', Auth::user()->account_id)
|
||||||
->where('clients.is_deleted', '=', false)
|
->where('clients.is_deleted', '=', false)
|
||||||
->where('invoices.is_deleted', '=', false)
|
->where('invoices.is_deleted', '=', false)
|
||||||
->where('invoices.is_quote', '=', false)
|
->where('invoices.invoice_type_id', '=', INVOICE_TYPE_STANDARD)
|
||||||
->where('invoices.is_recurring', '=', false);
|
->where('invoices.is_recurring', '=', false);
|
||||||
|
|
||||||
if(!$view_all){
|
if(!$view_all){
|
||||||
@ -121,7 +121,7 @@ class DashboardController extends BaseController
|
|||||||
$pastDue = $pastDue->where('invoices.user_id', '=', $user_id);
|
$pastDue = $pastDue->where('invoices.user_id', '=', $user_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
$pastDue = $pastDue->select(['invoices.due_date', 'invoices.balance', 'invoices.public_id', 'invoices.invoice_number', 'clients.name as client_name', 'contacts.email', 'contacts.first_name', 'contacts.last_name', 'clients.currency_id', 'clients.public_id as client_public_id', 'clients.user_id as client_user_id', 'is_quote'])
|
$pastDue = $pastDue->select(['invoices.due_date', 'invoices.balance', 'invoices.public_id', 'invoices.invoice_number', 'clients.name as client_name', 'contacts.email', 'contacts.first_name', 'contacts.last_name', 'clients.currency_id', 'clients.public_id as client_public_id', 'clients.user_id as client_user_id', 'invoice_type_id'])
|
||||||
->orderBy('invoices.due_date', 'asc')
|
->orderBy('invoices.due_date', 'asc')
|
||||||
->take(50)
|
->take(50)
|
||||||
->get();
|
->get();
|
||||||
@ -147,7 +147,7 @@ class DashboardController extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
$upcoming = $upcoming->take(50)
|
$upcoming = $upcoming->take(50)
|
||||||
->select(['invoices.due_date', 'invoices.balance', 'invoices.public_id', 'invoices.invoice_number', 'clients.name as client_name', 'contacts.email', 'contacts.first_name', 'contacts.last_name', 'clients.currency_id', 'clients.public_id as client_public_id', 'clients.user_id as client_user_id', 'is_quote'])
|
->select(['invoices.due_date', 'invoices.balance', 'invoices.public_id', 'invoices.invoice_number', 'clients.name as client_name', 'contacts.email', 'contacts.first_name', 'contacts.last_name', 'clients.currency_id', 'clients.public_id as client_public_id', 'clients.user_id as client_user_id', 'invoice_type_id'])
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
$payments = DB::table('payments')
|
$payments = DB::table('payments')
|
||||||
@ -173,7 +173,7 @@ class DashboardController extends BaseController
|
|||||||
$hasQuotes = false;
|
$hasQuotes = false;
|
||||||
foreach ([$upcoming, $pastDue] as $data) {
|
foreach ([$upcoming, $pastDue] as $data) {
|
||||||
foreach ($data as $invoice) {
|
foreach ($data as $invoice) {
|
||||||
if ($invoice->is_quote) {
|
if ($invoice->invoice_type_id == INVOICE_TYPE_QUOTE) {
|
||||||
$hasQuotes = true;
|
$hasQuotes = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,21 @@
|
|||||||
|
|
||||||
use App\Models\Document;
|
use App\Models\Document;
|
||||||
|
|
||||||
|
use App\Ninja\Repositories\DocumentRepository;
|
||||||
|
use App\Http\Requests\DocumentRequest;
|
||||||
|
use App\Http\Requests\CreateDocumentRequest;
|
||||||
|
|
||||||
class DocumentAPIController extends BaseAPIController
|
class DocumentAPIController extends BaseAPIController
|
||||||
{
|
{
|
||||||
|
protected $documentRepo;
|
||||||
|
|
||||||
public function __construct()
|
protected $entityType = ENTITY_DOCUMENT;
|
||||||
|
|
||||||
|
public function __construct(DocumentRepository $documentRepo)
|
||||||
{
|
{
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
|
|
||||||
|
$this->documentRepo = $documentRepo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function index()
|
public function index()
|
||||||
@ -16,16 +24,19 @@ class DocumentAPIController extends BaseAPIController
|
|||||||
//stub
|
//stub
|
||||||
}
|
}
|
||||||
|
|
||||||
public function show($publicId)
|
public function show(DocumentRequest $request)
|
||||||
{
|
{
|
||||||
$document = Document::scope($publicId)->firstOrFail();
|
$document = $request->entity();
|
||||||
|
|
||||||
return DocumentController::getDownloadResponse($document);
|
return DocumentController::getDownloadResponse($document);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function store()
|
public function store(CreateDocumentRequest $request)
|
||||||
{
|
{
|
||||||
//stub
|
|
||||||
|
$document = $this->documentRepo->upload($request->all());
|
||||||
|
|
||||||
|
return $this->itemResponse($document);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update()
|
public function update()
|
||||||
|
@ -14,6 +14,7 @@ use App\Ninja\Repositories\DocumentRepository;
|
|||||||
|
|
||||||
use App\Http\Requests\DocumentRequest;
|
use App\Http\Requests\DocumentRequest;
|
||||||
use App\Http\Requests\CreateDocumentRequest;
|
use App\Http\Requests\CreateDocumentRequest;
|
||||||
|
use App\Http\Requests\UpdateDocumentRequest;
|
||||||
|
|
||||||
class DocumentController extends BaseController
|
class DocumentController extends BaseController
|
||||||
{
|
{
|
||||||
@ -26,20 +27,20 @@ class DocumentController extends BaseController
|
|||||||
|
|
||||||
$this->documentRepo = $documentRepo;
|
$this->documentRepo = $documentRepo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get(DocumentRequest $request)
|
public function get(DocumentRequest $request)
|
||||||
{
|
{
|
||||||
return static::getDownloadResponse($request->entity());
|
return static::getDownloadResponse($request->entity());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getDownloadResponse($document){
|
public static function getDownloadResponse($document){
|
||||||
$direct_url = $document->getDirectUrl();
|
$direct_url = $document->getDirectUrl();
|
||||||
if($direct_url){
|
if($direct_url){
|
||||||
return redirect($direct_url);
|
return redirect($direct_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
$stream = $document->getStream();
|
$stream = $document->getStream();
|
||||||
|
|
||||||
if($stream){
|
if($stream){
|
||||||
$headers = [
|
$headers = [
|
||||||
'Content-Type' => Document::$types[$document->type]['mime'],
|
'Content-Type' => Document::$types[$document->type]['mime'],
|
||||||
@ -54,59 +55,55 @@ class DocumentController extends BaseController
|
|||||||
$response = Response::make($document->getRaw(), 200);
|
$response = Response::make($document->getRaw(), 200);
|
||||||
$response->header('content-type', Document::$types[$document->type]['mime']);
|
$response->header('content-type', Document::$types[$document->type]['mime']);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPreview(DocumentRequest $request)
|
public function getPreview(DocumentRequest $request)
|
||||||
{
|
{
|
||||||
$document = $request->entity();
|
$document = $request->entity();
|
||||||
|
|
||||||
if(empty($document->preview)){
|
if(empty($document->preview)){
|
||||||
return Response::view('error', array('error'=>'Preview does not exist!'), 404);
|
return Response::view('error', array('error'=>'Preview does not exist!'), 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
$direct_url = $document->getDirectPreviewUrl();
|
$direct_url = $document->getDirectPreviewUrl();
|
||||||
if($direct_url){
|
if($direct_url){
|
||||||
return redirect($direct_url);
|
return redirect($direct_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
$previewType = pathinfo($document->preview, PATHINFO_EXTENSION);
|
$previewType = pathinfo($document->preview, PATHINFO_EXTENSION);
|
||||||
$response = Response::make($document->getRawPreview(), 200);
|
$response = Response::make($document->getRawPreview(), 200);
|
||||||
$response->header('content-type', Document::$types[$previewType]['mime']);
|
$response->header('content-type', Document::$types[$previewType]['mime']);
|
||||||
|
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getVFSJS(DocumentRequest $request, $publicId, $name)
|
public function getVFSJS(DocumentRequest $request, $publicId, $name)
|
||||||
{
|
{
|
||||||
$document = $request->entity();
|
$document = $request->entity();
|
||||||
|
|
||||||
if(substr($name, -3)=='.js'){
|
if(substr($name, -3)=='.js'){
|
||||||
$name = substr($name, 0, -3);
|
$name = substr($name, 0, -3);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!$document->isPDFEmbeddable()){
|
if(!$document->isPDFEmbeddable()){
|
||||||
return Response::view('error', array('error'=>'Image does not exist!'), 404);
|
return Response::view('error', array('error'=>'Image does not exist!'), 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
$content = $document->preview?$document->getRawPreview():$document->getRaw();
|
$content = $document->preview?$document->getRawPreview():$document->getRaw();
|
||||||
$content = 'ninjaAddVFSDoc('.json_encode(intval($publicId).'/'.strval($name)).',"'.base64_encode($content).'")';
|
$content = 'ninjaAddVFSDoc('.json_encode(intval($publicId).'/'.strval($name)).',"'.base64_encode($content).'")';
|
||||||
$response = Response::make($content, 200);
|
$response = Response::make($content, 200);
|
||||||
$response->header('content-type', 'text/javascript');
|
$response->header('content-type', 'text/javascript');
|
||||||
$response->header('cache-control', 'max-age=31536000');
|
$response->header('cache-control', 'max-age=31536000');
|
||||||
|
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function postUpload(CreateDocumentRequest $request)
|
public function postUpload(CreateDocumentRequest $request)
|
||||||
{
|
{
|
||||||
if (!Utils::hasFeature(FEATURE_DOCUMENTS)) {
|
$result = $this->documentRepo->upload($request->all(), $doc_array);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$result = $this->documentRepo->upload(Input::all()['file'], $doc_array);
|
|
||||||
|
|
||||||
if(is_string($result)){
|
if(is_string($result)){
|
||||||
return Response::json([
|
return Response::json([
|
||||||
'error' => $result,
|
'error' => $result,
|
||||||
@ -120,4 +117,11 @@ class DocumentController extends BaseController
|
|||||||
], 200);
|
], 200);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function delete(UpdateDocumentRequest $request)
|
||||||
|
{
|
||||||
|
$request->entity()->delete();
|
||||||
|
|
||||||
|
return RESULT_SUCCESS;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -134,23 +134,23 @@ class ExportController extends BaseController
|
|||||||
|
|
||||||
if ($request->input(ENTITY_INVOICE)) {
|
if ($request->input(ENTITY_INVOICE)) {
|
||||||
$data['invoices'] = Invoice::scope()
|
$data['invoices'] = Invoice::scope()
|
||||||
|
->invoiceType(INVOICE_TYPE_STANDARD)
|
||||||
->with('user', 'client.contacts', 'invoice_status')
|
->with('user', 'client.contacts', 'invoice_status')
|
||||||
->withArchived()
|
->withArchived()
|
||||||
->where('is_quote', '=', false)
|
|
||||||
->where('is_recurring', '=', false)
|
->where('is_recurring', '=', false)
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
$data['quotes'] = Invoice::scope()
|
$data['quotes'] = Invoice::scope()
|
||||||
|
->invoiceType(INVOICE_TYPE_QUOTE)
|
||||||
->with('user', 'client.contacts', 'invoice_status')
|
->with('user', 'client.contacts', 'invoice_status')
|
||||||
->withArchived()
|
->withArchived()
|
||||||
->where('is_quote', '=', true)
|
|
||||||
->where('is_recurring', '=', false)
|
->where('is_recurring', '=', false)
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
$data['recurringInvoices'] = Invoice::scope()
|
$data['recurringInvoices'] = Invoice::scope()
|
||||||
|
->invoiceType(INVOICE_TYPE_STANDARD)
|
||||||
->with('user', 'client.contacts', 'invoice_status', 'frequency')
|
->with('user', 'client.contacts', 'invoice_status', 'frequency')
|
||||||
->withArchived()
|
->withArchived()
|
||||||
->where('is_quote', '=', false)
|
|
||||||
->where('is_recurring', '=', true)
|
->where('is_recurring', '=', true)
|
||||||
->get();
|
->get();
|
||||||
}
|
}
|
||||||
|
@ -134,6 +134,7 @@ class InvoiceApiController extends BaseAPIController
|
|||||||
'city',
|
'city',
|
||||||
'state',
|
'state',
|
||||||
'postal_code',
|
'postal_code',
|
||||||
|
'country_id',
|
||||||
'private_notes',
|
'private_notes',
|
||||||
'currency_code',
|
'currency_code',
|
||||||
] as $field) {
|
] as $field) {
|
||||||
@ -182,7 +183,7 @@ class InvoiceApiController extends BaseAPIController
|
|||||||
$invoice = Invoice::scope($invoice->public_id)
|
$invoice = Invoice::scope($invoice->public_id)
|
||||||
->with('client', 'invoice_items', 'invitations')
|
->with('client', 'invoice_items', 'invitations')
|
||||||
->first();
|
->first();
|
||||||
|
|
||||||
return $this->itemResponse($invoice);
|
return $this->itemResponse($invoice);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,7 +270,7 @@ class InvoiceApiController extends BaseAPIController
|
|||||||
$item[$key] = $val;
|
$item[$key] = $val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $item;
|
return $item;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -308,7 +309,7 @@ class InvoiceApiController extends BaseAPIController
|
|||||||
public function update(UpdateInvoiceAPIRequest $request, $publicId)
|
public function update(UpdateInvoiceAPIRequest $request, $publicId)
|
||||||
{
|
{
|
||||||
if ($request->action == ACTION_CONVERT) {
|
if ($request->action == ACTION_CONVERT) {
|
||||||
$quote = $request->entity();
|
$quote = $request->entity();
|
||||||
$invoice = $this->invoiceRepo->cloneInvoice($quote, $quote->id);
|
$invoice = $this->invoiceRepo->cloneInvoice($quote, $quote->id);
|
||||||
return $this->itemResponse($invoice);
|
return $this->itemResponse($invoice);
|
||||||
} elseif ($request->action) {
|
} elseif ($request->action) {
|
||||||
@ -322,7 +323,7 @@ class InvoiceApiController extends BaseAPIController
|
|||||||
$invoice = Invoice::scope($publicId)
|
$invoice = Invoice::scope($publicId)
|
||||||
->with('client', 'invoice_items', 'invitations')
|
->with('client', 'invoice_items', 'invitations')
|
||||||
->firstOrFail();
|
->firstOrFail();
|
||||||
|
|
||||||
return $this->itemResponse($invoice);
|
return $this->itemResponse($invoice);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -351,10 +352,23 @@ class InvoiceApiController extends BaseAPIController
|
|||||||
public function destroy(UpdateInvoiceAPIRequest $request)
|
public function destroy(UpdateInvoiceAPIRequest $request)
|
||||||
{
|
{
|
||||||
$invoice = $request->entity();
|
$invoice = $request->entity();
|
||||||
|
|
||||||
$this->invoiceRepo->delete($invoice);
|
$this->invoiceRepo->delete($invoice);
|
||||||
|
|
||||||
return $this->itemResponse($invoice);
|
return $this->itemResponse($invoice);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function download(InvoiceRequest $request)
|
||||||
|
{
|
||||||
|
$invoice = $request->entity();
|
||||||
|
$pdfString = $invoice->getPDFString();
|
||||||
|
|
||||||
|
header('Content-Type: application/pdf');
|
||||||
|
header('Content-Length: ' . strlen($pdfString));
|
||||||
|
header('Content-disposition: attachment; filename="' . $invoice->getFileName() . '"');
|
||||||
|
header('Cache-Control: public, must-revalidate, max-age=0');
|
||||||
|
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||||
|
|
||||||
|
return $pdfString;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -574,9 +574,9 @@ class InvoiceController extends BaseController
|
|||||||
'remove_created_by' => Auth::user()->hasFeature(FEATURE_REMOVE_CREATED_BY),
|
'remove_created_by' => Auth::user()->hasFeature(FEATURE_REMOVE_CREATED_BY),
|
||||||
'invoice_settings' => Auth::user()->hasFeature(FEATURE_INVOICE_SETTINGS),
|
'invoice_settings' => Auth::user()->hasFeature(FEATURE_INVOICE_SETTINGS),
|
||||||
];
|
];
|
||||||
$invoice->is_quote = intval($invoice->is_quote);
|
$invoice->invoice_type_id = intval($invoice->invoice_type_id);
|
||||||
|
|
||||||
$activityTypeId = $invoice->is_quote ? ACTIVITY_TYPE_UPDATE_QUOTE : ACTIVITY_TYPE_UPDATE_INVOICE;
|
$activityTypeId = $invoice->isType(INVOICE_TYPE_QUOTE) ? ACTIVITY_TYPE_UPDATE_QUOTE : ACTIVITY_TYPE_UPDATE_INVOICE;
|
||||||
$activities = Activity::scope(false, $invoice->account_id)
|
$activities = Activity::scope(false, $invoice->account_id)
|
||||||
->where('activity_type_id', '=', $activityTypeId)
|
->where('activity_type_id', '=', $activityTypeId)
|
||||||
->where('invoice_id', '=', $invoice->id)
|
->where('invoice_id', '=', $invoice->id)
|
||||||
@ -596,7 +596,7 @@ class InvoiceController extends BaseController
|
|||||||
'remove_created_by' => Auth::user()->hasFeature(FEATURE_REMOVE_CREATED_BY),
|
'remove_created_by' => Auth::user()->hasFeature(FEATURE_REMOVE_CREATED_BY),
|
||||||
'invoice_settings' => Auth::user()->hasFeature(FEATURE_INVOICE_SETTINGS),
|
'invoice_settings' => Auth::user()->hasFeature(FEATURE_INVOICE_SETTINGS),
|
||||||
];
|
];
|
||||||
$backup->is_quote = isset($backup->is_quote) && intval($backup->is_quote);
|
$backup->invoice_type_id = isset($backup->invoice_type_id) && intval($backup->invoice_type_id) == INVOICE_TYPE_QUOTE;
|
||||||
$backup->account = $invoice->account->toArray();
|
$backup->account = $invoice->account->toArray();
|
||||||
|
|
||||||
$versionsJson[$activity->id] = $backup;
|
$versionsJson[$activity->id] = $backup;
|
||||||
|
@ -77,8 +77,8 @@ class PaymentController extends BaseController
|
|||||||
public function create(PaymentRequest $request)
|
public function create(PaymentRequest $request)
|
||||||
{
|
{
|
||||||
$invoices = Invoice::scope()
|
$invoices = Invoice::scope()
|
||||||
|
->invoiceType(INVOICE_TYPE_STANDARD)
|
||||||
->where('is_recurring', '=', false)
|
->where('is_recurring', '=', false)
|
||||||
->where('is_quote', '=', false)
|
|
||||||
->where('invoices.balance', '>', 0)
|
->where('invoices.balance', '>', 0)
|
||||||
->with('client', 'invoice_status')
|
->with('client', 'invoice_status')
|
||||||
->orderBy('invoice_number')->get();
|
->orderBy('invoice_number')->get();
|
||||||
@ -108,7 +108,7 @@ class PaymentController extends BaseController
|
|||||||
$data = array(
|
$data = array(
|
||||||
'client' => null,
|
'client' => null,
|
||||||
'invoice' => null,
|
'invoice' => null,
|
||||||
'invoices' => Invoice::scope()->where('is_recurring', '=', false)->where('is_quote', '=', false)
|
'invoices' => Invoice::scope()->invoiceType(INVOICE_TYPE_STANDARD)->where('is_recurring', '=', false)
|
||||||
->with('client', 'invoice_status')->orderBy('invoice_number')->get(),
|
->with('client', 'invoice_status')->orderBy('invoice_number')->get(),
|
||||||
'payment' => $payment,
|
'payment' => $payment,
|
||||||
'method' => 'PUT',
|
'method' => 'PUT',
|
||||||
|
@ -113,16 +113,16 @@ class QuoteController extends BaseController
|
|||||||
$rates = TaxRate::scope()->orderBy('name')->get();
|
$rates = TaxRate::scope()->orderBy('name')->get();
|
||||||
$options = [];
|
$options = [];
|
||||||
$defaultTax = false;
|
$defaultTax = false;
|
||||||
|
|
||||||
foreach ($rates as $rate) {
|
foreach ($rates as $rate) {
|
||||||
$options[$rate->rate . ' ' . $rate->name] = $rate->name . ' ' . ($rate->rate+0) . '%';
|
$options[$rate->rate . ' ' . $rate->name] = $rate->name . ' ' . ($rate->rate+0) . '%';
|
||||||
|
|
||||||
// load default invoice tax
|
// load default invoice tax
|
||||||
if ($rate->id == $account->default_tax_rate_id) {
|
if ($rate->id == $account->default_tax_rate_id) {
|
||||||
$defaultTax = $rate;
|
$defaultTax = $rate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'entityType' => ENTITY_QUOTE,
|
'entityType' => ENTITY_QUOTE,
|
||||||
'account' => Auth::user()->account,
|
'account' => Auth::user()->account,
|
||||||
@ -130,7 +130,7 @@ class QuoteController extends BaseController
|
|||||||
'taxRateOptions' => $options,
|
'taxRateOptions' => $options,
|
||||||
'defaultTax' => $defaultTax,
|
'defaultTax' => $defaultTax,
|
||||||
'countries' => Cache::get('countries'),
|
'countries' => Cache::get('countries'),
|
||||||
'clients' => Client::scope()->with('contacts', 'country')->orderBy('name')->get(),
|
'clients' => Client::scope()->viewable()->with('contacts', 'country')->orderBy('name')->get(),
|
||||||
'taxRates' => TaxRate::scope()->orderBy('name')->get(),
|
'taxRates' => TaxRate::scope()->orderBy('name')->get(),
|
||||||
'currencies' => Cache::get('currencies'),
|
'currencies' => Cache::get('currencies'),
|
||||||
'sizes' => Cache::get('sizes'),
|
'sizes' => Cache::get('sizes'),
|
||||||
|
@ -168,7 +168,7 @@ class ReportController extends BaseController
|
|||||||
->groupBy($groupBy);
|
->groupBy($groupBy);
|
||||||
|
|
||||||
if ($entityType == ENTITY_INVOICE) {
|
if ($entityType == ENTITY_INVOICE) {
|
||||||
$records->where('is_quote', '=', false)
|
$records->where('invoice_type_id', '=', INVOICE_TYPE_STANDARD)
|
||||||
->where('is_recurring', '=', false);
|
->where('is_recurring', '=', false);
|
||||||
} elseif ($entityType == ENTITY_PAYMENT) {
|
} elseif ($entityType == ENTITY_PAYMENT) {
|
||||||
$records->join('invoices', 'invoices.id', '=', 'payments.invoice_id')
|
$records->join('invoices', 'invoices.id', '=', 'payments.invoice_id')
|
||||||
@ -374,7 +374,7 @@ class ReportController extends BaseController
|
|||||||
$query->where('invoice_date', '>=', $startDate)
|
$query->where('invoice_date', '>=', $startDate)
|
||||||
->where('invoice_date', '<=', $endDate)
|
->where('invoice_date', '<=', $endDate)
|
||||||
->where('is_deleted', '=', false)
|
->where('is_deleted', '=', false)
|
||||||
->where('is_quote', '=', false)
|
->where('invoice_type_id', '=', INVOICE_TYPE_STANDARD)
|
||||||
->where('is_recurring', '=', false)
|
->where('is_recurring', '=', false)
|
||||||
->with(['payments' => function($query) {
|
->with(['payments' => function($query) {
|
||||||
$query->withTrashed()
|
$query->withTrashed()
|
||||||
@ -429,7 +429,7 @@ class ReportController extends BaseController
|
|||||||
->with(['invoices' => function($query) use ($startDate, $endDate) {
|
->with(['invoices' => function($query) use ($startDate, $endDate) {
|
||||||
$query->where('invoice_date', '>=', $startDate)
|
$query->where('invoice_date', '>=', $startDate)
|
||||||
->where('invoice_date', '<=', $endDate)
|
->where('invoice_date', '<=', $endDate)
|
||||||
->where('is_quote', '=', false)
|
->where('invoice_type_id', '=', INVOICE_TYPE_STANDARD)
|
||||||
->where('is_recurring', '=', false)
|
->where('is_recurring', '=', false)
|
||||||
->withArchived();
|
->withArchived();
|
||||||
}]);
|
}]);
|
||||||
|
@ -23,10 +23,11 @@ class ApiCheck {
|
|||||||
{
|
{
|
||||||
$loggingIn = $request->is('api/v1/login') || $request->is('api/v1/register');
|
$loggingIn = $request->is('api/v1/login') || $request->is('api/v1/register');
|
||||||
$headers = Utils::getApiHeaders();
|
$headers = Utils::getApiHeaders();
|
||||||
|
$hasApiSecret = hash_equals($request->api_secret ?: '', env(API_SECRET));
|
||||||
|
|
||||||
if ($loggingIn) {
|
if ($loggingIn) {
|
||||||
// check API secret
|
// check API secret
|
||||||
if ( ! $request->api_secret || ! env(API_SECRET) || ! hash_equals($request->api_secret, env(API_SECRET))) {
|
if ( ! $hasApiSecret) {
|
||||||
sleep(ERROR_DELAY);
|
sleep(ERROR_DELAY);
|
||||||
return Response::json('Invalid secret', 403, $headers);
|
return Response::json('Invalid secret', 403, $headers);
|
||||||
}
|
}
|
||||||
@ -48,7 +49,7 @@ class ApiCheck {
|
|||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Utils::hasFeature(FEATURE_API) && !$loggingIn) {
|
if (!Utils::hasFeature(FEATURE_API) && !$hasApiSecret) {
|
||||||
return Response::json('API requires pro plan', 403, $headers);
|
return Response::json('API requires pro plan', 403, $headers);
|
||||||
} else {
|
} else {
|
||||||
$key = Auth::check() ? Auth::user()->account->id : $request->getClientIp();
|
$key = Auth::check() ? Auth::user()->account->id : $request->getClientIp();
|
||||||
@ -59,7 +60,7 @@ class ApiCheck {
|
|||||||
$hour_throttle = Cache::get("hour_throttle:{$key}", null);
|
$hour_throttle = Cache::get("hour_throttle:{$key}", null);
|
||||||
$last_api_request = Cache::get("last_api_request:{$key}", 0);
|
$last_api_request = Cache::get("last_api_request:{$key}", 0);
|
||||||
$last_api_diff = time() - $last_api_request;
|
$last_api_diff = time() - $last_api_request;
|
||||||
|
|
||||||
if (is_null($hour_throttle)) {
|
if (is_null($hour_throttle)) {
|
||||||
$new_hour_throttle = 0;
|
$new_hour_throttle = 0;
|
||||||
} else {
|
} else {
|
||||||
@ -83,4 +84,4 @@ class ApiCheck {
|
|||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,15 @@
|
|||||||
<?php namespace App\Http\Requests;
|
<?php namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use App\Models\Invoice;
|
||||||
|
use App\Models\Expense;
|
||||||
|
|
||||||
class CreateDocumentRequest extends DocumentRequest
|
class CreateDocumentRequest extends DocumentRequest
|
||||||
{
|
{
|
||||||
|
protected $autoload = [
|
||||||
|
ENTITY_INVOICE,
|
||||||
|
ENTITY_EXPENSE,
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if the user is authorized to make this request.
|
* Determine if the user is authorized to make this request.
|
||||||
*
|
*
|
||||||
@ -9,6 +17,18 @@ class CreateDocumentRequest extends DocumentRequest
|
|||||||
*/
|
*/
|
||||||
public function authorize()
|
public function authorize()
|
||||||
{
|
{
|
||||||
|
if ( ! $this->user()->hasFeature(FEATURE_DOCUMENTS)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->invoice && $this->user()->cannot('edit', $this->invoice)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->expense && $this->user()->cannot('edit', $this->expense)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return $this->user()->can('create', ENTITY_DOCUMENT);
|
return $this->user()->can('create', ENTITY_DOCUMENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -20,7 +40,8 @@ class CreateDocumentRequest extends DocumentRequest
|
|||||||
public function rules()
|
public function rules()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
//'file' => 'mimes:jpg'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,50 @@
|
|||||||
|
|
||||||
use Illuminate\Foundation\Http\FormRequest;
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
|
// https://laracasts.com/discuss/channels/general-discussion/laravel-5-modify-input-before-validation/replies/34366
|
||||||
abstract class Request extends FormRequest {
|
abstract class Request extends FormRequest {
|
||||||
|
|
||||||
//
|
// populate in subclass to auto load record
|
||||||
|
protected $autoload = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the input.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Validation\Factory $factory
|
||||||
|
* @return \Illuminate\Validation\Validator
|
||||||
|
*/
|
||||||
|
public function validator($factory)
|
||||||
|
{
|
||||||
|
return $factory->make(
|
||||||
|
$this->sanitizeInput(), $this->container->call([$this, 'rules']), $this->messages()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sanitize the input.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function sanitizeInput()
|
||||||
|
{
|
||||||
|
if (method_exists($this, 'sanitize')) {
|
||||||
|
$input = $this->container->call([$this, 'sanitize']);
|
||||||
|
} else {
|
||||||
|
$input = $this->all();
|
||||||
|
}
|
||||||
|
|
||||||
|
// autoload referenced entities
|
||||||
|
foreach ($this->autoload as $entityType) {
|
||||||
|
if ($id = $this->input("{$entityType}_public_id") ?: $this->input("{$entityType}_id")) {
|
||||||
|
$class = "App\\Models\\" . ucwords($entityType);
|
||||||
|
$entity = $class::scope($id)->firstOrFail();
|
||||||
|
$input[$entityType] = $entity;
|
||||||
|
$input[$entityType . '_id'] = $entity->id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->replace($input);
|
||||||
|
|
||||||
|
return $this->all();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
26
app/Http/Requests/UpdateDocumentRequest.php
Normal file
26
app/Http/Requests/UpdateDocumentRequest.php
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?php namespace App\Http\Requests;
|
||||||
|
|
||||||
|
class UpdateDocumentRequest extends DocumentRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorize()
|
||||||
|
{
|
||||||
|
return $this->user()->can('edit', $this->entity());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function rules()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -157,7 +157,8 @@ Route::group(['middleware' => 'auth:user'], function() {
|
|||||||
Route::get('documents/{documents}/{filename?}', 'DocumentController@get');
|
Route::get('documents/{documents}/{filename?}', 'DocumentController@get');
|
||||||
Route::get('documents/js/{documents}/{filename}', 'DocumentController@getVFSJS');
|
Route::get('documents/js/{documents}/{filename}', 'DocumentController@getVFSJS');
|
||||||
Route::get('documents/preview/{documents}/{filename?}', 'DocumentController@getPreview');
|
Route::get('documents/preview/{documents}/{filename?}', 'DocumentController@getPreview');
|
||||||
Route::post('document', 'DocumentController@postUpload');
|
Route::post('documents', 'DocumentController@postUpload');
|
||||||
|
Route::delete('documents/{documents}', 'DocumentController@delete');
|
||||||
|
|
||||||
Route::get('quotes/create/{client_id?}', 'QuoteController@create');
|
Route::get('quotes/create/{client_id?}', 'QuoteController@create');
|
||||||
Route::get('quotes/{invoices}/clone', 'InvoiceController@cloneInvoice');
|
Route::get('quotes/{invoices}/clone', 'InvoiceController@cloneInvoice');
|
||||||
@ -271,6 +272,7 @@ Route::group(['middleware' => 'api', 'prefix' => 'api/v1'], function()
|
|||||||
//Route::get('quotes', 'QuoteApiController@index');
|
//Route::get('quotes', 'QuoteApiController@index');
|
||||||
//Route::resource('quotes', 'QuoteApiController');
|
//Route::resource('quotes', 'QuoteApiController');
|
||||||
Route::get('invoices', 'InvoiceApiController@index');
|
Route::get('invoices', 'InvoiceApiController@index');
|
||||||
|
Route::get('download/{invoice_id}', 'InvoiceApiController@download');
|
||||||
Route::resource('invoices', 'InvoiceApiController');
|
Route::resource('invoices', 'InvoiceApiController');
|
||||||
Route::get('payments', 'PaymentApiController@index');
|
Route::get('payments', 'PaymentApiController@index');
|
||||||
Route::resource('payments', 'PaymentApiController');
|
Route::resource('payments', 'PaymentApiController');
|
||||||
@ -360,6 +362,9 @@ if (!defined('CONTACT_EMAIL')) {
|
|||||||
define('ENTITY_BANK_ACCOUNT', 'bank_account');
|
define('ENTITY_BANK_ACCOUNT', 'bank_account');
|
||||||
define('ENTITY_BANK_SUBACCOUNT', 'bank_subaccount');
|
define('ENTITY_BANK_SUBACCOUNT', 'bank_subaccount');
|
||||||
|
|
||||||
|
define('INVOICE_TYPE_STANDARD', 1);
|
||||||
|
define('INVOICE_TYPE_QUOTE', 2);
|
||||||
|
|
||||||
define('PERSON_CONTACT', 'contact');
|
define('PERSON_CONTACT', 'contact');
|
||||||
define('PERSON_USER', 'user');
|
define('PERSON_USER', 'user');
|
||||||
define('PERSON_VENDOR_CONTACT','vendorcontact');
|
define('PERSON_VENDOR_CONTACT','vendorcontact');
|
||||||
|
@ -18,7 +18,7 @@ class Account extends Eloquent
|
|||||||
{
|
{
|
||||||
use PresentableTrait;
|
use PresentableTrait;
|
||||||
use SoftDeletes;
|
use SoftDeletes;
|
||||||
|
|
||||||
public static $plan_prices = array(
|
public static $plan_prices = array(
|
||||||
PLAN_PRO => array(
|
PLAN_PRO => array(
|
||||||
PLAN_TERM_MONTHLY => PLAN_PRICE_PRO_MONTHLY,
|
PLAN_TERM_MONTHLY => PLAN_PRICE_PRO_MONTHLY,
|
||||||
@ -56,6 +56,11 @@ class Account extends Eloquent
|
|||||||
'currency_id',
|
'currency_id',
|
||||||
'language_id',
|
'language_id',
|
||||||
'military_time',
|
'military_time',
|
||||||
|
'invoice_taxes',
|
||||||
|
'invoice_item_taxes',
|
||||||
|
'show_item_taxes',
|
||||||
|
'default_tax_rate_id',
|
||||||
|
'enable_second_tax_rate',
|
||||||
];
|
];
|
||||||
|
|
||||||
public static $basicSettings = [
|
public static $basicSettings = [
|
||||||
@ -406,7 +411,7 @@ class Account extends Eloquent
|
|||||||
return $gateway;
|
return $gateway;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -426,27 +431,27 @@ class Account extends Eloquent
|
|||||||
if($this->logo == ''){
|
if($this->logo == ''){
|
||||||
$this->calculateLogoDetails();
|
$this->calculateLogoDetails();
|
||||||
}
|
}
|
||||||
|
|
||||||
return !empty($this->logo);
|
return !empty($this->logo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getLogoDisk(){
|
public function getLogoDisk(){
|
||||||
return Storage::disk(env('LOGO_FILESYSTEM', 'logos'));
|
return Storage::disk(env('LOGO_FILESYSTEM', 'logos'));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function calculateLogoDetails(){
|
protected function calculateLogoDetails(){
|
||||||
$disk = $this->getLogoDisk();
|
$disk = $this->getLogoDisk();
|
||||||
|
|
||||||
if($disk->exists($this->account_key.'.png')){
|
if($disk->exists($this->account_key.'.png')){
|
||||||
$this->logo = $this->account_key.'.png';
|
$this->logo = $this->account_key.'.png';
|
||||||
} else if($disk->exists($this->account_key.'.jpg')) {
|
} else if($disk->exists($this->account_key.'.jpg')) {
|
||||||
$this->logo = $this->account_key.'.jpg';
|
$this->logo = $this->account_key.'.jpg';
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!empty($this->logo)){
|
if(!empty($this->logo)){
|
||||||
$image = imagecreatefromstring($disk->get($this->logo));
|
$image = imagecreatefromstring($disk->get($this->logo));
|
||||||
$this->logo_width = imagesx($image);
|
$this->logo_width = imagesx($image);
|
||||||
$this->logo_height = imagesy($image);
|
$this->logo_height = imagesy($image);
|
||||||
$this->logo_size = $disk->size($this->logo);
|
$this->logo_size = $disk->size($this->logo);
|
||||||
} else {
|
} else {
|
||||||
$this->logo = null;
|
$this->logo = null;
|
||||||
@ -458,33 +463,33 @@ class Account extends Eloquent
|
|||||||
if(!$this->hasLogo()){
|
if(!$this->hasLogo()){
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$disk = $this->getLogoDisk();
|
$disk = $this->getLogoDisk();
|
||||||
return $disk->get($this->logo);
|
return $disk->get($this->logo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getLogoURL($cachebuster = false)
|
public function getLogoURL($cachebuster = false)
|
||||||
{
|
{
|
||||||
if(!$this->hasLogo()){
|
if(!$this->hasLogo()){
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$disk = $this->getLogoDisk();
|
$disk = $this->getLogoDisk();
|
||||||
$adapter = $disk->getAdapter();
|
$adapter = $disk->getAdapter();
|
||||||
|
|
||||||
if($adapter instanceof \League\Flysystem\Adapter\Local) {
|
if($adapter instanceof \League\Flysystem\Adapter\Local) {
|
||||||
// Stored locally
|
// Stored locally
|
||||||
$logo_url = str_replace(public_path(), url('/'), $adapter->applyPathPrefix($this->logo), $count);
|
$logo_url = str_replace(public_path(), url('/'), $adapter->applyPathPrefix($this->logo), $count);
|
||||||
|
|
||||||
if ($cachebuster) {
|
if ($cachebuster) {
|
||||||
$logo_url .= '?no_cache='.time();
|
$logo_url .= '?no_cache='.time();
|
||||||
}
|
}
|
||||||
|
|
||||||
if($count == 1){
|
if($count == 1){
|
||||||
return str_replace(DIRECTORY_SEPARATOR, '/', $logo_url);
|
return str_replace(DIRECTORY_SEPARATOR, '/', $logo_url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Document::getDirectFileUrl($this->logo, $this->getLogoDisk());
|
return Document::getDirectFileUrl($this->logo, $this->getLogoDisk());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -529,18 +534,18 @@ class Account extends Eloquent
|
|||||||
$invoice = Invoice::createNew();
|
$invoice = Invoice::createNew();
|
||||||
|
|
||||||
$invoice->is_recurring = false;
|
$invoice->is_recurring = false;
|
||||||
$invoice->is_quote = false;
|
$invoice->invoice_type_id = INVOICE_TYPE_STANDARD;
|
||||||
$invoice->invoice_date = Utils::today();
|
$invoice->invoice_date = Utils::today();
|
||||||
$invoice->start_date = Utils::today();
|
$invoice->start_date = Utils::today();
|
||||||
$invoice->invoice_design_id = $this->invoice_design_id;
|
$invoice->invoice_design_id = $this->invoice_design_id;
|
||||||
$invoice->client_id = $clientId;
|
$invoice->client_id = $clientId;
|
||||||
|
|
||||||
if ($entityType === ENTITY_RECURRING_INVOICE) {
|
if ($entityType === ENTITY_RECURRING_INVOICE) {
|
||||||
$invoice->invoice_number = microtime(true);
|
$invoice->invoice_number = microtime(true);
|
||||||
$invoice->is_recurring = true;
|
$invoice->is_recurring = true;
|
||||||
} else {
|
} else {
|
||||||
if ($entityType == ENTITY_QUOTE) {
|
if ($entityType == ENTITY_QUOTE) {
|
||||||
$invoice->is_quote = true;
|
$invoice->invoice_type_id = INVOICE_TYPE_QUOTE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->hasClientNumberPattern($invoice) && !$clientId) {
|
if ($this->hasClientNumberPattern($invoice) && !$clientId) {
|
||||||
@ -549,7 +554,7 @@ class Account extends Eloquent
|
|||||||
$invoice->invoice_number = $this->getNextInvoiceNumber($invoice);
|
$invoice->invoice_number = $this->getNextInvoiceNumber($invoice);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$clientId) {
|
if (!$clientId) {
|
||||||
$invoice->client = Client::createNew();
|
$invoice->client = Client::createNew();
|
||||||
$invoice->client->public_id = 0;
|
$invoice->client->public_id = 0;
|
||||||
@ -558,34 +563,34 @@ class Account extends Eloquent
|
|||||||
return $invoice;
|
return $invoice;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getNumberPrefix($isQuote)
|
public function getNumberPrefix($invoice_type_id)
|
||||||
{
|
{
|
||||||
if ( ! $this->hasFeature(FEATURE_INVOICE_SETTINGS)) {
|
if ( ! $this->hasFeature(FEATURE_INVOICE_SETTINGS)) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
return ($isQuote ? $this->quote_number_prefix : $this->invoice_number_prefix) ?: '';
|
return ($invoice_type_id == INVOICE_TYPE_QUOTE ? $this->quote_number_prefix : $this->invoice_number_prefix) ?: '';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function hasNumberPattern($isQuote)
|
public function hasNumberPattern($invoice_type_id)
|
||||||
{
|
{
|
||||||
if ( ! $this->hasFeature(FEATURE_INVOICE_SETTINGS)) {
|
if ( ! $this->hasFeature(FEATURE_INVOICE_SETTINGS)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $isQuote ? ($this->quote_number_pattern ? true : false) : ($this->invoice_number_pattern ? true : false);
|
return $invoice_type_id == INVOICE_TYPE_QUOTE ? ($this->quote_number_pattern ? true : false) : ($this->invoice_number_pattern ? true : false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function hasClientNumberPattern($invoice)
|
public function hasClientNumberPattern($invoice)
|
||||||
{
|
{
|
||||||
$pattern = $invoice->is_quote ? $this->quote_number_pattern : $this->invoice_number_pattern;
|
$pattern = $invoice->invoice_type_id == INVOICE_TYPE_QUOTE ? $this->quote_number_pattern : $this->invoice_number_pattern;
|
||||||
|
|
||||||
return strstr($pattern, '$custom');
|
return strstr($pattern, '$custom');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getNumberPattern($invoice)
|
public function getNumberPattern($invoice)
|
||||||
{
|
{
|
||||||
$pattern = $invoice->is_quote ? $this->quote_number_pattern : $this->invoice_number_pattern;
|
$pattern = $invoice->invoice_type_id == INVOICE_TYPE_QUOTE ? $this->quote_number_pattern : $this->invoice_number_pattern;
|
||||||
|
|
||||||
if (!$pattern) {
|
if (!$pattern) {
|
||||||
return false;
|
return false;
|
||||||
@ -595,7 +600,7 @@ class Account extends Eloquent
|
|||||||
$replace = [date('Y')];
|
$replace = [date('Y')];
|
||||||
|
|
||||||
$search[] = '{$counter}';
|
$search[] = '{$counter}';
|
||||||
$replace[] = str_pad($this->getCounter($invoice->is_quote), $this->invoice_number_padding, '0', STR_PAD_LEFT);
|
$replace[] = str_pad($this->getCounter($invoice->invoice_type_id), $this->invoice_number_padding, '0', STR_PAD_LEFT);
|
||||||
|
|
||||||
if (strstr($pattern, '{$userId}')) {
|
if (strstr($pattern, '{$userId}')) {
|
||||||
$search[] = '{$userId}';
|
$search[] = '{$userId}';
|
||||||
@ -638,9 +643,9 @@ class Account extends Eloquent
|
|||||||
return str_replace($search, $replace, $pattern);
|
return str_replace($search, $replace, $pattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCounter($isQuote)
|
public function getCounter($invoice_type_id)
|
||||||
{
|
{
|
||||||
return $isQuote && !$this->share_counter ? $this->quote_number_counter : $this->invoice_number_counter;
|
return $invoice_type_id == INVOICE_TYPE_QUOTE && !$this->share_counter ? $this->quote_number_counter : $this->invoice_number_counter;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function previewNextInvoiceNumber($entityType = ENTITY_INVOICE)
|
public function previewNextInvoiceNumber($entityType = ENTITY_INVOICE)
|
||||||
@ -651,31 +656,35 @@ class Account extends Eloquent
|
|||||||
|
|
||||||
public function getNextInvoiceNumber($invoice)
|
public function getNextInvoiceNumber($invoice)
|
||||||
{
|
{
|
||||||
if ($this->hasNumberPattern($invoice->is_quote)) {
|
if ($this->hasNumberPattern($invoice->invoice_type_id)) {
|
||||||
return $this->getNumberPattern($invoice);
|
$number = $this->getNumberPattern($invoice);
|
||||||
|
} else {
|
||||||
|
$counter = $this->getCounter($invoice->invoice_type_id);
|
||||||
|
$prefix = $this->getNumberPrefix($invoice->invoice_type_id);
|
||||||
|
$counterOffset = 0;
|
||||||
|
|
||||||
|
// confirm the invoice number isn't already taken
|
||||||
|
do {
|
||||||
|
$number = $prefix . str_pad($counter, $this->invoice_number_padding, '0', STR_PAD_LEFT);
|
||||||
|
$check = Invoice::scope(false, $this->id)->whereInvoiceNumber($number)->withTrashed()->first();
|
||||||
|
$counter++;
|
||||||
|
$counterOffset++;
|
||||||
|
} while ($check);
|
||||||
|
|
||||||
|
// update the invoice counter to be caught up
|
||||||
|
if ($counterOffset > 1) {
|
||||||
|
if ($invoice->isType(INVOICE_TYPE_QUOTE) && !$this->share_counter) {
|
||||||
|
$this->quote_number_counter += $counterOffset - 1;
|
||||||
|
} else {
|
||||||
|
$this->invoice_number_counter += $counterOffset - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->save();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$counter = $this->getCounter($invoice->is_quote);
|
if ($invoice->recurring_invoice_id) {
|
||||||
$prefix = $this->getNumberPrefix($invoice->is_quote);
|
$number = $this->recurring_invoice_number_prefix . $number;
|
||||||
$counterOffset = 0;
|
|
||||||
|
|
||||||
// confirm the invoice number isn't already taken
|
|
||||||
do {
|
|
||||||
$number = $prefix . str_pad($counter, $this->invoice_number_padding, '0', STR_PAD_LEFT);
|
|
||||||
$check = Invoice::scope(false, $this->id)->whereInvoiceNumber($number)->withTrashed()->first();
|
|
||||||
$counter++;
|
|
||||||
$counterOffset++;
|
|
||||||
} while ($check);
|
|
||||||
|
|
||||||
// update the invoice counter to be caught up
|
|
||||||
if ($counterOffset > 1) {
|
|
||||||
if ($invoice->is_quote && !$this->share_counter) {
|
|
||||||
$this->quote_number_counter += $counterOffset - 1;
|
|
||||||
} else {
|
|
||||||
$this->invoice_number_counter += $counterOffset - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->save();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $number;
|
return $number;
|
||||||
@ -683,19 +692,17 @@ class Account extends Eloquent
|
|||||||
|
|
||||||
public function incrementCounter($invoice)
|
public function incrementCounter($invoice)
|
||||||
{
|
{
|
||||||
if ($invoice->is_quote && !$this->share_counter) {
|
// if they didn't use the counter don't increment it
|
||||||
|
if ($invoice->invoice_number != $this->getNextInvoiceNumber($invoice)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($invoice->isType(INVOICE_TYPE_QUOTE) && !$this->share_counter) {
|
||||||
$this->quote_number_counter += 1;
|
$this->quote_number_counter += 1;
|
||||||
} else {
|
} else {
|
||||||
$default = $this->invoice_number_counter;
|
$this->invoice_number_counter += 1;
|
||||||
$actual = Utils::parseInt($invoice->invoice_number);
|
|
||||||
|
|
||||||
if ( ! $this->hasFeature(FEATURE_INVOICE_SETTINGS) && $default != $actual) {
|
|
||||||
$this->invoice_number_counter = $actual + 1;
|
|
||||||
} else {
|
|
||||||
$this->invoice_number_counter += 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->save();
|
$this->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -718,7 +725,7 @@ class Account extends Eloquent
|
|||||||
$query->where('updated_at', '>=', $updatedAt);
|
$query->where('updated_at', '>=', $updatedAt);
|
||||||
}
|
}
|
||||||
}]);
|
}]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function loadLocalizationSettings($client = false)
|
public function loadLocalizationSettings($client = false)
|
||||||
@ -731,8 +738,8 @@ class Account extends Eloquent
|
|||||||
Session::put(SESSION_DATE_FORMAT, $this->date_format ? $this->date_format->format : DEFAULT_DATE_FORMAT);
|
Session::put(SESSION_DATE_FORMAT, $this->date_format ? $this->date_format->format : DEFAULT_DATE_FORMAT);
|
||||||
Session::put(SESSION_DATE_PICKER_FORMAT, $this->date_format ? $this->date_format->picker_format : DEFAULT_DATE_PICKER_FORMAT);
|
Session::put(SESSION_DATE_PICKER_FORMAT, $this->date_format ? $this->date_format->picker_format : DEFAULT_DATE_PICKER_FORMAT);
|
||||||
|
|
||||||
$currencyId = ($client && $client->currency_id) ? $client->currency_id : $this->currency_id ?: DEFAULT_CURRENCY;
|
$currencyId = ($client && $client->currency_id) ? $client->currency_id : $this->currency_id ?: DEFAULT_CURRENCY;
|
||||||
$locale = ($client && $client->language_id) ? $client->language->locale : ($this->language_id ? $this->Language->locale : DEFAULT_LOCALE);
|
$locale = ($client && $client->language_id) ? $client->language->locale : ($this->language_id ? $this->Language->locale : DEFAULT_LOCALE);
|
||||||
|
|
||||||
Session::put(SESSION_CURRENCY, $currencyId);
|
Session::put(SESSION_CURRENCY, $currencyId);
|
||||||
Session::put(SESSION_LOCALE, $locale);
|
Session::put(SESSION_LOCALE, $locale);
|
||||||
@ -815,7 +822,7 @@ class Account extends Eloquent
|
|||||||
if ( ! Utils::isNinja()) {
|
if ( ! Utils::isNinja()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->company->trial_plan = $plan;
|
$this->company->trial_plan = $plan;
|
||||||
$this->company->trial_started = date_create()->format('Y-m-d');
|
$this->company->trial_started = date_create()->format('Y-m-d');
|
||||||
$this->company->save();
|
$this->company->save();
|
||||||
@ -826,18 +833,18 @@ class Account extends Eloquent
|
|||||||
if (Utils::isNinjaDev()) {
|
if (Utils::isNinjaDev()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$planDetails = $this->getPlanDetails();
|
$planDetails = $this->getPlanDetails();
|
||||||
$selfHost = !Utils::isNinjaProd();
|
$selfHost = !Utils::isNinjaProd();
|
||||||
|
|
||||||
if (!$selfHost && function_exists('ninja_account_features')) {
|
if (!$selfHost && function_exists('ninja_account_features')) {
|
||||||
$result = ninja_account_features($this, $feature);
|
$result = ninja_account_features($this, $feature);
|
||||||
|
|
||||||
if ($result != null) {
|
if ($result != null) {
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ($feature) {
|
switch ($feature) {
|
||||||
// Pro
|
// Pro
|
||||||
case FEATURE_CUSTOMIZE_INVOICE_DESIGN:
|
case FEATURE_CUSTOMIZE_INVOICE_DESIGN:
|
||||||
@ -854,7 +861,7 @@ class Account extends Eloquent
|
|||||||
case FEATURE_CLIENT_PORTAL_PASSWORD:
|
case FEATURE_CLIENT_PORTAL_PASSWORD:
|
||||||
case FEATURE_CUSTOM_URL:
|
case FEATURE_CUSTOM_URL:
|
||||||
return $selfHost || !empty($planDetails);
|
return $selfHost || !empty($planDetails);
|
||||||
|
|
||||||
// Pro; No trial allowed, unless they're trialing enterprise with an active pro plan
|
// Pro; No trial allowed, unless they're trialing enterprise with an active pro plan
|
||||||
case FEATURE_MORE_CLIENTS:
|
case FEATURE_MORE_CLIENTS:
|
||||||
return $selfHost || !empty($planDetails) && (!$planDetails['trial'] || !empty($this->getPlanDetails(false, false)));
|
return $selfHost || !empty($planDetails) && (!$planDetails['trial'] || !empty($this->getPlanDetails(false, false)));
|
||||||
@ -867,26 +874,26 @@ class Account extends Eloquent
|
|||||||
// Fallthrough
|
// Fallthrough
|
||||||
case FEATURE_CLIENT_PORTAL_CSS:
|
case FEATURE_CLIENT_PORTAL_CSS:
|
||||||
return !empty($planDetails);// A plan is required even for self-hosted users
|
return !empty($planDetails);// A plan is required even for self-hosted users
|
||||||
|
|
||||||
// Enterprise; No Trial allowed; grandfathered for old pro users
|
// Enterprise; No Trial allowed; grandfathered for old pro users
|
||||||
case FEATURE_USERS:// Grandfathered for old Pro users
|
case FEATURE_USERS:// Grandfathered for old Pro users
|
||||||
if($planDetails && $planDetails['trial']) {
|
if($planDetails && $planDetails['trial']) {
|
||||||
// Do they have a non-trial plan?
|
// Do they have a non-trial plan?
|
||||||
$planDetails = $this->getPlanDetails(false, false);
|
$planDetails = $this->getPlanDetails(false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $selfHost || !empty($planDetails) && ($planDetails['plan'] == PLAN_ENTERPRISE || $planDetails['started'] <= date_create(PRO_USERS_GRANDFATHER_DEADLINE));
|
return $selfHost || !empty($planDetails) && ($planDetails['plan'] == PLAN_ENTERPRISE || $planDetails['started'] <= date_create(PRO_USERS_GRANDFATHER_DEADLINE));
|
||||||
|
|
||||||
// Enterprise; No Trial allowed
|
// Enterprise; No Trial allowed
|
||||||
case FEATURE_DOCUMENTS:
|
case FEATURE_DOCUMENTS:
|
||||||
case FEATURE_USER_PERMISSIONS:
|
case FEATURE_USER_PERMISSIONS:
|
||||||
return $selfHost || !empty($planDetails) && $planDetails['plan'] == PLAN_ENTERPRISE && !$planDetails['trial'];
|
return $selfHost || !empty($planDetails) && $planDetails['plan'] == PLAN_ENTERPRISE && !$planDetails['trial'];
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isPro(&$plan_details = null)
|
public function isPro(&$plan_details = null)
|
||||||
{
|
{
|
||||||
if (!Utils::isNinjaProd()) {
|
if (!Utils::isNinjaProd()) {
|
||||||
@ -898,7 +905,7 @@ class Account extends Eloquent
|
|||||||
}
|
}
|
||||||
|
|
||||||
$plan_details = $this->getPlanDetails();
|
$plan_details = $this->getPlanDetails();
|
||||||
|
|
||||||
return !empty($plan_details);
|
return !empty($plan_details);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -913,36 +920,36 @@ class Account extends Eloquent
|
|||||||
}
|
}
|
||||||
|
|
||||||
$plan_details = $this->getPlanDetails();
|
$plan_details = $this->getPlanDetails();
|
||||||
|
|
||||||
return $plan_details && $plan_details['plan'] == PLAN_ENTERPRISE;
|
return $plan_details && $plan_details['plan'] == PLAN_ENTERPRISE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPlanDetails($include_inactive = false, $include_trial = true)
|
public function getPlanDetails($include_inactive = false, $include_trial = true)
|
||||||
{
|
{
|
||||||
if (!$this->company) {
|
if (!$this->company) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$plan = $this->company->plan;
|
$plan = $this->company->plan;
|
||||||
$trial_plan = $this->company->trial_plan;
|
$trial_plan = $this->company->trial_plan;
|
||||||
|
|
||||||
if(!$plan && (!$trial_plan || !$include_trial)) {
|
if(!$plan && (!$trial_plan || !$include_trial)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$trial_active = false;
|
$trial_active = false;
|
||||||
if ($trial_plan && $include_trial) {
|
if ($trial_plan && $include_trial) {
|
||||||
$trial_started = DateTime::createFromFormat('Y-m-d', $this->company->trial_started);
|
$trial_started = DateTime::createFromFormat('Y-m-d', $this->company->trial_started);
|
||||||
$trial_expires = clone $trial_started;
|
$trial_expires = clone $trial_started;
|
||||||
$trial_expires->modify('+2 weeks');
|
$trial_expires->modify('+2 weeks');
|
||||||
|
|
||||||
if ($trial_expires >= date_create()) {
|
if ($trial_expires >= date_create()) {
|
||||||
$trial_active = true;
|
$trial_active = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$plan_active = false;
|
$plan_active = false;
|
||||||
if ($plan) {
|
if ($plan) {
|
||||||
if ($this->company->plan_expires == null) {
|
if ($this->company->plan_expires == null) {
|
||||||
$plan_active = true;
|
$plan_active = true;
|
||||||
$plan_expires = false;
|
$plan_expires = false;
|
||||||
@ -953,11 +960,11 @@ class Account extends Eloquent
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$include_inactive && !$plan_active && !$trial_active) {
|
if (!$include_inactive && !$plan_active && !$trial_active) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should we show plan details or trial details?
|
// Should we show plan details or trial details?
|
||||||
if (($plan && !$trial_plan) || !$include_trial) {
|
if (($plan && !$trial_plan) || !$include_trial) {
|
||||||
$use_plan = true;
|
$use_plan = true;
|
||||||
@ -984,7 +991,7 @@ class Account extends Eloquent
|
|||||||
$use_plan = $plan_expires >= $trial_expires;
|
$use_plan = $plan_expires >= $trial_expires;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($use_plan) {
|
if ($use_plan) {
|
||||||
return array(
|
return array(
|
||||||
'trial' => false,
|
'trial' => false,
|
||||||
@ -1011,7 +1018,7 @@ class Account extends Eloquent
|
|||||||
if (!Utils::isNinjaProd()) {
|
if (!Utils::isNinjaProd()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$plan_details = $this->getPlanDetails();
|
$plan_details = $this->getPlanDetails();
|
||||||
|
|
||||||
return $plan_details && $plan_details['trial'];
|
return $plan_details && $plan_details['trial'];
|
||||||
@ -1026,7 +1033,7 @@ class Account extends Eloquent
|
|||||||
return array(PLAN_PRO, PLAN_ENTERPRISE);
|
return array(PLAN_PRO, PLAN_ENTERPRISE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->company->trial_plan == PLAN_PRO) {
|
if ($this->company->trial_plan == PLAN_PRO) {
|
||||||
if ($plan) {
|
if ($plan) {
|
||||||
return $plan != PLAN_PRO;
|
return $plan != PLAN_PRO;
|
||||||
@ -1034,28 +1041,28 @@ class Account extends Eloquent
|
|||||||
return array(PLAN_ENTERPRISE);
|
return array(PLAN_ENTERPRISE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCountTrialDaysLeft()
|
public function getCountTrialDaysLeft()
|
||||||
{
|
{
|
||||||
$planDetails = $this->getPlanDetails(true);
|
$planDetails = $this->getPlanDetails(true);
|
||||||
|
|
||||||
if(!$planDetails || !$planDetails['trial']) {
|
if(!$planDetails || !$planDetails['trial']) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
$today = new DateTime('now');
|
$today = new DateTime('now');
|
||||||
$interval = $today->diff($planDetails['expires']);
|
$interval = $today->diff($planDetails['expires']);
|
||||||
|
|
||||||
return $interval ? $interval->d : 0;
|
return $interval ? $interval->d : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRenewalDate()
|
public function getRenewalDate()
|
||||||
{
|
{
|
||||||
$planDetails = $this->getPlanDetails();
|
$planDetails = $this->getPlanDetails();
|
||||||
|
|
||||||
if ($planDetails) {
|
if ($planDetails) {
|
||||||
$date = $planDetails['expires'];
|
$date = $planDetails['expires'];
|
||||||
$date = max($date, date_create());
|
$date = max($date, date_create());
|
||||||
@ -1107,7 +1114,7 @@ class Account extends Eloquent
|
|||||||
'invoice_items',
|
'invoice_items',
|
||||||
'created_at',
|
'created_at',
|
||||||
'is_recurring',
|
'is_recurring',
|
||||||
'is_quote',
|
'invoice_type_id',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
foreach ($invoice->invoice_items as $invoiceItem) {
|
foreach ($invoice->invoice_items as $invoiceItem) {
|
||||||
@ -1185,7 +1192,7 @@ class Account extends Eloquent
|
|||||||
$field = "email_template_{$entityType}";
|
$field = "email_template_{$entityType}";
|
||||||
$template = $this->$field;
|
$template = $this->$field;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$template) {
|
if (!$template) {
|
||||||
$template = $this->getDefaultEmailTemplate($entityType, $message);
|
$template = $this->getDefaultEmailTemplate($entityType, $message);
|
||||||
}
|
}
|
||||||
@ -1275,7 +1282,7 @@ class Account extends Eloquent
|
|||||||
{
|
{
|
||||||
$url = SITE_URL;
|
$url = SITE_URL;
|
||||||
$iframe_url = $this->iframe_url;
|
$iframe_url = $this->iframe_url;
|
||||||
|
|
||||||
if ($iframe_url) {
|
if ($iframe_url) {
|
||||||
return "{$iframe_url}/?";
|
return "{$iframe_url}/?";
|
||||||
} else if ($this->subdomain) {
|
} else if ($this->subdomain) {
|
||||||
@ -1310,18 +1317,18 @@ class Account extends Eloquent
|
|||||||
if (!$entity) {
|
if (!$entity) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert (for example) 'custom_invoice_label1' to 'invoice.custom_value1'
|
// convert (for example) 'custom_invoice_label1' to 'invoice.custom_value1'
|
||||||
$field = str_replace(['invoice_', 'label'], ['', 'value'], $field);
|
$field = str_replace(['invoice_', 'label'], ['', 'value'], $field);
|
||||||
|
|
||||||
return Utils::isEmpty($entity->$field) ? false : true;
|
return Utils::isEmpty($entity->$field) ? false : true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function attatchPDF()
|
public function attachPDF()
|
||||||
{
|
{
|
||||||
return $this->hasFeature(FEATURE_PDF_ATTACHMENT) && $this->pdf_email_attachment;
|
return $this->hasFeature(FEATURE_PDF_ATTACHMENT) && $this->pdf_email_attachment;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getEmailDesignId()
|
public function getEmailDesignId()
|
||||||
{
|
{
|
||||||
return $this->hasFeature(FEATURE_CUSTOM_EMAILS) ? $this->email_design_id : EMAIL_DESIGN_PLAIN;
|
return $this->hasFeature(FEATURE_CUSTOM_EMAILS) ? $this->email_design_id : EMAIL_DESIGN_PLAIN;
|
||||||
@ -1329,11 +1336,11 @@ class Account extends Eloquent
|
|||||||
|
|
||||||
public function clientViewCSS(){
|
public function clientViewCSS(){
|
||||||
$css = '';
|
$css = '';
|
||||||
|
|
||||||
if ($this->hasFeature(FEATURE_CUSTOMIZE_INVOICE_DESIGN)) {
|
if ($this->hasFeature(FEATURE_CUSTOMIZE_INVOICE_DESIGN)) {
|
||||||
$bodyFont = $this->getBodyFontCss();
|
$bodyFont = $this->getBodyFontCss();
|
||||||
$headerFont = $this->getHeaderFontCss();
|
$headerFont = $this->getHeaderFontCss();
|
||||||
|
|
||||||
$css = 'body{'.$bodyFont.'}';
|
$css = 'body{'.$bodyFont.'}';
|
||||||
if ($headerFont != $bodyFont) {
|
if ($headerFont != $bodyFont) {
|
||||||
$css .= 'h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{'.$headerFont.'}';
|
$css .= 'h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{'.$headerFont.'}';
|
||||||
@ -1343,17 +1350,17 @@ class Account extends Eloquent
|
|||||||
// For self-hosted users, a white-label license is required for custom CSS
|
// For self-hosted users, a white-label license is required for custom CSS
|
||||||
$css .= $this->client_view_css;
|
$css .= $this->client_view_css;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $css;
|
return $css;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFontsUrl($protocol = ''){
|
public function getFontsUrl($protocol = ''){
|
||||||
$bodyFont = $this->getHeaderFontId();
|
$bodyFont = $this->getHeaderFontId();
|
||||||
$headerFont = $this->getBodyFontId();
|
$headerFont = $this->getBodyFontId();
|
||||||
|
|
||||||
$bodyFontSettings = Utils::getFromCache($bodyFont, 'fonts');
|
$bodyFontSettings = Utils::getFromCache($bodyFont, 'fonts');
|
||||||
$google_fonts = array($bodyFontSettings['google_font']);
|
$google_fonts = array($bodyFontSettings['google_font']);
|
||||||
|
|
||||||
if($headerFont != $bodyFont){
|
if($headerFont != $bodyFont){
|
||||||
$headerFontSettings = Utils::getFromCache($headerFont, 'fonts');
|
$headerFontSettings = Utils::getFromCache($headerFont, 'fonts');
|
||||||
$google_fonts[] = $headerFontSettings['google_font'];
|
$google_fonts[] = $headerFontSettings['google_font'];
|
||||||
@ -1361,7 +1368,7 @@ class Account extends Eloquent
|
|||||||
|
|
||||||
return ($protocol?$protocol.':':'').'//fonts.googleapis.com/css?family='.implode('|',$google_fonts);
|
return ($protocol?$protocol.':':'').'//fonts.googleapis.com/css?family='.implode('|',$google_fonts);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getHeaderFontId() {
|
public function getHeaderFontId() {
|
||||||
return ($this->hasFeature(FEATURE_CUSTOMIZE_INVOICE_DESIGN) && $this->header_font_id) ? $this->header_font_id : DEFAULT_HEADER_FONT;
|
return ($this->hasFeature(FEATURE_CUSTOMIZE_INVOICE_DESIGN) && $this->header_font_id) ? $this->header_font_id : DEFAULT_HEADER_FONT;
|
||||||
}
|
}
|
||||||
@ -1373,47 +1380,47 @@ class Account extends Eloquent
|
|||||||
public function getHeaderFontName(){
|
public function getHeaderFontName(){
|
||||||
return Utils::getFromCache($this->getHeaderFontId(), 'fonts')['name'];
|
return Utils::getFromCache($this->getHeaderFontId(), 'fonts')['name'];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getBodyFontName(){
|
public function getBodyFontName(){
|
||||||
return Utils::getFromCache($this->getBodyFontId(), 'fonts')['name'];
|
return Utils::getFromCache($this->getBodyFontId(), 'fonts')['name'];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getHeaderFontCss($include_weight = true){
|
public function getHeaderFontCss($include_weight = true){
|
||||||
$font_data = Utils::getFromCache($this->getHeaderFontId(), 'fonts');
|
$font_data = Utils::getFromCache($this->getHeaderFontId(), 'fonts');
|
||||||
$css = 'font-family:'.$font_data['css_stack'].';';
|
$css = 'font-family:'.$font_data['css_stack'].';';
|
||||||
|
|
||||||
if($include_weight){
|
if($include_weight){
|
||||||
$css .= 'font-weight:'.$font_data['css_weight'].';';
|
$css .= 'font-weight:'.$font_data['css_weight'].';';
|
||||||
}
|
}
|
||||||
|
|
||||||
return $css;
|
return $css;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getBodyFontCss($include_weight = true){
|
public function getBodyFontCss($include_weight = true){
|
||||||
$font_data = Utils::getFromCache($this->getBodyFontId(), 'fonts');
|
$font_data = Utils::getFromCache($this->getBodyFontId(), 'fonts');
|
||||||
$css = 'font-family:'.$font_data['css_stack'].';';
|
$css = 'font-family:'.$font_data['css_stack'].';';
|
||||||
|
|
||||||
if($include_weight){
|
if($include_weight){
|
||||||
$css .= 'font-weight:'.$font_data['css_weight'].';';
|
$css .= 'font-weight:'.$font_data['css_weight'].';';
|
||||||
}
|
}
|
||||||
|
|
||||||
return $css;
|
return $css;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFonts(){
|
public function getFonts(){
|
||||||
return array_unique(array($this->getHeaderFontId(), $this->getBodyFontId()));
|
return array_unique(array($this->getHeaderFontId(), $this->getBodyFontId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFontsData(){
|
public function getFontsData(){
|
||||||
$data = array();
|
$data = array();
|
||||||
|
|
||||||
foreach($this->getFonts() as $font){
|
foreach($this->getFonts() as $font){
|
||||||
$data[] = Utils::getFromCache($font, 'fonts');
|
$data[] = Utils::getFromCache($font, 'fonts');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFontFolders(){
|
public function getFontFolders(){
|
||||||
return array_map(function($item){return $item['folder'];}, $this->getFontsData());
|
return array_map(function($item){return $item['folder'];}, $this->getFontsData());
|
||||||
}
|
}
|
||||||
|
@ -6,20 +6,25 @@ use Auth;
|
|||||||
|
|
||||||
class Document extends EntityModel
|
class Document extends EntityModel
|
||||||
{
|
{
|
||||||
|
protected $fillable = [
|
||||||
|
'invoice_id',
|
||||||
|
'expense_id',
|
||||||
|
];
|
||||||
|
|
||||||
public static $extraExtensions = array(
|
public static $extraExtensions = array(
|
||||||
'jpg' => 'jpeg',
|
'jpg' => 'jpeg',
|
||||||
'tif' => 'tiff',
|
'tif' => 'tiff',
|
||||||
);
|
);
|
||||||
|
|
||||||
public static $allowedMimes = array(// Used by Dropzone.js; does not affect what the server accepts
|
public static $allowedMimes = array(// Used by Dropzone.js; does not affect what the server accepts
|
||||||
'image/png', 'image/jpeg', 'image/tiff', 'application/pdf', 'image/gif', 'image/vnd.adobe.photoshop', 'text/plain',
|
'image/png', 'image/jpeg', 'image/tiff', 'application/pdf', 'image/gif', 'image/vnd.adobe.photoshop', 'text/plain',
|
||||||
'application/zip', 'application/msword',
|
'application/zip', 'application/msword',
|
||||||
'application/excel', 'application/vnd.ms-excel', 'application/x-excel', 'application/x-msexcel',
|
'application/excel', 'application/vnd.ms-excel', 'application/x-excel', 'application/x-msexcel',
|
||||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet','application/postscript', 'image/svg+xml',
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet','application/postscript', 'image/svg+xml',
|
||||||
'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'application/vnd.ms-powerpoint',
|
'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'application/vnd.ms-powerpoint',
|
||||||
);
|
);
|
||||||
|
|
||||||
public static $types = array(
|
public static $types = array(
|
||||||
'png' => array(
|
'png' => array(
|
||||||
'mime' => 'image/png',
|
'mime' => 'image/png',
|
||||||
@ -70,18 +75,18 @@ class Document extends EntityModel
|
|||||||
'mime' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
'mime' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
public function fill(array $attributes)
|
public function fill(array $attributes)
|
||||||
{
|
{
|
||||||
parent::fill($attributes);
|
parent::fill($attributes);
|
||||||
|
|
||||||
if(empty($this->attributes['disk'])){
|
if(empty($this->attributes['disk'])){
|
||||||
$this->attributes['disk'] = env('DOCUMENT_FILESYSTEM', 'documents');
|
$this->attributes['disk'] = env('DOCUMENT_FILESYSTEM', 'documents');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function account()
|
public function account()
|
||||||
{
|
{
|
||||||
return $this->belongsTo('App\Models\Account');
|
return $this->belongsTo('App\Models\Account');
|
||||||
@ -101,7 +106,7 @@ class Document extends EntityModel
|
|||||||
{
|
{
|
||||||
return $this->belongsTo('App\Models\Invoice')->withTrashed();
|
return $this->belongsTo('App\Models\Invoice')->withTrashed();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDisk(){
|
public function getDisk(){
|
||||||
return Storage::disk(!empty($this->disk)?$this->disk:env('DOCUMENT_FILESYSTEM', 'documents'));
|
return Storage::disk(!empty($this->disk)?$this->disk:env('DOCUMENT_FILESYSTEM', 'documents'));
|
||||||
}
|
}
|
||||||
@ -110,19 +115,19 @@ class Document extends EntityModel
|
|||||||
{
|
{
|
||||||
$this->attributes['disk'] = $value?$value:env('DOCUMENT_FILESYSTEM', 'documents');
|
$this->attributes['disk'] = $value?$value:env('DOCUMENT_FILESYSTEM', 'documents');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDirectUrl(){
|
public function getDirectUrl(){
|
||||||
return static::getDirectFileUrl($this->path, $this->getDisk());
|
return static::getDirectFileUrl($this->path, $this->getDisk());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDirectPreviewUrl(){
|
public function getDirectPreviewUrl(){
|
||||||
return $this->preview?static::getDirectFileUrl($this->preview, $this->getDisk(), true):null;
|
return $this->preview?static::getDirectFileUrl($this->preview, $this->getDisk(), true):null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getDirectFileUrl($path, $disk, $prioritizeSpeed = false){
|
public static function getDirectFileUrl($path, $disk, $prioritizeSpeed = false){
|
||||||
$adapter = $disk->getAdapter();
|
$adapter = $disk->getAdapter();
|
||||||
$fullPath = $adapter->applyPathPrefix($path);
|
$fullPath = $adapter->applyPathPrefix($path);
|
||||||
|
|
||||||
if($adapter instanceof \League\Flysystem\AwsS3v3\AwsS3Adapter) {
|
if($adapter instanceof \League\Flysystem\AwsS3v3\AwsS3Adapter) {
|
||||||
$client = $adapter->getClient();
|
$client = $adapter->getClient();
|
||||||
$command = $client->getCommand('GetObject', [
|
$command = $client->getCommand('GetObject', [
|
||||||
@ -136,12 +141,12 @@ class Document extends EntityModel
|
|||||||
$secret = env('RACKSPACE_TEMP_URL_SECRET');
|
$secret = env('RACKSPACE_TEMP_URL_SECRET');
|
||||||
if($secret){
|
if($secret){
|
||||||
$object = $adapter->getContainer()->getObject($fullPath);
|
$object = $adapter->getContainer()->getObject($fullPath);
|
||||||
|
|
||||||
if(env('RACKSPACE_TEMP_URL_SECRET_SET')){
|
if(env('RACKSPACE_TEMP_URL_SECRET_SET')){
|
||||||
// Go ahead and set the secret too
|
// Go ahead and set the secret too
|
||||||
$object->getService()->getAccount()->setTempUrlSecret($secret);
|
$object->getService()->getAccount()->setTempUrlSecret($secret);
|
||||||
}
|
}
|
||||||
|
|
||||||
$url = $object->getUrl();
|
$url = $object->getUrl();
|
||||||
$expiry = strtotime('+10 minutes');
|
$expiry = strtotime('+10 minutes');
|
||||||
$urlPath = urldecode($url->getPath());
|
$urlPath = urldecode($url->getPath());
|
||||||
@ -150,64 +155,64 @@ class Document extends EntityModel
|
|||||||
return sprintf('%s?temp_url_sig=%s&temp_url_expires=%d', $url, $hash, $expiry);
|
return sprintf('%s?temp_url_sig=%s&temp_url_expires=%d', $url, $hash, $expiry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRaw(){
|
public function getRaw(){
|
||||||
$disk = $this->getDisk();
|
$disk = $this->getDisk();
|
||||||
|
|
||||||
return $disk->get($this->path);
|
return $disk->get($this->path);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getStream(){
|
public function getStream(){
|
||||||
$disk = $this->getDisk();
|
$disk = $this->getDisk();
|
||||||
|
|
||||||
return $disk->readStream($this->path);
|
return $disk->readStream($this->path);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRawPreview(){
|
public function getRawPreview(){
|
||||||
$disk = $this->getDisk();
|
$disk = $this->getDisk();
|
||||||
|
|
||||||
return $disk->get($this->preview);
|
return $disk->get($this->preview);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getUrl(){
|
public function getUrl(){
|
||||||
return url('documents/'.$this->public_id.'/'.$this->name);
|
return url('documents/'.$this->public_id.'/'.$this->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getClientUrl($invitation){
|
public function getClientUrl($invitation){
|
||||||
return url('client/documents/'.$invitation->invitation_key.'/'.$this->public_id.'/'.$this->name);
|
return url('client/documents/'.$invitation->invitation_key.'/'.$this->public_id.'/'.$this->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isPDFEmbeddable(){
|
public function isPDFEmbeddable(){
|
||||||
return $this->type == 'jpeg' || $this->type == 'png' || $this->preview;
|
return $this->type == 'jpeg' || $this->type == 'png' || $this->preview;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getVFSJSUrl(){
|
public function getVFSJSUrl(){
|
||||||
if(!$this->isPDFEmbeddable())return null;
|
if(!$this->isPDFEmbeddable())return null;
|
||||||
return url('documents/js/'.$this->public_id.'/'.$this->name.'.js');
|
return url('documents/js/'.$this->public_id.'/'.$this->name.'.js');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getClientVFSJSUrl(){
|
public function getClientVFSJSUrl(){
|
||||||
if(!$this->isPDFEmbeddable())return null;
|
if(!$this->isPDFEmbeddable())return null;
|
||||||
return url('client/documents/js/'.$this->public_id.'/'.$this->name.'.js');
|
return url('client/documents/js/'.$this->public_id.'/'.$this->name.'.js');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPreviewUrl(){
|
public function getPreviewUrl(){
|
||||||
return $this->preview?url('documents/preview/'.$this->public_id.'/'.$this->name.'.'.pathinfo($this->preview, PATHINFO_EXTENSION)):null;
|
return $this->preview?url('documents/preview/'.$this->public_id.'/'.$this->name.'.'.pathinfo($this->preview, PATHINFO_EXTENSION)):null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toArray()
|
public function toArray()
|
||||||
{
|
{
|
||||||
$array = parent::toArray();
|
$array = parent::toArray();
|
||||||
|
|
||||||
if(empty($this->visible) || in_array('url', $this->visible))$array['url'] = $this->getUrl();
|
if(empty($this->visible) || in_array('url', $this->visible))$array['url'] = $this->getUrl();
|
||||||
if(empty($this->visible) || in_array('preview_url', $this->visible))$array['preview_url'] = $this->getPreviewUrl();
|
if(empty($this->visible) || in_array('preview_url', $this->visible))$array['preview_url'] = $this->getPreviewUrl();
|
||||||
|
|
||||||
return $array;
|
return $array;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function cloneDocument(){
|
public function cloneDocument(){
|
||||||
$document = Document::createNew($this);
|
$document = Document::createNew($this);
|
||||||
$document->path = $this->path;
|
$document->path = $this->path;
|
||||||
@ -219,7 +224,7 @@ class Document extends EntityModel
|
|||||||
$document->size = $this->size;
|
$document->size = $this->size;
|
||||||
$document->width = $this->width;
|
$document->width = $this->width;
|
||||||
$document->height = $this->height;
|
$document->height = $this->height;
|
||||||
|
|
||||||
return $document;
|
return $document;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -230,11 +235,11 @@ Document::deleted(function ($document) {
|
|||||||
->where('documents.path', '=', $document->path)
|
->where('documents.path', '=', $document->path)
|
||||||
->where('documents.disk', '=', $document->disk)
|
->where('documents.disk', '=', $document->disk)
|
||||||
->count();
|
->count();
|
||||||
|
|
||||||
if(!$same_path_count){
|
if(!$same_path_count){
|
||||||
$document->getDisk()->delete($document->path);
|
$document->getDisk()->delete($document->path);
|
||||||
}
|
}
|
||||||
|
|
||||||
if($document->preview){
|
if($document->preview){
|
||||||
$same_preview_count = DB::table('documents')
|
$same_preview_count = DB::table('documents')
|
||||||
->where('documents.account_id', '=', $document->account_id)
|
->where('documents.account_id', '=', $document->account_id)
|
||||||
@ -245,5 +250,5 @@ Document::deleted(function ($document) {
|
|||||||
$document->getDisk()->delete($document->preview);
|
$document->getDisk()->delete($document->preview);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -30,7 +30,7 @@ class EntityModel extends Eloquent
|
|||||||
} else {
|
} else {
|
||||||
$lastEntity = $className::scope(false, $entity->account_id);
|
$lastEntity = $className::scope(false, $entity->account_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
$lastEntity = $lastEntity->orderBy('public_id', 'DESC')
|
$lastEntity = $lastEntity->orderBy('public_id', 'DESC')
|
||||||
->first();
|
->first();
|
||||||
|
|
||||||
@ -86,6 +86,15 @@ class EntityModel extends Eloquent
|
|||||||
return $query;
|
return $query;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function scopeViewable($query)
|
||||||
|
{
|
||||||
|
if (Auth::check() && ! Auth::user()->hasPermission('view_all')) {
|
||||||
|
$query->where($this->getEntityType(). 's.user_id', '=', Auth::user()->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
public function scopeWithArchived($query)
|
public function scopeWithArchived($query)
|
||||||
{
|
{
|
||||||
return $query->withTrashed()->where('is_deleted', '=', false);
|
return $query->withTrashed()->where('is_deleted', '=', false);
|
||||||
@ -110,7 +119,7 @@ class EntityModel extends Eloquent
|
|||||||
{
|
{
|
||||||
return 'App\\Ninja\\Transformers\\' . ucwords(Utils::toCamelCase($entityType)) . 'Transformer';
|
return 'App\\Ninja\\Transformers\\' . ucwords(Utils::toCamelCase($entityType)) . 'Transformer';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setNullValues()
|
public function setNullValues()
|
||||||
{
|
{
|
||||||
foreach ($this->fillable as $field) {
|
foreach ($this->fillable as $field) {
|
||||||
|
@ -29,9 +29,9 @@ class Invoice extends EntityModel implements BalanceAffecting
|
|||||||
'tax_name1',
|
'tax_name1',
|
||||||
'tax_rate1',
|
'tax_rate1',
|
||||||
'tax_name2',
|
'tax_name2',
|
||||||
'tax_rate2',
|
'tax_rate2',
|
||||||
];
|
];
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
'is_recurring' => 'boolean',
|
'is_recurring' => 'boolean',
|
||||||
'has_tasks' => 'boolean',
|
'has_tasks' => 'boolean',
|
||||||
@ -96,7 +96,7 @@ class Invoice extends EntityModel implements BalanceAffecting
|
|||||||
|
|
||||||
public function affectsBalance()
|
public function affectsBalance()
|
||||||
{
|
{
|
||||||
return !$this->is_quote && !$this->is_recurring;
|
return $this->isType(INVOICE_TYPE_STANDARD) && !$this->is_recurring;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getAdjustment()
|
public function getAdjustment()
|
||||||
@ -139,7 +139,7 @@ class Invoice extends EntityModel implements BalanceAffecting
|
|||||||
|
|
||||||
public function getAmountPaid($calculate = false)
|
public function getAmountPaid($calculate = false)
|
||||||
{
|
{
|
||||||
if ($this->is_quote || $this->is_recurring) {
|
if ($this->isType(INVOICE_TYPE_QUOTE) || $this->is_recurring) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,10 +230,23 @@ class Invoice extends EntityModel implements BalanceAffecting
|
|||||||
|
|
||||||
public function scopeInvoices($query)
|
public function scopeInvoices($query)
|
||||||
{
|
{
|
||||||
return $query->where('is_quote', '=', false)
|
return $query->where('invoice_type_id', '=', INVOICE_TYPE_STANDARD)
|
||||||
->where('is_recurring', '=', false);
|
->where('is_recurring', '=', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function scopeInvoiceType($query, $typeId)
|
||||||
|
{
|
||||||
|
return $query->where('invoice_type_id', '=', $typeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isType($typeId) {
|
||||||
|
return $this->invoice_type_id == $typeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isQuote() {
|
||||||
|
return $this->isType(INVOICE_TYPE_QUOTE);
|
||||||
|
}
|
||||||
|
|
||||||
public function markInvitationsSent($notify = false)
|
public function markInvitationsSent($notify = false)
|
||||||
{
|
{
|
||||||
foreach ($this->invitations as $invitation) {
|
foreach ($this->invitations as $invitation) {
|
||||||
@ -256,7 +269,7 @@ class Invoice extends EntityModel implements BalanceAffecting
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->is_quote) {
|
if ($this->isType(INVOICE_TYPE_QUOTE)) {
|
||||||
event(new QuoteInvitationWasEmailed($invitation));
|
event(new QuoteInvitationWasEmailed($invitation));
|
||||||
} else {
|
} else {
|
||||||
event(new InvoiceInvitationWasEmailed($invitation));
|
event(new InvoiceInvitationWasEmailed($invitation));
|
||||||
@ -292,7 +305,7 @@ class Invoice extends EntityModel implements BalanceAffecting
|
|||||||
|
|
||||||
public function markApproved()
|
public function markApproved()
|
||||||
{
|
{
|
||||||
if ($this->is_quote) {
|
if ($this->isType(INVOICE_TYPE_QUOTE)) {
|
||||||
$this->invoice_status_id = INVOICE_STATUS_APPROVED;
|
$this->invoice_status_id = INVOICE_STATUS_APPROVED;
|
||||||
$this->save();
|
$this->save();
|
||||||
}
|
}
|
||||||
@ -341,7 +354,7 @@ class Invoice extends EntityModel implements BalanceAffecting
|
|||||||
|
|
||||||
public function getEntityType()
|
public function getEntityType()
|
||||||
{
|
{
|
||||||
return $this->is_quote ? ENTITY_QUOTE : ENTITY_INVOICE;
|
return $this->isType(INVOICE_TYPE_QUOTE) ? ENTITY_QUOTE : ENTITY_INVOICE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isSent()
|
public function isSent()
|
||||||
@ -416,7 +429,7 @@ class Invoice extends EntityModel implements BalanceAffecting
|
|||||||
'invoice_design_id',
|
'invoice_design_id',
|
||||||
'invoice_fonts',
|
'invoice_fonts',
|
||||||
'features',
|
'features',
|
||||||
'is_quote',
|
'invoice_type_id',
|
||||||
'custom_value1',
|
'custom_value1',
|
||||||
'custom_value2',
|
'custom_value2',
|
||||||
'custom_taxes1',
|
'custom_taxes1',
|
||||||
@ -515,12 +528,12 @@ class Invoice extends EntityModel implements BalanceAffecting
|
|||||||
'name',
|
'name',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($this->expenses as $expense) {
|
foreach ($this->expenses as $expense) {
|
||||||
$expense->setVisible([
|
$expense->setVisible([
|
||||||
'documents',
|
'documents',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
foreach ($expense->documents as $document) {
|
foreach ($expense->documents as $document) {
|
||||||
$document->setVisible([
|
$document->setVisible([
|
||||||
'public_id',
|
'public_id',
|
||||||
@ -579,12 +592,12 @@ class Invoice extends EntityModel implements BalanceAffecting
|
|||||||
|
|
||||||
return $schedule[1]->getStart();
|
return $schedule[1]->getStart();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDueDate($invoice_date = null){
|
public function getDueDate($invoice_date = null){
|
||||||
if(!$this->is_recurring) {
|
if(!$this->is_recurring) {
|
||||||
return $this->due_date ? $this->due_date : null;
|
return $this->due_date ? $this->due_date : null;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
$now = time();
|
$now = time();
|
||||||
if($invoice_date) {
|
if($invoice_date) {
|
||||||
// If $invoice_date is specified, all calculations are based on that date
|
// If $invoice_date is specified, all calculations are based on that date
|
||||||
@ -598,7 +611,7 @@ class Invoice extends EntityModel implements BalanceAffecting
|
|||||||
$now = $invoice_date->getTimestamp();
|
$now = $invoice_date->getTimestamp();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if($this->due_date && $this->due_date != '0000-00-00'){
|
if($this->due_date && $this->due_date != '0000-00-00'){
|
||||||
// This is a recurring invoice; we're using a custom format here.
|
// This is a recurring invoice; we're using a custom format here.
|
||||||
// The year is always 1998; January is 1st, 2nd, last day of the month.
|
// The year is always 1998; January is 1st, 2nd, last day of the month.
|
||||||
@ -607,7 +620,7 @@ class Invoice extends EntityModel implements BalanceAffecting
|
|||||||
$monthVal = (int)date('n', $dueDateVal);
|
$monthVal = (int)date('n', $dueDateVal);
|
||||||
$dayVal = (int)date('j', $dueDateVal);
|
$dayVal = (int)date('j', $dueDateVal);
|
||||||
$dueDate = false;
|
$dueDate = false;
|
||||||
|
|
||||||
if($monthVal == 1) {// January; day of month
|
if($monthVal == 1) {// January; day of month
|
||||||
$currentDay = (int)date('j', $now);
|
$currentDay = (int)date('j', $now);
|
||||||
$lastDayOfMonth = (int)date('t', $now);
|
$lastDayOfMonth = (int)date('t', $now);
|
||||||
@ -634,7 +647,7 @@ class Invoice extends EntityModel implements BalanceAffecting
|
|||||||
if($dueDay > $lastDayOfMonth){
|
if($dueDay > $lastDayOfMonth){
|
||||||
// No later than the end of the month
|
// No later than the end of the month
|
||||||
$dueDay = $lastDayOfMonth;
|
$dueDay = $lastDayOfMonth;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$dueDate = mktime(0, 0, 0, $dueMonth, $dueDay, $dueYear);
|
$dueDate = mktime(0, 0, 0, $dueMonth, $dueDay, $dueYear);
|
||||||
@ -663,7 +676,7 @@ class Invoice extends EntityModel implements BalanceAffecting
|
|||||||
return date('Y-m-d', strtotime('+'.$days.' day', $now));
|
return date('Y-m-d', strtotime('+'.$days.' day', $now));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Couldn't calculate one
|
// Couldn't calculate one
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -681,11 +694,11 @@ class Invoice extends EntityModel implements BalanceAffecting
|
|||||||
$dateStart = $date->getStart();
|
$dateStart = $date->getStart();
|
||||||
$date = $this->account->formatDate($dateStart);
|
$date = $this->account->formatDate($dateStart);
|
||||||
$dueDate = $this->getDueDate($dateStart);
|
$dueDate = $this->getDueDate($dateStart);
|
||||||
|
|
||||||
if($dueDate) {
|
if($dueDate) {
|
||||||
$date .= ' <small>(' . trans('texts.due') . ' ' . $this->account->formatDate($dueDate) . ')</small>';
|
$date .= ' <small>(' . trans('texts.due') . ' ' . $this->account->formatDate($dueDate) . ')</small>';
|
||||||
}
|
}
|
||||||
|
|
||||||
$dates[] = $date;
|
$dates[] = $date;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -799,16 +812,16 @@ class Invoice extends EntityModel implements BalanceAffecting
|
|||||||
$invitation = $this->invitations[0];
|
$invitation = $this->invitations[0];
|
||||||
$link = $invitation->getLink('view', true);
|
$link = $invitation->getLink('view', true);
|
||||||
$key = env('PHANTOMJS_CLOUD_KEY');
|
$key = env('PHANTOMJS_CLOUD_KEY');
|
||||||
|
|
||||||
if (Utils::isNinjaDev()) {
|
if (Utils::isNinjaDev()) {
|
||||||
$link = env('TEST_LINK');
|
$link = env('TEST_LINK');
|
||||||
}
|
}
|
||||||
|
|
||||||
$url = "http://api.phantomjscloud.com/api/browser/v2/{$key}/?request=%7Burl:%22{$link}?phantomjs=true%22,renderType:%22html%22%7D";
|
$url = "http://api.phantomjscloud.com/api/browser/v2/{$key}/?request=%7Burl:%22{$link}?phantomjs=true%22,renderType:%22html%22%7D";
|
||||||
|
|
||||||
$pdfString = file_get_contents($url);
|
$pdfString = file_get_contents($url);
|
||||||
$pdfString = strip_tags($pdfString);
|
$pdfString = strip_tags($pdfString);
|
||||||
|
|
||||||
if ( ! $pdfString || strlen($pdfString) < 200) {
|
if ( ! $pdfString || strlen($pdfString) < 200) {
|
||||||
Utils::logError("PhantomJSCloud - failed to create pdf: {$pdfString}");
|
Utils::logError("PhantomJSCloud - failed to create pdf: {$pdfString}");
|
||||||
return false;
|
return false;
|
||||||
@ -861,14 +874,14 @@ class Invoice extends EntityModel implements BalanceAffecting
|
|||||||
return $total;
|
return $total;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if $calculatePaid is true we'll loop through each payment to
|
// if $calculatePaid is true we'll loop through each payment to
|
||||||
// determine the sum, otherwise we'll use the cached paid_to_date amount
|
// determine the sum, otherwise we'll use the cached paid_to_date amount
|
||||||
public function getTaxes($calculatePaid = false)
|
public function getTaxes($calculatePaid = false)
|
||||||
{
|
{
|
||||||
$taxes = [];
|
$taxes = [];
|
||||||
$taxable = $this->getTaxable();
|
$taxable = $this->getTaxable();
|
||||||
$paidAmount = $this->getAmountPaid($calculatePaid);
|
$paidAmount = $this->getAmountPaid($calculatePaid);
|
||||||
|
|
||||||
if ($this->tax_name1) {
|
if ($this->tax_name1) {
|
||||||
$invoiceTaxAmount = round($taxable * ($this->tax_rate1 / 100), 2);
|
$invoiceTaxAmount = round($taxable * ($this->tax_rate1 / 100), 2);
|
||||||
$invoicePaidAmount = $this->amount && $invoiceTaxAmount ? ($paidAmount / $this->amount * $invoiceTaxAmount) : 0;
|
$invoicePaidAmount = $this->amount && $invoiceTaxAmount ? ($paidAmount / $this->amount * $invoiceTaxAmount) : 0;
|
||||||
@ -883,7 +896,7 @@ class Invoice extends EntityModel implements BalanceAffecting
|
|||||||
|
|
||||||
foreach ($this->invoice_items as $invoiceItem) {
|
foreach ($this->invoice_items as $invoiceItem) {
|
||||||
$itemTaxAmount = $this->getItemTaxable($invoiceItem, $taxable);
|
$itemTaxAmount = $this->getItemTaxable($invoiceItem, $taxable);
|
||||||
|
|
||||||
if ($invoiceItem->tax_name1) {
|
if ($invoiceItem->tax_name1) {
|
||||||
$itemTaxAmount = round($taxable * ($invoiceItem->tax_rate1 / 100), 2);
|
$itemTaxAmount = round($taxable * ($invoiceItem->tax_rate1 / 100), 2);
|
||||||
$itemPaidAmount = $this->amount && $itemTaxAmount ? ($paidAmount / $this->amount * $itemTaxAmount) : 0;
|
$itemPaidAmount = $this->amount && $itemTaxAmount ? ($paidAmount / $this->amount * $itemTaxAmount) : 0;
|
||||||
@ -896,20 +909,20 @@ class Invoice extends EntityModel implements BalanceAffecting
|
|||||||
$this->calculateTax($taxes, $invoiceItem->tax_name2, $invoiceItem->tax_rate2, $itemTaxAmount, $itemPaidAmount);
|
$this->calculateTax($taxes, $invoiceItem->tax_name2, $invoiceItem->tax_rate2, $itemTaxAmount, $itemPaidAmount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $taxes;
|
return $taxes;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function calculateTax(&$taxes, $name, $rate, $amount, $paid)
|
private function calculateTax(&$taxes, $name, $rate, $amount, $paid)
|
||||||
{
|
{
|
||||||
if ( ! $amount) {
|
if ( ! $amount) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$amount = round($amount, 2);
|
$amount = round($amount, 2);
|
||||||
$paid = round($paid, 2);
|
$paid = round($paid, 2);
|
||||||
$key = $rate . ' ' . $name;
|
$key = $rate . ' ' . $name;
|
||||||
|
|
||||||
if ( ! isset($taxes[$key])) {
|
if ( ! isset($taxes[$key])) {
|
||||||
$taxes[$key] = [
|
$taxes[$key] = [
|
||||||
'name' => $name,
|
'name' => $name,
|
||||||
@ -920,14 +933,14 @@ class Invoice extends EntityModel implements BalanceAffecting
|
|||||||
}
|
}
|
||||||
|
|
||||||
$taxes[$key]['amount'] += $amount;
|
$taxes[$key]['amount'] += $amount;
|
||||||
$taxes[$key]['paid'] += $paid;
|
$taxes[$key]['paid'] += $paid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function hasDocuments(){
|
public function hasDocuments(){
|
||||||
if(count($this->documents))return true;
|
if(count($this->documents))return true;
|
||||||
return $this->hasExpenseDocuments();
|
return $this->hasExpenseDocuments();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function hasExpenseDocuments(){
|
public function hasExpenseDocuments(){
|
||||||
foreach($this->expenses as $expense){
|
foreach($this->expenses as $expense){
|
||||||
if(count($expense->documents))return true;
|
if(count($expense->documents))return true;
|
||||||
@ -957,7 +970,7 @@ Invoice::creating(function ($invoice) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Invoice::created(function ($invoice) {
|
Invoice::created(function ($invoice) {
|
||||||
if ($invoice->is_quote) {
|
if ($invoice->isType(INVOICE_TYPE_QUOTE)) {
|
||||||
event(new QuoteWasCreated($invoice));
|
event(new QuoteWasCreated($invoice));
|
||||||
} else {
|
} else {
|
||||||
event(new InvoiceWasCreated($invoice));
|
event(new InvoiceWasCreated($invoice));
|
||||||
@ -965,7 +978,7 @@ Invoice::created(function ($invoice) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Invoice::updating(function ($invoice) {
|
Invoice::updating(function ($invoice) {
|
||||||
if ($invoice->is_quote) {
|
if ($invoice->isType(INVOICE_TYPE_QUOTE)) {
|
||||||
event(new QuoteWasUpdated($invoice));
|
event(new QuoteWasUpdated($invoice));
|
||||||
} else {
|
} else {
|
||||||
event(new InvoiceWasUpdated($invoice));
|
event(new InvoiceWasUpdated($invoice));
|
||||||
|
@ -16,6 +16,24 @@ class Product extends EntityModel
|
|||||||
'default_tax_rate_id',
|
'default_tax_rate_id',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public static function getImportColumns()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'product_key',
|
||||||
|
'notes',
|
||||||
|
'cost',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getImportMap()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'product|item' => 'product_key',
|
||||||
|
'notes|description|details' => 'notes',
|
||||||
|
'cost|amount|price' => 'cost',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
public function getEntityType()
|
public function getEntityType()
|
||||||
{
|
{
|
||||||
return ENTITY_PRODUCT;
|
return ENTITY_PRODUCT;
|
||||||
|
@ -15,21 +15,38 @@ class BaseTransformer extends TransformerAbstract
|
|||||||
|
|
||||||
protected function hasClient($name)
|
protected function hasClient($name)
|
||||||
{
|
{
|
||||||
$name = strtolower($name);
|
$name = trim(strtolower($name));
|
||||||
return isset($this->maps[ENTITY_CLIENT][$name]);
|
return isset($this->maps[ENTITY_CLIENT][$name]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function hasProduct($key)
|
||||||
|
{
|
||||||
|
$key = trim(strtolower($key));
|
||||||
|
return isset($this->maps[ENTITY_PRODUCT][$key]);
|
||||||
|
}
|
||||||
|
|
||||||
protected function getString($data, $field)
|
protected function getString($data, $field)
|
||||||
{
|
{
|
||||||
return (isset($data->$field) && $data->$field) ? $data->$field : '';
|
return (isset($data->$field) && $data->$field) ? $data->$field : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function getNumber($data, $field)
|
||||||
|
{
|
||||||
|
return (isset($data->$field) && $data->$field) ? $data->$field : 0;
|
||||||
|
}
|
||||||
|
|
||||||
protected function getClientId($name)
|
protected function getClientId($name)
|
||||||
{
|
{
|
||||||
$name = strtolower($name);
|
$name = strtolower($name);
|
||||||
return isset($this->maps[ENTITY_CLIENT][$name]) ? $this->maps[ENTITY_CLIENT][$name] : null;
|
return isset($this->maps[ENTITY_CLIENT][$name]) ? $this->maps[ENTITY_CLIENT][$name] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function getProductId($name)
|
||||||
|
{
|
||||||
|
$name = strtolower($name);
|
||||||
|
return isset($this->maps[ENTITY_PRODUCT][$name]) ? $this->maps[ENTITY_PRODUCT][$name] : null;
|
||||||
|
}
|
||||||
|
|
||||||
protected function getCountryId($name)
|
protected function getCountryId($name)
|
||||||
{
|
{
|
||||||
$name = strtolower($name);
|
$name = strtolower($name);
|
||||||
@ -53,7 +70,7 @@ class BaseTransformer extends TransformerAbstract
|
|||||||
if ( ! $date instanceof DateTime) {
|
if ( ! $date instanceof DateTime) {
|
||||||
$date = DateTime::createFromFormat($format, $date);
|
$date = DateTime::createFromFormat($format, $date);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $date ? $date->format('Y-m-d') : null;
|
return $date ? $date->format('Y-m-d') : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,11 +104,11 @@ class BaseTransformer extends TransformerAbstract
|
|||||||
return isset($this->maps[ENTITY_INVOICE.'_'.ENTITY_CLIENT][$invoiceNumber])? $this->maps[ENTITY_INVOICE.'_'.ENTITY_CLIENT][$invoiceNumber] : null;
|
return isset($this->maps[ENTITY_INVOICE.'_'.ENTITY_CLIENT][$invoiceNumber])? $this->maps[ENTITY_INVOICE.'_'.ENTITY_CLIENT][$invoiceNumber] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected function getVendorId($name)
|
protected function getVendorId($name)
|
||||||
{
|
{
|
||||||
$name = strtolower($name);
|
$name = strtolower($name);
|
||||||
return isset($this->maps[ENTITY_VENDOR][$name]) ? $this->maps[ENTITY_VENDOR][$name] : null;
|
return isset($this->maps[ENTITY_VENDOR][$name]) ? $this->maps[ENTITY_VENDOR][$name] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
22
app/Ninja/Import/CSV/ProductTransformer.php
Normal file
22
app/Ninja/Import/CSV/ProductTransformer.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php namespace App\Ninja\Import\CSV;
|
||||||
|
|
||||||
|
use App\Ninja\Import\BaseTransformer;
|
||||||
|
use League\Fractal\Resource\Item;
|
||||||
|
|
||||||
|
class ProductTransformer extends BaseTransformer
|
||||||
|
{
|
||||||
|
public function transform($data)
|
||||||
|
{
|
||||||
|
if (empty($data->product_key) || $this->hasProduct($data->product_key)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Item($data, function ($data) {
|
||||||
|
return [
|
||||||
|
'product_key' => $this->getString($data, 'product_key'),
|
||||||
|
'notes' => $this->getString($data, 'notes'),
|
||||||
|
'cost' => $this->getNumber($data, 'cost'),
|
||||||
|
];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -60,7 +60,7 @@ class ContactMailer extends Mailer
|
|||||||
|
|
||||||
$sent = false;
|
$sent = false;
|
||||||
|
|
||||||
if ($account->attatchPDF() && !$pdfString) {
|
if ($account->attachPDF() && !$pdfString) {
|
||||||
$pdfString = $invoice->getPDFString();
|
$pdfString = $invoice->getPDFString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,7 +97,7 @@ class ContactMailer extends Mailer
|
|||||||
$account->loadLocalizationSettings();
|
$account->loadLocalizationSettings();
|
||||||
|
|
||||||
if ($sent === true) {
|
if ($sent === true) {
|
||||||
if ($invoice->is_quote) {
|
if ($invoice->isType(INVOICE_TYPE_QUOTE)) {
|
||||||
event(new QuoteWasEmailed($invoice));
|
event(new QuoteWasEmailed($invoice));
|
||||||
} else {
|
} else {
|
||||||
event(new InvoiceWasEmailed($invoice));
|
event(new InvoiceWasEmailed($invoice));
|
||||||
@ -176,7 +176,7 @@ class ContactMailer extends Mailer
|
|||||||
'documents' => $documentStrings,
|
'documents' => $documentStrings,
|
||||||
];
|
];
|
||||||
|
|
||||||
if ($account->attatchPDF()) {
|
if ($account->attachPDF()) {
|
||||||
$data['pdfString'] = $pdfString;
|
$data['pdfString'] = $pdfString;
|
||||||
$data['pdfFileName'] = $invoice->getFileName();
|
$data['pdfFileName'] = $invoice->getFileName();
|
||||||
}
|
}
|
||||||
@ -255,7 +255,7 @@ class ContactMailer extends Mailer
|
|||||||
'entityType' => ENTITY_INVOICE,
|
'entityType' => ENTITY_INVOICE,
|
||||||
];
|
];
|
||||||
|
|
||||||
if ($account->attatchPDF()) {
|
if ($account->attachPDF()) {
|
||||||
$data['pdfString'] = $invoice->getPDFString();
|
$data['pdfString'] = $invoice->getPDFString();
|
||||||
$data['pdfFileName'] = $invoice->getFileName();
|
$data['pdfFileName'] = $invoice->getFileName();
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ class InvoicePresenter extends EntityPresenter {
|
|||||||
{
|
{
|
||||||
if ($this->entity->partial > 0) {
|
if ($this->entity->partial > 0) {
|
||||||
return 'partial_due';
|
return 'partial_due';
|
||||||
} elseif ($this->entity->is_quote) {
|
} elseif ($this->entity->isType(INVOICE_TYPE_QUOTE)) {
|
||||||
return 'total';
|
return 'total';
|
||||||
} else {
|
} else {
|
||||||
return 'balance_due';
|
return 'balance_due';
|
||||||
|
@ -57,43 +57,49 @@ class DocumentRepository extends BaseRepository
|
|||||||
return $query;
|
return $query;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function upload($uploaded, &$doc_array=null)
|
public function upload($data, &$doc_array=null)
|
||||||
{
|
{
|
||||||
|
$uploaded = $data['file'];
|
||||||
$extension = strtolower($uploaded->getClientOriginalExtension());
|
$extension = strtolower($uploaded->getClientOriginalExtension());
|
||||||
if(empty(Document::$types[$extension]) && !empty(Document::$extraExtensions[$extension])){
|
if(empty(Document::$types[$extension]) && !empty(Document::$extraExtensions[$extension])){
|
||||||
$documentType = Document::$extraExtensions[$extension];
|
$documentType = Document::$extraExtensions[$extension];
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
$documentType = $extension;
|
$documentType = $extension;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(empty(Document::$types[$documentType])){
|
if(empty(Document::$types[$documentType])){
|
||||||
return 'Unsupported file type';
|
return 'Unsupported file type';
|
||||||
}
|
}
|
||||||
|
|
||||||
$documentTypeData = Document::$types[$documentType];
|
$documentTypeData = Document::$types[$documentType];
|
||||||
|
|
||||||
$filePath = $uploaded->path();
|
$filePath = $uploaded->path();
|
||||||
$name = $uploaded->getClientOriginalName();
|
$name = $uploaded->getClientOriginalName();
|
||||||
$size = filesize($filePath);
|
$size = filesize($filePath);
|
||||||
|
|
||||||
if($size/1000 > MAX_DOCUMENT_SIZE){
|
if($size/1000 > MAX_DOCUMENT_SIZE){
|
||||||
return 'File too large';
|
return 'File too large';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// don't allow a document to be linked to both an invoice and an expense
|
||||||
|
if (array_get($data, 'invoice_id') && array_get($data, 'expense_id')) {
|
||||||
|
unset($data['expense_id']);
|
||||||
|
}
|
||||||
|
|
||||||
$hash = sha1_file($filePath);
|
$hash = sha1_file($filePath);
|
||||||
$filename = \Auth::user()->account->account_key.'/'.$hash.'.'.$documentType;
|
$filename = \Auth::user()->account->account_key.'/'.$hash.'.'.$documentType;
|
||||||
|
|
||||||
$document = Document::createNew();
|
$document = Document::createNew();
|
||||||
|
$document->fill($data);
|
||||||
|
|
||||||
$disk = $document->getDisk();
|
$disk = $document->getDisk();
|
||||||
if(!$disk->exists($filename)){// Have we already stored the same file
|
if(!$disk->exists($filename)){// Have we already stored the same file
|
||||||
$stream = fopen($filePath, 'r');
|
$stream = fopen($filePath, 'r');
|
||||||
$disk->getDriver()->putStream($filename, $stream, ['mimetype'=>$documentTypeData['mime']]);
|
$disk->getDriver()->putStream($filename, $stream, ['mimetype'=>$documentTypeData['mime']]);
|
||||||
fclose($stream);
|
fclose($stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is an image; check if we need to create a preview
|
// This is an image; check if we need to create a preview
|
||||||
if(in_array($documentType, array('jpeg','png','gif','bmp','tiff','psd'))){
|
if(in_array($documentType, array('jpeg','png','gif','bmp','tiff','psd'))){
|
||||||
$makePreview = false;
|
$makePreview = false;
|
||||||
@ -105,32 +111,32 @@ class DocumentRepository extends BaseRepository
|
|||||||
// Needs to be converted
|
// Needs to be converted
|
||||||
$makePreview = true;
|
$makePreview = true;
|
||||||
} else if($width > DOCUMENT_PREVIEW_SIZE || $height > DOCUMENT_PREVIEW_SIZE){
|
} else if($width > DOCUMENT_PREVIEW_SIZE || $height > DOCUMENT_PREVIEW_SIZE){
|
||||||
$makePreview = true;
|
$makePreview = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(in_array($documentType,array('bmp','tiff','psd'))){
|
if(in_array($documentType,array('bmp','tiff','psd'))){
|
||||||
if(!class_exists('Imagick')){
|
if(!class_exists('Imagick')){
|
||||||
// Cant't read this
|
// Cant't read this
|
||||||
$makePreview = false;
|
$makePreview = false;
|
||||||
} else {
|
} else {
|
||||||
$imgManagerConfig['driver'] = 'imagick';
|
$imgManagerConfig['driver'] = 'imagick';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if($makePreview){
|
if($makePreview){
|
||||||
$previewType = 'jpeg';
|
$previewType = 'jpeg';
|
||||||
if(in_array($documentType, array('png','gif','tiff','psd'))){
|
if(in_array($documentType, array('png','gif','tiff','psd'))){
|
||||||
// Has transparency
|
// Has transparency
|
||||||
$previewType = 'png';
|
$previewType = 'png';
|
||||||
}
|
}
|
||||||
|
|
||||||
$document->preview = \Auth::user()->account->account_key.'/'.$hash.'.'.$documentType.'.x'.DOCUMENT_PREVIEW_SIZE.'.'.$previewType;
|
$document->preview = \Auth::user()->account->account_key.'/'.$hash.'.'.$documentType.'.x'.DOCUMENT_PREVIEW_SIZE.'.'.$previewType;
|
||||||
if(!$disk->exists($document->preview)){
|
if(!$disk->exists($document->preview)){
|
||||||
// We haven't created a preview yet
|
// We haven't created a preview yet
|
||||||
$imgManager = new ImageManager($imgManagerConfig);
|
$imgManager = new ImageManager($imgManagerConfig);
|
||||||
|
|
||||||
$img = $imgManager->make($filePath);
|
$img = $imgManager->make($filePath);
|
||||||
|
|
||||||
if($width <= DOCUMENT_PREVIEW_SIZE && $height <= DOCUMENT_PREVIEW_SIZE){
|
if($width <= DOCUMENT_PREVIEW_SIZE && $height <= DOCUMENT_PREVIEW_SIZE){
|
||||||
$previewWidth = $width;
|
$previewWidth = $width;
|
||||||
$previewHeight = $height;
|
$previewHeight = $height;
|
||||||
@ -141,9 +147,9 @@ class DocumentRepository extends BaseRepository
|
|||||||
$previewHeight = DOCUMENT_PREVIEW_SIZE;
|
$previewHeight = DOCUMENT_PREVIEW_SIZE;
|
||||||
$previewWidth = $width * DOCUMENT_PREVIEW_SIZE / $height;
|
$previewWidth = $width * DOCUMENT_PREVIEW_SIZE / $height;
|
||||||
}
|
}
|
||||||
|
|
||||||
$img->resize($previewWidth, $previewHeight);
|
$img->resize($previewWidth, $previewHeight);
|
||||||
|
|
||||||
$previewContent = (string) $img->encode($previewType);
|
$previewContent = (string) $img->encode($previewType);
|
||||||
$disk->put($document->preview, $previewContent);
|
$disk->put($document->preview, $previewContent);
|
||||||
$base64 = base64_encode($previewContent);
|
$base64 = base64_encode($previewContent);
|
||||||
@ -153,23 +159,23 @@ class DocumentRepository extends BaseRepository
|
|||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
$base64 = base64_encode(file_get_contents($filePath));
|
$base64 = base64_encode(file_get_contents($filePath));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$document->path = $filename;
|
$document->path = $filename;
|
||||||
$document->type = $documentType;
|
$document->type = $documentType;
|
||||||
$document->size = $size;
|
$document->size = $size;
|
||||||
$document->hash = $hash;
|
$document->hash = $hash;
|
||||||
$document->name = substr($name, -255);
|
$document->name = substr($name, -255);
|
||||||
|
|
||||||
if(!empty($imageSize)){
|
if(!empty($imageSize)){
|
||||||
$document->width = $imageSize[0];
|
$document->width = $imageSize[0];
|
||||||
$document->height = $imageSize[1];
|
$document->height = $imageSize[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
$document->save();
|
$document->save();
|
||||||
$doc_array = $document->toArray();
|
$doc_array = $document->toArray();
|
||||||
|
|
||||||
if(!empty($base64)){
|
if(!empty($base64)){
|
||||||
$mime = Document::$types[!empty($previewType)?$previewType:$documentType]['mime'];
|
$mime = Document::$types[!empty($previewType)?$previewType:$documentType]['mime'];
|
||||||
$doc_array['base64'] = 'data:'.$mime.';base64,'.$base64;
|
$doc_array['base64'] = 'data:'.$mime.';base64,'.$base64;
|
||||||
@ -177,10 +183,10 @@ class DocumentRepository extends BaseRepository
|
|||||||
|
|
||||||
return $document;
|
return $document;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getClientDatatable($contactId, $entityType, $search)
|
public function getClientDatatable($contactId, $entityType, $search)
|
||||||
{
|
{
|
||||||
|
|
||||||
$query = DB::table('invitations')
|
$query = DB::table('invitations')
|
||||||
->join('accounts', 'accounts.id', '=', 'invitations.account_id')
|
->join('accounts', 'accounts.id', '=', 'invitations.account_id')
|
||||||
->join('invoices', 'invoices.id', '=', 'invitations.invoice_id')
|
->join('invoices', 'invoices.id', '=', 'invitations.invoice_id')
|
||||||
@ -192,7 +198,7 @@ class DocumentRepository extends BaseRepository
|
|||||||
->where('clients.deleted_at', '=', null)
|
->where('clients.deleted_at', '=', null)
|
||||||
->where('invoices.is_recurring', '=', false)
|
->where('invoices.is_recurring', '=', false)
|
||||||
// This needs to be a setting to also hide the activity on the dashboard page
|
// This needs to be a setting to also hide the activity on the dashboard page
|
||||||
//->where('invoices.invoice_status_id', '>=', INVOICE_STATUS_SENT)
|
//->where('invoices.invoice_status_id', '>=', INVOICE_STATUS_SENT)
|
||||||
->select(
|
->select(
|
||||||
'invitations.invitation_key',
|
'invitations.invitation_key',
|
||||||
'invoices.invoice_number',
|
'invoices.invoice_number',
|
||||||
@ -205,22 +211,22 @@ class DocumentRepository extends BaseRepository
|
|||||||
$table = \Datatable::query($query)
|
$table = \Datatable::query($query)
|
||||||
->addColumn('invoice_number', function ($model) {
|
->addColumn('invoice_number', function ($model) {
|
||||||
return link_to(
|
return link_to(
|
||||||
'/view/'.$model->invitation_key,
|
'/view/'.$model->invitation_key,
|
||||||
$model->invoice_number
|
$model->invoice_number
|
||||||
)->toHtml();
|
)->toHtml();
|
||||||
})
|
})
|
||||||
->addColumn('name', function ($model) {
|
->addColumn('name', function ($model) {
|
||||||
return link_to(
|
return link_to(
|
||||||
'/client/documents/'.$model->invitation_key.'/'.$model->public_id.'/'.$model->name,
|
'/client/documents/'.$model->invitation_key.'/'.$model->public_id.'/'.$model->name,
|
||||||
$model->name,
|
$model->name,
|
||||||
['target'=>'_blank']
|
['target'=>'_blank']
|
||||||
)->toHtml();
|
)->toHtml();
|
||||||
})
|
})
|
||||||
->addColumn('document_date', function ($model) {
|
->addColumn('document_date', function ($model) {
|
||||||
return Utils::fromSqlDate($model->created_at);
|
return Utils::dateToString($model->created_at);
|
||||||
})
|
})
|
||||||
->addColumn('document_size', function ($model) {
|
->addColumn('document_size', function ($model) {
|
||||||
return Form::human_filesize($model->size);
|
return Form::human_filesize($model->size);
|
||||||
});
|
});
|
||||||
|
|
||||||
return $table->make();
|
return $table->make();
|
||||||
|
@ -148,33 +148,17 @@ class ExpenseRepository extends BaseRepository
|
|||||||
// Documents
|
// Documents
|
||||||
$document_ids = !empty($input['document_ids'])?array_map('intval', $input['document_ids']):array();;
|
$document_ids = !empty($input['document_ids'])?array_map('intval', $input['document_ids']):array();;
|
||||||
foreach ($document_ids as $document_id){
|
foreach ($document_ids as $document_id){
|
||||||
$document = Document::scope($document_id)->first();
|
// check document completed upload before user submitted form
|
||||||
if($document && Auth::user()->can('edit', $document)){
|
if ($document_id) {
|
||||||
$document->invoice_id = null;
|
$document = Document::scope($document_id)->first();
|
||||||
$document->expense_id = $expense->id;
|
if($document && Auth::user()->can('edit', $document)){
|
||||||
$document->save();
|
$document->invoice_id = null;
|
||||||
}
|
$document->expense_id = $expense->id;
|
||||||
}
|
$document->save();
|
||||||
|
|
||||||
if(!empty($input['documents']) && Auth::user()->can('create', ENTITY_DOCUMENT)){
|
|
||||||
// Fallback upload
|
|
||||||
$doc_errors = array();
|
|
||||||
foreach($input['documents'] as $upload){
|
|
||||||
$result = $this->documentRepo->upload($upload);
|
|
||||||
if(is_string($result)){
|
|
||||||
$doc_errors[] = $result;
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
$result->expense_id = $expense->id;
|
|
||||||
$result->save();
|
|
||||||
$document_ids[] = $result->public_id;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!empty($doc_errors)){
|
|
||||||
Session::flash('error', implode('<br>',array_map('htmlentities',$doc_errors)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// prevent loading all of the documents if we don't have to
|
// prevent loading all of the documents if we don't have to
|
||||||
if ( ! $expense->wasRecentlyCreated) {
|
if ( ! $expense->wasRecentlyCreated) {
|
||||||
foreach ($expense->documents as $document){
|
foreach ($expense->documents as $document){
|
||||||
|
@ -32,9 +32,9 @@ class InvoiceRepository extends BaseRepository
|
|||||||
public function all()
|
public function all()
|
||||||
{
|
{
|
||||||
return Invoice::scope()
|
return Invoice::scope()
|
||||||
|
->invoiceType(INVOICE_TYPE_STANDARD)
|
||||||
->with('user', 'client.contacts', 'invoice_status')
|
->with('user', 'client.contacts', 'invoice_status')
|
||||||
->withTrashed()
|
->withTrashed()
|
||||||
->where('is_quote', '=', false)
|
|
||||||
->where('is_recurring', '=', false)
|
->where('is_recurring', '=', false)
|
||||||
->get();
|
->get();
|
||||||
}
|
}
|
||||||
@ -106,7 +106,7 @@ class InvoiceRepository extends BaseRepository
|
|||||||
->join('frequencies', 'frequencies.id', '=', 'invoices.frequency_id')
|
->join('frequencies', 'frequencies.id', '=', 'invoices.frequency_id')
|
||||||
->join('contacts', 'contacts.client_id', '=', 'clients.id')
|
->join('contacts', 'contacts.client_id', '=', 'clients.id')
|
||||||
->where('invoices.account_id', '=', $accountId)
|
->where('invoices.account_id', '=', $accountId)
|
||||||
->where('invoices.is_quote', '=', false)
|
->where('invoices.invoice_type_id', '=', INVOICE_TYPE_STANDARD)
|
||||||
->where('contacts.deleted_at', '=', null)
|
->where('contacts.deleted_at', '=', null)
|
||||||
->where('invoices.is_recurring', '=', true)
|
->where('invoices.is_recurring', '=', true)
|
||||||
->where('contacts.is_primary', '=', true)
|
->where('contacts.is_primary', '=', true)
|
||||||
@ -156,7 +156,7 @@ class InvoiceRepository extends BaseRepository
|
|||||||
->join('frequencies', 'frequencies.id', '=', 'invoices.frequency_id')
|
->join('frequencies', 'frequencies.id', '=', 'invoices.frequency_id')
|
||||||
->where('invitations.contact_id', '=', $contactId)
|
->where('invitations.contact_id', '=', $contactId)
|
||||||
->where('invitations.deleted_at', '=', null)
|
->where('invitations.deleted_at', '=', null)
|
||||||
->where('invoices.is_quote', '=', false)
|
->where('invoices.invoice_type_id', '=', INVOICE_TYPE_STANDARD)
|
||||||
->where('invoices.is_deleted', '=', false)
|
->where('invoices.is_deleted', '=', false)
|
||||||
->where('clients.deleted_at', '=', null)
|
->where('clients.deleted_at', '=', null)
|
||||||
->where('invoices.is_recurring', '=', true)
|
->where('invoices.is_recurring', '=', true)
|
||||||
@ -203,14 +203,14 @@ class InvoiceRepository extends BaseRepository
|
|||||||
->join('contacts', 'contacts.client_id', '=', 'clients.id')
|
->join('contacts', 'contacts.client_id', '=', 'clients.id')
|
||||||
->where('invitations.contact_id', '=', $contactId)
|
->where('invitations.contact_id', '=', $contactId)
|
||||||
->where('invitations.deleted_at', '=', null)
|
->where('invitations.deleted_at', '=', null)
|
||||||
->where('invoices.is_quote', '=', $entityType == ENTITY_QUOTE)
|
->where('invoices.invoice_type_id', '=', $entityType == ENTITY_QUOTE ? INVOICE_TYPE_QUOTE : INVOICE_TYPE_STANDARD)
|
||||||
->where('invoices.is_deleted', '=', false)
|
->where('invoices.is_deleted', '=', false)
|
||||||
->where('clients.deleted_at', '=', null)
|
->where('clients.deleted_at', '=', null)
|
||||||
->where('contacts.deleted_at', '=', null)
|
->where('contacts.deleted_at', '=', null)
|
||||||
->where('contacts.is_primary', '=', true)
|
->where('contacts.is_primary', '=', true)
|
||||||
->where('invoices.is_recurring', '=', false)
|
->where('invoices.is_recurring', '=', false)
|
||||||
// This needs to be a setting to also hide the activity on the dashboard page
|
// This needs to be a setting to also hide the activity on the dashboard page
|
||||||
//->where('invoices.invoice_status_id', '>=', INVOICE_STATUS_SENT)
|
//->where('invoices.invoice_status_id', '>=', INVOICE_STATUS_SENT)
|
||||||
->select(
|
->select(
|
||||||
DB::raw('COALESCE(clients.currency_id, accounts.currency_id) currency_id'),
|
DB::raw('COALESCE(clients.currency_id, accounts.currency_id) currency_id'),
|
||||||
DB::raw('COALESCE(clients.country_id, accounts.country_id) country_id'),
|
DB::raw('COALESCE(clients.country_id, accounts.country_id) country_id'),
|
||||||
@ -287,7 +287,7 @@ class InvoiceRepository extends BaseRepository
|
|||||||
$account->invoice_footer = trim($data['invoice_footer']);
|
$account->invoice_footer = trim($data['invoice_footer']);
|
||||||
}
|
}
|
||||||
$account->save();
|
$account->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($data['invoice_number']) && !$invoice->is_recurring) {
|
if (isset($data['invoice_number']) && !$invoice->is_recurring) {
|
||||||
$invoice->invoice_number = trim($data['invoice_number']);
|
$invoice->invoice_number = trim($data['invoice_number']);
|
||||||
@ -329,7 +329,7 @@ class InvoiceRepository extends BaseRepository
|
|||||||
if ($invoice->auto_bill < AUTO_BILL_OFF || $invoice->auto_bill > AUTO_BILL_ALWAYS ) {
|
if ($invoice->auto_bill < AUTO_BILL_OFF || $invoice->auto_bill > AUTO_BILL_ALWAYS ) {
|
||||||
$invoice->auto_bill = AUTO_BILL_OFF;
|
$invoice->auto_bill = AUTO_BILL_OFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($data['recurring_due_date'])) {
|
if (isset($data['recurring_due_date'])) {
|
||||||
$invoice->due_date = $data['recurring_due_date'];
|
$invoice->due_date = $data['recurring_due_date'];
|
||||||
} elseif (isset($data['due_date'])) {
|
} elseif (isset($data['due_date'])) {
|
||||||
@ -351,7 +351,7 @@ class InvoiceRepository extends BaseRepository
|
|||||||
} else {
|
} else {
|
||||||
$invoice->terms = '';
|
$invoice->terms = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
$invoice->invoice_footer = (isset($data['invoice_footer']) && trim($data['invoice_footer'])) ? trim($data['invoice_footer']) : (!$publicId && $account->invoice_footer ? $account->invoice_footer : '');
|
$invoice->invoice_footer = (isset($data['invoice_footer']) && trim($data['invoice_footer'])) ? trim($data['invoice_footer']) : (!$publicId && $account->invoice_footer ? $account->invoice_footer : '');
|
||||||
$invoice->public_notes = isset($data['public_notes']) ? trim($data['public_notes']) : null;
|
$invoice->public_notes = isset($data['public_notes']) ? trim($data['public_notes']) : null;
|
||||||
|
|
||||||
@ -370,8 +370,8 @@ class InvoiceRepository extends BaseRepository
|
|||||||
|
|
||||||
// provide backwards compatability
|
// provide backwards compatability
|
||||||
if (isset($data['tax_name']) && isset($data['tax_rate'])) {
|
if (isset($data['tax_name']) && isset($data['tax_rate'])) {
|
||||||
$data['tax_name1'] = $data['tax_name'];
|
$data['tax_name1'] = $data['tax_name'];
|
||||||
$data['tax_rate1'] = $data['tax_rate'];
|
$data['tax_rate1'] = $data['tax_rate'];
|
||||||
}
|
}
|
||||||
|
|
||||||
$total = 0;
|
$total = 0;
|
||||||
@ -405,11 +405,11 @@ class InvoiceRepository extends BaseRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isset($item['tax_rate1']) && Utils::parseFloat($item['tax_rate1']) > 0) {
|
if (isset($item['tax_rate1']) && Utils::parseFloat($item['tax_rate1']) > 0) {
|
||||||
$invoiceItemTaxRate = Utils::parseFloat($item['tax_rate1']);
|
$invoiceItemTaxRate = Utils::parseFloat($item['tax_rate1']);
|
||||||
$itemTax += round($lineTotal * $invoiceItemTaxRate / 100, 2);
|
$itemTax += round($lineTotal * $invoiceItemTaxRate / 100, 2);
|
||||||
}
|
}
|
||||||
if (isset($item['tax_rate2']) && Utils::parseFloat($item['tax_rate2']) > 0) {
|
if (isset($item['tax_rate2']) && Utils::parseFloat($item['tax_rate2']) > 0) {
|
||||||
$invoiceItemTaxRate = Utils::parseFloat($item['tax_rate2']);
|
$invoiceItemTaxRate = Utils::parseFloat($item['tax_rate2']);
|
||||||
$itemTax += round($lineTotal * $invoiceItemTaxRate / 100, 2);
|
$itemTax += round($lineTotal * $invoiceItemTaxRate / 100, 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -453,7 +453,7 @@ class InvoiceRepository extends BaseRepository
|
|||||||
|
|
||||||
$taxAmount1 = round($total * $invoice->tax_rate1 / 100, 2);
|
$taxAmount1 = round($total * $invoice->tax_rate1 / 100, 2);
|
||||||
$taxAmount2 = round($total * $invoice->tax_rate2 / 100, 2);
|
$taxAmount2 = round($total * $invoice->tax_rate2 / 100, 2);
|
||||||
$total = round($total + $taxAmount1 + $taxAmount2, 2);
|
$total = round($total + $taxAmount1 + $taxAmount2, 2);
|
||||||
$total += $itemTax;
|
$total += $itemTax;
|
||||||
|
|
||||||
// custom fields not charged taxes
|
// custom fields not charged taxes
|
||||||
@ -476,43 +476,24 @@ class InvoiceRepository extends BaseRepository
|
|||||||
if ($publicId) {
|
if ($publicId) {
|
||||||
$invoice->invoice_items()->forceDelete();
|
$invoice->invoice_items()->forceDelete();
|
||||||
}
|
}
|
||||||
|
|
||||||
$document_ids = !empty($data['document_ids'])?array_map('intval', $data['document_ids']):array();;
|
$document_ids = !empty($data['document_ids'])?array_map('intval', $data['document_ids']):array();;
|
||||||
foreach ($document_ids as $document_id){
|
foreach ($document_ids as $document_id){
|
||||||
$document = Document::scope($document_id)->first();
|
$document = Document::scope($document_id)->first();
|
||||||
if($document && Auth::user()->can('edit', $document)){
|
if($document && Auth::user()->can('edit', $document)){
|
||||||
|
|
||||||
if($document->invoice_id && $document->invoice_id != $invoice->id){
|
if($document->invoice_id && $document->invoice_id != $invoice->id){
|
||||||
// From a clone
|
// From a clone
|
||||||
$document = $document->cloneDocument();
|
$document = $document->cloneDocument();
|
||||||
$document_ids[] = $document->public_id;// Don't remove this document
|
$document_ids[] = $document->public_id;// Don't remove this document
|
||||||
}
|
}
|
||||||
|
|
||||||
$document->invoice_id = $invoice->id;
|
$document->invoice_id = $invoice->id;
|
||||||
$document->expense_id = null;
|
$document->expense_id = null;
|
||||||
$document->save();
|
$document->save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!empty($data['documents']) && Auth::user()->can('create', ENTITY_DOCUMENT)){
|
|
||||||
// Fallback upload
|
|
||||||
$doc_errors = array();
|
|
||||||
foreach($data['documents'] as $upload){
|
|
||||||
$result = $this->documentRepo->upload($upload);
|
|
||||||
if(is_string($result)){
|
|
||||||
$doc_errors[] = $result;
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
$result->invoice_id = $invoice->id;
|
|
||||||
$result->save();
|
|
||||||
$document_ids[] = $result->public_id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(!empty($doc_errors)){
|
|
||||||
Session::flash('error', implode('<br>',array_map('htmlentities',$doc_errors)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($invoice->documents as $document){
|
foreach ($invoice->documents as $document){
|
||||||
if(!in_array($document->public_id, $document_ids)){
|
if(!in_array($document->public_id, $document_ids)){
|
||||||
// Removed
|
// Removed
|
||||||
@ -586,12 +567,12 @@ class InvoiceRepository extends BaseRepository
|
|||||||
|
|
||||||
// provide backwards compatability
|
// provide backwards compatability
|
||||||
if (isset($item['tax_name']) && isset($item['tax_rate'])) {
|
if (isset($item['tax_name']) && isset($item['tax_rate'])) {
|
||||||
$item['tax_name1'] = $item['tax_name'];
|
$item['tax_name1'] = $item['tax_name'];
|
||||||
$item['tax_rate1'] = $item['tax_rate'];
|
$item['tax_rate1'] = $item['tax_rate'];
|
||||||
}
|
}
|
||||||
|
|
||||||
$invoiceItem->fill($item);
|
$invoiceItem->fill($item);
|
||||||
|
|
||||||
$invoice->invoice_items()->save($invoiceItem);
|
$invoice->invoice_items()->save($invoiceItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -642,7 +623,7 @@ class InvoiceRepository extends BaseRepository
|
|||||||
'tax_name2',
|
'tax_name2',
|
||||||
'tax_rate2',
|
'tax_rate2',
|
||||||
'amount',
|
'amount',
|
||||||
'is_quote',
|
'invoice_type_id',
|
||||||
'custom_value1',
|
'custom_value1',
|
||||||
'custom_value2',
|
'custom_value2',
|
||||||
'custom_taxes1',
|
'custom_taxes1',
|
||||||
@ -654,7 +635,7 @@ class InvoiceRepository extends BaseRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($quotePublicId) {
|
if ($quotePublicId) {
|
||||||
$clone->is_quote = false;
|
$clone->invoice_type_id = INVOICE_TYPE_STANDARD;
|
||||||
$clone->quote_id = $quotePublicId;
|
$clone->quote_id = $quotePublicId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -675,9 +656,9 @@ class InvoiceRepository extends BaseRepository
|
|||||||
'cost',
|
'cost',
|
||||||
'qty',
|
'qty',
|
||||||
'tax_name1',
|
'tax_name1',
|
||||||
'tax_rate1',
|
'tax_rate1',
|
||||||
'tax_name2',
|
'tax_name2',
|
||||||
'tax_rate2',
|
'tax_rate2',
|
||||||
] as $field) {
|
] as $field) {
|
||||||
$cloneItem->$field = $item->$field;
|
$cloneItem->$field = $item->$field;
|
||||||
}
|
}
|
||||||
@ -686,7 +667,7 @@ class InvoiceRepository extends BaseRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach ($invoice->documents as $document) {
|
foreach ($invoice->documents as $document) {
|
||||||
$cloneDocument = $document->cloneDocument();
|
$cloneDocument = $document->cloneDocument();
|
||||||
$invoice->documents()->save($cloneDocument);
|
$invoice->documents()->save($cloneDocument);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -731,8 +712,8 @@ class InvoiceRepository extends BaseRepository
|
|||||||
public function findOpenInvoices($clientId)
|
public function findOpenInvoices($clientId)
|
||||||
{
|
{
|
||||||
return Invoice::scope()
|
return Invoice::scope()
|
||||||
|
->invoiceType(INVOICE_TYPE_STANDARD)
|
||||||
->whereClientId($clientId)
|
->whereClientId($clientId)
|
||||||
->whereIsQuote(false)
|
|
||||||
->whereIsRecurring(false)
|
->whereIsRecurring(false)
|
||||||
->whereDeletedAt(null)
|
->whereDeletedAt(null)
|
||||||
->whereHasTasks(true)
|
->whereHasTasks(true)
|
||||||
@ -760,7 +741,7 @@ class InvoiceRepository extends BaseRepository
|
|||||||
$invoice = Invoice::createNew($recurInvoice);
|
$invoice = Invoice::createNew($recurInvoice);
|
||||||
$invoice->client_id = $recurInvoice->client_id;
|
$invoice->client_id = $recurInvoice->client_id;
|
||||||
$invoice->recurring_invoice_id = $recurInvoice->id;
|
$invoice->recurring_invoice_id = $recurInvoice->id;
|
||||||
$invoice->invoice_number = $recurInvoice->account->recurring_invoice_number_prefix . $recurInvoice->account->getNextInvoiceNumber($recurInvoice);
|
$invoice->invoice_number = $recurInvoice->account->getNextInvoiceNumber($invoice);
|
||||||
$invoice->amount = $recurInvoice->amount;
|
$invoice->amount = $recurInvoice->amount;
|
||||||
$invoice->balance = $recurInvoice->amount;
|
$invoice->balance = $recurInvoice->amount;
|
||||||
$invoice->invoice_date = date_create()->format('Y-m-d');
|
$invoice->invoice_date = date_create()->format('Y-m-d');
|
||||||
@ -839,9 +820,9 @@ class InvoiceRepository extends BaseRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
$sql = implode(' OR ', $dates);
|
$sql = implode(' OR ', $dates);
|
||||||
$invoices = Invoice::whereAccountId($account->id)
|
$invoices = Invoice::invoiceType(INVOICE_TYPE_STANDARD)
|
||||||
|
->whereAccountId($account->id)
|
||||||
->where('balance', '>', 0)
|
->where('balance', '>', 0)
|
||||||
->where('is_quote', '=', false)
|
|
||||||
->where('is_recurring', '=', false)
|
->where('is_recurring', '=', false)
|
||||||
->whereRaw('('.$sql.')')
|
->whereRaw('('.$sql.')')
|
||||||
->get();
|
->get();
|
||||||
|
@ -11,6 +11,13 @@ class ProductRepository extends BaseRepository
|
|||||||
return 'App\Models\Product';
|
return 'App\Models\Product';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function all()
|
||||||
|
{
|
||||||
|
return Product::scope()
|
||||||
|
->withTrashed()
|
||||||
|
->get();
|
||||||
|
}
|
||||||
|
|
||||||
public function find($accountId)
|
public function find($accountId)
|
||||||
{
|
{
|
||||||
return DB::table('products')
|
return DB::table('products')
|
||||||
@ -30,11 +37,11 @@ class ProductRepository extends BaseRepository
|
|||||||
'products.deleted_at'
|
'products.deleted_at'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function save($data, $product = null)
|
public function save($data, $product = null)
|
||||||
{
|
{
|
||||||
$publicId = isset($data['public_id']) ? $data['public_id'] : false;
|
$publicId = isset($data['public_id']) ? $data['public_id'] : false;
|
||||||
|
|
||||||
if ($product) {
|
if ($product) {
|
||||||
// do nothing
|
// do nothing
|
||||||
} elseif ($publicId) {
|
} elseif ($publicId) {
|
||||||
@ -50,4 +57,4 @@ class ProductRepository extends BaseRepository
|
|||||||
return $product;
|
return $product;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ class DocumentTransformer extends EntityTransformer
|
|||||||
'type' => $document->type,
|
'type' => $document->type,
|
||||||
'invoice_id' => isset($document->invoice->public_id) ? (int) $document->invoice->public_id : null,
|
'invoice_id' => isset($document->invoice->public_id) ? (int) $document->invoice->public_id : null,
|
||||||
'expense_id' => isset($document->expense->public_id) ? (int) $document->expense->public_id : null,
|
'expense_id' => isset($document->expense->public_id) ? (int) $document->expense->public_id : null,
|
||||||
|
'updated_at' => $this->getTimestamp($document->updated_at),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ class InvoiceTransformer extends EntityTransformer
|
|||||||
public function __construct($account = null, $serializer = null, $client = null)
|
public function __construct($account = null, $serializer = null, $client = null)
|
||||||
{
|
{
|
||||||
parent::__construct($account, $serializer);
|
parent::__construct($account, $serializer);
|
||||||
|
|
||||||
$this->client = $client;
|
$this->client = $client;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,7 +93,7 @@ class InvoiceTransformer extends EntityTransformer
|
|||||||
'terms' => $invoice->terms,
|
'terms' => $invoice->terms,
|
||||||
'public_notes' => $invoice->public_notes,
|
'public_notes' => $invoice->public_notes,
|
||||||
'is_deleted' => (bool) $invoice->is_deleted,
|
'is_deleted' => (bool) $invoice->is_deleted,
|
||||||
'is_quote' => (bool) $invoice->is_quote,
|
'invoice_type_id' => (int) $invoice->invoice_type_id,
|
||||||
'is_recurring' => (bool) $invoice->is_recurring,
|
'is_recurring' => (bool) $invoice->is_recurring,
|
||||||
'frequency_id' => (int) $invoice->frequency_id,
|
'frequency_id' => (int) $invoice->frequency_id,
|
||||||
'start_date' => $invoice->start_date,
|
'start_date' => $invoice->start_date,
|
||||||
@ -119,6 +119,7 @@ class InvoiceTransformer extends EntityTransformer
|
|||||||
'quote_invoice_id' => (int) $invoice->quote_invoice_id,
|
'quote_invoice_id' => (int) $invoice->quote_invoice_id,
|
||||||
'custom_text_value1' => $invoice->custom_text_value1,
|
'custom_text_value1' => $invoice->custom_text_value1,
|
||||||
'custom_text_value2' => $invoice->custom_text_value2,
|
'custom_text_value2' => $invoice->custom_text_value2,
|
||||||
|
'is_quote' => (bool) $invoice->isType(INVOICE_TYPE_QUOTE), // Temp to support mobile app
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,8 +29,8 @@ class AppServiceProvider extends ServiceProvider {
|
|||||||
else{
|
else{
|
||||||
$contents = $image;
|
$contents = $image;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'data:image/jpeg;base64,' . base64_encode($contents);
|
return 'data:image/jpeg;base64,' . base64_encode($contents);
|
||||||
});
|
});
|
||||||
|
|
||||||
Form::macro('nav_link', function($url, $text, $url2 = '', $extra = '') {
|
Form::macro('nav_link', function($url, $text, $url2 = '', $extra = '') {
|
||||||
@ -58,11 +58,11 @@ class AppServiceProvider extends ServiceProvider {
|
|||||||
|
|
||||||
$str = '<li class="dropdown '.$class.'">
|
$str = '<li class="dropdown '.$class.'">
|
||||||
<a href="'.URL::to($types).'" class="dropdown-toggle">'.trans("texts.$types").'</a>';
|
<a href="'.URL::to($types).'" class="dropdown-toggle">'.trans("texts.$types").'</a>';
|
||||||
|
|
||||||
$items = [];
|
$items = [];
|
||||||
|
|
||||||
if($user->can('create', $type))$items[] = '<li><a href="'.URL::to($types.'/create').'">'.trans("texts.new_$type").'</a></li>';
|
if($user->can('create', $type))$items[] = '<li><a href="'.URL::to($types.'/create').'">'.trans("texts.new_$type").'</a></li>';
|
||||||
|
|
||||||
if ($type == ENTITY_INVOICE) {
|
if ($type == ENTITY_INVOICE) {
|
||||||
if(!empty($items))$items[] = '<li class="divider"></li>';
|
if(!empty($items))$items[] = '<li class="divider"></li>';
|
||||||
$items[] = '<li><a href="'.URL::to('recurring_invoices').'">'.trans("texts.recurring_invoices").'</a></li>';
|
$items[] = '<li><a href="'.URL::to('recurring_invoices').'">'.trans("texts.recurring_invoices").'</a></li>';
|
||||||
@ -81,7 +81,7 @@ class AppServiceProvider extends ServiceProvider {
|
|||||||
$items[] = '<li><a href="'.URL::to('vendors').'">'.trans("texts.vendors").'</a></li>';
|
$items[] = '<li><a href="'.URL::to('vendors').'">'.trans("texts.vendors").'</a></li>';
|
||||||
if($user->can('create', ENTITY_VENDOR))$items[] = '<li><a href="'.URL::to('vendors/create').'">'.trans("texts.new_vendor").'</a></li>';
|
if($user->can('create', ENTITY_VENDOR))$items[] = '<li><a href="'.URL::to('vendors/create').'">'.trans("texts.new_vendor").'</a></li>';
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!empty($items)){
|
if(!empty($items)){
|
||||||
$str.= '<ul class="dropdown-menu" id="menu1">'.implode($items).'</ul>';
|
$str.= '<ul class="dropdown-menu" id="menu1">'.implode($items).'</ul>';
|
||||||
}
|
}
|
||||||
@ -157,14 +157,14 @@ class AppServiceProvider extends ServiceProvider {
|
|||||||
|
|
||||||
return $str . '</ol>';
|
return $str . '</ol>';
|
||||||
});
|
});
|
||||||
|
|
||||||
Form::macro('human_filesize', function($bytes, $decimals = 1) {
|
Form::macro('human_filesize', function($bytes, $decimals = 1) {
|
||||||
$size = array('B','kB','MB','GB','TB','PB','EB','ZB','YB');
|
$size = array('B','kB','MB','GB','TB','PB','EB','ZB','YB');
|
||||||
$factor = floor((strlen($bytes) - 1) / 3);
|
$factor = floor((strlen($bytes) - 1) / 3);
|
||||||
if($factor == 0)$decimals=0;// There aren't fractional bytes
|
if($factor == 0)$decimals=0;// There aren't fractional bytes
|
||||||
return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . ' ' . @$size[$factor];
|
return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . ' ' . @$size[$factor];
|
||||||
});
|
});
|
||||||
|
|
||||||
Validator::extend('positive', function($attribute, $value, $parameters) {
|
Validator::extend('positive', function($attribute, $value, $parameters) {
|
||||||
return Utils::parseFloat($value) >= 0;
|
return Utils::parseFloat($value) >= 0;
|
||||||
});
|
});
|
||||||
|
@ -13,6 +13,7 @@ use App\Ninja\Repositories\ContactRepository;
|
|||||||
use App\Ninja\Repositories\ClientRepository;
|
use App\Ninja\Repositories\ClientRepository;
|
||||||
use App\Ninja\Repositories\InvoiceRepository;
|
use App\Ninja\Repositories\InvoiceRepository;
|
||||||
use App\Ninja\Repositories\PaymentRepository;
|
use App\Ninja\Repositories\PaymentRepository;
|
||||||
|
use App\Ninja\Repositories\ProductRepository;
|
||||||
use App\Ninja\Serializers\ArraySerializer;
|
use App\Ninja\Serializers\ArraySerializer;
|
||||||
use App\Models\Client;
|
use App\Models\Client;
|
||||||
use App\Models\Invoice;
|
use App\Models\Invoice;
|
||||||
@ -23,6 +24,7 @@ class ImportService
|
|||||||
protected $invoiceRepo;
|
protected $invoiceRepo;
|
||||||
protected $clientRepo;
|
protected $clientRepo;
|
||||||
protected $contactRepo;
|
protected $contactRepo;
|
||||||
|
protected $productRepo;
|
||||||
protected $processedRows = array();
|
protected $processedRows = array();
|
||||||
|
|
||||||
public static $entityTypes = [
|
public static $entityTypes = [
|
||||||
@ -31,6 +33,8 @@ class ImportService
|
|||||||
ENTITY_INVOICE,
|
ENTITY_INVOICE,
|
||||||
ENTITY_PAYMENT,
|
ENTITY_PAYMENT,
|
||||||
ENTITY_TASK,
|
ENTITY_TASK,
|
||||||
|
ENTITY_PRODUCT,
|
||||||
|
ENTITY_EXPENSE,
|
||||||
];
|
];
|
||||||
|
|
||||||
public static $sources = [
|
public static $sources = [
|
||||||
@ -45,7 +49,14 @@ class ImportService
|
|||||||
IMPORT_ZOHO,
|
IMPORT_ZOHO,
|
||||||
];
|
];
|
||||||
|
|
||||||
public function __construct(Manager $manager, ClientRepository $clientRepo, InvoiceRepository $invoiceRepo, PaymentRepository $paymentRepo, ContactRepository $contactRepo)
|
public function __construct(
|
||||||
|
Manager $manager,
|
||||||
|
ClientRepository $clientRepo,
|
||||||
|
InvoiceRepository $invoiceRepo,
|
||||||
|
PaymentRepository $paymentRepo,
|
||||||
|
ContactRepository $contactRepo,
|
||||||
|
ProductRepository $productRepo
|
||||||
|
)
|
||||||
{
|
{
|
||||||
$this->fractal = $manager;
|
$this->fractal = $manager;
|
||||||
$this->fractal->setSerializer(new ArraySerializer());
|
$this->fractal->setSerializer(new ArraySerializer());
|
||||||
@ -54,6 +65,7 @@ class ImportService
|
|||||||
$this->invoiceRepo = $invoiceRepo;
|
$this->invoiceRepo = $invoiceRepo;
|
||||||
$this->paymentRepo = $paymentRepo;
|
$this->paymentRepo = $paymentRepo;
|
||||||
$this->contactRepo = $contactRepo;
|
$this->contactRepo = $contactRepo;
|
||||||
|
$this->productRepo = $productRepo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function import($source, $files)
|
public function import($source, $files)
|
||||||
@ -216,8 +228,11 @@ class ImportService
|
|||||||
'invoice_number' => 'required|unique:invoices,invoice_number,,id,account_id,'.Auth::user()->account_id,
|
'invoice_number' => 'required|unique:invoices,invoice_number,,id,account_id,'.Auth::user()->account_id,
|
||||||
'discount' => 'positive',
|
'discount' => 'positive',
|
||||||
];
|
];
|
||||||
} else {
|
}
|
||||||
return true;
|
if ($entityType === ENTITY_PRODUCT) {
|
||||||
|
$rules = [
|
||||||
|
'product_key' => 'required',
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
$validator = Validator::make($data, $rules);
|
$validator = Validator::make($data, $rules);
|
||||||
@ -251,6 +266,14 @@ class ImportService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$productMap = [];
|
||||||
|
$products = $this->productRepo->all();
|
||||||
|
foreach ($products as $product) {
|
||||||
|
if ($key = strtolower(trim($product->product_key))) {
|
||||||
|
$productMap[$key] = $product->id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$countryMap = [];
|
$countryMap = [];
|
||||||
$countryMap2 = [];
|
$countryMap2 = [];
|
||||||
$countries = Cache::get('countries');
|
$countries = Cache::get('countries');
|
||||||
@ -269,6 +292,7 @@ class ImportService
|
|||||||
ENTITY_CLIENT => $clientMap,
|
ENTITY_CLIENT => $clientMap,
|
||||||
ENTITY_INVOICE => $invoiceMap,
|
ENTITY_INVOICE => $invoiceMap,
|
||||||
ENTITY_INVOICE.'_'.ENTITY_CLIENT => $invoiceClientMap,
|
ENTITY_INVOICE.'_'.ENTITY_CLIENT => $invoiceClientMap,
|
||||||
|
ENTITY_PRODUCT => $productMap,
|
||||||
'countries' => $countryMap,
|
'countries' => $countryMap,
|
||||||
'countries2' => $countryMap2,
|
'countries2' => $countryMap2,
|
||||||
'currencies' => $currencyMap,
|
'currencies' => $currencyMap,
|
||||||
@ -280,13 +304,9 @@ class ImportService
|
|||||||
$data = [];
|
$data = [];
|
||||||
|
|
||||||
foreach ($files as $entityType => $filename) {
|
foreach ($files as $entityType => $filename) {
|
||||||
if ($entityType === ENTITY_CLIENT) {
|
$class = "App\\Models\\" . ucwords($entityType);
|
||||||
$columns = Client::getImportColumns();
|
$columns = $class::getImportColumns();
|
||||||
$map = Client::getImportMap();
|
$map = $class::getImportMap();
|
||||||
} else {
|
|
||||||
$columns = Invoice::getImportColumns();
|
|
||||||
$map = Invoice::getImportMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lookup field translations
|
// Lookup field translations
|
||||||
foreach ($columns as $key => $value) {
|
foreach ($columns as $key => $value) {
|
||||||
@ -452,12 +472,8 @@ class ImportService
|
|||||||
private function convertToObject($entityType, $data, $map)
|
private function convertToObject($entityType, $data, $map)
|
||||||
{
|
{
|
||||||
$obj = new stdClass();
|
$obj = new stdClass();
|
||||||
|
$class = "App\\Models\\" . ucwords($entityType);
|
||||||
if ($entityType === ENTITY_CLIENT) {
|
$columns = $class::getImportColumns();
|
||||||
$columns = Client::getImportColumns();
|
|
||||||
} else {
|
|
||||||
$columns = Invoice::getImportColumns();
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($columns as $column) {
|
foreach ($columns as $column) {
|
||||||
$obj->$column = false;
|
$obj->$column = false;
|
||||||
|
@ -94,7 +94,7 @@ class InvoiceService extends BaseService
|
|||||||
{
|
{
|
||||||
$account = $quote->account;
|
$account = $quote->account;
|
||||||
|
|
||||||
if (!$quote->is_quote || $quote->quote_invoice_id) {
|
if (!$quote->isType(INVOICE_TYPE_QUOTE) || $quote->quote_invoice_id) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,8 +120,10 @@ class InvoiceService extends BaseService
|
|||||||
public function getDatatable($accountId, $clientPublicId = null, $entityType, $search)
|
public function getDatatable($accountId, $clientPublicId = null, $entityType, $search)
|
||||||
{
|
{
|
||||||
$datatable = new InvoiceDatatable( ! $clientPublicId, $clientPublicId);
|
$datatable = new InvoiceDatatable( ! $clientPublicId, $clientPublicId);
|
||||||
|
$datatable->entityType = $entityType;
|
||||||
|
|
||||||
$query = $this->invoiceRepo->getInvoices($accountId, $clientPublicId, $entityType, $search)
|
$query = $this->invoiceRepo->getInvoices($accountId, $clientPublicId, $entityType, $search)
|
||||||
->where('invoices.is_quote', '=', $entityType == ENTITY_QUOTE ? true : false);
|
->where('invoices.invoice_type_id', '=', $entityType == ENTITY_QUOTE ? INVOICE_TYPE_QUOTE : INVOICE_TYPE_STANDARD);
|
||||||
|
|
||||||
if(!Utils::hasPermission('view_all')){
|
if(!Utils::hasPermission('view_all')){
|
||||||
$query->where('invoices.user_id', '=', Auth::user()->id);
|
$query->where('invoices.user_id', '=', Auth::user()->id);
|
||||||
|
@ -132,7 +132,7 @@ class PushService
|
|||||||
*/
|
*/
|
||||||
private function entitySentMessage($invoice)
|
private function entitySentMessage($invoice)
|
||||||
{
|
{
|
||||||
if($invoice->is_quote)
|
if($invoice->isType(INVOICE_TYPE_QUOTE))
|
||||||
return trans("texts.notification_quote_sent_subject", ['invoice' => $invoice->invoice_number, 'client' => $invoice->client->name]);
|
return trans("texts.notification_quote_sent_subject", ['invoice' => $invoice->invoice_number, 'client' => $invoice->client->name]);
|
||||||
else
|
else
|
||||||
return trans("texts.notification_invoice_sent_subject", ['invoice' => $invoice->invoice_number, 'client' => $invoice->client->name]);
|
return trans("texts.notification_invoice_sent_subject", ['invoice' => $invoice->invoice_number, 'client' => $invoice->client->name]);
|
||||||
@ -163,7 +163,7 @@ class PushService
|
|||||||
*/
|
*/
|
||||||
private function entityViewedMessage($invoice)
|
private function entityViewedMessage($invoice)
|
||||||
{
|
{
|
||||||
if($invoice->is_quote)
|
if($invoice->isType(INVOICE_TYPE_QUOTE))
|
||||||
return trans("texts.notification_quote_viewed_subject", ['invoice' => $invoice->invoice_number, 'client' => $invoice->client->name]);
|
return trans("texts.notification_quote_viewed_subject", ['invoice' => $invoice->invoice_number, 'client' => $invoice->client->name]);
|
||||||
else
|
else
|
||||||
return trans("texts.notification_invoice_viewed_subject", ['invoice' => $invoice->invoice_number, 'client' => $invoice->client->name]);
|
return trans("texts.notification_invoice_viewed_subject", ['invoice' => $invoice->invoice_number, 'client' => $invoice->client->name]);
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
// Whether checkboxes should always be present in the POST data,
|
// Whether checkboxes should always be present in the POST data,
|
||||||
// no matter if you checked them or not
|
// no matter if you checked them or not
|
||||||
'push_checkboxes' => false,
|
'push_checkboxes' => true,
|
||||||
|
|
||||||
// The value a checkbox will have in the POST array if unchecked
|
// The value a checkbox will have in the POST array if unchecked
|
||||||
'unchecked_value' => 0,
|
'unchecked_value' => 0,
|
||||||
@ -181,4 +181,4 @@
|
|||||||
),
|
),
|
||||||
|
|
||||||
|
|
||||||
);
|
);
|
||||||
|
@ -14,9 +14,9 @@ class AddQuotes extends Migration {
|
|||||||
{
|
{
|
||||||
Schema::table('invoices', function($table)
|
Schema::table('invoices', function($table)
|
||||||
{
|
{
|
||||||
$table->boolean('is_quote')->default(0);
|
$table->boolean('invoice_type_id')->default(0);
|
||||||
$table->unsignedInteger('quote_id')->nullable();
|
$table->unsignedInteger('quote_id')->nullable();
|
||||||
$table->unsignedInteger('quote_invoice_id')->nullable();
|
$table->unsignedInteger('quote_invoice_id')->nullable();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,7 +29,7 @@ class AddQuotes extends Migration {
|
|||||||
{
|
{
|
||||||
Schema::table('invoices', function($table)
|
Schema::table('invoices', function($table)
|
||||||
{
|
{
|
||||||
$table->dropColumn('is_quote');
|
$table->dropColumn('invoice_type_id');
|
||||||
$table->dropColumn('quote_id');
|
$table->dropColumn('quote_id');
|
||||||
$table->dropColumn('quote_invoice_id');
|
$table->dropColumn('quote_invoice_id');
|
||||||
});
|
});
|
||||||
|
@ -15,8 +15,8 @@ class AddDocuments extends Migration {
|
|||||||
$table->unsignedInteger('logo_width');
|
$table->unsignedInteger('logo_width');
|
||||||
$table->unsignedInteger('logo_height');
|
$table->unsignedInteger('logo_height');
|
||||||
$table->unsignedInteger('logo_size');
|
$table->unsignedInteger('logo_size');
|
||||||
$table->boolean('invoice_embed_documents')->default(1);
|
$table->boolean('invoice_embed_documents')->default(0);
|
||||||
$table->boolean('document_email_attachment')->default(1);
|
$table->boolean('document_email_attachment')->default(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
\DB::table('accounts')->update(array('logo' => ''));
|
\DB::table('accounts')->update(array('logo' => ''));
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
|
||||||
|
class AddInvoiceTypeSupport extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
if (Schema::hasColumn('invoices', 'is_quote')) {
|
||||||
|
DB::update('update invoices set is_quote = is_quote + 1');
|
||||||
|
|
||||||
|
Schema::table('invoices', function ($table) {
|
||||||
|
$table->renameColumn('is_quote', 'invoice_type_id');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Schema::table('accounts', function($table)
|
||||||
|
{
|
||||||
|
$table->boolean('enable_second_tax_rate')->default(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
if (Schema::hasColumn('invoices', 'invoice_type_id')) {
|
||||||
|
DB::update('update invoices set invoice_type_id = invoice_type_id - 1');
|
||||||
|
}
|
||||||
|
|
||||||
|
Schema::table('accounts', function($table)
|
||||||
|
{
|
||||||
|
$table->dropColumn('enable_second_tax_rate');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
85
public/css/built.css
vendored
85
public/css/built.css
vendored
File diff suppressed because one or more lines are too long
4
public/css/built.public.css
vendored
4
public/css/built.public.css
vendored
File diff suppressed because one or more lines are too long
81
public/css/style.css
vendored
81
public/css/style.css
vendored
@ -101,7 +101,7 @@ border-bottom: 1px solid #dfe0e1;
|
|||||||
table.dataTable.no-footer {
|
table.dataTable.no-footer {
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
.table-striped>tbody>tr:nth-child(odd)>tr,
|
.table-striped>tbody>tr:nth-child(odd)>tr,
|
||||||
.table-striped>tbody>tr:nth-child(odd)>th {
|
.table-striped>tbody>tr:nth-child(odd)>th {
|
||||||
background-color: #FDFDFD;
|
background-color: #FDFDFD;
|
||||||
}
|
}
|
||||||
@ -192,7 +192,7 @@ opacity: 1;
|
|||||||
filter: alpha(opacity=100);
|
filter: alpha(opacity=100);
|
||||||
}
|
}
|
||||||
/*buttons*/
|
/*buttons*/
|
||||||
.btn { font-weight: bold;
|
.btn { font-weight: bold;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
padding: 9px 12px;
|
padding: 9px 12px;
|
||||||
}
|
}
|
||||||
@ -258,8 +258,8 @@ border-color: #0b4d78;
|
|||||||
}
|
}
|
||||||
|
|
||||||
.form-actions .btn,
|
.form-actions .btn,
|
||||||
.form-actions div.btn-group {
|
.form-actions div.btn-group {
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-actions .btn.btn-success:first-child {
|
.form-actions .btn.btn-success:first-child {
|
||||||
@ -377,7 +377,7 @@ border: none;
|
|||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background-color: #9b9b9b;
|
background-color: #9b9b9b;
|
||||||
|
|
||||||
}
|
}
|
||||||
.nav-tabs.nav-justified>li:first-child>a {
|
.nav-tabs.nav-justified>li:first-child>a {
|
||||||
border-radius: 3px 0 0 3px;
|
border-radius: 3px 0 0 3px;
|
||||||
@ -406,7 +406,7 @@ font-weight: bold;
|
|||||||
ul.dropdown-menu,
|
ul.dropdown-menu,
|
||||||
.twitter-typeahead .tt-menu {
|
.twitter-typeahead .tt-menu {
|
||||||
x-moz-box-shadow: 0 0 10px 2px rgba(0,0,0,.05);
|
x-moz-box-shadow: 0 0 10px 2px rgba(0,0,0,.05);
|
||||||
x-webkit-box-shadow: 0 0 10px 2px rgba(0,0,0,.05);
|
x-webkit-box-shadow: 0 0 10px 2px rgba(0,0,0,.05);
|
||||||
box-shadow: 0 0 10px 2px rgba(0,0,0,.05);
|
box-shadow: 0 0 10px 2px rgba(0,0,0,.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -466,7 +466,7 @@ background-clip: padding-box;
|
|||||||
|
|
||||||
|
|
||||||
/***********************************************
|
/***********************************************
|
||||||
Dashboard
|
Dashboard
|
||||||
************************************************/
|
************************************************/
|
||||||
|
|
||||||
.in-bold {
|
.in-bold {
|
||||||
@ -628,14 +628,14 @@ div.discount-group span {
|
|||||||
.navbar-default .navbar-nav > li > a:focus {
|
.navbar-default .navbar-nav > li > a:focus {
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
}
|
}
|
||||||
.navbar-default .navbar-nav > .active > a,
|
.navbar-default .navbar-nav > .active > a,
|
||||||
.navbar-default .navbar-nav > .active > a:hover,
|
.navbar-default .navbar-nav > .active > a:hover,
|
||||||
.navbar-default .navbar-nav > .active > a:focus {
|
.navbar-default .navbar-nav > .active > a:focus {
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
background-color: #3276b1;
|
background-color: #3276b1;
|
||||||
}
|
}
|
||||||
.navbar-default .navbar-nav > .open > a,
|
.navbar-default .navbar-nav > .open > a,
|
||||||
.navbar-default .navbar-nav > .open > a:hover,
|
.navbar-default .navbar-nav > .open > a:hover,
|
||||||
.navbar-default .navbar-nav > .open > a:focus {
|
.navbar-default .navbar-nav > .open > a:focus {
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
background-color: #3276b1;
|
background-color: #3276b1;
|
||||||
@ -649,8 +649,8 @@ div.discount-group span {
|
|||||||
border-top-color: #ffffff;
|
border-top-color: #ffffff;
|
||||||
border-bottom-color: #ffffff;
|
border-bottom-color: #ffffff;
|
||||||
}
|
}
|
||||||
.navbar-default .navbar-nav > .open > a .caret,
|
.navbar-default .navbar-nav > .open > a .caret,
|
||||||
.navbar-default .navbar-nav > .open > a:hover .caret,
|
.navbar-default .navbar-nav > .open > a:hover .caret,
|
||||||
.navbar-default .navbar-nav > .open > a:focus .caret {
|
.navbar-default .navbar-nav > .open > a:focus .caret {
|
||||||
border-top-color: #ffffff;
|
border-top-color: #ffffff;
|
||||||
border-bottom-color: #ffffff;
|
border-bottom-color: #ffffff;
|
||||||
@ -689,7 +689,7 @@ div.fb_iframe_widget {
|
|||||||
display: inline;
|
display: inline;
|
||||||
}
|
}
|
||||||
div.fb_iframe_widget > span {
|
div.fb_iframe_widget > span {
|
||||||
vertical-align: top !important;
|
vertical-align: top !important;
|
||||||
}
|
}
|
||||||
.pro-label {
|
.pro-label {
|
||||||
font-size:9px;
|
font-size:9px;
|
||||||
@ -728,12 +728,12 @@ box-shadow: 0px 0px 15px 0px rgba(0, 5, 5, 0.2);
|
|||||||
.plans-table .glyphicon-remove {background-color: #da4830;}
|
.plans-table .glyphicon-remove {background-color: #da4830;}
|
||||||
.plans-table .glyphicon-ok {background-color: #35c156;}
|
.plans-table .glyphicon-ok {background-color: #35c156;}
|
||||||
.plans-table .glyphicon-star {border-radius: 0; background-color: #2e2b2b;
|
.plans-table .glyphicon-star {border-radius: 0; background-color: #2e2b2b;
|
||||||
display: block;
|
display: block;
|
||||||
width: 60px;
|
width: 60px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -5px;
|
top: -5px;
|
||||||
right: -20px;
|
right: -20px;
|
||||||
-webkit-transform: rotate(45deg);
|
-webkit-transform: rotate(45deg);
|
||||||
-moz-transform: rotate(45deg);
|
-moz-transform: rotate(45deg);
|
||||||
-o-transform: rotate(45deg);
|
-o-transform: rotate(45deg);
|
||||||
@ -763,11 +763,11 @@ box-shadow: 0px 0px 15px 0px rgba(0, 5, 5, 0.2);
|
|||||||
.ellipsis {
|
.ellipsis {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.entityArchived {
|
.entityArchived {
|
||||||
color: #888 !important;
|
color: #888 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.entityDeleted {
|
.entityDeleted {
|
||||||
@ -775,12 +775,12 @@ box-shadow: 0px 0px 15px 0px rgba(0, 5, 5, 0.2);
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Custom, iPhone Retina */
|
/* Custom, iPhone Retina */
|
||||||
@media only screen and (min-width : 320px) {
|
@media only screen and (min-width : 320px) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Extra Small Devices, Phones */
|
/* Extra Small Devices, Phones */
|
||||||
@media only screen and (min-width : 480px) {
|
@media only screen and (min-width : 480px) {
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -828,7 +828,7 @@ box-shadow: 0px 0px 15px 0px rgba(0, 5, 5, 0.2);
|
|||||||
@media (max-width: 992px) {
|
@media (max-width: 992px) {
|
||||||
.hide-phone {
|
.hide-phone {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 767px) {
|
@media (max-width: 767px) {
|
||||||
@ -846,8 +846,8 @@ box-shadow: 0px 0px 15px 0px rgba(0, 5, 5, 0.2);
|
|||||||
.plans-table .free .cell { padding-right: 0; }
|
.plans-table .free .cell { padding-right: 0; }
|
||||||
.plans-table .free .cell:first-child {margin-right: 0;}
|
.plans-table .free .cell:first-child {margin-right: 0;}
|
||||||
.plans-table .cell div:first-child {margin-bottom: 5px;}
|
.plans-table .cell div:first-child {margin-bottom: 5px;}
|
||||||
.plans-table .cell .cta {margin-bottom: 0 !important;}
|
.plans-table .cell .cta {margin-bottom: 0 !important;}
|
||||||
.plans-table .pro {margin-top: 40px;}
|
.plans-table .pro {margin-top: 40px;}
|
||||||
}
|
}
|
||||||
|
|
||||||
label[for=recommendedGateway_id2].radio{
|
label[for=recommendedGateway_id2].radio{
|
||||||
@ -976,14 +976,14 @@ button .glyphicon {
|
|||||||
|
|
||||||
.pro-plan-modal a.button {
|
.pro-plan-modal a.button {
|
||||||
font-family: 'roboto_slabregular', Georgia, Times, serif;
|
font-family: 'roboto_slabregular', Georgia, Times, serif;
|
||||||
background: #f38c4f;
|
background: #f38c4f;
|
||||||
background: -moz-linear-gradient(top, #f38c4f 0%, #db7134 100%);
|
background: -moz-linear-gradient(top, #f38c4f 0%, #db7134 100%);
|
||||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f38c4f), color-stop(100%,#db7134));
|
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f38c4f), color-stop(100%,#db7134));
|
||||||
background: -webkit-linear-gradient(top, #f38c4f 0%,#db7134 100%);
|
background: -webkit-linear-gradient(top, #f38c4f 0%,#db7134 100%);
|
||||||
background: -o-linear-gradient(top, #f38c4f 0%,#db7134 100%);
|
background: -o-linear-gradient(top, #f38c4f 0%,#db7134 100%);
|
||||||
background: -ms-linear-gradient(top, #f38c4f 0%,#db7134 100%);
|
background: -ms-linear-gradient(top, #f38c4f 0%,#db7134 100%);
|
||||||
background: linear-gradient(to bottom, #f38c4f 0%,#db7134 100%);
|
background: linear-gradient(to bottom, #f38c4f 0%,#db7134 100%);
|
||||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f38c4f', endColorstr='#db7134',GradientType=0 );
|
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f38c4f', endColorstr='#db7134',GradientType=0 );
|
||||||
text-shadow: 1px 1px 1px rgba(0, 0, 0, .25);
|
text-shadow: 1px 1px 1px rgba(0, 0, 0, .25);
|
||||||
width: 68%;
|
width: 68%;
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
@ -1046,7 +1046,7 @@ ul.user-accounts a:hover div.remove {
|
|||||||
|
|
||||||
.invoice-contact .tooltip-inner {
|
.invoice-contact .tooltip-inner {
|
||||||
text-align:left;
|
text-align:left;
|
||||||
width: 350px;
|
width: 350px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.smaller {
|
.smaller {
|
||||||
@ -1103,10 +1103,3 @@ div.panel-body div.panel-body {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropzone .fallback-doc{
|
|
||||||
display:none;
|
|
||||||
}
|
|
||||||
.dropzone.dz-browser-not-supported .fallback-doc{
|
|
||||||
display:block;
|
|
||||||
}
|
|
Binary file not shown.
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 16 KiB |
Binary file not shown.
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 24 KiB |
Binary file not shown.
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 37 KiB |
@ -1312,6 +1312,15 @@ $LANG = array(
|
|||||||
'security' => 'Security',
|
'security' => 'Security',
|
||||||
'see_whats_new' => 'See what\'s new in v:version',
|
'see_whats_new' => 'See what\'s new in v:version',
|
||||||
'wait_for_upload' => 'Please wait for the document upload to complete.',
|
'wait_for_upload' => 'Please wait for the document upload to complete.',
|
||||||
|
'upgrade_for_permissions' => 'Upgrade to our Enterprise plan to enable permissions.',
|
||||||
|
'enable_second_tax_rate' => 'Enable specifying a <b>second tax rate</b>',
|
||||||
|
'payment_file' => 'Payment File',
|
||||||
|
'expense_file' => 'Expense File',
|
||||||
|
'product_file' => 'Product File',
|
||||||
|
'import_products' => 'Import Products',
|
||||||
|
'products_will_create' => 'products will be created.',
|
||||||
|
'product_key' => 'Product',
|
||||||
|
'created_products' => 'Successfully created :count product(s)',
|
||||||
|
|
||||||
'view_dashboard' => 'View Dashboard',
|
'view_dashboard' => 'View Dashboard',
|
||||||
'client_session_expired' => 'Session Expired',
|
'client_session_expired' => 'Session Expired',
|
||||||
|
@ -7,9 +7,9 @@
|
|||||||
<script src="{{ asset('js/quill.min.js') }}" type="text/javascript"></script>
|
<script src="{{ asset('js/quill.min.js') }}" type="text/javascript"></script>
|
||||||
@stop
|
@stop
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
@parent
|
@parent
|
||||||
|
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
|
|
||||||
#logo {
|
#logo {
|
||||||
@ -23,10 +23,11 @@
|
|||||||
->autocomplete('on')
|
->autocomplete('on')
|
||||||
->rules([
|
->rules([
|
||||||
'name' => 'required',
|
'name' => 'required',
|
||||||
|
'website' => 'url',
|
||||||
]) !!}
|
]) !!}
|
||||||
|
|
||||||
{{ Former::populate($account) }}
|
{{ Former::populate($account) }}
|
||||||
|
|
||||||
@include('accounts.nav', ['selected' => ACCOUNT_COMPANY_DETAILS])
|
@include('accounts.nav', ['selected' => ACCOUNT_COMPANY_DETAILS])
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@ -76,7 +77,7 @@
|
|||||||
<h3 class="panel-title">{!! trans('texts.address') !!}</h3>
|
<h3 class="panel-title">{!! trans('texts.address') !!}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body form-padding-right">
|
<div class="panel-body form-padding-right">
|
||||||
|
|
||||||
{!! Former::text('address1')->autocomplete('address-line1') !!}
|
{!! Former::text('address1')->autocomplete('address-line1') !!}
|
||||||
{!! Former::text('address2')->autocomplete('address-line2') !!}
|
{!! Former::text('address2')->autocomplete('address-line2') !!}
|
||||||
{!! Former::text('city')->autocomplete('address-level2') !!}
|
{!! Former::text('city')->autocomplete('address-level2') !!}
|
||||||
@ -88,7 +89,7 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<h3 class="panel-title">{!! trans('texts.signature') !!}</h3>
|
<h3 class="panel-title">{!! trans('texts.signature') !!}</h3>
|
||||||
@ -96,25 +97,25 @@
|
|||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
|
|
||||||
<div class="col-md-10 col-md-offset-1">
|
<div class="col-md-10 col-md-offset-1">
|
||||||
{!! Former::textarea('email_footer')->style('display:none')->raw() !!}
|
{!! Former::textarea('email_footer')->style('display:none')->raw() !!}
|
||||||
<div id="signatureEditor" class="form-control" style="min-height:160px" onclick="focusEditor()"></div>
|
<div id="signatureEditor" class="form-control" style="min-height:160px" onclick="focusEditor()"></div>
|
||||||
@include('partials/quill_toolbar', ['name' => 'signature'])
|
@include('partials/quill_toolbar', ['name' => 'signature'])
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<center>
|
<center>
|
||||||
{!! Button::success(trans('texts.save'))->submit()->large()->appendIcon(Icon::create('floppy-disk')) !!}
|
{!! Button::success(trans('texts.save'))->submit()->large()->appendIcon(Icon::create('floppy-disk')) !!}
|
||||||
</center>
|
</center>
|
||||||
|
|
||||||
{!! Former::close() !!}
|
{!! Former::close() !!}
|
||||||
|
|
||||||
{!! Form::open(['url' => 'remove_logo', 'class' => 'removeLogoForm']) !!}
|
{!! Form::open(['url' => 'remove_logo', 'class' => 'removeLogoForm']) !!}
|
||||||
{!! Form::close() !!}
|
{!! Form::close() !!}
|
||||||
|
|
||||||
|
|
||||||
@ -158,4 +159,4 @@
|
|||||||
|
|
||||||
@section('onReady')
|
@section('onReady')
|
||||||
$('#name').focus();
|
$('#name').focus();
|
||||||
@stop
|
@stop
|
||||||
|
@ -4,9 +4,7 @@
|
|||||||
@parent
|
@parent
|
||||||
|
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
.contact-file,
|
.import-file {
|
||||||
.task-file,
|
|
||||||
.payment-file {
|
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -34,7 +32,7 @@
|
|||||||
|
|
||||||
@foreach (\App\Services\ImportService::$entityTypes as $entityType)
|
@foreach (\App\Services\ImportService::$entityTypes as $entityType)
|
||||||
{!! Former::file("{$entityType}_file")
|
{!! Former::file("{$entityType}_file")
|
||||||
->addGroupClass("{$entityType}-file") !!}
|
->addGroupClass("import-file {$entityType}-file") !!}
|
||||||
@endforeach
|
@endforeach
|
||||||
|
|
||||||
{!! Former::actions( Button::info(trans('texts.upload'))->submit()->large()->appendIcon(Icon::create('open'))) !!}
|
{!! Former::actions( Button::info(trans('texts.upload'))->submit()->large()->appendIcon(Icon::create('open'))) !!}
|
||||||
@ -67,13 +65,17 @@
|
|||||||
trans('texts.payments') => array('name' => ENTITY_PAYMENT, 'value' => 1),
|
trans('texts.payments') => array('name' => ENTITY_PAYMENT, 'value' => 1),
|
||||||
])->check(ENTITY_CLIENT)->check(ENTITY_TASK)->check(ENTITY_INVOICE)->check(ENTITY_PAYMENT) !!}
|
])->check(ENTITY_CLIENT)->check(ENTITY_TASK)->check(ENTITY_INVOICE)->check(ENTITY_PAYMENT) !!}
|
||||||
|
|
||||||
{!! Former::actions( Button::primary(trans('texts.download'))->submit()->large()->appendIcon(Icon::create('download-alt'))) !!}
|
{!! Former::actions( Button::primary(trans('texts.download'))->submit()->large()->appendIcon(Icon::create('download-alt'))) !!}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{!! Former::close() !!}
|
{!! Former::close() !!}
|
||||||
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
$(function() {
|
||||||
|
setFileTypesVisible();
|
||||||
|
});
|
||||||
|
|
||||||
function setEntityTypesVisible() {
|
function setEntityTypesVisible() {
|
||||||
var selector = '.entity-types input[type=checkbox]';
|
var selector = '.entity-types input[type=checkbox]';
|
||||||
if ($('#format').val() === 'JSON') {
|
if ($('#format').val() === 'JSON') {
|
||||||
@ -103,4 +105,4 @@
|
|||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@stop
|
@stop
|
||||||
|
@ -7,18 +7,16 @@
|
|||||||
|
|
||||||
{!! Former::open('/import_csv')->addClass('warn-on-exit') !!}
|
{!! Former::open('/import_csv')->addClass('warn-on-exit') !!}
|
||||||
|
|
||||||
@if (isset($data[ENTITY_CLIENT]))
|
@foreach (App\Services\ImportService::$entityTypes as $entityType)
|
||||||
@include('accounts.partials.map', $data[ENTITY_CLIENT])
|
@if (isset($data[$entityType]))
|
||||||
@endif
|
@include('accounts.partials.map', $data[$entityType])
|
||||||
|
@endif
|
||||||
|
@endforeach
|
||||||
|
|
||||||
@if (isset($data[ENTITY_INVOICE]))
|
{!! Former::actions(
|
||||||
@include('accounts.partials.map', $data[ENTITY_INVOICE])
|
|
||||||
@endif
|
|
||||||
|
|
||||||
{!! Former::actions(
|
|
||||||
Button::normal(trans('texts.cancel'))->large()->asLinkTo(URL::to('/settings/import_export'))->appendIcon(Icon::create('remove-circle')),
|
Button::normal(trans('texts.cancel'))->large()->asLinkTo(URL::to('/settings/import_export'))->appendIcon(Icon::create('remove-circle')),
|
||||||
Button::success(trans('texts.import'))->submit()->large()->appendIcon(Icon::create('floppy-disk'))) !!}
|
Button::success(trans('texts.import'))->submit()->large()->appendIcon(Icon::create('floppy-disk'))) !!}
|
||||||
|
|
||||||
{!! Former::close() !!}
|
{!! Former::close() !!}
|
||||||
|
|
||||||
@stop
|
@stop
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
@extends('header')
|
@extends('header')
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
@parent
|
@parent
|
||||||
|
|
||||||
@include('accounts.nav', ['selected' => ACCOUNT_TAX_RATES])
|
@include('accounts.nav', ['selected' => ACCOUNT_TAX_RATES])
|
||||||
@ -10,12 +10,13 @@
|
|||||||
{{ Former::populateField('invoice_taxes', intval($account->invoice_taxes)) }}
|
{{ Former::populateField('invoice_taxes', intval($account->invoice_taxes)) }}
|
||||||
{{ Former::populateField('invoice_item_taxes', intval($account->invoice_item_taxes)) }}
|
{{ Former::populateField('invoice_item_taxes', intval($account->invoice_item_taxes)) }}
|
||||||
{{ Former::populateField('show_item_taxes', intval($account->show_item_taxes)) }}
|
{{ Former::populateField('show_item_taxes', intval($account->show_item_taxes)) }}
|
||||||
|
{{ Former::populateField('enable_second_tax_rate', intval($account->enable_second_tax_rate)) }}
|
||||||
|
|
||||||
|
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<h3 class="panel-title">{!! trans('texts.tax_settings') !!}</h3>
|
<h3 class="panel-title">{!! trans('texts.tax_settings') !!}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
|
|
||||||
{!! Former::checkbox('invoice_taxes')
|
{!! Former::checkbox('invoice_taxes')
|
||||||
@ -30,6 +31,10 @@
|
|||||||
->text(trans('texts.show_line_item_tax'))
|
->text(trans('texts.show_line_item_tax'))
|
||||||
->label(' ') !!}
|
->label(' ') !!}
|
||||||
|
|
||||||
|
{!! Former::checkbox('enable_second_tax_rate')
|
||||||
|
->text(trans('texts.enable_second_tax_rate'))
|
||||||
|
->label(' ') !!}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{!! Former::select('default_tax_rate_id')
|
{!! Former::select('default_tax_rate_id')
|
||||||
@ -51,22 +56,22 @@
|
|||||||
|
|
||||||
@include('partials.bulk_form', ['entityType' => ENTITY_TAX_RATE])
|
@include('partials.bulk_form', ['entityType' => ENTITY_TAX_RATE])
|
||||||
|
|
||||||
{!! Datatable::table()
|
{!! Datatable::table()
|
||||||
->addColumn(
|
->addColumn(
|
||||||
trans('texts.name'),
|
trans('texts.name'),
|
||||||
trans('texts.rate'),
|
trans('texts.rate'),
|
||||||
trans('texts.action'))
|
trans('texts.action'))
|
||||||
->setUrl(url('api/tax_rates/'))
|
->setUrl(url('api/tax_rates/'))
|
||||||
->setOptions('sPaginationType', 'bootstrap')
|
->setOptions('sPaginationType', 'bootstrap')
|
||||||
->setOptions('bFilter', false)
|
->setOptions('bFilter', false)
|
||||||
->setOptions('bAutoWidth', false)
|
->setOptions('bAutoWidth', false)
|
||||||
->setOptions('aoColumns', [[ "sWidth"=> "40%" ], [ "sWidth"=> "40%" ], ["sWidth"=> "20%"]])
|
->setOptions('aoColumns', [[ "sWidth"=> "40%" ], [ "sWidth"=> "40%" ], ["sWidth"=> "20%"]])
|
||||||
->setOptions('aoColumnDefs', [['bSortable'=>false, 'aTargets'=>[2]]])
|
->setOptions('aoColumnDefs', [['bSortable'=>false, 'aTargets'=>[2]]])
|
||||||
->render('datatable') !!}
|
->render('datatable') !!}
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
window.onDatatableReady = actionListHandler;
|
window.onDatatableReady = actionListHandler;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
@stop
|
@stop
|
||||||
|
@ -150,7 +150,7 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@foreach ($upcoming as $invoice)
|
@foreach ($upcoming as $invoice)
|
||||||
@if (!$invoice->is_quote)
|
@if ($invoice->invoice_type_id == INVOICE_TYPE_STANDARD)
|
||||||
<tr>
|
<tr>
|
||||||
<td>{!! \App\Models\Invoice::calcLink($invoice) !!}</td>
|
<td>{!! \App\Models\Invoice::calcLink($invoice) !!}</td>
|
||||||
@can('viewByOwner', [ENTITY_CLIENT, $invoice->client_user_id])
|
@can('viewByOwner', [ENTITY_CLIENT, $invoice->client_user_id])
|
||||||
@ -185,7 +185,7 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@foreach ($pastDue as $invoice)
|
@foreach ($pastDue as $invoice)
|
||||||
@if (!$invoice->is_quote)
|
@if ($invoice->invoice_type_id == INVOICE_TYPE_STANDARD)
|
||||||
<tr>
|
<tr>
|
||||||
<td>{!! \App\Models\Invoice::calcLink($invoice) !!}</td>
|
<td>{!! \App\Models\Invoice::calcLink($invoice) !!}</td>
|
||||||
@can('viewByOwner', [ENTITY_CLIENT, $invoice->client_user_id])
|
@can('viewByOwner', [ENTITY_CLIENT, $invoice->client_user_id])
|
||||||
@ -224,7 +224,7 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@foreach ($upcoming as $invoice)
|
@foreach ($upcoming as $invoice)
|
||||||
@if ($invoice->is_quote)
|
@if ($invoice->invoice_type_id == INVOICE_TYPE_STANDARD)
|
||||||
<tr>
|
<tr>
|
||||||
<td>{!! \App\Models\Invoice::calcLink($invoice) !!}</td>
|
<td>{!! \App\Models\Invoice::calcLink($invoice) !!}</td>
|
||||||
<td>{!! link_to('/clients/'.$invoice->client_public_id, trim($invoice->client_name) ?: (trim($invoice->first_name . ' ' . $invoice->last_name) ?: $invoice->email)) !!}</td>
|
<td>{!! link_to('/clients/'.$invoice->client_public_id, trim($invoice->client_name) ?: (trim($invoice->first_name . ' ' . $invoice->last_name) ?: $invoice->email)) !!}</td>
|
||||||
@ -255,7 +255,7 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@foreach ($pastDue as $invoice)
|
@foreach ($pastDue as $invoice)
|
||||||
@if ($invoice->is_quote)
|
@if ($invoice->invoice_type_id == INVOICE_TYPE_STANDARD)
|
||||||
<tr>
|
<tr>
|
||||||
<td>{!! \App\Models\Invoice::calcLink($invoice) !!}</td>
|
<td>{!! \App\Models\Invoice::calcLink($invoice) !!}</td>
|
||||||
<td>{!! link_to('/clients/'.$invoice->client_public_id, trim($invoice->client_name) ?: (trim($invoice->first_name . ' ' . $invoice->last_name) ?: $invoice->email)) !!}</td>
|
<td>{!! link_to('/clients/'.$invoice->client_public_id, trim($invoice->client_name) ?: (trim($invoice->first_name . ' ' . $invoice->last_name) ?: $invoice->email)) !!}</td>
|
||||||
|
@ -15,7 +15,10 @@
|
|||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
|
|
||||||
{!! Former::open($url)->addClass('warn-on-exit main-form')->method($method) !!}
|
{!! Former::open($url)
|
||||||
|
->addClass('warn-on-exit main-form')
|
||||||
|
->onsubmit('return onFormSubmit(event)')
|
||||||
|
->method($method) !!}
|
||||||
<div style="display:none">
|
<div style="display:none">
|
||||||
{!! Former::text('action') !!}
|
{!! Former::text('action') !!}
|
||||||
</div>
|
</div>
|
||||||
@ -111,15 +114,8 @@
|
|||||||
<div class="col-md-12 col-sm-8">
|
<div class="col-md-12 col-sm-8">
|
||||||
<div role="tabpanel" class="tab-pane" id="attached-documents" style="position:relative;z-index:9">
|
<div role="tabpanel" class="tab-pane" id="attached-documents" style="position:relative;z-index:9">
|
||||||
<div id="document-upload" class="dropzone">
|
<div id="document-upload" class="dropzone">
|
||||||
<div class="fallback">
|
|
||||||
<input name="documents[]" type="file" multiple />
|
|
||||||
</div>
|
|
||||||
<div data-bind="foreach: documents">
|
<div data-bind="foreach: documents">
|
||||||
<div class="fallback-doc">
|
<input type="hidden" name="document_ids[]" data-bind="value: public_id"/>
|
||||||
<a href="#" class="fallback-doc-remove" data-bind="click: $parent.removeDocument"><i class="fa fa-close"></i></a>
|
|
||||||
<span data-bind="text:name"></span>
|
|
||||||
<input type="hidden" name="document_ids[]" data-bind="value: public_id"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -154,6 +150,15 @@
|
|||||||
clientMap[client.public_id] = client;
|
clientMap[client.public_id] = client;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onFormSubmit(event) {
|
||||||
|
if (window.countUploadingDocuments > 0) {
|
||||||
|
alert("{!! trans('texts.wait_for_upload') !!}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
function onClientChange() {
|
function onClientChange() {
|
||||||
var clientId = $('select#client_id').val();
|
var clientId = $('select#client_id').val();
|
||||||
var client = clientMap[clientId];
|
var client = clientMap[clientId];
|
||||||
@ -225,21 +230,24 @@
|
|||||||
|
|
||||||
// Initialize document upload
|
// Initialize document upload
|
||||||
dropzone = new Dropzone('#document-upload', {
|
dropzone = new Dropzone('#document-upload', {
|
||||||
url:{!! json_encode(url('document')) !!},
|
url:{!! json_encode(url('documents')) !!},
|
||||||
params:{
|
params:{
|
||||||
_token:"{{ Session::getToken() }}"
|
_token:"{{ Session::getToken() }}"
|
||||||
},
|
},
|
||||||
acceptedFiles:{!! json_encode(implode(',',\App\Models\Document::$allowedMimes)) !!},
|
acceptedFiles:{!! json_encode(implode(',',\App\Models\Document::$allowedMimes)) !!},
|
||||||
addRemoveLinks:true,
|
addRemoveLinks:true,
|
||||||
|
dictRemoveFileConfirmation:"{{trans('texts.are_you_sure')}}",
|
||||||
@foreach(['default_message', 'fallback_message', 'fallback_text', 'file_too_big', 'invalid_file_type', 'response_error', 'cancel_upload', 'cancel_upload_confirmation', 'remove_file'] as $key)
|
@foreach(['default_message', 'fallback_message', 'fallback_text', 'file_too_big', 'invalid_file_type', 'response_error', 'cancel_upload', 'cancel_upload_confirmation', 'remove_file'] as $key)
|
||||||
"dict{{strval($key)}}":"{{trans('texts.dropzone_'.Utils::toClassCase($key))}}",
|
"dict{{strval($key)}}":"{{trans('texts.dropzone_'.Utils::toClassCase($key))}}",
|
||||||
@endforeach
|
@endforeach
|
||||||
maxFileSize:{{floatval(MAX_DOCUMENT_SIZE/1000)}},
|
maxFilesize:{{floatval(MAX_DOCUMENT_SIZE/1000)}},
|
||||||
});
|
});
|
||||||
if(dropzone instanceof Dropzone){
|
if(dropzone instanceof Dropzone){
|
||||||
dropzone.on("addedfile",handleDocumentAdded);
|
dropzone.on("addedfile",handleDocumentAdded);
|
||||||
dropzone.on("removedfile",handleDocumentRemoved);
|
dropzone.on("removedfile",handleDocumentRemoved);
|
||||||
dropzone.on("success",handleDocumentUploaded);
|
dropzone.on("success",handleDocumentUploaded);
|
||||||
|
dropzone.on("canceled",handleDocumentCanceled);
|
||||||
|
dropzone.on("error",handleDocumentError);
|
||||||
for (var i=0; i<model.documents().length; i++) {
|
for (var i=0; i<model.documents().length; i++) {
|
||||||
var document = model.documents()[i];
|
var document = model.documents()[i];
|
||||||
var mockFile = {
|
var mockFile = {
|
||||||
@ -362,6 +370,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.countUploadingDocuments = 0;
|
||||||
@if (Auth::user()->account->hasFeature(FEATURE_DOCUMENTS))
|
@if (Auth::user()->account->hasFeature(FEATURE_DOCUMENTS))
|
||||||
function handleDocumentAdded(file){
|
function handleDocumentAdded(file){
|
||||||
// open document when clicked
|
// open document when clicked
|
||||||
@ -373,20 +382,36 @@
|
|||||||
if(file.mock)return;
|
if(file.mock)return;
|
||||||
file.index = model.documents().length;
|
file.index = model.documents().length;
|
||||||
model.addDocument({name:file.name, size:file.size, type:file.type});
|
model.addDocument({name:file.name, size:file.size, type:file.type});
|
||||||
|
window.countUploadingDocuments++;
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDocumentRemoved(file){
|
function handleDocumentRemoved(file){
|
||||||
model.removeDocument(file.public_id);
|
model.removeDocument(file.public_id);
|
||||||
|
$.ajax({
|
||||||
|
url: '{{ '/documents/' }}' + file.public_id,
|
||||||
|
type: 'DELETE',
|
||||||
|
success: function(result) {
|
||||||
|
// Do something with the result
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDocumentUploaded(file, response){
|
function handleDocumentUploaded(file, response){
|
||||||
file.public_id = response.document.public_id
|
file.public_id = response.document.public_id
|
||||||
model.documents()[file.index].update(response.document);
|
model.documents()[file.index].update(response.document);
|
||||||
|
window.countUploadingDocuments--;
|
||||||
if(response.document.preview_url){
|
if(response.document.preview_url){
|
||||||
dropzone.emit('thumbnail', file, response.document.preview_url);
|
dropzone.emit('thumbnail', file, response.document.preview_url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleDocumentCanceled() {
|
||||||
|
window.countUploadingDocuments--;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDocumentError() {
|
||||||
|
window.countUploadingDocuments--;
|
||||||
|
}
|
||||||
@endif
|
@endif
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -243,7 +243,7 @@
|
|||||||
@endif
|
@endif
|
||||||
<th style="min-width:120px" data-bind="text: costLabel">{{ $invoiceLabels['unit_cost'] }}</th>
|
<th style="min-width:120px" data-bind="text: costLabel">{{ $invoiceLabels['unit_cost'] }}</th>
|
||||||
<th style="{{ $account->hide_quantity ? 'display:none' : 'min-width:120px' }}" data-bind="text: qtyLabel">{{ $invoiceLabels['quantity'] }}</th>
|
<th style="{{ $account->hide_quantity ? 'display:none' : 'min-width:120px' }}" data-bind="text: qtyLabel">{{ $invoiceLabels['quantity'] }}</th>
|
||||||
<th style="min-width:180px;display:none;" data-bind="visible: $root.invoice_item_taxes.show">{{ trans('texts.tax') }}</th>
|
<th style="min-width:{{ $account->enable_second_tax_rate ? 180 : 120 }}px;display:none;" data-bind="visible: $root.invoice_item_taxes.show">{{ trans('texts.tax') }}</th>
|
||||||
<th style="min-width:120px;">{{ trans('texts.line_total') }}</th>
|
<th style="min-width:120px;">{{ trans('texts.line_total') }}</th>
|
||||||
<th style="min-width:32px;" class="hide-border"></th>
|
<th style="min-width:32px;" class="hide-border"></th>
|
||||||
</tr>
|
</tr>
|
||||||
@ -288,16 +288,18 @@
|
|||||||
->addOption('', '')
|
->addOption('', '')
|
||||||
->options($taxRateOptions)
|
->options($taxRateOptions)
|
||||||
->data_bind('value: tax1')
|
->data_bind('value: tax1')
|
||||||
->addClass('tax-select')
|
->addClass($account->enable_second_tax_rate ? 'tax-select' : '')
|
||||||
->raw() !!}
|
->raw() !!}
|
||||||
<input type="text" data-bind="value: tax_name1, attr: {name: 'invoice_items[' + $index() + '][tax_name1]'}" style="display:none">
|
<input type="text" data-bind="value: tax_name1, attr: {name: 'invoice_items[' + $index() + '][tax_name1]'}" style="display:none">
|
||||||
<input type="text" data-bind="value: tax_rate1, attr: {name: 'invoice_items[' + $index() + '][tax_rate1]'}" style="display:none">
|
<input type="text" data-bind="value: tax_rate1, attr: {name: 'invoice_items[' + $index() + '][tax_rate1]'}" style="display:none">
|
||||||
{!! Former::select('')
|
<div data-bind="visible: $root.invoice().account.enable_second_tax_rate == '1'">
|
||||||
->addOption('', '')
|
{!! Former::select('')
|
||||||
->options($taxRateOptions)
|
->addOption('', '')
|
||||||
->data_bind('value: tax2')
|
->options($taxRateOptions)
|
||||||
->addClass('tax-select')
|
->data_bind('value: tax2')
|
||||||
->raw() !!}
|
->addClass('tax-select')
|
||||||
|
->raw() !!}
|
||||||
|
</div>
|
||||||
<input type="text" data-bind="value: tax_name2, attr: {name: 'invoice_items[' + $index() + '][tax_name2]'}" style="display:none">
|
<input type="text" data-bind="value: tax_name2, attr: {name: 'invoice_items[' + $index() + '][tax_name2]'}" style="display:none">
|
||||||
<input type="text" data-bind="value: tax_rate2, attr: {name: 'invoice_items[' + $index() + '][tax_rate2]'}" style="display:none">
|
<input type="text" data-bind="value: tax_rate2, attr: {name: 'invoice_items[' + $index() + '][tax_rate2]'}" style="display:none">
|
||||||
</td>
|
</td>
|
||||||
@ -362,15 +364,8 @@
|
|||||||
<div role="tabpanel" class="tab-pane" id="attached-documents" style="position:relative;z-index:9">
|
<div role="tabpanel" class="tab-pane" id="attached-documents" style="position:relative;z-index:9">
|
||||||
<div id="document-upload">
|
<div id="document-upload">
|
||||||
<div class="dropzone">
|
<div class="dropzone">
|
||||||
<div class="fallback">
|
|
||||||
<input name="documents[]" type="file" multiple />
|
|
||||||
</div>
|
|
||||||
<div data-bind="foreach: documents">
|
<div data-bind="foreach: documents">
|
||||||
<div class="fallback-doc">
|
<input type="hidden" name="document_ids[]" data-bind="value: public_id"/>
|
||||||
<a href="#" class="fallback-doc-remove" data-bind="click: $parent.removeDocument"><i class="fa fa-close"></i></a>
|
|
||||||
<span data-bind="text:name"></span>
|
|
||||||
<input type="hidden" name="document_ids[]" data-bind="value: public_id"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@if ($invoice->hasExpenseDocuments())
|
@if ($invoice->hasExpenseDocuments())
|
||||||
@ -438,17 +433,19 @@
|
|||||||
->id('taxRateSelect1')
|
->id('taxRateSelect1')
|
||||||
->addOption('', '')
|
->addOption('', '')
|
||||||
->options($taxRateOptions)
|
->options($taxRateOptions)
|
||||||
->addClass('tax-select')
|
->addClass($account->enable_second_tax_rate ? 'tax-select' : '')
|
||||||
->data_bind('value: tax1')
|
->data_bind('value: tax1')
|
||||||
->raw() !!}
|
->raw() !!}
|
||||||
<input type="text" name="tax_name1" data-bind="value: tax_name1" style="display:none">
|
<input type="text" name="tax_name1" data-bind="value: tax_name1" style="display:none">
|
||||||
<input type="text" name="tax_rate1" data-bind="value: tax_rate1" style="display:none">
|
<input type="text" name="tax_rate1" data-bind="value: tax_rate1" style="display:none">
|
||||||
|
<div data-bind="visible: $root.invoice().account.enable_second_tax_rate == '1'">
|
||||||
{!! Former::select('')
|
{!! Former::select('')
|
||||||
->addOption('', '')
|
->addOption('', '')
|
||||||
->options($taxRateOptions)
|
->options($taxRateOptions)
|
||||||
->addClass('tax-select')
|
->addClass('tax-select')
|
||||||
->data_bind('value: tax2')
|
->data_bind('value: tax2')
|
||||||
->raw() !!}
|
->raw() !!}
|
||||||
|
</div>
|
||||||
<input type="text" name="tax_name2" data-bind="value: tax_name2" style="display:none">
|
<input type="text" name="tax_name2" data-bind="value: tax_name2" style="display:none">
|
||||||
<input type="text" name="tax_rate2" data-bind="value: tax_rate2" style="display:none">
|
<input type="text" name="tax_rate2" data-bind="value: tax_rate2" style="display:none">
|
||||||
</td>
|
</td>
|
||||||
@ -848,7 +845,7 @@
|
|||||||
model.invoice().has_tasks(true);
|
model.invoice().has_tasks(true);
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
if(model.invoice().expenses() && !model.invoice().public_id()){
|
if(model.invoice().expenses().length && !model.invoice().public_id()){
|
||||||
model.expense_currency_id({{ isset($expenseCurrencyId) ? $expenseCurrencyId : 0 }});
|
model.expense_currency_id({{ isset($expenseCurrencyId) ? $expenseCurrencyId : 0 }});
|
||||||
|
|
||||||
// move the blank invoice line item to the end
|
// move the blank invoice line item to the end
|
||||||
@ -1010,21 +1007,24 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
window.dropzone = new Dropzone('#document-upload .dropzone', {
|
window.dropzone = new Dropzone('#document-upload .dropzone', {
|
||||||
url:{!! json_encode(url('document')) !!},
|
url:{!! json_encode(url('documents')) !!},
|
||||||
params:{
|
params:{
|
||||||
_token:"{{ Session::getToken() }}"
|
_token:"{{ Session::getToken() }}"
|
||||||
},
|
},
|
||||||
acceptedFiles:{!! json_encode(implode(',',\App\Models\Document::$allowedMimes)) !!},
|
acceptedFiles:{!! json_encode(implode(',',\App\Models\Document::$allowedMimes)) !!},
|
||||||
addRemoveLinks:true,
|
addRemoveLinks:true,
|
||||||
|
dictRemoveFileConfirmation:"{{trans('texts.are_you_sure')}}",
|
||||||
@foreach(['default_message', 'fallback_message', 'fallback_text', 'file_too_big', 'invalid_file_type', 'response_error', 'cancel_upload', 'cancel_upload_confirmation', 'remove_file'] as $key)
|
@foreach(['default_message', 'fallback_message', 'fallback_text', 'file_too_big', 'invalid_file_type', 'response_error', 'cancel_upload', 'cancel_upload_confirmation', 'remove_file'] as $key)
|
||||||
"dict{{Utils::toClassCase($key)}}":"{{trans('texts.dropzone_'.$key)}}",
|
"dict{{Utils::toClassCase($key)}}":"{{trans('texts.dropzone_'.$key)}}",
|
||||||
@endforeach
|
@endforeach
|
||||||
maxFileSize:{{floatval(MAX_DOCUMENT_SIZE/1000)}},
|
maxFilesize:{{floatval(MAX_DOCUMENT_SIZE/1000)}},
|
||||||
});
|
});
|
||||||
if(dropzone instanceof Dropzone){
|
if(dropzone instanceof Dropzone){
|
||||||
dropzone.on("addedfile",handleDocumentAdded);
|
dropzone.on("addedfile",handleDocumentAdded);
|
||||||
dropzone.on("removedfile",handleDocumentRemoved);
|
dropzone.on("removedfile",handleDocumentRemoved);
|
||||||
dropzone.on("success",handleDocumentUploaded);
|
dropzone.on("success",handleDocumentUploaded);
|
||||||
|
dropzone.on("canceled",handleDocumentCanceled);
|
||||||
|
dropzone.on("error",handleDocumentError);
|
||||||
for (var i=0; i<model.invoice().documents().length; i++) {
|
for (var i=0; i<model.invoice().documents().length; i++) {
|
||||||
var document = model.invoice().documents()[i];
|
var document = model.invoice().documents()[i];
|
||||||
var mockFile = {
|
var mockFile = {
|
||||||
@ -1202,8 +1202,13 @@
|
|||||||
if (confirm('{!! trans("texts.confirm_email_$entityType") !!}' + '\n\n' + getSendToEmails())) {
|
if (confirm('{!! trans("texts.confirm_email_$entityType") !!}' + '\n\n' + getSendToEmails())) {
|
||||||
var accountLanguageId = parseInt({{ $account->language_id ?: '0' }});
|
var accountLanguageId = parseInt({{ $account->language_id ?: '0' }});
|
||||||
var clientLanguageId = parseInt(model.invoice().client().language_id()) || 0;
|
var clientLanguageId = parseInt(model.invoice().client().language_id()) || 0;
|
||||||
|
var attachPDF = {{ $account->attachPDF() ? 'true' : 'false' }};
|
||||||
|
|
||||||
|
// if they aren't attaching the pdf no need to generate it
|
||||||
|
if ( ! attachPDF) {
|
||||||
|
submitAction('email');
|
||||||
// if the client's language is different then we can't use the browser version of the PDF
|
// if the client's language is different then we can't use the browser version of the PDF
|
||||||
if (clientLanguageId && clientLanguageId != accountLanguageId) {
|
} else if (clientLanguageId && clientLanguageId != accountLanguageId) {
|
||||||
submitAction('email');
|
submitAction('email');
|
||||||
} else {
|
} else {
|
||||||
preparePdfData('email');
|
preparePdfData('email');
|
||||||
@ -1466,6 +1471,13 @@
|
|||||||
function handleDocumentRemoved(file){
|
function handleDocumentRemoved(file){
|
||||||
model.invoice().removeDocument(file.public_id);
|
model.invoice().removeDocument(file.public_id);
|
||||||
refreshPDF(true);
|
refreshPDF(true);
|
||||||
|
$.ajax({
|
||||||
|
url: '{{ '/documents/' }}' + file.public_id,
|
||||||
|
type: 'DELETE',
|
||||||
|
success: function(result) {
|
||||||
|
// Do something with the result
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDocumentUploaded(file, response){
|
function handleDocumentUploaded(file, response){
|
||||||
@ -1473,11 +1485,18 @@
|
|||||||
model.invoice().documents()[file.index].update(response.document);
|
model.invoice().documents()[file.index].update(response.document);
|
||||||
window.countUploadingDocuments--;
|
window.countUploadingDocuments--;
|
||||||
refreshPDF(true);
|
refreshPDF(true);
|
||||||
|
|
||||||
if(response.document.preview_url){
|
if(response.document.preview_url){
|
||||||
dropzone.emit('thumbnail', file, response.document.preview_url);
|
dropzone.emit('thumbnail', file, response.document.preview_url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleDocumentCanceled() {
|
||||||
|
window.countUploadingDocuments--;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDocumentError() {
|
||||||
|
window.countUploadingDocuments--;
|
||||||
|
}
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -4,15 +4,15 @@
|
|||||||
@parent
|
@parent
|
||||||
|
|
||||||
@include('money_script')
|
@include('money_script')
|
||||||
|
|
||||||
@foreach ($invoice->client->account->getFontFolders() as $font)
|
@foreach ($invoice->client->account->getFontFolders() as $font)
|
||||||
<script src="{{ asset('js/vfs_fonts/'.$font.'.js') }}" type="text/javascript"></script>
|
<script src="{{ asset('js/vfs_fonts/'.$font.'.js') }}" type="text/javascript"></script>
|
||||||
@endforeach
|
@endforeach
|
||||||
<script src="{{ asset('pdf.built.js') }}" type="text/javascript"></script>
|
<script src="{{ asset('pdf.built.js') }}" type="text/javascript"></script>
|
||||||
|
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
body {
|
body {
|
||||||
background-color: #f8f8f8;
|
background-color: #f8f8f8;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-menu li a{
|
.dropdown-menu li a{
|
||||||
@ -99,7 +99,7 @@
|
|||||||
@include('partials.checkout_com_payment')
|
@include('partials.checkout_com_payment')
|
||||||
@else
|
@else
|
||||||
<div class="pull-right" style="text-align:right">
|
<div class="pull-right" style="text-align:right">
|
||||||
@if ($invoice->is_quote)
|
@if ($invoice->isQuote())
|
||||||
{!! Button::normal(trans('texts.download_pdf'))->withAttributes(['onclick' => 'onDownloadClick()'])->large() !!}
|
{!! Button::normal(trans('texts.download_pdf'))->withAttributes(['onclick' => 'onDownloadClick()'])->large() !!}
|
||||||
@if ($showApprove)
|
@if ($showApprove)
|
||||||
{!! Button::success(trans('texts.approve'))->asLinkTo(URL::to('/approve/' . $invitation->invitation_key))->large() !!}
|
{!! Button::success(trans('texts.approve'))->asLinkTo(URL::to('/approve/' . $invitation->invitation_key))->large() !!}
|
||||||
@ -110,7 +110,7 @@
|
|||||||
{!! DropdownButton::success(trans('texts.pay_now'))->withContents($paymentTypes)->large() !!}
|
{!! DropdownButton::success(trans('texts.pay_now'))->withContents($paymentTypes)->large() !!}
|
||||||
@else
|
@else
|
||||||
<a href='{!! $paymentURL !!}' class="btn btn-success btn-lg">{{ trans('texts.pay_now') }}</a>
|
<a href='{!! $paymentURL !!}' class="btn btn-success btn-lg">{{ trans('texts.pay_now') }}</a>
|
||||||
@endif
|
@endif
|
||||||
@else
|
@else
|
||||||
{!! Button::normal(trans('texts.download_pdf'))->withAttributes(['onclick' => 'onDownloadClick()'])->large() !!}
|
{!! Button::normal(trans('texts.download_pdf'))->withAttributes(['onclick' => 'onDownloadClick()'])->large() !!}
|
||||||
@if ($account->isNinjaAccount())
|
@if ($account->isNinjaAccount())
|
||||||
@ -141,7 +141,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@if ($account->hasFeature(FEATURE_DOCUMENTS) && $account->invoice_embed_documents)
|
@if ($account->hasFeature(FEATURE_DOCUMENTS) && $account->invoice_embed_documents)
|
||||||
@foreach ($invoice->documents as $document)
|
@foreach ($invoice->documents as $document)
|
||||||
@if($document->isPDFEmbeddable())
|
@if($document->isPDFEmbeddable())
|
||||||
@ -164,7 +164,7 @@
|
|||||||
remove_created_by:{{ $invoice->client->account->hasFeature(FEATURE_REMOVE_CREATED_BY) ? 'true' : 'false' }},
|
remove_created_by:{{ $invoice->client->account->hasFeature(FEATURE_REMOVE_CREATED_BY) ? 'true' : 'false' }},
|
||||||
invoice_settings:{{ $invoice->client->account->hasFeature(FEATURE_INVOICE_SETTINGS) ? 'true' : 'false' }}
|
invoice_settings:{{ $invoice->client->account->hasFeature(FEATURE_INVOICE_SETTINGS) ? 'true' : 'false' }}
|
||||||
};
|
};
|
||||||
invoice.is_quote = {{ $invoice->is_quote ? 'true' : 'false' }};
|
invoice.is_quote = {{ $invoice->isQuote() ? 'true' : 'false' }};
|
||||||
invoice.contact = {!! $contact->toJson() !!};
|
invoice.contact = {!! $contact->toJson() !!};
|
||||||
|
|
||||||
function getPDFString(cb) {
|
function getPDFString(cb) {
|
||||||
@ -181,16 +181,16 @@
|
|||||||
doc.getDataUrl(function(pdfString) {
|
doc.getDataUrl(function(pdfString) {
|
||||||
document.write(pdfString);
|
document.write(pdfString);
|
||||||
document.close();
|
document.close();
|
||||||
|
|
||||||
if (window.hasOwnProperty('pjsc_meta')) {
|
if (window.hasOwnProperty('pjsc_meta')) {
|
||||||
window['pjsc_meta'].remainingTasks--;
|
window['pjsc_meta'].remainingTasks--;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@else
|
@else
|
||||||
refreshPDF();
|
refreshPDF();
|
||||||
@endif
|
@endif
|
||||||
});
|
});
|
||||||
|
|
||||||
function onDownloadClick() {
|
function onDownloadClick() {
|
||||||
var doc = generatePDF(invoice, invoice.invoice_design.javascript, true);
|
var doc = generatePDF(invoice, invoice.invoice_design.javascript, true);
|
||||||
var fileName = invoice.is_quote ? invoiceLabels.quote : invoiceLabels.invoice;
|
var fileName = invoice.is_quote ? invoiceLabels.quote : invoiceLabels.invoice;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
@extends('header')
|
@extends('header')
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
@parent
|
@parent
|
||||||
@include('accounts.nav', ['selected' => ACCOUNT_USER_MANAGEMENT])
|
@include('accounts.nav', ['selected' => ACCOUNT_USER_MANAGEMENT])
|
||||||
|
|
||||||
@ -31,13 +31,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if (Utils::hasFeature(FEATURE_USER_PERMISSIONS))
|
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<h3 class="panel-title">{!! trans('texts.permissions') !!}</h3>
|
<h3 class="panel-title">{!! trans('texts.permissions') !!}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body form-padding-right">
|
<div class="panel-body form-padding-right">
|
||||||
|
|
||||||
|
@if ( ! Utils::hasFeature(FEATURE_USER_PERMISSIONS))
|
||||||
|
<div class="alert alert-warning">{{ trans('texts.upgrade_for_permissions') }}</div>
|
||||||
|
<script>
|
||||||
|
$(function() {
|
||||||
|
$('input[type=checkbox]').prop('disabled', true);
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
@endif
|
||||||
|
|
||||||
{!! Former::checkbox('is_admin')
|
{!! Former::checkbox('is_admin')
|
||||||
->label(' ')
|
->label(' ')
|
||||||
@ -61,12 +68,11 @@
|
|||||||
->id('permissions_edit_all')
|
->id('permissions_edit_all')
|
||||||
->text(trans('texts.user_edit_all'))
|
->text(trans('texts.user_edit_all'))
|
||||||
->help(trans('texts.edit_all_help')) !!}
|
->help(trans('texts.edit_all_help')) !!}
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@endif
|
|
||||||
|
|
||||||
{!! Former::actions(
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{!! Former::actions(
|
||||||
Button::normal(trans('texts.cancel'))->asLinkTo(URL::to('/settings/user_management'))->appendIcon(Icon::create('remove-circle'))->large(),
|
Button::normal(trans('texts.cancel'))->asLinkTo(URL::to('/settings/user_management'))->appendIcon(Icon::create('remove-circle'))->large(),
|
||||||
Button::success(trans($user && $user->confirmed ? 'texts.save' : 'texts.send_invite'))->submit()->large()->appendIcon(Icon::create($user && $user->confirmed ? 'floppy-disk' : 'send'))
|
Button::success(trans($user && $user->confirmed ? 'texts.save' : 'texts.send_invite'))->submit()->large()->appendIcon(Icon::create($user && $user->confirmed ? 'floppy-disk' : 'send'))
|
||||||
)!!}
|
)!!}
|
||||||
@ -88,4 +94,4 @@
|
|||||||
if(!viewChecked)$('#permissions_edit_all').prop('checked',false)
|
if(!viewChecked)$('#permissions_edit_all').prop('checked',false)
|
||||||
}
|
}
|
||||||
fixCheckboxes();
|
fixCheckboxes();
|
||||||
@stop
|
@stop
|
||||||
|
Loading…
x
Reference in New Issue
Block a user