diff --git a/app/Http/Controllers/DashboardController.php b/app/Http/Controllers/DashboardController.php index 27d4ab04af57..847afbf7d574 100644 --- a/app/Http/Controllers/DashboardController.php +++ b/app/Http/Controllers/DashboardController.php @@ -6,6 +6,7 @@ use View; use App\Models\Activity; use App\Models\Invoice; use App\Models\Payment; +use App\Models\VendorActivity; class DashboardController extends BaseController { @@ -68,6 +69,13 @@ class DashboardController extends BaseController ->take(50) ->get(); + $vendoractivities = VendorActivity::where('vendor_activities.account_id', '=', Auth::user()->account_id) + ->with('vendor.vendorcontacts', 'user', 'account') + ->where('activity_type_id', '>', 0) + ->orderBy('created_at', 'desc') + ->take(50) + ->get(); + $pastDue = DB::table('invoices') ->leftJoin('clients', 'clients.id', '=', 'invoices.client_id') ->leftJoin('contacts', 'contacts.client_id', '=', 'clients.id') @@ -141,6 +149,7 @@ class DashboardController extends BaseController 'payments' => $payments, 'title' => trans('texts.dashboard'), 'hasQuotes' => $hasQuotes, + 'vendoractivities' => $vendoractivities, ]; return View::make('dashboard', $data); diff --git a/app/Http/Controllers/PublicVendorController.php b/app/Http/Controllers/PublicVendorController.php new file mode 100644 index 000000000000..46440676923f --- /dev/null +++ b/app/Http/Controllers/PublicVendorController.php @@ -0,0 +1,198 @@ +activityRepo = $activityRepo; + $this->vendor = $activityRepo->vendor; + } + + public function dashboard() + { + if (!$invitation = $this->getInvitation()) { + return $this->returnError(); + } + + $account = $invitation->account; + $color = $account->primary_color ? $account->primary_color : '#0b4d78'; + + $data = [ + 'color' => $color, + 'account' => $account, + 'client' => $this->vendor, + 'hideLogo' => $account->isWhiteLabel(), + 'clientViewCSS' => $account->clientViewCSS(), + ]; + + return response()->view('invited.dashboard', $data); + } + + public function activityDatatable() + { + if (!$invitation = $this->getInvitation()) { + return false; + } + + + $query = $this->activityRepo->findByClientId($invoice->client_id); + $query->where('vendor_activities.adjustment', '!=', 0); + + return Datatable::query($query) + ->addColumn('vendor_activities.id', function ($model) { return Utils::timestampToDateTimeString(strtotime($model->created_at)); }) + ->addColumn('activity_type_id', function ($model) { + $data = [ + 'client' => Utils::getClientDisplayName($model), + 'user' => $model->is_system ? ('' . trans('texts.system') . '') : ($model->user_first_name . ' ' . $model->user_last_name), + 'invoice' => trans('texts.invoice') . ' ' . $model->invoice, + 'contact' => Utils::getClientDisplayName($model), + 'payment' => trans('texts.payment') . ($model->payment ? ' ' . $model->payment : ''), + ]; + + return trans("texts.activity_{$model->activity_type_id}", $data); + }) + ->addColumn('balance', function ($model) { return Utils::formatMoney($model->balance, $model->currency_id, $model->country_id); }) + ->addColumn('adjustment', function ($model) { return $model->adjustment != 0 ? Utils::wrapAdjustment($model->adjustment, $model->currency_id, $model->country_id) : ''; }) + ->make(); + } + + public function invoiceIndex() + { + if (!$invitation = $this->getInvitation()) { + return $this->returnError(); + } + $account = $invitation->account; + $color = $account->primary_color ? $account->primary_color : '#0b4d78'; + + $data = [ + 'color' => $color, + 'hideLogo' => $account->isWhiteLabel(), + 'clientViewCSS' => $account->clientViewCSS(), + 'title' => trans('texts.invoices'), + 'entityType' => ENTITY_INVOICE, + 'columns' => Utils::trans(['invoice_number', 'invoice_date', 'invoice_total', 'balance_due', 'due_date']), + ]; + + return response()->view('public_list', $data); + } + + public function invoiceDatatable() + { + if (!$invitation = $this->getInvitation()) { + return ''; + } + + return $this->invoiceRepo->getClientDatatable($invitation->contact_id, ENTITY_INVOICE, Input::get('sSearch')); + } + + + public function paymentIndex() + { + if (!$invitation = $this->getInvitation()) { + return $this->returnError(); + } + $account = $invitation->account; + $color = $account->primary_color ? $account->primary_color : '#0b4d78'; + + $data = [ + 'color' => $color, + 'hideLogo' => $account->isWhiteLabel(), + 'clientViewCSS' => $account->clientViewCSS(), + 'entityType' => ENTITY_PAYMENT, + 'title' => trans('texts.payments'), + 'columns' => Utils::trans(['invoice', 'transaction_reference', 'method', 'payment_amount', 'payment_date']) + ]; + + return response()->view('public_list', $data); + } + + public function paymentDatatable() + { + if (!$invitation = $this->getInvitation()) { + return false; + } + $payments = $this->paymentRepo->findForContact($invitation->contact->id, Input::get('sSearch')); + + return Datatable::query($payments) + ->addColumn('invoice_number', function ($model) { return $model->invitation_key ? link_to('/view/'.$model->invitation_key, $model->invoice_number) : $model->invoice_number; }) + ->addColumn('transaction_reference', function ($model) { return $model->transaction_reference ? $model->transaction_reference : 'Manual entry'; }) + ->addColumn('payment_type', function ($model) { return $model->payment_type ? $model->payment_type : ($model->account_gateway_id ? 'Online payment' : ''); }) + ->addColumn('amount', function ($model) { return Utils::formatMoney($model->amount, $model->currency_id, $model->country_id); }) + ->addColumn('payment_date', function ($model) { return Utils::dateToString($model->payment_date); }) + ->make(); + } + + public function quoteIndex() + { + if (!$invitation = $this->getInvitation()) { + return $this->returnError(); + } + $account = $invitation->account; + $color = $account->primary_color ? $account->primary_color : '#0b4d78'; + + $data = [ + 'color' => $color, + 'hideLogo' => $account->isWhiteLabel(), + 'clientViewCSS' => $account->clientViewCSS(), + 'title' => trans('texts.quotes'), + 'entityType' => ENTITY_QUOTE, + 'columns' => Utils::trans(['quote_number', 'quote_date', 'quote_total', 'due_date']), + ]; + + return response()->view('public_list', $data); + } + + + public function quoteDatatable() + { + if (!$invitation = $this->getInvitation()) { + return false; + } + + return $this->invoiceRepo->getClientDatatable($invitation->contact_id, ENTITY_QUOTE, Input::get('sSearch')); + } + + private function returnError() + { + return response()->view('error', [ + 'error' => trans('texts.invoice_not_found'), + 'hideHeader' => true, + 'clientViewCSS' => $account->clientViewCSS(), + ]); + } + + private function getInvitation() + { + $invitationKey = session('invitation_key'); + + if (!$invitationKey) { + return false; + } + + $invitation = VendorInvitation::where('invitation_key', '=', $invitationKey)->first(); + + if (!$invitation || $invitation->is_deleted) { + return false; + } + + $invoice = $invitation->invoice; + + if (!$invoice || $invoice->is_deleted) { + return false; + } + + return $invitation; + } + +} \ No newline at end of file diff --git a/app/Http/Requests/UpdateVendorRequest.php b/app/Http/Requests/UpdateVendorRequest.php new file mode 100644 index 000000000000..dd9f79db034d --- /dev/null +++ b/app/Http/Requests/UpdateVendorRequest.php @@ -0,0 +1,29 @@ + 'valid_contacts', + ]; + } +} diff --git a/app/Listeners/VendorActivityListener.php b/app/Listeners/VendorActivityListener.php new file mode 100644 index 000000000000..806b20433479 --- /dev/null +++ b/app/Listeners/VendorActivityListener.php @@ -0,0 +1,55 @@ +activityRepo = $activityRepo; + } + + // Vendors + public function createdVendor(VendorWasCreated $event) + { + $this->activityRepo->create( + $event->vendor, + ACTIVITY_TYPE_CREATE_VENDOR + ); + } + + public function deletedVendor(VendorWasDeleted $event) + { + $this->activityRepo->create( + $event->vendor, + ACTIVITY_TYPE_DELETE_CLIENT + ); + } + + public function archivedVendor(VendorWasArchived $event) + { + if ($event->client->is_deleted) { + return; + } + + $this->activityRepo->create( + $event->vendor, + ACTIVITY_TYPE_ARCHIVE_CLIENT + ); + } + + public function restoredVendor(VendorWasRestored $event) + { + $this->activityRepo->create( + $event->vendor, + ACTIVITY_TYPE_RESTORE_CLIENT + ); + } +} diff --git a/app/Models/VendorActivity.php b/app/Models/VendorActivity.php index 94208cfbc6a7..4b2a166668dd 100644 --- a/app/Models/VendorActivity.php +++ b/app/Models/VendorActivity.php @@ -42,15 +42,20 @@ class VendorActivity extends Eloquent { $account = $this->account; $vendor = $this->vendor; $user = $this->user; - $contactId = $this->contact_id; - $isSystem = $this->is_system; + $contactId = $this->contact_id; + $isSystem = $this->is_system; - $data = [ - 'vendor' => link_to($vendor->getRoute(), $vendor->getDisplayName()), - 'user' => $isSystem ? '' . trans('texts.system') . '' : $user->getDisplayName(), - 'vendorcontact' => $contactId ? $vendor->getDisplayName() : $user->getDisplayName(), - ]; + if($vendor) { + $route = $vendor->getRoute(); + $data = [ + 'vendor' => link_to($route, $vendor->getDisplayName()), + 'user' => $isSystem ? '' . trans('texts.system') . '' : $user->getDisplayName(), + 'vendorcontact' => $contactId ? $vendor->getDisplayName() : $user->getDisplayName(), + ]; + } else { + return trans("texts.invalid_activity"); + } return trans("texts.activity_{$activityTypeId}", $data); } } diff --git a/app/Ninja/Mailers/VendorContactMailer.php b/app/Ninja/Mailers/VendorContactMailer.php new file mode 100644 index 000000000000..6779ebcb9905 --- /dev/null +++ b/app/Ninja/Mailers/VendorContactMailer.php @@ -0,0 +1,152 @@ +vendor; + $account = $invoice->account; + + if (Auth::check()) { + $user = Auth::user(); + } else { + $user = $invitation->user; + if ($invitation->user->trashed()) { + $user = $account->users()->orderBy('id')->first(); + } + } + + if (!$user->email || !$user->registered) { + return trans('texts.email_errors.user_unregistered'); + } elseif (!$user->confirmed) { + return trans('texts.email_errors.user_unconfirmed'); + } elseif (!$invitation->contact->email) { + return trans('texts.email_errors.invalid_contact_email'); + } elseif ($invitation->contact->trashed()) { + return trans('texts.email_errors.inactive_contact'); + } + + $variables = [ + 'account' => $account, + 'vendor' => $vendor, + 'invitation' => $invitation + ]; + + $data = [ + 'body' => $this->processVariables($body, $variables), + 'link' => $invitation->getLink(), + 'entityType' => $invoice->getEntityType(), + 'invitation' => $invitation, + 'account' => $account, + 'vendor' => $vendor, + 'invoice' => $invoice, + ]; + + if ($account->attatchPDF()) { + $data['pdfString'] = $pdfString; + $data['pdfFileName'] = $invoice->getFileName(); + } + + $subject = $this->processVariables($subject, $variables); + $fromEmail = $user->email; + + if ($account->email_design_id == EMAIL_DESIGN_PLAIN) { + $view = ENTITY_INVOICE; + } else { + $view = 'design' . ($account->email_design_id - 1); + } + + $response = $this->sendTo($invitation->contact->email, $fromEmail, $account->getDisplayName(), $subject, $view, $data); + + if ($response === true) { + return true; + } else { + return $response; + } + } + + public function sendLicensePaymentConfirmation($name, $email, $amount, $license, $productId) + { + $view = 'license_confirmation'; + $subject = trans('texts.payment_subject'); + + if ($productId == PRODUCT_ONE_CLICK_INSTALL) { + $license = "Softaculous install license: $license"; + } elseif ($productId == PRODUCT_INVOICE_DESIGNS) { + $license = "Invoice designs license: $license"; + } elseif ($productId == PRODUCT_WHITE_LABEL) { + $license = "White label license: $license"; + } + + $data = [ + 'vendor' => $name, + 'amount' => Utils::formatMoney($amount, DEFAULT_CURRENCY, DEFAULT_COUNTRY), + 'license' => $license + ]; + + $this->sendTo($email, CONTACT_EMAIL, CONTACT_NAME, $subject, $view, $data); + } + + private function processVariables($template, $data) + { + $account = $data['account']; + $vendor = $data['vendor']; + $invitation = $data['invitation']; + $invoice = $invitation->invoice; + + $variables = [ + '$footer' => $account->getEmailFooter(), + '$vendor' => $vendor->getDisplayName(), + '$account' => $account->getDisplayName(), + '$contact' => $invitation->contact->getDisplayName(), + '$firstName' => $invitation->contact->first_name, + '$amount' => $account->formatMoney($data['amount'], $vendor), + '$invoice' => $invoice->invoice_number, + '$quote' => $invoice->invoice_number, + '$link' => $invitation->getLink(), + '$dueDate' => $account->formatDate($invoice->due_date), + '$viewLink' => $invitation->getLink(), + '$viewButton' => HTML::emailViewButton($invitation->getLink(), $invoice->getEntityType()), + '$paymentLink' => $invitation->getLink('payment'), + '$paymentButton' => HTML::emailPaymentButton($invitation->getLink('payment')), + '$customClient1' => $account->custom_vendor_label1, + '$customClient2' => $account->custom_vendor_label2, + '$customInvoice1' => $account->custom_invoice_text_label1, + '$customInvoice2' => $account->custom_invoice_text_label2, + ]; + + // Add variables for available payment types + foreach (Gateway::$paymentTypes as $type) { + $camelType = Gateway::getPaymentTypeName($type); + $type = Utils::toSnakeCase($camelType); + $variables["\${$camelType}Link"] = $invitation->getLink() . "/{$type}"; + $variables["\${$camelType}Button"] = HTML::emailPaymentButton($invitation->getLink('payment') . "/{$type}"); + } + + $str = str_replace(array_keys($variables), array_values($variables), $template); + + return $str; + } +} diff --git a/app/Ninja/Repositories/VendorActivityRepository.php b/app/Ninja/Repositories/VendorActivityRepository.php index d776e19c5ae7..bf9cb9d50d81 100644 --- a/app/Ninja/Repositories/VendorActivityRepository.php +++ b/app/Ninja/Repositories/VendorActivityRepository.php @@ -68,9 +68,9 @@ class VendorActivityRepository public function findByVendorId($vendorId) { return DB::table('vendor_activities') - ->join('accounts', 'accounts.id', '=', 'activities.account_id') - ->join('users', 'users.id', '=', 'activities.user_id') - ->join('vendors', 'vendors.id', '=', 'activities.vendor_id') + ->join('accounts', 'accounts.id', '=', 'vendor_activities.account_id') + ->join('users', 'users.id', '=', 'vendor_activities.user_id') + ->join('vendors', 'vendors.id', '=', 'vendor_activities.vendor_id') ->leftJoin('vendor_contacts', 'vendor_contacts.vendor_id', '=', 'vendors.id') ->where('vendors.id', '=', $vendorId) ->where('vendor_contacts.is_primary', '=', 1) diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index 68823f059e2b..dbed40c5fa3c 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -892,6 +892,7 @@ return array( 'activity_27' => ':user restored payment :payment', 'activity_28' => ':user restored :credit credit', 'activity_29' => ':contact approved quote :quote', + 'activity_30' => ':user created :vendor', 'payment' => 'Payment', 'system' => 'System', diff --git a/resources/views/dashboard.blade.php b/resources/views/dashboard.blade.php index 56f9690610ec..b3f1042509f8 100644 --- a/resources/views/dashboard.blade.php +++ b/resources/views/dashboard.blade.php @@ -84,6 +84,13 @@ {!! $activity->getMessage() !!} @endforeach + @foreach ($vendoractivities as $activity) +
{{ trans('texts.id_number').': '.$vendor->id_number }}
+ @endif + @if ($vendor->vat_number) +{{ trans('texts.vat_number').': '.$vendor->vat_number }}
+ @endif + + @if ($vendor->address1) + {{ $vendor->address1 }}{{ $vendor->private_notes }}
+ @endif + + @if ($vendor->vendor_industry) + {{ $vendor->vendor_industry->name }}{!! Utils::formatWebsite($vendor->website) !!}
+ @endif + + @if ($vendor->language) +{{ $vendor->language->name }}
+ @endif + +{{ $vendor->payment_terms ? trans('texts.payment_terms') . ": " . trans('texts.payment_terms_net') . " " . $vendor->payment_terms : '' }}
+{{ trans('texts.paid_to_date') }} | +{{ Utils::formatMoney($vendor->paid_to_date, $vendor->getCurrencyId()) }} | +
{{ trans('texts.balance') }} | +{{ Utils::formatMoney($vendor->balance, $vendor->getCurrencyId()) }} | +
{{ trans('texts.credit') }} | +{{ Utils::formatMoney($credit, $vendor->getCurrencyId()) }} | +