Merge upstream/v5-develop

This commit is contained in:
Benjamin Beganović 2021-05-10 11:50:05 +02:00
commit 85822c45ac
128 changed files with 275076 additions and 267942 deletions

View File

@ -51,11 +51,13 @@ TRUSTED_PROXIES=
NINJA_ENVIRONMENT=selfhost
PHANTOMJS_PDF_GENERATION=true
#options - snappdf / phantom / hosted_ninja
PDF_GENERATOR=phantom
PHANTOMJS_KEY='a-demo-key-with-low-quota-per-ip-address'
PHANTOMJS_SECRET=secret
UPDATE_SECRET=
UPDATE_SECRET=secret
COMPOSER_AUTH='{"github-oauth": {"github.com": "${{ secrets.GITHUB_TOKEN }}"}}'
SENTRY_LARAVEL_DSN=https://cc7e8e2c678041689e53e409b7dba236@sentry.invoicing.co/5

View File

@ -1,90 +0,0 @@
language: php
sudo: true
dist: xenial
services:
- mysql
- xvfb
# Prevent tests from taking more than 50 minutes
group: deprecated-2017Q4
php:
# - 7.3
- 7.4
# - nightly
addons:
apt:
packages:
- libonig-dev
chrome: stable
hosts:
- www.ninja.test
cache:
directories:
- vendor
- $HOME/.composer/cache
env:
global:
- COMPOSER_DISCARD_CHANGES=true
- COMPOSER_NO_INTERACTION=1
- COMPOSER_DISABLE_XDEBUG_WARN=1
before_install:
# set GitHub token and update composer
- if [ -n "$GH_TOKEN" ]; then composer config github-oauth.github.com ${GH_TOKEN}; fi;
- composer self-update 1.10.19 && composer -V
# - export USE_ZEND_ALLOC=0
- rvm use 1.9.3 --install --fuzzy
- cp .env.travis .env
install:
# install Composer dependencies
# - rm composer.lock
# these providers require referencing git commit's which cause Travis to fail
# - sed -i '/mollie/d' composer.json
# - sed -i '/2checkout/d' composer.json
- travis_retry composer install --no-interaction
before_script:
# copy configuration files
- php artisan key:generate --no-interaction
- sed -i '$a NINJA_DEV=true' .env
# create the database and user
- mysql -u root -e "create database IF NOT EXISTS ninja01;"
- mysql -u root -e "create database IF NOT EXISTS ninja02;"
- mysql -u root -e "GRANT ALL PRIVILEGES ON ninja01.* To 'ninja'@'localhost' IDENTIFIED BY 'ninja'; FLUSH PRIVILEGES;"
- mysql -u root -e "GRANT ALL PRIVILEGES ON ninja02.* To 'ninja'@'localhost' IDENTIFIED BY 'ninja'; FLUSH PRIVILEGES;"
# migrate and seed the database
- php artisan migrate --database=db-ninja-01 --seed --no-interaction
- php artisan migrate --database=db-ninja-02 --seed --no-interaction
- php artisan optimize
- npm install
- npm run production
# migrate and seed the database
# Start webserver on ninja.test:8000
# export DISPLAY=:99.0
# sh -e /etc/init.d/xvfb start
# sleep 3
# ./vendor/laravel/dusk/bin/chromedriver-linux &
- php artisan dusk:chrome-driver 80
- php artisan serve &
script:
- php ./vendor/bin/phpunit --debug --verbose --coverage-clover=coverage.xml
#- php artisan dusk
#- npm test
after_success:
- bash <(curl -s https://codecov.io/bash)
notifications:
email:
on_success: never
on_failure: change
slack:
invoiceninja: SLraaKBDvjeRuRtY9o3Yvp1b

View File

@ -1,6 +1,9 @@
# Release notes
## [Unreleased (daily channel)](https://github.com/invoiceninja/invoiceninja/tree/v5-develop)
- Add Cache-control: no-cache to prevent overaggressive caching of assets
## [v5.1.56-release](https://github.com/invoiceninja/invoiceninja/releases/tag/v5.1.56-release)
## Fixed:
- Fix User created/updated/deleted Actvity display format
- Fix for Stripe autobill / token regression
@ -9,7 +12,7 @@
- Invoice / Quote / Credit created notifications
- Logout route - deletes all auth tokens
## [v5.1.54-release](https://github.com/invoiceninja/invoiceninja/releases/tag/v5.1.50-release)
## [v5.1.54-release](https://github.com/invoiceninja/invoiceninja/releases/tag/v5.1.54-release)
## Fixed:
- Fixes for e-mails, encoding & parsing invalid HTML

4
CODE_OF_CONDUCT.md Normal file
View File

@ -0,0 +1,4 @@
# Invoice Ninja Code of Conduct
The development team has invested a tremendous amount of time and energy into this project. While we appreciate that bugs can be frustrating we ask that our community refrain from insults and snide remarks. We're happy to provide support to both our hosted and selfhosted communities but ask that feedback is always polite.

View File

@ -1 +1 @@
5.1.55
5.1.61

View File

@ -0,0 +1,120 @@
<?php
namespace App\Console\Commands;
use App\Models\Company;
use App\Models\User;
use App\Utils\CurlUtils;
use Illuminate\Console\Command;
class MobileLocalization extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'ninja:mobile-localization {--type=}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Generate mobile localization resources';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$type = strtolower($this->option('type'));
switch ($type) {
case 'laravel':
$this->laravelResources();
break;
default:
$this->flutterResources();
break;
}
}
private function laravelResources()
{
$resources = $this->getResources();
foreach ($resources as $key => $val) {
$transKey = "texts.{$key}";
if (trans($transKey) == $transKey) {
echo "'$key' => '$val',\n";
}
}
}
private function flutterResources()
{
$languages = cache('languages');
$resources = $this->getResources();
foreach ($languages as $language) {
if ($language->locale == 'en') {
continue;
}
echo "'{$language->locale}': {\n";
foreach ($resources as $key => $val) {
$text = trim(addslashes(trans("texts.{$key}", [], $language->locale)));
if (substr($text, 0, 6) == 'texts.') {
$text = $resources->$key;
}
$text = str_replace(array('<b>', '</b>'), '', $text);
$text = str_replace(array('<i>', '</i>'), '', $text);
$text = str_replace(array('<strong>', '</strong>'), '', $text);
echo "'$key': '$text',\n";
}
echo "},\n";
}
}
private function getResources()
{
$url = 'https://raw.githubusercontent.com/invoiceninja/flutter-client/develop/lib/utils/i18n.dart';
$data = CurlUtils::get($url);
$start = strpos($data, 'do not remove comment') + 25;
$end = strpos($data, '},', $start);
$data = substr($data, $start, $end - $start - 5);
$data = str_replace("\n", "", $data);
$data = str_replace("\"", "\'", $data);
$data = str_replace("'", "\"", $data);
return json_decode('{' . rtrim($data, ',') . '}');
}
protected function getOptions()
{
return [
['type', null, InputOption::VALUE_OPTIONAL, 'Type', null],
];
}
}

View File

@ -42,43 +42,42 @@ class PostUpdate extends Command
{
set_time_limit(0);
nlog('running post update');
info('running post update');
try {
Artisan::call('migrate', ['--force' => true]);
} catch (\Exception $e) {
nlog("I wasn't able to migrate the data.");
info("I wasn't able to migrate the data.");
}
nlog("finished migrating");
exec('vendor/bin/composer install --no-dev -o');
$output = [];
nlog("finished running composer install ");
exec('vendor/bin/composer install --no-dev -o', $output);
info(print_r($output,1));
info("finished running composer install ");
try {
Artisan::call('optimize');
} catch (\Exception $e) {
nlog("I wasn't able to optimize.");
info("I wasn't able to optimize.");
}
nlog("optimized");
info("optimized");
try {
Artisan::call('view:clear');
} catch (\Exception $e) {
nlog("I wasn't able to clear the views.");
info("I wasn't able to clear the views.");
}
nlog("view cleared");
/* For the following to work, the web user (www-data) must own all the directories */
info("view cleared");
VersionCheck::dispatch();
nlog("sent for version check");
info("Sent for version check");
// echo "Done.";
}
}

View File

@ -11,7 +11,9 @@
namespace App\DataMapper\Analytics;
class AccountCreated
use Turbo124\Beacon\ExampleMetric\GenericCounter;
class AccountCreated extends GenericCounter
{
/**
* The type of Sample.

View File

@ -11,7 +11,9 @@
namespace App\DataMapper\Analytics;
class AccountDeleted
use Turbo124\Beacon\ExampleMetric\GenericCounter;
class AccountDeleted extends GenericCounter
{
/**
* The type of Sample.

View File

@ -0,0 +1,72 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\DataMapper\Analytics;
use Turbo124\Beacon\ExampleMetric\GenericMixedMetric;
class DbQuery extends GenericMixedMetric
{
/**
* The type of Sample.
*
* Monotonically incrementing counter
*
* - counter
*
* @var string
*/
public $type = 'mixed_metric';
/**
* The name of the counter.
* @var string
*/
public $name = 'db.queries';
/**
* The datetime of the counter measurement.
*
* date("Y-m-d H:i:s")
*
* @var DateTime
*/
public $datetime;
/**
* The Class failure name
* set to 0.
*
* @var string
*/
public $string_metric5 = 'method';
public $string_metric6 = 'url';
public $string_metric7 = 'ip_address';
/**
* The counter
* set to 1.
*
* @var string
*/
public $int_metric1 = 1;
public $double_metric2 = 1;
public function __construct($string_metric5, $string_metric6, $int_metric1, $double_metric2, $string_metric7) {
$this->int_metric1 = $int_metric1;
$this->string_metric5 = $string_metric5;
$this->string_metric6 = $string_metric6;
$this->double_metric2 = $double_metric2;
$this->string_metric7 = $string_metric7;
}
}

View File

@ -11,7 +11,9 @@
namespace App\DataMapper\Analytics;
class EmailFailure
use Turbo124\Beacon\ExampleMetric\GenericMixedMetric;
class EmailFailure extends GenericMixedMetric
{
/**
* The type of Sample.

View File

@ -11,7 +11,9 @@
namespace App\DataMapper\Analytics;
class EmailInvoiceFailure
use Turbo124\Beacon\ExampleMetric\GenericMixedMetric;
class EmailInvoiceFailure extends GenericMixedMetric
{
/**
* The type of Sample.

View File

@ -11,7 +11,9 @@
namespace App\DataMapper\Analytics;
class EmailSuccess
use Turbo124\Beacon\ExampleMetric\GenericMixedMetric;
class EmailSuccess extends GenericMixedMetric
{
/**

View File

@ -11,7 +11,9 @@
namespace App\DataMapper\Analytics;
class LoginFailure
use Turbo124\Beacon\ExampleMetric\GenericCounter;
class LoginFailure extends GenericCounter
{
/**
* The type of Sample.

View File

@ -11,7 +11,9 @@
namespace App\DataMapper\Analytics;
class LoginSuccess
use Turbo124\Beacon\ExampleMetric\GenericCounter;
class LoginSuccess extends GenericCounter
{
/**
* The type of Sample.

View File

@ -0,0 +1,79 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\DataMapper\Analytics\Mail;
use Turbo124\Beacon\ExampleMetric\GenericMixedMetric;
class EmailBounce extends GenericMixedMetric
{
/**
* The type of Sample.
*
* Monotonically incrementing counter
*
* - counter
*
* @var string
*/
public $type = 'mixed_metric';
/**
* The name of the counter.
* @var string
*/
public $name = 'job.bounce.email';
/**
* The datetime of the counter measurement.
*
* date("Y-m-d H:i:s")
*
* @var DateTime
*/
public $datetime;
/**
* The Class failure name
* set to 0.
*
* @var string
*/
public $string_metric5 = 'tag';
/**
* The exception string
* set to 0.
*
* @var string
*/
public $string_metric6 = 'from';
/**
* Company Key
* @var string
*/
public $string_metric7 = 'messageid';
/**
* The counter
* set to 1.
*
* @var string
*/
public $int_metric1 = 1;
public function __construct($string_metric5,$string_metric6,$string_metric7) {
$this->string_metric5 = $string_metric5;
$this->string_metric6 = $string_metric6;
$this->string_metric7 = $string_metric7;
}
}

View File

@ -0,0 +1,79 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\DataMapper\Analytics\Mail;
use Turbo124\Beacon\ExampleMetric\GenericMixedMetric;
class EmailSpam extends GenericMixedMetric
{
/**
* The type of Sample.
*
* Monotonically incrementing counter
*
* - counter
*
* @var string
*/
public $type = 'mixed_metric';
/**
* The name of the counter.
* @var string
*/
public $name = 'job.spam.email';
/**
* The datetime of the counter measurement.
*
* date("Y-m-d H:i:s")
*
* @var DateTime
*/
public $datetime;
/**
* The Class failure name
* set to 0.
*
* @var string
*/
public $string_metric5 = 'tag';
/**
* The exception string
* set to 0.
*
* @var string
*/
public $string_metric6 = 'from';
/**
* Company Key
* @var string
*/
public $string_metric7 = 'messageid';
/**
* The counter
* set to 1.
*
* @var string
*/
public $int_metric1 = 1;
public function __construct($string_metric5,$string_metric6,$string_metric7) {
$this->string_metric5 = $string_metric5;
$this->string_metric6 = $string_metric6;
$this->string_metric7 = $string_metric7;
}
}

View File

@ -11,7 +11,9 @@
namespace App\DataMapper\Analytics;
class MigrationFailure
use Turbo124\Beacon\ExampleMetric\GenericMixedMetric;
class MigrationFailure extends GenericMixedMetric
{
/**
* The type of Sample.

View File

@ -11,7 +11,9 @@
namespace App\DataMapper\Analytics;
class SendRecurringFailure
use Turbo124\Beacon\ExampleMetric\GenericMixedMetric;
class SendRecurringFailure extends GenericMixedMetric
{
/**
* The type of Sample.

View File

@ -12,6 +12,7 @@
namespace App\Events\Product;
use App\Models\Company;
use App\Models\Product;
use Illuminate\Queue\SerializesModels;
/**

View File

@ -51,6 +51,7 @@ class RecurringInvoiceToInvoiceFactory
$invoice->client_id = $client->id;
$invoice->auto_bill_enabled = $recurring_invoice->auto_bill_enabled;
$invoice->paid_to_date = 0;
$invoice->design_id = $recurring_invoice->design_id;
return $invoice;
}

View File

@ -14,12 +14,14 @@ namespace App\Http\Controllers;
use App\Http\Requests\Activity\DownloadHistoricalEntityRequest;
use App\Models\Activity;
use App\Transformers\ActivityTransformer;
use App\Utils\HostedPDF\NinjaPdf;
use App\Utils\PhantomJS\Phantom;
use App\Utils\Traits\Pdf\PdfMaker;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use stdClass;
use Symfony\Component\HttpFoundation\StreamedResponse;
use stdClass;
class ActivityController extends BaseController
{
@ -139,7 +141,15 @@ class ActivityController extends BaseController
return response()->json(['message'=> ctrans('texts.no_backup_exists'), 'errors' => new stdClass], 404);
}
$pdf = $this->makePdf(null, null, $backup->html_backup);
if (config('ninja.phantomjs_pdf_generation') || config('ninja.pdf_generator') == 'phantom') {
$pdf = (new Phantom)->convertHtmlToPdf($backup->html_backup);
}
elseif(config('ninja.invoiceninja_hosted_pdf_generation') || config('ninja.pdf_generator') == 'hosted_ninja'){
$pdf = (new NinjaPdf())->build($backup->html_backup);
}
else {
$pdf = $this->makePdf(null, null, $backup->html_backup);
}
if (isset($activity->invoice_id)) {
$filename = $activity->invoice->numberFormatter().'.pdf';

View File

@ -194,8 +194,15 @@ class LoginController extends BaseController
}
$user->setCompany($user->account->default_company);
$timeout = auth()->user()->company()->default_password_timeout / 60000;
Cache::put(auth()->user()->hashed_id.'_logged_in', Str::random(64), $timeout);
$timeout = $user->company()->default_password_timeout;
if($timeout == 0)
$timeout = 30*60*1000*1000;
else
$timeout = $timeout/1000;
Cache::put($user->hashed_id.'_logged_in', Str::random(64), $timeout);
$cu = CompanyUser::query()
->where('user_id', auth()->user()->id);
@ -209,7 +216,8 @@ class LoginController extends BaseController
});
return $this->listResponse($cu);
return $this->timeConstrainedResponse($cu);
// return $this->listResponse($cu);
} else {
@ -282,6 +290,10 @@ class LoginController extends BaseController
}
});
if($request->has('current_company') && $request->input('current_company') == 'true')
$cu->where("company_id", $company_token->company_id);
return $this->refreshResponse($cu);
}
@ -328,7 +340,15 @@ class LoginController extends BaseController
Auth::login($existing_user, true);
$existing_user->setCompany($existing_user->account->default_company);
$timeout = $existing_user->company()->default_password_timeout / 60000;
$timeout = $existing_user->company()->default_password_timeout;
if($timeout == 0)
$timeout = 30*60*1000*1000;
else
$timeout = $timeout/1000;
Cache::put($existing_user->hashed_id.'_logged_in', Str::random(64), $timeout);
$cu = CompanyUser::query()
@ -342,7 +362,7 @@ class LoginController extends BaseController
}
});
return $this->listResponse($cu);
return $this->timeConstrainedResponse($cu);
}
}
@ -370,7 +390,15 @@ class LoginController extends BaseController
auth()->user()->email_verified_at = now();
auth()->user()->save();
$timeout = auth()->user()->company()->default_password_timeout / 60000;
$timeout = auth()->user()->company()->default_password_timeout;
if($timeout == 0)
$timeout = 30*60*1000*1000;
else
$timeout = $timeout/1000;
Cache::put(auth()->user()->hashed_id.'_logged_in', Str::random(64), $timeout);
$cu = CompanyUser::whereUserId(auth()->user()->id);
@ -383,7 +411,7 @@ class LoginController extends BaseController
}
});
return $this->listResponse($cu);
return $this->timeConstrainedResponse($cu);
}
return response()

View File

@ -107,10 +107,11 @@ class BaseController extends Controller
'token',
'company.activities',
'company.documents',
//'company.users.company_user',
'company.users.company_user',
'company.tax_rates',
'company.groups',
'company.payment_terms',
'company.designs.company',
];
public function __construct()
@ -219,7 +220,7 @@ class BaseController extends Controller
},
'company.company_gateways' => function ($query) use ($user) {
$query->whereNotNull('updated_at');
$query->whereNotNull('updated_at')->with('gateway');
if(!$user->isAdmin())
$query->where('company_gateways.user_id', $user->id);
@ -367,6 +368,192 @@ class BaseController extends Controller
return $this->response($this->manager->createData($resource)->toArray());
}
protected function timeConstrainedResponse($query)
{
$user = auth()->user();
if ($user->getCompany()->is_large)
$this->manager->parseIncludes($this->mini_load);
else
$this->manager->parseIncludes($this->first_load);
$this->serializer = request()->input('serializer') ?: EntityTransformer::API_SERIALIZER_ARRAY;
if ($this->serializer === EntityTransformer::API_SERIALIZER_JSON) {
$this->manager->setSerializer(new JsonApiSerializer());
} else {
$this->manager->setSerializer(new ArraySerializer());
}
$transformer = new $this->entity_transformer($this->serializer);
$created_at = request()->has('created_at') ? request()->input('created_at') : 0;
$created_at = date('Y-m-d H:i:s', $created_at);
$query->with(
[
'company' => function ($query) use ($created_at, $user) {
$query->whereNotNull('created_at')->with('documents');
},
'company.clients' => function ($query) use ($created_at, $user) {
$query->where('clients.created_at', '>=', $created_at)->with('contacts.company', 'gateway_tokens', 'documents');
if(!$user->hasPermission('view_client'))
$query->where('clients.user_id', $user->id)->orWhere('clients.assigned_user_id', $user->id);
},
'company.company_gateways' => function ($query) use ($user) {
$query->whereNotNull('created_at')->with('gateway');
if(!$user->isAdmin())
$query->where('company_gateways.user_id', $user->id);
},
'company.credits'=> function ($query) use ($created_at, $user) {
$query->where('created_at', '>=', $created_at)->with('invitations', 'documents');
if(!$user->hasPermission('view_credit'))
$query->where('credits.user_id', $user->id)->orWhere('credits.assigned_user_id', $user->id);
},
// 'company.designs'=> function ($query) use ($created_at, $user) {
// $query->where('created_at', '>=', $created_at)->with('company');
// if(!$user->isAdmin())
// $query->where('designs.user_id', $user->id);
// },
'company.documents'=> function ($query) use ($created_at, $user) {
$query->where('created_at', '>=', $created_at);
},
'company.expenses'=> function ($query) use ($created_at, $user) {
$query->where('created_at', '>=', $created_at)->with('documents');
if(!$user->hasPermission('view_expense'))
$query->where('expenses.user_id', $user->id)->orWhere('expenses.assigned_user_id', $user->id);
},
'company.groups' => function ($query) use ($created_at, $user) {
$query->where('created_at', '>=', $created_at);
if(!$user->isAdmin())
$query->where('group_settings.user_id', $user->id);
},
'company.invoices'=> function ($query) use ($created_at, $user) {
$query->where('created_at', '>=', $created_at)->with('invitations', 'documents');
if(!$user->hasPermission('view_invoice'))
$query->where('invoices.user_id', $user->id)->orWhere('invoices.assigned_user_id', $user->id);
},
'company.payments'=> function ($query) use ($created_at, $user) {
$query->where('created_at', '>=', $created_at)->with('paymentables', 'documents');
if(!$user->hasPermission('view_payment'))
$query->where('payments.user_id', $user->id)->orWhere('payments.assigned_user_id', $user->id);
},
'company.payment_terms'=> function ($query) use ($created_at, $user) {
$query->where('created_at', '>=', $created_at);
if(!$user->isAdmin())
$query->where('payment_terms.user_id', $user->id);
},
'company.products' => function ($query) use ($created_at, $user) {
$query->where('created_at', '>=', $created_at)->with('documents');
if(!$user->hasPermission('view_product'))
$query->where('products.user_id', $user->id)->orWhere('products.assigned_user_id', $user->id);
},
'company.projects'=> function ($query) use ($created_at, $user) {
$query->where('created_at', '>=', $created_at)->with('documents');
if(!$user->hasPermission('view_project'))
$query->where('projects.user_id', $user->id)->orWhere('projects.assigned_user_id', $user->id);
},
'company.quotes'=> function ($query) use ($created_at, $user) {
$query->where('created_at', '>=', $created_at)->with('invitations', 'documents');
if(!$user->hasPermission('view_quote'))
$query->where('quotes.user_id', $user->id)->orWhere('quotes.assigned_user_id', $user->id);
},
'company.recurring_invoices'=> function ($query) use ($created_at, $user) {
$query->where('created_at', '>=', $created_at)->with('invitations', 'documents');
if(!$user->hasPermission('view_recurring_invoice'))
$query->where('recurring_invoices.user_id', $user->id)->orWhere('recurring_invoices.assigned_user_id', $user->id);
},
'company.tasks'=> function ($query) use ($created_at, $user) {
$query->where('created_at', '>=', $created_at)->with('documents');
if(!$user->hasPermission('view_task'))
$query->where('tasks.user_id', $user->id)->orWhere('tasks.assigned_user_id', $user->id);
},
'company.tax_rates' => function ($query) use ($created_at, $user) {
$query->where('created_at', '>=', $created_at);
if(!$user->isAdmin())
$query->where('tax_rates.user_id', $user->id);
},
'company.vendors'=> function ($query) use ($created_at, $user) {
$query->where('created_at', '>=', $created_at)->with('contacts', 'documents');
if(!$user->hasPermission('view_vendor'))
$query->where('vendors.user_id', $user->id)->orWhere('vendors.assigned_user_id', $user->id);
},
'company.expense_categories'=> function ($query) use ($created_at, $user) {
$query->where('created_at', '>=', $created_at);
if(!$user->isAdmin())
$query->where('expense_categories.user_id', $user->id);
},
'company.task_statuses'=> function ($query) use ($created_at, $user) {
$query->where('created_at', '>=', $created_at);
if(!$user->isAdmin())
$query->where('task_statuses.user_id', $user->id);
},
'company.activities'=> function ($query) use($user) {
if(!$user->isAdmin())
$query->where('activities.user_id', $user->id);
},
'company.subscriptions'=> function ($query) use($created_at, $user) {
$query->where('created_at', '>=', $created_at);
if(!$user->isAdmin())
$query->where('subscriptions.user_id', $user->id);
}
]
);
if ($query instanceof Builder) {
$limit = request()->input('per_page', 20);
$paginator = $query->paginate($limit);
$query = $paginator->getCollection();
$resource = new Collection($query, $transformer, $this->entity_type);
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
} else {
$resource = new Collection($query, $transformer, $this->entity_type);
}
return $this->response($this->manager->createData($resource)->toArray());
}
protected function listResponse($query)
{
$this->buildManager();

View File

@ -282,7 +282,7 @@ class ClientController extends BaseController
$this->uploadLogo($request->file('company_logo'), $client->company, $client);
event(new ClientWasUpdated($client, $client->company, Ninja::eventVars(auth()->user()->id)));
event(new ClientWasUpdated($client, $client->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
return $this->itemResponse($client->fresh());
}
@ -380,7 +380,7 @@ class ClientController extends BaseController
$this->uploadLogo($request->file('company_logo'), $client->company, $client);
event(new ClientWasCreated($client, $client->company, Ninja::eventVars(auth()->user()->id)));
event(new ClientWasCreated($client, $client->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
return $this->itemResponse($client);
}

View File

@ -50,6 +50,8 @@ class InvoiceController extends Controller
{
set_time_limit(0);
$invoice->service()->removeUnpaidGatewayFees()->save();
$data = [
'invoice' => $invoice,
];
@ -164,7 +166,7 @@ class InvoiceController extends Controller
if ($invoices->count() == 1) {
return response()->streamDownload(function () use ($invoices) {
echo file_get_contents($invoices->first()->pdf_file_path());
}, basename($invoices->first()->pdf_file_path()));
}, basename($invoices->first()->pdf_file_path()), ['Cache-Control:' => 'no-cache']);
//return response()->download(TempFile::path($invoices->first()->pdf_file_path()), basename($invoices->first()->pdf_file_path()));
}

View File

@ -97,6 +97,10 @@ class PaymentController extends Controller
$payable_invoices = collect($request->payable_invoices);
$invoices = Invoice::whereIn('id', $this->transformKeys($payable_invoices->pluck('invoice_id')->toArray()))->get();
$invoices->each(function($invoice){
$invoice->service()->removeUnpaidGatewayFees()->save();
});
/* pop non payable invoice from the $payable_invoices array */
$payable_invoices = $payable_invoices->filter(function ($payable_invoice) use ($invoices) {

View File

@ -78,7 +78,7 @@ class QuoteController extends Controller
if ($quotes->count() == 1) {
return response()->streamDownload(function () use ($invoices) {
echo file_get_contents($invoices->first()->pdf_file_path());
}, basename($invoices->first()->pdf_file_path()));
}, basename($invoices->first()->pdf_file_path()), ['Cache-Control:' => 'no-cache']);
//return response()->download(TempFile::path($invoices->first()->pdf_file_path()), basename($quotes->first()->pdf_file_path()));
}

View File

@ -201,7 +201,7 @@ class CreditController extends BaseController
->fillDefaults()
->save();
event(new CreditWasCreated($credit, $credit->company, Ninja::eventVars(auth()->user()->id)));
event(new CreditWasCreated($credit, $credit->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
return $this->itemResponse($credit);
}
@ -378,7 +378,7 @@ class CreditController extends BaseController
$credit->service()->deletePdf();
event(new CreditWasUpdated($credit, $credit->company, Ninja::eventVars(auth()->user()->id)));
event(new CreditWasUpdated($credit, $credit->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
return $this->itemResponse($credit);
}
@ -538,7 +538,7 @@ class CreditController extends BaseController
case 'download':
return response()->streamDownload(function () use ($credit) {
echo file_get_contents($credit->pdf_file_path());
}, basename($credit->pdf_file_path()));
}, basename($credit->pdf_file_path()), ['Cache-Control:' => 'no-cache']);
//return response()->download(TempFile::path($credit->pdf_file_path()), basename($credit->pdf_file_path()));
break;
case 'archive':
@ -589,7 +589,7 @@ class CreditController extends BaseController
$file_path = $credit->service()->getCreditPdf($invitation);
return response()->download($file_path);
return response()->download($file_path, basename($file_path), ['Cache-Control:' => 'no-cache']);
}
/**

View File

@ -157,7 +157,7 @@ class EmailController extends BaseController
$this->entity_transformer = QuoteTransformer::class;
if ($entity_obj->invitations->count() >= 1)
event(new QuoteWasEmailed($entity_obj->invitations->first(), $entity_obj->company, Ninja::eventVars(auth()->user()->id), 'quote'));
event(new QuoteWasEmailed($entity_obj->invitations->first(), $entity_obj->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null), 'quote'));
}
@ -166,7 +166,7 @@ class EmailController extends BaseController
$this->entity_transformer = CreditTransformer::class;
if ($entity_obj->invitations->count() >= 1)
event(new CreditWasEmailed($entity_obj->invitations->first(), $entity_obj->company, Ninja::eventVars(auth()->user()->id), 'credit'));
event(new CreditWasEmailed($entity_obj->invitations->first(), $entity_obj->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null), 'credit'));
}

View File

@ -279,7 +279,7 @@ class ExpenseController extends BaseController
$this->uploadLogo($request->file('company_logo'), $expense->company, $expense);
event(new ExpenseWasUpdated($expense, $expense->company, Ninja::eventVars(auth()->user()->id)));
event(new ExpenseWasUpdated($expense, $expense->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
return $this->itemResponse($expense->fresh());
}
@ -373,7 +373,7 @@ class ExpenseController extends BaseController
{
$expense = $this->expense_repo->save($request->all(), ExpenseFactory::create(auth()->user()->company()->id, auth()->user()->id));
event(new ExpenseWasCreated($expense, $expense->company, Ninja::eventVars(auth()->user()->id)));
event(new ExpenseWasCreated($expense, $expense->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
return $this->itemResponse($expense);
}

View File

@ -218,7 +218,7 @@ class InvoiceController extends BaseController
->triggeredActions($request)
->save();
event(new InvoiceWasCreated($invoice, $invoice->company, Ninja::eventVars(auth()->user()->id)));
event(new InvoiceWasCreated($invoice, $invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
return $this->itemResponse($invoice);
}
@ -399,7 +399,7 @@ class InvoiceController extends BaseController
$invoice->service()->deletePdf();
event(new InvoiceWasUpdated($invoice, $invoice->company, Ninja::eventVars(auth()->user()->id)));
event(new InvoiceWasUpdated($invoice, $invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
return $this->itemResponse($invoice);
}
@ -673,7 +673,7 @@ class InvoiceController extends BaseController
case 'download':
return response()->streamDownload(function () use ($invoice) {
echo file_get_contents($invoice->pdf_file_path());
}, basename($invoice->pdf_file_path()));
}, basename($invoice->pdf_file_path()), ['Cache-Control:' => 'no-cache']);
//return response()->download(TempFile::path($invoice->pdf_file_path()), basename($invoice->pdf_file_path()));
break;
case 'restore':
@ -795,7 +795,7 @@ class InvoiceController extends BaseController
$file_path = $invoice->service()->getInvoicePdf($contact);
return response()->download($file_path, basename($file_path));
return response()->download($file_path, basename($file_path), ['Cache-Control:' => 'no-cache']);
}
/**
@ -850,7 +850,7 @@ class InvoiceController extends BaseController
$file = public_path("storage/{$file_path}");
return response()->download($file, basename($file));
return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache']);
} catch (\Exception $e) {
return response(['message' => 'Oops, something went wrong. Make sure you have symlink to storage/ in public/ directory.'], 500);
}

View File

@ -59,9 +59,11 @@ class LogoutController extends BaseController
*/
public function index(Request $request)
{
CompanyToken::with('company')
$ct = CompanyToken::with('company.tokens')
->whereRaw('BINARY `token`= ?', [$request->header('X-API-TOKEN')])
->company
->first();
$ct->company
->tokens()
->where('is_system', true)
->forceDelete();

View File

@ -18,8 +18,10 @@ use App\Jobs\Mail\NinjaMailerJob;
use App\Jobs\Mail\NinjaMailerObject;
use App\Jobs\Util\StartMigration;
use App\Mail\ExistingMigration;
use App\Mail\Migration\MaxCompanies;
use App\Models\Company;
use App\Models\CompanyToken;
use App\Utils\Ninja;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
@ -231,19 +233,51 @@ class MigrationController extends BaseController
nlog($request->all());
}
try {
return response()->json([
'_id' => Str::uuid(),
'method' => config('queue.default'),
'started_at' => now(),
], 200);
} finally {
// Controller logic here
foreach ($companies as $company) {
$is_valid = $request->file($company->company_index)->isValid();
if (!$is_valid) {
// We might want to send user something's wrong with migration or nope?
continue;
}
$user = auth()->user();
$company_count = $user->account->companies()->count();
// Look for possible existing company (based on company keys).
$existing_company = Company::whereRaw('BINARY `company_key` = ?', [$company->company_key])->first();
if(!$existing_company && $company_count >=10) {
$nmo = new NinjaMailerObject;
$nmo->mailable = new MaxCompanies($user->account->companies()->first());
$nmo->company = $user->account->companies()->first();
$nmo->settings = $user->account->companies()->first()->settings;
$nmo->to_user = $user;
NinjaMailerJob::dispatch($nmo);
return;
}
elseif($existing_company && $company_count > 10 ){
$nmo = new NinjaMailerObject;
$nmo->mailable = new MaxCompanies($user->account->companies()->first());
$nmo->company = $user->account->companies()->first();
$nmo->settings = $user->account->companies()->first()->settings;
$nmo->to_user = $user;
NinjaMailerJob::dispatch($nmo);
return;
}
$checks = [
'existing_company' => $existing_company ? (bool)1 : false,
'force' => property_exists($company, 'force') ? (bool) $company->force : false,
@ -254,9 +288,9 @@ class MigrationController extends BaseController
nlog('Migrating: Existing company without force. (CASE_01)');
$nmo = new NinjaMailerObject;
$nmo->mailable = new ExistingMigration();
$nmo->company = $existing_company;
$nmo->settings = $existing_company->settings;
$nmo->mailable = new ExistingMigration($existing_company);
$nmo->company = $user->account->companies()->first();
$nmo->settings = $user->account->companies()->first();
$nmo->to_user = $user;
NinjaMailerJob::dispatch($nmo);
@ -345,20 +379,18 @@ class MigrationController extends BaseController
return;
}
try {
// StartMigration::dispatch(base_path("storage/app/public/$migration_file"), $user, $fresh_company)->delay(now()->addSeconds(5));
nlog("starting migration job");
nlog($migration_file);
StartMigration::dispatch($migration_file, $user, $fresh_company);
} catch (\Exception $e) {
nlog($e->getMessage());
}
if(Ninja::isHosted())
StartMigration::dispatch($migration_file, $user, $fresh_company)->onQueue('migration');
else
StartMigration::dispatch($migration_file, $user, $fresh_company);
}
return response()->json([
'_id' => Str::uuid(),
'method' => config('queue.default'),
'started_at' => now(),
], 200);
}
}
}

View File

@ -208,6 +208,9 @@ class PaymentController extends BaseController
{
$payment = $this->payment_repo->save($request->all(), PaymentFactory::create(auth()->user()->company()->id, auth()->user()->id));
if($request->has('email_receipt') && $request->input('email_receipt') == 'true' && !$payment->client->getSetting('client_manual_payment_notification'))
$payment->service()->sendEmail();
return $this->itemResponse($payment);
}
@ -382,7 +385,7 @@ class PaymentController extends BaseController
$payment = $this->payment_repo->save($request->all(), $payment);
event(new PaymentWasUpdated($payment, $payment->company, Ninja::eventVars(auth()->user()->id)));
event(new PaymentWasUpdated($payment, $payment->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
return $this->itemResponse($payment);
}

View File

@ -11,6 +11,8 @@
namespace App\Http\Controllers;
use App\DataMapper\Analytics\EmailBounce;
use App\DataMapper\Analytics\EmailSpam;
use App\Jobs\Util\SystemLogger;
use App\Libraries\MultiDB;
use App\Models\CreditInvitation;
@ -19,6 +21,7 @@ use App\Models\QuoteInvitation;
use App\Models\RecurringInvoiceInvitation;
use App\Models\SystemLog;
use Illuminate\Http\Request;
use Turbo124\Beacon\Facades\LightLogs;
/**
* Class PostMarkController.
@ -71,8 +74,7 @@ class PostMarkController extends BaseController
if($request->header('X-API-SECURITY') && $request->header('X-API-SECURITY') == config('postmark.secret'))
{
nlog($request->all());
// nlog($request->all());
MultiDB::findAndSetDbByCompanyKey($request->input('Tag'));
@ -157,6 +159,14 @@ class PostMarkController extends BaseController
$this->invitation->email_status = 'bounced';
$this->invitation->save();
$bounce = new EmailBounce(
$request->input('Tag'),
$request->input('From'),
$request->input('MessageID')
);
LightLogs::create($bounce)->batch();
SystemLogger::dispatch($request->all(), SystemLog::CATEGORY_MAIL, SystemLog::EVENT_MAIL_BOUNCED, SystemLog::TYPE_WEBHOOK_RESPONSE, $this->invitation->contact->client);
}
@ -191,6 +201,14 @@ class PostMarkController extends BaseController
$this->invitation->email_status = 'spam';
$this->invitation->save();
$spam = new EmailSpam(
$request->input('Tag'),
$request->input('From'),
$request->input('MessageID')
);
LightLogs::create($bounce)->batch();
SystemLogger::dispatch($request->all(), SystemLog::CATEGORY_MAIL, SystemLog::EVENT_MAIL_SPAM_COMPLAINT, SystemLog::TYPE_WEBHOOK_RESPONSE, $this->invitation->contact->client);
}

View File

@ -131,18 +131,18 @@ class PreviewController extends BaseController
}
//if phantom js...... inject here..
if (config('ninja.phantomjs_pdf_generation')) {
if (config('ninja.phantomjs_pdf_generation') || config('ninja.pdf_generator') == 'phantom') {
return (new Phantom)->convertHtmlToPdf($maker->getCompiledHTML(true));
}
if(config('ninja.invoiceninja_hosted_pdf_generation')){
if(config('ninja.invoiceninja_hosted_pdf_generation') || config('ninja.pdf_generator') == 'hosted_ninja'){
return (new NinjaPdf())->build($maker->getCompiledHTML(true));
}
//else
$file_path = PreviewPdf::dispatchNow($maker->getCompiledHTML(true), auth()->user()->company());
return response()->download($file_path)->deleteFileAfterSend(true);
return response()->download($file_path, basename($file_path), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
}
return $this->blankEntity();

View File

@ -211,7 +211,7 @@ class QuoteController extends BaseController
$quote = $quote->service()->fillDefaults()->save();
event(new QuoteWasCreated($quote, $quote->company, Ninja::eventVars(auth()->user()->id)));
event(new QuoteWasCreated($quote, $quote->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
return $this->itemResponse($quote);
}
@ -389,7 +389,7 @@ class QuoteController extends BaseController
$quote->service()->deletePdf();
event(new QuoteWasUpdated($quote, $quote->company, Ninja::eventVars(auth()->user()->id)));
event(new QuoteWasUpdated($quote, $quote->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
return $this->itemResponse($quote);
}
@ -677,7 +677,7 @@ class QuoteController extends BaseController
case 'download':
return response()->streamDownload(function () use ($quote) {
echo file_get_contents($quote->pdf_file_path());
}, basename($quote->pdf_file_path()));
}, basename($quote->pdf_file_path()), ['Cache-Control:' => 'no-cache']);
//return response()->download(TempFile::path($quote->pdf_file_path()), basename($quote->pdf_file_path()));
break;
case 'restore':
@ -730,7 +730,7 @@ class QuoteController extends BaseController
$file_path = $quote->service()->getQuotePdf($contact);
return response()->download($file_path);
return response()->download($file_path, basename($file_path), ['Cache-Control:' => 'no-cache']);
}
/**

View File

@ -490,7 +490,7 @@ class RecurringInvoiceController extends BaseController
$file_path = $recurring_invoice->service()->getInvoicePdf($contact);
return response()->download($file_path, basename($file_path));
return response()->download($file_path, basename($file_path), ['Cache-Control:' => 'no-cache']);
}
/**

View File

@ -58,25 +58,20 @@ class SelfUpdateController extends BaseController
set_time_limit(0);
define('STDIN', fopen('php://stdin', 'r'));
if (Ninja::isNinja()) {
if (Ninja::isHosted()) {
return response()->json(['message' => ctrans('texts.self_update_not_available')], 403);
}
if(!$this->testWritable())
throw new FilePermissionsFailure('Cannot update system because files are not writable!');
$this->testWritable();
// Check if new version is available
//if($updater->source()->isNewVersionAvailable()) {
// Get the new version available
$versionAvailable = $updater->source()->getVersionAvailable();
// Get the new version available
$versionAvailable = $updater->source()->getVersionAvailable();
// Create a release
$release = $updater->source()->fetch($versionAvailable);
// Create a release
$release = $updater->source()->fetch($versionAvailable);
$updater->source()->update($release);
$updater->source()->update($release);
//}
$cacheCompiled = base_path('bootstrap/cache/compiled.php');
if (file_exists($cacheCompiled)) { unlink ($cacheCompiled); }
@ -84,7 +79,6 @@ class SelfUpdateController extends BaseController
if (file_exists($cacheServices)) { unlink ($cacheServices); }
Artisan::call('clear-compiled');
Artisan::call('cache:clear');
Artisan::call('route:clear');
Artisan::call('view:clear');
Artisan::call('config:clear');
@ -105,7 +99,9 @@ class SelfUpdateController extends BaseController
// nlog($file->getPathname());
if ($file->isFile() && ! $file->isWritable()) {
throw new FilePermissionsFailure($file);
// throw new FilePermissionsFailure($file);
nlog("Cannot update system because {$file->getFileName()} is not writable");
throw new FilePermissionsFailure("Cannot update system because {$file->getFileName()} is not writable");
return false;
}
}

View File

@ -284,18 +284,17 @@ class SetupController extends Controller
if (file_exists($cacheCompiled)) {
unlink ($cacheCompiled);
}
$cacheServices = base_path('bootstrap/cache/services.php');
if (file_exists($cacheServices)) {
unlink ($cacheServices);
}
Artisan::call('clear-compiled');
Artisan::call('cache:clear');
Artisan::call('debugbar:clear');
Artisan::call('route:clear');
Artisan::call('view:clear');
Artisan::call('config:clear');
Cache::flush();
Artisan::call('migrate', ['--force' => true]);
Artisan::call('db:seed', ['--force' => true]);

View File

@ -43,20 +43,30 @@ class StripeConnectController extends BaseController
'country' => $request->getCompany()->country()->iso_3166_2,
];
$exists = CompanyGateway::query()
$company_gateway = CompanyGateway::query()
->where('gateway_key', 'd14dd26a47cecc30fdd65700bfb67b34')
->where('company_id', $request->getCompany()->id)
->first();
if ($exists) {
return render('gateways.stripe.connect.existing');
if ($company_gateway) {
$config = decrypt($company_gateway->config);
if(property_exists($config, 'account_id'))
return render('gateways.stripe.connect.existing');
}
$account = Account::create($data);
nlog($account);
$link = Account::link($account->id, $token);
$company_gateway = CompanyGatewayFactory::create($request->getCompany()->id, $request->getContact()->id);
nlog($link);
if(!$company_gateway)
$company_gateway = CompanyGatewayFactory::create($request->getCompany()->id, $request->getContact()->id);
$company_gateway->fill([
'gateway_key' => 'd14dd26a47cecc30fdd65700bfb67b34',

View File

@ -177,7 +177,7 @@ class SubscriptionController extends BaseController
{
$subscription = $this->subscription_repo->save($request->all(), SubscriptionFactory::create(auth()->user()->company()->id, auth()->user()->id));
event(new SubscriptionWasCreated($subscription, $subscription->company, Ninja::eventVars(auth()->user()->id)));
event(new SubscriptionWasCreated($subscription, $subscription->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
return $this->itemResponse($subscription);
}
@ -352,7 +352,7 @@ class SubscriptionController extends BaseController
$subscription = $this->subscription_repo->save($request->all(), $subscription);
event(new SubscriptionWasUpdated($subscription, $subscription->company, Ninja::eventVars(auth()->user()->id)));
event(new SubscriptionWasUpdated($subscription, $subscription->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
return $this->itemResponse($subscription);
}

View File

@ -284,7 +284,7 @@ class TaskController extends BaseController
if($task->status_order != $old_task->status_order)
$this->task_repo->sortStatuses($old_task, $task);
event(new TaskWasUpdated($task, $task->company, Ninja::eventVars(auth()->user()->id)));
event(new TaskWasUpdated($task, $task->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
return $this->itemResponse($task->fresh());
}
@ -378,7 +378,7 @@ class TaskController extends BaseController
{
$task = $this->task_repo->save($request->all(), TaskFactory::create(auth()->user()->company()->id, auth()->user()->id));
event(new TaskWasCreated($task, $task->company, Ninja::eventVars(auth()->user()->id)));
event(new TaskWasCreated($task, $task->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
return $this->itemResponse($task);
}

View File

@ -211,7 +211,7 @@ class UserController extends BaseController
nlog("in the store method of the usercontroller class");
event(new UserWasCreated($user, auth()->user(), $company, Ninja::eventVars(auth()->user()->id)));
event(new UserWasCreated($user, auth()->user(), $company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
return $this->itemResponse($user->fresh());
}
@ -401,7 +401,7 @@ class UserController extends BaseController
$user->company_user()->update(["permissions_updated_at" => now()]);
}
event(new UserWasUpdated($user, auth()->user(), auth()->user()->company, Ninja::eventVars(auth()->user()->id)));
event(new UserWasUpdated($user, auth()->user(), auth()->user()->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
return $this->itemResponse($user);
}
@ -474,7 +474,7 @@ class UserController extends BaseController
/* If the user passes the company user we archive the company user */
$user = $this->user_repo->delete($request->all(), $user);
event(new UserWasDeleted($user, auth()->user(), auth()->user()->company, Ninja::eventVars(auth()->user()->id)));
event(new UserWasDeleted($user, auth()->user(), auth()->user()->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
return $this->itemResponse($user->fresh());
}

View File

@ -278,7 +278,7 @@ class VendorController extends BaseController
$this->uploadLogo($request->file('company_logo'), $vendor->company, $vendor);
event(new VendorWasUpdated($vendor, $vendor->company, Ninja::eventVars(auth()->user()->id)));
event(new VendorWasUpdated($vendor, $vendor->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
return $this->itemResponse($vendor->fresh());
}
@ -376,7 +376,7 @@ class VendorController extends BaseController
$this->uploadLogo($request->file('company_logo'), $vendor->company, $vendor);
event(new VendorWasCreated($vendor, $vendor->company, Ninja::eventVars(auth()->user()->id)));
event(new VendorWasCreated($vendor, $vendor->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
return $this->itemResponse($vendor);
}

View File

@ -40,14 +40,13 @@ class PasswordProtection
$timeout = auth()->user()->company()->default_password_timeout;
if($timeout == 0)
$timeout = null;
$timeout = 30*60*1000*1000;
else
$timeout = now()->addMinutes($timeout/60000);
$timeout = $timeout/1000;
if (Cache::get(auth()->user()->hashed_id.'_logged_in')) {
Cache::pull(auth()->user()->hashed_id.'_logged_in');
Cache::add(auth()->user()->hashed_id.'_logged_in', Str::random(64), $timeout);
Cache::put(auth()->user()->hashed_id.'_logged_in', Str::random(64), $timeout);
return $next($request);
@ -69,12 +68,12 @@ class PasswordProtection
//If OAuth and user also has a password set - check both
if ($existing_user = MultiDB::hasUser($query) && auth()->user()->has_password && Hash::check(auth()->user()->password, $request->header('X-API-PASSWORD'))) {
Cache::add(auth()->user()->hashed_id.'_logged_in', Str::random(64), $timeout);
Cache::put(auth()->user()->hashed_id.'_logged_in', Str::random(64), $timeout);
return $next($request);
}
elseif($existing_user = MultiDB::hasUser($query) && !auth()->user()->has_password){
Cache::add(auth()->user()->hashed_id.'_logged_in', Str::random(64), $timeout);
Cache::put(auth()->user()->hashed_id.'_logged_in', Str::random(64), $timeout);
return $next($request);
}
}
@ -84,7 +83,7 @@ class PasswordProtection
}elseif ($request->header('X-API-PASSWORD') && Hash::check($request->header('X-API-PASSWORD'), auth()->user()->password)) {
Cache::add(auth()->user()->hashed_id.'_logged_in', Str::random(64), $timeout);
Cache::put(auth()->user()->hashed_id.'_logged_in', Str::random(64), $timeout);
return $next($request);

View File

@ -11,10 +11,13 @@
namespace App\Http\Middleware;
use App\DataMapper\Analytics\DbQuery;
use App\Utils\Ninja;
use Closure;
use DB;
use Illuminate\Http\Request;
use Log;
use Turbo124\Beacon\Facades\LightLogs;
/**
* Class QueryLogging.
@ -31,33 +34,35 @@ class QueryLogging
*/
public function handle(Request $request, Closure $next)
{
$timeStart = microtime(true);
// Enable query logging for development
if (config('ninja.app_env') == 'production') {
if (!Ninja::isHosted() || !config('beacon.enabled')) {
return $next($request);
}
$timeStart = microtime(true);
DB::enableQueryLog();
$response = $next($request);
if (config('ninja.app_env') != 'production') {
// hide requests made by debugbar
if (strstr($request->url(), '_debugbar') === false) {
// hide requests made by debugbar
if (strstr($request->url(), '_debugbar') === false) {
$queries = DB::getQueryLog();
$count = count($queries);
$timeEnd = microtime(true);
$time = $timeEnd - $timeStart;
$queries = DB::getQueryLog();
$count = count($queries);
$timeEnd = microtime(true);
$time = $timeEnd - $timeStart;
nlog($request->method().' - '.$request->url().": $count queries - ".$time);
nlog($request->method().' - '.urldecode($request->url()).": $count queries - ".$time);
// nlog($request->method().' - '.urldecode($request->fullUrl()).": $count queries - ".$time);
// if($count > 50)
//nlog($queries);
}
// if($count > 50)
//nlog($queries);
LightLogs::create(new DbQuery($request->method(), urldecode($request->url()), $count, $time, request()->ip()))
->batch();
}
return $response;
}
}

View File

@ -55,12 +55,17 @@ class StoreCompanyRequest extends Request
{
$input = $this->all();
if(array_key_exists('portal_domain', $input) && strlen($input['portal_domain']) > 1)
$input['portal_domain'] = str_replace("http:", "https:", $input['portal_domain']);
if (array_key_exists('google_analytics_url', $input)) {
$input['google_analytics_key'] = $input['google_analytics_url'];
}
$company_settings = CompanySettings::defaults();
//@todo this code doesn't make sense as we never return $company_settings anywhere
//@deprecated???
if (array_key_exists('settings', $input) && ! empty($input['settings'])) {
foreach ($input['settings'] as $key => $value) {
$company_settings->{$key} = $value;

View File

@ -59,7 +59,10 @@ class UpdateCompanyRequest extends Request
protected function prepareForValidation()
{
$input = $this->all();
// nlog($input);
if(array_key_exists('portal_domain', $input) && strlen($input['portal_domain']) > 1)
$input['portal_domain'] = str_replace("http:", "https:", $input['portal_domain']);
if (array_key_exists('settings', $input)) {
$input['settings'] = $this->filterSaveableSettings($input['settings']);
}

View File

@ -23,7 +23,7 @@ class UpdateUserRequest extends Request
*/
public function authorize() : bool
{
return auth()->user()->id === $this->id || auth()->user()->isAdmin();
return auth()->user()->id == $this->user->id || auth()->user()->isAdmin();
}
public function rules()
@ -45,9 +45,9 @@ class UpdateUserRequest extends Request
{
$input = $this->all();
if (isset($input['company_user']) && ! auth()->user()->isAdmin()) {
unset($input['company_user']);
}
// if (isset($input['company_user']) && ! auth()->user()->isAdmin()) {
// unset($input['company_user']);
// }
$this->replace($input);
}

View File

@ -21,6 +21,8 @@ class RecurringInvoicesCron
{
use Dispatchable;
public $tries = 1;
/**
* Create a new job instance.
*

View File

@ -91,13 +91,21 @@ class CreateEntityPdf implements ShouldQueue
public function handle()
{
/* Set the locale*/
App::setLocale($this->contact->preferredLocale());
/* Forget the singleton*/
App::forgetInstance('translator');
/* Init a new copy of the translator*/
$t = app('translator');
/* Set customized translations _NOW_ */
Lang::replace(Ninja::transformTranslations($this->entity->client->getMergedSettings()));
$this->entity->service()->deletePdf();
if (config('ninja.phantomjs_pdf_generation')) {
if (config('ninja.phantomjs_pdf_generation') || config('ninja.pdf_generator') == 'phantom') {
return (new Phantom)->generate($this->invitation);
}
@ -163,7 +171,7 @@ class CreateEntityPdf implements ShouldQueue
try {
if(config('ninja.invoiceninja_hosted_pdf_generation')){
if(config('ninja.invoiceninja_hosted_pdf_generation') || config('ninja.pdf_generator') == 'hosted_ninja'){
$pdf = (new NinjaPdf())->build($maker->getCompiledHTML(true));
}
else {

View File

@ -60,6 +60,8 @@ class EmailEntity implements ShouldQueue
public $template_data; //The data to be merged into the template
public $tries = 1;
/**
* EmailEntity constructor.
*
@ -142,7 +144,7 @@ class EmailEntity implements ShouldQueue
{
switch ($this->entity_string) {
case 'invoice':
event(new InvoiceWasEmailedAndFailed($this->invitation, $this->company, $message, $this->reminder_template, Ninja::eventVars(auth()->user()->id)));
event(new InvoiceWasEmailedAndFailed($this->invitation, $this->company, $message, $this->reminder_template, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
break;
default:

View File

@ -75,13 +75,20 @@ class NinjaMailerJob implements ShouldQueue
if (strlen($this->nmo->settings->reply_to_email) > 1) {
$reply_to_name = strlen($this->nmo->settings->reply_to_name) > 1 ? $this->nmo->settings->reply_to_name : $this->nmo->company->present()->name();
if(property_exists($this->nmo->settings, 'reply_to_name'))
$reply_to_name = strlen($this->nmo->settings->reply_to_name) > 3 ? $this->nmo->settings->reply_to_name : $this->nmo->settings->reply_to_email;
else
$reply_to_name = $this->nmo->settings->reply_to_email;
$this->nmo->mailable->replyTo($this->nmo->settings->reply_to_email, $reply_to_name);
}
if (strlen($this->nmo->settings->bcc_email) > 1)
$this->nmo->mailable->bcc($this->nmo->settings->bcc_email, $this->nmo->settings->bcc_email);
if (strlen($this->nmo->settings->bcc_email) > 1) {
nlog('bcc list available');
nlog($this->nmo->settings->bcc_email);
$this->nmo->mailable->bcc(explode(",", $this->nmo->settings->bcc_email), 'Blind Copy');
}
//send email

View File

@ -85,7 +85,10 @@ class UpdateOrCreateProduct implements ShouldQueue
$product->notes = isset($item->notes) ? $item->notes : '';
//$product->cost = isset($item->cost) ? $item->cost : 0; //this value shouldn't be updated.
$product->price = isset($item->cost) ? $item->cost : 0;
$product->quantity = isset($item->quantity) ? $item->quantity : 0;
if(!$product->id)
$product->quantity = isset($item->quantity) ? $item->quantity : 0;
$product->tax_name1 = isset($item->tax_name1) ? $item->tax_name1 : '';
$product->tax_rate1 = isset($item->tax_rate1) ? $item->tax_rate1 : 0;
$product->tax_name2 = isset($item->tax_name2) ? $item->tax_name2 : '';

View File

@ -94,7 +94,14 @@ class SendRecurring implements ShouldQueue
$invoice->invitations->each(function ($invitation) use ($invoice) {
if ($invitation->contact && strlen($invitation->contact->email) >=1) {
EmailEntity::dispatch($invitation, $invoice->company);
try{
EmailEntity::dispatch($invitation, $invoice->company);
}
catch(\Exception $e) {
nlog($e->getMessage());
}
nlog("Firing email for invoice {$invoice->number}");
}
});

View File

@ -31,6 +31,7 @@ use App\Factory\VendorFactory;
use App\Http\Requests\Company\UpdateCompanyRequest;
use App\Http\ValidationRules\ValidCompanyGatewayFeesAndLimitsRule;
use App\Http\ValidationRules\ValidUserForCompany;
use App\Jobs\Company\CreateCompanyTaskStatuses;
use App\Jobs\Company\CreateCompanyToken;
use App\Jobs\Ninja\CheckCompanyData;
use App\Jobs\Ninja\CompanySizeCheck;
@ -70,6 +71,7 @@ use App\Repositories\QuoteRepository;
use App\Repositories\UserRepository;
use App\Repositories\VendorContactRepository;
use App\Repositories\VendorRepository;
use App\Utils\Ninja;
use App\Utils\Traits\CleanLineItems;
use App\Utils\Traits\CompanyGatewayFeesAndLimitsSaver;
use App\Utils\Traits\MakesHash;
@ -82,10 +84,11 @@ use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Http\UploadedFile;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
use Turbo124\Beacon\Facades\LightLogs;
use Illuminate\Support\Facades\Mail;
class Import implements ShouldQueue
{
@ -192,15 +195,6 @@ class Import implements ShouldQueue
$array = json_decode(file_get_contents($this->file_path), 1);
$data = $array['data'];
// disable some functionality here:
// 1. disable update_products
$update_product_state = $this->company->update_products;
$this->company->update_products = false;
$this->company->save();
foreach ($this->available_imports as $import) {
if (! array_key_exists($import, $data)) {
//throw new ResourceNotAvailableForMigration("Resource {$key} is not available for migration.");
@ -220,9 +214,7 @@ class Import implements ShouldQueue
// $this->fixClientBalances();
$check_data = CheckCompanyData::dispatchNow($this->company, md5(time()));
//reset functionality here
$this->company->update_products = $update_product_state;
$this->company->save();
try{
Mail::to($this->user->email, $this->user->name())
@ -237,10 +229,13 @@ class Import implements ShouldQueue
//company size check
if ($this->company->invoices()->count() > 1000 || $this->company->products()->count() > 1000 || $this->company->clients()->count() > 1000) {
$company->is_large = true;
$company->save();
$this->company->is_large = true;
$this->company->save();
}
// CreateCompanyPaymentTerms::dispatchNow($sp035a66, $spaa9f78);
CreateCompanyTaskStatuses::dispatchNow($this->company, $this->user);
info('Completed🚀🚀🚀🚀🚀 at '.now());
unlink($this->file_path);
@ -466,6 +461,21 @@ class Import implements ShouldQueue
$user_repository = null;
}
private function checkUniqueConstraint($model, $column, $value)
{
$value = trim($value);
$model_query = (new $model())
->query()
->where($column, $value)
->exists();
if($model_query)
return $value.'_'. Str::random(5);
return $value;
}
/**
* @param array $data
* @throws Exception
@ -483,6 +493,7 @@ class Import implements ShouldQueue
$modified['user_id'] = $this->processUserId($resource);
$modified['balance'] = $modified['balance'] ?: 0;
$modified['paid_to_date'] = $modified['paid_to_date'] ?: 0;
$modified['number'] = $this->checkUniqueConstraint(Client::class, 'number', $modified['number']);
unset($modified['id']);
unset($modified['contacts']);
@ -495,6 +506,14 @@ class Import implements ShouldQueue
)
);
if(array_key_exists('created_at', $modified))
$client->created_at = Carbon::parse($modified['created_at']);
if(array_key_exists('updated_at', $modified))
$client->updated_at = Carbon::parse($modified['updated_at']);
$client->save(['timestamps' => false]);
$client->contacts()->forceDelete();
if (array_key_exists('contacts', $resource)) { // need to remove after importing new migration.json
@ -568,6 +587,12 @@ class Import implements ShouldQueue
unset($modified['id']);
unset($modified['contacts']);
if(array_key_exists('created_at', $modified))
$modified['created_at'] = Carbon::parse($modified['created_at']);
if(array_key_exists('updated_at', $modified))
$modified['updated_at'] = Carbon::parse($modified['updated_at']);
$vendor = $vendor_repository->save(
$modified,
VendorFactory::create(
@ -635,6 +660,12 @@ class Import implements ShouldQueue
$modified['company_id'] = $this->company->id;
$modified['user_id'] = $this->processUserId($resource);
if(array_key_exists('created_at', $modified))
$modified['created_at'] = Carbon::parse($modified['created_at']);
if(array_key_exists('updated_at', $modified))
$modified['updated_at'] = Carbon::parse($modified['updated_at']);
unset($modified['id']);
$product_repository->save(
@ -681,6 +712,12 @@ class Import implements ShouldQueue
$modified['company_id'] = $this->company->id;
$modified['line_items'] = $this->cleanItems($modified['line_items']);
if(array_key_exists('created_at', $modified))
$modified['created_at'] = Carbon::parse($modified['created_at']);
if(array_key_exists('updated_at', $modified))
$modified['updated_at'] = Carbon::parse($modified['updated_at']);
unset($modified['id']);
if (array_key_exists('invitations', $resource)) {
@ -688,6 +725,8 @@ class Import implements ShouldQueue
$resource['invitations'][$key]['client_contact_id'] = $this->transformId('client_contacts', $invite['client_contact_id']);
$resource['invitations'][$key]['user_id'] = $modified['user_id'];
$resource['invitations'][$key]['company_id'] = $this->company->id;
$resource['invitations'][$key]['email_status'] = '';
unset($resource['invitations'][$key]['recurring_invoice_id']);
unset($resource['invitations'][$key]['id']);
@ -752,6 +791,7 @@ class Import implements ShouldQueue
$resource['invitations'][$key]['client_contact_id'] = $this->transformId('client_contacts', $invite['client_contact_id']);
$resource['invitations'][$key]['user_id'] = $modified['user_id'];
$resource['invitations'][$key]['company_id'] = $this->company->id;
$resource['invitations'][$key]['email_status'] = '';
unset($resource['invitations'][$key]['invoice_id']);
unset($resource['invitations'][$key]['id']);
}
@ -814,6 +854,12 @@ class Import implements ShouldQueue
$modified['user_id'] = $this->processUserId($resource);
$modified['company_id'] = $this->company->id;
if(array_key_exists('created_at', $modified))
$modified['created_at'] = Carbon::parse($modified['created_at']);
if(array_key_exists('updated_at', $modified))
$modified['updated_at'] = Carbon::parse($modified['updated_at']);
unset($modified['id']);
$credit = $credit_repository->save(
@ -873,6 +919,18 @@ class Import implements ShouldQueue
$modified['company_id'] = $this->company->id;
if(array_key_exists('created_at', $modified))
$modified['created_at'] = Carbon::parse($modified['created_at']);
if(array_key_exists('updated_at', $modified))
$modified['updated_at'] = Carbon::parse($modified['updated_at']);
if(array_key_exists('tax_rate1', $modified) && is_null($modified['tax_rate1']))
$modified['tax_rate1'] = 0;
if(array_key_exists('tax_rate2', $modified) && is_null($modified['tax_rate2']))
$modified['tax_rate2'] = 0;
unset($modified['id']);
@ -881,6 +939,7 @@ class Import implements ShouldQueue
$resource['invitations'][$key]['client_contact_id'] = $this->transformId('client_contacts', $invite['client_contact_id']);
$resource['invitations'][$key]['user_id'] = $modified['user_id'];
$resource['invitations'][$key]['company_id'] = $this->company->id;
$resource['invitations'][$key]['email_status'] = '';
unset($resource['invitations'][$key]['invoice_id']);
unset($resource['invitations'][$key]['id']);
}
@ -894,6 +953,14 @@ class Import implements ShouldQueue
QuoteFactory::create($this->company->id, $modified['user_id'])
);
if(array_key_exists('created_at', $modified))
$quote->created_at = $modified['created_at'];
if(array_key_exists('updated_at', $modified))
$quote->updated_at = $modified['updated_at'];
$quote->save(['timestamps' => false]);
$old_user_key = array_key_exists('user_id', $resource) ?? $this->user->id;
$key = "quotes_{$resource['id']}";
@ -960,6 +1027,14 @@ class Import implements ShouldQueue
PaymentFactory::create($this->company->id, $modified['user_id'])
);
if(array_key_exists('created_at', $modified))
$payment->created_at = Carbon::parse($modified['created_at']);
if(array_key_exists('updated_at', $modified))
$payment->updated_at = Carbon::parse($modified['updated_at']);
$payment->save(['timestamps' => false]);
if (array_key_exists('company_gateway_id', $resource) && isset($resource['company_gateway_id']) && $resource['company_gateway_id'] != 'NULL') {
$payment->company_gateway_id = $this->transformId('company_gateways', $resource['company_gateway_id']);
$payment->save();
@ -1182,6 +1257,11 @@ class Import implements ShouldQueue
$modified['fees_and_limits'] = $this->cleanFeesAndLimits($modified['fees_and_limits']);
}
if(Ninja::isHosted() && $modified['gateway_key'] == 'd14dd26a37cecc30fdd65700bfb55b23'){
$modified['gateway_key'] = 'd14dd26a47cecc30fdd65700bfb67b34';
$modified['fees_and_limits'] = [];
}
$company_gateway = CompanyGateway::create($modified);
$key = "company_gateways_{$resource['id']}";
@ -1322,6 +1402,16 @@ class Import implements ShouldQueue
$task = Task::Create($modified);
if(array_key_exists('created_at', $modified))
$task->created_at = Carbon::parse($modified['created_at']);
if(array_key_exists('updated_at', $modified))
$task->updated_at = Carbon::parse($modified['updated_at']);
$task->save(['timestamps' => false]);
$old_user_key = array_key_exists('user_id', $resource) ?? $this->user->id;
$this->ids['tasks'] = [
@ -1402,6 +1492,16 @@ class Import implements ShouldQueue
$expense = Expense::Create($modified);
if(array_key_exists('created_at', $modified))
$expense->created_at = Carbon::parse($modified['created_at']);
if(array_key_exists('updated_at', $modified))
$expense->updated_at = Carbon::parse($modified['updated_at']);
$expense->save(['timestamps' => false]);
$old_user_key = array_key_exists('user_id', $resource) ?? $this->user->id;
$key = "expenses_{$resource['id']}";

View File

@ -23,6 +23,8 @@ class SchedulerCheck implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $tries = 1;
public function __construct()
{
}
@ -45,14 +47,17 @@ class SchedulerCheck implements ShouldQueue
Artisan::call('migrate', ['--force' => true]);
} catch (\Exception $e) {
nlog("I wasn't able to migrate the data.");
nlog($e->getMessage());
}
try {
Artisan::call('optimize');
Artisan::call('clear-compiled');
Artisan::call('route:clear');
Artisan::call('config:cache');
} catch (\Exception $e) {
nlog($e->getMessage());
nlog("I wasn't able to optimize.");
nlog($e->getMessage());
}
@ -60,6 +65,7 @@ class SchedulerCheck implements ShouldQueue
Artisan::call('view:clear');
} catch (\Exception $e) {
nlog("I wasn't able to clear the views.");
nlog($e->getMessage());
}

View File

@ -91,6 +91,21 @@ class StartMigration implements ShouldQueue
$archive = $zip->open(public_path("storage/{$this->filepath}"));
$filename = pathinfo($this->filepath, PATHINFO_FILENAME);
if($this->company->id == $this->company->account->default_company_id)
{
$new_default_company = $this->company->account->companies->first();
if ($new_default_company) {
$this->company->account->default_company_id = $new_default_company->id;
$this->company->account->save();
}
}
$update_product_flag = $this->company->update_products;
$this->company->update_products = false;
$this->company->save();
try {
if (! $archive) {
throw new ProcessingMigrationArchiveFailed('Processing migration archive failed. Migration file is possibly corrupted.');
@ -113,7 +128,16 @@ class StartMigration implements ShouldQueue
Storage::deleteDirectory(public_path("storage/migrations/{$filename}"));
} catch (NonExistingMigrationFile | ProcessingMigrationArchiveFailed | ResourceNotAvailableForMigration | MigrationValidatorFailed | ResourceDependencyMissing $e) {
$this->company->account->default_company_id = $this->company->id;
$this->company->account->save();
$this->company->update_products = $update_product_flag;
$this->company->save();
} catch (NonExistingMigrationFile | ProcessingMigrationArchiveFailed | ResourceNotAvailableForMigration | MigrationValidatorFailed | ResourceDependencyMissing | \Exception $e) {
$this->company->update_products = $update_product_flag;
$this->company->save();
Mail::to($this->user)->send(new MigrationFailed($e, $e->getMessage()));

View File

@ -54,6 +54,9 @@ class SystemLogger implements ShouldQueue
'type_id' => $this->type_id,
];
if(!$this->log)
return;
SystemLog::create($sl);
}
}

View File

@ -46,7 +46,10 @@ class VersionCheck implements ShouldQueue
{
$account = Account::first();
if($account->plan == 'white_label' && $account->plan_expires->lt(now())){
if(!$account)
return;
if($account->plan == 'white_label' && $account->plan_expires && $account->plan_expires->lt(now())){
$account->plan = null;
$account->plan_expires = null;
$account->save();

View File

@ -42,7 +42,7 @@ class CreateAccountActivity implements ShouldQueue
if(Ninja::isHosted())
{
$nmo = new NinjaMailerObject;
$nmo->mailable = new Modules\Admin\Mail\Welcome($event->user);
$nmo->mailable = new \Modules\Admin\Mail\Welcome($event->user);
$nmo->company = $event->company;
$nmo->settings = $event->company->settings;
$nmo->to_user = $event->user;

View File

@ -47,6 +47,7 @@ class InvoiceEmailActivity implements ShouldQueue
$fields->user_id = $event->invitation->invoice->user_id;
$fields->company_id = $event->invitation->invoice->company_id;
$fields->client_contact_id = $event->invitation->invoice->client_contact_id;
$fields->client_id = $event->invitation->invoice->client_id;
$fields->activity_type_id = Activity::EMAIL_INVOICE;
$this->activity_repo->save($fields, $event->invitation->invoice, $event->event_vars);

View File

@ -48,6 +48,7 @@ class QuoteEmailActivity implements ShouldQueue
$fields->user_id = $event->invitation->quote->user_id;
$fields->company_id = $event->invitation->quote->company_id;
$fields->client_contact_id = $event->invitation->quote->client_contact_id;
$fields->client_id = $event->invitation->quote->client_id;
$fields->activity_type_id = Activity::EMAIL_QUOTE;
$this->activity_repo->save($fields, $event->invitation->quote, $event->event_vars);

View File

@ -62,20 +62,20 @@ class EntityCreatedObject
switch ($this->entity_type) {
case 'invoice':
$this->template_subject = "texts.notification_invoice_created_subject";
$this->template_body = "texts.notification_invoice_sent";
$this->template_body = "texts.notification_invoice_created_body";
break;
case 'quote':
$this->template_subject = "texts.notification_quote_created_subject";
$this->template_body = "texts.notification_quote_sent";
$this->template_body = "texts.notification_quote_created_body";
break;
case 'credit':
$this->template_subject = "texts.notification_credit_created_subject";
$this->template_body = "texts.notification_credit_sent";
$this->template_body = "texts.notification_credit_created_body";
break;
default:
$this->template_subject = "texts.notification_invoice_created_subject";
$this->template_body = "texts.notification_invoice_sent";
$this->template_body = "texts.notification_invoice_created_body";
break;
}
}

View File

@ -10,14 +10,22 @@ class ExistingMigration extends Mailable
{
// use Queueable, SerializesModels;
public $company;
public $settings;
public $logo;
public $company_name;
/**
* Create a new message instance.
*
* @return void
*/
public function __construct()
public function __construct($company)
{
//
$this->company = $company;
}
/**
@ -27,8 +35,11 @@ class ExistingMigration extends Mailable
*/
public function build()
{
return $this->from(config('mail.from.address'), config('mail.from.name'))
$this->settings = $this->company->settings;
$this->logo = $this->company->present()->logo();
$this->company_name = $this->company->present()->name();
return $this->from(config('mail.from.address'), config('mail.from.name'))
->view('email.migration.existing');
}
}

View File

@ -0,0 +1,51 @@
<?php
namespace App\Mail\Migration;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class MaxCompanies extends Mailable
{
// use Queueable, SerializesModels;
public $company;
public $settings;
public $logo;
public $title;
public $message;
public $whitelabel;
/**
* Create a new message instance.
*
* @return void
*/
public function __construct($company)
{
$this->company = $company;
}
/**
* Build the message.
*
* @return $this
*/
public function build()
{
$this->settings = $this->company->settings;
$this->logo = $this->company->present()->logo();
$this->title = ctrans('texts.max_companies');
$this->message = ctrans('texts.max_companies_desc');
$this->whitelabel = $this->company->account->isPaid();
return $this->from(config('mail.from.address'), config('mail.from.name'))
->view('email.migration.max_companies');
}
}

View File

@ -79,8 +79,9 @@ class Client extends BaseModel implements HasLocalePreference
protected $with = [
'gateway_tokens',
'documents'
//'currency',
'documents',
'contacts.company',
// 'currency',
// 'primary_contact',
// 'country',
// 'contacts',

View File

@ -88,6 +88,7 @@ class Company extends BaseModel
'oauth_password_required',
'invoice_task_datelog',
'default_password_timeout',
'show_task_end_date',
];
protected $hidden = [

View File

@ -65,6 +65,7 @@ class CompanyGateway extends BaseModel
'3758e7f7c6f4cecf0f4f348b9a00f456' => 304,
'3b6621f970ab18887c4f6dca78d3f8bb' => 305,
'54faab2ab6e3223dbe848b1686490baa' => 306,
'd14dd26a47cecc30fdd65700bfb67b34' => 301,
];
protected $touches = [];
@ -225,7 +226,7 @@ class CompanyGateway extends BaseModel
{
$config = $this->getConfig();
if ($this->gateway->provider == 'Stripe' && strpos($config->publishableKey, 'test')) {
if ($this->gateway->provider == 'Stripe' && property_exists($config, 'publishableKey') && strpos($config->publishableKey, 'test')) {
return true;
}

View File

@ -128,6 +128,11 @@ class Credit extends BaseModel
return $this->hasManyThrough(Backup::class, Activity::class);
}
public function activities()
{
return $this->hasMany(Activity::class)->orderBy('id', 'DESC')->take(300);
}
public function company()
{
return $this->belongsTo(Company::class);
@ -255,10 +260,10 @@ class Credit extends BaseModel
}
if (! $invitation) {
event(new CreditWasUpdated($this, $this->company, Ninja::eventVars(auth()->user()->id)));
event(new CreditWasUpdated($this, $this->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
CreateEntityPdf::dispatchNow($this->invitations->first());
} else {
event(new CreditWasUpdated($this, $this->company, Ninja::eventVars(auth()->user()->id)));
event(new CreditWasUpdated($this, $this->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
CreateEntityPdf::dispatchNow($invitation);
}

View File

@ -129,7 +129,7 @@ class CreditInvitation extends BaseModel
$storage_path = Storage::url($this->credit->client->quote_filepath().$this->credit->numberFormatter().'.pdf');
if (! Storage::exists($this->credit->client->credit_filepath().$this->credit->numberFormatter().'.pdf')) {
event(new CreditWasUpdated($this, $this->company, Ninja::eventVars(auth()->user()->id)));
event(new CreditWasUpdated($this, $this->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
CreateEntityPdf::dispatchNow($this);
}

View File

@ -85,6 +85,7 @@ class Gateway extends StaticModel
return [GatewayType::PAYPAL => ['refund' => true, 'token_billing' => false]]; //Paypal
break;
case 20:
case 56:
return [GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true],
GatewayType::BANK_TRANSFER => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable']],
GatewayType::ALIPAY => ['refund' => false, 'token_billing' => false],

View File

@ -204,7 +204,7 @@ class Invoice extends BaseModel
public function activities()
{
return $this->hasMany(Activity::class);
return $this->hasMany(Activity::class)->orderBy('id', 'DESC')->take(300);
}
public function history()
@ -249,19 +249,14 @@ class Invoice extends BaseModel
$partial_due_date = $this->partial_due_Date ? Carbon::parse($this->partial_due_date) : false;
if ($this->status_id == self::STATUS_SENT && $due_date && $due_date->gt(now())) {
nlog("1 unpaid");
return self::STATUS_UNPAID;
} elseif ($this->status_id == self::STATUS_PARTIAL && $partial_due_date && $partial_due_date->gt(now())) {
nlog("2 partial");
return self::STATUS_PARTIAL;
} elseif ($this->status_id == self::STATUS_SENT && $due_date && $due_date->lt(now())) {
nlog("3 overdue");
return self::STATUS_OVERDUE;
} elseif ($this->status_id == self::STATUS_PARTIAL && $partial_due_date && $partial_due_date->lt(now())) {
nlog("4 overdue");
return self::STATUS_OVERDUE;
} else {
nlog("status id ");
return $this->status_id;
}
}
@ -400,9 +395,19 @@ class Invoice extends BaseModel
public function pdf_file_path($invitation = null, string $type = 'url')
{
if (! $invitation) {
$invitation = $this->invitations->first();
if($this->invitations()->exists())
$invitation = $this->invitations()->first();
else{
$this->service()->createInvitations();
$invitation = $this->invitations()->first();
}
}
if(!$invitation)
throw new \Exception('Hard fail, could not create an invitation - is there a valid contact?');
$storage_path = Storage::$type($this->client->invoice_filepath().$this->numberFormatter().'.pdf');
if (! Storage::exists($this->client->invoice_filepath().$this->numberFormatter().'.pdf')) {

View File

@ -243,7 +243,7 @@ class Payment extends BaseModel
$this->status_id = $this->refunded == $this->amount ? self::STATUS_REFUNDED : self::STATUS_PARTIALLY_REFUNDED;
$this->save();
event(new PaymentWasRefunded($this, $refund_change, $this->company, Ninja::eventVars(auth()->user()->id)));
event(new PaymentWasRefunded($this, $refund_change, $this->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
}
return true;
@ -280,7 +280,7 @@ class Payment extends BaseModel
$this->status_id = self::STATUS_CANCELLED;
$this->save();
event(new PaymentWasVoided($this, $this->company, Ninja::eventVars(auth()->user()->id)));
event(new PaymentWasVoided($this, $this->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
}
public function getLink()

View File

@ -27,7 +27,8 @@ class ClientPresenter extends EntityPresenter
return $this->entity->name;
}
$contact = $this->entity->primary_contact->first();
//$contact = $this->entity->primary_contact->first();
$contact = $this->entity->contacts->first();
$contact_name = 'No Contact Set';

View File

@ -130,6 +130,11 @@ class Quote extends BaseModel
return $this->hasManyThrough(Backup::class, Activity::class);
}
public function activities()
{
return $this->hasMany(Activity::class)->orderBy('id', 'DESC')->take(300);
}
public function user()
{
return $this->belongsTo(User::class)->withTrashed();
@ -213,7 +218,7 @@ class Quote extends BaseModel
nlog($storage_path);
if (! Storage::exists($this->client->quote_filepath().$this->numberFormatter().'.pdf')) {
event(new QuoteWasUpdated($this, $this->company, Ninja::eventVars(auth()->user()->id)));
event(new QuoteWasUpdated($this, $this->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
CreateEntityPdf::dispatchNow($invitation);
}

View File

@ -133,7 +133,7 @@ class QuoteInvitation extends BaseModel
$storage_path = Storage::url($this->quote->client->quote_filepath().$this->quote->numberFormatter().'.pdf');
if (! Storage::exists($this->quote->client->quote_filepath().$this->quote->numberFormatter().'.pdf')) {
event(new QuoteWasUpdated($this->quote, $this->company, Ninja::eventVars(auth()->user()->id)));
event(new QuoteWasUpdated($this->quote, $this->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
CreateEntityPdf::dispatchNow($this);
}

View File

@ -13,6 +13,7 @@ namespace App\Observers;
use App\Events\Company\CompanyDocumentsDeleted;
use App\Models\Company;
use App\Utils\Ninja;
class CompanyObserver
{
@ -35,7 +36,16 @@ class CompanyObserver
*/
public function updated(Company $company)
{
//
if(Ninja::isHosted() && $company->portal_mode == 'domain' && $company->isDirty('portal_domain'))
{
nlog('company observer - updated');
nlog($company->portal_domain);
nlog($company->getOriginal('portal_domain'));
//fire event to build new custom portal domain
\Modules\Admin\Jobs\Domain\CustomDomain::dispatch($company->getOriginal('portal_domain'), $company)->onQueue('domain');
}
}
/**

View File

@ -55,7 +55,7 @@ class BaseRepository
$className = $this->getEventClass($entity, 'Archived');
if (class_exists($className)) {
event(new $className($entity, $entity->company, Ninja::eventVars(auth()->user()->id)));
event(new $className($entity, $entity->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
}
}
@ -81,7 +81,7 @@ class BaseRepository
$className = $this->getEventClass($entity, 'Restored');
if (class_exists($className)) {
event(new $className($entity, $fromDeleted, $entity->company, Ninja::eventVars(auth()->user()->id)));
event(new $className($entity, $fromDeleted, $entity->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
}
}
@ -102,7 +102,7 @@ class BaseRepository
$className = $this->getEventClass($entity, 'Deleted');
if (class_exists($className) && ! ($entity instanceof Company)) {
event(new $className($entity, $entity->company, Ninja::eventVars(auth()->user()->id)));
event(new $className($entity, $entity->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
}
}
@ -302,11 +302,6 @@ class BaseRepository
/* Perform model specific tasks */
if ($model instanceof Invoice) {
nlog("in base");
nlog($state['finished_amount']);
nlog($state['starting_amount']);
nlog($model->status_id);
if (($state['finished_amount'] != $state['starting_amount']) && ($model->status_id != Invoice::STATUS_DRAFT)) {
$model->service()->updateStatus()->save();

View File

@ -23,6 +23,7 @@ use App\Models\RecurringInvoiceInvitation;
use App\Repositories\BaseRepository;
use App\Utils\Traits\MakesHash;
use App\Utils\Traits\SavesDocuments;
use Illuminate\Support\Carbon;
use ReflectionClass;
/**
@ -59,6 +60,12 @@ class InvoiceMigrationRepository extends BaseRepository
$tmp_data = $data;
if(array_key_exists('tax_rate1', $tmp_data) && is_null($tmp_data['tax_rate1']))
$tmp_data['tax_rate1'] = 0;
if(array_key_exists('tax_rate2', $tmp_data) && is_null($tmp_data['tax_rate2']))
$tmp_data['tax_rate2'] = 0;
/* We need to unset some variable as we sometimes unguard the model */
if (isset($tmp_data['invitations'])) {
@ -71,7 +78,15 @@ class InvoiceMigrationRepository extends BaseRepository
$model->fill($tmp_data);
$model->status_id = $tmp_data['status_id'];
$model->save();
if($tmp_data['created_at'])
$model->created_at = Carbon::parse($tmp_data['created_at']);
if($tmp_data['updated_at'])
$model->updated_at = Carbon::parse($tmp_data['updated_at']);
$model->save(['timestamps' => false]);
if (array_key_exists('documents', $data)) {
$this->saveDocuments($data['documents'], $model);
@ -107,35 +122,7 @@ class InvoiceMigrationRepository extends BaseRepository
InvoiceInvitation::reguard();
RecurringInvoiceInvitation::reguard();
/*
if (isset($data['invitations'])) {
$invitations = collect($data['invitations']);
$model->invitations->pluck('key')->diff($invitations->pluck('key'))->each(function ($invitation) use ($resource) {
$this->getInvitation($invitation, $resource)->delete();
});
foreach ($data['invitations'] as $invitation) {
//if no invitations are present - create one.
if (! $this->getInvitation($invitation, $resource)) {
if (isset($invitation['id'])) {
unset($invitation['id']);
}
//make sure we are creating an invite for a contact who belongs to the client only!
$contact = ClientContact::find($invitation['client_contact_id']);
if ($contact && $model->client_id == $contact->client_id) {
$new_invitation = $invitation_factory_class::create($model->company_id, $model->user_id);
$new_invitation->{$lcfirst_resource_id} = $model->id;
$new_invitation->client_contact_id = $contact->id;
$new_invitation->save();
}
}
}
}
*/
$model->load('invitations');
/* If no invitations have been created, this is our fail safe to maintain state*/
@ -152,18 +139,12 @@ class InvoiceMigrationRepository extends BaseRepository
if ($class->name == Invoice::class || $class->name == RecurringInvoice::class) {
if (($state['finished_amount'] != $state['starting_amount']) && ($model->status_id != Invoice::STATUS_DRAFT)) {
// $model->ledger()->updateInvoiceBalance(($state['finished_amount'] - $state['starting_amount']));
// $model->client->service()->updateBalance(($state['finished_amount'] - $state['starting_amount']))->save();
}
if (! $model->design_id) {
$model->design_id = $this->decodePrimaryKey($client->getSetting('invoice_design_id'));
}
if ($model->company->update_products !== false) {
UpdateOrCreateProduct::dispatchNow($model->line_items, $model, $model->company);
}
}
if ($class->name == Credit::class) {

View File

@ -159,7 +159,7 @@ class PaymentRepository extends BaseRepository {
if ($payment->client->getSetting('client_manual_payment_notification'))
$payment->service()->sendEmail();
event( new PaymentWasCreated( $payment, $payment->company, Ninja::eventVars(auth()->user()->id) ) );
event( new PaymentWasCreated( $payment, $payment->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null) ) );
}
nlog("payment amount = {$payment->amount}");
@ -209,7 +209,7 @@ class PaymentRepository extends BaseRepository {
$payment = $payment->service()->deletePayment();
event(new PaymentWasDeleted($payment, $payment->company, Ninja::eventVars(auth()->user()->id)));
event(new PaymentWasDeleted($payment, $payment->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
return $payment;
//return parent::delete($payment);

View File

@ -89,10 +89,22 @@ class UserRepository extends BaseRepository
$data['company_user']['notifications'] = CompanySettings::notificationDefaults();
$user->companies()->attach($company->id, $data['company_user']);
} else {
$cu->fill($data['company_user']);
$cu->restore();
$cu->tokens()->restore();
$cu->save();
if(auth()->user()->isAdmin())
{
$cu->fill($data['company_user']);
$cu->restore();
$cu->tokens()->restore();
$cu->save();
}
else {
$cu->notifications = $data['company_user']['notifications'];
$cu->settings = $data['company_user']['settings'];
$cu->save();
}
}
$user->with(['company_users' => function ($query) use ($company, $user) {
@ -123,7 +135,7 @@ class UserRepository extends BaseRepository
$cu->forceDelete();
}
event(new UserWasDeleted($user, $company, Ninja::eventVars(auth()->user()->id)));
event(new UserWasDeleted($user, $company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
$user->delete();
@ -146,7 +158,7 @@ class UserRepository extends BaseRepository
$cu->delete();
}
event(new UserWasDeleted($user, auth()->user(), $company, Ninja::eventVars(auth()->user()->id)));
event(new UserWasDeleted($user, auth()->user(), $company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
$user->is_deleted = true;
$user->save();
@ -164,7 +176,7 @@ class UserRepository extends BaseRepository
$user->delete();
event(new UserWasArchived($user, auth()->user(), auth()->user()->company, Ninja::eventVars(auth()->user()->id)));
event(new UserWasArchived($user, auth()->user(), auth()->user()->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
}
@ -189,7 +201,7 @@ class UserRepository extends BaseRepository
$cu->restore();
event(new UserWasRestored($user, auth()->user(), auth()->user()->company, Ninja::eventVars(auth()->user()->id)));
event(new UserWasRestored($user, auth()->user(), auth()->user()->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
}
}

View File

@ -55,6 +55,16 @@ class CreateInvitations extends AbstractService
}
});
if($this->invoice->invitations()->count() == 0) {
$contact = $this->createBlankContact();
$ii = InvoiceInvitationFactory::create($this->invoice->company_id, $this->invoice->user_id);
$ii->invoice_id = $this->invoice->id;
$ii->client_contact_id = $contact->id;
$ii->save();
}
return $this->invoice;
}
@ -65,5 +75,7 @@ class CreateInvitations extends AbstractService
$new_contact->contact_key = Str::random(40);
$new_contact->is_primary = true;
$new_contact->save();
return $new_contact;
}
}

View File

@ -60,7 +60,7 @@ class GenerateDeliveryNote
$file_path = sprintf('%s%s_delivery_note.pdf', $this->invoice->client->invoice_filepath(), $this->invoice->number);
if (config('ninja.phantomjs_pdf_generation')) {
if (config('ninja.phantomjs_pdf_generation') || config('ninja.pdf_generator') == 'phantom') {
return (new Phantom)->generate($this->invoice->invitations->first());
}
@ -92,7 +92,7 @@ class GenerateDeliveryNote
// Storage::makeDirectory($this->invoice->client->invoice_filepath(), 0775);
if(config('ninja.invoiceninja_hosted_pdf_generation')){
if(config('ninja.invoiceninja_hosted_pdf_generation') || config('ninja.pdf_generator') == 'hosted_ninja'){
$pdf = (new NinjaPdf())->build($maker->getCompiledHTML(true));
}
else {

View File

@ -110,6 +110,7 @@ class InvoiceService
public function addGatewayFee(CompanyGateway $company_gateway, $gateway_type_id, float $amount)
{
$this->invoice = (new AddGatewayFee($company_gateway, $gateway_type_id, $this->invoice, $amount))->run();
return $this;

View File

@ -112,10 +112,10 @@ class RefundPayment
if (isset($this->refund_data['invoices'])) {
foreach ($this->refund_data['invoices'] as $invoice) {
$fields->invoice_id = $invoice['invoice_id'];
$activity_repo->save($fields, $this->payment, Ninja::eventVars(auth()->user()->id));
$activity_repo->save($fields, $this->payment, Ninja::eventVars(auth()->user() ? auth()->user()->id : null));
}
} else {
$activity_repo->save($fields, $this->payment, Ninja::eventVars(auth()->user()->id));
$activity_repo->save($fields, $this->payment, Ninja::eventVars(auth()->user() ? auth()->user()->id : null));
}
return $this;

View File

@ -157,6 +157,8 @@ class CompanyTransformer extends EntityTransformer
'oauth_password_required' => (bool)$company->oauth_password_required,
'session_timeout' => (int)$company->session_timeout,
'default_password_timeout' => (int) $company->default_password_timeout,
'invoice_task_datelog' => (bool) $company->invoice_task_datelog,
'show_task_end_date' => (bool) $company->show_task_end_date,
];
}

View File

@ -38,10 +38,12 @@ class CompanyUserTransformer extends EntityTransformer
public function transform(CompanyUser $company_user)
{
$blank_obj = new \stdClass;
return [
'permissions' => $company_user->permissions ?: '',
'notifications' => (object) $company_user->notifications,
'settings' => (object) $company_user->settings,
'notifications' => (object) $company_user->notifications ?: $blank_obj,
'settings' => (object) $company_user->settings ?: $blank_obj,
'is_owner' => (bool) $company_user->is_owner,
'is_admin' => (bool) $company_user->is_admin,
'is_locked' => (bool) $company_user->is_locked,
@ -49,6 +51,7 @@ class CompanyUserTransformer extends EntityTransformer
'archived_at' => (int) $company_user->deleted_at,
'created_at' => (int) $company_user->created_at,
'permissions_updated_at' => (int) $company_user->permissions_updated_at,
//'number_years_active' => (int) $company_user->number_years_active,
];
}

View File

@ -31,7 +31,7 @@ class CreditInvitationTransformer extends EntityTransformer
'updated_at' => (int) $invitation->updated_at,
'archived_at' => (int) $invitation->deleted_at,
'created_at' => (int) $invitation->created_at,
'email_status' => $invitation->email_status,
'email_status' => $invitation->email_status ?: '',
'email_error' => (string)$invitation->email_error,
];
}

View File

@ -11,10 +11,12 @@
namespace App\Transformers;
use App\Models\Activity;
use App\Models\Backup;
use App\Models\Credit;
use App\Models\CreditInvitation;
use App\Models\Document;
use App\Transformers\ActivityTransformer;
use App\Utils\Traits\MakesHash;
class CreditTransformer extends EntityTransformer
@ -31,8 +33,16 @@ class CreditTransformer extends EntityTransformer
'history',
// 'client',
'documents',
'activities',
];
public function includeActivities(Credit $credit)
{
$transformer = new ActivityTransformer($this->serializer);
return $this->includeCollection($credit->activities, $transformer, Activity::class);
}
public function includeHistory(Credit $credit)
{
$transformer = new InvoiceHistoryTransformer($this->serializer);

View File

@ -31,7 +31,7 @@ class InvoiceInvitationTransformer extends EntityTransformer
'updated_at' => (int) $invitation->updated_at,
'archived_at' => (int) $invitation->deleted_at,
'created_at' => (int) $invitation->created_at,
'email_status' => $invitation->email_status,
'email_status' => $invitation->email_status ?: '',
'email_error' => (string)$invitation->email_error,
];
}

View File

@ -11,12 +11,14 @@
namespace App\Transformers;
use App\Models\Activity;
use App\Models\Backup;
use App\Models\Client;
use App\Models\Document;
use App\Models\Invoice;
use App\Models\InvoiceInvitation;
use App\Models\Payment;
use App\Transformers\ActivityTransformer;
use App\Utils\Traits\MakesHash;
class InvoiceTransformer extends EntityTransformer
@ -33,6 +35,7 @@ class InvoiceTransformer extends EntityTransformer
'history',
'payments',
'client',
'activities',
// 'documents',
];
@ -80,6 +83,14 @@ class InvoiceTransformer extends EntityTransformer
return $this->includeCollection($invoice->documents, $transformer, Document::class);
}
public function includeActivities(Invoice $invoice)
{
$transformer = new ActivityTransformer($this->serializer);
return $this->includeCollection($invoice->activities, $transformer, Activity::class);
}
public function transform(Invoice $invoice)
{
return [

View File

@ -31,7 +31,7 @@ class QuoteInvitationTransformer extends EntityTransformer
'updated_at' => (int) $invitation->updated_at,
'archived_at' => (int) $invitation->deleted_at,
'created_at' => (int) $invitation->created_at,
'email_status' => $invitation->email_status,
'email_status' => $invitation->email_status ?: '',
'email_error' => (string)$invitation->email_error,
];
}

View File

@ -11,10 +11,12 @@
namespace App\Transformers;
use App\Models\Activity;
use App\Models\Backup;
use App\Models\Document;
use App\Models\Quote;
use App\Models\QuoteInvitation;
use App\Transformers\ActivityTransformer;
use App\Utils\Traits\MakesHash;
class QuoteTransformer extends EntityTransformer
@ -30,10 +32,18 @@ class QuoteTransformer extends EntityTransformer
'invitations',
'documents',
'history',
'activities',
// 'payments',
// 'client',
];
public function includeActivities(Quote $quote)
{
$transformer = new ActivityTransformer($this->serializer);
return $this->includeCollection($quote->activities, $transformer, Activity::class);
}
public function includeHistory(Quote $quote)
{
$transformer = new InvoiceHistoryTransformer($this->serializer);

View File

@ -31,7 +31,7 @@ class RecurringInvoiceInvitationTransformer extends EntityTransformer
'updated_at' => (int) $invitation->updated_at,
'archived_at' => (int) $invitation->deleted_at,
'created_at' => (int) $invitation->created_at,
'email_status' => $invitation->email_status,
'email_status' => $invitation->email_status ?: '',
'email_error' => (string)$invitation->email_error,
];
}

View File

@ -11,10 +11,12 @@
namespace App\Transformers;
use App\Models\Activity;
use App\Models\Document;
use App\Models\Invoice;
use App\Models\RecurringInvoice;
use App\Models\RecurringInvoiceInvitation;
use App\Transformers\ActivityTransformer;
use App\Utils\Traits\MakesHash;
class RecurringInvoiceTransformer extends EntityTransformer
@ -29,6 +31,7 @@ class RecurringInvoiceTransformer extends EntityTransformer
protected $availableIncludes = [
'invitations',
'documents',
'activities',
// 'payments',
// 'client',
];
@ -62,6 +65,13 @@ class RecurringInvoiceTransformer extends EntityTransformer
return $this->includeItem($invoice->client, $transformer, ENTITY_CLIENT);
}
*/
public function includeActivities(RecurringInvoice $invoice)
{
$transformer = new ActivityTransformer($this->serializer);
return $this->includeCollection($invoice->activities, $transformer, Activity::class);
}
public function includeInvitations(RecurringInvoice $invoice)
{
$transformer = new RecurringInvoiceInvitationTransformer($this->serializer);

View File

@ -238,7 +238,7 @@ class HtmlEngine
$data['$vat_number'] = ['value' => $this->client->vat_number ?: '&nbsp;', 'label' => ctrans('texts.vat_number')];
$data['$website'] = ['value' => $this->client->present()->website() ?: '&nbsp;', 'label' => ctrans('texts.website')];
$data['$phone'] = ['value' => $this->client->present()->phone() ?: '&nbsp;', 'label' => ctrans('texts.phone')];
$data['$country'] = ['value' => isset($this->client->country->name) ? $this->client->country->name : '', 'label' => ctrans('texts.country')];
$data['$country'] = ['value' => isset($this->client->country->name) ? ctrans('texts.country_' . $this->client->country->name) : '', 'label' => ctrans('texts.country')];
$data['$email'] = ['value' => isset($this->contact) ? $this->contact->email : 'no contact email on record', 'label' => ctrans('texts.email')];
$data['$client_name'] = ['value' => $this->entity->present()->clientName() ?: '&nbsp;', 'label' => ctrans('texts.client_name')];
$data['$client.name'] = &$data['$client_name'];
@ -460,7 +460,7 @@ class HtmlEngine
$country = Country::find($this->settings->country_id);
if ($country) {
return $country->name;
return ctrans('texts.country_' . $country->name);
}
return '&nbsp;';

View File

@ -12,6 +12,7 @@
namespace App\Utils;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Str;
/**
@ -69,6 +70,28 @@ class Statics
$data = [];
foreach (config('ninja.cached_tables') as $name => $class) {
// if (!Cache::has($name)) {
// // check that the table exists in case the migration is pending
// if (!Schema::hasTable((new $class())->getTable())) {
// continue;
// }
// if ($name == 'payment_terms') {
// $orderBy = 'num_days';
// } elseif ($name == 'fonts') {
// $orderBy = 'sort_order';
// } elseif (in_array($name, ['currencies', 'industries', 'languages', 'countries', 'banks'])) {
// $orderBy = 'name';
// } else {
// $orderBy = 'id';
// }
// $tableData = $class::orderBy($orderBy)->get();
// if ($tableData->count()) {
// Cache::forever($name, $tableData);
// }
// }
$data[$name] = Cache::get($name);
}

Some files were not shown because too many files have changed in this diff Show More