Fixes for company_user (#3477)

* Minor fixes for user delete

* Working on notifications

* Refactor for notifications

* Fix for checking settings withe negative integers

* Set payment id to 0

* Move pdf download to client side routes

* fixes for company_user

* Fixes for company_user
This commit is contained in:
David Bomba 2020-03-11 22:05:05 +11:00 committed by GitHub
parent ee0a529118
commit dc8ecbeeed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 287 additions and 92 deletions

View File

@ -24,39 +24,39 @@ class CompanySettings extends BaseSettings {
/*Group settings based on functionality*/ /*Group settings based on functionality*/
/*Invoice*/ /*Invoice*/
public $auto_archive_invoice = false; public $auto_archive_invoice = false;
public $lock_sent_invoices = false; public $lock_sent_invoices = false;
public $enable_client_portal_tasks = false; public $enable_client_portal_tasks = false;
public $enable_client_portal_password = false; public $enable_client_portal_password = false;
public $enable_client_portal = true;//implemented public $enable_client_portal = true;//implemented
public $enable_client_portal_dashboard = true;//implemented public $enable_client_portal_dashboard = true;//implemented
public $signature_on_pdf = false; public $signature_on_pdf = false;
public $document_email_attachment = false; public $document_email_attachment = false;
public $send_portal_password = false; public $send_portal_password = false;
public $portal_design_id = '1'; public $portal_design_id = '1';
public $timezone_id = ''; public $timezone_id = '';
public $date_format_id = ''; public $date_format_id = '';
public $military_time = false; public $military_time = false;
public $language_id = ''; public $language_id = '';
public $show_currency_code = false; public $show_currency_code = false;
public $company_gateway_ids = ''; public $company_gateway_ids = '';
public $currency_id = '1'; public $currency_id = '1';
public $custom_value1 = ''; public $custom_value1 = '';
public $custom_value2 = ''; public $custom_value2 = '';
public $custom_value3 = ''; public $custom_value3 = '';
public $custom_value4 = ''; public $custom_value4 = '';
public $default_task_rate = 0; public $default_task_rate = 0;
public $payment_terms = 1; public $payment_terms = 1;
public $send_reminders = false; public $send_reminders = false;
public $custom_message_dashboard = ''; public $custom_message_dashboard = '';
public $custom_message_unpaid_invoice = ''; public $custom_message_unpaid_invoice = '';
@ -116,10 +116,10 @@ class CompanySettings extends BaseSettings {
public $enabled_item_tax_rates = 0; public $enabled_item_tax_rates = 0;
public $invoice_design_id = 'VolejRejNm'; public $invoice_design_id = 'VolejRejNm';
public $quote_design_id = 'VolejRejNm'; public $quote_design_id = 'VolejRejNm';
public $credit_design_id = 'VolejRejNm'; public $credit_design_id = 'VolejRejNm';
public $invoice_footer = ''; public $invoice_footer = '';
public $credit_footer = ''; public $credit_footer = '';
public $credit_terms = ''; public $credit_terms = '';
public $invoice_labels = ''; public $invoice_labels = '';
public $tax_name1 = ''; public $tax_name1 = '';
public $tax_rate1 = 0; public $tax_rate1 = 0;
@ -127,7 +127,7 @@ class CompanySettings extends BaseSettings {
public $tax_rate2 = 0; public $tax_rate2 = 0;
public $tax_name3 = ''; public $tax_name3 = '';
public $tax_rate3 = 0; public $tax_rate3 = 0;
public $payment_type_id = '1'; public $payment_type_id = '0';
public $invoice_fields = ''; public $invoice_fields = '';
public $show_accept_invoice_terms = false; public $show_accept_invoice_terms = false;

View File

@ -146,12 +146,14 @@ class AccountController extends BaseController
public function store(CreateAccountRequest $request) public function store(CreateAccountRequest $request)
{ {
$account = CreateAccount::dispatchNow($request->all()); $account = CreateAccount::dispatchNow($request->all());
if(!($account instanceof Account)) if(!($account instanceof Account))
return $account; return $account;
$ct = CompanyUser::whereUserId(auth()->user()->id); $ct = CompanyUser::whereUserId(auth()->user()->id);
config(['ninja.company_id' => $ct->first()->company->id]);
return $this->listResponse($ct); return $this->listResponse($ct);
} }

View File

@ -62,7 +62,7 @@ class InvitationController extends Controller
public function routerForDownload(string $entity, string $invitation_key) public function routerForDownload(string $entity, string $invitation_key)
{ {
return redirect('/'.$entity.'/'.$invitation_key.'/download_pdf'); return redirect('client/'.$entity.'/'.$invitation_key.'/download_pdf');
} }
public function routerForIframe(string $entity, string $client_hash, string $invitation_key) public function routerForIframe(string $entity, string $client_hash, string $invitation_key)

View File

@ -36,7 +36,7 @@ class SetInviteDb
$entity = null; $entity = null;
if(!$request->route('entity')) if(!$request->route('entity'))
$entity = $request->segment(1); $entity = $request->segment(2);
else else
$entity = $request->route('entity'); $entity = $request->route('entity');

View File

@ -29,6 +29,8 @@ class VersionCheck implements ShouldQueue
$version_file = file_get_contents(config('ninja.version_url')); $version_file = file_get_contents(config('ninja.version_url'));
\Log::error($version_file);
if($version_file) if($version_file)
Account::whereNotNull('id')->update(['latest_version' => $version_file]); Account::whereNotNull('id')->update(['latest_version' => $version_file]);

View File

@ -14,9 +14,11 @@ namespace App\Listeners\Invoice;
use App\Models\Activity; use App\Models\Activity;
use App\Models\ClientContact; use App\Models\ClientContact;
use App\Models\InvoiceInvitation; use App\Models\InvoiceInvitation;
use App\Notifications\Admin\EntitySentNotification;
use App\Notifications\Admin\InvoiceSentNotification; use App\Notifications\Admin\InvoiceSentNotification;
use App\Repositories\ActivityRepository; use App\Repositories\ActivityRepository;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use App\Utils\Traits\Notifications\UserNotifies;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
@ -25,6 +27,8 @@ use Illuminate\Support\Facades\Notification;
class InvoiceEmailedNotification implements ShouldQueue class InvoiceEmailedNotification implements ShouldQueue
{ {
use UserNotifies;
public function __construct() public function __construct()
{ {
} }
@ -41,16 +45,23 @@ class InvoiceEmailedNotification implements ShouldQueue
foreach($invitation->company->company_users as $company_user) foreach($invitation->company->company_users as $company_user)
{ {
$user = $company_user->user;
$company_user->user->notify(new InvoiceSentNotification($invitation, $invitation->company)); $notification = new EntitySentNotification($invitation, 'invoice');
$notification->method = $this->findUserNotificationTypes($invitation, $company_user, 'invoice', ['all_notifications', 'invoice_sent']);
$user->notify($notification);
} }
if(isset($invitation->company->slack_webhook_url)){ // if(isset($invitation->company->slack_webhook_url)){
Notification::route('slack', $invitation->company->slack_webhook_url) // Notification::route('slack', $invitation->company->slack_webhook_url)
->notify(new InvoiceSentNotification($invitation, $invitation->company, true)); // ->notify(new EntitySentNotification($invitation, $invitation->company, true));
} // }
} }
} }

View File

@ -12,12 +12,15 @@
namespace App\Listeners\Misc; namespace App\Listeners\Misc;
use App\Notifications\Admin\EntityViewedNotification; use App\Notifications\Admin\EntityViewedNotification;
use App\Utils\Traits\Notifications\UserNotifies;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Support\Facades\Notification; use Illuminate\Support\Facades\Notification;
class InvitationViewedListener implements ShouldQueue class InvitationViewedListener implements ShouldQueue
{ {
use UserNotifies;
/** /**
* Create the event listener. * Create the event listener.
* *
@ -40,36 +43,10 @@ class InvitationViewedListener implements ShouldQueue
foreach($invitation->company->company_users as $company_user) foreach($invitation->company->company_users as $company_user)
{ {
$notifiable_methods = [];
$notifications = $company_user->notifications;
$entity_viewed = "{$entity_name}_viewed"; $entity_viewed = "{$entity_name}_viewed";
/*** Check for Mail notifications***/ $notification->method = $this->findUserNotificationTypes($invitation, $company_user, $entity_name, ['all_notifications', $entity_viewed]);
$all_user_notifications = '';
if($invitation->{$entity_name}->user_id == $company_user->user_id || $invitation->{$entity_name}->assigned_user_id == $company_user->user_id)
$all_user_notifications = "all_user_notifications";
$possible_permissions = [$entity_viewed, "all_notifications", $all_user_notifications];
$permission_count = array_intersect($possible_permissions, $notifications->email);
if(count($permission_count) >=1)
array_push($notifiable_methods, 'mail');
/*** Check for Mail notifications***/
/*** Check for Slack notifications***/
//@TODO when hillel implements this we can uncomment this.
// $permission_count = array_intersect($possible_permissions, $notifications->slack);
// if(count($permission_count) >=1)
// array_push($notifiable_methods, 'slack');
/*** Check for Slack notifications***/
$notification->method = $notifiable_methods;
$company_user->user->notify($notification); $company_user->user->notify($notification);
} }

View File

@ -107,4 +107,11 @@ class CompanyUser extends Pivot
return $this->hasMany(CompanyToken::class, 'user_id', 'user_id'); return $this->hasMany(CompanyToken::class, 'user_id', 'user_id');
} }
public function scopeAuthCompany($query)
{
$query->where('company_id', auth()->user()->companyId());
return $query;
}
} }

View File

@ -187,7 +187,10 @@ class User extends Authenticatable implements MustVerifyEmail
$this->id = auth()->user()->id; $this->id = auth()->user()->id;
} }
return $this->hasOneThrough(CompanyUser::class, CompanyToken::class, 'user_id', 'company_id', 'id', 'company_id')->where('company_user.user_id', $this->id)->withTrashed(); return $this->hasOneThrough(CompanyUser::class, CompanyToken::class, 'user_id', 'company_id', 'id', 'company_id')
->where('company_user.user_id', $this->id)
->where('company_user.company_id', auth()->user()->company()->id)
->withTrashed();
} }
/** /**

View File

@ -0,0 +1,151 @@
<?php
namespace App\Notifications\Admin;
use App\Utils\Number;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class EntitySentNotification extends Notification implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* Create a new notification instance.
*
* @return void
*/
protected $invitation;
protected $entity;
protected $entity_name;
protected $settings;
public $is_system;
public $method;
protected $contact;
protected $company;
public function __construct($invitation, $entity_name, $is_system = false, $settings = null)
{
$this->invitation = $invitation;
$this->entity_name = $entity_name;
$this->entity = $invitation->{$entity_name};
$this->contact = $invitation->contact;
$this->company = $invitation->company;
$this->settings = $this->entity->client->getMergedSettings();
$this->is_system = $is_system;
$this->method = null;
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
return $this->method ?: [];
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
$amount = Number::formatMoney($this->entity->amount, $this->entity->client);
$subject = ctrans("texts.notification_{$this->entity_name}_sent_subject",
[
'client' => $this->contact->present()->name(),
'invoice' => $this->entity->number,
]);
$data = [
'title' => $subject,
'message' => ctrans("texts.notification_{$this->entity_name}_sent",
[
'amount' => $amount,
'client' => $this->contact->present()->name(),
'invoice' => $this->entity->number,
]),
'url' => $this->invitation->getAdminLink(),
'button' => ctrans("texts.view_{$this->entity_name}"),
'signature' => $this->settings->email_signature,
'logo' => $this->company->present()->logo(),
];
return (new MailMessage)
->subject($subject)
->markdown('email.admin.generic', $data);
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
//
];
}
public function toSlack($notifiable)
{
$logo = $this->invitation->company->present()->logo();
$amount = Number::formatMoney($this->entity->amount, $this->entity->client);
// return (new SlackMessage)
// ->success()
// ->from(ctrans('texts.notification_bot'))
// ->image($logo)
// ->content(ctrans('texts.notification_invoice_sent',
// [
// 'amount' => $amount,
// 'client' => $this->contact->present()->name(),
// 'invoice' => $this->invoice->number
// ]));
return (new SlackMessage)
->from(ctrans('texts.notification_bot'))
->success()
->image('https://app.invoiceninja.com/favicon-v2.png')
->content(trans("texts.notification_{$this->entity_name}_sent_subject",
[
'amount' => $amount,
'client' => $this->contact->present()->name(),
'invoice' => $this->entity->number
]))
->attachment(function ($attachment) use($amount){
$attachment->title(ctrans('texts.invoice_number_placeholder', ['invoice' => $this->entity->number]), $this->invitation->getAdminLink())
->fields([
ctrans('texts.client') => $this->contact->present()->name(),
ctrans('texts.amount') => $amount,
]);
});
}
}

View File

@ -97,17 +97,6 @@ class EntityViewedNotification extends Notification implements ShouldQueue
$logo = $this->company->present()->logo(); $logo = $this->company->present()->logo();
$amount = Number::formatMoney($this->entity->amount, $this->entity->client); $amount = Number::formatMoney($this->entity->amount, $this->entity->client);
// return (new SlackMessage)
// ->success()
// ->from(ctrans('texts.notification_bot'))
// ->image($logo)
// ->content(ctrans("texts.notification_{$this->entity_name}_viewed",
// [
// 'amount' => $amount,
// 'client' => $this->contact->present()->name(),
// $this->entity_name => $this->entity->number
// ]));
return (new SlackMessage) return (new SlackMessage)
->from(ctrans('texts.notification_bot')) ->from(ctrans('texts.notification_bot'))
->success() ->success()
@ -142,7 +131,7 @@ class EntityViewedNotification extends Notification implements ShouldQueue
'client' => $this->contact->present()->name(), 'client' => $this->contact->present()->name(),
$this->entity_name => $this->entity->number, $this->entity_name => $this->entity->number,
]), ]),
'url' => config('ninja.site_url') . "/client/{$this->entity_name}/" . $this->invitation->key . "?silent=true", 'url' => $this->invitation->getAdminLink(),
'button' => ctrans("texts.view_{$this->entity_name}"), 'button' => ctrans("texts.view_{$this->entity_name}"),
'signature' => $this->settings->email_signature, 'signature' => $this->settings->email_signature,
'logo' => $this->company->present()->logo(), 'logo' => $this->company->present()->logo(),

View File

@ -41,7 +41,7 @@ class UserPolicy extends EntityPolicy
*/ */
public function edit(User $user, $user_entity) : bool public function edit(User $user, $user_entity) : bool
{ {
$company_user = CompanyUser::whereUserId($user->id)->company()->first(); $company_user = CompanyUser::whereUserId($user->id)->AuthCompany()->first();
return ($user->isAdmin() && $company_user); return ($user->isAdmin() && $company_user);
} }

View File

@ -91,12 +91,14 @@ class UserRepository extends BaseRepository
->whereCompanyId($company->id) ->whereCompanyId($company->id)
->first(); ->first();
$cu->tokens()->delete(); $cu->tokens()->forceDelete();
$cu->delete(); $cu->forceDelete();
} }
else
$user->delete(); $user->delete();
return $user->fresh(); return $user->fresh();
} }
} }

View File

@ -99,6 +99,8 @@ class UserTransformer extends EntityTransformer
{ {
$transformer = new CompanyUserTransformer($this->serializer); $transformer = new CompanyUserTransformer($this->serializer);
return $this->includeItem($user->company_user, $transformer, CompanyUser::class); $cu = $user->company_users()->whereCompanyId(config('ninja.company_id'))->first();
return $this->includeItem($cu, $transformer, CompanyUser::class);
} }
} }

View File

@ -197,7 +197,7 @@ trait ClientGroupSettingsSaver
switch ($key) { switch ($key) {
case 'int': case 'int':
case 'integer': case 'integer':
return ctype_digit(strval($value)); return ctype_digit(strval(abs($value)));
case 'real': case 'real':
case 'float': case 'float':
case 'double': case 'double':

View File

@ -214,7 +214,7 @@ trait CompanySettingsSaver
switch ($key) { switch ($key) {
case 'int': case 'int':
case 'integer': case 'integer':
return ctype_digit(strval($value)); return ctype_digit(strval(abs($value)));
case 'real': case 'real':
case 'float': case 'float':
case 'double': case 'double':

View File

@ -0,0 +1,40 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com)
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Utils\Traits\Notifications;
/**
* Class UserNotifies
* @package App\Utils\Traits
*/
trait UserNotifies
{
public function findUserNotificationTypes($invitation, $company_user, $entity_name, $required_permissions) :array
{
$notifiable_methods = [];
$notifications = $company_user->notifications;
if($invitation->{$entity_name}->user_id == $company_user->_user_id || $invitation->{$entity_name}->assigned_user_id == $company_user->user_id)
array_push($required_permissions, "all_user_notifications");
if(count(array_intersect($required_permissions, $notifications->email)) >=1)
array_push($notifiable_methods, 'mail');
// if(count(array_intersect($required_permissions, $notifications->slack)) >=1)
// array_push($notifiable_methods, 'slack');
return $notifiable_methods;
}
}

View File

@ -190,7 +190,7 @@ trait SettingsSaver
switch ($key) { switch ($key) {
case 'int': case 'int':
case 'integer': case 'integer':
return ctype_digit(strval($value)); return ctype_digit(strval(abs($value)));
case 'real': case 'real':
case 'float': case 'float':
case 'double': case 'double':

View File

@ -250,11 +250,15 @@ $LANG = array(
'invoice_link_message' => 'To view the invoice click the link below:', 'invoice_link_message' => 'To view the invoice click the link below:',
'notification_invoice_paid_subject' => 'Invoice :invoice was paid by :client', 'notification_invoice_paid_subject' => 'Invoice :invoice was paid by :client',
'notification_invoice_sent_subject' => 'Invoice :invoice was sent to :client', 'notification_invoice_sent_subject' => 'Invoice :invoice was sent to :client',
'notification_quote_sent_subject' => 'Quote :invoice was sent to :client',
'notification_credit_sent_subject' => 'Credit :invoice was sent to :client',
'notification_invoice_viewed_subject' => 'Invoice :invoice was viewed by :client', 'notification_invoice_viewed_subject' => 'Invoice :invoice was viewed by :client',
'notification_credit_viewed_subject' => 'Credit :credit was viewed by :client', 'notification_credit_viewed_subject' => 'Credit :credit was viewed by :client',
'notification_quote_viewed_subject' => 'Quote :quote was viewed by :client', 'notification_quote_viewed_subject' => 'Quote :quote was viewed by :client',
'notification_invoice_paid' => 'A payment of :amount was made by client :client towards Invoice :invoice.', 'notification_invoice_paid' => 'A payment of :amount was made by client :client towards Invoice :invoice.',
'notification_invoice_sent' => 'The following client :client was emailed Invoice :invoice for :amount.', 'notification_invoice_sent' => 'The following client :client was emailed Invoice :invoice for :amount.',
'notification_quote_sent' => 'The following client :client was emailed Quote :invoice for :amount.',
'notification_credit_sent' => 'The following client :client was emailed Credit :invoice for :amount.',
'notification_invoice_viewed' => 'The following client :client viewed Invoice :invoice for :amount.', 'notification_invoice_viewed' => 'The following client :client viewed Invoice :invoice for :amount.',
'notification_credit_viewed' => 'The following client :client viewed Credit :credit for :amount.', 'notification_credit_viewed' => 'The following client :client viewed Credit :credit for :amount.',
'notification_quote_viewed' => 'The following client :client viewed Quote :quote for :amount.', 'notification_quote_viewed' => 'The following client :client viewed Quote :quote for :amount.',

View File

@ -46,11 +46,19 @@ Route::group(['middleware' => ['auth:contact','locale'], 'prefix' => 'client', '
Route::group(['middleware' => ['invite_db'], 'prefix' => 'client', 'as' => 'client.'], function () { Route::group(['middleware' => ['invite_db'], 'prefix' => 'client', 'as' => 'client.'], function () {
Route::get('invoice/{invitation_key}/download_pdf', 'InvoiceController@downloadPdf')->name('invoice.download_pdf');
Route::get('quote/{invitation_key}/download_pdf', 'QuoteController@downloadPdf')->name('quote.download_pdf');
Route::get('credit/{invitation_key}/download_pdf', 'CreditController@downloadPdf')->name('credit.download_pdf');
Route::get('{entity}/{invitation_key}/download', 'ClientPortal\InvitationController@routerForDownload');
/*Invitation catches*/ /*Invitation catches*/
Route::get('{entity}/{invitation_key}','ClientPortal\InvitationController@router'); Route::get('{entity}/{invitation_key}','ClientPortal\InvitationController@router');
Route::get('{entity}/{client_hash}/{invitation_key}','ClientPortal\InvitationController@routerForIframe'); //should never need this Route::get('{entity}/{client_hash}/{invitation_key}','ClientPortal\InvitationController@routerForIframe'); //should never need this
Route::get('payment_hook/{company_gateway_id}/{gateway_type_id}','ClientPortal\PaymentHookController@process'); Route::get('payment_hook/{company_gateway_id}/{gateway_type_id}','ClientPortal\PaymentHookController@process');
}); });
Route::fallback('BaseController@notFoundClient'); Route::fallback('BaseController@notFoundClient');

View File

@ -18,10 +18,7 @@ Route::group(['middleware' => ['invite_db'], 'prefix' => '', 'as' => ''], functi
/*Invitation catches*/ /*Invitation catches*/
Route::get('{entity}/{invitation_key}/download', 'ClientPortal\InvitationController@routerForDownload');
Route::get('invoice/{invitation_key}/download_pdf', 'InvoiceController@downloadPdf')->name('invoice.download_pdf');
Route::get('quote/{invitation_key}/download_pdf', 'QuoteController@downloadPdf')->name('quote.download_pdf');
Route::get('credit/{invitation_key}/download_pdf', 'CreditController@downloadPdf')->name('credit.download_pdf');
}); });

View File

@ -41,7 +41,7 @@ class CompanySettingsTest extends TestCase
Session::start(); Session::start();
$this->faker = \Faker\Factory::create(); $this->faker = \Faker\Factory::create();
$this->withoutExceptionHandling();
Model::reguard(); Model::reguard();
@ -68,7 +68,7 @@ class CompanySettingsTest extends TestCase
catch(ValidationException $e) { catch(ValidationException $e) {
$message = json_decode($e->validator->getMessageBag(),1); $message = json_decode($e->validator->getMessageBag(),1);
// \Log::error($message); \Log::error($message);
} }
if($response) { if($response) {