From 36489b936b7eeef3d061b7b33de8e64a50d7b097 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 7 Feb 2018 18:20:53 +0200 Subject: [PATCH] Proposals --- app/Constants.php | 1 + app/Http/Requests/CreateProposalRequest.php | 2 +- app/Http/Requests/UpdateProposalRequest.php | 2 +- app/Models/Invitation.php | 110 +--------------- app/Models/Proposal.php | 8 ++ app/Models/ProposalInvitation.php | 86 +++++++++++++ app/Models/Traits/Inviteable.php | 118 ++++++++++++++++++ app/Ninja/Repositories/ProposalRepository.php | 29 +++++ resources/views/invoices/edit.blade.php | 1 - 9 files changed, 247 insertions(+), 110 deletions(-) create mode 100644 app/Models/ProposalInvitation.php create mode 100644 app/Models/Traits/Inviteable.php diff --git a/app/Constants.php b/app/Constants.php index 7a80973e78b6..638d10c2e177 100644 --- a/app/Constants.php +++ b/app/Constants.php @@ -46,6 +46,7 @@ if (! defined('APP_NAME')) { define('ENTITY_PROPOSAL_TEMPLATE', 'proposal_template'); define('ENTITY_PROPOSAL_SNIPPET', 'proposal_snippet'); define('ENTITY_PROPOSAL_CATEGORY', 'proposal_category'); + define('ENTITY_PROPOSAL_INVITATION', 'proposal_invitation'); define('INVOICE_TYPE_STANDARD', 1); define('INVOICE_TYPE_QUOTE', 2); diff --git a/app/Http/Requests/CreateProposalRequest.php b/app/Http/Requests/CreateProposalRequest.php index cac756a49204..1c1c0fd59888 100644 --- a/app/Http/Requests/CreateProposalRequest.php +++ b/app/Http/Requests/CreateProposalRequest.php @@ -22,7 +22,7 @@ class CreateProposalRequest extends ProposalRequest public function rules() { return [ - 'quote_id' => 'required', + 'invoice_id' => 'required', ]; } } diff --git a/app/Http/Requests/UpdateProposalRequest.php b/app/Http/Requests/UpdateProposalRequest.php index d525f11e8dc2..8e106e045232 100644 --- a/app/Http/Requests/UpdateProposalRequest.php +++ b/app/Http/Requests/UpdateProposalRequest.php @@ -26,7 +26,7 @@ class UpdateProposalRequest extends ProposalRequest } return [ - 'quote_id' => 'required', + 'invoice_id' => 'required', ]; } } diff --git a/app/Models/Invitation.php b/app/Models/Invitation.php index 76d395efd744..d9fc6164b5c1 100644 --- a/app/Models/Invitation.php +++ b/app/Models/Invitation.php @@ -2,10 +2,9 @@ namespace App\Models; -use Carbon; use Illuminate\Database\Eloquent\SoftDeletes; -use Utils; use App\Models\LookupInvitation; +use App\Models\Traits\Inviteable; /** * Class Invitation. @@ -13,6 +12,8 @@ use App\Models\LookupInvitation; class Invitation extends EntityModel { use SoftDeletes; + use Inviteable; + /** * @var array */ @@ -57,111 +58,6 @@ class Invitation extends EntityModel { return $this->belongsTo('App\Models\Account'); } - - // If we're getting the link for PhantomJS to generate the PDF - // we need to make sure it's served from our site - - /** - * @param string $type - * @param bool $forceOnsite - * - * @return string - */ - public function getLink($type = 'view', $forceOnsite = false, $forcePlain = false) - { - if (! $this->account) { - $this->load('account'); - } - - $account = $this->account; - $iframe_url = $account->iframe_url; - $url = trim(SITE_URL, '/'); - - if (env('REQUIRE_HTTPS')) { - $url = str_replace('http://', 'https://', $url); - } - - if ($account->hasFeature(FEATURE_CUSTOM_URL)) { - if (Utils::isNinjaProd() && ! Utils::isReseller()) { - $url = $account->present()->clientPortalLink(); - } - - if ($iframe_url && ! $forceOnsite) { - return "{$iframe_url}?{$this->invitation_key}"; - } elseif ($this->account->subdomain && ! $forcePlain) { - $url = Utils::replaceSubdomain($url, $account->subdomain); - } - } - - return "{$url}/{$type}/{$this->invitation_key}"; - } - - /** - * @return bool|string - */ - public function getStatus() - { - $hasValue = false; - $parts = []; - $statuses = $this->message_id ? ['sent', 'opened', 'viewed'] : ['sent', 'viewed']; - - foreach ($statuses as $status) { - $field = "{$status}_date"; - $date = ''; - if ($this->$field && $this->field != '0000-00-00 00:00:00') { - $date = Utils::dateToString($this->$field); - $hasValue = true; - $parts[] = trans('texts.invitation_status_' . $status) . ': ' . $date; - } - } - - return $hasValue ? implode($parts, '
') : false; - } - - /** - * @return mixed - */ - public function getName() - { - return $this->invitation_key; - } - - /** - * @param null $messageId - */ - public function markSent($messageId = null) - { - $this->message_id = $messageId; - $this->email_error = null; - $this->sent_date = Carbon::now()->toDateTimeString(); - $this->save(); - } - - public function isSent() - { - return $this->sent_date && $this->sent_date != '0000-00-00 00:00:00'; - } - - public function markViewed() - { - $invoice = $this->invoice; - $client = $invoice->client; - - $this->viewed_date = Carbon::now()->toDateTimeString(); - $this->save(); - - $invoice->markViewed(); - $client->markLoggedIn(); - } - - public function signatureDiv() - { - if (! $this->signature_base64) { - return false; - } - - return sprintf('

%s: %s', $this->signature_base64, trans('texts.signed'), Utils::fromSqlDateTime($this->signature_date)); - } } Invitation::creating(function ($invitation) diff --git a/app/Models/Proposal.php b/app/Models/Proposal.php index 497830e012e4..2b2b0f892843 100644 --- a/app/Models/Proposal.php +++ b/app/Models/Proposal.php @@ -64,6 +64,14 @@ class Proposal extends EntityModel return $this->belongsTo('App\Models\Invoice')->withTrashed(); } + /** + * @return mixed + */ + public function proposal_invitations() + { + return $this->hasMany('App\Models\ProposalInvitation')->orderBy('proposal_invitations.contact_id'); + } + /** * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ diff --git a/app/Models/ProposalInvitation.php b/app/Models/ProposalInvitation.php new file mode 100644 index 000000000000..c2895ffcbb27 --- /dev/null +++ b/app/Models/ProposalInvitation.php @@ -0,0 +1,86 @@ +belongsTo('App\Models\Proposal')->withTrashed(); + } + + /** + * @return mixed + */ + public function contact() + { + return $this->belongsTo('App\Models\Contact')->withTrashed(); + } + + /** + * @return mixed + */ + public function user() + { + return $this->belongsTo('App\Models\User')->withTrashed(); + } + + /** + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function account() + { + return $this->belongsTo('App\Models\Account'); + } +} + +/* +ProposalInvitation::creating(function ($invitation) +{ + LookupProposalInvitation::createNew($invitation->account->account_key, [ + 'invitation_key' => $invitation->invitation_key, + ]); +}); + +ProposalInvitation::updating(function ($invitation) { + $dirty = $invitation->getDirty(); + if (array_key_exists('message_id', $dirty)) { + LookupProposalInvitation::updateInvitation($invitation->account->account_key, $invitation); + } +}); + +ProposalInvitation::deleted(function ($invitation) +{ + if ($invitation->forceDeleting) { + LookupProposalInvitation::deleteWhere([ + 'invitation_key' => $invitation->invitation_key, + ]); + } +}); +*/ diff --git a/app/Models/Traits/Inviteable.php b/app/Models/Traits/Inviteable.php new file mode 100644 index 000000000000..825755a4936d --- /dev/null +++ b/app/Models/Traits/Inviteable.php @@ -0,0 +1,118 @@ +account) { + $this->load('account'); + } + + $account = $this->account; + $iframe_url = $account->iframe_url; + $url = trim(SITE_URL, '/'); + + if (env('REQUIRE_HTTPS')) { + $url = str_replace('http://', 'https://', $url); + } + + if ($account->hasFeature(FEATURE_CUSTOM_URL)) { + if (Utils::isNinjaProd() && ! Utils::isReseller()) { + $url = $account->present()->clientPortalLink(); + } + + if ($iframe_url && ! $forceOnsite) { + return "{$iframe_url}?{$this->invitation_key}"; + } elseif ($this->account->subdomain && ! $forcePlain) { + $url = Utils::replaceSubdomain($url, $account->subdomain); + } + } + + return "{$url}/{$type}/{$this->invitation_key}"; + } + + /** + * @return bool|string + */ + public function getStatus() + { + $hasValue = false; + $parts = []; + $statuses = $this->message_id ? ['sent', 'opened', 'viewed'] : ['sent', 'viewed']; + + foreach ($statuses as $status) { + $field = "{$status}_date"; + $date = ''; + if ($this->$field && $this->field != '0000-00-00 00:00:00') { + $date = Utils::dateToString($this->$field); + $hasValue = true; + $parts[] = trans('texts.invitation_status_' . $status) . ': ' . $date; + } + } + + return $hasValue ? implode($parts, '
') : false; + } + + /** + * @return mixed + */ + public function getName() + { + return $this->invitation_key; + } + + /** + * @param null $messageId + */ + public function markSent($messageId = null) + { + $this->message_id = $messageId; + $this->email_error = null; + $this->sent_date = Carbon::now()->toDateTimeString(); + $this->save(); + } + + public function isSent() + { + return $this->sent_date && $this->sent_date != '0000-00-00 00:00:00'; + } + + public function markViewed() + { + $this->viewed_date = Carbon::now()->toDateTimeString(); + $this->save(); + + if ($this->invoice) { + $invoice = $this->invoice; + $client = $invoice->client; + + $invoice->markViewed(); + $client->markLoggedIn(); + } + } + + public function signatureDiv() + { + if (! $this->signature_base64) { + return false; + } + + return sprintf('

%s: %s', $this->signature_base64, trans('texts.signed'), Utils::fromSqlDateTime($this->signature_date)); + } +} diff --git a/app/Ninja/Repositories/ProposalRepository.php b/app/Ninja/Repositories/ProposalRepository.php index 83714b810226..416a2f271c3a 100644 --- a/app/Ninja/Repositories/ProposalRepository.php +++ b/app/Ninja/Repositories/ProposalRepository.php @@ -5,6 +5,7 @@ namespace App\Ninja\Repositories; use App\Models\Proposal; use App\Models\Invoice; use App\Models\ProposalTemplate; +use App\Models\ProposalInvitation; use Auth; use DB; use Utils; @@ -89,6 +90,34 @@ class ProposalRepository extends BaseRepository $proposal->save(); + // create invitations + $contactIds = []; + + foreach ($proposal->invoice->invitations as $invitation) { + $conactIds[] = $invitation->contact_id; + $found = false; + foreach ($proposal->proposal_invitations as $proposalInvitation) { + if ($invitation->contact_id == $proposalInvitation->contact_id) { + $found = true; + break; + } + } + if (! $found) { + $proposalInvitation = ProposalInvitation::createNew(); + $proposalInvitation->proposal_id = $proposal->id; + $proposalInvitation->contact_id = $invitation->contact_id; + $proposalInvitation->invitation_key = strtolower(str_random(RANDOM_KEY_LENGTH)); + $proposalInvitation->save(); + } + } + + // delete invitations + foreach ($proposal->proposal_invitations as $proposalInvitation) { + if (! in_array($proposalInvitation->contact_id, $conactIds)) { + $proposalInvitation->delete(); + } + } + return $proposal; } } diff --git a/resources/views/invoices/edit.blade.php b/resources/views/invoices/edit.blade.php index a4d33075daac..ba0f368b08ca 100644 --- a/resources/views/invoices/edit.blade.php +++ b/resources/views/invoices/edit.blade.php @@ -878,7 +878,6 @@ ko.mapping.fromJS(invoice, model.invoice().mapping, model.invoice); model.invoice().is_recurring({{ $invoice->is_recurring ? '1' : '0' }}); model.invoice().start_date_orig(model.invoice().start_date()); - @if ($invoice->id) var invitationContactIds = {!! json_encode($invitationContactIds) !!}; var client = clientMap[invoice.client.public_id];