markTestSkipped('Catalog tables are missing.'); } } protected function seedRequiredData(): void { try { if (! Category::query()->exists()) { Category::factory()->create([ 'parent_id' => null, ]); } if (! Channel::query()->exists()) { Channel::factory()->create(); } } catch (\Exception $e) { $this->markTestSkipped('Test database not properly configured: '.$e->getMessage()); } } protected function createSimpleProduct(array $overrides = []): Product { $this->seedRequiredData(); $attributeFamilyId = (int) (DB::table('attribute_families')->value('id') ?? 0); if ($attributeFamilyId === 0) { $this->markTestSkipped('Run Bagisto seeders for attribute_families.'); } $product = Product::factory()->create([ 'type' => 'simple', 'attribute_family_id' => $attributeFamilyId, ...$overrides, ]); $this->upsertProductAttributeValue($product->id, 'name', 'Test '.$product->sku); $this->upsertProductAttributeValue($product->id, 'url_key', strtolower($product->sku)); $this->upsertProductAttributeValue($product->id, 'status', 1, null, 'default'); $this->upsertProductAttributeValue($product->id, 'price', 10.0, null, null); return $product; } protected function upsertProductAttributeValue( int $productId, string $attributeCode, mixed $value, ?string $locale = 'en', ?string $channel = null ): void { $attribute = Attribute::query()->where('code', $attributeCode)->first(); if (! $attribute) { $this->markTestSkipped(sprintf('Required attribute "%s" not found. Run Bagisto seeders for attributes.', $attributeCode)); } $type = (string) ($attribute->type ?? 'text'); $field = ProductAttributeValue::$attributeTypeFields[$type] ?? 'text_value'; $payload = [ 'product_id' => $productId, 'attribute_id' => (int) $attribute->id, 'locale' => $attribute->value_per_locale ? $locale : null, 'channel' => $attribute->value_per_channel ? ($channel ?? 'default') : null, 'text_value' => null, 'boolean_value' => null, 'integer_value' => null, 'float_value' => null, 'datetime_value' => null, 'date_value' => null, 'json_value' => null, ]; $payload[$field] = $field === 'boolean_value' ? (bool) $value : ($field === 'float_value' ? (float) $value : $value); $payload['unique_id'] = implode('|', array_filter([ $payload['channel'], $payload['locale'], $productId, (int) $attribute->id, ])); ProductAttributeValue::query()->updateOrCreate( [ 'product_id' => $productId, 'attribute_id' => (int) $attribute->id, 'locale' => $payload['locale'], 'channel' => $payload['channel'], ], $payload ); } protected function attributeText(int $productId, string $code): ?string { $attribute = Attribute::query()->where('code', $code)->first(); if (! $attribute) { return null; } $field = ProductAttributeValue::$attributeTypeFields[$attribute->type] ?? 'text_value'; $row = ProductAttributeValue::query() ->where('product_id', $productId) ->where('attribute_id', $attribute->id) ->orderByDesc('id') ->first(); if (! $row) { return null; } $value = $row->{$field}; return $value === null ? null : (string) $value; } protected function ensureInventory(Product $product, int $qty = 50): int { $inventorySourceId = (int) (DB::table('inventory_sources')->value('id') ?? 0); if (! $inventorySourceId) { $this->markTestSkipped('No inventory_sources found. Run Bagisto seeders for inventory sources.'); } DB::table('product_inventories')->updateOrInsert( [ 'product_id' => $product->id, 'inventory_source_id' => $inventorySourceId, 'vendor_id' => 0, ], ['qty' => $qty] ); return $inventorySourceId; } protected function createNamedCategory(string $name, string $locale = 'en'): Category { $category = Category::factory()->create([ 'parent_id' => null, ]); DB::table('category_translations')->updateOrInsert( [ 'category_id' => $category->id, 'locale' => $locale, ], [ 'name' => $name, 'slug' => strtolower(str_replace(' ', '-', $name)).'-'.$category->id, 'url_path' => strtolower(str_replace(' ', '-', $name)).'-'.$category->id, 'description' => $name, ] ); return $category->fresh(); } }