feat: Implement inventory policy for product variants
- Introduced InventoryPolicy enum to manage tracked and unlimited inventory types. - Updated ProductVariant model to include inventory_policy and cantidad_vendida attributes. - Modified addItem and updateItem methods in Cart to check available quantity based on inventory policy. - Added migration to include inventory_policy and cantidad_vendida in productos_variantes table. - Enhanced tests to validate inventory behavior for both tracked and unlimited policies. - Updated various request and resource classes to handle new inventory fields.
This commit is contained in:
@@ -2,14 +2,19 @@
|
||||
|
||||
namespace Tests\Feature\Cart;
|
||||
|
||||
use App\Domains\Attachable\Enums\AttachmentType;
|
||||
use App\Domains\Attachable\Models\Attachment;
|
||||
use App\Domains\Auth\Models\User;
|
||||
use App\Domains\Catalog\Models\Product;
|
||||
use App\Domains\Catalog\Enums\InventoryPolicy;
|
||||
use App\Domains\Catalog\Models\Attribute;
|
||||
use App\Domains\Catalog\Models\ProductVariant;
|
||||
use App\Domains\Catalog\Models\Category;
|
||||
use App\Domains\Catalog\Models\Product;
|
||||
use App\Domains\Catalog\Models\ProductAttribute;
|
||||
use App\Domains\Catalog\Models\ProductVariant;
|
||||
use App\Domains\Catalog\Models\ProductVariantDefinition;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Str;
|
||||
use Tests\TestCase;
|
||||
|
||||
class CartControllerTest extends TestCase
|
||||
@@ -29,7 +34,7 @@ class CartControllerTest extends TestCase
|
||||
'status' => 'active',
|
||||
'items' => [],
|
||||
'subtotal' => '0.00',
|
||||
]
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -315,18 +320,73 @@ class CartControllerTest extends TestCase
|
||||
->assertJsonValidationErrors(['cantidad' => 'El máximo que se puede agregar es 2.']);
|
||||
}
|
||||
|
||||
public function test_unlimited_inventory_can_be_reserved_updated_and_released_without_real_stock(): void
|
||||
{
|
||||
$variant = $this->createVariantForTenant(
|
||||
'acme',
|
||||
0,
|
||||
'10.00',
|
||||
'unlimited',
|
||||
InventoryPolicy::Unlimited,
|
||||
);
|
||||
|
||||
$response = $this->postJson('/api/tenants/acme/cart/items', [
|
||||
'product_variant_id' => $variant->id,
|
||||
'cantidad' => 100,
|
||||
])->assertOk();
|
||||
|
||||
$guestToken = $response->getCookie('guest_token', false)?->getValue();
|
||||
$this->assertDatabaseHas('productos_variantes', [
|
||||
'id' => $variant->id,
|
||||
'stock_real' => 0,
|
||||
'stock_reservado' => 100,
|
||||
]);
|
||||
|
||||
$this->call(
|
||||
'PATCH',
|
||||
"/api/tenants/acme/cart/items/{$variant->id}",
|
||||
[],
|
||||
['guest_token' => $guestToken],
|
||||
[],
|
||||
['HTTP_Accept' => 'application/json', 'CONTENT_TYPE' => 'application/json'],
|
||||
json_encode(['cantidad' => 150]),
|
||||
)->assertOk();
|
||||
|
||||
$this->assertDatabaseHas('productos_variantes', [
|
||||
'id' => $variant->id,
|
||||
'stock_real' => 0,
|
||||
'stock_reservado' => 150,
|
||||
]);
|
||||
|
||||
$this->call(
|
||||
'DELETE',
|
||||
"/api/tenants/acme/cart/items/{$variant->id}",
|
||||
[],
|
||||
['guest_token' => $guestToken],
|
||||
[],
|
||||
['HTTP_Accept' => 'application/json'],
|
||||
)->assertOk();
|
||||
|
||||
$this->assertDatabaseHas('productos_variantes', [
|
||||
'id' => $variant->id,
|
||||
'stock_real' => 0,
|
||||
'stock_reservado' => 0,
|
||||
]);
|
||||
}
|
||||
|
||||
protected function createVariantForTenant(
|
||||
string $tenantCode,
|
||||
int $stock,
|
||||
string $price,
|
||||
string $slugPrefix = 'shirt',
|
||||
InventoryPolicy $inventoryPolicy = InventoryPolicy::Tracked,
|
||||
): ProductVariant {
|
||||
$tenant = Tenant::query()->where('codigo', $tenantCode)->first();
|
||||
if (! $tenant) {
|
||||
$this->createTenant($tenantCode, ucfirst($tenantCode), "{$tenantCode}.com");
|
||||
}
|
||||
|
||||
$category = \App\Domains\Catalog\Models\Category::query()->create([
|
||||
$category = Category::query()->create([
|
||||
'tenant_code' => $tenantCode,
|
||||
'nombre' => "{$slugPrefix} category {$tenantCode}",
|
||||
]);
|
||||
@@ -342,6 +402,7 @@ class CartControllerTest extends TestCase
|
||||
|
||||
return ProductVariant::query()->create([
|
||||
'producto_id' => $product->id,
|
||||
'inventory_policy' => $inventoryPolicy->value,
|
||||
'slug' => "{$slugPrefix}-variant-".ProductVariant::query()->count(),
|
||||
'nombre' => ucfirst($slugPrefix).' Variant',
|
||||
'stock' => $stock,
|
||||
@@ -352,21 +413,21 @@ class CartControllerTest extends TestCase
|
||||
|
||||
protected function createTenant(string $codigo, string $nombre, string $dominio): Tenant
|
||||
{
|
||||
$hdrKey = (string) \Illuminate\Support\Str::uuid();
|
||||
$ftrKey = (string) \Illuminate\Support\Str::uuid();
|
||||
$hdrKey = (string) Str::uuid();
|
||||
$ftrKey = (string) Str::uuid();
|
||||
|
||||
$headerAttachment = \App\Domains\Attachable\Models\Attachment::create([
|
||||
$headerAttachment = Attachment::create([
|
||||
'key' => $hdrKey,
|
||||
'path' => 'tenants/' . $hdrKey . '.png',
|
||||
'path' => 'tenants/'.$hdrKey.'.png',
|
||||
'filename' => 'logo_header.png',
|
||||
'type' => \App\Domains\Attachable\Enums\AttachmentType::Image,
|
||||
'type' => AttachmentType::Image,
|
||||
'mime_type' => 'image/png',
|
||||
]);
|
||||
$footerAttachment = \App\Domains\Attachable\Models\Attachment::create([
|
||||
$footerAttachment = Attachment::create([
|
||||
'key' => $ftrKey,
|
||||
'path' => 'tenants/' . $ftrKey . '.png',
|
||||
'path' => 'tenants/'.$ftrKey.'.png',
|
||||
'filename' => 'logo_footer.png',
|
||||
'type' => \App\Domains\Attachable\Enums\AttachmentType::Image,
|
||||
'type' => AttachmentType::Image,
|
||||
'mime_type' => 'image/png',
|
||||
]);
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace Tests\Feature\Catalog;
|
||||
|
||||
use App\Domains\Attachable\Enums\AttachmentType;
|
||||
use App\Domains\Attachable\Models\Attachment;
|
||||
use App\Domains\Catalog\Enums\InventoryPolicy;
|
||||
use App\Domains\Catalog\Models\Attribute;
|
||||
use App\Domains\Catalog\Models\Brand;
|
||||
use App\Domains\Catalog\Models\Product;
|
||||
@@ -768,6 +769,67 @@ class ProductControllerTest extends TestCase
|
||||
$response->assertJsonValidationErrors(['variant_id']);
|
||||
}
|
||||
|
||||
public function test_it_creates_an_unlimited_default_variant_and_exposes_inventory_fields(): void
|
||||
{
|
||||
$response = $this->postJson("/api/tenants/{$this->tenant->codigo}/productos", [
|
||||
'categoria_id' => 1,
|
||||
'brand_id' => $this->brand->id,
|
||||
'slug' => 'unlimited-product',
|
||||
'nombre' => 'Unlimited Product',
|
||||
'precio' => 100,
|
||||
'stock' => 0,
|
||||
'inventory_policy' => InventoryPolicy::Unlimited->value,
|
||||
]);
|
||||
|
||||
$response->assertCreated();
|
||||
|
||||
$product = Product::query()->where('slug', 'unlimited-product')->firstOrFail();
|
||||
$variant = $product->variants()->firstOrFail();
|
||||
$this->assertSame(InventoryPolicy::Unlimited, $variant->inventory_policy);
|
||||
|
||||
$this->getJson("/api/tenants/{$this->tenant->codigo}/productos/{$product->id}")
|
||||
->assertOk()
|
||||
->assertJsonPath('data.variant.id', $variant->id)
|
||||
->assertJsonPath('data.variant.inventory_policy', InventoryPolicy::Unlimited->value)
|
||||
->assertJsonPath('data.variant.cantidad_maxima', null)
|
||||
->assertJsonPath('data.variant.cantidad_vendida', 0)
|
||||
->assertJsonPath('data.variants_map.0.inventory_policy', InventoryPolicy::Unlimited->value)
|
||||
->assertJsonPath('data.variants_map.0.cantidad_maxima', null)
|
||||
->assertJsonPath('data.variants_map.0.cantidad_vendida', 0);
|
||||
}
|
||||
|
||||
public function test_it_rejects_invalid_or_updated_inventory_policies(): void
|
||||
{
|
||||
$this->postJson("/api/tenants/{$this->tenant->codigo}/productos", [
|
||||
'categoria_id' => 1,
|
||||
'brand_id' => $this->brand->id,
|
||||
'slug' => 'invalid-policy',
|
||||
'nombre' => 'Invalid Policy',
|
||||
'precio' => 100,
|
||||
'inventory_policy' => 'sometimes',
|
||||
])->assertUnprocessable()->assertJsonValidationErrors(['inventory_policy']);
|
||||
|
||||
$product = Product::query()->create([
|
||||
'tenant_codigo' => $this->tenant->codigo,
|
||||
'categoria_id' => 1,
|
||||
'brand_id' => $this->brand->id,
|
||||
'slug' => 'immutable-policy',
|
||||
'nombre' => 'Immutable Policy',
|
||||
'precio' => 100,
|
||||
]);
|
||||
$variant = $product->variants()->create([
|
||||
'stock' => 0,
|
||||
'inventory_policy' => InventoryPolicy::Unlimited->value,
|
||||
]);
|
||||
|
||||
$this->putJson(
|
||||
"/api/tenants/{$this->tenant->codigo}/productos/{$product->id}/variants/{$variant->id}",
|
||||
['inventory_policy' => InventoryPolicy::Tracked->value],
|
||||
)->assertUnprocessable()->assertJsonValidationErrors(['inventory_policy']);
|
||||
|
||||
$this->assertSame(InventoryPolicy::Unlimited, $variant->fresh()->inventory_policy);
|
||||
}
|
||||
|
||||
private function createAttachment(string $path): Attachment
|
||||
{
|
||||
return Attachment::create([
|
||||
|
||||
@@ -2,8 +2,12 @@
|
||||
|
||||
namespace Tests\Feature\Integration;
|
||||
|
||||
use App\Domains\Attachable\Enums\AttachmentType;
|
||||
use App\Domains\Attachable\Models\Attachment;
|
||||
use App\Domains\Auth\Models\User;
|
||||
use App\Domains\Cart\Models\Cart;
|
||||
use App\Domains\Catalog\Enums\InventoryPolicy;
|
||||
use App\Domains\Catalog\Models\Category;
|
||||
use App\Domains\Catalog\Models\Product;
|
||||
use App\Domains\Catalog\Models\ProductVariant;
|
||||
use App\Domains\Integration\Models\Integration;
|
||||
@@ -14,6 +18,7 @@ use App\Domains\Tenant\Models\Tenant;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Str;
|
||||
use Tests\TestCase;
|
||||
|
||||
class TelepagosWebhookTest extends TestCase
|
||||
@@ -24,7 +29,7 @@ class TelepagosWebhookTest extends TestCase
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
config(['services.integrations.secret' => 'base64:' . base64_encode(random_bytes(32))]);
|
||||
config(['services.integrations.secret' => 'base64:'.base64_encode(random_bytes(32))]);
|
||||
Cache::flush();
|
||||
}
|
||||
|
||||
@@ -113,6 +118,13 @@ class TelepagosWebhookTest extends TestCase
|
||||
'total' => 50,
|
||||
]);
|
||||
|
||||
$this->assertDatabaseHas('productos_variantes', [
|
||||
'id' => $variant->id,
|
||||
'stock_real' => 9,
|
||||
'stock_reservado' => 2,
|
||||
'cantidad_vendida' => 1,
|
||||
]);
|
||||
|
||||
$this->assertDatabaseMissing('compra_items', [
|
||||
'compra_id' => $newerPurchase->id,
|
||||
]);
|
||||
@@ -122,6 +134,60 @@ class TelepagosWebhookTest extends TestCase
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_transfer_webhook_buys_unlimited_inventory_without_reducing_real_stock(): void
|
||||
{
|
||||
$tenant = $this->createTenant('unlimited', 'Unlimited', 'unlimited.com.ar');
|
||||
$this->configureTelepagosIntegration($tenant);
|
||||
$user = User::factory()->create();
|
||||
$variant = $this->createVariantForTenant(
|
||||
'unlimited',
|
||||
0,
|
||||
'50.00',
|
||||
'service',
|
||||
InventoryPolicy::Unlimited,
|
||||
);
|
||||
$purchase = $this->createPendingTransferPurchase(
|
||||
$tenant,
|
||||
$user->id,
|
||||
$variant->id,
|
||||
3,
|
||||
'87654321',
|
||||
);
|
||||
|
||||
Http::fake([
|
||||
'https://api.telepagos.com.ar/v2/auth/token' => Http::response([
|
||||
'status' => 'ok',
|
||||
'token' => 'test-token',
|
||||
'expires_at' => now()->addHour()->toIso8601String(),
|
||||
]),
|
||||
'https://api.telepagos.com.ar/v2/payment/cashin/7000' => Http::response([
|
||||
'status' => 'ok',
|
||||
'data' => [
|
||||
'amount' => 150,
|
||||
'operation_id' => 1,
|
||||
'transaction_id' => 'tx-unlimited',
|
||||
'buyer' => ['cuit' => '20876543219'],
|
||||
],
|
||||
]),
|
||||
]);
|
||||
|
||||
$this->postJson('/api/webhooks/telepagos/unlimited', ['id' => '7000'])
|
||||
->assertOk()
|
||||
->assertJsonPath('status', 'success');
|
||||
|
||||
$this->assertDatabaseHas('compras', [
|
||||
'id' => $purchase->id,
|
||||
'status' => Purchase::STATUS_PAID,
|
||||
]);
|
||||
$this->assertDatabaseHas('productos_variantes', [
|
||||
'id' => $variant->id,
|
||||
'inventory_policy' => InventoryPolicy::Unlimited->value,
|
||||
'stock_real' => 0,
|
||||
'stock_reservado' => 0,
|
||||
'cantidad_vendida' => 3,
|
||||
]);
|
||||
}
|
||||
|
||||
private function createPendingTransferPurchase(
|
||||
Tenant $tenant,
|
||||
int $userId,
|
||||
@@ -184,8 +250,9 @@ class TelepagosWebhookTest extends TestCase
|
||||
int $stock,
|
||||
string $price,
|
||||
string $slugPrefix = 'shirt',
|
||||
InventoryPolicy $inventoryPolicy = InventoryPolicy::Tracked,
|
||||
): ProductVariant {
|
||||
$category = \App\Domains\Catalog\Models\Category::query()->create([
|
||||
$category = Category::query()->create([
|
||||
'tenant_code' => $tenantCode,
|
||||
'nombre' => "{$slugPrefix} category {$tenantCode}",
|
||||
]);
|
||||
@@ -201,6 +268,7 @@ class TelepagosWebhookTest extends TestCase
|
||||
|
||||
return ProductVariant::query()->create([
|
||||
'producto_id' => $product->id,
|
||||
'inventory_policy' => $inventoryPolicy->value,
|
||||
'slug' => "{$slugPrefix}-variant-".ProductVariant::query()->count(),
|
||||
'nombre' => ucfirst($slugPrefix).' Variant',
|
||||
'stock' => $stock,
|
||||
@@ -211,21 +279,21 @@ class TelepagosWebhookTest extends TestCase
|
||||
|
||||
private function createTenant(string $codigo, string $nombre, string $dominio): Tenant
|
||||
{
|
||||
$hdrKey = (string) \Illuminate\Support\Str::uuid();
|
||||
$ftrKey = (string) \Illuminate\Support\Str::uuid();
|
||||
$hdrKey = (string) Str::uuid();
|
||||
$ftrKey = (string) Str::uuid();
|
||||
|
||||
$headerAttachment = \App\Domains\Attachable\Models\Attachment::create([
|
||||
$headerAttachment = Attachment::create([
|
||||
'key' => $hdrKey,
|
||||
'path' => 'tenants/' . $hdrKey . '.png',
|
||||
'path' => 'tenants/'.$hdrKey.'.png',
|
||||
'filename' => 'logo_header.png',
|
||||
'type' => \App\Domains\Attachable\Enums\AttachmentType::Image,
|
||||
'type' => AttachmentType::Image,
|
||||
'mime_type' => 'image/png',
|
||||
]);
|
||||
$footerAttachment = \App\Domains\Attachable\Models\Attachment::create([
|
||||
$footerAttachment = Attachment::create([
|
||||
'key' => $ftrKey,
|
||||
'path' => 'tenants/' . $ftrKey . '.png',
|
||||
'path' => 'tenants/'.$ftrKey.'.png',
|
||||
'filename' => 'logo_footer.png',
|
||||
'type' => \App\Domains\Attachable\Enums\AttachmentType::Image,
|
||||
'type' => AttachmentType::Image,
|
||||
'mime_type' => 'image/png',
|
||||
]);
|
||||
|
||||
|
||||
@@ -2,14 +2,19 @@
|
||||
|
||||
namespace Tests\Feature\Purchase;
|
||||
|
||||
use App\Domains\Attachable\Enums\AttachmentType;
|
||||
use App\Domains\Attachable\Models\Attachment;
|
||||
use App\Domains\Auth\Models\User;
|
||||
use App\Domains\Cart\Models\Cart;
|
||||
use App\Domains\Catalog\Enums\InventoryPolicy;
|
||||
use App\Domains\Catalog\Models\Category;
|
||||
use App\Domains\Catalog\Models\Product;
|
||||
use App\Domains\Catalog\Models\ProductVariant;
|
||||
use App\Domains\Purchase\Models\Purchase;
|
||||
use App\Domains\Purchase\Services\CheckoutService;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Str;
|
||||
use Tests\TestCase;
|
||||
|
||||
class StorePurchaseTest extends TestCase
|
||||
@@ -22,7 +27,7 @@ class StorePurchaseTest extends TestCase
|
||||
$user = User::factory()->create([
|
||||
'email' => 'buyer@example.com',
|
||||
]);
|
||||
$category = \App\Domains\Catalog\Models\Category::query()->create([
|
||||
$category = Category::query()->create([
|
||||
'tenant_code' => 'sonder',
|
||||
'nombre' => 'Test Category',
|
||||
]);
|
||||
@@ -237,6 +242,13 @@ class StorePurchaseTest extends TestCase
|
||||
$checkoutService->confirmPurchase($purchase);
|
||||
$purchase->refresh()->markAsPaid();
|
||||
|
||||
$this->assertDatabaseHas('productos_variantes', [
|
||||
'id' => $variant->id,
|
||||
'stock_real' => 8,
|
||||
'stock_reservado' => 0,
|
||||
'cantidad_vendida' => 2,
|
||||
]);
|
||||
|
||||
$this->assertSoftDeleted('carritos', [
|
||||
'id' => $purchase->cart_id,
|
||||
]);
|
||||
@@ -401,18 +413,48 @@ class StorePurchaseTest extends TestCase
|
||||
->assertJsonValidationErrors(['cart_id']);
|
||||
}
|
||||
|
||||
public function test_it_confirms_unlimited_inventory_without_reducing_real_stock_and_is_idempotent(): void
|
||||
{
|
||||
$this->createTenant('sonder', 'Sonder', 'sonder.com.ar');
|
||||
$user = User::factory()->create();
|
||||
$variant = $this->createVariantForTenant(
|
||||
'sonder',
|
||||
0,
|
||||
'50.00',
|
||||
'unlimited',
|
||||
InventoryPolicy::Unlimited,
|
||||
);
|
||||
$purchase = $this->createCheckoutPurchase($user, 'sonder', $variant, 25);
|
||||
$purchase->update(['payment_method' => 'transfer']);
|
||||
|
||||
$checkoutService = app(CheckoutService::class);
|
||||
$purchase = $checkoutService->completePurchase($purchase);
|
||||
$checkoutService->confirmPurchase($purchase);
|
||||
$checkoutService->confirmPurchase($purchase);
|
||||
|
||||
$this->assertDatabaseHas('productos_variantes', [
|
||||
'id' => $variant->id,
|
||||
'inventory_policy' => InventoryPolicy::Unlimited->value,
|
||||
'stock_real' => 0,
|
||||
'stock_reservado' => 0,
|
||||
'cantidad_vendida' => 25,
|
||||
]);
|
||||
$this->assertDatabaseCount('compra_items', 1);
|
||||
}
|
||||
|
||||
protected function createVariantForTenant(
|
||||
string $tenantCode,
|
||||
int $stock,
|
||||
string $price,
|
||||
string $slugPrefix = 'shirt',
|
||||
InventoryPolicy $inventoryPolicy = InventoryPolicy::Tracked,
|
||||
): ProductVariant {
|
||||
$tenant = Tenant::query()->where('codigo', $tenantCode)->first();
|
||||
if (! $tenant) {
|
||||
$this->createTenant($tenantCode, ucfirst($tenantCode), "{$tenantCode}.com");
|
||||
}
|
||||
|
||||
$category = \App\Domains\Catalog\Models\Category::query()->create([
|
||||
$category = Category::query()->create([
|
||||
'tenant_code' => $tenantCode,
|
||||
'nombre' => "{$slugPrefix} category {$tenantCode}",
|
||||
]);
|
||||
@@ -428,6 +470,7 @@ class StorePurchaseTest extends TestCase
|
||||
|
||||
return ProductVariant::query()->create([
|
||||
'producto_id' => $product->id,
|
||||
'inventory_policy' => $inventoryPolicy->value,
|
||||
'slug' => "{$slugPrefix}-variant-".ProductVariant::query()->count(),
|
||||
'nombre' => ucfirst($slugPrefix).' Variant',
|
||||
'stock' => $stock,
|
||||
@@ -462,21 +505,21 @@ class StorePurchaseTest extends TestCase
|
||||
|
||||
protected function createTenant(string $codigo, string $nombre, string $dominio): Tenant
|
||||
{
|
||||
$hdrKey = (string) \Illuminate\Support\Str::uuid();
|
||||
$ftrKey = (string) \Illuminate\Support\Str::uuid();
|
||||
$hdrKey = (string) Str::uuid();
|
||||
$ftrKey = (string) Str::uuid();
|
||||
|
||||
$headerAttachment = \App\Domains\Attachable\Models\Attachment::create([
|
||||
$headerAttachment = Attachment::create([
|
||||
'key' => $hdrKey,
|
||||
'path' => 'tenants/' . $hdrKey . '.png',
|
||||
'path' => 'tenants/'.$hdrKey.'.png',
|
||||
'filename' => 'logo_header.png',
|
||||
'type' => \App\Domains\Attachable\Enums\AttachmentType::Image,
|
||||
'type' => AttachmentType::Image,
|
||||
'mime_type' => 'image/png',
|
||||
]);
|
||||
$footerAttachment = \App\Domains\Attachable\Models\Attachment::create([
|
||||
$footerAttachment = Attachment::create([
|
||||
'key' => $ftrKey,
|
||||
'path' => 'tenants/' . $ftrKey . '.png',
|
||||
'path' => 'tenants/'.$ftrKey.'.png',
|
||||
'filename' => 'logo_footer.png',
|
||||
'type' => \App\Domains\Attachable\Enums\AttachmentType::Image,
|
||||
'type' => AttachmentType::Image,
|
||||
'mime_type' => 'image/png',
|
||||
]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user