diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 10020d0d3..31bae8771 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -3,7 +3,6 @@ on: push: branches: - '1.x' - - 'development' workflow_dispatch: diff --git a/.github/workflows/ec2-deploy.yaml b/.github/workflows/ec2-deploy.yaml index 0d52c46a2..2017d8bd1 100644 --- a/.github/workflows/ec2-deploy.yaml +++ b/.github/workflows/ec2-deploy.yaml @@ -1,6 +1,10 @@ name: Deploy to EC2 on: + push: + branches: + - 'development' + workflow_dispatch: jobs: @@ -20,4 +24,25 @@ jobs: username: ${{ secrets.AWS_EC2_USERNAME }} key: ${{ secrets.AWS_EC2_PRIVATE_SSH_KEY }} source: . - target: ${{secrets.AWS_EC2_TARGET_DIR}} \ No newline at end of file + target: ${{secrets.AWS_EC2_TARGET_DIR}} + docker-command: + name: Docker commands + runs-on: ubuntu-latest + needs: deploy-to-ec2 + environment: ${{ github.ref_name }} + steps: + - name: executing remote ssh commands using password + uses: appleboy/ssh-action@v1.0.3 + with: + host: ${{ secrets.AWS_EC2_HOST }} + username: ${{ secrets.AWS_EC2_USERNAME }} + key: ${{ secrets.AWS_EC2_PRIVATE_SSH_KEY }} + script: | + cd ${{secrets.AWS_EC2_TARGET_DIR}} + docker compose -f docker-compose.dev.yml up -d + docker exec -i phpkanvas-ecosystem php artisan lighthouse:cache + docker exec -i phpkanvas-ecosystem php artisan config:cache + docker restart queue + docker restart queue-notifications + docker restart queue-social + docker restart laravel-scheduler diff --git a/app/Console/Commands/Connectors/Notifications/MailCaddieLabCommand.php b/app/Console/Commands/Connectors/Notifications/MailCaddieLabCommand.php index d62874f53..e54c31b51 100644 --- a/app/Console/Commands/Connectors/Notifications/MailCaddieLabCommand.php +++ b/app/Console/Commands/Connectors/Notifications/MailCaddieLabCommand.php @@ -5,6 +5,7 @@ use Illuminate\Console\Command; use Kanvas\Apps\Repositories\AppsRepository; use Kanvas\Connectors\Notifications\Jobs\MailCaddieLabJob; +use Kanvas\Guild\Customers\Repositories\PeoplesRepository; class MailCaddieLabCommand extends Command { @@ -13,9 +14,21 @@ class MailCaddieLabCommand extends Command public function handle() { $this->info('Sending internal mail to Caddie Lab'); - //dump($this->argument('email')); + $app = AppsRepository::findFirstByKey($this->argument('apps_id')); $email = $this->argument('email'); - MailCaddieLabJob::dispatch($app, $email); + + $peoplesIn7Days = PeoplesRepository::getByDaysCreated(7, $app); + $peoplesIn28Days = PeoplesRepository::getByDaysCreated(28, $app); + + $this->info('We will be sending email to ' . count($peoplesIn7Days) . ' people that have been created in the last 7 days'); + $this->info('We will be sending email to ' . count($peoplesIn28Days) . ' people that have been created in the last 28 days'); + + if ($this->confirm('Are you sure you want to send this email?')) { + MailCaddieLabJob::dispatch($app, $email); + $this->info('Emails have been dispatched.'); + } else { + $this->info('Email sending has been canceled.'); + } } } diff --git a/app/Console/Commands/DeleteUsersRequestedCommand.php b/app/Console/Commands/DeleteUsersRequestedCommand.php new file mode 100644 index 000000000..d54dea2aa --- /dev/null +++ b/app/Console/Commands/DeleteUsersRequestedCommand.php @@ -0,0 +1,52 @@ +argument('apps_id'); + if ($appsId) { + $app = Apps::findFirstOrFail($appsId); + $this->info('Deleting user from app: ' . $app->name); + } + $days = $appsId ? $app->get('days_to_delete') : 30; + $users = RequestDeletedAccount::when($appsId, function ($query) use ($appsId) { + return $query->where('apps_id', $appsId); + })->where(DB::raw('DATEDIFF(request_date, CURDATE())'), '>', $days) + ->where('is_deleted', 0) + ->get(); + foreach ($users as $user) { + echo 'Deleting user: ' . $user->email . PHP_EOL; + $user->associateUsers()->deActive(); + } + } +} diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index b8e80aedb..a16608d57 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -7,6 +7,7 @@ use Spatie\Health\Commands\DispatchQueueCheckJobsCommand; use Spatie\Health\Commands\RunHealthChecksCommand; use Spatie\Health\Commands\ScheduleCheckHeartbeatCommand; +use App\Console\Commands\DeleteUsersRequestedCommand; class Kernel extends ConsoleKernel { @@ -20,6 +21,7 @@ protected function schedule(Schedule $schedule) $schedule->command(RunHealthChecksCommand::class)->everyMinute(); $schedule->command(DispatchQueueCheckJobsCommand::class)->everyMinute(); $schedule->command(ScheduleCheckHeartbeatCommand::class)->everyMinute(); + $schedule->command(DeleteUsersRequestedCommand::class)->dailyAt('00:00'); /* $schedule->command(MailCaddieLabCommand::class, [getenv('CADDIE_APP_KEY')]) ->dailyAt('13:00') ->timezone('America/Santo_Domingo') ; */ diff --git a/app/GraphQL/ActionEngine/Builders/Engagements/TaskEngagementBuilder.php b/app/GraphQL/ActionEngine/Builders/Engagements/TaskEngagementBuilder.php new file mode 100644 index 000000000..986ec202e --- /dev/null +++ b/app/GraphQL/ActionEngine/Builders/Engagements/TaskEngagementBuilder.php @@ -0,0 +1,43 @@ +user()->getCurrentCompany(); + $user = auth()->user(); + $app = app(Apps::class); + + $lead = Lead::getByIdFromCompanyApp($args['lead_id'], $company, $app); + $leadId = $lead->getId(); + + return TaskListItem::leftJoin('company_task_engagement_items', function ($join) use ($lead) { + $join->on('company_task_list_items.id', '=', 'company_task_engagement_items.task_list_item_id') + ->where('company_task_engagement_items.lead_id', '=', $lead->getId()); + }) + ->select( + 'company_task_list_items.*', + 'company_task_engagement_items.lead_id', + 'company_task_engagement_items.status', + 'company_task_engagement_items.engagement_start_id', + 'company_task_engagement_items.engagement_end_id', + 'company_task_engagement_items.created_at', + 'company_task_engagement_items.updated_at' + ); + } +} diff --git a/app/GraphQL/ActionEngine/Mutations/Engagements/TaskEngagementMutation.php b/app/GraphQL/ActionEngine/Mutations/Engagements/TaskEngagementMutation.php new file mode 100644 index 000000000..2e897fe1c --- /dev/null +++ b/app/GraphQL/ActionEngine/Mutations/Engagements/TaskEngagementMutation.php @@ -0,0 +1,53 @@ +user(); + $company = $user->getCurrentCompany(); + $app = app(Apps::class); + $status = $request['status']; + $lead = Lead::getByIdFromCompanyApp($request['lead_id'], $company, $app); + + $taskListItem = TaskListItem::getById($id); + + if ($taskListItem->companyAction->companies_id != $company->getId()) { + throw new ValidationException('You are not allowed to change the status of this task , company mismatch'); + } + + if ($taskListItem->companyAction->apps_id != $app->getId()) { + throw new ValidationException('You are not allowed to change the status of this task , app mismatch'); + } + + $taskEngagementItem = TaskEngagementItem::fromCompany($company) + ->fromApp($app) + ->where('task_list_item_id', $taskListItem->getId()) + ->where('lead_id', $lead->getId()) + ->first(); + + if (! $taskEngagementItem) { + $taskEngagementItem = new TaskEngagementItem(); + $taskEngagementItem->task_list_item_id = $taskListItem->getId(); + $taskEngagementItem->lead_id = $lead->getId(); + $taskEngagementItem->companies_id = $company->getId(); + $taskEngagementItem->apps_id = $app->getId(); + $taskEngagementItem->users_id = $user->getId(); + } + + $taskEngagementItem->status = $status; + + return $taskEngagementItem->saveOrFail(); + } +} diff --git a/app/GraphQL/Ecosystem/Mutations/Users/UserManagementMutation.php b/app/GraphQL/Ecosystem/Mutations/Users/UserManagementMutation.php index c30cb2e3c..9d8ef5072 100644 --- a/app/GraphQL/Ecosystem/Mutations/Users/UserManagementMutation.php +++ b/app/GraphQL/Ecosystem/Mutations/Users/UserManagementMutation.php @@ -19,6 +19,7 @@ use Kanvas\Notifications\Templates\ChangePasswordUserLogged; use Kanvas\Users\Actions\CreateInviteAction; use Kanvas\Users\Actions\ProcessInviteAction; +use Kanvas\Users\Actions\RequestDeleteAccountAction as RequestDeleteAction; use Kanvas\Users\DataTransferObject\CompleteInviteInput; use Kanvas\Users\DataTransferObject\Invite as InviteDto; use Kanvas\Users\Models\Users; @@ -184,4 +185,9 @@ public function updatePhotoProfile(mixed $rootValue, array $request): Users return $user; } + + public function requestDeleteAccount(mixed $rootValue, array $request): bool + { + return (new RequestDeleteAction(app(Apps::class), auth()->user()))->execute(); + } } diff --git a/app/GraphQL/Guild/Mutations/Peoples/PeopleManagementMutation.php b/app/GraphQL/Guild/Mutations/Peoples/PeopleManagementMutation.php index 93c94ac54..6f88fe7ec 100644 --- a/app/GraphQL/Guild/Mutations/Peoples/PeopleManagementMutation.php +++ b/app/GraphQL/Guild/Mutations/Peoples/PeopleManagementMutation.php @@ -39,6 +39,7 @@ public function create(mixed $root, array $req): ModelsPeople 'google_contact_id' => $data['google_contact_id'] ?? null, 'apple_contact_id' => $data['apple_contact_id'] ?? null, 'linkedin_contact_id' => $data['linkedin_contact_id'] ?? null, + 'tags' => $data['tags'] ?? [], 'custom_fields' => $data['custom_fields'] ?? [], ]); @@ -69,6 +70,7 @@ public function update(mixed $root, array $req): ModelsPeople 'google_contact_id' => $data['google_contact_id'] ?? null, 'apple_contact_id' => $data['apple_contact_id'] ?? null, 'linkedin_contact_id' => $data['linkedin_contact_id'] ?? null, + 'tags' => $data['tags'] ?? [], 'custom_fields' => $data['custom_fields'] ?? [], ]); diff --git a/app/GraphQL/Inventory/Mutations/Variants/Variants.php b/app/GraphQL/Inventory/Mutations/Variants/Variants.php index 27e3b5c19..e5b5deaed 100644 --- a/app/GraphQL/Inventory/Mutations/Variants/Variants.php +++ b/app/GraphQL/Inventory/Mutations/Variants/Variants.php @@ -5,13 +5,13 @@ namespace App\GraphQL\Inventory\Mutations\Variants; use Kanvas\Inventory\Attributes\Repositories\AttributesRepository; -use Kanvas\Inventory\Channels\Models\Channels; use Kanvas\Inventory\Channels\Repositories\ChannelRepository; +use Kanvas\Inventory\Channels\Services\ChannelService; use Kanvas\Inventory\Status\Repositories\StatusRepository; use Kanvas\Inventory\Variants\Actions\AddAttributeAction; use Kanvas\Inventory\Variants\Actions\AddToWarehouseAction as AddToWarehouse; -use Kanvas\Inventory\Variants\Actions\AddVariantToChannelAction; use Kanvas\Inventory\Variants\Actions\CreateVariantsAction; +use Kanvas\Inventory\Variants\Actions\UpdateVariantsAction; use Kanvas\Inventory\Variants\DataTransferObject\VariantChannel; use Kanvas\Inventory\Variants\DataTransferObject\Variants as VariantDto; use Kanvas\Inventory\Variants\DataTransferObject\VariantsWarehouses; @@ -89,6 +89,7 @@ public function create(mixed $root, array $req): VariantModel ); } } + return $variantModel; } @@ -103,17 +104,23 @@ public function update(mixed $root, array $req): VariantModel } $variant = VariantsRepository::getById((int) $req['id'], $company); - $variant->update($req['input']); + $req['input']['products_id'] = $variant->product->getId(); + $variantDto = VariantDto::viaRequest($req['input'], auth()->user()); + $variantModel = (new UpdateVariantsAction($variant, $variantDto, auth()->user()))->execute(); if (isset($req['input']['attributes'])) { - $variant->addAttributes(auth()->user(), $req['input']['attributes']); + $variantModel->addAttributes(auth()->user(), $req['input']['attributes']); } if (isset($req['input']['warehouses'])) { - WarehouseService::updateWarehouseVariant($variant, auth()->user(), $req['input']['warehouses']); + WarehouseService::updateWarehouseVariant($variantModel, auth()->user(), $req['input']['warehouses']); } - return $variant; + if (isset($req['input']['channels'])) { + ChannelService::updateChannelVariant($variantModel, $req['input']['channels']); + } + + return $variantModel; } /** diff --git a/app/GraphQL/Social/Mutations/Tags/TagsManagement.php b/app/GraphQL/Social/Mutations/Tags/TagsManagement.php new file mode 100644 index 000000000..0fda51242 --- /dev/null +++ b/app/GraphQL/Social/Mutations/Tags/TagsManagement.php @@ -0,0 +1,101 @@ +user(); + $request['input']['company'] = auth()->user()->getCurrentCompany(); + + $dto = TagData::from($request['input']); + + return (new CreateTagAction($dto))->execute(); + } + + public function update(mixed $root, array $request): Tag + { + $app = app(Apps::class); + + $tag = Tag::when(! auth()->user()->isAdmin(), function ($query) { + return $query->where('users_id', auth()->user()->getId()); + })->where('id', $request['id']) + ->fromApp($app) + ->firstOrFail(); + + $tag->update($request['input']); + + return $tag; + } + + public function delete(mixed $root, array $request): bool + { + $app = app(Apps::class); + $tag = Tag::when(! auth()->user()->isAdmin(), function ($query) { + return $query->where('users_id', auth()->user()->getId()); + }) + ->where('id', $request['id']) + ->fromApp($app) + ->firstOrFail(); + + + return $tag->delete(); + } + + public function follow(mixed $root, array $request): bool + { + $app = app(Apps::class); + $tag = Tag::getById($request['id'], $app); + $isFollowing = UsersFollowsRepository::isFollowing(auth()->user(), $tag); + if ($isFollowing) { + return (new UnFollowAction(auth()->user(), $tag))->execute(); + } else { + return (bool)(new FollowAction(auth()->user(), $tag))->execute(); + } + } + + public function attachTagToEntity(mixed $root, array $request): bool + { + $app = app(Apps::class); + $tag = Tag::getById($request['input']['tag_id'], $app); + $user = auth()->user(); + + $systemModule = SystemModules::getByUuid($request['input']['system_module_uuid'], $app); + + //$entity = $systemModule->model_name::getById((int)$request['input']['entity_id'], $app); + $entity = SystemModulesRepository::getEntityFromInput( + new SystemModuleEntityInput( + $systemModule->name, + $systemModule->uuid, + $request['input']['entity_id'] + ), + $user, + useCompanyReference: false + ); + + $tag->entities()->attach($entity->getId(), [ + //'entity_namespace' => $systemModule->model_name, + //'apps_id' => $tag->apps_id, + //'companies_id' => $user->getCurrentCompany()->getId(), + 'users_id' => $user->getId(), + 'taggable_type' => $systemModule->model_name, + ]); + + return true; + } +} diff --git a/app/GraphQL/Social/Queries/Tags/TagsQueries.php b/app/GraphQL/Social/Queries/Tags/TagsQueries.php new file mode 100644 index 000000000..e9196babb --- /dev/null +++ b/app/GraphQL/Social/Queries/Tags/TagsQueries.php @@ -0,0 +1,25 @@ +where('entity_id', $root->getKey()); + $query->where('taggable_type', $systemModule->model_name); + $query->where('apps_id', $systemModule->apps_id); + })->where('is_deleted', StateEnums::NO->getValue()); + } +} diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index f18ee81a4..8dc3fed64 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -29,18 +29,20 @@ use Kanvas\Inventory\Warehouses\Observers\WarehouseObserver; use Kanvas\Notifications\Events\PushNotificationsEvent; use Kanvas\Notifications\Listeners\NotificationsListener; +use Kanvas\Sessions\Models\Sessions; +use Kanvas\Sessions\Observers\SessionObserver; use Kanvas\Social\Messages\Models\UserMessage; use Kanvas\Social\Messages\Models\UserMessageActivity; use Kanvas\Social\Messages\Observers\UserMessageActivityObserver; use Kanvas\Social\Messages\Observers\UserMessageObserver; use Kanvas\Social\UsersLists\Models\UserList; use Kanvas\Social\UsersLists\Observers\UsersListsObserver; +use Kanvas\Users\Models\UserCompanyApps; use Kanvas\Users\Models\Users; use Kanvas\Users\Models\UsersAssociatedApps; use Kanvas\Users\Observers\UsersAssociatedAppsObserver; -use Kanvas\Users\Observers\UsersObserver; -use Kanvas\Users\Models\UserCompanyApps; use Kanvas\Users\Observers\UsersAssociatedCompaniesObserver; +use Kanvas\Users\Observers\UsersObserver; class EventServiceProvider extends ServiceProvider { diff --git a/clear_cache.sh b/clear_cache.sh new file mode 100644 index 000000000..d687758f1 --- /dev/null +++ b/clear_cache.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +docker exec -i phpkanvas-ecosystem php artisan lighthouse:cache +docker exec -i phpkanvas-ecosystem php artisan config:cache \ No newline at end of file diff --git a/composer.json b/composer.json index bbded12e7..ac883ea1e 100644 --- a/composer.json +++ b/composer.json @@ -75,6 +75,7 @@ "Kanvas\\Souk\\": "src/Domains/Souk", "Kanvas\\Workflow\\": "src/Domains/Workflow", "Kanvas\\Connectors\\": "src/Domains/Connectors", + "Kanvas\\ActionEngine\\": "src/Domains/ActionEngine", "Database\\Factories\\": "database/factories/", "Database\\Seeders\\": "database/seeders/" } diff --git a/composer.lock b/composer.lock index 2f3fde0b8..10b2aae9d 100644 --- a/composer.lock +++ b/composer.lock @@ -1195,16 +1195,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.310.0", + "version": "3.314.2", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "8ac02d36c609c6507136e5996f60cfd5152b4fd7" + "reference": "1f5ccf9c73a66fb85c7c5de8f11ed69e44c636ef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/8ac02d36c609c6507136e5996f60cfd5152b4fd7", - "reference": "8ac02d36c609c6507136e5996f60cfd5152b4fd7", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/1f5ccf9c73a66fb85c7c5de8f11ed69e44c636ef", + "reference": "1f5ccf9c73a66fb85c7c5de8f11ed69e44c636ef", "shasum": "" }, "require": { @@ -1284,9 +1284,9 @@ "support": { "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", "issues": "https://github.com/aws/aws-sdk-php/issues", - "source": "https://github.com/aws/aws-sdk-php/tree/3.310.0" + "source": "https://github.com/aws/aws-sdk-php/tree/3.314.2" }, - "time": "2024-06-04T18:05:36+00:00" + "time": "2024-06-14T18:11:34+00:00" }, { "name": "berkayk/onesignal-laravel", @@ -2503,16 +2503,16 @@ }, { "name": "google/apiclient-services", - "version": "v0.358.0", + "version": "v0.359.0", "source": { "type": "git", "url": "https://github.com/googleapis/google-api-php-client-services.git", - "reference": "a6daf60ee25cb45b6e3dbd04b62d1df39a609fbd" + "reference": "e975e6d0efa47f7e49280c4ea7fd6a93b6d7e338" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/a6daf60ee25cb45b6e3dbd04b62d1df39a609fbd", - "reference": "a6daf60ee25cb45b6e3dbd04b62d1df39a609fbd", + "url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/e975e6d0efa47f7e49280c4ea7fd6a93b6d7e338", + "reference": "e975e6d0efa47f7e49280c4ea7fd6a93b6d7e338", "shasum": "" }, "require": { @@ -2541,9 +2541,9 @@ ], "support": { "issues": "https://github.com/googleapis/google-api-php-client-services/issues", - "source": "https://github.com/googleapis/google-api-php-client-services/tree/v0.358.0" + "source": "https://github.com/googleapis/google-api-php-client-services/tree/v0.359.0" }, - "time": "2024-06-03T01:02:16+00:00" + "time": "2024-06-10T01:02:17+00:00" }, { "name": "google/auth", @@ -2607,16 +2607,16 @@ }, { "name": "google/cloud-core", - "version": "v1.58.2", + "version": "v1.59.0", "source": { "type": "git", "url": "https://github.com/googleapis/google-cloud-php-core.git", - "reference": "56df58d70cca3c429e2cfd32270e42596f6c002f" + "reference": "56d31be2663780a7ed0736bee60d01f047bf15c0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/googleapis/google-cloud-php-core/zipball/56df58d70cca3c429e2cfd32270e42596f6c002f", - "reference": "56df58d70cca3c429e2cfd32270e42596f6c002f", + "url": "https://api.github.com/repos/googleapis/google-cloud-php-core/zipball/56d31be2663780a7ed0736bee60d01f047bf15c0", + "reference": "56d31be2663780a7ed0736bee60d01f047bf15c0", "shasum": "" }, "require": { @@ -2667,9 +2667,9 @@ ], "description": "Google Cloud PHP shared dependency, providing functionality useful to all components.", "support": { - "source": "https://github.com/googleapis/google-cloud-php-core/tree/v1.58.2" + "source": "https://github.com/googleapis/google-cloud-php-core/tree/v1.59.0" }, - "time": "2024-06-01T03:14:01+00:00" + "time": "2024-06-07T22:33:41+00:00" }, { "name": "google/cloud-storage", @@ -2928,16 +2928,16 @@ }, { "name": "google/protobuf", - "version": "v4.27.0", + "version": "v4.27.1", "source": { "type": "git", "url": "https://github.com/protocolbuffers/protobuf-php.git", - "reference": "28cf84ac822e2c8f82edd7c84cc792cacb822faa" + "reference": "c471e2b3afe61bf41f22d1ca926b24e7ce96c598" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/28cf84ac822e2c8f82edd7c84cc792cacb822faa", - "reference": "28cf84ac822e2c8f82edd7c84cc792cacb822faa", + "url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/c471e2b3afe61bf41f22d1ca926b24e7ce96c598", + "reference": "c471e2b3afe61bf41f22d1ca926b24e7ce96c598", "shasum": "" }, "require": { @@ -2966,9 +2966,9 @@ "proto" ], "support": { - "source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.27.0" + "source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.27.1" }, - "time": "2024-05-22T21:40:13+00:00" + "time": "2024-06-05T16:59:28+00:00" }, { "name": "graham-campbell/result-type", @@ -6381,16 +6381,16 @@ }, { "name": "nuwave/lighthouse", - "version": "v6.36.3", + "version": "v6.37.0", "source": { "type": "git", "url": "https://github.com/nuwave/lighthouse.git", - "reference": "dc3125d6b29da7552dfcf61dc5470aedc1cc533a" + "reference": "7f9a6d844ab8ce83f50805973a6bb066e449f953" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nuwave/lighthouse/zipball/dc3125d6b29da7552dfcf61dc5470aedc1cc533a", - "reference": "dc3125d6b29da7552dfcf61dc5470aedc1cc533a", + "url": "https://api.github.com/repos/nuwave/lighthouse/zipball/7f9a6d844ab8ce83f50805973a6bb066e449f953", + "reference": "7f9a6d844ab8ce83f50805973a6bb066e449f953", "shasum": "" }, "require": { @@ -6427,7 +6427,7 @@ "mll-lab/php-cs-fixer-config": "^5", "mockery/mockery": "^1.5", "nesbot/carbon": "^2.62.1", - "orchestra/testbench": "^7.7 || ^8.8 || ^9", + "orchestra/testbench": "^7.11 || ^8.8 || ^9", "phpbench/phpbench": "^1.2.6", "phpstan/extension-installer": "^1", "phpstan/phpstan": "^1.10.3", @@ -6511,7 +6511,7 @@ "type": "patreon" } ], - "time": "2024-05-05T09:15:52+00:00" + "time": "2024-06-11T09:58:58+00:00" }, { "name": "nyholm/psr7", @@ -8018,16 +8018,16 @@ }, { "name": "psy/psysh", - "version": "v0.12.3", + "version": "v0.12.4", "source": { "type": "git", "url": "https://github.com/bobthecow/psysh.git", - "reference": "b6b6cce7d3ee8fbf31843edce5e8f5a72eff4a73" + "reference": "2fd717afa05341b4f8152547f142cd2f130f6818" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/b6b6cce7d3ee8fbf31843edce5e8f5a72eff4a73", - "reference": "b6b6cce7d3ee8fbf31843edce5e8f5a72eff4a73", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/2fd717afa05341b4f8152547f142cd2f130f6818", + "reference": "2fd717afa05341b4f8152547f142cd2f130f6818", "shasum": "" }, "require": { @@ -8091,9 +8091,9 @@ ], "support": { "issues": "https://github.com/bobthecow/psysh/issues", - "source": "https://github.com/bobthecow/psysh/tree/v0.12.3" + "source": "https://github.com/bobthecow/psysh/tree/v0.12.4" }, - "time": "2024-04-02T15:57:53+00:00" + "time": "2024-06-10T01:18:23+00:00" }, { "name": "ralouphie/getallheaders", @@ -8667,16 +8667,16 @@ }, { "name": "sentry/sentry-laravel", - "version": "4.5.1", + "version": "4.6.0", "source": { "type": "git", "url": "https://github.com/getsentry/sentry-laravel.git", - "reference": "a15b2f5fa7b446f006eb93134e237c38a11776cf" + "reference": "75c11944211ce7707bb92e717c5bda93a1759438" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/getsentry/sentry-laravel/zipball/a15b2f5fa7b446f006eb93134e237c38a11776cf", - "reference": "a15b2f5fa7b446f006eb93134e237c38a11776cf", + "url": "https://api.github.com/repos/getsentry/sentry-laravel/zipball/75c11944211ce7707bb92e717c5bda93a1759438", + "reference": "75c11944211ce7707bb92e717c5bda93a1759438", "shasum": "" }, "require": { @@ -8740,7 +8740,7 @@ ], "support": { "issues": "https://github.com/getsentry/sentry-laravel/issues", - "source": "https://github.com/getsentry/sentry-laravel/tree/4.5.1" + "source": "https://github.com/getsentry/sentry-laravel/tree/4.6.0" }, "funding": [ { @@ -8752,7 +8752,7 @@ "type": "custom" } ], - "time": "2024-04-24T08:58:34+00:00" + "time": "2024-06-11T12:23:24+00:00" }, { "name": "shopify/shopify-api", @@ -8982,16 +8982,16 @@ }, { "name": "spatie/laravel-data", - "version": "4.6.0", + "version": "4.7.0", "source": { "type": "git", "url": "https://github.com/spatie/laravel-data.git", - "reference": "ee513f693f8ab8f915dc26cae079d7b2e5999a65" + "reference": "92af136b14f57c72b1b8e36cd5f7e274fb56385a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-data/zipball/ee513f693f8ab8f915dc26cae079d7b2e5999a65", - "reference": "ee513f693f8ab8f915dc26cae079d7b2e5999a65", + "url": "https://api.github.com/repos/spatie/laravel-data/zipball/92af136b14f57c72b1b8e36cd5f7e274fb56385a", + "reference": "92af136b14f57c72b1b8e36cd5f7e274fb56385a", "shasum": "" }, "require": { @@ -9004,7 +9004,7 @@ "require-dev": { "fakerphp/faker": "^1.14", "friendsofphp/php-cs-fixer": "^3.0", - "inertiajs/inertia-laravel": "dev-master#4508fd1", + "inertiajs/inertia-laravel": "^1.2", "livewire/livewire": "^3.0", "mockery/mockery": "^1.6", "nesbot/carbon": "^2.63", @@ -9054,7 +9054,7 @@ ], "support": { "issues": "https://github.com/spatie/laravel-data/issues", - "source": "https://github.com/spatie/laravel-data/tree/4.6.0" + "source": "https://github.com/spatie/laravel-data/tree/4.7.0" }, "funding": [ { @@ -9062,7 +9062,7 @@ "type": "github" } ], - "time": "2024-05-03T15:01:04+00:00" + "time": "2024-06-13T12:07:24+00:00" }, { "name": "spatie/laravel-google-cloud-storage", @@ -12991,16 +12991,16 @@ }, { "name": "twilio/sdk", - "version": "8.1.0", + "version": "8.1.1", "source": { "type": "git", "url": "https://github.com/twilio/twilio-php.git", - "reference": "547e6ceca9a1491a645065f344ddb592a3456298" + "reference": "d59f57a548e2659fb305be11690b7b0aaf40a84c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twilio/twilio-php/zipball/547e6ceca9a1491a645065f344ddb592a3456298", - "reference": "547e6ceca9a1491a645065f344ddb592a3456298", + "url": "https://api.github.com/repos/twilio/twilio-php/zipball/d59f57a548e2659fb305be11690b7b0aaf40a84c", + "reference": "d59f57a548e2659fb305be11690b7b0aaf40a84c", "shasum": "" }, "require": { @@ -13038,9 +13038,9 @@ ], "support": { "issues": "https://github.com/twilio/twilio-php/issues", - "source": "https://github.com/twilio/twilio-php/tree/8.1.0" + "source": "https://github.com/twilio/twilio-php/tree/8.1.1" }, - "time": "2024-05-24T11:10:19+00:00" + "time": "2024-06-06T11:46:47+00:00" }, { "name": "vladimir-yuldashev/laravel-queue-rabbitmq", @@ -13446,16 +13446,16 @@ }, { "name": "webonyx/graphql-php", - "version": "v15.11.1", + "version": "v15.12.2", "source": { "type": "git", "url": "https://github.com/webonyx/graphql-php.git", - "reference": "ab4ff2719b101dc3bfc3aaaf800edc21a98c56dc" + "reference": "21bc031e4b0c2035e4d4ddb88ffef98e2f11c97f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webonyx/graphql-php/zipball/ab4ff2719b101dc3bfc3aaaf800edc21a98c56dc", - "reference": "ab4ff2719b101dc3bfc3aaaf800edc21a98c56dc", + "url": "https://api.github.com/repos/webonyx/graphql-php/zipball/21bc031e4b0c2035e4d4ddb88ffef98e2f11c97f", + "reference": "21bc031e4b0c2035e4d4ddb88ffef98e2f11c97f", "shasum": "" }, "require": { @@ -13468,14 +13468,14 @@ "amphp/http-server": "^2.1", "dms/phpunit-arraysubset-asserts": "dev-master", "ergebnis/composer-normalize": "^2.28", - "friendsofphp/php-cs-fixer": "3.51.0", + "friendsofphp/php-cs-fixer": "3.58.1", "mll-lab/php-cs-fixer-config": "^5", "nyholm/psr7": "^1.5", "phpbench/phpbench": "^1.2", "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "1.10.59", - "phpstan/phpstan-phpunit": "1.3.16", - "phpstan/phpstan-strict-rules": "1.5.2", + "phpstan/phpstan": "1.11.4", + "phpstan/phpstan-phpunit": "1.4.0", + "phpstan/phpstan-strict-rules": "1.6.0", "phpunit/phpunit": "^9.5 || ^10", "psr/http-message": "^1 || ^2", "react/http": "^1.6", @@ -13508,7 +13508,7 @@ ], "support": { "issues": "https://github.com/webonyx/graphql-php/issues", - "source": "https://github.com/webonyx/graphql-php/tree/v15.11.1" + "source": "https://github.com/webonyx/graphql-php/tree/v15.12.2" }, "funding": [ { @@ -13516,7 +13516,7 @@ "type": "open_collective" } ], - "time": "2024-03-11T10:21:05+00:00" + "time": "2024-06-13T07:28:14+00:00" } ], "packages-dev": [ @@ -13867,16 +13867,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.11.1", + "version": "1.12.0", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" + "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", + "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", "shasum": "" }, "require": { @@ -13884,11 +13884,12 @@ }, "conflict": { "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3,<3.2.2" + "doctrine/common": "<2.13.3 || >=3 <3.2.2" }, "require-dev": { "doctrine/collections": "^1.6.8", "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" }, "type": "library", @@ -13914,7 +13915,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" + "source": "https://github.com/myclabs/DeepCopy/tree/1.12.0" }, "funding": [ { @@ -13922,7 +13923,7 @@ "type": "tidelift" } ], - "time": "2023-03-08T13:26:56+00:00" + "time": "2024-06-12T14:39:25+00:00" }, { "name": "nunomaduro/collision", @@ -14141,16 +14142,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.11.3", + "version": "1.11.4", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "e64220a05c1209fc856d58e789c3b7a32c0bb9a5" + "reference": "9100a76ce8015b9aa7125b9171ae3a76887b6c82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/e64220a05c1209fc856d58e789c3b7a32c0bb9a5", - "reference": "e64220a05c1209fc856d58e789c3b7a32c0bb9a5", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/9100a76ce8015b9aa7125b9171ae3a76887b6c82", + "reference": "9100a76ce8015b9aa7125b9171ae3a76887b6c82", "shasum": "" }, "require": { @@ -14195,7 +14196,7 @@ "type": "github" } ], - "time": "2024-05-31T13:53:37+00:00" + "time": "2024-06-06T12:19:22+00:00" }, { "name": "phpunit/php-code-coverage", @@ -14522,16 +14523,16 @@ }, { "name": "phpunit/phpunit", - "version": "11.1.3", + "version": "11.2.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "d475be032238173ca3b0a516f5cc291d174708ae" + "reference": "3e1843a58adc9c433ee6170bdee7d615f7ccc20b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d475be032238173ca3b0a516f5cc291d174708ae", - "reference": "d475be032238173ca3b0a516f5cc291d174708ae", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3e1843a58adc9c433ee6170bdee7d615f7ccc20b", + "reference": "3e1843a58adc9c433ee6170bdee7d615f7ccc20b", "shasum": "" }, "require": { @@ -14570,7 +14571,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "11.1-dev" + "dev-main": "11.2-dev" } }, "autoload": { @@ -14602,7 +14603,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/11.1.3" + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.2.2" }, "funding": [ { @@ -14618,7 +14619,7 @@ "type": "tidelift" } ], - "time": "2024-04-24T06:34:25+00:00" + "time": "2024-06-15T09:14:53+00:00" }, { "name": "sebastian/cli-parser", diff --git a/database/migrations/2024_05_31_045942_request_deleted_account.php b/database/migrations/2024_05_31_045942_request_deleted_account.php new file mode 100644 index 000000000..3e4fb6d00 --- /dev/null +++ b/database/migrations/2024_05_31_045942_request_deleted_account.php @@ -0,0 +1,33 @@ +id(); + $table->bigInteger('apps_id')->unsigned()->index('apps_id'); + $table->bigInteger('users_id')->unsigned()->index('users_id'); + $table->string('email')->index('email'); + $table->string('reason')->nullable(); + $table->dateTime('request_date')->index('request_date'); + $table->integer('is_deleted')->default(0); + $table->timestamps(); + $table->index(['apps_id', 'users_id', 'request_date']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('request_deleted_accounts'); + } +}; diff --git a/database/migrations/ActionEngine/2024_06_03_011634_create_task_list.php b/database/migrations/ActionEngine/2024_06_03_011634_create_task_list.php new file mode 100644 index 000000000..d8117b9b1 --- /dev/null +++ b/database/migrations/ActionEngine/2024_06_03_011634_create_task_list.php @@ -0,0 +1,46 @@ +id(); + $table->uuid('uuid')->index(); + $table->bigInteger('apps_id')->index(); + $table->bigInteger('companies_id')->index(); + $table->bigInteger('users_id')->index(); + $table->string('name'); + $table->json('config')->nullable(); + $table->timestamps(); + $table->tinyInteger('is_deleted')->default(0)->index(); + }); + + Schema::create('company_task_list_items', function (Blueprint $table) { + $table->id(); + $table->foreignId('task_list_id')->constrained('company_task_list')->onDelete('cascade'); + $table->string('name'); + $table->bigInteger('companies_action_id')->index(); + $table->enum('status', ['pending', 'in_progress', 'completed'])->default('pending')->comment('pending, in_progress, completed')->index(); + $table->json('config')->nullable(); + $table->decimal('weight', 8, 2)->default(0)->index(); + $table->timestamps(); + $table->tinyInteger('is_deleted')->default(0)->index(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('company_task_list'); + Schema::dropIfExists('company_task_list_items'); + } +}; diff --git a/database/migrations/ActionEngine/2024_06_08_173343_company_task_engagement_items.php b/database/migrations/ActionEngine/2024_06_08_173343_company_task_engagement_items.php new file mode 100644 index 000000000..7e6c13fb1 --- /dev/null +++ b/database/migrations/ActionEngine/2024_06_08_173343_company_task_engagement_items.php @@ -0,0 +1,45 @@ +bigInteger('task_list_item_id')->index(); + $table->bigInteger('lead_id')->index(); + $table->bigInteger('companies_id')->index(); + $table->bigInteger('apps_id')->index(); + $table->bigInteger('users_id')->index(); + $table->enum('status', ['pending', 'in_progress', 'completed'])->default('pending')->comment('pending, in_progress, completed')->index(); + $table->bigInteger('engagement_start_id')->nullable()->index(); + $table->bigInteger('engagement_end_id')->nullable()->index(); + $table->json('config')->nullable(); + $table->timestamps(); + $table->tinyInteger('is_deleted')->default(0)->index(); + + $table->primary(['task_list_item_id', 'lead_id']); + $table->index(['task_list_item_id', 'lead_id', 'companies_id'], 'task_list_company_index'); + $table->index(['task_list_item_id', 'lead_id', 'apps_id'], 'task_list_apps_index'); + $table->index(['task_list_item_id', 'lead_id', 'users_id'], 'task_list_users_index'); + $table->index(['task_list_item_id', 'lead_id', 'engagement_start_id'], 'task_list_engagement_start_index'); + $table->index(['task_list_item_id', 'lead_id', 'engagement_end_id'], 'task_list_engagement_end_index'); + $table->index(['task_list_item_id', 'lead_id', 'apps_id', 'companies_id'], 'task_list_apps_company_index'); + $table->index(['task_list_item_id', 'lead_id', 'status'], 'task_list_status_index'); + $table->index(['task_list_item_id', 'lead_id', 'is_deleted'], 'task_list_deleted_index'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('company_task_engagement_items'); + } +}; diff --git a/database/migrations/Social/2024_06_05_154618_tags_entities.php b/database/migrations/Social/2024_06_05_154618_tags_entities.php new file mode 100644 index 000000000..fcfaf4ab0 --- /dev/null +++ b/database/migrations/Social/2024_06_05_154618_tags_entities.php @@ -0,0 +1,37 @@ +id(); + $table->integer('tags_id')->index(); + $table->integer('entity_id')->index(); + $table->string('entity_namespace')->index(); //@todo remove + $table->integer('companies_id')->index(); + $table->integer('apps_id')->index(); + $table->integer('users_id')->index(); + $table->boolean('is_deleted')->default(0); + $table->timestamp('created_at')->index()->useCurrent(); + $table->datetime('updated_at')->nullable()->index(); + $table->index(['tags_id', 'entity_id', 'is_deleted'], 'tags_entities_index_tag'); + $table->index(['tags_id', 'entity_id', 'apps_id', 'is_deleted'], 'tags_entities_app_index'); + $table->index(['tags_id', 'entity_id', 'companies_id', 'apps_id', 'is_deleted'], 'tags_entities_index'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('tags_entities'); + } +}; diff --git a/database/migrations/Social/2024_06_05_172750_taggable_type.php b/database/migrations/Social/2024_06_05_172750_taggable_type.php new file mode 100644 index 000000000..69d5093c0 --- /dev/null +++ b/database/migrations/Social/2024_06_05_172750_taggable_type.php @@ -0,0 +1,27 @@ +string('taggable_type')->nullable()->after('entity_id'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('tags_entities', function (Blueprint $table) { + $table->dropColumn('taggable_type'); + }); + } +}; diff --git a/database/migrations/Social/2024_06_12_041705_refact_tags_entities.php b/database/migrations/Social/2024_06_12_041705_refact_tags_entities.php new file mode 100644 index 000000000..00313e3ee --- /dev/null +++ b/database/migrations/Social/2024_06_12_041705_refact_tags_entities.php @@ -0,0 +1,31 @@ +dropColumn('entity_namespace'); + $table->dropColumn('apps_id'); + $table->dropColumn('companies_id'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('tags_entities', function (Blueprint $table) { + $table->string('entity_namespace', 255)->nullable(); + $table->integer('apps_id')->nullable(); + $table->integer('companies_id')->nullable(); + }); + } +}; diff --git a/database/seeders/SystemModuleSeeder.php b/database/seeders/SystemModuleSeeder.php index 871be5015..95fa17ac5 100755 --- a/database/seeders/SystemModuleSeeder.php +++ b/database/seeders/SystemModuleSeeder.php @@ -20,7 +20,7 @@ public function run() [ 'name' => 'Companies', 'slug' => 'companies', - 'model_name' => 'Kanvas\\Models\\Companies', + 'model_name' => 'Kanvas\\Companies\\Models\\Companies', 'uuid' => (string) Str::uuid(), 'apps_id' => '1', 'parents_id' => '0', diff --git a/development.Dockerfile b/development.Dockerfile index a96eff554..cc7c89b4b 100644 --- a/development.Dockerfile +++ b/development.Dockerfile @@ -27,29 +27,24 @@ RUN apt-get update && apt-get install -y \ vim -# Set working directory -WORKDIR /app - -# RUN groupadd -g 1000 unit -# RUN useradd -u 1000 -ms /bin/bash -g unit unit - -# Copy code to /var/www -COPY . /app -# COPY chown -R unit:unit /var/www/html/ - -# Install composer RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer -RUN composer install --optimize-autoloader - +COPY . /var/www/html/ +# COPY chown -R unit:unit /var/www/html/ # add root to www group # RUN chmod -R ug+w var/www/html/storage +# RUN cp docker/php-fpm.conf /usr/local/etc/php-fpm.d/zzz-php-fpm-production.conf + +WORKDIR /var/www/html/ RUN cp docker/docker-php-ext-opcache-prod.ini /usr/local/etc/php/conf.d/docker-php-ext-opcache.ini RUN cp docker/php.ini /usr/local/etc/php/conf.d/zx-app-config.ini -# RUN cp docker/php-fpm.conf /usr/local/etc/php-fpm.d/zzz-php-fpm-production.conf -WORKDIR /var/www/html/ +RUN chmod -R 755 /var/www/html/ +RUN chmod -R 777 /var/www/html/storage/ +RUN chmod -R 777 /var/www/html/storage/logs/ + +RUN composer install --optimize-autoloader EXPOSE 8080 \ No newline at end of file diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml new file mode 100644 index 000000000..6d37afdbb --- /dev/null +++ b/docker-compose.dev.yml @@ -0,0 +1,132 @@ +services: + php: + container_name: php${APP_CONTAINER_NAME} + build: + context: . + dockerfile: development.Dockerfile + extra_hosts: + - 'host.docker.internal:host-gateway' + environment: + WWWUSER: '${WWWUSER}' + LARAVEL_SAIL: 1 + XDEBUG_MODE: '${SAIL_XDEBUG_MODE:-off}' + XDEBUG_CONFIG: '${SAIL_XDEBUG_CONFIG:-client_host=host.docker.internal}' + volumes: + - '.:/var/www/html' + - ./docker/docker-php-ext-opcache.ini:/usr/local/etc/php/conf.d/docker-php-ext-opcache.ini + - ./docker/php.ini:/usr/local/etc/php/conf.d/xz-custom.ini + networks: + - sail + queue: + container_name: queue + build: + context: . + dockerfile: development.Dockerfile + extra_hosts: + - 'host.docker.internal:host-gateway' + command: ["sh", "-c", "php artisan config:cache && php artisan queue:work --tries=3 --timeout=1750"] + environment: + WWWUSER: '${WWWUSER}' + LARAVEL_SAIL: 1 + XDEBUG_MODE: '${SAIL_XDEBUG_MODE:-off}' + XDEBUG_CONFIG: '${SAIL_XDEBUG_CONFIG:-client_host=host.docker.internal}' + volumes: + - '.:/var/www/html' + - ./docker/docker-php-ext-opcache.ini:/usr/local/etc/php/conf.d/docker-php-ext-opcache.ini + - ./docker/php.ini:/usr/local/etc/php/conf.d/xz-custom.ini + networks: + - sail + queue-social: + container_name: queue-social + build: + context: . + dockerfile: development.Dockerfile + extra_hosts: + - 'host.docker.internal:host-gateway' + command: ["sh", "-c", "php artisan config:cache && php artisan queue:work --queue kanvas-social --tries=3 --timeout=1750"] + environment: + WWWUSER: '${WWWUSER}' + LARAVEL_SAIL: 1 + XDEBUG_MODE: '${SAIL_XDEBUG_MODE:-off}' + XDEBUG_CONFIG: '${SAIL_XDEBUG_CONFIG:-client_host=host.docker.internal}' + volumes: + - '.:/var/www/html' + - ./docker/docker-php-ext-opcache.ini:/usr/local/etc/php/conf.d/docker-php-ext-opcache.ini + - ./docker/php.ini:/usr/local/etc/php/conf.d/xz-custom.ini + networks: + - sail + queue-notifications: + container_name: queue-notifications + build: + context: . + dockerfile: development.Dockerfile + extra_hosts: + - 'host.docker.internal:host-gateway' + command: ["sh", "-c", "php artisan config:cache && php artisan queue:work --queue notifications --tries=3 --timeout=1750"] + environment: + WWWUSER: '${WWWUSER}' + LARAVEL_SAIL: 1 + XDEBUG_MODE: '${SAIL_XDEBUG_MODE:-off}' + XDEBUG_CONFIG: '${SAIL_XDEBUG_CONFIG:-client_host=host.docker.internal}' + volumes: + - '.:/var/www/html' + - ./docker/docker-php-ext-opcache.ini:/usr/local/etc/php/conf.d/docker-php-ext-opcache.ini + - ./docker/php.ini:/usr/local/etc/php/conf.d/xz-custom.ini + networks: + - sail + laravel-scheduler: + container_name: laravel-scheduler + build: + context: . + dockerfile: development.Dockerfile + extra_hosts: + - 'host.docker.internal:host-gateway' + command: ["sh", "-c", "php artisan config:cache && php artisan schedule:work"] + environment: + WWWUSER: '${WWWUSER}' + LARAVEL_SAIL: 1 + XDEBUG_MODE: '${SAIL_XDEBUG_MODE:-off}' + XDEBUG_CONFIG: '${SAIL_XDEBUG_CONFIG:-client_host=host.docker.internal}' + volumes: + - '.:/var/www/html' + - ./docker/docker-php-ext-opcache.ini:/usr/local/etc/php/conf.d/docker-php-ext-opcache.ini + - ./docker/php.ini:/usr/local/etc/php/conf.d/xz-custom.ini + networks: + - sail + nginx: + image: nginx:latest + container_name: nginx${APP_CONTAINER_NAME} + ports: + - "80:80" + links: + - php + volumes: + - '.:/var/www/html' + - ./docker/nginx.conf:/etc/nginx/conf.d/default.conf + networks: + - sail + depends_on: + - php + healthcheck: + test: ["CMD", "service", "nginx", "status"] + retries: 3 + timeout: 5s + redis: + container_name: redis${APP_CONTAINER_NAME} + image: 'redis:alpine' + ports: + - '${FORWARD_REDIS_PORT:-6379}:6379' + volumes: + - 'sail-redis:/data' + networks: + - sail + healthcheck: + test: [ "CMD", "redis-cli", "ping" ] + retries: 3 + timeout: 5s +networks: + sail: + driver: bridge +volumes: + sail-redis: + driver: local diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 7ad5618af..406df4de3 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -6,7 +6,7 @@ services: dockerfile: 1.x.Dockerfile extra_hosts: - 'host.docker.internal:host-gateway' - # command: ["sh", "-c", "php artisan lighthouse:cache && php artisan config:cache"] + command: ["sh", "-c", "php artisan lighthouse:cache && php artisan config:cache"] environment: WWWUSER: '${WWWUSER}' LARAVEL_SAIL: 1 diff --git a/docker-compose.yml b/docker-compose.yml index d06e1815c..74a3129b3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,3 @@ -version: '3' services: php: container_name: php${APP_CONTAINER_NAME} diff --git a/graphql/schema.graphql b/graphql/schema.graphql index a68f99d0b..0cf3cc5bb 100755 --- a/graphql/schema.graphql +++ b/graphql/schema.graphql @@ -28,4 +28,5 @@ scalar Null @scalar(class: "MLL\\GraphQLScalars\\NullScalar") #import schemas/Social/*.graphql #import schemas/ContentEngine/*.graphql #import schemas/Souk/*.graphql +#import schemas/ActionEngine/*.graphql #import schemas/Ecosystem/Admin/*.graphql diff --git a/graphql/schemas/ActionEngine/action.graphql b/graphql/schemas/ActionEngine/action.graphql new file mode 100644 index 000000000..0ce94cf87 --- /dev/null +++ b/graphql/schemas/ActionEngine/action.graphql @@ -0,0 +1,30 @@ +type Action { + id: ID! + name: String! + slug: String! + description: String + icon: String + form_fields: Mixed + form_config: Mixed + is_active: Boolean! + is_published: Boolean! + collects_info: Boolean! + config: Mixed + parent: Action + children: [Action!] +} + +type CompanyAction { + id: ID! + action: Action! + company: Company! + name: String! + description: String + form_config: Mixed + status: String + is_active: Boolean! + is_published: Boolean! + weight: Float! + parent: CompanyAction + children: [CompanyAction!] +} diff --git a/graphql/schemas/ActionEngine/engagement.graphql b/graphql/schemas/ActionEngine/engagement.graphql new file mode 100644 index 000000000..1291a66c4 --- /dev/null +++ b/graphql/schemas/ActionEngine/engagement.graphql @@ -0,0 +1,11 @@ +type Engagement { + id: ID! + uuid: String! + user: User! @belongsTo + company_action: CompanyAction! @belongsTo + message: Message! @belongsTo + lead: Lead! @belongsTo + people: People @belongsTo + entity_uuid: String! + slug: String! +} diff --git a/graphql/schemas/ActionEngine/task.graphql b/graphql/schemas/ActionEngine/task.graphql new file mode 100644 index 000000000..69bc1e1ea --- /dev/null +++ b/graphql/schemas/ActionEngine/task.graphql @@ -0,0 +1,46 @@ +type TaskList { + id: ID! + name: String! + company: Company! + config: Mixed + tasks: [TaskListItem!] @hasMany +} + +type TaskListItem { + id: ID! + name: String! + status: String! + due_date: Date + completed_date: Date + config: Mixed + action: CompanyAction! + weight: Float! +} + +type LeadTaskEngagementItem { + id: ID! + name: String! + company_action: CompanyAction! @belongsTo(relation: "companyAction") + status: String + config: Mixed + engagement_start: Engagement @hasOne(relation: "engagementStart") + engagement_end: Engagement @hasOne(relation: "engagementEnd") + created_at: DateTime + updated_at: DateTime +} + +extend type Query @guard { + leadTaskItems(lead_id: ID!): [LeadTaskEngagementItem!]! + @paginate( + builder: "App\\GraphQL\\ActionEngine\\Builders\\Engagements\\TaskEngagementBuilder@getLeadTaskItems" + defaultCount: 25 + scopes: ["notDeleted"] + ) +} + +extend type Mutation @guard { + changeTaskEngagementItemStatus(id: ID!, lead_id: ID!, status: String!): Boolean! + @field( + resolver: "App\\GraphQL\\ActionEngine\\Mutations\\Engagements\\TaskEngagementMutation@changeEngagementTaskItemStatus" + ) +} \ No newline at end of file diff --git a/graphql/schemas/Ecosystem/user.graphql b/graphql/schemas/Ecosystem/user.graphql index c3bdde818..7553668ec 100644 --- a/graphql/schemas/Ecosystem/user.graphql +++ b/graphql/schemas/Ecosystem/user.graphql @@ -236,6 +236,11 @@ extend type Mutation { @field( resolver: "App\\GraphQL\\Ecosystem\\Mutations\\Users\\TwoFactorAuthMutation@setToggleTwoFactorAuthIn30Days" ) + requestDeleteAccount: Boolean! + @guard + @field( + resolver: "App\\GraphQL\\Ecosystem\\Mutations\\Users\\UserManagementMutation@requestDeleteAccount" + ) } type Query @guard { diff --git a/graphql/schemas/Guild/lead.graphql b/graphql/schemas/Guild/lead.graphql index ee2465d94..93e10bd34 100644 --- a/graphql/schemas/Guild/lead.graphql +++ b/graphql/schemas/Guild/lead.graphql @@ -23,6 +23,11 @@ type Lead { participants: [LeadsParticipants!]! @hasMany channels: [SocialChannel]! @hasMany(relation: "socialChannels") systemModule: SystemModule + tags: [Tag!] + @paginate( + defaultCount: 25 + builder: "App\\GraphQL\\Social\\Queries\\Tags\\TagsQueries@getTagsBuilder" + ) files: [Filesystem!]! @paginate( defaultCount: 25 diff --git a/graphql/schemas/Guild/organization.graphql b/graphql/schemas/Guild/organization.graphql index e74497cbd..c8493c4be 100644 --- a/graphql/schemas/Guild/organization.graphql +++ b/graphql/schemas/Guild/organization.graphql @@ -5,6 +5,11 @@ type Organization { user: User! @belongsTo name: String! address: String + tags: [Tag!] + @paginate( + defaultCount: 25 + builder: "App\\GraphQL\\Social\\Queries\\Tags\\TagsQueries@getTagsBuilder" + ) } input OrganizationInput { @@ -19,13 +24,21 @@ input OrganizationPeopleInput { extend type Mutation @guard { createOrganization(input: OrganizationInput!): Organization - @field(resolver: "App\\GraphQL\\Guild\\Mutations\\Organizations\\OrganizationManagementMutation@create") + @field( + resolver: "App\\GraphQL\\Guild\\Mutations\\Organizations\\OrganizationManagementMutation@create" + ) updateOrganization(id: ID!, input: OrganizationInput!): Organization - @field(resolver: "App\\GraphQL\\Guild\\Mutations\\Organizations\\OrganizationManagementMutation@update") + @field( + resolver: "App\\GraphQL\\Guild\\Mutations\\Organizations\\OrganizationManagementMutation@update" + ) deleteOrganization(id: ID!): Boolean - @field(resolver: "App\\GraphQL\\Guild\\Mutations\\Organizations\\OrganizationManagementMutation@delete") + @field( + resolver: "App\\GraphQL\\Guild\\Mutations\\Organizations\\OrganizationManagementMutation@delete" + ) restoreOrganization(id: ID!): Boolean - @field(resolver: "App\\GraphQL\\Guild\\Mutations\\Organizations\\OrganizationManagementMutation@restore") + @field( + resolver: "App\\GraphQL\\Guild\\Mutations\\Organizations\\OrganizationManagementMutation@restore" + ) addPeopleToOrganization(input: OrganizationPeopleInput!): Boolean! @field( resolver: "App\\GraphQL\\Guild\\Mutations\\Organizations\\PeopleOrganizationMutation@add" @@ -38,14 +51,7 @@ extend type Mutation @guard { extend type Query @guard { organizations( - where: _ - @whereConditions( - columns: [ - "id" - "name" - "uuid" - ] - ) + where: _ @whereConditions(columns: ["id", "name", "uuid"]) orderBy: _ @orderBy(columns: ["id", "created_at", "updated_at", "name"]) ): [Organization!]! @paginate( diff --git a/graphql/schemas/Guild/people.graphql b/graphql/schemas/Guild/people.graphql index dc7ddc312..6877e96e9 100644 --- a/graphql/schemas/Guild/people.graphql +++ b/graphql/schemas/Guild/people.graphql @@ -20,6 +20,11 @@ type People { defaultCount: 25 builder: "App\\GraphQL\\Ecosystem\\Queries\\CustomFields\\CustomFieldQuery@getAllByGraphType" ) + tags: [Tag!] + @paginate( + defaultCount: 25 + builder: "App\\GraphQL\\Social\\Queries\\Tags\\TagsQueries@getTagsBuilder" + ) } type PeopleRelationship { @@ -43,6 +48,7 @@ input PeopleInput { address: [AddressInput!] files: [FilesystemInputUrl!] custom_fields: [CustomFieldEntityInput!] + tags: [TagInput!] created_at: DateTime } @@ -70,6 +76,8 @@ extend type Query @guard { @whereHasConditions(relation: "emails", columns: ["id", "value"]) hasPhones: _ @whereHasConditions(relation: "phones", columns: ["id", "value"]) + hasTags: _ + @whereHasConditions(relation: "tags", columns: ["name"]) hasCustomFields: _ @whereHasConditions( relation: "customFields" diff --git a/graphql/schemas/Inventory/attributes.graphql b/graphql/schemas/Inventory/attributes.graphql index 8a2f62264..83dc2a3bd 100644 --- a/graphql/schemas/Inventory/attributes.graphql +++ b/graphql/schemas/Inventory/attributes.graphql @@ -67,6 +67,7 @@ extend type Mutation @guard { } extend type Query @guard { attributes( + search: String @search where: _ @whereConditions( columns: [ diff --git a/graphql/schemas/Inventory/category.graphql b/graphql/schemas/Inventory/category.graphql index 62ab4bc25..006f94b0a 100644 --- a/graphql/schemas/Inventory/category.graphql +++ b/graphql/schemas/Inventory/category.graphql @@ -54,6 +54,7 @@ extend type Mutation @guard { extend type Query @guard { categories( + search: String @search where: _ @whereConditions( columns: [ diff --git a/graphql/schemas/Inventory/channel.graphql b/graphql/schemas/Inventory/channel.graphql index a99096a5a..4e0f33320 100644 --- a/graphql/schemas/Inventory/channel.graphql +++ b/graphql/schemas/Inventory/channel.graphql @@ -48,6 +48,7 @@ extend type Mutation @guard { } extend type Query @guard { channels( + search: String @search where: _ @whereConditions( columns: [ diff --git a/graphql/schemas/Inventory/product.graphql b/graphql/schemas/Inventory/product.graphql index fd1604ed2..e3a994a1f 100644 --- a/graphql/schemas/Inventory/product.graphql +++ b/graphql/schemas/Inventory/product.graphql @@ -29,6 +29,11 @@ type Product { defaultCount: 25 builder: "App\\GraphQL\\Ecosystem\\Queries\\CustomFields\\CustomFieldQuery@getAllByGraphType" ) + tags: [Tag!] + @paginate( + defaultCount: 25 + builder: "App\\GraphQL\\Social\\Queries\\Tags\\TagsQueries@getTagsBuilder" + ) } input ProductInput { diff --git a/graphql/schemas/Inventory/productType.graphql b/graphql/schemas/Inventory/productType.graphql index bda707e31..2a221a0fd 100644 --- a/graphql/schemas/Inventory/productType.graphql +++ b/graphql/schemas/Inventory/productType.graphql @@ -63,6 +63,7 @@ extend type Mutation @guard { } extend type Query { productTypes( + search: String @search where: _ @whereConditions( columns: [ diff --git a/graphql/schemas/Inventory/status.graphql b/graphql/schemas/Inventory/status.graphql index 2a8bb12f6..c48a6d963 100644 --- a/graphql/schemas/Inventory/status.graphql +++ b/graphql/schemas/Inventory/status.graphql @@ -37,6 +37,7 @@ extend type Mutation @guard { extend type Query @guard { status( + search: String @search where: _ @whereConditions( columns: [ diff --git a/graphql/schemas/Inventory/variant.graphql b/graphql/schemas/Inventory/variant.graphql index 117539de4..3cf0798f9 100644 --- a/graphql/schemas/Inventory/variant.graphql +++ b/graphql/schemas/Inventory/variant.graphql @@ -9,6 +9,8 @@ type Variant { html_description: String sku: String ean: String + barcode: String + serial_number: String status: Status files: [Filesystem] @paginate( @@ -21,21 +23,27 @@ type Variant { product: Product warehouses: [WarehouseVariantRelationship!]! @hasMany(relation: "variantWarehouses") - attributes: [VariantsAttributes!]! + attributes: [VariantsAttributes!]! @BelongsToMany(relation: "attributes") companies: Company! @belongsTo(relation: "company") channels: [VariantChannelRelationship!]! + @BelongsToMany(relation: "channels") channel: VariantPricingInfo - @field(resolver: "App\\GraphQL\\Inventory\\Types\\ChannelInfoType@price") + @field( + resolver: "App\\GraphQL\\Inventory\\Types\\ChannelInfoType@price" + ) metadata: Mixed - @field(resolver: "App\\GraphQL\\Inventory\\Types\\MetadataType@linkedStores") + @field( + resolver: "App\\GraphQL\\Inventory\\Types\\MetadataType@linkedStores" + ) custom_fields: [CustomField!]! @paginate( defaultCount: 25 builder: "App\\GraphQL\\Ecosystem\\Queries\\CustomFields\\CustomFieldQuery@getAllByGraphType" ) + inventory_quantity: Int @method(name: "getTotalQuantity") } -type VariantPricingInfo{ +type VariantPricingInfo { price: Float! discounted_price: Float! quantity: Int! @@ -77,19 +85,21 @@ input VariantsInput { input VariantsUpdateInput { products_id: ID - name: String + name: String! description: String short_description: String html_description: String files: [FilesystemInputUrl!] status: StatusReferenceInput - sku: String + sku: String! ean: String barcode: String attributes: [VariantsAttributesInput!] serial_number: String is_published: Boolean warehouses: [WarehouseReferenceInput!] + channels: [VariantChannelReferenceInput!] + custom_fields: [CustomFieldEntityInput!] } extend type Mutation @guard { diff --git a/graphql/schemas/Inventory/variantChannel.graphql b/graphql/schemas/Inventory/variantChannel.graphql index e8d380076..fa8193996 100644 --- a/graphql/schemas/Inventory/variantChannel.graphql +++ b/graphql/schemas/Inventory/variantChannel.graphql @@ -30,10 +30,13 @@ type ChannelsPricesHistoryRelationship { from_date: String! } type VariantChannelRelationship { + id: ID! name: String + description: String price: Float - warehouses_id: Int discounted_price: Float + warehouses_id: ID! + slug: String is_published: Boolean prices_history: [ChannelsPricesHistoryRelationship!]! @field( @@ -124,7 +127,8 @@ extend type Query { id: ID! attributes: Mixed search: String @search - where: _ @whereConditions(columnsEnum: ChannelVariantsFilterByAttributesEnum) + where: _ + @whereConditions(columnsEnum: ChannelVariantsFilterByAttributesEnum) orderBy: _ @orderBy( columns: [ diff --git a/graphql/schemas/Inventory/warehouse.graphql b/graphql/schemas/Inventory/warehouse.graphql index 100c9af2c..e07e7fb39 100644 --- a/graphql/schemas/Inventory/warehouse.graphql +++ b/graphql/schemas/Inventory/warehouse.graphql @@ -62,6 +62,7 @@ extend type Mutation @guard { } extend type Query @guard { warehouses( + search: String @search where: _ @whereConditions( columns: [ diff --git a/graphql/schemas/Social/channels.graphql b/graphql/schemas/Social/channels.graphql index b677e054a..d896b4070 100644 --- a/graphql/schemas/Social/channels.graphql +++ b/graphql/schemas/Social/channels.graphql @@ -13,7 +13,7 @@ type SocialChannel { last_message_id: ID messages: [Message!]! @belongsToMany users: [User!]! @belongsToMany - systemModule: SystemModule! + systemModule: SystemModule! @belongsTo } input SocialChannelInput { diff --git a/graphql/schemas/Social/message.graphql b/graphql/schemas/Social/message.graphql index 3336e9308..661f48c6e 100644 --- a/graphql/schemas/Social/message.graphql +++ b/graphql/schemas/Social/message.graphql @@ -15,7 +15,7 @@ type Message { parent: Message user: User! messageType: MessageType! - appModuleMessage: AppModuleMessage! + appModuleMessage: AppModuleMessage myInteraction: myInteraction @method(name: "getMyInteraction") comments: [MessageComments!]! custom_fields: [CustomField!]! @@ -23,6 +23,12 @@ type Message { defaultCount: 25 builder: "App\\GraphQL\\Ecosystem\\Queries\\CustomFields\\CustomFieldQuery@getAllByGraphType" ) + tags: [Tag!] + @paginate + ( + defaultCount: 25 + builder: "App\\GraphQL\\Social\\Queries\\Tags\\TagsQueries@getTagsBuilder" + ) } type AppModuleMessage { @@ -107,6 +113,7 @@ extend type Query @guard { "parent_unique_id" "companies_id" "uuid" + "users_id" "message_types_id" "message" "reactions_count" diff --git a/graphql/schemas/Social/tags.graphql b/graphql/schemas/Social/tags.graphql new file mode 100644 index 000000000..edd811b51 --- /dev/null +++ b/graphql/schemas/Social/tags.graphql @@ -0,0 +1,67 @@ +input TagInput { + name: String! + slug: String + weight: Int +} + +type Tag { + id: ID! + user: User! + name: String! + slug: String + weight: Int + created_at: String + updated_at: String + taggables: [TagEntity!]! @hasMany(method: "taggables") +} + +type TagEntity { + id: ID! + entity_id: ID! + tags_id: ID! + system_module_name: String! + system_module: SystemModule! @belongsTo(method: "systemModule") + created_at: String + updated_at: String +} + +input AttachTagEntityInput { + entity_id: ID! + tag_id: ID! + system_module_uuid: String! +} + +extend type Mutation @guard { + createTag(input: TagInput!): Tag + @field( + resolver: "App\\GraphQL\\Social\\Mutations\\Tags\\TagsManagement@create" + ) + updateTag(id: ID!, input: TagInput!): Tag + @field( + resolver: "App\\GraphQL\\Social\\Mutations\\Tags\\TagsManagement@update" + ) + deleteTag(id: ID!): Boolean + @field( + resolver: "App\\GraphQL\\Social\\Mutations\\Tags\\TagsManagement@delete" + ) + followTag(id: ID!): Boolean + @field( + resolver: "App\\GraphQL\\Social\\Mutations\\Tags\\TagsManagement@follow" + ) + attachTagToEntity(input: AttachTagEntityInput!): Boolean + @field( + resolver: "App\\GraphQL\\Social\\Mutations\\Tags\\TagsManagement@attachTagToEntity" + ) +} + +extend type Query @guard { + tags( + where: _ @whereConditions(columns: ["id", "name", "slug", "weight"]) + orderBy: _ @orderBy(columns: ["id"]) + ): [Tag!]! + @paginate( + model: "Kanvas\\Social\\Tags\\Models\\Tag" + scopes: ["fromApp"] + defaultCount: 15 + ) +} diff --git a/src/Baka/Traits/DatabaseSearchableTrait.php b/src/Baka/Traits/DatabaseSearchableTrait.php new file mode 100644 index 000000000..64239a646 --- /dev/null +++ b/src/Baka/Traits/DatabaseSearchableTrait.php @@ -0,0 +1,32 @@ +engine('database'); + } + + public function toSearchableArray(): array + { + return [ + 'id' => $this->id, + 'name' => $this->name, + ]; + } +} diff --git a/src/Baka/Traits/KanvasModelTrait.php b/src/Baka/Traits/KanvasModelTrait.php index 2edbad36b..1a1b74560 100644 --- a/src/Baka/Traits/KanvasModelTrait.php +++ b/src/Baka/Traits/KanvasModelTrait.php @@ -43,7 +43,7 @@ public static function getByName(string $name, ?AppInterface $app = null): self ->firstOrFail(); } catch (ModelNotFoundException $e) { //we want to expose the not found msg - throw new ExceptionsModelNotFoundException("No record found for $name"); + throw new ExceptionsModelNotFoundException($e->getMessage() . " $name"); } } @@ -58,7 +58,7 @@ public static function getByUuid(string $uuid, ?AppInterface $app = null): self ->firstOrFail(); } catch (ModelNotFoundException $e) { //we want to expose the not found msg - throw new ExceptionsModelNotFoundException("No record found for $uuid"); + throw new ExceptionsModelNotFoundException($e->getMessage() . " $uuid"); } } @@ -73,7 +73,7 @@ public static function getById(mixed $id, ?AppInterface $app = null): self ->firstOrFail(); } catch (ModelNotFoundException $e) { //we want to expose the not found msg - throw new ExceptionsModelNotFoundException("No record found for $id"); + throw new ExceptionsModelNotFoundException($e->getMessage() . " $id"); } } diff --git a/src/Domains/ActionEngine/Actions/Models/Action.php b/src/Domains/ActionEngine/Actions/Models/Action.php index d906fe831..1284edae2 100644 --- a/src/Domains/ActionEngine/Actions/Models/Action.php +++ b/src/Domains/ActionEngine/Actions/Models/Action.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Domains\ActionEngine\Actions\Models; +namespace Kanvas\ActionEngine\Actions\Models; use Baka\Traits\UuidTrait; use Kanvas\ActionEngine\Models\BaseModel; diff --git a/src/Domains/ActionEngine/Actions/Models/CompanyAction.php b/src/Domains/ActionEngine/Actions/Models/CompanyAction.php index 7382c1b8e..0c0fcadc3 100644 --- a/src/Domains/ActionEngine/Actions/Models/CompanyAction.php +++ b/src/Domains/ActionEngine/Actions/Models/CompanyAction.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Domains\ActionEngine\Actions\Models; +namespace Kanvas\ActionEngine\Actions\Models; use Baka\Traits\UuidTrait; use Illuminate\Database\Eloquent\Relations\BelongsTo; diff --git a/src/Domains/ActionEngine/Engagegments/Models/Engagement.php b/src/Domains/ActionEngine/Engagements/Models/Engagement.php similarity index 92% rename from src/Domains/ActionEngine/Engagegments/Models/Engagement.php rename to src/Domains/ActionEngine/Engagements/Models/Engagement.php index 9243fd69d..6702190bd 100644 --- a/src/Domains/ActionEngine/Engagegments/Models/Engagement.php +++ b/src/Domains/ActionEngine/Engagements/Models/Engagement.php @@ -2,11 +2,11 @@ declare(strict_types=1); -namespace Domains\ActionEngine\Engagements\Models; +namespace Kanvas\ActionEngine\Engagements\Models; use Baka\Traits\UuidTrait; -use Domains\ActionEngine\Actions\Models\CompanyAction; use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Kanvas\ActionEngine\Actions\Models\CompanyAction; use Kanvas\ActionEngine\Models\BaseModel; use Kanvas\Guild\Customers\Models\People; use Kanvas\Guild\Leads\Models\Lead; diff --git a/src/Domains/ActionEngine/Pipelines/Models/Pipeline.php b/src/Domains/ActionEngine/Pipelines/Models/Pipeline.php index 0bdb16d08..f9bb0a80a 100644 --- a/src/Domains/ActionEngine/Pipelines/Models/Pipeline.php +++ b/src/Domains/ActionEngine/Pipelines/Models/Pipeline.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Domains\ActionEngine\Pipelines\Models; +namespace Kanvas\ActionEngine\Pipelines\Models; use Baka\Traits\UuidTrait; use Illuminate\Database\Eloquent\Relations\HasMany; diff --git a/src/Domains/ActionEngine/Pipelines/Models/PipelineStage.php b/src/Domains/ActionEngine/Pipelines/Models/PipelineStage.php index 00d28a0ef..7cc0016a4 100644 --- a/src/Domains/ActionEngine/Pipelines/Models/PipelineStage.php +++ b/src/Domains/ActionEngine/Pipelines/Models/PipelineStage.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Domains\ActionEngine\Pipelines\Models; +namespace Kanvas\ActionEngine\Pipelines\Models; use Baka\Traits\UuidTrait; use Illuminate\Database\Eloquent\Relations\BelongsTo; diff --git a/src/Domains/ActionEngine/Pipelines/Models/PipelineStageMessage.php b/src/Domains/ActionEngine/Pipelines/Models/PipelineStageMessage.php index ed7c4ea89..2500c5eb7 100644 --- a/src/Domains/ActionEngine/Pipelines/Models/PipelineStageMessage.php +++ b/src/Domains/ActionEngine/Pipelines/Models/PipelineStageMessage.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Domains\ActionEngine\Pipelines\Models; +namespace Kanvas\ActionEngine\Pipelines\Models; use Baka\Traits\UuidTrait; use Illuminate\Database\Eloquent\Relations\BelongsTo; diff --git a/src/Domains/ActionEngine/Tasks/Models/TaskEngagementItem.php b/src/Domains/ActionEngine/Tasks/Models/TaskEngagementItem.php new file mode 100644 index 000000000..695ffc94b --- /dev/null +++ b/src/Domains/ActionEngine/Tasks/Models/TaskEngagementItem.php @@ -0,0 +1,59 @@ + Json::class, + ]; + + protected $primaryKey = ['task_list_item_id','lead_id']; + + public function item(): BelongsTo + { + return $this->belongsTo(TaskListItem::class, 'task_list_item_id'); + } + + public function lead(): BelongsTo + { + return $this->belongsTo(Lead::class, 'lead_id'); + } + + public function engagementStart(): HasOne + { + return $this->hasOne(Engagement::class, 'id', 'engagement_start_id'); + } + + public function engagementEnd(): HasOne + { + return $this->hasOne(Engagement::class, 'id', 'engagement_end_id'); + } +} diff --git a/src/Domains/ActionEngine/Tasks/Models/TaskList.php b/src/Domains/ActionEngine/Tasks/Models/TaskList.php new file mode 100644 index 000000000..6b1cd6e56 --- /dev/null +++ b/src/Domains/ActionEngine/Tasks/Models/TaskList.php @@ -0,0 +1,38 @@ + Json::class, + ]; + + public function tasks(): HasMany + { + return $this->hasMany(TaskListItem::class, 'task_list_id')->orderBy('weight'); + } +} diff --git a/src/Domains/ActionEngine/Tasks/Models/TaskListItem.php b/src/Domains/ActionEngine/Tasks/Models/TaskListItem.php new file mode 100644 index 000000000..20149b039 --- /dev/null +++ b/src/Domains/ActionEngine/Tasks/Models/TaskListItem.php @@ -0,0 +1,62 @@ + Json::class, + ]; + + public function task(): BelongsTo + { + return $this->belongsTo(TaskList::class, 'task_list_id'); + } + + public function companyAction(): BelongsTo + { + return $this->belongsTo(CompanyAction::class, 'companies_action_id'); + } + + /** + * temp relationship to engagement will only work on LeadTaskEngagementItem + */ + public function engagementStart(): HasOne + { + return $this->hasOne(Engagement::class, 'id', 'engagement_start_id'); + } + + /** + * temp relationship to engagement will only work on LeadTaskEngagementItem + */ + public function engagementEnd(): HasOne + { + return $this->hasOne(Engagement::class, 'id', 'engagement_end_id'); + } +} diff --git a/src/Domains/Connectors/Internal/Activities/UserCustomFieldActivity.php b/src/Domains/Connectors/Internal/Activities/UserCustomFieldActivity.php new file mode 100644 index 000000000..ace16e323 --- /dev/null +++ b/src/Domains/Connectors/Internal/Activities/UserCustomFieldActivity.php @@ -0,0 +1,40 @@ +overwriteAppService($app); + + $customField = $params['customField'] ?? null; + + if (! $customField) { + return ['No custom field configured to set for user']; + } + + //set custom field to user + foreach ($customField as $key => $value) { + $value = $value['value'] ?? null; + $isPublic = $value['is_public'] ?? false; + $user->set($key, $value, $isPublic); + } + + return [ + 'user_id' => $user->getId(), + 'custom_field' => $customField, + ]; + } +} diff --git a/src/Domains/Connectors/Internal/Workflows/UserCustomFieldWorkflow.php b/src/Domains/Connectors/Internal/Workflows/UserCustomFieldWorkflow.php new file mode 100644 index 000000000..d7e9c8d18 --- /dev/null +++ b/src/Domains/Connectors/Internal/Workflows/UserCustomFieldWorkflow.php @@ -0,0 +1,22 @@ +Customer->post($customer); - $shopifyUserKey = CustomFieldEnum::USER_SHOPIFY_ID->value . '-' . $app->getId(); + //$shopifyUserKey = CustomFieldEnum::USER_SHOPIFY_ID->value . '-' . $app->getId(); + $shopifyUserKey = CustomFieldEnum::USER_SHOPIFY_ID->value; $user->set($shopifyUserKey, $customer['id']); return [ diff --git a/src/Domains/Guild/Customers/Actions/CreatePeopleAction.php b/src/Domains/Guild/Customers/Actions/CreatePeopleAction.php index 566b94d2a..91ca58a2c 100644 --- a/src/Domains/Guild/Customers/Actions/CreatePeopleAction.php +++ b/src/Domains/Guild/Customers/Actions/CreatePeopleAction.php @@ -57,6 +57,10 @@ public function execute(): People $people->setCustomFields($this->peopleData->custom_fields); $people->saveCustomFields(); + if (count($this->peopleData->tags)) { + $people->syncTags(array_column($this->peopleData->tags, 'name')); + } + if ($this->peopleData->contacts->count()) { $existingContacts = $people->contacts()->pluck('value')->toArray(); $contactsToAdd = []; diff --git a/src/Domains/Guild/Customers/Actions/UpdatePeopleAction.php b/src/Domains/Guild/Customers/Actions/UpdatePeopleAction.php index 7bdcb9e06..426019d1a 100644 --- a/src/Domains/Guild/Customers/Actions/UpdatePeopleAction.php +++ b/src/Domains/Guild/Customers/Actions/UpdatePeopleAction.php @@ -8,7 +8,6 @@ use Kanvas\Guild\Customers\Models\Address; use Kanvas\Guild\Customers\Models\Contact; use Kanvas\Guild\Customers\Models\People; -use Spatie\LaravelData\DataCollection; class UpdatePeopleAction { @@ -43,6 +42,10 @@ public function execute(): People $this->people->setCustomFields($this->peopleData->custom_fields); $this->people->saveCustomFields(); + if (count($this->peopleData->tags)) { + $this->people->syncTags(array_column($this->peopleData->tags, 'name')); + } + if ($this->peopleData->contacts->count()) { $contacts = []; $this->people->contacts()->delete(); diff --git a/src/Domains/Guild/Customers/DataTransferObject/People.php b/src/Domains/Guild/Customers/DataTransferObject/People.php index 4c0f4fcda..15da6d8de 100644 --- a/src/Domains/Guild/Customers/DataTransferObject/People.php +++ b/src/Domains/Guild/Customers/DataTransferObject/People.php @@ -38,6 +38,7 @@ public function __construct( public readonly ?string $linkedin_contact_id = null, public readonly ?string $middlename = null, public readonly array $custom_fields = [], + public readonly array $tags = [], public readonly ?string $created_at = null ) { } diff --git a/src/Domains/Guild/Customers/Factories/PeopleFactory.php b/src/Domains/Guild/Customers/Factories/PeopleFactory.php index 4ea6858cf..fe10e4912 100644 --- a/src/Domains/Guild/Customers/Factories/PeopleFactory.php +++ b/src/Domains/Guild/Customers/Factories/PeopleFactory.php @@ -5,7 +5,6 @@ namespace Kanvas\Guild\Customers\Factories; use Illuminate\Database\Eloquent\Factories\Factory; -use Kanvas\Enums\AppEnums; use Kanvas\Guild\Customers\Models\People; class PeopleFactory extends Factory @@ -18,8 +17,25 @@ public function definition() 'firstname' => fake()->firstName, 'lastname' => fake()->lastName, 'name' => fake()->name, - 'companies_id' => AppEnums::GLOBAL_COMPANY_ID->getValue(), 'users_id' => 1, ]; } + + public function withAppId(int $appId) + { + return $this->state(function (array $attributes) use ($appId) { + return [ + 'apps_id' => $appId, + ]; + }); + } + + public function withCompanyId(int $companyId) + { + return $this->state(function (array $attributes) use ($companyId) { + return [ + 'companies_id' => $companyId, + ]; + }); + } } diff --git a/src/Domains/Guild/Customers/Jobs/CustomerImporterJob.php b/src/Domains/Guild/Customers/Jobs/CustomerImporterJob.php index c59ad3871..4d8507662 100644 --- a/src/Domains/Guild/Customers/Jobs/CustomerImporterJob.php +++ b/src/Domains/Guild/Customers/Jobs/CustomerImporterJob.php @@ -101,6 +101,7 @@ public function handle() 'apple_contact_id' => $customerData['apple_contact_id'] ?? null, 'linkedin_contact_id' => $customerData['linkedin_contact_id'] ?? null, 'custom_fields' => $customerData['custom_fields'] ?? [], + 'tags' => $customerData['tags'] ?? [], 'created_at' => $customerData['created_at'] ?? null, ]); diff --git a/src/Domains/Guild/Customers/Models/People.php b/src/Domains/Guild/Customers/Models/People.php index 55af8b428..4e6163897 100644 --- a/src/Domains/Guild/Customers/Models/People.php +++ b/src/Domains/Guild/Customers/Models/People.php @@ -10,6 +10,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany; use Kanvas\Guild\Customers\Factories\PeopleFactory; use Kanvas\Guild\Models\BaseModel; +use Kanvas\Social\Tags\Traits\HasTagsTrait; use Laravel\Scout\Searchable; /** @@ -17,6 +18,7 @@ * * @property int $id * @property string $uuid + * @property int $apps_id * @property int $users_id * @property int $companies_id * @property string $name @@ -35,6 +37,7 @@ class People extends BaseModel { use UuidTrait; use Searchable; + use HasTagsTrait; protected $table = 'peoples'; protected $guarded = []; diff --git a/src/Domains/Guild/Leads/Factories/LeadFactory.php b/src/Domains/Guild/Leads/Factories/LeadFactory.php index aefce4e1e..66da63714 100644 --- a/src/Domains/Guild/Leads/Factories/LeadFactory.php +++ b/src/Domains/Guild/Leads/Factories/LeadFactory.php @@ -5,6 +5,7 @@ namespace Kanvas\Guild\Leads\Factories; use Illuminate\Database\Eloquent\Factories\Factory; +use Kanvas\Apps\Models\Apps; use Kanvas\Companies\Models\Companies; use Kanvas\Enums\AppEnums; use Kanvas\Guild\Customers\Models\People; @@ -17,6 +18,8 @@ class LeadFactory extends Factory public function definition() { $company = Companies::factory()->create(); + $app = app(Apps::class); + return [ 'firstname' => fake()->firstName, 'lastname' => fake()->lastName, @@ -24,7 +27,7 @@ public function definition() 'companies_id' => $company->getId(), 'companies_branches_id' => AppEnums::GLOBAL_COMPANY_ID->getValue(), 'users_id' => 1, - 'people_id' => People::factory()->create()->getId(), + 'people_id' => People::factory()->withAppId($app->getId())->withCompanyId($company->getId())->create()->getId(), 'leads_receivers_id' => 0, 'leads_owner_id' => 1, ]; diff --git a/src/Domains/Guild/Leads/Models/Lead.php b/src/Domains/Guild/Leads/Models/Lead.php index 130765dce..7da7fa494 100644 --- a/src/Domains/Guild/Leads/Models/Lead.php +++ b/src/Domains/Guild/Leads/Models/Lead.php @@ -23,6 +23,7 @@ use Kanvas\Guild\Pipelines\Models\PipelineStage; use Kanvas\Social\Channels\Models\Channel; use Kanvas\Social\Follows\Traits\FollowersTrait; +use Kanvas\Social\Tags\Traits\HasTagsTrait; use Kanvas\SystemModules\Models\SystemModules; use Kanvas\Users\Models\Users; use Kanvas\Workflow\Traits\CanUseWorkflow; @@ -61,6 +62,7 @@ class Lead extends BaseModel { use UuidTrait; use Searchable; + use HasTagsTrait; use FollowersTrait; use CanUseWorkflow; diff --git a/src/Domains/Guild/Organizations/Models/Organization.php b/src/Domains/Guild/Organizations/Models/Organization.php index e8eec2f63..5ca622024 100644 --- a/src/Domains/Guild/Organizations/Models/Organization.php +++ b/src/Domains/Guild/Organizations/Models/Organization.php @@ -8,6 +8,7 @@ use Illuminate\Database\Eloquent\Relations\HasManyThrough; use Kanvas\Guild\Customers\Models\People; use Kanvas\Guild\Models\BaseModel; +use Kanvas\Social\Tags\Traits\HasTagsTrait; /** * Class Organization. @@ -23,6 +24,7 @@ class Organization extends BaseModel { use UuidTrait; + use HasTagsTrait; protected $table = 'organizations'; protected $guarded = []; diff --git a/src/Domains/Inventory/Attributes/Models/Attributes.php b/src/Domains/Inventory/Attributes/Models/Attributes.php index afa033285..644a77876 100644 --- a/src/Domains/Inventory/Attributes/Models/Attributes.php +++ b/src/Domains/Inventory/Attributes/Models/Attributes.php @@ -12,6 +12,7 @@ use Kanvas\Apps\Models\Apps; use Kanvas\Companies\Models\Companies; use Kanvas\Inventory\Models\BaseModel; +use Baka\Traits\DatabaseSearchableTrait; use Kanvas\Inventory\Variants\Models\VariantsAttributes; /** @@ -27,6 +28,7 @@ class Attributes extends BaseModel { use UuidTrait; use CascadeSoftDeletes; + use DatabaseSearchableTrait; public $table = 'attributes'; public $guarded = []; diff --git a/src/Domains/Inventory/Categories/Models/Categories.php b/src/Domains/Inventory/Categories/Models/Categories.php index cb717b6b5..080946650 100644 --- a/src/Domains/Inventory/Categories/Models/Categories.php +++ b/src/Domains/Inventory/Categories/Models/Categories.php @@ -12,6 +12,7 @@ use Kanvas\Companies\Models\Companies; use Kanvas\Inventory\Models\BaseModel; use Kanvas\Inventory\Products\Models\ProductsCategories; +use Baka\Traits\DatabaseSearchableTrait; use Kanvas\Inventory\Traits\ScopesTrait; class Categories extends BaseModel @@ -19,6 +20,7 @@ class Categories extends BaseModel use UuidTrait; use SlugTrait; use ScopesTrait; + use DatabaseSearchableTrait; protected $table = 'categories'; protected $guarded = []; diff --git a/src/Domains/Inventory/Channels/Models/Channels.php b/src/Domains/Inventory/Channels/Models/Channels.php index 3dbcf4a57..95127c83a 100644 --- a/src/Domains/Inventory/Channels/Models/Channels.php +++ b/src/Domains/Inventory/Channels/Models/Channels.php @@ -4,18 +4,15 @@ namespace Kanvas\Inventory\Channels\Models; +use Baka\Traits\DatabaseSearchableTrait; use Baka\Traits\SlugTrait; use Baka\Traits\UuidTrait; use Illuminate\Database\Eloquent\Casts\Attribute; -use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; -use Kanvas\Apps\Models\Apps; -use Kanvas\Companies\Models\Companies; use Kanvas\Inventory\Models\BaseModel; use Kanvas\Inventory\Traits\DefaultTrait; use Kanvas\Inventory\Variants\Models\Variants; use Kanvas\Inventory\Variants\Models\VariantsChannels; -use Kanvas\Users\Models\Users; /** * Class Channels. @@ -37,35 +34,12 @@ class Channels extends BaseModel { use UuidTrait; use SlugTrait; + use DatabaseSearchableTrait; use DefaultTrait; protected $table = 'channels'; protected $guarded = []; - /** - * Get the companies that owns the Warehouses. - */ - public function companies(): BelongsTo - { - return $this->belongsTo(Companies::class, 'companies_id'); - } - - /** - * - */ - public function apps(): BelongsTo - { - return $this->belongsTo(Apps::class, 'apps_id'); - } - - /** - * users. - */ - public function users(): BelongsTo - { - return $this->belongsTo(Users::class, 'users_id'); - } - /** * Available products in this channel */ @@ -97,4 +71,32 @@ public function pricesHistory(): HasMany 'channels_id' ); } + + public function price(): Attribute + { + return Attribute::make( + get: fn () => $this->pivot->price, + ); + } + + public function discountedPrice(): Attribute + { + return Attribute::make( + get: fn () => $this->pivot->discounted_price, + ); + } + + public function isPublished(): Attribute + { + return Attribute::make( + get: fn () => $this->pivot ? $this->pivot->is_published : ($this->attributes['is_published'] ?? true), + ); + } + + public function warehousesId(): Attribute + { + return Attribute::make( + get: fn () => $this->pivot->warehouses_id, + ); + } } diff --git a/src/Domains/Inventory/Channels/Services/ChannelService.php b/src/Domains/Inventory/Channels/Services/ChannelService.php new file mode 100644 index 000000000..7c44c26ff --- /dev/null +++ b/src/Domains/Inventory/Channels/Services/ChannelService.php @@ -0,0 +1,36 @@ +variantChannels()->forcedelete(); + foreach ($variantsChannels as $variantChannel) { + $warehouse = WarehouseRepository::getById((int) $variantChannel['warehouses_id']); + $channel = ChannelRepository::getById((int) $variantChannel['channels_id'], $variant->product->company()->get()->first()); + $variantChannelDto = VariantChannel::from($variantChannel); + + VariantService::addVariantChannel( + $variant, + $warehouse, + $channel, + $variantChannelDto + ); + } + + return $variant; + } +} diff --git a/src/Domains/Inventory/Models/BaseModel.php b/src/Domains/Inventory/Models/BaseModel.php index 4377beed7..78d13d2d9 100644 --- a/src/Domains/Inventory/Models/BaseModel.php +++ b/src/Domains/Inventory/Models/BaseModel.php @@ -33,7 +33,25 @@ class BaseModel extends EloquentModel 'is_deleted' => 0, ]; + /** + * Prevent laravel from cast is_deleted as date using carbon. + * + */ + protected $casts = [ + 'is_deleted' => 'boolean', + ]; + protected $connection = 'inventory'; public const DELETED_AT = 'is_deleted'; + + /** + * Determine if the model instance has been soft-deleted. + * + * @return bool + */ + public function trashed() + { + return $this->{$this->getDeletedAtColumn()}; + } } diff --git a/src/Domains/Inventory/Products/Models/Products.php b/src/Domains/Inventory/Products/Models/Products.php index bdb1362df..b147782e9 100644 --- a/src/Domains/Inventory/Products/Models/Products.php +++ b/src/Domains/Inventory/Products/Models/Products.php @@ -23,6 +23,7 @@ use Kanvas\Inventory\Variants\Services\VariantService; use Kanvas\Inventory\Warehouses\Models\Warehouses; use Kanvas\Social\Interactions\Traits\LikableTrait; +use Kanvas\Social\Tags\Traits\HasTagsTrait; use Laravel\Scout\Searchable; /** @@ -50,6 +51,7 @@ class Products extends BaseModel use SlugTrait; use LikableTrait; use HasShopifyCustomField; + use HasTagsTrait; use Searchable { search as public traitSearch; } diff --git a/src/Domains/Inventory/ProductsTypes/Models/ProductsTypes.php b/src/Domains/Inventory/ProductsTypes/Models/ProductsTypes.php index 75ef48425..936022192 100644 --- a/src/Domains/Inventory/ProductsTypes/Models/ProductsTypes.php +++ b/src/Domains/Inventory/ProductsTypes/Models/ProductsTypes.php @@ -16,6 +16,7 @@ use Kanvas\Inventory\Models\BaseModel; use Kanvas\Inventory\Products\Factories\ProductTypeFactory; use Kanvas\Inventory\Products\Models\Products; +use Baka\Traits\DatabaseSearchableTrait; use Kanvas\Inventory\Traits\ScopesTrait; /** @@ -36,6 +37,7 @@ class ProductsTypes extends BaseModel use UuidTrait; use SlugTrait; use ScopesTrait; + use DatabaseSearchableTrait; use CascadeSoftDeletes; protected $table = 'products_types'; diff --git a/src/Domains/Inventory/Status/Models/Status.php b/src/Domains/Inventory/Status/Models/Status.php index 79501d929..7b3641164 100644 --- a/src/Domains/Inventory/Status/Models/Status.php +++ b/src/Domains/Inventory/Status/Models/Status.php @@ -7,6 +7,7 @@ use Baka\Traits\SlugTrait; use Illuminate\Database\Eloquent\Relations\HasMany; use Kanvas\Inventory\Models\BaseModel; +use Baka\Traits\DatabaseSearchableTrait; use Kanvas\Inventory\Traits\DefaultTrait; use Kanvas\Inventory\Variants\Models\Variants; use Kanvas\Inventory\Variants\Models\VariantsWarehouses; @@ -24,7 +25,7 @@ class Status extends BaseModel { use SlugTrait; - //use Searchable; + use DatabaseSearchableTrait; use DefaultTrait; protected $table = 'status'; diff --git a/src/Domains/Inventory/Status/Models/VariantWarehouseStatusHistory.php b/src/Domains/Inventory/Status/Models/VariantWarehouseStatusHistory.php index 22a630099..64de52a76 100644 --- a/src/Domains/Inventory/Status/Models/VariantWarehouseStatusHistory.php +++ b/src/Domains/Inventory/Status/Models/VariantWarehouseStatusHistory.php @@ -27,7 +27,8 @@ class VariantWarehouseStatusHistory extends BaseModel protected $table = 'products_variants_warehouse_status_history'; protected $guarded = []; - protected $primaryKey = ['products_variants_id', 'warehouses_id']; + protected $primaryKey = ['products_variants_warehouse_id', 'status_id']; + protected $forceDeleting = true; /** * Get the user that owns the Variants. diff --git a/src/Domains/Inventory/Variants/Actions/CreateVariantsAction.php b/src/Domains/Inventory/Variants/Actions/CreateVariantsAction.php index 335720170..334f7471d 100644 --- a/src/Domains/Inventory/Variants/Actions/CreateVariantsAction.php +++ b/src/Domains/Inventory/Variants/Actions/CreateVariantsAction.php @@ -6,9 +6,12 @@ use Baka\Support\Str; use Baka\Users\Contracts\UserInterface; +use Illuminate\Support\Facades\Validator; use Kanvas\Companies\Repositories\CompaniesRepository; +use Kanvas\Exceptions\ValidationException; use Kanvas\Inventory\Variants\DataTransferObject\Variants as VariantsDto; use Kanvas\Inventory\Variants\Models\Variants; +use Kanvas\Inventory\Variants\Validations\UniqueSkuRule; class CreateVariantsAction { @@ -31,9 +34,17 @@ public function execute(): Variants $this->user ); + $validator = Validator::make( + ['sku' => $this->variantDto->sku], + ['sku' => new UniqueSkuRule($this->variantDto->product->app, $this->variantDto->product->company)] + ); + + if ($validator->fails()) { + throw new ValidationException($validator->messages()->__toString()); + } + $search = [ 'products_id' => $this->variantDto->product->getId(), - 'slug' => $this->variantDto->slug ?? Str::slug($this->variantDto->name), 'sku' => $this->variantDto->sku, 'companies_id' => $this->variantDto->product->companies_id, 'apps_id' => $this->variantDto->product->apps_id, @@ -44,6 +55,7 @@ public function execute(): Variants [ 'name' => $this->variantDto->name, 'users_id' => $this->user->getId(), + 'slug' => $this->variantDto->slug ?? Str::slug($this->variantDto->name), 'description' => $this->variantDto->description, 'short_description' => $this->variantDto->short_description, 'html_description' => $this->variantDto->html_description, diff --git a/src/Domains/Inventory/Variants/Actions/UpdateVariantsAction.php b/src/Domains/Inventory/Variants/Actions/UpdateVariantsAction.php new file mode 100644 index 000000000..bcfcc9835 --- /dev/null +++ b/src/Domains/Inventory/Variants/Actions/UpdateVariantsAction.php @@ -0,0 +1,66 @@ +variantDto->product->company()->get()->first(), + $this->user + ); + + $validator = Validator::make( + ['sku' => $this->variantDto->sku], + ['sku' => new UniqueSkuRule($this->variant->app, $this->variant->company, $this->variant)] + ); + + if ($validator->fails()) { + throw new ValidationException($validator->messages()->__toString()); + } + + $this->variant->update( + [ + 'name' => $this->variantDto->name, + 'slug' => $this->variantDto->slug ?? Str::slug($this->variantDto->name), + 'sku' => $this->variantDto->sku, + 'users_id' => $this->user->getId(), + 'description' => $this->variantDto->description, + 'short_description' => $this->variantDto->short_description, + 'html_description' => $this->variantDto->html_description, + 'status_id' => $this->variantDto->status_id, + 'ean' => $this->variantDto->ean, + 'barcode' => $this->variantDto->barcode, + 'serial_number' => $this->variantDto->serial_number, + + ] + ); + + return $this->variant; + } +} diff --git a/src/Domains/Inventory/Variants/Models/Variants.php b/src/Domains/Inventory/Variants/Models/Variants.php index 531060e7c..6695be94c 100644 --- a/src/Domains/Inventory/Variants/Models/Variants.php +++ b/src/Domains/Inventory/Variants/Models/Variants.php @@ -180,7 +180,7 @@ public function channels(): BelongsToMany 'products_variants_id', 'channels_id' ) - ->withPivot('price', 'discounted_price', 'is_published', 'warehouses_id'); + ->withPivot('price', 'discounted_price', 'is_published', 'warehouses_id', 'channels_id'); } /** @@ -224,7 +224,7 @@ public function addAttributes(UserInterface $user, array $attributes): void 'isVisible' => false, 'isSearchable' => false, 'isFiltrable' => false, - 'slug' => Str::slug($attribute['name']) + 'slug' => Str::slug($attribute['name']), ]); $attributeModel = (new CreateAttribute($attributesDto, $user))->execute(); } @@ -328,4 +328,33 @@ protected function makeAllSearchableUsing(Builder $query): Builder { return $query->whereRelation('warehouses', 'warehouses.is_deleted', 0); } + + /** + * Get the total amount of variants in all the warehouses. + */ + public function getTotalQuantity(): int + { + if (! $totalVariantQuantity = $this->get('total_variant_quantity')) { + return (int) $this->setTotalQuantity(); + } + + return (int) $totalVariantQuantity; + } + + /** + * Set the total amount of variants in all the warehouses. + */ + public function setTotalQuantity(): int + { + $total = $this->variantWarehouses() + ->where('is_deleted', 0) + ->sum('quantity'); + + $this->set( + 'total_variant_quantity', + $total + ); + + return (int) $total; + } } diff --git a/src/Domains/Inventory/Variants/Models/VariantsChannels.php b/src/Domains/Inventory/Variants/Models/VariantsChannels.php index bf8752f91..ddbd63ad0 100644 --- a/src/Domains/Inventory/Variants/Models/VariantsChannels.php +++ b/src/Domains/Inventory/Variants/Models/VariantsChannels.php @@ -40,6 +40,7 @@ class VariantsChannels extends BaseModel protected $guarded = []; protected $primaryKey = ['product_variants_warehouse_id', 'channels_id']; + protected $forceDeleting = true; public function channel(): BelongsTo { diff --git a/src/Domains/Inventory/Variants/Models/VariantsWarehouses.php b/src/Domains/Inventory/Variants/Models/VariantsWarehouses.php index d2bdf9d17..cb276f542 100644 --- a/src/Domains/Inventory/Variants/Models/VariantsWarehouses.php +++ b/src/Domains/Inventory/Variants/Models/VariantsWarehouses.php @@ -6,6 +6,7 @@ use Baka\Traits\NoAppRelationshipTrait; use Baka\Traits\NoCompanyRelationshipTrait; +use Dyrynda\Database\Support\CascadeSoftDeletes; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; @@ -44,8 +45,11 @@ class VariantsWarehouses extends BaseModel { use NoAppRelationshipTrait; use NoCompanyRelationshipTrait; + use CascadeSoftDeletes; protected $table = 'products_variants_warehouses'; + protected $cascadeDeletes = ['variantWarehousesStatusHistory', 'pricesHistory', 'variantChannels']; + protected $guarded = []; /** @@ -100,6 +104,19 @@ public function pricesHistory(): HasMany ); } + public function variantWarehousesStatusHistory(): HasMany + { + return $this->hasMany( + VariantWarehouseStatusHistory::class, + 'products_variants_warehouse_id' + ); + } + + public function variantChannels(): HasMany + { + return $this->hasMany(VariantsChannels::class, 'product_variants_warehouse_id')->where('is_published', 1); + } + /** * Get the status history with the status information. * diff --git a/src/Domains/Inventory/Variants/Models/VariantsWarehousesPriceHistory.php b/src/Domains/Inventory/Variants/Models/VariantsWarehousesPriceHistory.php index c11a79f18..a94376582 100644 --- a/src/Domains/Inventory/Variants/Models/VariantsWarehousesPriceHistory.php +++ b/src/Domains/Inventory/Variants/Models/VariantsWarehousesPriceHistory.php @@ -23,6 +23,8 @@ class VariantsWarehousesPriceHistory extends BaseModel use NoCompanyRelationshipTrait; protected $table = 'products_variants_warehouses_price_history'; + protected $primaryKey = 'product_variants_warehouse_id'; public $timestamps = false; protected $guarded = []; + protected $forceDeleting = true; } diff --git a/src/Domains/Inventory/Variants/Services/VariantService.php b/src/Domains/Inventory/Variants/Services/VariantService.php index 95e7034b2..e5f36049c 100644 --- a/src/Domains/Inventory/Variants/Services/VariantService.php +++ b/src/Domains/Inventory/Variants/Services/VariantService.php @@ -15,6 +15,7 @@ use Kanvas\Inventory\Variants\Actions\CreateVariantsAction; use Kanvas\Inventory\Variants\Actions\UpdateToChannelAction; use Kanvas\Inventory\Variants\Actions\UpdateToWarehouseAction; +use Kanvas\Inventory\Variants\Actions\UpdateVariantsAction; use Kanvas\Inventory\Variants\DataTransferObject\VariantChannel as VariantChannelDto; use Kanvas\Inventory\Variants\DataTransferObject\Variants as VariantsDto; use Kanvas\Inventory\Variants\DataTransferObject\VariantsWarehouses; @@ -41,7 +42,14 @@ public static function createVariantsFromArray(Products $product, array $variant ...$variant, ]); - $variantModel = (new CreateVariantsAction($variantDto, $user))->execute(); + $existVariantUpdate = Variants::fromCompany($product->company)->fromApp($product->app)->where('sku', $variantDto->sku); + + if (! $existVariantUpdate->exists()) { + $variantModel = (new CreateVariantsAction($variantDto, $user))->execute(); + } else { + $variantModel = (new UpdateVariantsAction($existVariantUpdate->first(), $variantDto, $user))->execute(); + } + $company = $variantDto->product->company; if (isset($variant['custom_fields']) && ! empty($variant['custom_fields'])) { diff --git a/src/Domains/Inventory/Variants/Validations/UniqueSkuRule.php b/src/Domains/Inventory/Variants/Validations/UniqueSkuRule.php new file mode 100644 index 000000000..e1c79dee5 --- /dev/null +++ b/src/Domains/Inventory/Variants/Validations/UniqueSkuRule.php @@ -0,0 +1,36 @@ +fromCompany($this->company) + ->fromApp($this->app); + + if ($this->variant) { + $query->where('id', '!=', $this->variant->getId()); + } + + if ($query->exists()) { + $fail("The $attribute has already been taken."); + } + } +} diff --git a/src/Domains/Inventory/Warehouses/Models/Warehouses.php b/src/Domains/Inventory/Warehouses/Models/Warehouses.php index 1133330ce..09df6e1d0 100644 --- a/src/Domains/Inventory/Warehouses/Models/Warehouses.php +++ b/src/Domains/Inventory/Warehouses/Models/Warehouses.php @@ -4,6 +4,7 @@ namespace Kanvas\Inventory\Warehouses\Models; +use Baka\Traits\DatabaseSearchableTrait; use Baka\Traits\UuidTrait; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -35,6 +36,7 @@ class Warehouses extends BaseModel { use UuidTrait; use DefaultTrait; + use DatabaseSearchableTrait; protected $table = 'warehouses'; diff --git a/src/Domains/Inventory/Warehouses/Observers/VariantsWarehouseObserver.php b/src/Domains/Inventory/Warehouses/Observers/VariantsWarehouseObserver.php index 14738a94a..6a5e08109 100644 --- a/src/Domains/Inventory/Warehouses/Observers/VariantsWarehouseObserver.php +++ b/src/Domains/Inventory/Warehouses/Observers/VariantsWarehouseObserver.php @@ -25,6 +25,11 @@ public function saved(VariantsWarehouses $variantWarehouse): void 'total_products', $variantWarehouse->getTotalProducts() ); + + $variantWarehouse->variant->set( + 'total_variant_quantity', + $variantWarehouse->variant->setTotalQuantity() + ); } if ($variantWarehouse->wasChanged('status_id')) { @@ -41,5 +46,10 @@ public function created(VariantsWarehouses $variantWarehouse): void 'total_products', $variantWarehouse->getTotalProducts() ); + + $variantWarehouse->variant->set( + 'total_variant_quantity', + $variantWarehouse->variant->setTotalQuantity() + ); } } diff --git a/src/Domains/Inventory/Warehouses/Services/WarehouseService.php b/src/Domains/Inventory/Warehouses/Services/WarehouseService.php index b8e847aaf..54eb27808 100644 --- a/src/Domains/Inventory/Warehouses/Services/WarehouseService.php +++ b/src/Domains/Inventory/Warehouses/Services/WarehouseService.php @@ -32,6 +32,11 @@ public static function updateWarehouseVariant(Variants $variant, UserInterface $ foreach ($warehouses as $warehouseData) { $warehouseModel = WarehouseRepository::getById((int) $warehouseData['id'], $variant->product->company); + $variantWarehouseData = $variant->variantWarehouses()->where('warehouses_id', $warehouseModel->getId())->withTrashed()->first(); + + if ($variantWarehouseData && $variantWarehouseData->trashed()) { + $variantWarehouseData->restore(); + } if (isset($warehouseData['status'])) { $warehouseData['status_id'] = StatusRepository::getById( @@ -97,7 +102,7 @@ public static function removeVariantWarehouses( $variant->company, $user ); - $variantWarehouse = $variant->variantWarehouses('warehouses_id', $warehouse->getId()); + $variantWarehouse = $variant->variantWarehouses()->where('warehouses_id', $warehouse->getId()); $variantWarehouse->first()->delete(); return $variant; diff --git a/src/Domains/Social/Messages/Models/Message.php b/src/Domains/Social/Messages/Models/Message.php index c875ec3e3..5e86890a8 100644 --- a/src/Domains/Social/Messages/Models/Message.php +++ b/src/Domains/Social/Messages/Models/Message.php @@ -5,7 +5,9 @@ namespace Kanvas\Social\Messages\Models; use Baka\Casts\Json; +use Baka\Traits\SoftDeletesTrait; use Baka\Traits\UuidTrait; +use Dyrynda\Database\Support\CascadeSoftDeletes; use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -18,11 +20,10 @@ use Kanvas\Social\MessagesComments\Models\MessageComment; use Kanvas\Social\MessagesTypes\Models\MessageType; use Kanvas\Social\Models\BaseModel; +use Kanvas\Social\Tags\Traits\HasTagsTrait; use Kanvas\Social\Topics\Models\Topic; use Kanvas\Users\Models\Users; use Laravel\Scout\Searchable; -use Baka\Traits\SoftDeletesTrait; -use Dyrynda\Database\Support\CascadeSoftDeletes; /** * Class Message @@ -47,6 +48,7 @@ class Message extends BaseModel use UuidTrait; use Searchable; use HasFactory; + use HasTagsTrait; use CascadeSoftDeletes; use SoftDeletesTrait; diff --git a/src/Domains/Social/Tags/Actions/CreateTagAction.php b/src/Domains/Social/Tags/Actions/CreateTagAction.php new file mode 100644 index 000000000..82f152441 --- /dev/null +++ b/src/Domains/Social/Tags/Actions/CreateTagAction.php @@ -0,0 +1,28 @@ + $this->tagData->app->getId(), + 'companies_id' => $this->tagData->company->getId(), + 'name' => $this->tagData->name, + ], [ + 'users_id' => $this->tagData->user?->getId(), + 'weight' => $this->tagData->weight ?? 0, + ]); + } +} diff --git a/src/Domains/Social/Tags/DataTransferObjects/Tag.php b/src/Domains/Social/Tags/DataTransferObjects/Tag.php new file mode 100644 index 000000000..dd41a0fd9 --- /dev/null +++ b/src/Domains/Social/Tags/DataTransferObjects/Tag.php @@ -0,0 +1,23 @@ +hasMany(TagEntity::class, 'tags_id'); + } + + public function entities() + { + return $this->morphToMany(Tag::class, 'taggable', 'tags_entities', 'tags_id', 'entity_id') + ->using(TagEntity::class) + ->withPivot('entity_namespace', 'companies_id', 'apps_id', 'users_id', 'is_deleted', 'created_at', 'updated_at'); + } + + public function getTable() + { + $databaseName = DB::connection($this->connection)->getDatabaseName(); + return $databaseName . '.tags'; + } +} diff --git a/src/Domains/Social/Tags/Models/TagEntity.php b/src/Domains/Social/Tags/Models/TagEntity.php new file mode 100644 index 000000000..ae4da96d1 --- /dev/null +++ b/src/Domains/Social/Tags/Models/TagEntity.php @@ -0,0 +1,37 @@ +morphTo(null, 'taggable_type', 'entity_id'); + } + + public function systemModule(): BelongsTo + { + return $this->belongsTo(SystemModules::class, 'taggable_type', 'model_name')->where('apps_id', $this->apps_id); + } +} diff --git a/src/Domains/Social/Tags/Traits/HasTagsTrait.php b/src/Domains/Social/Tags/Traits/HasTagsTrait.php new file mode 100644 index 000000000..27dc59253 --- /dev/null +++ b/src/Domains/Social/Tags/Traits/HasTagsTrait.php @@ -0,0 +1,80 @@ +morphToMany(ModelsTag::class, 'taggable', $dbConnection . '.tags_entities', 'entity_id', 'tags_id') + ->using(TagEntity::class); + + return $query; + } + + public function addTag( + string $tag, + ?AppInterface $app = null, + ?UserInterface $user = null, + ?CompanyInterface $company = null + ): void { + $app = $this->app ?? $app; + $user = $this->user ?? $user; + $company = $company ?? $this->company; + + $tag = (new CreateTagAction( + new Tag( + $app, + $user, + $company, + $tag + ) + ))->execute(); + + $this->tags()->attach($this->getId(), [ + 'tags_id' => $tag->getId(), + 'users_id' => $user->getId(), + 'is_deleted' => 0, + ]); + } + + public function addTags( + array $tags, + ?AppInterface $app = null, + ?UserInterface $user = null, + ?CompanyInterface $company = null + ): void { + foreach ($tags as $tag) { + $this->addTag($tag, $app, $user, $company); + } + } + + public function removeTag(string $tag): void + { + $this->tags()->where('name', $tag)->delete(); + } + + public function removeTags(array $tags): void + { + $this->tags()->whereIn('name', $tags)->delete(); + } + + public function syncTags(array $tags): void + { + $this->tags()->detach(); + $this->addTags($tags); + } +} diff --git a/src/Kanvas/Auth/Socialite/Drivers/GoogleDriver.php b/src/Kanvas/Auth/Socialite/Drivers/GoogleDriver.php index fd531ec3d..db9fcdb50 100644 --- a/src/Kanvas/Auth/Socialite/Drivers/GoogleDriver.php +++ b/src/Kanvas/Auth/Socialite/Drivers/GoogleDriver.php @@ -9,6 +9,7 @@ use Kanvas\Auth\Exceptions\AuthenticationException; use Kanvas\Auth\Socialite\Contracts\DriverInterface; use Kanvas\Auth\Socialite\DataTransferObject\User; +use Kanvas\Exceptions\ValidationException; class GoogleDriver implements DriverInterface { @@ -17,6 +18,11 @@ class GoogleDriver implements DriverInterface public function __construct(protected array $config) { $this->client = new Google_Client(); + + if (! isset($this->config['client_id']) || ! isset($this->config['client_secret']) || ! isset($this->config['redirect_uri'])) { + throw new ValidationException('Missing google client_id, client_secret or redirect_uri'); + } + $this->client->setClientId($this->config['client_id']); $this->client->setClientSecret($this->config['client_secret']); $this->client->setRedirectUri($this->config['redirect_uri']); diff --git a/src/Kanvas/Companies/Repositories/CompaniesRepository.php b/src/Kanvas/Companies/Repositories/CompaniesRepository.php index 2f72f8f8a..3b6252c40 100644 --- a/src/Kanvas/Companies/Repositories/CompaniesRepository.php +++ b/src/Kanvas/Companies/Repositories/CompaniesRepository.php @@ -88,6 +88,10 @@ public static function userAssociatedToCompany(Companies $company, Users $user): */ public static function userAssociatedToCompanyAndBranch(Companies $company, CompaniesBranches $branch, Users $user): UsersAssociatedCompanies { + if ($user->isAppOwner()) { + return new UsersAssociatedCompanies(); + } + try { return UsersAssociatedCompanies::where('users_id', $user->getKey()) ->where('companies_id', $company->getKey()) diff --git a/src/Kanvas/Enums/AppEnums.php b/src/Kanvas/Enums/AppEnums.php index 9d9bae31a..11ddbeec0 100644 --- a/src/Kanvas/Enums/AppEnums.php +++ b/src/Kanvas/Enums/AppEnums.php @@ -96,7 +96,7 @@ public function getValue(): mixed self::KANVAS_APP_REGION_HEADER => 'X-Kanvas-Region', self::KANVAS_APP_COMPANY_AUTH_HEADER => 'Company-Authorization', //@deprecated self::DISPLAYNAME_LOGIN => 'displayname_login', - self::VERSION => '1.0-BETA-35', + self::VERSION => '1.0-BETA-36', self::ANONYMOUS_USER_ID => -1, self::DEFAULT_APP_JWT_TOKEN_NAME => 'kanvas-login', }; diff --git a/src/Kanvas/SystemModules/Repositories/SystemModulesRepository.php b/src/Kanvas/SystemModules/Repositories/SystemModulesRepository.php index 72b89f208..fd334eb92 100644 --- a/src/Kanvas/SystemModules/Repositories/SystemModulesRepository.php +++ b/src/Kanvas/SystemModules/Repositories/SystemModulesRepository.php @@ -61,7 +61,7 @@ public static function getByName(string $name, ?AppInterface $app = null): Syste /** * Get the entity from the input */ - public static function getEntityFromInput(SystemModuleInputInterface $entityInput, Users $user): Model + public static function getEntityFromInput(SystemModuleInputInterface $entityInput, Users $user, bool $useCompanyReference = true): Model { $systemModule = self::getByUuidOrModelName($entityInput->systemModuleUuid); @@ -80,7 +80,7 @@ public static function getEntityFromInput(SystemModuleInputInterface $entityInpu if (! $hasAppId && ! $hasCompanyId && (! $isUser && ! $isCompany)) { throw new InternalServerErrorException('This system module doesn\'t allow external custom fields'); } - $field = $hasUuid ? 'uuid' : 'id'; + $field = $hasUuid && Str::isUuid($entityInput->entityId) ? 'uuid' : 'id'; if ($isUser || $isCompany) { $entity = $entityModel::where('uuid', $entityInput->entityId) @@ -104,7 +104,7 @@ public static function getEntityFromInput(SystemModuleInputInterface $entityInpu ); } } else { - if ($user->isAppOwner()) { + if ($user->isAppOwner() || ! $useCompanyReference) { $entity = $entityModel::where($field, $entityInput->entityId) ->fromApp() ->notDeleted() diff --git a/src/Kanvas/Users/Actions/RequestDeleteAccountAction.php b/src/Kanvas/Users/Actions/RequestDeleteAccountAction.php new file mode 100644 index 000000000..ec59e8fe2 --- /dev/null +++ b/src/Kanvas/Users/Actions/RequestDeleteAccountAction.php @@ -0,0 +1,30 @@ + $this->app->getId(), + 'users_id' => $this->user->getId(), + 'email' => $this->user->email, + 'request_date' => date('Y-m-d H:i:s'), + ]); + + return true; + } +} diff --git a/src/Kanvas/Users/Models/RequestDeletedAccount.php b/src/Kanvas/Users/Models/RequestDeletedAccount.php new file mode 100644 index 000000000..64bc4958f --- /dev/null +++ b/src/Kanvas/Users/Models/RequestDeletedAccount.php @@ -0,0 +1,29 @@ +belongsTo(UsersAssociatedApps::class, 'users_id', 'users_id') + ->where('apps_id', $this->apps_id); + } +} diff --git a/tests/GraphQL/ActionEngine/CompanyTaskTest.php b/tests/GraphQL/ActionEngine/CompanyTaskTest.php new file mode 100644 index 000000000..de1a7f140 --- /dev/null +++ b/tests/GraphQL/ActionEngine/CompanyTaskTest.php @@ -0,0 +1,108 @@ +user(); + $branch = $user->getCurrentBranch(); + $title = fake()->title(); + + if (empty($input)) { + $input = [ + 'branch_id' => $branch->getId(), + 'title' => $title, + 'pipeline_stage_id' => 0, + 'people' => [ + 'firstname' => fake()->firstName(), + 'lastname' => fake()->lastName(), + 'contacts' => [ + [ + 'value' => fake()->email(), + 'contacts_types_id' => 1, + 'weight' => 0, + ], + ], + 'address' => [ + [ + 'address' => fake()->address(), + 'city' => fake()->city(), + 'state' => fake()->state(), + 'country' => fake()->country(), + 'zip' => fake()->postcode(), + ], + ], + ], + 'custom_fields' => [], + 'files' => [ + [ + 'url' => 'https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf', + 'name' => 'dummy.pdf', + ], + ], + ]; + } + + return $this->graphQL(' + mutation($input: LeadInput!) { + createLead(input: $input) { + id + uuid + people { + id + }, + systemModule{ + id + } + } + } + ', [ + 'input' => $input, + ])->json(); + } + + public function testGetLeadTaskEngagement() + { + $lead = $this->createLeadAndGetResponse(); + $leadId = $lead['data']['createLead']['id']; + + $this->graphQL(' + query leadTasks($lead_id: ID!) { + leadTaskItems(lead_id: $lead_id) { + data { + name + company_action { + name + action { + slug + } + } + status + config + engagement_start { + uuid + message { + message + } + lead { + title + } + slug + entity_uuid + } engagement_end { + uuid + } + } + } + } + ', [ + 'lead_id' => $leadId, // Passing the lead ID to the GraphQL query + ])->assertOk(); + } +} diff --git a/tests/GraphQL/Ecosystem/Users/UserTest.php b/tests/GraphQL/Ecosystem/Users/UserTest.php index f88d00137..e7aa12349 100644 --- a/tests/GraphQL/Ecosystem/Users/UserTest.php +++ b/tests/GraphQL/Ecosystem/Users/UserTest.php @@ -7,9 +7,9 @@ use Illuminate\Support\Facades\Mail; use Kanvas\Apps\Models\Apps; use Kanvas\Auth\DataTransferObject\LoginInput; +use Kanvas\Enums\AppEnums; use Kanvas\Users\Models\Users; use Tests\TestCase; -use Kanvas\Enums\AppEnums; class UserTest extends TestCase { @@ -281,4 +281,19 @@ public function testDeleteUserSetting() ], ]); } + + public function testRequestDeleteAccount() + { + $this->graphQL(/** @lang GraphQL */ + ' + mutation requestDeleteAccount { + requestDeleteAccount + } + ' + )->assertJson([ + 'data' => [ + 'requestDeleteAccount' => true, + ], + ]); + } } diff --git a/tests/GraphQL/Inventory/VariantTest.php b/tests/GraphQL/Inventory/VariantTest.php index c774978e3..e5c4afbe7 100644 --- a/tests/GraphQL/Inventory/VariantTest.php +++ b/tests/GraphQL/Inventory/VariantTest.php @@ -129,6 +129,7 @@ public function testUpdateVariant(): void $data = [ 'name' => fake()->name, 'description' => fake()->text, + 'sku' => $data['sku'] ]; $this->graphQL(' mutation($id: ID! $data: VariantsUpdateInput!) { @@ -136,6 +137,7 @@ public function testUpdateVariant(): void { id name + sku description } }', ['id' => $id, 'data' => $data])->assertJson([ diff --git a/tests/GraphQL/Social/TagsTest.php b/tests/GraphQL/Social/TagsTest.php new file mode 100644 index 000000000..1cd657e45 --- /dev/null +++ b/tests/GraphQL/Social/TagsTest.php @@ -0,0 +1,391 @@ + fake()->name(), + 'weight' => random_int(1, 100), + ]; + $this->graphQL(/** @lang GRAPHQL */ + ' + mutation createTag( + $input: TagInput! + ) + { + createTag(input: $input) { + id + name + slug + weight + } + } + ', + [ + 'input' => $input, + ] + )->assertJson([ + 'data' => [ + 'createTag' => $input, + ], + ]); + } + + public function testCreateTagWithoutSlug() + { + $input = [ + 'name' => fake()->name(), + 'weight' => random_int(1, 100), + ]; + $this->graphQL(/** @lang GRAPHQL */ + ' + mutation createTag( + $input: TagInput! + ) + { + createTag(input: $input) { + id + name + slug + weight + } + } + ', + [ + 'input' => $input, + ] + )->assertJson([ + 'data' => [ + 'createTag' => $input, + ], + ]); + } + + public function testUpdateTag() + { + $input = [ + 'name' => fake()->name(), + 'slug' => Str::slug(fake()->name()), + 'weight' => random_int(1, 100), + ]; + $response = $this->graphQL(/** @lang GRAPHQL */ + ' + mutation createTag( + $input: TagInput! + ) + { + createTag(input: $input) { + id + name + slug + weight + } + } + ', + [ + 'input' => $input, + ] + ); + $tag = $response->json('data.createTag'); + $input['name'] = fake()->name(); + $input['slug'] = Str::slug(fake()->name()); + $input['weight'] = random_int(1, 100); + $this->graphQL(/** @lang GRAPHQL */ + ' + mutation updateTag( + $id: ID! + $input: TagInput! + ) + { + updateTag(id: $id, input: $input) { + id + name + slug + weight + } + } + ', + [ + 'id' => $tag['id'], + 'input' => $input, + ] + )->assertJson([ + 'data' => [ + 'updateTag' => $input, + ], + ]); + } + + public function testDeleteTag() + { + $input = [ + 'name' => fake()->name(), + 'slug' => Str::slug(fake()->name()), + 'weight' => random_int(1, 100), + ]; + $response = $this->graphQL(/** @lang GRAPHQL */ + ' + mutation createTag( + $input: TagInput! + ) + { + createTag(input: $input) { + id + name + slug + weight + } + } + ', + [ + 'input' => $input, + ] + ); + $tag = $response->json('data.createTag'); + $this->graphQL(/** @lang GRAPHQL */ + ' + mutation deleteTag( + $id: ID! + ) + { + deleteTag(id: $id) + } + ', + [ + 'id' => $tag['id'], + ] + )->assertJson([ + 'data' => [ + 'deleteTag' => true, + ], + ]); + } + + public function testFollowTag() + { + $input = [ + 'name' => fake()->name(), + 'slug' => Str::slug(fake()->name()), + 'weight' => random_int(1, 100), + ]; + $response = $this->graphQL(/** @lang GRAPHQL */ + ' + mutation createTag( + $input: TagInput! + ) + { + createTag(input: $input) { + id + name + slug + weight + } + } + ', + [ + 'input' => $input, + ] + ); + $tag = $response->json('data.createTag'); + $this->graphQL(/** @lang GRAPHQL */ + ' + mutation followTag( + $tag_id: ID! + ) + { + followTag(id: $tag_id) + } + ', + [ + 'tag_id' => $tag['id'], + ] + )->assertJson([ + 'data' => [ + 'followTag' => true, + ], + ]); + } + + public function testAttachTagToMessage() + { + $messageType = MessageType::factory()->create(); + $message = fake()->text(); + Message::makeAllSearchable(); + $app = app(Apps::class); + + $response = $this->graphQL( + ' + mutation createMessage($input: MessageInput!) { + createMessage(input: $input) { + id + message + } + } + ', + [ + 'input' => [ + 'message' => $message, + 'message_verb' => $messageType->verb, + 'system_modules_id' => 1, + 'entity_id' => '1', + ], + ] + ); + $systemModule = SystemModules::fromApp($app) + ->where('model_name', Message::class) + ->first(); + + $message = $response->json('data.createMessage'); + + $input = [ + 'name' => fake()->name(), + 'weight' => random_int(1, 100), + ]; + + $response = $this->graphQL(/** @lang GRAPHQL */ + ' + mutation createTag( + $input: TagInput! + ) + { + createTag(input: $input) { + id + name + weight + } + } + ', + [ + 'input' => $input, + ] + ); + + $tag = $response->json('data.createTag'); + $attach = [ + 'tag_id' => $tag['id'], + 'system_module_uuid' => $systemModule->uuid, + 'entity_id' => $message['id'], + ]; + $this->graphQL(/** @lang GRAPHQL */ + ' + mutation attachTagToEntity( + $input: AttachTagEntityInput! + ) + { + attachTagToEntity(input: $input) + } + ', + [ + 'input' => $attach, + ] + )->assertJson([ + 'data' => [ + 'attachTagToEntity' => true, + ], + ]); + + $this->graphQL(' + query tag( + $where: QueryTagsWhereWhereConditions + ){ + tags(where: $where) { + data { + id + name + slug + weight + taggables { + tags_id + entity_id + } + } + } + } + ', [ + 'where' => [ + 'value' => $tag['id'], + 'column' => 'ID', + 'operator' => 'EQ', + ], + ])->assertJson([ + 'data' => [ + 'tags' => [ + 'data' => [ + [ + 'id' => $tag['id'], + 'name' => $input['name'], + 'weight' => $input['weight'], + 'taggables' => [ + [ + 'tags_id' => $tag['id'], + 'entity_id' => $message['id'], + ], + ], + ], + ], + ], + ], + ]); + + $this->graphQL( + ' + query message( + $where: QueryMessagesWhereWhereConditions + ){ + messages(where: $where) { + data { + id + message + tags { + data { + id + name + slug + weight + } + } + } + } + }', + [ + 'where' => [ + 'value' => $message['id'], + 'column' => 'ID', + 'operator' => 'EQ', + ], + ] + )->assertJson([ + 'data' => [ + 'messages' => [ + 'data' => [ + [ + 'id' => $message['id'], + 'message' => $message['message'], + 'tags' => [ + 'data' => [ + [ + 'id' => $tag['id'], + 'name' => $input['name'], + 'weight' => $input['weight'], + ] + ], + ] + ], + ], + ], + ], + ]); + } +} diff --git a/tests/Social/Integration/TagsTest.php b/tests/Social/Integration/TagsTest.php new file mode 100644 index 000000000..99f1d3fe3 --- /dev/null +++ b/tests/Social/Integration/TagsTest.php @@ -0,0 +1,84 @@ +user()->getCurrentCompany(); + $people = People::factory()->withAppId($app->getId())->withCompanyId($company->getId())->create(); + + $people->addTag('test'); + + $this->assertNotEmpty($people->tags()->get()); + $this->assertCount(1, $people->tags()->get()); + } + + public function testAddMultipleTagsToEntity(): void + { + $app = app(Apps::class); + $company = auth()->user()->getCurrentCompany(); + $people = People::factory()->withAppId($app->getId())->withCompanyId($company->getId())->create(); + + $people->addTags(['test', 'test2']); + + $this->assertNotEmpty($people->tags()->get()); + $this->assertCount(2, $people->tags()->get()); + } + + public function testRemoveTagFromEntity(): void + { + $app = app(Apps::class); + $company = auth()->user()->getCurrentCompany(); + $people = People::factory()->withAppId($app->getId())->withCompanyId($company->getId())->create(); + + $people->addTag('test'); + $people->addTag('test2'); + + $this->assertCount(2, $people->tags()->get()); + + $people->removeTag('test'); + + $this->assertCount(1, $people->tags()->get()); + } + + public function testRemoveTagsFromEntity(): void + { + $app = app(Apps::class); + $company = auth()->user()->getCurrentCompany(); + $people = People::factory()->withAppId($app->getId())->withCompanyId($company->getId())->create(); + + $people->addTag('test'); + $people->addTag('test2'); + + $this->assertCount(2, $people->tags()->get()); + + $people->removeTags(['test', 'test2']); + + $this->assertCount(0, $people->tags()->get()); + } + + public function testSyncTagsFromEntity(): void + { + $app = app(Apps::class); + $company = auth()->user()->getCurrentCompany(); + $people = People::factory()->withAppId($app->getId())->withCompanyId($company->getId())->create(); + + $people->addTag('test'); + $people->addTag('test2'); + + $this->assertCount(2, $people->tags()->get()); + + $people->syncTags(['test3', 'test4']); + + $this->assertCount(2, $people->tags()->get()); + } +}