From da389051b9884bf1d6d7eead060aaf164b6f8d0e Mon Sep 17 00:00:00 2001 From: "Ronald A. Richardson" Date: Thu, 17 Oct 2024 16:51:07 +0800 Subject: [PATCH] patched creating customer with order, and loading order relations in json resources --- composer.json | 2 +- extension.json | 2 +- package.json | 2 +- .../Exceptions/UserAlreadyExistsException.php | 43 +++++++++++ .../Controllers/Api/v1/OrderController.php | 20 ++++- server/src/Http/Resources/v1/PurchaseRate.php | 14 +++- .../src/Http/Resources/v1/TrackingStatus.php | 14 +++- server/src/Models/Contact.php | 74 +++++++++++++++++-- 8 files changed, 153 insertions(+), 18 deletions(-) diff --git a/composer.json b/composer.json index 914030a6..39b86679 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "fleetbase/fleetops-api", - "version": "0.5.11", + "version": "0.5.12", "description": "Fleet & Transport Management Extension for Fleetbase", "keywords": [ "fleetbase-extension", diff --git a/extension.json b/extension.json index e7470196..7f8b761a 100644 --- a/extension.json +++ b/extension.json @@ -1,6 +1,6 @@ { "name": "Fleet-Ops", - "version": "0.5.11", + "version": "0.5.12", "description": "Fleet & Transport Management Extension for Fleetbase", "repository": "https://github.com/fleetbase/fleetops", "license": "AGPL-3.0-or-later", diff --git a/package.json b/package.json index d69a1255..6e2ac77b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@fleetbase/fleetops-engine", - "version": "0.5.11", + "version": "0.5.12", "description": "Fleet & Transport Management Extension for Fleetbase", "fleetbase": { "route": "fleet-ops" diff --git a/server/src/Exceptions/UserAlreadyExistsException.php b/server/src/Exceptions/UserAlreadyExistsException.php index 46d4157b..9343b855 100644 --- a/server/src/Exceptions/UserAlreadyExistsException.php +++ b/server/src/Exceptions/UserAlreadyExistsException.php @@ -2,6 +2,49 @@ namespace Fleetbase\FleetOps\Exceptions; +use Fleetbase\Models\User; + +/** + * Class UserAlreadyExistsException. + * + * Exception thrown when attempting to create or register a user that already exists in the system. + * + * This exception provides additional context by including the existing user object, allowing + * developers to access details about the duplicate user that triggered the exception. + */ class UserAlreadyExistsException extends \Exception { + /** + * The user that already exists in the system. + */ + private ?User $user; + + /** + * UserAlreadyExistsException constructor. + * + * Initializes a new instance of the UserAlreadyExistsException. + * + * @param string $message the exception message describing the error + * @param User|null $user The existing user that caused the exception. Optional. + * @param int $code The exception code. Default is 0. + * @param \Throwable|null $previous The previous throwable used for exception chaining. Optional. + */ + public function __construct(string $message = '', ?User $user = null, int $code = 0, ?\Throwable $previous = null) + { + parent::__construct($message, $code, $previous); + $this->user = $user; + } + + /** + * Retrieves the user that already exists. + * + * This method returns the `User` instance that triggered the exception, providing access + * to the user's details such as ID, name, email, etc. + * + * @return User|null the existing user object, or null if not provided + */ + public function getUser(): ?User + { + return $this->user; + } } diff --git a/server/src/Http/Controllers/Api/v1/OrderController.php b/server/src/Http/Controllers/Api/v1/OrderController.php index 3bef09f1..1bbf407e 100644 --- a/server/src/Http/Controllers/Api/v1/OrderController.php +++ b/server/src/Http/Controllers/Api/v1/OrderController.php @@ -5,6 +5,7 @@ use Fleetbase\FleetOps\Events\OrderDispatchFailed; use Fleetbase\FleetOps\Events\OrderReady; use Fleetbase\FleetOps\Events\OrderStarted; +use Fleetbase\FleetOps\Exceptions\UserAlreadyExistsException; use Fleetbase\FleetOps\Flow\Activity; use Fleetbase\FleetOps\Http\Requests\CreateOrderRequest; use Fleetbase\FleetOps\Http\Requests\ScheduleOrderRequest; @@ -226,16 +227,29 @@ public function create(CreateOrderRequest $request) try { $customer = Contact::firstOrCreate( [ - 'email' => $customer['email'], - 'type' => 'customer', + 'company_uuid' => session('company'), + 'email' => $customer['email'], + 'type' => 'customer', ], [ ...$customer, - 'type' => 'customer', + 'company_uuid' => session('company'), + 'type' => 'customer', ] ); } catch (\Exception $e) { return response()->apiError('Failed to find or create customer for order.'); + } catch (UserAlreadyExistsException $e) { + try { + // If user already exist then assign user to this customer and the company + $existingUser = $e->getUser(); + // Assign user to customer + if ($existingUser && $customer) { + $customer->assignUser($existingUser); + } + } catch (\Exception $e) { + return response()->apiError('Failed to find or create customer for order.'); + } } if ($customer instanceof Contact) { diff --git a/server/src/Http/Resources/v1/PurchaseRate.php b/server/src/Http/Resources/v1/PurchaseRate.php index e4fa7701..006b611c 100644 --- a/server/src/Http/Resources/v1/PurchaseRate.php +++ b/server/src/Http/Resources/v1/PurchaseRate.php @@ -61,8 +61,18 @@ public function toWebhookPayload() */ public function serviceQuote() { - $this->loadMissing('serviceQuote'); + if ($this->resource && method_exists($this->resource, 'loadMissing')) { + $this->resource->loadMissing('serviceQuote'); - return $this->serviceQuote ? new ServiceQuote($this->serviceQuote) : null; + return $this->resource->serviceQuote ? new ServiceQuote($this->resource->serviceQuote) : null; + } + + if (method_exists($this, 'loadMissing')) { + $this->loadMissing('serviceQuote'); + + return $this->serviceQuote ? new ServiceQuote($this->serviceQuote) : null; + } + + return null; } } diff --git a/server/src/Http/Resources/v1/TrackingStatus.php b/server/src/Http/Resources/v1/TrackingStatus.php index 59632367..32b2b683 100644 --- a/server/src/Http/Resources/v1/TrackingStatus.php +++ b/server/src/Http/Resources/v1/TrackingStatus.php @@ -67,8 +67,18 @@ public function toWebhookPayload() */ public function trackingNumber() { - $this->loadMissing('trackingNumber'); + if ($this->resource && method_exists($this->resource, 'loadMissing')) { + $this->resource->loadMissing('trackingNumber'); - return $this->trackingNumber ? new TrackingNumber($this->trackingNumber) : null; + return $this->resource->trackingNumber ? new TrackingNumber($this->resource->trackingNumber) : null; + } + + if (method_exists($this, 'loadMissing')) { + $this->loadMissing('trackingNumber'); + + return $this->trackingNumber ? new TrackingNumber($this->trackingNumber) : null; + } + + return null; } } diff --git a/server/src/Models/Contact.php b/server/src/Models/Contact.php index 0f03bb44..c5315693 100644 --- a/server/src/Models/Contact.php +++ b/server/src/Models/Contact.php @@ -262,21 +262,23 @@ public static function createFromImport(array $row, bool $saveInstance = false): * user to the contact. Optionally, it sends an invitation to the newly created user via email. * * @param Contact $contact the contact instance from which the user should be created - * @param bool $sendInvite (optional) Whether to send an invitation to the newly created user. Default is true. + * @param bool $sendInvite (optional) Whether to send an invitation to the newly created user. Default is false. * * @return User the newly created user instance * * @throws UserAlreadyExistsException if a user with the same email or phone number already exists */ - public static function createUserFromContact(Contact $contact, bool $sendInvite = true): User + public static function createUserFromContact(Contact $contact, bool $sendInvite = false): User { // Check if user already exist with email or phone number - $userAlreadyExists = User::where(function ($query) use ($contact) { + $existingUser = User::where(function ($query) use ($contact) { $query->where('email', $contact->email); - $query->orWhere('phone', $contact->phone); + if ($contact->phone) { + $query->orWhere('phone', $contact->phone); + } })->first(); - if ($userAlreadyExists) { - throw new UserAlreadyExistsException('User already exists, try to assigning the user to this contact.'); + if ($existingUser) { + throw new UserAlreadyExistsException('User already exists, try to assigning the user to this contact.', $existingUser); } // Load company @@ -330,6 +332,62 @@ public static function createUserFromContact(Contact $contact, bool $sendInvite return $user; } + /** + * Assigns a user to the company and optionally sends an invitation email. + * + * This method performs the following actions: + * 1. Associates the provided user with the current company, assigning them a role based on the + * company's type (e.g., 'Fleet-Ops Customer' or 'Fleet-Ops Contact'). + * 2. If the user is of type 'customer', assigns the 'Fleet-Ops Customer' role specifically. + * 3. Updates the current entity's `user_uuid` to reference the assigned user. + * 4. Optionally sends an invitation email to the user, inviting them to join the company. + * + * @param User $user the user to be assigned to the company + * @param bool $sendInvite Determines whether to send an invitation email to the user. Defaults to false. + * + * @return self returns the current instance to allow method chaining + * + * @throws \Exception if an error occurs during user assignment or while sending the invitation + */ + public function assignUser(User $user, bool $sendInvite = false): self + { + // Load company + $this->loadMissing('copmany'); + + // Assing to company + $user->assignCompany($this->company, $this->type === 'customer' ? 'Fleet-Ops Customer' : 'Fleet-Ops Contact'); + + // Get the company user instance + $companyUser = $user->getCompanyUser($this->company); + + // Assign customer role + $companyUser->assignSingleRole('Fleet-Ops Customer'); + + // Set user to contact + $this->update(['user_uuid' => $user->uuid]); + + // Optionally, send invite + if ($sendInvite) { + // send invitation to user + $invitation = Invite::create([ + 'company_uuid' => $user->company_uuid, + 'created_by_uuid' => session('user'), + 'subject_uuid' => $user->company_uuid, + 'subject_type' => Utils::getMutationType('company'), + 'protocol' => 'email', + 'recipients' => [$user->email], + 'reason' => 'join_company', + ]); + + // notify user + $user->notify(new UserInvited($invitation)); + } + + $this->setRelation('user', $user); + + return $this; + } + public function syncWithUser(): bool { $updates = []; @@ -364,11 +422,11 @@ public function syncWithUser(): bool * This method is a wrapper around the createUserFromContact method. It allows creating a user directly * from a contact instance and, optionally, sending an invitation to the newly created user. * - * @param bool $sendInvite (optional) Whether to send an invitation to the newly created user. Default is true. + * @param bool $sendInvite (optional) Whether to send an invitation to the newly created user. Default is false. * * @return User the newly created user instance */ - public function createUser(bool $sendInvite = true): User + public function createUser(bool $sendInvite = false): User { return static::createUserFromContact($this, $sendInvite); }