diff --git a/app/DataMapper/CompanySettings.php b/app/DataMapper/CompanySettings.php index 5323644ffd56..ba8b48467be8 100644 --- a/app/DataMapper/CompanySettings.php +++ b/app/DataMapper/CompanySettings.php @@ -24,39 +24,39 @@ class CompanySettings extends BaseSettings { /*Group settings based on functionality*/ /*Invoice*/ - public $auto_archive_invoice = false; - public $lock_sent_invoices = false; + public $auto_archive_invoice = false; + public $lock_sent_invoices = false; - public $enable_client_portal_tasks = false; - public $enable_client_portal_password = false; - public $enable_client_portal = true;//implemented - public $enable_client_portal_dashboard = true;//implemented - public $signature_on_pdf = false; - public $document_email_attachment = false; - public $send_portal_password = false; + public $enable_client_portal_tasks = false; + public $enable_client_portal_password = false; + public $enable_client_portal = true;//implemented + public $enable_client_portal_dashboard = true;//implemented + public $signature_on_pdf = false; + public $document_email_attachment = false; + public $send_portal_password = false; - public $portal_design_id = '1'; + public $portal_design_id = '1'; - public $timezone_id = ''; - public $date_format_id = ''; - public $military_time = false; + public $timezone_id = ''; + public $date_format_id = ''; + public $military_time = false; - public $language_id = ''; - public $show_currency_code = false; + public $language_id = ''; + 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_value2 = ''; - public $custom_value3 = ''; - public $custom_value4 = ''; + public $custom_value1 = ''; + public $custom_value2 = ''; + public $custom_value3 = ''; + public $custom_value4 = ''; - public $default_task_rate = 0; + public $default_task_rate = 0; - public $payment_terms = 1; - public $send_reminders = false; + public $payment_terms = 1; + public $send_reminders = false; public $custom_message_dashboard = ''; public $custom_message_unpaid_invoice = ''; @@ -116,10 +116,10 @@ class CompanySettings extends BaseSettings { public $enabled_item_tax_rates = 0; public $invoice_design_id = 'VolejRejNm'; public $quote_design_id = 'VolejRejNm'; - public $credit_design_id = 'VolejRejNm'; + public $credit_design_id = 'VolejRejNm'; public $invoice_footer = ''; - public $credit_footer = ''; - public $credit_terms = ''; + public $credit_footer = ''; + public $credit_terms = ''; public $invoice_labels = ''; public $tax_name1 = ''; public $tax_rate1 = 0; @@ -127,7 +127,7 @@ class CompanySettings extends BaseSettings { public $tax_rate2 = 0; public $tax_name3 = ''; public $tax_rate3 = 0; - public $payment_type_id = '1'; + public $payment_type_id = '0'; public $invoice_fields = ''; public $show_accept_invoice_terms = false; diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index e53ef3543914..d17c04c748ad 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -146,12 +146,14 @@ class AccountController extends BaseController public function store(CreateAccountRequest $request) { $account = CreateAccount::dispatchNow($request->all()); - + if(!($account instanceof Account)) return $account; $ct = CompanyUser::whereUserId(auth()->user()->id); - + + config(['ninja.company_id' => $ct->first()->company->id]); + return $this->listResponse($ct); } diff --git a/app/Http/Controllers/ClientPortal/InvitationController.php b/app/Http/Controllers/ClientPortal/InvitationController.php index e5e5f9d21e62..5e3e2ed2c965 100644 --- a/app/Http/Controllers/ClientPortal/InvitationController.php +++ b/app/Http/Controllers/ClientPortal/InvitationController.php @@ -62,7 +62,7 @@ class InvitationController extends Controller 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) diff --git a/app/Http/Middleware/SetInviteDb.php b/app/Http/Middleware/SetInviteDb.php index 43ff2511880b..dec0b19913b1 100644 --- a/app/Http/Middleware/SetInviteDb.php +++ b/app/Http/Middleware/SetInviteDb.php @@ -36,7 +36,7 @@ class SetInviteDb $entity = null; if(!$request->route('entity')) - $entity = $request->segment(1); + $entity = $request->segment(2); else $entity = $request->route('entity'); diff --git a/app/Jobs/Util/VersionCheck.php b/app/Jobs/Util/VersionCheck.php index 7838905b5615..c123f569d7da 100644 --- a/app/Jobs/Util/VersionCheck.php +++ b/app/Jobs/Util/VersionCheck.php @@ -29,6 +29,8 @@ class VersionCheck implements ShouldQueue $version_file = file_get_contents(config('ninja.version_url')); +\Log::error($version_file); + if($version_file) Account::whereNotNull('id')->update(['latest_version' => $version_file]); diff --git a/app/Listeners/Invoice/InvoiceEmailedNotification.php b/app/Listeners/Invoice/InvoiceEmailedNotification.php index 17b2aede2b34..d759ebbaa0cb 100644 --- a/app/Listeners/Invoice/InvoiceEmailedNotification.php +++ b/app/Listeners/Invoice/InvoiceEmailedNotification.php @@ -14,9 +14,11 @@ namespace App\Listeners\Invoice; use App\Models\Activity; use App\Models\ClientContact; use App\Models\InvoiceInvitation; +use App\Notifications\Admin\EntitySentNotification; use App\Notifications\Admin\InvoiceSentNotification; use App\Repositories\ActivityRepository; use App\Utils\Traits\MakesHash; +use App\Utils\Traits\Notifications\UserNotifies; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Support\Facades\Log; @@ -25,6 +27,8 @@ use Illuminate\Support\Facades\Notification; class InvoiceEmailedNotification implements ShouldQueue { + use UserNotifies; + public function __construct() { } @@ -41,16 +45,23 @@ class InvoiceEmailedNotification implements ShouldQueue 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) - ->notify(new InvoiceSentNotification($invitation, $invitation->company, true)); + // Notification::route('slack', $invitation->company->slack_webhook_url) + // ->notify(new EntitySentNotification($invitation, $invitation->company, true)); - } + // } } + + } diff --git a/app/Listeners/Misc/InvitationViewedListener.php b/app/Listeners/Misc/InvitationViewedListener.php index 862652011573..347bd01c7613 100644 --- a/app/Listeners/Misc/InvitationViewedListener.php +++ b/app/Listeners/Misc/InvitationViewedListener.php @@ -12,12 +12,15 @@ namespace App\Listeners\Misc; use App\Notifications\Admin\EntityViewedNotification; +use App\Utils\Traits\Notifications\UserNotifies; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Support\Facades\Notification; class InvitationViewedListener implements ShouldQueue { + use UserNotifies; + /** * Create the event listener. * @@ -40,36 +43,10 @@ class InvitationViewedListener implements ShouldQueue foreach($invitation->company->company_users as $company_user) { - $notifiable_methods = []; - - $notifications = $company_user->notifications; $entity_viewed = "{$entity_name}_viewed"; - /*** Check for Mail notifications***/ - $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; + $notification->method = $this->findUserNotificationTypes($invitation, $company_user, $entity_name, ['all_notifications', $entity_viewed]); $company_user->user->notify($notification); } diff --git a/app/Models/CompanyUser.php b/app/Models/CompanyUser.php index 79e2466c2a12..1437604ab1bf 100644 --- a/app/Models/CompanyUser.php +++ b/app/Models/CompanyUser.php @@ -107,4 +107,11 @@ class CompanyUser extends Pivot return $this->hasMany(CompanyToken::class, 'user_id', 'user_id'); } + + public function scopeAuthCompany($query) + { + $query->where('company_id', auth()->user()->companyId()); + + return $query; + } } diff --git a/app/Models/User.php b/app/Models/User.php index 3f33928b6429..904101e2baa2 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -187,7 +187,10 @@ class User extends Authenticatable implements MustVerifyEmail $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(); } /** diff --git a/app/Notifications/Admin/EntitySentNotification.php b/app/Notifications/Admin/EntitySentNotification.php new file mode 100644 index 000000000000..b6b49993980c --- /dev/null +++ b/app/Notifications/Admin/EntitySentNotification.php @@ -0,0 +1,151 @@ +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, + ]); + }); + } + +} diff --git a/app/Notifications/Admin/EntityViewedNotification.php b/app/Notifications/Admin/EntityViewedNotification.php index e342c39edf53..85eaca0b097a 100644 --- a/app/Notifications/Admin/EntityViewedNotification.php +++ b/app/Notifications/Admin/EntityViewedNotification.php @@ -97,17 +97,6 @@ class EntityViewedNotification extends Notification implements ShouldQueue $logo = $this->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_{$this->entity_name}_viewed", - // [ - // 'amount' => $amount, - // 'client' => $this->contact->present()->name(), - // $this->entity_name => $this->entity->number - // ])); - return (new SlackMessage) ->from(ctrans('texts.notification_bot')) ->success() @@ -142,7 +131,7 @@ class EntityViewedNotification extends Notification implements ShouldQueue 'client' => $this->contact->present()->name(), $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}"), 'signature' => $this->settings->email_signature, 'logo' => $this->company->present()->logo(), diff --git a/app/Policies/UserPolicy.php b/app/Policies/UserPolicy.php index 9b7323560042..3f8950fe074d 100644 --- a/app/Policies/UserPolicy.php +++ b/app/Policies/UserPolicy.php @@ -41,7 +41,7 @@ class UserPolicy extends EntityPolicy */ 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); } diff --git a/app/Repositories/UserRepository.php b/app/Repositories/UserRepository.php index 1f84ea331c7a..2378add527fa 100644 --- a/app/Repositories/UserRepository.php +++ b/app/Repositories/UserRepository.php @@ -91,12 +91,14 @@ class UserRepository extends BaseRepository ->whereCompanyId($company->id) ->first(); - $cu->tokens()->delete(); - $cu->delete(); + $cu->tokens()->forceDelete(); + $cu->forceDelete(); } - else - $user->delete(); + + $user->delete(); return $user->fresh(); + } + } diff --git a/app/Transformers/UserTransformer.php b/app/Transformers/UserTransformer.php index a1c3f2ccf67d..7c3e017914df 100644 --- a/app/Transformers/UserTransformer.php +++ b/app/Transformers/UserTransformer.php @@ -99,6 +99,8 @@ class UserTransformer extends EntityTransformer { $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); } } diff --git a/app/Utils/Traits/ClientGroupSettingsSaver.php b/app/Utils/Traits/ClientGroupSettingsSaver.php index 6188ad834a40..2107d70bb29c 100644 --- a/app/Utils/Traits/ClientGroupSettingsSaver.php +++ b/app/Utils/Traits/ClientGroupSettingsSaver.php @@ -197,7 +197,7 @@ trait ClientGroupSettingsSaver switch ($key) { case 'int': case 'integer': - return ctype_digit(strval($value)); + return ctype_digit(strval(abs($value))); case 'real': case 'float': case 'double': diff --git a/app/Utils/Traits/CompanySettingsSaver.php b/app/Utils/Traits/CompanySettingsSaver.php index e7e806129ba8..3a0489e47c02 100644 --- a/app/Utils/Traits/CompanySettingsSaver.php +++ b/app/Utils/Traits/CompanySettingsSaver.php @@ -214,7 +214,7 @@ trait CompanySettingsSaver switch ($key) { case 'int': case 'integer': - return ctype_digit(strval($value)); + return ctype_digit(strval(abs($value))); case 'real': case 'float': case 'double': diff --git a/app/Utils/Traits/Notifications/UserNotifies.php b/app/Utils/Traits/Notifications/UserNotifies.php new file mode 100644 index 000000000000..6b41ce2220f7 --- /dev/null +++ b/app/Utils/Traits/Notifications/UserNotifies.php @@ -0,0 +1,40 @@ +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; + + } + +} \ No newline at end of file diff --git a/app/Utils/Traits/SettingsSaver.php b/app/Utils/Traits/SettingsSaver.php index 2ea4e97e82d9..5cd31e09dfa2 100644 --- a/app/Utils/Traits/SettingsSaver.php +++ b/app/Utils/Traits/SettingsSaver.php @@ -190,7 +190,7 @@ trait SettingsSaver switch ($key) { case 'int': case 'integer': - return ctype_digit(strval($value)); + return ctype_digit(strval(abs($value))); case 'real': case 'float': case 'double': diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index baa861a8453d..c2ac516bd6d1 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -250,11 +250,15 @@ $LANG = array( 'invoice_link_message' => 'To view the invoice click the link below:', 'notification_invoice_paid_subject' => 'Invoice :invoice was paid by :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_credit_viewed_subject' => 'Credit :credit 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_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_credit_viewed' => 'The following client :client viewed Credit :credit for :amount.', 'notification_quote_viewed' => 'The following client :client viewed Quote :quote for :amount.', diff --git a/routes/client.php b/routes/client.php index 3d859e537a31..02fc47f29199 100644 --- a/routes/client.php +++ b/routes/client.php @@ -46,11 +46,19 @@ Route::group(['middleware' => ['auth:contact','locale'], 'prefix' => 'client', ' 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*/ Route::get('{entity}/{invitation_key}','ClientPortal\InvitationController@router'); 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::fallback('BaseController@notFoundClient'); \ No newline at end of file diff --git a/routes/web.php b/routes/web.php index 8850bde4cac1..7ee56d50f85d 100644 --- a/routes/web.php +++ b/routes/web.php @@ -18,10 +18,7 @@ Route::group(['middleware' => ['invite_db'], 'prefix' => '', 'as' => ''], functi /*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'); + }); diff --git a/tests/Feature/CompanySettingsTest.php b/tests/Feature/CompanySettingsTest.php index 1d7d49534093..28e0e05586c6 100644 --- a/tests/Feature/CompanySettingsTest.php +++ b/tests/Feature/CompanySettingsTest.php @@ -41,7 +41,7 @@ class CompanySettingsTest extends TestCase Session::start(); $this->faker = \Faker\Factory::create(); - +$this->withoutExceptionHandling(); Model::reguard(); @@ -68,7 +68,7 @@ class CompanySettingsTest extends TestCase catch(ValidationException $e) { $message = json_decode($e->validator->getMessageBag(),1); - // \Log::error($message); + \Log::error($message); } if($response) {