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:
2026-07-16 08:36:59 -03:00
parent 716d8e447c
commit 10157d7c63
16 changed files with 547 additions and 73 deletions

View File

@@ -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',
]);