diff --git a/database/migrations/Workflow/2024_09_15_232722_create_integration_history_table.php b/database/migrations/Workflow/2024_09_15_232722_create_integration_history_table.php new file mode 100644 index 000000000..4a1ec81d2 --- /dev/null +++ b/database/migrations/Workflow/2024_09_15_232722_create_integration_history_table.php @@ -0,0 +1,47 @@ +id(); + $table->unsignedBigInteger('apps_id')->default(0); + $table->string('entity_namespace'); + $table->unsignedBigInteger('entity_id'); + $table->unsignedBigInteger('integrations_company_id'); + $table->unsignedBigInteger('status_id'); + $table->text('response')->nullable(); + $table->text('exception')->nullable(); + $table->timestamps(); + $table->tinyInteger('is_deleted')->default(0); + + // Foreign key constraints + $table->foreign('integrations_company_id')->references('id')->on('integration_companies'); + $table->foreign('status_id')->references('id')->on('status'); + + $table->index('entity_id', 'entity_id_index'); + $table->index('status_id', 'status_id_index'); + $table->index('integrations_company_id', 'integrations_company_id_index'); + $table->index('apps_id', 'apps_id_index'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('entity_integration_history'); + } +}; diff --git a/src/Domains/Connectors/Shopify/Workflows/Activities/ExportProductToShopifyActivity.php b/src/Domains/Connectors/Shopify/Workflows/Activities/ExportProductToShopifyActivity.php index 3f3bd4e68..c0c22fbee 100644 --- a/src/Domains/Connectors/Shopify/Workflows/Activities/ExportProductToShopifyActivity.php +++ b/src/Domains/Connectors/Shopify/Workflows/Activities/ExportProductToShopifyActivity.php @@ -10,8 +10,11 @@ use Kanvas\Inventory\Products\Models\Products; use Kanvas\Workflow\Enums\IntegrationsEnum; use Kanvas\Workflow\Enums\StatusEnum; +use Kanvas\Workflow\Integrations\Actions\AddEntityIntegrationHistoryAction; +use Kanvas\Workflow\Integrations\DataTransferObject\EntityIntegrationHistory; use Kanvas\Workflow\Integrations\Models\IntegrationsCompany; use Kanvas\Workflow\Integrations\Models\Status; +use Throwable; use Workflow\Activity; class ExportProductToShopifyActivity extends Activity @@ -21,6 +24,7 @@ class ExportProductToShopifyActivity extends Activity public function execute(Products $product, Apps $app, array $params): array { $response = []; + $exception = null; $status = Status::where('slug', StatusEnum::ACTIVE->value) ->where('apps_id', 0) ->first(); @@ -43,7 +47,32 @@ public function execute(Products $product, Apps $app, array $params): array warehouses: $warehouse ); - $response = $shopifyService->saveProduct($product, ShopifyStatusEnum::ACTIVE); + try { + $response = $shopifyService->saveProduct($product, ShopifyStatusEnum::ACTIVE); + $historyResponse = json_encode($response); + $status = Status::where('slug', StatusEnum::CONNECTED->value) + ->where('apps_id', 0) + ->first(); + } catch (Throwable $exception) { + $status = Status::where('slug', StatusEnum::FAILED->value) + ->where('apps_id', 0) + ->first(); + } + + $dto = new EntityIntegrationHistory( + app: $app, + integrationCompany: $integrationCompany, + status: $status, + entity: $product, + response: $historyResponse ?? null, + exception: $exception + ); + + (new AddEntityIntegrationHistoryAction( + dto: $dto, + app: $app, + status: $status + ))->execute(); } } } @@ -51,7 +80,7 @@ public function execute(Products $product, Apps $app, array $params): array return [ 'company' => $product->company->getId(), 'product' => $product->getId(), - 'shopify_response' => $response, + 'shopify_response' => $response ?? [], ]; } } diff --git a/src/Domains/Inventory/Products/Models/Products.php b/src/Domains/Inventory/Products/Models/Products.php index caf49eb4f..823d4d698 100644 --- a/src/Domains/Inventory/Products/Models/Products.php +++ b/src/Domains/Inventory/Products/Models/Products.php @@ -31,6 +31,8 @@ use Kanvas\Inventory\Warehouses\Models\Warehouses; use Kanvas\Social\Interactions\Traits\LikableTrait; use Kanvas\Social\Tags\Traits\HasTagsTrait; +use Kanvas\Workflow\Contracts\EntityIntegrationInterface; +use Kanvas\Workflow\Contracts\IntegrationInterface; use Kanvas\Workflow\Traits\CanUseWorkflow; use Laravel\Scout\Searchable; @@ -53,7 +55,7 @@ * @property string $published_at * @property bool $is_deleted */ -class Products extends BaseModel +class Products extends BaseModel implements EntityIntegrationInterface { use UuidTrait; use SlugTrait; diff --git a/src/Domains/Inventory/Variants/Models/Variants.php b/src/Domains/Inventory/Variants/Models/Variants.php index 2cb05a33e..5f93c6558 100644 --- a/src/Domains/Inventory/Variants/Models/Variants.php +++ b/src/Domains/Inventory/Variants/Models/Variants.php @@ -29,6 +29,7 @@ use Kanvas\Inventory\Variants\Actions\AddAttributeAction; use Kanvas\Inventory\Warehouses\Models\Warehouses; use Kanvas\Social\Interactions\Traits\SocialInteractionsTrait; +use Kanvas\Workflow\Contracts\EntityIntegrationInterface; use Laravel\Scout\Searchable; /** @@ -49,7 +50,7 @@ * @property string barcode * @property string serial_number */ -class Variants extends BaseModel +class Variants extends BaseModel implements EntityIntegrationInterface { use SlugTrait; use UuidTrait; diff --git a/src/Domains/Workflow/Contracts/EntityIntegrationInterface.php b/src/Domains/Workflow/Contracts/EntityIntegrationInterface.php new file mode 100644 index 000000000..00a51b805 --- /dev/null +++ b/src/Domains/Workflow/Contracts/EntityIntegrationInterface.php @@ -0,0 +1,10 @@ +entity_namespace = get_class($this->dto->entity); + $integrationHistory->entity_id = $this->dto->entity->getId(); + $integrationHistory->apps_id = $this->app->getId(); + $integrationHistory->integrations_company_id = $this->dto->integrationCompany->getId(); + $integrationHistory->status_id = $this->dto->status->getId(); + $integrationHistory->response = $this->dto->response; + $integrationHistory->exception = $this->dto->exception; + + $integrationHistory->saveOrFail(); + return $integrationHistory; + } +} diff --git a/src/Domains/Workflow/Integrations/DataTransferObject/EntityIntegrationHistory.php b/src/Domains/Workflow/Integrations/DataTransferObject/EntityIntegrationHistory.php new file mode 100644 index 000000000..a620583d6 --- /dev/null +++ b/src/Domains/Workflow/Integrations/DataTransferObject/EntityIntegrationHistory.php @@ -0,0 +1,29 @@ + 'boolean', + ]; + + public function integrationCompany(): BelongsTo + { + return $this->belongsTo(IntegrationsCompany::class, 'integrations_company_id'); + } + + public function entity(): BelongsTo + { + return $this->belongsTo($this->entity_class_name, 'entity_id'); + } + + public function status(): BelongsTo + { + return $this->belongsTo(Status::class, 'status_id'); + } + + public function setStatus(Status $status): void + { + $this->status_id = $status->getId(); + $this->saveOrFail(); + } +} diff --git a/src/Domains/Workflow/Integrations/Models/IntegrationsCompany.php b/src/Domains/Workflow/Integrations/Models/IntegrationsCompany.php index d3b8160d3..4ea9080eb 100644 --- a/src/Domains/Workflow/Integrations/Models/IntegrationsCompany.php +++ b/src/Domains/Workflow/Integrations/Models/IntegrationsCompany.php @@ -6,6 +6,7 @@ use Baka\Casts\Json; use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\HasMany; use Kanvas\Companies\Models\Companies; use Kanvas\Inventory\Regions\Models\Regions; use Kanvas\Workflow\Enums\StatusEnum; @@ -43,6 +44,11 @@ public function status(): BelongsTo return $this->belongsTo(Status::class, 'status_id'); } + public function history(): HasMany + { + return $this->hasMany(EntityIntegrationHistory::class, 'integrations_company_id'); + } + public function setStatus(Status $status): void { $this->status_id = $status->getId(); diff --git a/src/Domains/Workflow/Integrations/Services/EntityIntegrationHistoryService.php b/src/Domains/Workflow/Integrations/Services/EntityIntegrationHistoryService.php new file mode 100644 index 000000000..e05d80090 --- /dev/null +++ b/src/Domains/Workflow/Integrations/Services/EntityIntegrationHistoryService.php @@ -0,0 +1,51 @@ +getId()) + ->where('companies_id', $this->company->getId()) + ->when($region, function ($query, $region) { + return $query->where('region_id', $region->getId()); + }) + ->get(); + + foreach ($integrationsCompany as $integrationCompany) { + if ($integrationCompany->history()->where('apps_id', $this->app->getId())->exists()) { + $integrationStatus = array_merge( + $integrationStatus, + $integrationCompany->history->map(function ($history) { + return $history; + })->all() + ); + }; + } + return $integrationStatus; + } +} diff --git a/tests/Connectors/Integration/Shopify/ExportProductToShopifyActivityTest.php b/tests/Connectors/Integration/Shopify/ExportProductToShopifyActivityTest.php index 3a5a21803..7bfaca7e5 100644 --- a/tests/Connectors/Integration/Shopify/ExportProductToShopifyActivityTest.php +++ b/tests/Connectors/Integration/Shopify/ExportProductToShopifyActivityTest.php @@ -7,6 +7,9 @@ use Kanvas\Apps\Models\Apps; use Kanvas\Connectors\Shopify\Workflows\Activities\ExportProductToShopifyActivity; use Kanvas\Inventory\Products\Models\Products; +use Kanvas\Workflow\Integrations\Models\EntityIntegrationHistory; +use Kanvas\Workflow\Integrations\Services\EntityIntegrationHistoryService; +use Kanvas\Workflow\Models\Integrations; use Kanvas\Workflow\Models\StoredWorkflow; use Tests\Connectors\Traits\HasShopifyConfiguration; use Tests\TestCase; @@ -38,9 +41,48 @@ public function testExportProductWorkflow(): void params: [] ); - //We need to DELETE the exported product after the test. + //@todo We need to DELETE the exported product after the test. $this->assertArrayHasKey('shopify_response', $result); $this->assertArrayHasKey('company', $result); $this->assertArrayHasKey('product', $result); } + + public function testIntegrationHistory(): void + { + $integration = Integrations::first(); + $product = Products::first(); + + $variant = $product->variants()->first(); + $warehouse = $variant->warehouses()->first(); + + $this->setupShopifyIntegration($product, $warehouse->region); + + $exportActivity = new ExportProductToShopifyActivity( + 0, + now()->toDateTimeString(), + StoredWorkflow::make(), + [] + ); + + $app = app(Apps::class); + + $result = $exportActivity->execute( + product: $product, + app: $app, + params: [] + ); + + //@todo We need to DELETE the exported product after the test. + $this->assertArrayHasKey('shopify_response', $result); + $this->assertArrayHasKey('company', $result); + $this->assertArrayHasKey('product', $result); + + $histories = (new EntityIntegrationHistoryService( + app: $app, + company: $product->company + ))->getByIntegration($integration); + + $this->assertNotEmpty($histories); + $this->assertInstanceOf(EntityIntegrationHistory::class, $histories[0]); + } } diff --git a/tests/Connectors/Traits/HasShopifyConfiguration.php b/tests/Connectors/Traits/HasShopifyConfiguration.php index 830d9759b..e1b343d94 100644 --- a/tests/Connectors/Traits/HasShopifyConfiguration.php +++ b/tests/Connectors/Traits/HasShopifyConfiguration.php @@ -59,6 +59,6 @@ public function setupShopifyIntegration(Products $product, Regions $region) // for the time being this can only work with shopify integration. // we need to figure out how to standard is it. - (new CreateIntegrationCompanyAction($integrationDto, auth()->user(), $status))->execute(); + return (new CreateIntegrationCompanyAction($integrationDto, auth()->user(), $status))->execute(); } }