diff --git a/app/Events/Credit/CreditWasRestored.php b/app/Events/Credit/CreditWasRestored.php new file mode 100644 index 000000000000..4884c3749dc7 --- /dev/null +++ b/app/Events/Credit/CreditWasRestored.php @@ -0,0 +1,45 @@ +credit = $credit; + $this->company = $company; + $this->event_vars = $event_vars; + + } +} diff --git a/app/Events/Quote/QuoteWasApproved.php b/app/Events/Quote/QuoteWasApproved.php index ee8ba01cec2a..bea13b86a602 100644 --- a/app/Events/Quote/QuoteWasApproved.php +++ b/app/Events/Quote/QuoteWasApproved.php @@ -25,6 +25,8 @@ class QuoteWasApproved { use Dispatchable, InteractsWithSockets, SerializesModels; + public $contact; + public $quote; public $company; @@ -35,8 +37,9 @@ class QuoteWasApproved * * @return void */ - public function __construct(Quote $quote, Company $company, array $event_vars) + public function __construct(ClientContact $contact, Quote $quote, Company $company, array $event_vars) { + $this->contact = $contact; $this->quote = $quote; $this->company = $company; $this->event_vars = $event_vars; diff --git a/app/Http/Controllers/ClientPortal/QuoteController.php b/app/Http/Controllers/ClientPortal/QuoteController.php index 2358eac02b32..511fb41b35de 100644 --- a/app/Http/Controllers/ClientPortal/QuoteController.php +++ b/app/Http/Controllers/ClientPortal/QuoteController.php @@ -109,7 +109,7 @@ class QuoteController extends Controller if ($process) { foreach ($quotes as $quote) { $quote->service()->approve()->save(); - event(new QuoteWasApproved($quote, $quote->company, Ninja::eventVars())); + event(new QuoteWasApproved(auth()->user(), $quote, $quote->company, Ninja::eventVars())); } return redirect() diff --git a/app/Http/Controllers/CompanyUserController.php b/app/Http/Controllers/CompanyUserController.php index 572e6bb541b8..fa6d5ccfb9d2 100644 --- a/app/Http/Controllers/CompanyUserController.php +++ b/app/Http/Controllers/CompanyUserController.php @@ -27,8 +27,6 @@ class CompanyUserController extends BaseController public function __construct() { parent::__construct(); - - //$this->middleware('guest'); } /** @@ -38,7 +36,6 @@ class CompanyUserController extends BaseController */ public function index() { - // return view('signup.index'); } /** @@ -48,11 +45,10 @@ class CompanyUserController extends BaseController */ public function create() { - // } - public function store(CreateAccountRequest $request) + public function store() { } @@ -125,8 +121,8 @@ class CompanyUserController extends BaseController */ public function update(UpdateCompanyUserRequest $request, User $user) { - $company = auth()->user()->company(); + $company = auth()->user()->company(); $company_user = CompanyUser::whereUserId($user->id)->whereCompanyId($company->id)->first(); @@ -145,6 +141,7 @@ class CompanyUserController extends BaseController $company_user->save(); return $this->itemResponse($company_user->fresh()); + } /** diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index f48df4bc5da3..ea6495b06775 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -13,6 +13,8 @@ namespace App\Http\Controllers; use App\DataMapper\CompanySettings; use App\DataMapper\DefaultSettings; +use App\Events\User\UserEmailAddressChangedNewEmail; +use App\Events\User\UserEmailAddressChangedOldEmail; use App\Factory\UserFactory; use App\Filters\UserFilters; use App\Http\Controllers\Traits\VerifiesUserEmail; @@ -25,11 +27,13 @@ use App\Http\Requests\User\ShowUserRequest; use App\Http\Requests\User\StoreUserRequest; use App\Http\Requests\User\UpdateUserRequest; use App\Jobs\Company\CreateCompanyToken; +use App\Jobs\User\UserEmailChanged; use App\Models\CompanyToken; use App\Models\CompanyUser; use App\Models\User; use App\Repositories\UserRepository; use App\Transformers\UserTransformer; +use App\Utils\Ninja; use App\Utils\Traits\MakesHash; use Illuminate\Http\Request; use Illuminate\Support\Facades\Log; @@ -367,8 +371,14 @@ class UserController extends BaseController */ public function update(UpdateUserRequest $request, User $user) { + $old_email = $user->email; + $new_email = $request->input('email'); + $user = $this->user_repo->save($request->all(), $user); + if($user) + UserEmailChanged::dispatch($new_email, $old_email, auth()->user()->company()); + return $this->itemResponse($user); } diff --git a/app/Http/ValidationRules/Credit/ValidCreditsRules.php b/app/Http/ValidationRules/Credit/ValidCreditsRules.php index 527b5a2f310b..507d0262fc20 100644 --- a/app/Http/ValidationRules/Credit/ValidCreditsRules.php +++ b/app/Http/ValidationRules/Credit/ValidCreditsRules.php @@ -83,6 +83,24 @@ class ValidCreditsRules implements Rule return false; } + if(count($this->input['credits']) >=1){ + + $total_payments = $this->input['amount'] + array_sum(array_column($this->input['credits'], 'amount')); + +info(print_r($this->input,1)); +info("total payments = {$total_payments}"); +info("total credits available = " . array_sum(array_column($this->input['credits'], 'amount'))); +info("total invoices payable = " . array_sum(array_column($this->input['invoices'], 'amount'))); + + if($total_payments > array_sum(array_column($this->input['invoices'], 'amount'))){ + + $this->error_msg = "Sum of total payments and credits is greater than the total of invoices"; + return false; + } + + } + + return true; } diff --git a/app/Jobs/Quote/QuoteWorkflowSettings.php b/app/Jobs/Quote/QuoteWorkflowSettings.php index 54288c0fd604..cd155cc498ae 100644 --- a/app/Jobs/Quote/QuoteWorkflowSettings.php +++ b/app/Jobs/Quote/QuoteWorkflowSettings.php @@ -49,14 +49,15 @@ class QuoteWorkflowSettings implements ShouldQueue */ public function handle() { - if ($this->client->getSetting('auto_archive_quote')) { - $this->base_repository->archive($this->quote); - } if ($this->client->getSetting('auto_email_quote')) { $this->quote->invitations->each(function ($invitation, $key) { $this->quote->service()->sendEmail($invitation->contact); }); } + + if ($this->client->getSetting('auto_archive_quote')) { + $this->base_repository->archive($this->quote); + } } } diff --git a/app/Jobs/User/UserEmailChanged.php b/app/Jobs/User/UserEmailChanged.php new file mode 100644 index 000000000000..16f6eb892dc8 --- /dev/null +++ b/app/Jobs/User/UserEmailChanged.php @@ -0,0 +1,109 @@ +new_email = $new_email; + $this->old_email = $old_email; + $this->company = $company; + } + + public function handle() + { + //Set DB + MultiDB::setDb($this->company->db); + + //if we need to set an email driver do it now + $this->setMailDriver($this->company->settings->email_sending_method); + + $mail_obj = new \stdClass; + $mail_obj->subject = ctrans('texts.email_address_changed'); + $mail_obj->markdown = 'email.admin.generic'; + $mail_obj->from = [$this->company->owner()->email, $this->company->owner()->present()->name()]; + $mail_obj->tag = $this->company->company_key; + $mail_obj->data = $this->getData(); + + + //send email + Mail::to($this->old_email) + ->send(new UserNotificationMailer($mail_obj)); + + Mail::to($this->new_email) + ->send(new UserNotificationMailer($mail_obj)); + + + //catch errors + if (count(Mail::failures()) > 0) { + $this->logMailError(Mail::failures()); + } + + } + + private function getData() + { + return [ + 'title' => ctrans('texts.email_address_changed'), + 'message' => ctrans( + 'texts.email_address_changed_message', + ['old_email' => $this->old_email, + 'new_email' => $this->new_email, + ] + ), + 'url' => config('ninja.app_url'), + 'button' => ctrans('texts.account_login'), + 'signature' => $this->company->owner()->signature, + 'logo' => $this->company->present()->logo(), + ]; + } + + private function logMailError($errors) + { + SystemLogger::dispatch( + $errors, + SystemLog::CATEGORY_MAIL, + SystemLog::EVENT_MAIL_SEND, + SystemLog::TYPE_FAILURE, + $this->company + ); + } + +} diff --git a/app/Listeners/Credit/CreditRestoredActivity.php b/app/Listeners/Credit/CreditRestoredActivity.php new file mode 100644 index 000000000000..4920fb343dfe --- /dev/null +++ b/app/Listeners/Credit/CreditRestoredActivity.php @@ -0,0 +1,58 @@ +activity_repo = $activity_repo; + } + + /** + * Handle the event. + * + * @param object $event + * @return void + */ + public function handle($event) + { + + MultiDB::setDb($event->company->db); + + $fields = new \stdClass; + + $fields->credit_id = $event->credit->id; + $fields->user_id = $event->credit->user_id; + $fields->company_id = $event->credit->company_id; + $fields->activity_type_id = Activity::RESTORE_CREDIT; + + $this->activity_repo->save($fields, $event->credit, $event->event_vars); + + } +} diff --git a/app/Listeners/Invoice/InvoiceRestoredActivity.php b/app/Listeners/Invoice/InvoiceRestoredActivity.php new file mode 100644 index 000000000000..1832ccdc873b --- /dev/null +++ b/app/Listeners/Invoice/InvoiceRestoredActivity.php @@ -0,0 +1,57 @@ +activity_repo = $activity_repo; + } + + /** + * Handle the event. + * + * @param object $event + * @return void + */ + public function handle($event) + { + + MultiDB::setDb($event->company->db); + + $fields = new \stdClass; + + $fields->invoice_id = $event->invoice->id; + $fields->user_id = $event->invoice->user_id; + $fields->company_id = $event->invoice->company_id; + $fields->activity_type_id = Activity::RESTORE_INVOICE; + + $this->activity_repo->save($fields, $event->invoice, $event->event_vars); + } +} diff --git a/app/Listeners/Payment/PaymentRestoredActivity.php b/app/Listeners/Payment/PaymentRestoredActivity.php new file mode 100644 index 000000000000..828a0b046640 --- /dev/null +++ b/app/Listeners/Payment/PaymentRestoredActivity.php @@ -0,0 +1,57 @@ +activity_repo = $activity_repo; + } + + /** + * Handle the event. + * + * @param object $event + * @return void + */ + public function handle($event) + { + + MultiDB::setDb($event->company->db); + + $fields = new \stdClass; + + $fields->payment_id = $event->payment->id; + $fields->user_id = $event->payment->user_id; + $fields->company_id = $event->payment->company_id; + $fields->activity_type_id = Activity::RESTORE_PAYMENT; + + $this->activity_repo->save($fields, $event->payment, $event->event_vars); + } +} diff --git a/app/Listeners/Quote/QuoteApprovedActivity.php b/app/Listeners/Quote/QuoteApprovedActivity.php new file mode 100644 index 000000000000..5ac5c14d49a8 --- /dev/null +++ b/app/Listeners/Quote/QuoteApprovedActivity.php @@ -0,0 +1,58 @@ +activity_repo = $activity_repo; + } + + /** + * Handle the event. + * + * @param object $event + * @return void + */ + public function handle($event) + { + + MultiDB::setDb($event->company->db); + + $fields = new \stdClass; + + $fields->quote_id = $event->quote->id; + $fields->user_id = $event->quote->user_id; + $fields->client_contact_id = $event->contact->id; + $fields->company_id = $event->payment->company_id; + $fields->activity_type_id = Activity::RESTORE_PAYMENT; + + $this->activity_repo->save($fields, $event->payment, $event->event_vars); + } +} diff --git a/app/Listeners/Quote/QuoteArchivedActivity.php b/app/Listeners/Quote/QuoteArchivedActivity.php new file mode 100644 index 000000000000..ea8cfb7d8a1b --- /dev/null +++ b/app/Listeners/Quote/QuoteArchivedActivity.php @@ -0,0 +1,57 @@ +activity_repo = $activity_repo; + } + + /** + * Handle the event. + * + * @param object $event + * @return void + */ + public function handle($event) + { + + MultiDB::setDb($event->company->db); + + $fields = new \stdClass; + + $fields->quote_id = $event->quote->id; + $fields->user_id = $event->quote->user_id; + $fields->company_id = $event->quote->company_id; + $fields->activity_type_id = Activity::ARCHIVE_QUOTE; + + $this->activity_repo->save($fields, $event->quote, $event->event_vars); + } +} diff --git a/app/Listeners/Quote/QuoteDeletedActivity.php b/app/Listeners/Quote/QuoteDeletedActivity.php new file mode 100644 index 000000000000..f20f12a83a27 --- /dev/null +++ b/app/Listeners/Quote/QuoteDeletedActivity.php @@ -0,0 +1,57 @@ +activity_repo = $activity_repo; + } + + /** + * Handle the event. + * + * @param object $event + * @return void + */ + public function handle($event) + { + + MultiDB::setDb($event->company->db); + + $fields = new \stdClass; + + $fields->quote_id = $event->quote->id; + $fields->user_id = $event->quote->user_id; + $fields->company_id = $event->quote->company_id; + $fields->activity_type_id = Activity::DELETE_QUOTE; + + $this->activity_repo->save($fields, $event->quote, $event->event_vars); + } +} diff --git a/app/Listeners/Quote/QuoteRestoredActivity.php b/app/Listeners/Quote/QuoteRestoredActivity.php new file mode 100644 index 000000000000..9c15bd9114f4 --- /dev/null +++ b/app/Listeners/Quote/QuoteRestoredActivity.php @@ -0,0 +1,57 @@ +activity_repo = $activity_repo; + } + + /** + * Handle the event. + * + * @param object $event + * @return void + */ + public function handle($event) + { + + MultiDB::setDb($event->company->db); + + $fields = new \stdClass; + + $fields->quote_id = $event->quote->id; + $fields->user_id = $event->quote->user_id; + $fields->company_id = $event->quote->company_id; + $fields->activity_type_id = Activity::RESTORE_QUOTE; + + $this->activity_repo->save($fields, $event->quote, $event->event_vars); + } +} diff --git a/app/Mail/User/UserNotificationMailer.php b/app/Mail/User/UserNotificationMailer.php new file mode 100644 index 000000000000..755067bf84cd --- /dev/null +++ b/app/Mail/User/UserNotificationMailer.php @@ -0,0 +1,46 @@ +mail_obj = $mail_obj; + } + + /** + * Build the message. + * + * @return $this + */ + public function build() + { + return $this->from($this->mail_obj->from[0], $this->mail_obj->from[1]) //todo + ->subject($this->mail_obj->subject) + ->markdown($this->mail_obj->markdown, $this->mail_obj->data) + ->withSwiftMessage(function ($message) { + $message->getHeaders()->addTextHeader('Tag', $this->mail_obj->tag); + }); + } +} diff --git a/app/Models/Activity.php b/app/Models/Activity.php index 79432dabbc3a..8c56b8d41561 100644 --- a/app/Models/Activity.php +++ b/app/Models/Activity.php @@ -36,14 +36,14 @@ class Activity extends StaticModel const UPDATE_QUOTE=19; // const EMAIL_QUOTE=20; // const VIEW_QUOTE=21; // - const ARCHIVE_QUOTE=22; - const DELETE_QUOTE=23; - const RESTORE_QUOTE=24; - const RESTORE_INVOICE=25; - const RESTORE_CLIENT=26; - const RESTORE_PAYMENT=27; - const RESTORE_CREDIT=28; - const APPROVE_QUOTE=29; + const ARCHIVE_QUOTE=22; // + const DELETE_QUOTE=23; // + const RESTORE_QUOTE=24; // + const RESTORE_INVOICE=25; // + const RESTORE_CLIENT=26; // + const RESTORE_PAYMENT=27; // + const RESTORE_CREDIT=28; // + const APPROVE_QUOTE=29; // const CREATE_VENDOR=30; const ARCHIVE_VENDOR=31; const DELETE_VENDOR=32; diff --git a/app/Models/Payment.php b/app/Models/Payment.php index d76b283905c2..cc161528a9f1 100644 --- a/app/Models/Payment.php +++ b/app/Models/Payment.php @@ -65,7 +65,8 @@ class Payment extends BaseModel 'date', 'transaction_reference', 'number', - 'is_manual' + 'is_manual', + 'private_notes', ]; protected $casts = [ diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index d493ca9e84cf..d94e889d73b6 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -26,6 +26,7 @@ use App\Events\Credit\CreditWasCreated; use App\Events\Credit\CreditWasDeleted; use App\Events\Credit\CreditWasEmailedAndFailed; use App\Events\Credit\CreditWasMarkedSent; +use App\Events\Credit\CreditWasRestored; use App\Events\Credit\CreditWasUpdated; use App\Events\Design\DesignWasArchived; use App\Events\Invoice\InvoiceWasArchived; @@ -35,6 +36,7 @@ use App\Events\Invoice\InvoiceWasDeleted; use App\Events\Invoice\InvoiceWasEmailed; use App\Events\Invoice\InvoiceWasMarkedSent; use App\Events\Invoice\InvoiceWasPaid; +use App\Events\Invoice\InvoiceWasRestored; use App\Events\Invoice\InvoiceWasReversed; use App\Events\Invoice\InvoiceWasUpdated; use App\Events\Invoice\InvoiceWasViewed; @@ -43,11 +45,15 @@ use App\Events\Payment\PaymentWasArchived; use App\Events\Payment\PaymentWasCreated; use App\Events\Payment\PaymentWasDeleted; use App\Events\Payment\PaymentWasRefunded; +use App\Events\Payment\PaymentWasRestored; use App\Events\Payment\PaymentWasUpdated; use App\Events\Payment\PaymentWasVoided; use App\Events\Quote\QuoteWasApproved; +use App\Events\Quote\QuoteWasArchived; use App\Events\Quote\QuoteWasCreated; +use App\Events\Quote\QuoteWasDeleted; use App\Events\Quote\QuoteWasEmailed; +use App\Events\Quote\QuoteWasRestored; use App\Events\Quote\QuoteWasUpdated; use App\Events\Quote\QuoteWasViewed; use App\Events\User\UserLoggedIn; @@ -69,6 +75,7 @@ use App\Listeners\Activity\QuoteUpdatedActivity; use App\Listeners\Activity\RestoreClientActivity; use App\Listeners\Activity\UpdatedCreditActivity; use App\Listeners\Contact\UpdateContactLastLogin; +use App\Listeners\Credit\CreditRestoredActivity; use App\Listeners\Document\DeleteCompanyDocuments; use App\Listeners\Invoice\CreateInvoiceActivity; use App\Listeners\Invoice\CreateInvoiceHtmlBackup; @@ -79,12 +86,17 @@ use App\Listeners\Invoice\InvoiceDeletedActivity; use App\Listeners\Invoice\InvoiceEmailActivity; use App\Listeners\Invoice\InvoiceEmailFailedActivity; use App\Listeners\Invoice\InvoiceEmailedNotification; +use App\Listeners\Invoice\InvoiceRestoredActivity; use App\Listeners\Invoice\InvoiceViewedActivity; use App\Listeners\Invoice\UpdateInvoiceActivity; use App\Listeners\Invoice\UpdateInvoiceInvitations; use App\Listeners\Misc\InvitationViewedListener; use App\Listeners\Payment\PaymentNotification; +use App\Listeners\Payment\PaymentRestoredActivity; +use App\Listeners\Quote\QuoteArchivedActivity; +use App\Listeners\Quote\QuoteDeletedActivity; use App\Listeners\Quote\QuoteEmailActivity; +use App\Listeners\Quote\QuoteRestoredActivity; use App\Listeners\Quote\QuoteViewedActivity; use App\Listeners\Quote\ReachWorkflowSettings; use App\Listeners\SendVerificationNotification; @@ -132,6 +144,9 @@ class EventServiceProvider extends ServiceProvider PaymentWasVoided::class => [ PaymentVoidedActivity::class, ], + PaymentWasRestored::class =>[ + PaymentRestoredActivity::class, + ], // Clients ClientWasCreated::class =>[ CreatedClientActivity::class, @@ -176,6 +191,9 @@ class EventServiceProvider extends ServiceProvider CreditWasArchived::class => [ CreditArchivedActivity::class, ], + CreditWasRestored::class => [ + CreditRestoredActivity::class, + ], //Designs DesignWasArchived::class => [ ], @@ -216,6 +234,9 @@ class EventServiceProvider extends ServiceProvider InvoiceWasArchived::class => [ InvoiceArchivedActivity::class, ], + InvoiceWasRestored::class => [ + InvoiceRestoredActivity::class, + ], InvoiceWasReversed::class => [ ], InvoiceWasCancelled::class => [ @@ -241,7 +262,15 @@ class EventServiceProvider extends ServiceProvider QuoteWasViewed::class => [ QuoteViewedActivity::class, ], - + QuoteWasArchived::class => [ + QuoteArchivedActivity::class, + ], + QuoteWasDeleted::class => [ + QuoteDeletedActivity::class, + ], + QuoteWasRestored::class => [ + QuoteRestoredActivity::class, + ], ]; /** diff --git a/app/Repositories/PaymentRepository.php b/app/Repositories/PaymentRepository.php index e9d5c38aaa68..e15861e5e1d7 100644 --- a/app/Repositories/PaymentRepository.php +++ b/app/Repositories/PaymentRepository.php @@ -82,13 +82,15 @@ class PaymentRepository extends BaseRepository $data['amount'] = array_sum(array_column($data['invoices'], 'amount')); $client = Client::find($data['client_id']); - info("updating client balance from {$client->balance} by this much ".$data['amount']); + //info("updating client balance from {$client->balance} by this much ".$data['amount']); $client->service()->updatePaidToDate($data['amount'])->save(); } } + //info(print_r($data,1)); + /*Fill the payment*/ $payment->fill($data); $payment->status_id = Payment::STATUS_COMPLETED; diff --git a/app/Services/Recurring/RecurringService.php b/app/Services/Recurring/RecurringService.php new file mode 100644 index 000000000000..353a37ac3b7f --- /dev/null +++ b/app/Services/Recurring/RecurringService.php @@ -0,0 +1,28 @@ +recurring_entity = $recurring_entity; + } + + //set schedules - update next_send_dates +} diff --git a/composer.lock b/composer.lock index 9c525997c685..d001e3a70818 100644 --- a/composer.lock +++ b/composer.lock @@ -5841,16 +5841,16 @@ }, { "name": "spatie/browsershot", - "version": "3.37.1", + "version": "3.37.2", "source": { "type": "git", "url": "https://github.com/spatie/browsershot.git", - "reference": "7d526a458ce870a07669bd2416313a4d62f3f15d" + "reference": "32d2984079ed8fe690f4dc5b7b6c205ae0a7b0fd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/browsershot/zipball/7d526a458ce870a07669bd2416313a4d62f3f15d", - "reference": "7d526a458ce870a07669bd2416313a4d62f3f15d", + "url": "https://api.github.com/repos/spatie/browsershot/zipball/32d2984079ed8fe690f4dc5b7b6c205ae0a7b0fd", + "reference": "32d2984079ed8fe690f4dc5b7b6c205ae0a7b0fd", "shasum": "" }, "require": { @@ -5899,7 +5899,7 @@ "type": "github" } ], - "time": "2020-07-08T07:20:45+00:00" + "time": "2020-07-21T22:40:58+00:00" }, { "name": "spatie/image",