From 3ba700f79d0e83a9441e65b1d8526743b8bd1677 Mon Sep 17 00:00:00 2001 From: Raul Perusquia Date: Tue, 1 Oct 2024 10:14:35 +0800 Subject: [PATCH 1/8] working on refactoring hr --- .../HumanResources/Employee/StoreEmployee.php | 15 +- .../Traits/HasEmployeePositionGenerator.php | 44 ++- .../Employee/UpdateEmployee.php | 2 +- .../JobPosition/SyncUserJobPositions.php | 7 +- app/Actions/SysAdmin/Guest/StoreGuest.php | 2 +- .../User/SyncRolesFromJobPositions.php | 19 ++ app/Actions/SysAdmin/User/UI/EditUser.php | 2 +- .../User/UpdateUsersPseudoJobPositions.php} | 44 ++- .../Transfers/Aurora/FetchAuroraEmployees.php | 14 +- .../Transfers/Aurora/FetchAuroraUsers.php | 49 ++- .../SysAdmin/Authorisation/RolesEnum.php | 4 +- app/Models/SysAdmin/User.php | 2 +- .../Aurora/FetchAuroraDeletedEmployee.php | 3 + app/Transfers/Aurora/FetchAuroraEmployee.php | 283 ++++++------------ app/Transfers/Aurora/FetchAuroraUser.php | 19 +- app/Transfers/Aurora/WithAuroraParsers.php | 210 ++++++++++++- app/Transfers/AuroraOrganisationService.php | 2 + config/blueprint/job_positions.php | 10 +- .../Forms/Fields/EmployeePosition.vue | 2 +- routes/grp/web/models/sysAdmin/user.php | 4 +- tests/Feature/SysAdminTest.php | 4 +- 21 files changed, 447 insertions(+), 294 deletions(-) rename app/Actions/{HumanResources/Employee/UpdateEmployeeOtherOrganisationJobPositions.php => SysAdmin/User/UpdateUsersPseudoJobPositions.php} (66%) diff --git a/app/Actions/HumanResources/Employee/StoreEmployee.php b/app/Actions/HumanResources/Employee/StoreEmployee.php index a28f843deb..5306ad098f 100644 --- a/app/Actions/HumanResources/Employee/StoreEmployee.php +++ b/app/Actions/HumanResources/Employee/StoreEmployee.php @@ -80,9 +80,14 @@ public function handle(Organisation|Workplace $parent, array $modelData): Employ } $jobPositions = []; + foreach ($positions as $positionData) { - $jobPosition = JobPosition::firstWhere('slug', $positionData['slug']); - $jobPositions[$jobPosition->id] = $positionData['scopes']; + + /** @var JobPosition $jobPosition */ + $jobPosition = $this->organisation->jobPositions()->firstWhere('code', $positionData['code']); + if($jobPosition){ + $jobPositions[$jobPosition->id] = $positionData['scopes']; + } } SyncEmployeeJobPositions::run($employee, $jobPositions); @@ -154,7 +159,7 @@ public function rules(): array 'job_title' => ['sometimes', 'nullable', 'string', 'max:256'], 'state' => ['required', Rule::enum(EmployeeStateEnum::class)], 'positions' => ['sometimes', 'array'], - 'positions.*.slug' => ['sometimes', 'string'], + 'positions.*.code' => ['sometimes', 'string'], 'positions.*.scopes' => ['sometimes', 'array'], 'positions.*.scopes.organisations.slug.*' => ['sometimes', Rule::exists('organisations', 'slug')->where('group_id', $this->organisation->group_id)], 'positions.*.scopes.warehouses.slug.*' => ['sometimes', Rule::exists('warehouses', 'slug')->where('organisation_id', $this->organisation->id)], @@ -188,6 +193,7 @@ public function rules(): array public function action(Organisation|Workplace $parent, array $modelData, int $hydratorsDelay = 0, bool $strict = true): Employee { + $this->asAction = true; $this->strict = $strict; $this->hydratorsDelay = $hydratorsDelay; @@ -205,6 +211,9 @@ public function action(Organisation|Workplace $parent, array $modelData, int $hy public function asController(Organisation $organisation, ActionRequest $request): Employee { + + + $this->initialisation($organisation, $request); return $this->handle($organisation, $this->validatedData); diff --git a/app/Actions/HumanResources/Employee/Traits/HasEmployeePositionGenerator.php b/app/Actions/HumanResources/Employee/Traits/HasEmployeePositionGenerator.php index 310c3590b2..59828b4514 100644 --- a/app/Actions/HumanResources/Employee/Traits/HasEmployeePositionGenerator.php +++ b/app/Actions/HumanResources/Employee/Traits/HasEmployeePositionGenerator.php @@ -15,28 +15,39 @@ trait HasEmployeePositionGenerator { public function generatePositions(Organisation $organisation, array $modelData): array { - $jobPositions = []; + + + $rawJobPositions = []; foreach (Arr::get($modelData, 'positions', []) as $positionData) { + + /** @var JobPosition $jobPosition */ - if (in_array($positionData['slug'], [ + if (in_array($positionData['code'], [ 'group_admin', 'group_sysadmin', 'group_procurement' ])) { - $jobPosition = $organisation->group->jobPositions()->firstWhere('code', $positionData['slug']); + $jobPosition = $organisation->group->jobPositions()->firstWhere('code', $positionData['code']); } else { - $jobPosition = $organisation->jobPositions()->firstWhere('code', $positionData['slug']); + $jobPosition = $organisation->jobPositions()->firstWhere('code', $positionData['code']); } - $jobPositions[$jobPosition->id] = []; + + + + $rawJobPositions[$jobPosition->id] = []; foreach (Arr::get($positionData, 'scopes', []) as $key => $scopes) { + + + + $scopeData = match ($key) { 'shops' => [ 'Shop' => $organisation->shops->whereIn('slug', $scopes['slug'])->pluck('id')->toArray() ], - 'warehouses' => [ + 'warehouses'=> [ 'Warehouse' => $organisation->warehouses->whereIn('slug', $scopes['slug'])->pluck('id')->toArray() ], 'fulfilments' => [ @@ -45,18 +56,29 @@ public function generatePositions(Organisation $organisation, array $modelData): default => [] }; - if (isset($jobPositions[$jobPosition->id])) { - $jobPositions[$jobPosition->id] = array_merge_recursive($jobPositions[$jobPosition->id], $scopeData); + if (isset($rawJobPositions[$jobPosition->id])) { + $rawJobPositions[$jobPosition->id] = array_merge_recursive($rawJobPositions[$jobPosition->id], $scopeData); } else { - $jobPositions[$jobPosition->id] = $scopeData; + $rawJobPositions[$jobPosition->id] = $scopeData; } } } - foreach ($jobPositions as $id => $scopes) { + + + $jobPositions=[]; + foreach ($rawJobPositions as $id => $scopes) { + foreach ($scopes as $scopeKey => $ids) { - $jobPositions[$id][$scopeKey] = array_values(array_unique($ids)); + $rawJobPositions[$id][$scopeKey] = array_values(array_unique($ids)); } + + $jobPositions[$id] = [ + 'scopes'=>$rawJobPositions[$id], + 'organisation_id'=>$organisation->id + ]; + + } return $jobPositions; diff --git a/app/Actions/HumanResources/Employee/UpdateEmployee.php b/app/Actions/HumanResources/Employee/UpdateEmployee.php index c5ebe14533..d52ac33be4 100644 --- a/app/Actions/HumanResources/Employee/UpdateEmployee.php +++ b/app/Actions/HumanResources/Employee/UpdateEmployee.php @@ -147,7 +147,7 @@ public function rules(): array 'job_title' => ['sometimes', 'nullable', 'string', 'max:256'], 'state' => ['sometimes', 'required', new Enum(EmployeeStateEnum::class)], 'positions' => ['sometimes', 'array'], - 'positions.*.slug' => ['sometimes', 'string'], + 'positions.*.code' => ['sometimes', 'string'], 'positions.*.scopes' => ['sometimes', 'array'], 'positions.*.scopes.warehouses.slug.*' => ['sometimes', Rule::exists('warehouses', 'slug')->where('organisation_id', $this->organisation->id)], 'positions.*.scopes.fulfilments.slug.*' => ['sometimes', Rule::exists('fulfilments', 'slug')->where('organisation_id', $this->organisation->id)], diff --git a/app/Actions/HumanResources/JobPosition/SyncUserJobPositions.php b/app/Actions/HumanResources/JobPosition/SyncUserJobPositions.php index 76b45528be..7e3c7a2694 100644 --- a/app/Actions/HumanResources/JobPosition/SyncUserJobPositions.php +++ b/app/Actions/HumanResources/JobPosition/SyncUserJobPositions.php @@ -28,8 +28,11 @@ public function handle(User $user, array $jobPositions): void $user->pseudoJobPositions()->detach($removeJobPositions); + + foreach ($newJobPositionsIds as $jobPositionId) { - $pseudJobPositionsData = [ + + $pseudoJobPositionsData = [ 'group_id' => $user->group_id, 'scopes' => $jobPositions[$jobPositionId]['scopes'], 'organisation_id' => $jobPositions[$jobPositionId]['organisation_id'] @@ -38,7 +41,7 @@ public function handle(User $user, array $jobPositions): void $user->pseudoJobPositions()->attach( [ - $jobPositionId => $pseudJobPositionsData + $jobPositionId => $pseudoJobPositionsData ], ); } diff --git a/app/Actions/SysAdmin/Guest/StoreGuest.php b/app/Actions/SysAdmin/Guest/StoreGuest.php index 591dae8aa4..6df02a8cb4 100644 --- a/app/Actions/SysAdmin/Guest/StoreGuest.php +++ b/app/Actions/SysAdmin/Guest/StoreGuest.php @@ -139,7 +139,7 @@ public function rules(): array 'email' => ['sometimes', 'nullable', 'email'], 'positions' => ['sometimes', 'array'], - 'positions.*.slug' => ['sometimes', 'string', Rule::exists('job_positions', 'slug')->where('group_id', $this->group->id)], + 'positions.*.code' => ['sometimes', 'string'], 'positions.*.scopes' => ['sometimes', 'array'], 'positions.*.scopes.organisations.slug.*' => ['sometimes', Rule::exists('organisations', 'slug')->where('group_id', $this->group->id)], diff --git a/app/Actions/SysAdmin/User/SyncRolesFromJobPositions.php b/app/Actions/SysAdmin/User/SyncRolesFromJobPositions.php index 5e01cf8f4c..8fdc3c3493 100644 --- a/app/Actions/SysAdmin/User/SyncRolesFromJobPositions.php +++ b/app/Actions/SysAdmin/User/SyncRolesFromJobPositions.php @@ -25,17 +25,27 @@ public function handle(User $user): void $roles = []; + + foreach ($user->employees as $employee) { foreach ($employee->jobPositions as $jobPosition) { + + + $roles = $this->getRoles($roles, $jobPosition); } } + foreach ($user->pseudoJobPositions as $jobPosition) { + + $roles = $this->getRoles($roles, $jobPosition); } + print_r($roles); + $user->syncRoles($roles); if ($user->roles()->where('name', RolesEnum::GROUP_ADMIN->value)->exists()) { @@ -81,6 +91,10 @@ public function handle(User $user): void private function getRoles($roles, JobPosition $jobPosition): array { + + // print '>>>>>>>'.$jobPosition->code." <<<\n"; + // print_r($jobPosition->pivot->scopes); + $jobPosition->refresh(); if ($jobPosition->scope == JobPositionScopeEnum::ORGANISATION || $jobPosition->scope == JobPositionScopeEnum::GROUP) { $roles = array_merge($roles, $jobPosition->roles()->pluck('id')->all()); @@ -96,9 +110,14 @@ private function getRoles($roles, JobPosition $jobPosition): array private function getRolesFromJobPosition(JobPosition $jobPosition): array { + + //print '*******'.$jobPosition->code>" <<<\n"; + $roles = []; $jobPosition->refresh(); foreach ($jobPosition->roles as $role) { + + if (in_array($role->scope_id, $jobPosition->pivot->scopes[$role->scope_type])) { $roles[] = $role->id; } diff --git a/app/Actions/SysAdmin/User/UI/EditUser.php b/app/Actions/SysAdmin/User/UI/EditUser.php index 830ad1220a..44ea5f1228 100644 --- a/app/Actions/SysAdmin/User/UI/EditUser.php +++ b/app/Actions/SysAdmin/User/UI/EditUser.php @@ -167,7 +167,7 @@ class_basename(Warehouse::class) => Organisation::all()->map(function (Organisat 'organisation_list' => $organisationList, 'updateRoute' => [ 'method' => 'patch', - "name" => "grp.models.user.other-organisation.update", + "name" => "grp.models.user.pseudo-job-positions.update", 'parameters' => [ 'user' => $user->id ] diff --git a/app/Actions/HumanResources/Employee/UpdateEmployeeOtherOrganisationJobPositions.php b/app/Actions/SysAdmin/User/UpdateUsersPseudoJobPositions.php similarity index 66% rename from app/Actions/HumanResources/Employee/UpdateEmployeeOtherOrganisationJobPositions.php rename to app/Actions/SysAdmin/User/UpdateUsersPseudoJobPositions.php index 013d1ee82d..baeab50ab2 100644 --- a/app/Actions/HumanResources/Employee/UpdateEmployeeOtherOrganisationJobPositions.php +++ b/app/Actions/SysAdmin/User/UpdateUsersPseudoJobPositions.php @@ -1,25 +1,25 @@ - * Created: Fri, 26 Aug 2022 00:49:45 Malaysia Time, Kuala Lumpur, Malaysia - * Copyright (c) 2022, Raul A Perusquia F + * Author: Raul Perusquia + * Created: Mon, 30 Sept 2024 18:51:48 Malaysia Time, Kuala Lumpur, Malaysia + * Copyright (c) 2024, Raul A Perusquia Flores */ -namespace App\Actions\HumanResources\Employee; +namespace App\Actions\SysAdmin\User; use App\Actions\GrpAction; +use App\Actions\HumanResources\Employee\HasPositionsRules; use App\Actions\HumanResources\Employee\Traits\HasEmployeePositionGenerator; -use App\Actions\HumanResources\JobPosition\SyncEmployeeOtherOrganisationJobPositions; +use App\Actions\HumanResources\JobPosition\SyncUserJobPositions; use App\Actions\Traits\WithActionUpdate; use App\Http\Resources\HumanResources\EmployeeResource; use App\Models\HumanResources\Employee; use App\Models\SysAdmin\Organisation; use App\Models\SysAdmin\User; -use Illuminate\Support\Arr; use Illuminate\Validation\Rule; use Lorisleiva\Actions\ActionRequest; -class UpdateEmployeeOtherOrganisationJobPositions extends GrpAction +class UpdateUsersPseudoJobPositions extends GrpAction { use WithActionUpdate; use HasPositionsRules; @@ -31,17 +31,15 @@ class UpdateEmployeeOtherOrganisationJobPositions extends GrpAction private User $user; - private Organisation $otherOrganisation; - public function handle(User $user, Organisation $otherOrganisation, array $modelData): User + private Organisation $organisation; + + public function handle(User $user, Organisation $organisation, array $modelData): User { - foreach ($user->employees as $employee) { - if (Arr::exists($modelData, 'positions')) { - $jobPositions = $this->generatePositions($employee->organisation, $modelData); - //SyncEmployeeOtherOrganisationJobPositions::run($employee, $otherOrganisation, $jobPositions); - Arr::forget($modelData, 'positions'); - } - } + $jobPositions = $this->generatePositions($organisation, $modelData); + + + SyncUserJobPositions::run($user, $jobPositions); $user->refresh(); @@ -62,11 +60,11 @@ public function rules(): array { return [ 'positions' => ['sometimes', 'array'], - 'positions.*.slug' => ['sometimes', 'string'], + 'positions.*.code' => ['sometimes', 'string'], 'positions.*.scopes' => ['sometimes', 'array'], - 'positions.*.scopes.warehouses.slug.*' => ['sometimes', Rule::exists('warehouses', 'slug')->where('organisation_id', $this->otherOrganisation->id)], - 'positions.*.scopes.fulfilments.slug.*' => ['sometimes', Rule::exists('fulfilments', 'slug')->where('organisation_id', $this->otherOrganisation->id)], - 'positions.*.scopes.shops.slug.*' => ['sometimes', Rule::exists('shops', 'slug')->where('organisation_id', $this->otherOrganisation->id)], + 'positions.*.scopes.warehouses.slug.*' => ['sometimes', Rule::exists('warehouses', 'slug')->where('organisation_id', $this->organisation->id)], + 'positions.*.scopes.fulfilments.slug.*' => ['sometimes', Rule::exists('fulfilments', 'slug')->where('organisation_id', $this->organisation->id)], + 'positions.*.scopes.shops.slug.*' => ['sometimes', Rule::exists('shops', 'slug')->where('organisation_id', $this->organisation->id)], ]; } @@ -74,7 +72,7 @@ public function action(User $user, Organisation $otherOrganisation, $modelData): { $this->asAction = true; $this->user = $user; - $this->otherOrganisation = $otherOrganisation; + $this->organisation = $otherOrganisation; $this->initialisation($user->group, $modelData); @@ -84,7 +82,7 @@ public function action(User $user, Organisation $otherOrganisation, $modelData): public function prepareForValidation(ActionRequest $request): void { $this->preparePositionsForValidation(); - if ($this->user->employees()->count() === 0) { + if ($this->user->employees()->where('user_has_models.organisation_id', $this->organisation->id)->exists()) { abort(419); } } @@ -92,7 +90,7 @@ public function prepareForValidation(ActionRequest $request): void public function asController(User $user, Organisation $organisation, ActionRequest $request): User { $this->user = $user; - $this->otherOrganisation = $organisation; + $this->organisation = $organisation; $this->initialisation(app('group'), $request); diff --git a/app/Actions/Transfers/Aurora/FetchAuroraEmployees.php b/app/Actions/Transfers/Aurora/FetchAuroraEmployees.php index 9c19585190..3895e9efc9 100644 --- a/app/Actions/Transfers/Aurora/FetchAuroraEmployees.php +++ b/app/Actions/Transfers/Aurora/FetchAuroraEmployees.php @@ -38,7 +38,11 @@ public function handle(SourceOrganisationService $organisationSource, int $organ if ($employeeData = $organisationSource->fetchEmployee($organisationSourceId)) { - if ($employee = Employee::where('source_id', $employeeData['employee']['source_id'])->first()) { + + + $sourceId = $employeeData['employee']['source_id']; + + if ($employee = Employee::where('source_id', $sourceId)->first()) { $employee = UpdateEmployee::make()->action( employee: $employee, modelData: $employeeData['employee'], @@ -61,20 +65,18 @@ public function handle(SourceOrganisationService $organisationSource, int $organ UpdateEmployeeWorkingHours::run($employee, $employeeData['working_hours']); - - if (Arr::has($employeeData, 'user')) { + if (Arr::has($employeeData, 'user.source_id')) { $updateUser = true; $user = $employee->getUser(); if (!$user) { - $user = $employee->group->users()->where('username', $employeeData['user']['username']) - ->first(); + $user = $employee->group->users()->where('username', $employeeData['user']['username'])->first(); if ($user) { $updateUser = false; $user = AttachEmployeeToUser::make()->action( $user, $employee, [ - 'status' => $employeeData['user']['user_model_status'], + 'status' => $employeeData['user']['user_model_status'], 'source_id' => $employeeData['user']['source_id'], ] ); diff --git a/app/Actions/Transfers/Aurora/FetchAuroraUsers.php b/app/Actions/Transfers/Aurora/FetchAuroraUsers.php index c58b6834b2..cc2e37fdd8 100644 --- a/app/Actions/Transfers/Aurora/FetchAuroraUsers.php +++ b/app/Actions/Transfers/Aurora/FetchAuroraUsers.php @@ -10,6 +10,7 @@ use App\Actions\SysAdmin\User\AttachEmployeeToUser; use App\Actions\SysAdmin\User\StoreUser; use App\Actions\SysAdmin\User\UpdateUser; +use App\Actions\SysAdmin\User\UpdateUsersPseudoJobPositions; use App\Models\SysAdmin\User; use App\Transfers\SourceOrganisationService; use Exception; @@ -24,6 +25,7 @@ class FetchAuroraUsers extends FetchAuroraAction public function handle(SourceOrganisationService $organisationSource, int $organisationSourceId): ?User { if ($userData = $organisationSource->fetchUser($organisationSourceId)) { + if ($userData['user']) { if ($user = User::withTrashed()->where('source_id', $userData['user']['source_id'])->first()) { try { @@ -42,40 +44,39 @@ public function handle(SourceOrganisationService $organisationSource, int $organ } } - if (Db::table('user_has_model') + if ($foundUserData=Db::table('user_has_models') ->select('user_id') ->where('group_id', $organisationSource->getOrganisation()->group_id) ->where('source_id', $userData['user']['source_id'])->first()) { - return User::where('id', $user->id)->first(); - } + $user=User::where('id', $foundUserData->user_id)->first(); - $user = $organisationSource->getOrganisation()->group->users()->where('username', $userData['user']['username'])->first(); - if (!$user) { + return $user; + } - } else { - // User found - - if ($userData['parent_type'] == 'Staff') { - $user = AttachEmployeeToUser::run( - $user, - $userData['parent'], - [ - 'status' => $userData['user']['user_model_status'], - 'source_id' => $userData['user']['source_id'], - ] - ); - $this->updateAurora($user); - return $user; - } + + $user = $organisationSource->getOrganisation()->group->users()->where('username', $userData['user']['username'])->first(); - } + if ($user) { + $user = UpdateUsersPseudoJobPositions::make()->action( + $user, + $organisationSource->getOrganisation(), + [ + 'positions' => $userData['user']['positions'] + ] + ); + } else { + // User not found + // todo add as a guest + //dd($userData['user']); + } + // elseif($userData['parent_type']=='Staff'){ // try { @@ -98,10 +99,7 @@ public function handle(SourceOrganisationService $organisationSource, int $organ } - $sourceData = explode(':', $user->source_id); - DB::connection('aurora')->table('User Deleted Dimension') - ->where('User Key', $sourceData[1]) - ->update(['aiku_id' => $user->id]); + return $user; } @@ -113,7 +111,6 @@ public function handle(SourceOrganisationService $organisationSource, int $organ private function updateAurora($user): void { - $sourceData = explode(':', $user->source_id); DB::connection('aurora')->table('User Deleted Dimension') ->where('User Key', $sourceData[1]) diff --git a/app/Enums/SysAdmin/Authorisation/RolesEnum.php b/app/Enums/SysAdmin/Authorisation/RolesEnum.php index 67016962d3..4ad9040ef5 100644 --- a/app/Enums/SysAdmin/Authorisation/RolesEnum.php +++ b/app/Enums/SysAdmin/Authorisation/RolesEnum.php @@ -141,8 +141,8 @@ public function label(): string RolesEnum::SHOPKEEPER_SUPERVISOR => __('Shopkeeper supervisor'), RolesEnum::DISCOUNTS_CLERK => __('Discounts clerk'), RolesEnum::DISCOUNTS_SUPERVISOR => __('Discounts supervisor'), - RolesEnum::MARKETING_CLERK => __('Discounts clerk'), - RolesEnum::MARKETING_SUPERVISOR => __('Discounts supervisor'), + RolesEnum::MARKETING_CLERK => __('Marketing clerk'), + RolesEnum::MARKETING_SUPERVISOR => __('Marketing supervisor'), RolesEnum::MANUFACTURING_ADMIN => __('Manufacturing admin'), RolesEnum::MANUFACTURING_ORCHESTRATOR => __('Manufacturing orchestrator'), RolesEnum::MANUFACTURING_LINE_MANAGER => __('Manufacturing line manager'), diff --git a/app/Models/SysAdmin/User.php b/app/Models/SysAdmin/User.php index d4f2ad48bf..196f26ae5a 100644 --- a/app/Models/SysAdmin/User.php +++ b/app/Models/SysAdmin/User.php @@ -254,7 +254,7 @@ public function tasks(): MorphToMany public function pseudoJobPositions(): BelongsToMany { return $this->belongsToMany(JobPosition::class, 'user_has_pseudo_job_positions')->withTimestamps() - ->using(UserHasPseudoJobPositions::class); + ->using(UserHasPseudoJobPositions::class)->withPivot(['scopes']); } } diff --git a/app/Transfers/Aurora/FetchAuroraDeletedEmployee.php b/app/Transfers/Aurora/FetchAuroraDeletedEmployee.php index 1c3e5e00f5..277df5c915 100644 --- a/app/Transfers/Aurora/FetchAuroraDeletedEmployee.php +++ b/app/Transfers/Aurora/FetchAuroraDeletedEmployee.php @@ -16,6 +16,9 @@ protected function parseModel(): void { $auDeletedModel = json_decode(gzuncompress($this->auroraModelData->{'Staff Deleted Metadata'})); + if($auDeletedModel->data->{'Staff Type'} == 'Contractor'){ + return; + } $this->parsedData['employee'] = [ diff --git a/app/Transfers/Aurora/FetchAuroraEmployee.php b/app/Transfers/Aurora/FetchAuroraEmployee.php index 0f56d98597..067d4285c9 100644 --- a/app/Transfers/Aurora/FetchAuroraEmployee.php +++ b/app/Transfers/Aurora/FetchAuroraEmployee.php @@ -25,151 +25,120 @@ public function fetch(int $id): ?array $this->auroraModelData = $this->fetchData($id); if ($this->auroraModelData) { - $this->parseModel(); - $this->parseUser(); - $this->parsePhoto(); - $this->parsePositions(); - } - + if ($this->auroraModelData->{'Staff Type'} == 'Contractor') { + return null; + } - return $this->parsedData; - } + $data = []; + $errors = []; + if ($this->parseDate($this->auroraModelData->{'Staff Valid From'}) == '') { + $errors = [ + 'missing' => ['created_at', 'employment_start_at'] + ]; + } - protected function parsePositions(): void - { - $rawJobPositions = $this->parseJobPositions(); - $shops = []; - if (Arr::has($this->parsedData, 'user')) { - $userSourceData = explode(':', $this->parsedData['user']['source_id']); - $shops = $this->getAuroraUserShopScopes($userSourceData[1]); - } - - $positions = []; - foreach ($rawJobPositions as $jobPositionSlug) { - $jobPosition = JobPosition::where('slug', $jobPositionSlug)->firstOrFail(); - $scopes = []; - $add = true; - if ($jobPosition->scope == JobPositionScopeEnum::SHOPS) { - $scopes = ['Shop' => $shops]; - if (count($shops) == 0) { - $add = false; - } - } elseif ($jobPosition->scope == JobPositionScopeEnum::WAREHOUSES) { - $scopes = ['Warehouse' => $this->organisation->warehouses()->pluck('id')->all()]; - } elseif ($jobPosition->scope == JobPositionScopeEnum::PRODUCTIONS) { - $scopes = ['Production' => $this->organisation->productions()->pluck('id')->all()]; - } elseif ($jobPosition->scope == JobPositionScopeEnum::ORGANISATION) { - $scopes = ['Organisation' => [$this->organisation->id]]; + $working_hours = json_decode($this->auroraModelData->{'Staff Working Hours'}, true); + if ($working_hours) { + $working_hours['week_distribution'] = array_change_key_case( + json_decode($this->auroraModelData->{'Staff Working Hours Per Week Metadata'}, true) + ); } + $workingHours = json_decode($this->auroraModelData->{'Staff Working Hours'}, true); + $weekDistribution = json_decode($this->auroraModelData->{'Staff Working Hours Per Week Metadata'}, true); - if ($add) { - $positions[] = [ - 'slug' => $jobPosition->slug, - 'scopes' => $scopes - ]; + if ($workingHours and $weekDistribution) { + $workingHours['week_distribution'] = array_change_key_case($weekDistribution); } - } - $this->parsedData['employee']['positions'] = $positions; - } + $salary = json_decode($this->auroraModelData->{'Staff Salary'}, true); + if ($salary) { + $salary = array_change_key_case($salary); + } + if ($this->auroraModelData->{'Staff Address'}) { + $data['address'] = $this->auroraModelData->{'Staff Address'}; + } - protected function parseModel(): void - { - $data = []; - $errors = []; - if ($this->parseDate($this->auroraModelData->{'Staff Valid From'}) == '') { - $errors = [ - 'missing' => ['created_at', 'employment_start_at'] - ]; - } + $this->parsedData['working_hours'] = $working_hours ?? []; - $working_hours = json_decode($this->auroraModelData->{'Staff Working Hours'}, true); - if ($working_hours) { - $working_hours['week_distribution'] = array_change_key_case( - json_decode($this->auroraModelData->{'Staff Working Hours Per Week Metadata'}, true) - ); - } - $workingHours = json_decode($this->auroraModelData->{'Staff Working Hours'}, true); - $weekDistribution = json_decode($this->auroraModelData->{'Staff Working Hours Per Week Metadata'}, true); + if ($this->auroraModelData->{'Staff ID'}) { + $workerNumber = preg_replace('/[()]/', '', $this->auroraModelData->{'Staff ID'}); + $workerNumber = preg_replace('/\s+/', '-', $workerNumber); + } else { + $workerNumber = $this->auroraModelData->{'Staff Key'}; + } - if ($workingHours and $weekDistribution) { - $workingHours['week_distribution'] = array_change_key_case($weekDistribution); - } + $createdAt = $this->auroraModelData->{'Staff Valid From'}; + if (!$createdAt) { + $firstAction = DB::connection('aurora')->table('History Dimension') + ->select('History Date as date') + ->where('Subject', 'Staff') + ->where('Subject Key', $this->auroraModelData->{'Staff Key'}) + ->orderBy('History Date')->first(); - $salary = json_decode($this->auroraModelData->{'Staff Salary'}, true); - if ($salary) { - $salary = array_change_key_case($salary); - } + if ($firstAction) { + $createdAt = $firstAction->date; + } + } - if ($this->auroraModelData->{'Staff Address'}) { - $data['address'] = $this->auroraModelData->{'Staff Address'}; - } - $this->parsedData['working_hours'] = $working_hours ?? []; + $this->parsedData['employee'] = [ + 'alias' => $this->auroraModelData->{'Staff Alias'}, + 'contact_name' => $this->auroraModelData->{'Staff Name'}, + 'email' => $this->auroraModelData->{'Staff Email'} ?: null, + 'phone' => $this->auroraModelData->{'Staff Telephone'} ?: null, + 'identity_document_number' => $this->auroraModelData->{'Staff Official ID'} ?: null, + 'date_of_birth' => $this->parseDate($this->auroraModelData->{'Staff Birthday'}), + 'worker_number' => $workerNumber, + 'emergency_contact' => $this->auroraModelData->{'Staff Next of Kind'} ?: null, + 'job_title' => $this->auroraModelData->{'Staff Job Title'} ?: null, + 'salary' => $salary, + 'employment_start_at' => $this->parseDate($this->auroraModelData->{'Staff Valid From'}), + 'employment_end_at' => $this->parseDate($this->auroraModelData->{'Staff Valid To'}), + 'type' => Str::snake($this->auroraModelData->{'Staff Type'}, '-'), + 'state' => match ($this->auroraModelData->{'Staff Currently Working'}) { + 'No' => EmployeeStateEnum::LEFT, + default => EmployeeStateEnum::WORKING + }, + 'data' => $data, + 'errors' => $errors, + 'source_id' => $this->organisation->id.':'.$this->auroraModelData->{'Staff Key'}, + 'fetched_at' => now(), + 'last_fetched_at' => now() + ]; + if ($createdAt) { + $this->parsedData['employee']['created_at'] = $createdAt; + } - if ($this->auroraModelData->{'Staff ID'}) { - $workerNumber = preg_replace('/[()]/', '', $this->auroraModelData->{'Staff ID'}); - $workerNumber = preg_replace('/\s+/', '-', $workerNumber); - } else { - $workerNumber = $this->auroraModelData->{'Staff Key'}; - } + $this->parsedData['user'] = $this->parseUser(); + $this->parsedData['photo'] =$this->parseUserPhoto(); - $createdAt = $this->auroraModelData->{'Staff Valid From'}; - if (!$createdAt) { + $userId = null; - $firstAction = DB::connection('aurora')->table('History Dimension') - ->select('History Date as date') - ->where('Subject', 'Staff') - ->where('Subject Key', $this->auroraModelData->{'Staff Key'}) - ->orderBy('History Date')->first(); - if ($firstAction) { - $createdAt = $firstAction->date; + + if (Arr::has($this->parsedData, 'user.source_id')) { + $userSourceData = explode(':', $this->parsedData['user']['source_id']); + $userId = $userSourceData[1]; } + $this->parsedData['employee']['positions'] = $this->parsePositions($userId); } - $this->parsedData['employee'] = [ - 'alias' => $this->auroraModelData->{'Staff Alias'}, - 'contact_name' => $this->auroraModelData->{'Staff Name'}, - 'email' => $this->auroraModelData->{'Staff Email'} ?: null, - 'phone' => $this->auroraModelData->{'Staff Telephone'} ?: null, - 'identity_document_number' => $this->auroraModelData->{'Staff Official ID'} ?: null, - 'date_of_birth' => $this->parseDate($this->auroraModelData->{'Staff Birthday'}), - 'worker_number' => $workerNumber, - 'emergency_contact' => $this->auroraModelData->{'Staff Next of Kind'} ?: null, - 'job_title' => $this->auroraModelData->{'Staff Job Title'} ?: null, - 'salary' => $salary, - 'employment_start_at' => $this->parseDate($this->auroraModelData->{'Staff Valid From'}), - 'employment_end_at' => $this->parseDate($this->auroraModelData->{'Staff Valid To'}), - 'type' => Str::snake($this->auroraModelData->{'Staff Type'}, '-'), - 'state' => match ($this->auroraModelData->{'Staff Currently Working'}) { - 'No' => EmployeeStateEnum::LEFT, - default => EmployeeStateEnum::WORKING - }, - 'data' => $data, - 'errors' => $errors, - 'source_id' => $this->organisation->id.':'.$this->auroraModelData->{'Staff Key'}, - 'fetched_at' => now(), - 'last_fetched_at' => now() - ]; - - if ($createdAt) { - $this->parsedData['employee']['created_at'] = $createdAt; - } + return $this->parsedData;; } - private function parseUser(): void + + private function parseUser(): ?array { $auroraUserData = DB::connection('aurora') ->table('User Dimension as users') @@ -201,7 +170,7 @@ private function parseUser(): void } - $this->parsedData['user'] = [ + return [ 'source_id' => $this->organisation->id.':'.$auroraUserData->{'User Key'}, 'username' => Str::kebab(Str::lower($username)), 'status' => true, @@ -214,47 +183,8 @@ private function parseUser(): void 'last_fetched_at' => now() ]; } - } - - - private function parsePhoto(): void - { - $profileImages = $this->getModelImagesCollection( - 'Staff', - $this->auroraModelData->{'Staff Key'} - )->map(function ($auroraImage) { - return $this->fetchImage($auroraImage); - }); - $this->parsedData['photo'] = $profileImages->toArray(); - } - - private function parseJobPositions(): array - { - $jobPositions = $this->organisation->jobPositions()->pluck('id', 'slug')->all(); - - $jobPositionCodes = []; - foreach (explode(',', $this->auroraModelData->staff_positions) as $sourceStaffPosition) { - $jobPositionCodes = array_merge( - $jobPositionCodes, - explode( - ',', - $this->parseJobPosition( - isSupervisor: $this->auroraModelData->{'Staff Is Supervisor'} == 'Yes', - sourceCode: $sourceStaffPosition - ) - ) - ); - } - $jobPositionIds = []; - - foreach ($jobPositionCodes as $jobPositionCode) { - if (array_key_exists($jobPositionCode, $jobPositions)) { - $jobPositionIds[$jobPositions[$jobPositionCode]] = $jobPositionCode; - } - } - - return $jobPositionIds; + return null; } @@ -262,52 +192,11 @@ protected function fetchData($id): object|null { return DB::connection('aurora') ->table('Staff Dimension') + ->leftJoin('User Dimension', 'Staff Key', 'User Parent Key') ->selectRaw('*,(select GROUP_CONCAT(`Role Code`) from `Staff Role Bridge` SRB where (SRB.`Staff Key`=`Staff Dimension`.`Staff Key`) ) as staff_positions') + ->selectRaw('(select GROUP_CONCAT(`User Group Key`) from `User Group User Bridge` UGUB where (UGUB.`User Key`=`User Dimension`.`User Key`) ) as staff_groups') ->where('Staff Key', $id)->first(); } - protected function parseJobPosition($isSupervisor, $sourceCode): string - { - return match ($sourceCode) { - 'WAHM' => 'wah-m', - 'WAHSK' => 'wah-sk', - 'WAHSC' => 'wah-sc', - 'PICK' => 'dist-pik,dist-pak', - 'OHADM' => 'dist-m', - 'PRODM' => 'prod-m', - 'PRODO' => 'prod-w', - 'CUSM' => 'cus-m', - 'CUS' => 'cus-c', - 'MRK' => $isSupervisor ? 'mrk-m' : 'mrk-c', - 'WEB' => $isSupervisor ? 'shk-m' : 'shk-c', - 'HR' => $isSupervisor ? 'hr-m' : 'hr-c', - default => strtolower($sourceCode) - }; - } - - protected function getAuroraUserShopScopes($userID): array - { - $shops = []; - - - foreach ( - DB::connection('aurora') - ->table('User Right Scope Bridge') - ->where('User Key', $userID)->get() as $rawScope - ) { - if ($rawScope->{'Scope'} == 'Store') { - $shop = $this->parseShop($this->organisation->id.':'.$rawScope->{'Scope Key'}); - $shops[$shop->id] = $shop->id; - } - if ($rawScope->{'Scope'} == 'Website') { - $website = $this->parseWebsite($this->organisation->id.':'.$rawScope->{'Scope Key'}); - - $shops[$website->shop_id] = $website->shop_id; - } - } - - - return array_keys($shops); - } } diff --git a/app/Transfers/Aurora/FetchAuroraUser.php b/app/Transfers/Aurora/FetchAuroraUser.php index 90e404797c..f5296a478a 100644 --- a/app/Transfers/Aurora/FetchAuroraUser.php +++ b/app/Transfers/Aurora/FetchAuroraUser.php @@ -14,11 +14,17 @@ class FetchAuroraUser extends FetchAurora { + use WithAuroraImages; + protected function parseModel(): void { + if (!in_array($this->auroraModelData->{'User Type'}, ['Staff', 'Contractor'])) { + return; + } + $parent = null; $this->parsedData['parent_type'] = 'Guest'; - if ($this->auroraModelData->{'User Parent Type'} == 'Staff') { + if ($this->auroraModelData->{'User Type'} == 'Staff') { $this->parsedData['parent_type'] = 'Staff'; $parent = $this->parseEmployee($this->organisation->id.':'.$this->auroraModelData->{'User Parent Key'}); @@ -45,6 +51,8 @@ protected function parseModel(): void $this->parsedData['parent'] = $parent; + + $this->parsedData['user'] = [ @@ -57,8 +65,11 @@ protected function parseModel(): void 'language_id' => $this->parseLanguageID($this->auroraModelData->{'User Preferred Locale'}), 'reset_password' => false, 'fetched_at' => now(), - 'last_fetched_at' => now() + 'last_fetched_at' => now(), + 'positions' => $this->parsePositions($this->auroraModelData->{'User Key'}) ]; + + $this->parseUserPhoto(); } @@ -66,6 +77,10 @@ protected function fetchData($id): object|null { return DB::connection('aurora') ->table('User Dimension') + ->leftJoin('Staff Dimension','Staff Key','User Parent Key') + ->selectRaw('*,(select GROUP_CONCAT(`Role Code`) from `Staff Role Bridge` SRB where (SRB.`Staff Key`=`Staff Dimension`.`Staff Key`) ) as staff_positions') + ->selectRaw('(select GROUP_CONCAT(`User Group Key`) from `User Group User Bridge` UGUB where (UGUB.`User Key`=`User Dimension`.`User Key`) ) as staff_groups') + ->where('User Key', $id)->first(); } } diff --git a/app/Transfers/Aurora/WithAuroraParsers.php b/app/Transfers/Aurora/WithAuroraParsers.php index 2fde239847..b8f6ebe002 100644 --- a/app/Transfers/Aurora/WithAuroraParsers.php +++ b/app/Transfers/Aurora/WithAuroraParsers.php @@ -46,6 +46,7 @@ use App\Actions\Transfers\Aurora\FetchAuroraWebsites; use App\Enums\Catalogue\ProductCategory\ProductCategoryTypeEnum; use App\Enums\Helpers\TaxNumber\TaxNumberStatusEnum; +use App\Enums\HumanResources\JobPosition\JobPositionScopeEnum; use App\Models\Accounting\OrgPaymentServiceProvider; use App\Models\Accounting\Payment; use App\Models\Accounting\PaymentAccount; @@ -69,6 +70,7 @@ use App\Models\Helpers\Timezone; use App\Models\HumanResources\ClockingMachine; use App\Models\HumanResources\Employee; +use App\Models\HumanResources\JobPosition; use App\Models\Inventory\Location; use App\Models\Inventory\OrgStock; use App\Models\Inventory\Warehouse; @@ -89,6 +91,7 @@ use Exception; use Illuminate\Support\Arr; use Illuminate\Support\Carbon; +use Illuminate\Support\Facades\DB; use Illuminate\Support\Str; trait WithAuroraParsers @@ -124,8 +127,8 @@ protected function parseTaxNumber(?string $number, ?int $countryID, array $rawDa 'data' => [], 'valid' => Arr::get($rawData, 'Customer Tax Number Valid') == 'Yes', 'status' => match (Arr::get($rawData, 'Customer Tax Number Valid')) { - 'Yes' => TaxNumberStatusEnum::VALID, - 'No' => TaxNumberStatusEnum::INVALID, + 'Yes' => TaxNumberStatusEnum::VALID, + 'No' => TaxNumberStatusEnum::INVALID, default => TaxNumberStatusEnum::UNKNOWN } @@ -172,7 +175,7 @@ protected function parseLanguageID($locale): int|null 'code', match ($locale) { 'zh_CN.UTF-8' => 'zh-CN', - default => substr($locale, 0, 2) + default => substr($locale, 0, 2) } )->first()->id; } catch (Exception) { @@ -426,7 +429,7 @@ public function parseOrgStock($sourceId): ?OrgStock $sourceData = explode(':', $sourceId); if (!$orgStock) { - $res = FetchAuroraStocks::run($this->organisationSource, $sourceData[1]); + $res = FetchAuroraStocks::run($this->organisationSource, $sourceData[1]); $orgStock = $res['orgStock']; } @@ -524,8 +527,8 @@ public function parsePaymentAccount($sourceId): ?PaymentAccount public function parseEmployee($sourceId): ?Employee { - $employee = Employee::withTrashed()->where('source_id', $sourceId)->first(); - $sourceData = explode(':', $sourceId); + $employee = Employee::withTrashed()->where('source_id', $sourceId)->first(); + $sourceData = explode(':', $sourceId); if (!$employee) { $employee = FetchAuroraEmployees::run($this->organisationSource, $sourceData[1]); } @@ -646,7 +649,6 @@ public function parseShippingZoneSchema($sourceId): ShippingZoneSchema public function parseShippingZone($sourceId): ?ShippingZone { - $shippingZone = ShippingZone::where('source_id', $sourceId)->first(); if (!$shippingZone) { $sourceData = explode(':', $sourceId); @@ -660,8 +662,8 @@ public function parseCharge($sourceId): ?Charge { $charge = Charge::where('source_id', $sourceId)->first(); if (!$charge) { - $sourceData = explode(':', $sourceId); - $charge = FetchAuroraCharges::run($this->organisationSource, $sourceData[1]); + $sourceData = explode(':', $sourceId); + $charge = FetchAuroraCharges::run($this->organisationSource, $sourceData[1]); } return $charge; @@ -693,9 +695,199 @@ public function parseOfferCampaign($sourceId): ?OfferCampaign return $offerCampaign; } + protected function parsePositions($userID): array + { + $rawJobPositions = $this->parseJobPositions(); + + + $shops = []; + + + if ($userID) { + $shops = $this->getAuroraUserShopScopes($userID); + } + + $positions = []; + foreach ($rawJobPositions as $jobPositionCode) { + /** @var JobPosition $jobPosition */ + $jobPosition = $this->organisation->jobPositions()->where('code', $jobPositionCode)->firstOrFail(); + $scopes = []; + $add = true; + if ($jobPosition->scope == JobPositionScopeEnum::SHOPS) { + $scopes = ['shops' => ['slug' => $shops]]; + if (count($shops) == 0) { + $add = false; + } + } elseif ($jobPosition->scope == JobPositionScopeEnum::WAREHOUSES) { + $scopes = [ + 'warehouses' => + [ + 'slug' => $this->organisation->warehouses()->pluck('slug')->all() + ] + ]; + } elseif ($jobPosition->scope == JobPositionScopeEnum::PRODUCTIONS) { + $scopes = [ + 'productions' => + [ + 'slug' => $this->organisation->productions()->pluck('slug')->all() + ] + ]; + } elseif ($jobPosition->scope == JobPositionScopeEnum::ORGANISATION) { + $scopes = ['organisations' => [$this->organisation->id]]; + } + + if ($add) { + $positions[] = [ + 'code' => $jobPosition->code, + 'scopes' => $scopes + ]; + } + } + + + return $positions; + } + + private function parseJobPositions(): array + { + $jobPositions = $this->organisation->jobPositions()->pluck('id', 'code')->all(); + + + $jobPositionCodes = []; + + + foreach (explode(',', $this->auroraModelData->staff_groups) as $sourceStaffGroups) { + $jobPositionCode = $this->parseStaffGroups( + isSupervisor: $this->auroraModelData->{'Staff Is Supervisor'} == 'Yes', + staffGroupKey: $sourceStaffGroups + ); + + if ($jobPositionCode) { + $jobPositionCodes = array_merge( + $jobPositionCodes, + explode( + ',', + $jobPositionCode + ) + ); + } + } + + + foreach (explode(',', $this->auroraModelData->staff_positions) as $sourceStaffPosition) { + $jobPositionCode = $this->parseJobPosition( + isSupervisor: $this->auroraModelData->{'Staff Is Supervisor'} == 'Yes', + sourceCode: $sourceStaffPosition + ); + if ($jobPositionCode) { + $jobPositionCodes = array_merge( + $jobPositionCodes, + explode( + ',', + $jobPositionCode + ) + ); + } + } + + + $jobPositionIds = []; + + foreach ($jobPositionCodes as $jobPositionCode) { + if (array_key_exists($jobPositionCode, $jobPositions)) { + $jobPositionIds[$jobPositions[$jobPositionCode]] = $jobPositionCode; + } + } + + return $jobPositionIds; + } + + protected function parseStaffGroups($isSupervisor, $staffGroupKey): ?string + { + return match ((int)$staffGroupKey) { + 1 => 'org-admin', + 6 => 'hr-c', + 20 => 'hr-m', + 8, 21, 28 => 'buy', + 7 => 'prod-m', + 4 => 'prod-c', + 3 => 'wah-sc', + 22 => 'wah-m', + 23 => $isSupervisor ? 'acc-m' : 'acc-c', + 24 => 'dist-pik', + 25 => 'dist-pak', + 16 => 'cus-m', + 2 => 'cus-c', + 18 => 'shk-m', + 9, 26 => 'shk-c', + 30 => 'mkt-m', + 29 => 'mkt-c', + 32 => 'ful-m', + default => null + }; + } + + + protected function parseJobPosition($isSupervisor, $sourceCode): string + { + return match ($sourceCode) { + 'WAHM' => 'wah-m', + 'WAHSK' => 'wah-sk', + 'WAHSC' => 'wah-sc', + 'PICK' => 'dist-pik,dist-pak', + 'OHADM' => 'dist-m', + 'PRODM' => 'prod-m', + 'PRODO' => 'prod-w', + 'CUSM' => 'cus-m', + 'CUS' => 'cus-c', + 'MRK' => $isSupervisor ? 'mrk-m' : 'mrk-c', + 'WEB' => $isSupervisor ? 'shk-m' : 'shk-c', + 'HR' => $isSupervisor ? 'hr-m' : 'hr-c', + default => strtolower($sourceCode) + }; + } + + protected function getAuroraUserShopScopes($userID): array + { + $shops = []; + + + foreach ( + DB::connection('aurora') + ->table('User Right Scope Bridge') + ->where('User Key', $userID)->get() as $rawScope + ) { + if ($rawScope->{'Scope'} == 'Store') { + $shop = $this->parseShop($this->organisation->id.':'.$rawScope->{'Scope Key'}); + $shops[$shop->slug] = true; + } + if ($rawScope->{'Scope'} == 'Website') { + $website = $this->parseWebsite($this->organisation->id.':'.$rawScope->{'Scope Key'}); + + $shops[$website->shop->slug] = true; + } + } + + + return array_keys($shops); + } + + + protected function parseUserPhoto(): array + { + $profileImages = $this->getModelImagesCollection( + 'Staff', + $this->auroraModelData->{'Staff Key'} + )->map(function ($auroraImage) { + return $this->fetchImage($auroraImage); + }); + return $profileImages->toArray(); + } + public function clearTextWithHtml($string): string { $string = preg_replace('##i', "\n", $string); + return strip_tags(html_entity_decode(htmlspecialchars_decode($string))); } diff --git a/app/Transfers/AuroraOrganisationService.php b/app/Transfers/AuroraOrganisationService.php index 818240228d..a8d55f8a64 100644 --- a/app/Transfers/AuroraOrganisationService.php +++ b/app/Transfers/AuroraOrganisationService.php @@ -25,6 +25,7 @@ use App\Transfers\Aurora\FetchAuroraDeletedStock; use App\Transfers\Aurora\FetchAuroraDeletedSupplier; use App\Transfers\Aurora\FetchAuroraDeletedSupplierProduct; +use App\Transfers\Aurora\FetchAuroraDeletedUser; use App\Transfers\Aurora\FetchAuroraDeliveryNote; use App\Transfers\Aurora\FetchAuroraDeliveryNoteTransaction; use App\Transfers\Aurora\FetchAuroraDepartment; @@ -71,6 +72,7 @@ use App\Transfers\Aurora\FetchAuroraTradeUnit; use App\Transfers\Aurora\FetchAuroraTradeUnitImages; use App\Transfers\Aurora\FetchAuroraTransaction; +use App\Transfers\Aurora\FetchAuroraUser; use App\Transfers\Aurora\FetchAuroraWarehouse; use App\Transfers\Aurora\FetchAuroraWarehouseArea; use App\Transfers\Aurora\FetchAuroraWebpage; diff --git a/config/blueprint/job_positions.php b/config/blueprint/job_positions.php index 0747d7b177..23c4f62950 100644 --- a/config/blueprint/job_positions.php +++ b/config/blueprint/job_positions.php @@ -148,7 +148,8 @@ 'scope' => JobPositionScopeEnum::SHOPS, 'roles' => [ RolesEnum::SHOPKEEPER_SUPERVISOR, - RolesEnum::WEBMASTER_SUPERVISOR + RolesEnum::WEBMASTER_SUPERVISOR, + RolesEnum::DISCOUNTS_SUPERVISOR ], 'organisation_types' => [ OrganisationTypeEnum::SHOP, @@ -162,7 +163,8 @@ 'department' => 'products', 'roles' => [ RolesEnum::SHOPKEEPER_CLERK, - RolesEnum::WEBMASTER_CLERK + RolesEnum::WEBMASTER_CLERK, + RolesEnum::DISCOUNTS_CLERK ], 'organisation_types' => [ OrganisationTypeEnum::SHOP, @@ -171,7 +173,7 @@ ], 'mrk-m' => [ 'code' => 'mrk-m', - 'name' => 'Deals supervisor', + 'name' => 'Marketing supervisor', 'scope' => JobPositionScopeEnum::SHOPS, 'department' => 'products', 'roles' => [ @@ -184,7 +186,7 @@ ], 'mrk-c' => [ 'code' => 'mrk-c', - 'name' => 'Deals clerk', + 'name' => 'Marketing clerk', 'scope' => JobPositionScopeEnum::SHOPS, 'department' => 'products', 'roles' => [ diff --git a/resources/js/Components/Forms/Fields/EmployeePosition.vue b/resources/js/Components/Forms/Fields/EmployeePosition.vue index 4aee6476b5..c484fc38db 100644 --- a/resources/js/Components/Forms/Fields/EmployeePosition.vue +++ b/resources/js/Components/Forms/Fields/EmployeePosition.vue @@ -298,7 +298,7 @@ const optionsJob = reactive({ mrk: { key: 'mrk', - department: trans("Discounts"), + department: trans("Marketing"), icon: "fal fa-bullhorn", scope: 'shop', subDepartment: [ diff --git a/routes/grp/web/models/sysAdmin/user.php b/routes/grp/web/models/sysAdmin/user.php index fd20847ea1..b4af671f09 100644 --- a/routes/grp/web/models/sysAdmin/user.php +++ b/routes/grp/web/models/sysAdmin/user.php @@ -5,11 +5,11 @@ * Copyright (c) 2024, Raul A Perusquia Flores */ -use App\Actions\HumanResources\Employee\UpdateEmployeeOtherOrganisationJobPositions; +use App\Actions\SysAdmin\User\UpdateUsersPseudoJobPositions; use App\Actions\SysAdmin\User\UpdateUser; use Illuminate\Support\Facades\Route; Route::name('user.')->prefix('user/{user:id}')->group(function () { Route::patch('', UpdateUser::class)->name('update'); - Route::patch('organisation/{organisation:id}', UpdateEmployeeOtherOrganisationJobPositions::class)->name('other-organisation.update')->withoutScopedBindings(); + Route::patch('organisation/{organisation:id}', UpdateUsersPseudoJobPositions::class)->name('pseudo-job-positions.updated')->withoutScopedBindings(); }); diff --git a/tests/Feature/SysAdminTest.php b/tests/Feature/SysAdminTest.php index 178ced2637..75be861177 100644 --- a/tests/Feature/SysAdminTest.php +++ b/tests/Feature/SysAdminTest.php @@ -16,7 +16,6 @@ use App\Actions\Helpers\ReindexSearch; use App\Actions\Helpers\TimeZone\UI\GetTimeZonesOptions; use App\Actions\HumanResources\Employee\StoreEmployee; -use App\Actions\HumanResources\Employee\UpdateEmployeeOtherOrganisationJobPositions; use App\Actions\SysAdmin\Admin\StoreAdmin; use App\Actions\SysAdmin\Group\HydrateGroup; use App\Actions\SysAdmin\Group\StoreGroup; @@ -27,6 +26,7 @@ use App\Actions\SysAdmin\Organisation\HydrateOrganisations; use App\Actions\SysAdmin\Organisation\StoreOrganisation; use App\Actions\SysAdmin\Organisation\UpdateOrganisation; +use App\Actions\SysAdmin\User\UpdateUsersPseudoJobPositions; use App\Actions\SysAdmin\User\UpdateUser; use App\Actions\SysAdmin\User\UpdateUserStatus; use App\Actions\SysAdmin\User\UserAddRoles; @@ -612,7 +612,7 @@ ->and($jobPosition1)->toBeInstanceOf(JobPosition::class); - $user = UpdateEmployeeOtherOrganisationJobPositions::make()->action( + $user = UpdateUsersPseudoJobPositions::make()->action( $user, $org2, [ From 4aedb7b12e462b450fe320cdb23587a333258a95 Mon Sep 17 00:00:00 2001 From: Raul Perusquia Date: Tue, 1 Oct 2024 15:32:54 +0800 Subject: [PATCH 2/8] working on refactoring hr (part 2) --- .../HumanResources/Employee/StoreEmployee.php | 28 +++--- .../Traits/HasEmployeePositionGenerator.php | 86 ------------------ .../Employee/UpdateEmployee.php | 28 +++--- .../Employee/ValidatePinEmployee.php | 5 +- .../JobPosition/SyncEmployeeJobPositions.php | 7 +- .../JobPosition/SyncUserJobPositions.php | 6 +- app/Actions/SysAdmin/Guest/StoreGuest.php | 30 ++++--- .../Hydrators/UserHydrateAuthorisedModels.php | 2 + app/Actions/SysAdmin/User/StoreUser.php | 8 +- .../User/SyncRolesFromJobPositions.php | 54 +++++++----- .../User/UpdateUsersPseudoJobPositions.php | 19 ++-- app/Actions/SysAdmin/User/UserAddRoles.php | 5 -- .../WithPreparePositionsForValidation.php} | 9 +- .../Traits/WithReorganisePositions.php | 87 +++++++++++++++++++ .../Transfers/Aurora/FetchAuroraEmployees.php | 1 - .../Transfers/Aurora/FetchAuroraUsers.php | 6 +- .../Aurora/FetchAuroraDeletedEmployee.php | 2 +- app/Transfers/Aurora/FetchAuroraEmployee.php | 21 +++-- app/Transfers/Aurora/FetchAuroraUser.php | 2 +- app/Transfers/Aurora/WithAuroraParsers.php | 35 ++++++-- ...22_08_25_050000_create_employees_table.php | 1 + 21 files changed, 244 insertions(+), 198 deletions(-) delete mode 100644 app/Actions/HumanResources/Employee/Traits/HasEmployeePositionGenerator.php rename app/Actions/{HumanResources/Employee/HasPositionsRules.php => Traits/WithPreparePositionsForValidation.php} (88%) create mode 100644 app/Actions/Traits/WithReorganisePositions.php diff --git a/app/Actions/HumanResources/Employee/StoreEmployee.php b/app/Actions/HumanResources/Employee/StoreEmployee.php index 5306ad098f..a0bcd87b68 100644 --- a/app/Actions/HumanResources/Employee/StoreEmployee.php +++ b/app/Actions/HumanResources/Employee/StoreEmployee.php @@ -14,9 +14,10 @@ use App\Actions\SysAdmin\Group\Hydrators\GroupHydrateEmployees; use App\Actions\SysAdmin\Organisation\Hydrators\OrganisationHydrateEmployees; use App\Actions\SysAdmin\User\StoreUser; +use App\Actions\Traits\WithPreparePositionsForValidation; +use App\Actions\Traits\WithReorganisePositions; use App\Enums\HumanResources\Employee\EmployeeStateEnum; use App\Models\HumanResources\Employee; -use App\Models\HumanResources\JobPosition; use App\Models\HumanResources\Workplace; use App\Models\SysAdmin\Organisation; use App\Rules\AlphaDashDot; @@ -31,10 +32,12 @@ class StoreEmployee extends OrgAction { - use HasPositionsRules; + use WithPreparePositionsForValidation; + use WithReorganisePositions; public function handle(Organisation|Workplace $parent, array $modelData): Employee { + if (class_basename($parent) === 'Workplace') { $organisation = $parent->organisation; data_set($modelData, 'organisation_id', $organisation->id); @@ -50,6 +53,8 @@ public function handle(Organisation|Workplace $parent, array $modelData): Employ $positions = Arr::get($modelData, 'positions', []); data_forget($modelData, 'positions'); + $positions = $this->reorganisePositionsSlugsToIds($positions); + /** @var Employee $employee */ $employee = $parent->employees()->create($modelData); @@ -61,6 +66,7 @@ public function handle(Organisation|Workplace $parent, array $modelData): Employ if (Arr::get($credentials, 'username')) { + StoreUser::make()->action( $employee, [ @@ -79,18 +85,8 @@ public function handle(Organisation|Workplace $parent, array $modelData): Employ ); } - $jobPositions = []; + SyncEmployeeJobPositions::run($employee, $positions); - foreach ($positions as $positionData) { - - /** @var JobPosition $jobPosition */ - $jobPosition = $this->organisation->jobPositions()->firstWhere('code', $positionData['code']); - if($jobPosition){ - $jobPositions[$jobPosition->id] = $positionData['scopes']; - } - } - - SyncEmployeeJobPositions::run($employee, $jobPositions); EmployeeHydrateWeekWorkingHours::dispatch($employee); GroupHydrateEmployees::dispatch($employee->group); OrganisationHydrateEmployees::dispatch($organisation); @@ -111,6 +107,7 @@ public function authorize(ActionRequest $request): bool public function prepareForValidation(ActionRequest $request): void { + $this->preparePositionsForValidation(); if (!$this->get('username')) { $this->set('username', null); } @@ -159,7 +156,7 @@ public function rules(): array 'job_title' => ['sometimes', 'nullable', 'string', 'max:256'], 'state' => ['required', Rule::enum(EmployeeStateEnum::class)], 'positions' => ['sometimes', 'array'], - 'positions.*.code' => ['sometimes', 'string'], + 'positions.*.slug' => ['sometimes', 'string'], 'positions.*.scopes' => ['sometimes', 'array'], 'positions.*.scopes.organisations.slug.*' => ['sometimes', Rule::exists('organisations', 'slug')->where('group_id', $this->organisation->group_id)], 'positions.*.scopes.warehouses.slug.*' => ['sometimes', Rule::exists('warehouses', 'slug')->where('organisation_id', $this->organisation->id)], @@ -211,9 +208,6 @@ public function action(Organisation|Workplace $parent, array $modelData, int $hy public function asController(Organisation $organisation, ActionRequest $request): Employee { - - - $this->initialisation($organisation, $request); return $this->handle($organisation, $this->validatedData); diff --git a/app/Actions/HumanResources/Employee/Traits/HasEmployeePositionGenerator.php b/app/Actions/HumanResources/Employee/Traits/HasEmployeePositionGenerator.php deleted file mode 100644 index 59828b4514..0000000000 --- a/app/Actions/HumanResources/Employee/Traits/HasEmployeePositionGenerator.php +++ /dev/null @@ -1,86 +0,0 @@ - - * Created: Tue, 14 May 2024 22:15:37 British Summer Time, Sheffield, UK - * Copyright (c) 2024, Raul A Perusquia Flores - */ - -namespace App\Actions\HumanResources\Employee\Traits; - -use App\Models\HumanResources\JobPosition; -use App\Models\SysAdmin\Organisation; -use Illuminate\Support\Arr; - -trait HasEmployeePositionGenerator -{ - public function generatePositions(Organisation $organisation, array $modelData): array - { - - - $rawJobPositions = []; - foreach (Arr::get($modelData, 'positions', []) as $positionData) { - - - /** @var JobPosition $jobPosition */ - if (in_array($positionData['code'], [ - 'group_admin', - 'group_sysadmin', - 'group_procurement' - ])) { - $jobPosition = $organisation->group->jobPositions()->firstWhere('code', $positionData['code']); - - } else { - $jobPosition = $organisation->jobPositions()->firstWhere('code', $positionData['code']); - } - - - - - - $rawJobPositions[$jobPosition->id] = []; - foreach (Arr::get($positionData, 'scopes', []) as $key => $scopes) { - - - - - $scopeData = match ($key) { - 'shops' => [ - 'Shop' => $organisation->shops->whereIn('slug', $scopes['slug'])->pluck('id')->toArray() - ], - 'warehouses'=> [ - 'Warehouse' => $organisation->warehouses->whereIn('slug', $scopes['slug'])->pluck('id')->toArray() - ], - 'fulfilments' => [ - 'Fulfilment' => $organisation->fulfilments->whereIn('slug', $scopes['slug'])->pluck('id')->toArray() - ], - default => [] - }; - - if (isset($rawJobPositions[$jobPosition->id])) { - $rawJobPositions[$jobPosition->id] = array_merge_recursive($rawJobPositions[$jobPosition->id], $scopeData); - } else { - $rawJobPositions[$jobPosition->id] = $scopeData; - } - } - } - - - - $jobPositions=[]; - foreach ($rawJobPositions as $id => $scopes) { - - foreach ($scopes as $scopeKey => $ids) { - $rawJobPositions[$id][$scopeKey] = array_values(array_unique($ids)); - } - - $jobPositions[$id] = [ - 'scopes'=>$rawJobPositions[$id], - 'organisation_id'=>$organisation->id - ]; - - - } - - return $jobPositions; - } -} diff --git a/app/Actions/HumanResources/Employee/UpdateEmployee.php b/app/Actions/HumanResources/Employee/UpdateEmployee.php index d52ac33be4..79f486e4f5 100644 --- a/app/Actions/HumanResources/Employee/UpdateEmployee.php +++ b/app/Actions/HumanResources/Employee/UpdateEmployee.php @@ -8,13 +8,14 @@ namespace App\Actions\HumanResources\Employee; use App\Actions\HumanResources\Employee\Search\EmployeeRecordSearch; -use App\Actions\HumanResources\Employee\Traits\HasEmployeePositionGenerator; use App\Actions\HumanResources\JobPosition\SyncEmployeeJobPositions; use App\Actions\OrgAction; use App\Actions\SysAdmin\Group\Hydrators\GroupHydrateEmployees; use App\Actions\SysAdmin\Organisation\Hydrators\OrganisationHydrateEmployees; use App\Actions\SysAdmin\User\UpdateUser; +use App\Actions\Traits\WithPreparePositionsForValidation; use App\Actions\Traits\WithActionUpdate; +use App\Actions\Traits\WithReorganisePositions; use App\Enums\HumanResources\Employee\EmployeeStateEnum; use App\Enums\SysAdmin\User\UserAuthTypeEnum; use App\Http\Resources\HumanResources\EmployeeResource; @@ -31,8 +32,8 @@ class UpdateEmployee extends OrgAction { use WithActionUpdate; - use HasPositionsRules; - use HasEmployeePositionGenerator; + use WithPreparePositionsForValidation; + use WithReorganisePositions; protected bool $asAction = false; @@ -40,16 +41,22 @@ class UpdateEmployee extends OrgAction public function handle(Employee $employee, array $modelData): Employee { - if (Arr::exists($modelData, 'positions')) { - $jobPositions = $this->generatePositions($employee->organisation, $modelData); - SyncEmployeeJobPositions::run($employee, $jobPositions); - Arr::forget($modelData, 'positions'); - } + $positions = Arr::get($modelData, 'positions', []); + data_forget($modelData, 'positions'); + $positions = $this->reorganisePositionsSlugsToIds($positions); + + SyncEmployeeJobPositions::run($employee, $positions); $credentials = Arr::only($modelData, ['username', 'password', 'auth_type','user_model_status']); - data_forget($modelData, ['username', 'password', 'auth_type','user_model_status']); + data_forget($modelData, 'username'); + data_forget($modelData, 'password'); + data_forget($modelData, 'auth_type'); + data_forget($modelData, 'user_model_status'); + + + $employee = $this->update($employee, $modelData, ['data', 'salary']); @@ -147,7 +154,7 @@ public function rules(): array 'job_title' => ['sometimes', 'nullable', 'string', 'max:256'], 'state' => ['sometimes', 'required', new Enum(EmployeeStateEnum::class)], 'positions' => ['sometimes', 'array'], - 'positions.*.code' => ['sometimes', 'string'], + 'positions.*.slug' => ['sometimes', 'string'], 'positions.*.scopes' => ['sometimes', 'array'], 'positions.*.scopes.warehouses.slug.*' => ['sometimes', Rule::exists('warehouses', 'slug')->where('organisation_id', $this->organisation->id)], 'positions.*.scopes.fulfilments.slug.*' => ['sometimes', Rule::exists('fulfilments', 'slug')->where('organisation_id', $this->organisation->id)], @@ -186,7 +193,6 @@ public function rules(): array ]; $rules['password'] = ['sometimes', 'required', app()->isLocal() || app()->environment('testing') ? null : Password::min(8)->uncompromised()]; - $rules['user_model_status'] = ['sometimes', 'boolean']; } diff --git a/app/Actions/HumanResources/Employee/ValidatePinEmployee.php b/app/Actions/HumanResources/Employee/ValidatePinEmployee.php index b8f3e14bdd..be012b3b6e 100644 --- a/app/Actions/HumanResources/Employee/ValidatePinEmployee.php +++ b/app/Actions/HumanResources/Employee/ValidatePinEmployee.php @@ -7,8 +7,8 @@ namespace App\Actions\HumanResources\Employee; -use App\Actions\HumanResources\Employee\Traits\HasEmployeePositionGenerator; use App\Actions\OrgAction; +use App\Actions\Traits\WithPreparePositionsForValidation; use App\Actions\Traits\WithActionUpdate; use App\Http\Resources\HumanResources\EmployeeHanResource; use App\Models\HumanResources\Employee; @@ -17,8 +17,7 @@ class ValidatePinEmployee extends OrgAction { use WithActionUpdate; - use HasPositionsRules; - use HasEmployeePositionGenerator; + use WithPreparePositionsForValidation; protected bool $asAction = false; diff --git a/app/Actions/HumanResources/JobPosition/SyncEmployeeJobPositions.php b/app/Actions/HumanResources/JobPosition/SyncEmployeeJobPositions.php index e7e1b7f6c7..cd90910216 100644 --- a/app/Actions/HumanResources/JobPosition/SyncEmployeeJobPositions.php +++ b/app/Actions/HumanResources/JobPosition/SyncEmployeeJobPositions.php @@ -41,11 +41,12 @@ public function handle(Employee $employee, array $jobPositions): void ); } + foreach ($employee->users as $user) { + SyncRolesFromJobPositions::run($user); + } if (count($newJobPositionsIds) || count($removeJobPositions)) { - if ($user = $employee->getUser()) { - SyncRolesFromJobPositions::run($user); - } + EmployeeHydrateJobPositionsShare::run($employee); diff --git a/app/Actions/HumanResources/JobPosition/SyncUserJobPositions.php b/app/Actions/HumanResources/JobPosition/SyncUserJobPositions.php index 7e3c7a2694..d4f509f965 100644 --- a/app/Actions/HumanResources/JobPosition/SyncUserJobPositions.php +++ b/app/Actions/HumanResources/JobPosition/SyncUserJobPositions.php @@ -32,10 +32,12 @@ public function handle(User $user, array $jobPositions): void foreach ($newJobPositionsIds as $jobPositionId) { + $jobPosition = JobPosition::find($jobPositionId); + $pseudoJobPositionsData = [ 'group_id' => $user->group_id, - 'scopes' => $jobPositions[$jobPositionId]['scopes'], - 'organisation_id' => $jobPositions[$jobPositionId]['organisation_id'] + 'scopes' => $jobPositions[$jobPositionId], + 'organisation_id' => $jobPosition->organisation_id ]; diff --git a/app/Actions/SysAdmin/Guest/StoreGuest.php b/app/Actions/SysAdmin/Guest/StoreGuest.php index 6df02a8cb4..68b5f4ef1c 100644 --- a/app/Actions/SysAdmin/Guest/StoreGuest.php +++ b/app/Actions/SysAdmin/Guest/StoreGuest.php @@ -12,7 +12,8 @@ use App\Actions\SysAdmin\Group\Hydrators\GroupHydrateGuests; use App\Actions\SysAdmin\Guest\Hydrators\GuestHydrateUniversalSearch; use App\Actions\SysAdmin\User\StoreUser; -use App\Models\HumanResources\JobPosition; +use App\Actions\Traits\WithPreparePositionsForValidation; +use App\Actions\Traits\WithReorganisePositions; use App\Models\SysAdmin\Group; use App\Models\SysAdmin\Guest; use App\Rules\AlphaDashDot; @@ -23,20 +24,23 @@ use Illuminate\Http\RedirectResponse; use Illuminate\Support\Arr; use Illuminate\Support\Facades\Redirect; -use Illuminate\Validation\Rule; -use Illuminate\Validation\Validator; use Illuminate\Support\Str; +use Illuminate\Validation\Rule; use Illuminate\Validation\Rules\Password; +use Illuminate\Validation\Validator; use Lorisleiva\Actions\ActionRequest; class StoreGuest extends GrpAction { + use WithPreparePositionsForValidation; + use WithReorganisePositions; private bool $validatePhone = false; public function handle(Group $group, array $modelData): Guest { $positions = Arr::get($modelData, 'positions', []); data_forget($modelData, 'positions'); + $positions = $this->reorganisePositionsSlugsToIds($positions); /** @var Guest $guest */ @@ -63,18 +67,9 @@ public function handle(Group $group, array $modelData): Guest ] ); - $jobPositions = []; - foreach ($positions as $positionData) { - $jobPosition = JobPosition::firstWhere('slug', $positionData['slug']); - $jobPositions[$jobPosition->id] = [ - 'scopes' => $positionData['scopes'], - 'organisation_id' => $jobPosition->organisation_id - ]; - - } - SyncUserJobPositions::run($user, $jobPositions); + SyncUserJobPositions::run($user, $positions); GroupHydrateGuests::dispatch($group); @@ -99,6 +94,13 @@ public function prepareForValidation(): void if ($this->get('phone')) { $this->set('phone', preg_replace('/[^0-9+]/', '', $this->get('phone'))); } + + if ($this->get('positions')) { + $this->set('phone', preg_replace('/[^0-9+]/', '', $this->get('phone'))); + } + + $this->preparePositionsForValidation(); + } public function afterValidator(Validator $validator, ActionRequest $request): void @@ -139,7 +141,7 @@ public function rules(): array 'email' => ['sometimes', 'nullable', 'email'], 'positions' => ['sometimes', 'array'], - 'positions.*.code' => ['sometimes', 'string'], + 'positions.*.slug' => ['sometimes', 'string'], 'positions.*.scopes' => ['sometimes', 'array'], 'positions.*.scopes.organisations.slug.*' => ['sometimes', Rule::exists('organisations', 'slug')->where('group_id', $this->group->id)], diff --git a/app/Actions/SysAdmin/User/Hydrators/UserHydrateAuthorisedModels.php b/app/Actions/SysAdmin/User/Hydrators/UserHydrateAuthorisedModels.php index 74f78afbdd..9383c35795 100644 --- a/app/Actions/SysAdmin/User/Hydrators/UserHydrateAuthorisedModels.php +++ b/app/Actions/SysAdmin/User/Hydrators/UserHydrateAuthorisedModels.php @@ -31,6 +31,7 @@ public function handle(User $user): void $authorisedProductions = []; + foreach ($user->getAllPermissions() as $permission) { if ($permission->scope_type === 'Organisation') { $authorisedOrganisations[$permission->scope_id] = ['org_id' => $permission->scope_id]; @@ -69,6 +70,7 @@ public function handle(User $user): void 'number_authorised_warehouses' => count($authorisedWarehouses), 'number_authorised_productions' => count($authorisedProductions), ]; + $user->update($stats); foreach ($user->group->organisations as $organisation) { diff --git a/app/Actions/SysAdmin/User/StoreUser.php b/app/Actions/SysAdmin/User/StoreUser.php index 6f344005cf..72564579b7 100644 --- a/app/Actions/SysAdmin/User/StoreUser.php +++ b/app/Actions/SysAdmin/User/StoreUser.php @@ -53,20 +53,14 @@ public function handle(Guest|Employee $parent, array $modelData = []): User ]); } - - - if ($this->hydratorsDelay) { SetIconAsUserImage::dispatch($user)->delay($this->hydratorsDelay); } else { SetIconAsUserImage::run($user); } + SyncRolesFromJobPositions::run($user); UserRecordSearch::dispatch($user); - - - - GroupHydrateUsers::dispatch($user->group)->delay($this->hydratorsDelay); diff --git a/app/Actions/SysAdmin/User/SyncRolesFromJobPositions.php b/app/Actions/SysAdmin/User/SyncRolesFromJobPositions.php index 8fdc3c3493..40da51075d 100644 --- a/app/Actions/SysAdmin/User/SyncRolesFromJobPositions.php +++ b/app/Actions/SysAdmin/User/SyncRolesFromJobPositions.php @@ -14,6 +14,8 @@ use App\Models\HumanResources\JobPosition; use App\Models\SysAdmin\Role; use App\Models\SysAdmin\User; +use Exception; +use Illuminate\Console\Command; use Lorisleiva\Actions\Concerns\AsAction; class SyncRolesFromJobPositions @@ -24,29 +26,26 @@ public function handle(User $user): void { $roles = []; + if ($user->status) { + foreach ($user->employees as $employee) { + foreach ($employee->jobPositions as $jobPosition) { + $roles = $this->getRoles($roles, $jobPosition); + } + } - foreach ($user->employees as $employee) { - foreach ($employee->jobPositions as $jobPosition) { - - - + foreach ($user->pseudoJobPositions as $jobPosition) { $roles = $this->getRoles($roles, $jobPosition); } } - foreach ($user->pseudoJobPositions as $jobPosition) { - - $roles = $this->getRoles($roles, $jobPosition); - } + $user->syncRoles($roles); - print_r($roles); - $user->syncRoles($roles); if ($user->roles()->where('name', RolesEnum::GROUP_ADMIN->value)->exists()) { foreach ($user->group->organisations as $organisation) { @@ -92,40 +91,51 @@ public function handle(User $user): void private function getRoles($roles, JobPosition $jobPosition): array { - // print '>>>>>>>'.$jobPosition->code." <<<\n"; - // print_r($jobPosition->pivot->scopes); - $jobPosition->refresh(); if ($jobPosition->scope == JobPositionScopeEnum::ORGANISATION || $jobPosition->scope == JobPositionScopeEnum::GROUP) { $roles = array_merge($roles, $jobPosition->roles()->pluck('id')->all()); } else { $roles = array_merge( $roles, - $this->getRolesFromJobPosition($jobPosition) + $this->getRolesOrganisationScopes($jobPosition) ); } return $roles; } - private function getRolesFromJobPosition(JobPosition $jobPosition): array + private function getRolesOrganisationScopes(JobPosition $jobPosition): array { - //print '*******'.$jobPosition->code>" <<<\n"; - $roles = []; $jobPosition->refresh(); foreach ($jobPosition->roles as $role) { - - if (in_array($role->scope_id, $jobPosition->pivot->scopes[$role->scope_type])) { $roles[] = $role->id; } - - return $roles; } return $roles; } + public string $commandSignature = 'user:sync-roles-from-positions {user : User slug}'; + + public function asCommand(Command $command): int + { + + try { + /** @var User $user */ + $user = User::where('slug', $command->argument('user'))->firstOrFail(); + } catch (Exception) { + $command->error('User not found'); + + return 1; + } + setPermissionsTeamId($user->group->id); + $this->handle($user); + + return 0; + } + + } diff --git a/app/Actions/SysAdmin/User/UpdateUsersPseudoJobPositions.php b/app/Actions/SysAdmin/User/UpdateUsersPseudoJobPositions.php index baeab50ab2..d23ebfc88c 100644 --- a/app/Actions/SysAdmin/User/UpdateUsersPseudoJobPositions.php +++ b/app/Actions/SysAdmin/User/UpdateUsersPseudoJobPositions.php @@ -8,22 +8,23 @@ namespace App\Actions\SysAdmin\User; use App\Actions\GrpAction; -use App\Actions\HumanResources\Employee\HasPositionsRules; -use App\Actions\HumanResources\Employee\Traits\HasEmployeePositionGenerator; use App\Actions\HumanResources\JobPosition\SyncUserJobPositions; +use App\Actions\Traits\WithPreparePositionsForValidation; use App\Actions\Traits\WithActionUpdate; +use App\Actions\Traits\WithReorganisePositions; use App\Http\Resources\HumanResources\EmployeeResource; use App\Models\HumanResources\Employee; use App\Models\SysAdmin\Organisation; use App\Models\SysAdmin\User; +use Illuminate\Support\Arr; use Illuminate\Validation\Rule; use Lorisleiva\Actions\ActionRequest; class UpdateUsersPseudoJobPositions extends GrpAction { use WithActionUpdate; - use HasPositionsRules; - use HasEmployeePositionGenerator; + use WithPreparePositionsForValidation; + use WithReorganisePositions; protected bool $asAction = false; @@ -36,10 +37,14 @@ class UpdateUsersPseudoJobPositions extends GrpAction public function handle(User $user, Organisation $organisation, array $modelData): User { - $jobPositions = $this->generatePositions($organisation, $modelData); - SyncUserJobPositions::run($user, $jobPositions); + $positions = Arr::get($modelData, 'positions', []); + $positions = $this->reorganisePositionsSlugsToIds($positions); + + + + SyncUserJobPositions::run($user, $positions); $user->refresh(); @@ -60,7 +65,7 @@ public function rules(): array { return [ 'positions' => ['sometimes', 'array'], - 'positions.*.code' => ['sometimes', 'string'], + 'positions.*.slug' => ['sometimes', 'string'], 'positions.*.scopes' => ['sometimes', 'array'], 'positions.*.scopes.warehouses.slug.*' => ['sometimes', Rule::exists('warehouses', 'slug')->where('organisation_id', $this->organisation->id)], 'positions.*.scopes.fulfilments.slug.*' => ['sometimes', Rule::exists('fulfilments', 'slug')->where('organisation_id', $this->organisation->id)], diff --git a/app/Actions/SysAdmin/User/UserAddRoles.php b/app/Actions/SysAdmin/User/UserAddRoles.php index d9507c4a92..bacce7f3a0 100644 --- a/app/Actions/SysAdmin/User/UserAddRoles.php +++ b/app/Actions/SysAdmin/User/UserAddRoles.php @@ -93,10 +93,5 @@ public function action(User $user, array $roleNames): User return $this->handle($user, $this->get('roles')); } - public string $commandSignature = 'user:add-roles {user : User slug} {roles* : list of roles}'; - - - - } diff --git a/app/Actions/HumanResources/Employee/HasPositionsRules.php b/app/Actions/Traits/WithPreparePositionsForValidation.php similarity index 88% rename from app/Actions/HumanResources/Employee/HasPositionsRules.php rename to app/Actions/Traits/WithPreparePositionsForValidation.php index 9fad5923da..7002eaaf8c 100644 --- a/app/Actions/HumanResources/Employee/HasPositionsRules.php +++ b/app/Actions/Traits/WithPreparePositionsForValidation.php @@ -1,15 +1,15 @@ - * Created: Tue, 14 May 2024 22:15:37 British Summer Time, Sheffield, UK + * Created: Tue, 01 Oct 2024 10:50:08 Malaysia Time, Kuala Lumpur, Malaysia * Copyright (c) 2024, Raul A Perusquia Flores */ -namespace App\Actions\HumanResources\Employee; +namespace App\Actions\Traits; use Illuminate\Support\Arr; -trait HasPositionsRules +trait WithPreparePositionsForValidation { public function preparePositionsForValidation(): void { @@ -39,6 +39,9 @@ public function preparePositionsForValidation(): void $this->fill($positions); } + + + } } diff --git a/app/Actions/Traits/WithReorganisePositions.php b/app/Actions/Traits/WithReorganisePositions.php new file mode 100644 index 0000000000..45df8f5f74 --- /dev/null +++ b/app/Actions/Traits/WithReorganisePositions.php @@ -0,0 +1,87 @@ + + * Created: Tue, 01 Oct 2024 10:50:08 Malaysia Time, Kuala Lumpur, Malaysia + * Copyright (c) 2024, Raul A Perusquia Flores + */ + +namespace App\Actions\Traits; + +use App\Models\Catalogue\Shop; +use App\Models\Fulfilment\Fulfilment; +use App\Models\HumanResources\JobPosition; +use App\Models\Inventory\Warehouse; +use App\Models\Manufacturing\Production; +use App\Models\SysAdmin\Organisation; +use Illuminate\Support\Arr; + +trait WithReorganisePositions +{ + public function reorganisePositionsSlugsToIds($positionsWithSlugs): array + { + if (count($positionsWithSlugs) == 0) { + return []; + } + + + $positions = []; + foreach ($positionsWithSlugs as $positionData) { + $jobPosition = JobPosition::firstWhere('slug', $positionData['slug']); + + + $positions[$jobPosition->id] = $this->reorganiseScopes($positionData['scopes']); + } + + + return $positions; + } + + private function reorganiseScopes($scopesWithSlugs): array + { + $scopes = []; + foreach ($scopesWithSlugs as $key => $value) { + $scopeIds = []; + $scopeModel = $key; + foreach (Arr::get($value, 'slug', []) as $slug) { + if ($key == 'organisations') { + $scopeModel = 'Organisation'; + $model = Organisation::where('slug', $slug)->first(); + if ($model) { + $scopeIds[] = $model->id; + } + } elseif ($key == 'shops') { + $scopeModel = 'Shop'; + $model = Shop::where('slug', $slug)->first(); + if ($model) { + $scopeIds[] = $model->id; + } + + } elseif ($key == 'productions') { + $scopeModel = 'Production'; + $model = Production::where('slug', $slug)->first(); + if ($model) { + $scopeIds[] = $model->id; + } + } elseif ($key == 'warehouses') { + $scopeModel = 'Warehouse'; + $model = Warehouse::where('slug', $slug)->first(); + if ($model) { + $scopeIds[] = $model->id; + } + } elseif ($key == 'fulfilments') { + $scopeModel = 'Fulfilment'; + $model = Fulfilment::where('slug', $slug)->first(); + if ($model) { + $scopeIds[] = $model->id; + } + } + } + + $scopes[$scopeModel] = $scopeIds; + } + + + return $scopes; + } + +} diff --git a/app/Actions/Transfers/Aurora/FetchAuroraEmployees.php b/app/Actions/Transfers/Aurora/FetchAuroraEmployees.php index 3895e9efc9..828ecac15d 100644 --- a/app/Actions/Transfers/Aurora/FetchAuroraEmployees.php +++ b/app/Actions/Transfers/Aurora/FetchAuroraEmployees.php @@ -39,7 +39,6 @@ public function handle(SourceOrganisationService $organisationSource, int $organ if ($employeeData = $organisationSource->fetchEmployee($organisationSourceId)) { - $sourceId = $employeeData['employee']['source_id']; if ($employee = Employee::where('source_id', $sourceId)->first()) { diff --git a/app/Actions/Transfers/Aurora/FetchAuroraUsers.php b/app/Actions/Transfers/Aurora/FetchAuroraUsers.php index cc2e37fdd8..1711efac15 100644 --- a/app/Actions/Transfers/Aurora/FetchAuroraUsers.php +++ b/app/Actions/Transfers/Aurora/FetchAuroraUsers.php @@ -7,7 +7,6 @@ namespace App\Actions\Transfers\Aurora; -use App\Actions\SysAdmin\User\AttachEmployeeToUser; use App\Actions\SysAdmin\User\StoreUser; use App\Actions\SysAdmin\User\UpdateUser; use App\Actions\SysAdmin\User\UpdateUsersPseudoJobPositions; @@ -24,6 +23,7 @@ class FetchAuroraUsers extends FetchAuroraAction public function handle(SourceOrganisationService $organisationSource, int $organisationSourceId): ?User { + setPermissionsTeamId($organisationSource->getOrganisation()->group_id); if ($userData = $organisationSource->fetchUser($organisationSourceId)) { if ($userData['user']) { @@ -44,12 +44,12 @@ public function handle(SourceOrganisationService $organisationSource, int $organ } } - if ($foundUserData=Db::table('user_has_models') + if ($foundUserData = Db::table('user_has_models') ->select('user_id') ->where('group_id', $organisationSource->getOrganisation()->group_id) ->where('source_id', $userData['user']['source_id'])->first()) { - $user=User::where('id', $foundUserData->user_id)->first(); + $user = User::where('id', $foundUserData->user_id)->first(); diff --git a/app/Transfers/Aurora/FetchAuroraDeletedEmployee.php b/app/Transfers/Aurora/FetchAuroraDeletedEmployee.php index 277df5c915..df22ed6f13 100644 --- a/app/Transfers/Aurora/FetchAuroraDeletedEmployee.php +++ b/app/Transfers/Aurora/FetchAuroraDeletedEmployee.php @@ -16,7 +16,7 @@ protected function parseModel(): void { $auDeletedModel = json_decode(gzuncompress($this->auroraModelData->{'Staff Deleted Metadata'})); - if($auDeletedModel->data->{'Staff Type'} == 'Contractor'){ + if ($auDeletedModel->data->{'Staff Type'} == 'Contractor') { return; } diff --git a/app/Transfers/Aurora/FetchAuroraEmployee.php b/app/Transfers/Aurora/FetchAuroraEmployee.php index 067d4285c9..136e568ff8 100644 --- a/app/Transfers/Aurora/FetchAuroraEmployee.php +++ b/app/Transfers/Aurora/FetchAuroraEmployee.php @@ -8,8 +8,6 @@ namespace App\Transfers\Aurora; use App\Enums\HumanResources\Employee\EmployeeStateEnum; -use App\Enums\HumanResources\JobPosition\JobPositionScopeEnum; -use App\Models\HumanResources\JobPosition; use Illuminate\Support\Arr; use Illuminate\Support\Facades\DB; use Illuminate\Support\Str; @@ -116,13 +114,23 @@ public function fetch(int $id): ?array } - $this->parsedData['user'] = $this->parseUser(); - $this->parsedData['photo'] =$this->parseUserPhoto(); + $userData = $this->parseUser(); + // if ($userData) { + // $this->parsedData['employee']['username'] = $userData['username']; + // $this->parsedData['employee']['legacy_password'] = $userData['legacy_password']; + // $this->parsedData['employee']['reset_password'] = $userData['reset_password']; + // $this->parsedData['employee']['user_model_status'] = $userData['user_model_status']; + // $this->parsedData['employee']['password'] = $userData['password']; + // } - $userId = null; + $this->parsedData['user'] = $userData; + + $this->parsedData['photo'] = $this->parseUserPhoto(); + $userId = null; + if (Arr::has($this->parsedData, 'user.source_id')) { $userSourceData = explode(':', $this->parsedData['user']['source_id']); @@ -134,7 +142,7 @@ public function fetch(int $id): ?array } - return $this->parsedData;; + return $this->parsedData; } @@ -177,6 +185,7 @@ private function parseUser(): ?array 'user_model_status' => $status, 'created_at' => $auroraUserData->{'User Created'}, 'legacy_password' => $legacyPassword, + 'password' => (app()->isLocal() ? 'hello' : wordwrap(Str::random(), 4, '-', true)), 'language_id' => $this->parseLanguageID($auroraUserData->{'User Preferred Locale'}), 'reset_password' => false, 'fetched_at' => now(), diff --git a/app/Transfers/Aurora/FetchAuroraUser.php b/app/Transfers/Aurora/FetchAuroraUser.php index f5296a478a..7c50f31aee 100644 --- a/app/Transfers/Aurora/FetchAuroraUser.php +++ b/app/Transfers/Aurora/FetchAuroraUser.php @@ -77,7 +77,7 @@ protected function fetchData($id): object|null { return DB::connection('aurora') ->table('User Dimension') - ->leftJoin('Staff Dimension','Staff Key','User Parent Key') + ->leftJoin('Staff Dimension', 'Staff Key', 'User Parent Key') ->selectRaw('*,(select GROUP_CONCAT(`Role Code`) from `Staff Role Bridge` SRB where (SRB.`Staff Key`=`Staff Dimension`.`Staff Key`) ) as staff_positions') ->selectRaw('(select GROUP_CONCAT(`User Group Key`) from `User Group User Bridge` UGUB where (UGUB.`User Key`=`User Dimension`.`User Key`) ) as staff_groups') diff --git a/app/Transfers/Aurora/WithAuroraParsers.php b/app/Transfers/Aurora/WithAuroraParsers.php index b8f6ebe002..51ccec81cb 100644 --- a/app/Transfers/Aurora/WithAuroraParsers.php +++ b/app/Transfers/Aurora/WithAuroraParsers.php @@ -700,6 +700,7 @@ protected function parsePositions($userID): array $rawJobPositions = $this->parseJobPositions(); + $shops = []; @@ -713,6 +714,8 @@ protected function parsePositions($userID): array $jobPosition = $this->organisation->jobPositions()->where('code', $jobPositionCode)->firstOrFail(); $scopes = []; $add = true; + + if ($jobPosition->scope == JobPositionScopeEnum::SHOPS) { $scopes = ['shops' => ['slug' => $shops]]; if (count($shops) == 0) { @@ -733,18 +736,39 @@ protected function parsePositions($userID): array ] ]; } elseif ($jobPosition->scope == JobPositionScopeEnum::ORGANISATION) { - $scopes = ['organisations' => [$this->organisation->id]]; + $scopes = [ + 'organisations' => + [ + 'slug' => [$this->organisation->slug] + ] + + ]; + } elseif ($jobPosition->scope == JobPositionScopeEnum::FULFILMENTS || $jobPosition->scope == JobPositionScopeEnum::FULFILMENTS_WAREHOUSES) { + $scopes = [ + 'fulfilments' => + [ + 'slug' => $this->organisation->fulfilments()->pluck('slug')->all() + ], + 'warehouses' => + [ + 'slug' => $this->organisation->warehouses()->pluck('slug')->all() + ] + + ]; } + if ($add) { $positions[] = [ - 'code' => $jobPosition->code, + 'slug' => $jobPosition->slug, 'scopes' => $scopes ]; } } + + return $positions; } @@ -853,9 +877,7 @@ protected function getAuroraUserShopScopes($userID): array foreach ( - DB::connection('aurora') - ->table('User Right Scope Bridge') - ->where('User Key', $userID)->get() as $rawScope + DB::connection('aurora')->table('User Right Scope Bridge')->where('User Key', $userID)->get() as $rawScope ) { if ($rawScope->{'Scope'} == 'Store') { $shop = $this->parseShop($this->organisation->id.':'.$rawScope->{'Scope Key'}); @@ -875,12 +897,13 @@ protected function getAuroraUserShopScopes($userID): array protected function parseUserPhoto(): array { - $profileImages = $this->getModelImagesCollection( + $profileImages = $this->getModelImagesCollection( 'Staff', $this->auroraModelData->{'Staff Key'} )->map(function ($auroraImage) { return $this->fetchImage($auroraImage); }); + return $profileImages->toArray(); } diff --git a/database/migrations/2022_08_25_050000_create_employees_table.php b/database/migrations/2022_08_25_050000_create_employees_table.php index c7c0040b4d..6994e058e4 100644 --- a/database/migrations/2022_08_25_050000_create_employees_table.php +++ b/database/migrations/2022_08_25_050000_create_employees_table.php @@ -47,6 +47,7 @@ public function up(): void $table->timestampsTz(); $table = $this->softDeletes($table); $table->string('source_id')->nullable()->unique(); + $table->jsonb('migration_data'); }); DB::statement("CREATE INDEX ON employees (lower('worker_number')) "); DB::statement("CREATE INDEX ON employees (lower('alias')) "); From b04268a371c2cac5555aaba7732c0226c94b9364 Mon Sep 17 00:00:00 2001 From: Raul Perusquia Date: Tue, 1 Oct 2024 15:33:13 +0800 Subject: [PATCH 3/8] #519 Fetch Aurora Customer History (Notes) foundations --- .../CRM/CustomerNote/StoreCustomerNote.php | 102 ++++++++++++++++++ .../CRM/CustomerNote/UpdateCustomerNote.php | 68 ++++++++++++ .../Aurora/FetchAuroraCustomerNotes.php | 101 +++++++++++++++++ .../Transfers/Aurora/FetchAuroraCustomers.php | 35 +++--- app/Models/CRM/CustomerNote.php | 67 ++++++++++++ .../Aurora/FetchAuroraCustomerNote.php | 60 +++++++++++ app/Transfers/AuroraOrganisationService.php | 16 ++- app/Transfers/WowsbarOrganisationService.php | 15 +++ .../2023_08_30_053303_create_audits_table.php | 3 + 9 files changed, 450 insertions(+), 17 deletions(-) create mode 100644 app/Actions/CRM/CustomerNote/StoreCustomerNote.php create mode 100644 app/Actions/CRM/CustomerNote/UpdateCustomerNote.php create mode 100644 app/Actions/Transfers/Aurora/FetchAuroraCustomerNotes.php create mode 100644 app/Models/CRM/CustomerNote.php create mode 100644 app/Transfers/Aurora/FetchAuroraCustomerNote.php diff --git a/app/Actions/CRM/CustomerNote/StoreCustomerNote.php b/app/Actions/CRM/CustomerNote/StoreCustomerNote.php new file mode 100644 index 0000000000..c12f9f43d0 --- /dev/null +++ b/app/Actions/CRM/CustomerNote/StoreCustomerNote.php @@ -0,0 +1,102 @@ + + * Created: Tue, 01 Oct 2024 10:17:17 Malaysia Time, Kuala Lumpur, Malaysia + * Copyright (c) 2024, Raul A Perusquia Flores + */ + +namespace App\Actions\CRM\CustomerNote; + +use App\Actions\OrgAction; +use App\Actions\Traits\WithModelAddressActions; +use App\Models\CRM\Customer; +use App\Models\CRM\CustomerNote; +use App\Models\SysAdmin\User; +use Illuminate\Validation\Rule; +use Lorisleiva\Actions\ActionRequest; +use OwenIt\Auditing\Resolvers\IpAddressResolver; +use OwenIt\Auditing\Resolvers\UrlResolver; +use OwenIt\Auditing\Resolvers\UserAgentResolver; +use OwenIt\Auditing\Resolvers\UserResolver; + +class StoreCustomerNote extends OrgAction +{ + use WithModelAddressActions; + + + public function handle(Customer $customer, array $modelData): CustomerNote + { + /** @var User $user */ + $user = UserResolver::resolve(); + + data_set($modelData, 'group_id', $customer->group_id); + data_set($modelData, 'organisation_id', $customer->organisation_id); + data_set($modelData, 'shop_id', $customer->shop_id); + data_set($modelData, 'customer_id', $customer->id); + + data_set($modelData, 'user_type', class_basename($user), overwrite: false); + data_set($modelData, 'user_id', $user->id, overwrite: false); + + data_set($modelData, 'auditable_type', 'Customer'); + data_set($modelData, 'auditable_id', $customer->id); + + data_set($modelData, 'tags', ['customer_notes']); + data_set($modelData, 'event', 'note_created'); + data_set($modelData, 'new_values', ['note' => $modelData['note']]); + + data_set($modelData, 'url', UrlResolver::resolve($customer)); + data_set($modelData, 'ip_address', IpAddressResolver::resolve($customer)); + data_set($modelData, 'user_agent', UserAgentResolver::resolve($customer)); + + + /** @var CustomerNote $CustomerNote */ + $CustomerNote = $customer->customerNotes()->create($modelData); + + + return $CustomerNote; + } + + public function authorize(ActionRequest $request): bool + { + if ($this->asAction) { + return true; + } + + return $request->user()->hasPermissionTo("crm.{$this->shop->id}.edit"); + } + + public function rules(): array + { + $rules = [ + 'note' => ['required', 'string', 'max:1024'], + ]; + + if (!$this->strict) { + $rules['user_type'] = ['sometimes', Rule::in(['User', 'WebUser'])]; + $rules['user_id'] = ['required', 'integer']; + } + + return $rules; + } + + + public function action(Customer $customer, array $modelData, int $hydratorsDelay = 0, bool $strict = true): CustomerNote + { + $this->asAction = true; + $this->strict = $strict; + $this->hydratorsDelay = $hydratorsDelay; + $this->initialisationFromShop($customer->shop, $modelData); + + return $this->handle($customer, $this->validatedData); + } + + public function asController(Customer $customer, ActionRequest $request): CustomerNote + { + $this->asAction = true; + $this->initialisationFromShop($customer->shop, $request); + + return $this->handle($customer, $this->validatedData); + } + + +} diff --git a/app/Actions/CRM/CustomerNote/UpdateCustomerNote.php b/app/Actions/CRM/CustomerNote/UpdateCustomerNote.php new file mode 100644 index 0000000000..c4bec50ead --- /dev/null +++ b/app/Actions/CRM/CustomerNote/UpdateCustomerNote.php @@ -0,0 +1,68 @@ + + * Created: Tue, 01 Oct 2024 10:17:17 Malaysia Time, Kuala Lumpur, Malaysia + * Copyright (c) 2024, Raul A Perusquia Flores + */ + +namespace App\Actions\CRM\CustomerNote; + +use App\Actions\OrgAction; +use App\Actions\Traits\WithActionUpdate; +use App\Models\Catalogue\Shop; +use App\Models\CRM\CustomerNote; +use App\Models\SysAdmin\Organisation; +use Lorisleiva\Actions\ActionRequest; + +class UpdateCustomerNote extends OrgAction +{ + use WithActionUpdate; + + public function handle(CustomerNote $customerNote, array $modelData): CustomerNote + { + return $this->update($customerNote, $modelData, ['data']); + } + + public function authorize(ActionRequest $request): bool + { + if ($this->asAction) { + return true; + } + + return $request->user()->hasPermissionTo("crm.{$this->shop->id}.edit"); + } + + public function rules(): array + { + $rules = [ + 'note' => ['sometimes', 'string', 'max:1024'], + ]; + + if (!$this->strict) { + $rules['note'] = ['sometimes', 'string', 'max:4096']; + } + + return $rules; + } + + + public function asController(Organisation $organisation, Shop $shop, CustomerNote $customerNote, ActionRequest $request): CustomerNote + { + $this->initialisationFromShop($shop, $request); + + return $this->handle($customerNote, $this->validatedData); + } + + public function action(CustomerNote $customerNote, array $modelData, int $hydratorsDelay = 0, bool $strict = true): CustomerNote + { + $this->asAction = true; + $this->strict = $strict; + $this->hydratorsDelay = $hydratorsDelay; + $this->setRawAttributes($modelData); + $this->initialisationFromShop($customerNote->shop, $modelData); + + return $this->handle($customerNote, $this->validatedData); + } + + +} diff --git a/app/Actions/Transfers/Aurora/FetchAuroraCustomerNotes.php b/app/Actions/Transfers/Aurora/FetchAuroraCustomerNotes.php new file mode 100644 index 0000000000..415bd056e1 --- /dev/null +++ b/app/Actions/Transfers/Aurora/FetchAuroraCustomerNotes.php @@ -0,0 +1,101 @@ + + * Created: Tue, 01 Oct 2024 10:17:17 Malaysia Time, Kuala Lumpur, Malaysia + * Copyright (c) 2024, Raul A Perusquia Flores + */ + +namespace App\Actions\Transfers\Aurora; + +use App\Actions\CRM\CustomerNote\StoreCustomerNote; +use App\Actions\CRM\CustomerNote\UpdateCustomerNote; +use App\Models\CRM\CustomerNote; +use App\Transfers\SourceOrganisationService; +use Exception; +use Illuminate\Database\Query\Builder; +use Illuminate\Support\Facades\DB; +use Throwable; + +class FetchAuroraCustomerNotes extends FetchAuroraAction +{ + public string $commandSignature = 'fetch:customer-notes {organisations?*} {--s|source_id=} {--N|only_new : Fetch only new} {--d|db_suffix=} {--r|reset}'; + + + public function handle(SourceOrganisationService $organisationSource, int $organisationSourceId): ?CustomerNote + { + if ($CustomerNoteData = $organisationSource->fetchCustomerNote($organisationSourceId)) { + if ($CustomerNote = CustomerNote::where('source_id', $CustomerNoteData['customer_note']['source_id']) + ->first()) { + try { + + $CustomerNote = UpdateCustomerNote::make()->action( + CustomerNote: $CustomerNote, + modelData: $CustomerNoteData['customer_note'], + hydratorsDelay: 60, + strict: false, + ); + $this->recordChange($organisationSource, $CustomerNote->wasChanged()); + } catch (Exception $e) { + $this->recordError($organisationSource, $e, $CustomerNoteData['customer_note'], 'CustomerNote', 'update'); + + return null; + } + } else { + + + try { + $CustomerNote = StoreCustomerNote::make()->action( + customer: $CustomerNoteData['customer'], + modelData: $CustomerNoteData['customer_note'], + hydratorsDelay: 60, + strict: false, + ); + $sourceData = explode(':', $CustomerNote->source_id); + DB::connection('aurora')->table('History Dimension') + ->where('History Key', $sourceData[1]) + ->update(['aiku_id' => $CustomerNote->id]); + } catch (Exception|Throwable $e) { + $this->recordError($organisationSource, $e, $CustomerNoteData['customer_note'], 'CustomerNote', 'store'); + return null; + } + } + + + return $CustomerNote; + } + + return null; + } + + public function getModelsQuery(): Builder + { + $query = DB::connection('aurora') + ->table('History Dimension') + ->where('Direct Object', 'Note') + ->where('Indirect Object', 'Customer') + ->select('History Key as source_id') + ->orderBy('source_id'); + + if ($this->onlyNew) { + $query->whereNull('aiku_notes_id'); + } + + return $query; + } + + public function count(): ?int + { + $query = DB::connection('aurora')->table('History Dimension') + ->where('Direct Object', 'Note') + ->where('Indirect Object', 'Customer'); + + if ($this->onlyNew) { + $query->whereNull('aiku_notes_id'); + } + + return $query->count(); + } + + + +} diff --git a/app/Actions/Transfers/Aurora/FetchAuroraCustomers.php b/app/Actions/Transfers/Aurora/FetchAuroraCustomers.php index db79eebfe2..4580273269 100644 --- a/app/Actions/Transfers/Aurora/FetchAuroraCustomers.php +++ b/app/Actions/Transfers/Aurora/FetchAuroraCustomers.php @@ -21,13 +21,14 @@ use Exception; use Illuminate\Database\Query\Builder; use Illuminate\Support\Facades\DB; +use Throwable; class FetchAuroraCustomers extends FetchAuroraAction { use WithAuroraAttachments; use WithAuroraParsers; - public string $commandSignature = 'fetch:customers {organisations?*} {--s|source_id=} {--S|shop= : Shop slug} {--w|with=* : Accepted values: clients orders web-users attachments portfolio} {--N|only_new : Fetch only new} {--d|db_suffix=} {--r|reset}'; + public string $commandSignature = 'fetch:customers {organisations?*} {--s|source_id=} {--S|shop= : Shop slug} {--w|with=* : Accepted values: clients orders web-users attachments portfolio full} {--N|only_new : Fetch only new} {--d|db_suffix=} {--r|reset}'; public function handle(SourceOrganisationService $organisationSource, int $organisationSourceId): ?Customer @@ -61,7 +62,11 @@ public function handle(SourceOrganisationService $organisationSource, int $organ ); $this->recordNew($organisationSource); - } catch (Exception $e) { + $sourceData = explode(':', $customer->source_id); + DB::connection('aurora')->table('Customer Dimension') + ->where('Customer Key', $sourceData[1]) + ->update(['aiku_id' => $customer->id]); + } catch (Exception|Throwable $e) { $this->recordError($organisationSource, $e, $customerData['customer'], 'Customer', 'store'); return null; @@ -71,7 +76,7 @@ public function handle(SourceOrganisationService $organisationSource, int $organ $sourceData = explode(':', $customer->source_id); - if (in_array('products', $with)) { + if (in_array('products', $with) || in_array('full', $with)) { foreach ( DB::connection('aurora') ->table('Product Dimension') @@ -84,7 +89,12 @@ public function handle(SourceOrganisationService $organisationSource, int $organ } - if ($customer->shop->type == ShopTypeEnum::DROPSHIPPING and in_array('clients', $with)) { + if ($customer->shop->type == ShopTypeEnum::DROPSHIPPING and + ( + in_array('clients', $with) || in_array('full', $with) + ) + + ) { foreach ( DB::connection('aurora') ->table('Customer Client Dimension') @@ -96,7 +106,11 @@ public function handle(SourceOrganisationService $organisationSource, int $organ } } - if ($customer->shop->type == ShopTypeEnum::DROPSHIPPING and in_array('portfolio', $with)) { + if ($customer->shop->type == ShopTypeEnum::DROPSHIPPING and + ( + in_array('portfolio', $with) || in_array('full', $with) + ) + ) { foreach ( DB::connection('aurora') ->table('Customer Portfolio Fact') @@ -108,7 +122,7 @@ public function handle(SourceOrganisationService $organisationSource, int $organ } } - if (in_array('orders', $with)) { + if (in_array('orders', $with) || in_array('full', $with)) { foreach ( DB::connection('aurora') ->table('Order Dimension') @@ -121,7 +135,7 @@ public function handle(SourceOrganisationService $organisationSource, int $organ } - if (in_array('web-users', $with)) { + if (in_array('web-users', $with) || in_array('full', $with)) { foreach ( DB::connection('aurora') ->table('Website User Dimension') @@ -134,12 +148,7 @@ public function handle(SourceOrganisationService $organisationSource, int $organ } - DB::connection('aurora')->table('Customer Dimension') - ->where('Customer Key', $sourceData[1]) - ->update(['aiku_id' => $customer->id]); - - - if (in_array('attachments', $this->with)) { + if (in_array('attachments', $this->with) || in_array('full', $with)) { $sourceData = explode(':', $customer->source_id); foreach ($this->parseAttachments($sourceData[1]) ?? [] as $attachmentData) { SaveModelAttachment::run( diff --git a/app/Models/CRM/CustomerNote.php b/app/Models/CRM/CustomerNote.php new file mode 100644 index 0000000000..a684e83fd5 --- /dev/null +++ b/app/Models/CRM/CustomerNote.php @@ -0,0 +1,67 @@ + + * Created: Tue, 01 Oct 2024 10:17:17 Malaysia Time, Kuala Lumpur, Malaysia + * Copyright (c) 2024, Raul A Perusquia Flores + */ + +namespace App\Models\CRM; + +use App\Models\Traits\InCustomer; +use Illuminate\Database\Eloquent\Model; + +/** + * + * + * @property int $id + * @property int|null $group_id + * @property int|null $organisation_id + * @property int|null $shop_id + * @property int|null $website_id + * @property int|null $customer_id + * @property string|null $user_type + * @property int|null $user_id + * @property array $tags + * @property string $auditable_type + * @property int $auditable_id + * @property string $event + * @property string|null $comments + * @property array|null $old_values + * @property array|null $new_values + * @property string|null $url + * @property string|null $ip_address + * @property string|null $user_agent + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property string|null $fetched_at + * @property string|null $last_fetched_at + * @property string|null $source_id + * @property-read \App\Models\CRM\Customer|null $customer + * @property-read \App\Models\SysAdmin\Group|null $group + * @property-read \App\Models\SysAdmin\Organisation|null $organisation + * @property-read \App\Models\Catalogue\Shop|null $shop + * @method static \Illuminate\Database\Eloquent\Builder|CustomerNote newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|CustomerNote newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|CustomerNote query() + * @mixin \Eloquent + */ +class CustomerNote extends Model +{ + use InCustomer; + + protected $table = 'audits'; + + protected $casts = [ + 'tags' => 'array', + 'old_values' => 'array', + 'new_values' => 'array', + ]; + + protected $attributes = [ + 'tags' => '{}', + 'old_values' => '{}', + 'new_values' => '{}', + ]; + + protected $guarded = []; +} diff --git a/app/Transfers/Aurora/FetchAuroraCustomerNote.php b/app/Transfers/Aurora/FetchAuroraCustomerNote.php new file mode 100644 index 0000000000..1ebeef3b56 --- /dev/null +++ b/app/Transfers/Aurora/FetchAuroraCustomerNote.php @@ -0,0 +1,60 @@ + + * Created: Tue, 01 Oct 2024 10:17:17 Malaysia Time, Kuala Lumpur, Malaysia + * Copyright (c) 2024, Raul A Perusquia Flores + */ + +namespace App\Transfers\Aurora; + +use Illuminate\Support\Facades\DB; + +class FetchAuroraCustomerNote extends FetchAurora +{ + protected function parseModel(): void + { + $customer = $this->parseCustomer( + $this->organisation->id.':'.$this->auroraModelData->{'Indirect Object Key'} + ); + + $this->parsedData['customer'] = $customer; + + $user = null; + + if ($this->auroraModelData->{'Subject'} == 'Staff') { + $employee = $this->parseEmployee( + $this->organisation->id.':'.$this->auroraModelData->{'Subject Key'} + ); + $user = $employee->getUser(); + + + + } + + + $this->parsedData['customer_note'] = + [ + 'note' => $this->auroraModelData->{'History Abstract'}, + 'created_at' => $this->auroraModelData->{'History Date'}, + 'source_id' => $this->organisation->id.':'.$this->auroraModelData->{'History Key'}, + 'fetched_at' => now(), + 'last_fetched_at' => now(), + ]; + + + if ($user) { + $this->parsedData['customer_note']['user_type'] = 'User'; + $this->parsedData['customer_note']['user_id'] = $user->id; + } else { + dd($this->auroraModelData); + } + } + + + protected function fetchData($id): object|null + { + return DB::connection('aurora') + ->table('History Dimension') + ->where('History Key', $id)->first(); + } +} diff --git a/app/Transfers/AuroraOrganisationService.php b/app/Transfers/AuroraOrganisationService.php index a25c04cd93..253d899f77 100644 --- a/app/Transfers/AuroraOrganisationService.php +++ b/app/Transfers/AuroraOrganisationService.php @@ -18,6 +18,7 @@ use App\Transfers\Aurora\FetchAuroraCredit; use App\Transfers\Aurora\FetchAuroraCustomer; use App\Transfers\Aurora\FetchAuroraCustomerClient; +use App\Transfers\Aurora\FetchAuroraCustomerNote; use App\Transfers\Aurora\FetchAuroraDeletedCustomer; use App\Transfers\Aurora\FetchAuroraDeletedEmployee; use App\Transfers\Aurora\FetchAuroraDeletedInvoice; @@ -441,13 +442,20 @@ public function fetchOffer($id): ?array return (new FetchAuroraOffer($this))->fetch($id); } - public function fetchDeletedUser($id) + public function fetchCustomerNote($id): ?array { - // TODO: Implement fetchDeletedUser() method. + return (new FetchAuroraCustomerNote($this))->fetch($id); } - public function fetchUser($id) + public function fetchDeletedUser($id): ?array { - // TODO: Implement fetchUser() method. + return (new FetchAuroraDeletedUser($this))->fetch($id); } + + public function fetchUser($id): ?array + { + return (new FetchAuroraUser($this))->fetch($id); + } + + } diff --git a/app/Transfers/WowsbarOrganisationService.php b/app/Transfers/WowsbarOrganisationService.php index 1596955f21..00a8415d89 100644 --- a/app/Transfers/WowsbarOrganisationService.php +++ b/app/Transfers/WowsbarOrganisationService.php @@ -379,5 +379,20 @@ public function fetchOffer($id): ?array return null; } + public function fetchCustomerNote($id): ?array + { + return null; + } + + public function fetchDeletedUser($id): ?array + { + return null; + } + + public function fetchUser($id): ?array + { + return null; + } + } diff --git a/database/migrations/2023_08_30_053303_create_audits_table.php b/database/migrations/2023_08_30_053303_create_audits_table.php index d3f9fb0624..56032d09ea 100644 --- a/database/migrations/2023_08_30_053303_create_audits_table.php +++ b/database/migrations/2023_08_30_053303_create_audits_table.php @@ -36,6 +36,9 @@ public function up(): void $table->ipAddress()->nullable(); $table->string('user_agent', 1023)->nullable(); $table->timestampsTz(); + $table->datetimeTz('fetched_at')->nullable(); + $table->datetimeTz('last_fetched_at')->nullable(); + $table->string('source_id')->nullable()->unique(); $table->index([$morphPrefix.'_id', $morphPrefix.'_type']); }); } From 4f9542c90dde7d7a1d9e0c5eeeb61ff185f10d32 Mon Sep 17 00:00:00 2001 From: Artha Date: Tue, 1 Oct 2024 15:34:35 +0800 Subject: [PATCH 4/8] fix: employees --- .../Employee/ValidatePinEmployee.php | 41 ++++++++++++++----- routes/han/han-app.php | 2 +- tests/Feature/HanAppTest.php | 2 +- 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/app/Actions/HumanResources/Employee/ValidatePinEmployee.php b/app/Actions/HumanResources/Employee/ValidatePinEmployee.php index 3a8b314601..8b0f56a5a2 100644 --- a/app/Actions/HumanResources/Employee/ValidatePinEmployee.php +++ b/app/Actions/HumanResources/Employee/ValidatePinEmployee.php @@ -11,7 +11,10 @@ use App\Actions\OrgAction; use App\Actions\Traits\WithActionUpdate; use App\Http\Resources\HumanResources\EmployeeHanResource; +use App\Models\HumanResources\ClockingMachine; use App\Models\HumanResources\Employee; +use Illuminate\Support\Arr; +use Illuminate\Validation\Rule; use Lorisleiva\Actions\ActionRequest; class ValidatePinEmployee extends OrgAction @@ -23,10 +26,11 @@ class ValidatePinEmployee extends OrgAction protected bool $asAction = false; private Employee $employee; + private ClockingMachine $clockingMachine; - public function handle(Employee $employee): Employee + public function handle(ClockingMachine $clockingMachine, array $modalData): Employee { - return $employee; + return $clockingMachine->workplace->employees()->where('pin', Arr::get($modalData, 'pin'))->first(); } public function authorize(): bool @@ -34,25 +38,42 @@ public function authorize(): bool return true; } - public function action(Employee $employee, array $modelData, bool $audit = true): Employee + public function action(ClockingMachine $clockingMachine, array $modelData, bool $audit = true): Employee { + $this->clockingMachine = $clockingMachine; + if (!$audit) { Employee::disableAuditing(); } $this->asAction = true; - $this->employee = $employee; - $this->initialisation($employee->organisation, $modelData); + $this->initialisation($clockingMachine->organisation, $modelData); + + return $this->handle($clockingMachine, $modelData); + } + + public function rules(): array + { + $clockingMachineId = $this->clockingMachine->id; - return $this->handle($employee); + return [ + 'pin' => ['required', Rule::exists('employees', 'pin')->where(function ($query) use ($clockingMachineId) { + $query->whereHas('workplaces', function ($query) use ($clockingMachineId) { + $query->where('clocking_machine_id', $clockingMachineId); + }); + })] + ]; } - public function asController(Employee $employee, ActionRequest $request): Employee + public function asController(ActionRequest $request): Employee { - $this->employee = $employee; - $this->initialisation($employee->organisation, $request); + /** @var ClockingMachine $clockingMachine */ + $clockingMachine = $request->user(); + $this->clockingMachine = $clockingMachine; + + $this->initialisation($clockingMachine->organisation, $request); - return $this->handle($employee); + return $this->handle($clockingMachine, $this->validatedData); } public function jsonResponse(Employee $employee): EmployeeHanResource diff --git a/routes/han/han-app.php b/routes/han/han-app.php index 1b45d4fb39..13872e897e 100644 --- a/routes/han/han-app.php +++ b/routes/han/han-app.php @@ -14,7 +14,7 @@ Route::name('han.')->group(function () { Route::middleware(['auth:sanctum'])->group(function () { Route::prefix('employee')->group(function () { - Route::get('{employee:pin}/pin', ValidatePinEmployee::class)->name('employee.pin.validate'); + Route::get('pin', ValidatePinEmployee::class)->name('employee.pin.validate'); Route::post('{employee:id}/clocking', [StoreClocking::class, 'han'])->name('employee.clocking.store'); }); diff --git a/tests/Feature/HanAppTest.php b/tests/Feature/HanAppTest.php index bb4f499e32..9a84dae599 100644 --- a/tests/Feature/HanAppTest.php +++ b/tests/Feature/HanAppTest.php @@ -140,7 +140,7 @@ ->and($otherOrganisation->id)->not->toBe($this->organisation->id) ->and($employeeOtherOrganisation)->toBeInstanceOf(Employee::class); - $response = $this->getJson(route('han.employee.pin.validate', ['employee' => $employeeOtherOrganisation->pin])); + $response = $this->getJson(route('han.employee.pin.validate')); $response->assertStatus(404); }); From 65f0ade2715790d1ea50e47931aa9047fd65df82 Mon Sep 17 00:00:00 2001 From: Artha Date: Tue, 1 Oct 2024 15:37:15 +0800 Subject: [PATCH 5/8] fix: han app etst --- tests/Feature/HanAppTest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/Feature/HanAppTest.php b/tests/Feature/HanAppTest.php index 9a84dae599..fff2627527 100644 --- a/tests/Feature/HanAppTest.php +++ b/tests/Feature/HanAppTest.php @@ -140,7 +140,9 @@ ->and($otherOrganisation->id)->not->toBe($this->organisation->id) ->and($employeeOtherOrganisation)->toBeInstanceOf(Employee::class); - $response = $this->getJson(route('han.employee.pin.validate')); + $response = $this->postJson(route('han.employee.pin.validate'), [ + 'pin' => $employeeOtherOrganisation->pin + ]); $response->assertStatus(404); }); From 9d2bf03329d7331159d7ba019105903c57c45f3b Mon Sep 17 00:00:00 2001 From: Artha Date: Tue, 1 Oct 2024 15:41:42 +0800 Subject: [PATCH 6/8] fix: route han --- routes/han/han-app.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/han/han-app.php b/routes/han/han-app.php index 13872e897e..dd45f15be0 100644 --- a/routes/han/han-app.php +++ b/routes/han/han-app.php @@ -14,7 +14,7 @@ Route::name('han.')->group(function () { Route::middleware(['auth:sanctum'])->group(function () { Route::prefix('employee')->group(function () { - Route::get('pin', ValidatePinEmployee::class)->name('employee.pin.validate'); + Route::post('pin', ValidatePinEmployee::class)->name('employee.pin.validate'); Route::post('{employee:id}/clocking', [StoreClocking::class, 'han'])->name('employee.clocking.store'); }); From a567041d39b1443c5846eb547082f88790be555a Mon Sep 17 00:00:00 2001 From: Artha Date: Tue, 1 Oct 2024 16:31:44 +0800 Subject: [PATCH 7/8] fix: employee pin validation --- .../Employee/UpdateEmployee.php | 6 ++--- .../Employee/ValidatePinEmployee.php | 22 +++++++------------ app/Models/HumanResources/Employee.php | 2 +- 3 files changed, 12 insertions(+), 18 deletions(-) diff --git a/app/Actions/HumanResources/Employee/UpdateEmployee.php b/app/Actions/HumanResources/Employee/UpdateEmployee.php index 79f486e4f5..e999111d3b 100644 --- a/app/Actions/HumanResources/Employee/UpdateEmployee.php +++ b/app/Actions/HumanResources/Employee/UpdateEmployee.php @@ -58,7 +58,7 @@ public function handle(Employee $employee, array $modelData): Employee - $employee = $this->update($employee, $modelData, ['data', 'salary']); + $employee = $this->update($employee, $modelData['state'], ['data', 'salary']); if (Arr::hasAny($employee->getChanges(), ['worker_number', 'worker_number', 'contact_name', 'work_email', 'job_title', 'email'])) { EmployeeRecordSearch::dispatch($employee); @@ -115,7 +115,7 @@ public function rules(): array ), ], - 'employment_start_at' => ['sometimes', 'nullable', 'date'], + 'state.employment_start_at' => ['sometimes', 'nullable', 'date'], 'work_email' => [ 'sometimes', 'nullable', @@ -152,7 +152,7 @@ public function rules(): array 'contact_name' => ['sometimes', 'string', 'max:256'], 'date_of_birth' => ['sometimes', 'nullable', 'date', 'before_or_equal:today'], 'job_title' => ['sometimes', 'nullable', 'string', 'max:256'], - 'state' => ['sometimes', 'required', new Enum(EmployeeStateEnum::class)], + 'state.state' => ['sometimes', 'required', new Enum(EmployeeStateEnum::class)], 'positions' => ['sometimes', 'array'], 'positions.*.slug' => ['sometimes', 'string'], 'positions.*.scopes' => ['sometimes', 'array'], diff --git a/app/Actions/HumanResources/Employee/ValidatePinEmployee.php b/app/Actions/HumanResources/Employee/ValidatePinEmployee.php index 4be66bc786..f1b3d04f4a 100644 --- a/app/Actions/HumanResources/Employee/ValidatePinEmployee.php +++ b/app/Actions/HumanResources/Employee/ValidatePinEmployee.php @@ -13,8 +13,8 @@ use App\Http\Resources\HumanResources\EmployeeHanResource; use App\Models\HumanResources\ClockingMachine; use App\Models\HumanResources\Employee; +use App\Models\SysAdmin\Organisation; use Illuminate\Support\Arr; -use Illuminate\Validation\Rule; use Lorisleiva\Actions\ActionRequest; class ValidatePinEmployee extends OrgAction @@ -27,9 +27,9 @@ class ValidatePinEmployee extends OrgAction private Employee $employee; private ClockingMachine $clockingMachine; - public function handle(ClockingMachine $clockingMachine, array $modalData): Employee + public function handle(Organisation $organisation, array $modalData) { - return $clockingMachine->workplace->employees()->where('pin', Arr::get($modalData, 'pin'))->first(); + return $organisation->employees()->where('pin', Arr::get($modalData, 'pin'))->first(); } public function authorize(): bool @@ -37,7 +37,7 @@ public function authorize(): bool return true; } - public function action(ClockingMachine $clockingMachine, array $modelData, bool $audit = true): Employee + public function action(ClockingMachine $clockingMachine, array $modelData, bool $audit = true) { $this->clockingMachine = $clockingMachine; @@ -48,23 +48,17 @@ public function action(ClockingMachine $clockingMachine, array $modelData, bool $this->initialisation($clockingMachine->organisation, $modelData); - return $this->handle($clockingMachine, $modelData); + return $this->handle($clockingMachine->organisation, $modelData); } public function rules(): array { - $clockingMachineId = $this->clockingMachine->id; - return [ - 'pin' => ['required', Rule::exists('employees', 'pin')->where(function ($query) use ($clockingMachineId) { - $query->whereHas('workplaces', function ($query) use ($clockingMachineId) { - $query->where('clocking_machine_id', $clockingMachineId); - }); - })] + 'pin' => ['required', 'exists:employees,pin'] ]; } - public function asController(ActionRequest $request): Employee + public function asController(ActionRequest $request) { /** @var ClockingMachine $clockingMachine */ $clockingMachine = $request->user(); @@ -72,7 +66,7 @@ public function asController(ActionRequest $request): Employee $this->initialisation($clockingMachine->organisation, $request); - return $this->handle($clockingMachine, $this->validatedData); + return $this->handle($clockingMachine->organisation, $this->validatedData); } public function jsonResponse(Employee $employee): EmployeeHanResource diff --git a/app/Models/HumanResources/Employee.php b/app/Models/HumanResources/Employee.php index d25eedcd21..3ed90d3bd0 100644 --- a/app/Models/HumanResources/Employee.php +++ b/app/Models/HumanResources/Employee.php @@ -138,7 +138,7 @@ class Employee extends Model implements HasMedia, Auditable 'errors' => '{}', 'salary' => '{}', 'working_hours' => '{}', - // 'migration_data' => '{}' + 'migration_data' => '{}' ]; protected $guarded = []; From 377e1af0665d05b681f7bcbf026e1e8a1a3553e4 Mon Sep 17 00:00:00 2001 From: KirinZero0 Date: Tue, 1 Oct 2024 16:54:37 +0800 Subject: [PATCH 8/8] #922 --- .../HumanResources/Employee/UpdateEmployee.php | 4 ++-- .../Employee/ValidatePinEmployee.php | 6 +++++- tests/Feature/HanAppTest.php | 16 +++++++++++----- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/app/Actions/HumanResources/Employee/UpdateEmployee.php b/app/Actions/HumanResources/Employee/UpdateEmployee.php index e999111d3b..9ca8d9ba95 100644 --- a/app/Actions/HumanResources/Employee/UpdateEmployee.php +++ b/app/Actions/HumanResources/Employee/UpdateEmployee.php @@ -58,7 +58,7 @@ public function handle(Employee $employee, array $modelData): Employee - $employee = $this->update($employee, $modelData['state'], ['data', 'salary']); + $employee = $this->update($employee, $modelData, ['data', 'salary']); if (Arr::hasAny($employee->getChanges(), ['worker_number', 'worker_number', 'contact_name', 'work_email', 'job_title', 'email'])) { EmployeeRecordSearch::dispatch($employee); @@ -152,7 +152,7 @@ public function rules(): array 'contact_name' => ['sometimes', 'string', 'max:256'], 'date_of_birth' => ['sometimes', 'nullable', 'date', 'before_or_equal:today'], 'job_title' => ['sometimes', 'nullable', 'string', 'max:256'], - 'state.state' => ['sometimes', 'required', new Enum(EmployeeStateEnum::class)], + 'state' => ['sometimes', 'required', new Enum(EmployeeStateEnum::class)], 'positions' => ['sometimes', 'array'], 'positions.*.slug' => ['sometimes', 'string'], 'positions.*.scopes' => ['sometimes', 'array'], diff --git a/app/Actions/HumanResources/Employee/ValidatePinEmployee.php b/app/Actions/HumanResources/Employee/ValidatePinEmployee.php index f1b3d04f4a..cf88213059 100644 --- a/app/Actions/HumanResources/Employee/ValidatePinEmployee.php +++ b/app/Actions/HumanResources/Employee/ValidatePinEmployee.php @@ -10,6 +10,7 @@ use App\Actions\OrgAction; use App\Actions\Traits\WithPreparePositionsForValidation; use App\Actions\Traits\WithActionUpdate; +use App\Enums\HumanResources\Employee\EmployeeStateEnum; use App\Http\Resources\HumanResources\EmployeeHanResource; use App\Models\HumanResources\ClockingMachine; use App\Models\HumanResources\Employee; @@ -29,7 +30,10 @@ class ValidatePinEmployee extends OrgAction public function handle(Organisation $organisation, array $modalData) { - return $organisation->employees()->where('pin', Arr::get($modalData, 'pin'))->first(); + $employee = $organisation->employees() + ->where('state', '!=', EmployeeStateEnum::LEFT->value) + ->where('pin', Arr::get($modalData, 'pin'))->firstOrFail(); + return $employee; } public function authorize(): bool diff --git a/tests/Feature/HanAppTest.php b/tests/Feature/HanAppTest.php index fff2627527..2a560b8145 100644 --- a/tests/Feature/HanAppTest.php +++ b/tests/Feature/HanAppTest.php @@ -90,7 +90,9 @@ test('find employee by pin', function () { Sanctum::actingAs($this->clockingMachine); - $response = $this->getJson(route('han.employee.pin.validate', ['employee' => $this->employee->pin])); + $response = $this->postJson(route('han.employee.pin.validate'), [ + 'pin' => $this->employee->pin + ]); $response ->assertStatus(200) @@ -126,8 +128,10 @@ test('do not find employee using wrong pin', function () { Sanctum::actingAs($this->clockingMachine); - $response = $this->getJson(route('han.employee.pin.validate', ['employee' => 'XX11XX'])); - $response->assertStatus(404); + $response = $this->postJson(route('han.employee.pin.validate'), [ + 'pin' => 'XX11XX' + ]); + $response->assertStatus(422); }); @@ -151,6 +155,8 @@ UpdateEmployee::make()->action($this->employee, ['state' => EmployeeStateEnum::LEFT]); $this->employee->refresh(); - $response = $this->getJson(route('han.employee.pin.validate', ['employee' => $this->employee->pin])); - $response->assertStatus(405); + $response = $this->postJson(route('han.employee.pin.validate'), [ + 'pin' => $this->employee->pin + ]); + $response->assertStatus(404); });