feat(sales): filter and serialize admin statuses

This commit is contained in:
ncoronel 2026-09-02 12:42:11 -03:00
parent f74bf41931
commit dcf105c316
6 changed files with 88 additions and 14 deletions

View File

@ -20,7 +20,7 @@ class AdminAppSaleIndexRequest extends FormRequest
'q' => ['sometimes', 'nullable', 'string', 'max:255'],
'id' => ['sometimes', 'nullable', 'integer', 'min:1'],
'sale_date' => ['sometimes', 'nullable', 'date_format:Y-m-d'],
'status' => ['sometimes', 'nullable', 'string', Rule::in(Purchase::statuses())],
'status' => ['sometimes', 'nullable', 'string', Rule::in(Purchase::adminStatusCodes())],
'sort_by' => ['sometimes', 'string', 'in:id,date,customer_name,quantity,status,total'],
'sort_direction' => ['sometimes', 'string', 'in:asc,desc'],
'page' => ['sometimes', 'integer', 'min:1'],

View File

@ -23,6 +23,12 @@ class SaleModificationResource extends JsonResource
'attribute' => $this->attribute,
'old_value' => $this->old_value,
'new_value' => $this->new_value,
'admin_status' => is_string($this->new_value)
? Purchase::adminStatusFor($this->new_value)
: null,
'status_label' => is_string($this->new_value)
? Purchase::adminStatusNameFor($this->new_value)
: null,
'changed_at' => $this->changed_at->utc()->toIso8601String(),
'date' => $this->changed_at->format('Y-m-d'),
'time' => $this->changed_at->format('H:i:s'),
@ -31,6 +37,8 @@ class SaleModificationResource extends JsonResource
'id' => $sale->id,
'customer_name' => $sale->nombre_apellido,
'status' => $sale->status,
'admin_status' => Purchase::adminStatusFor($sale->status),
'status_label' => Purchase::adminStatusNameFor($sale->status),
] : null,
'modified_by' => $user ? [
'id' => $user->id,

View File

@ -20,6 +20,8 @@ class SaleResource extends JsonResource
'customer_name' => $this->nombre_apellido,
'quantity' => (int) ($this->quantity ?? 0),
'status' => $this->status,
'admin_status' => Purchase::adminStatusFor($this->status),
'status_label' => Purchase::adminStatusNameFor($this->status),
'total' => number_format((float) $this->total, 2, '.', ''),
'tickets_count' => $ticketsCount,
'has_generated_tickets' => $ticketsCount > 0,

View File

@ -126,7 +126,6 @@ class AdminAppSaleService
return Purchase::query()
->where('tenant_codigo', $tenant->codigo)
->where('status', '!=', Purchase::STATUS_SUPERSEDED)
->when($filters['q'] ?? null, function (Builder $query, string $search): void {
$term = trim($search);
@ -144,12 +143,10 @@ class AdminAppSaleService
)
->when(
$filters['status'] ?? null,
fn (Builder $query, string $status): Builder => $status === Purchase::STATUS_PENDING_PAYMENT
? $query->whereIn('status', [
Purchase::STATUS_PENDING_PAYMENT,
Purchase::STATUS_IN_REVIEW,
])
: $query->where('status', $status)
fn (Builder $query, string $status): Builder => $query->whereIn(
'status',
Purchase::realStatusesForAdminStatus($status),
)
)
->select('compras.*')
->selectRaw(

View File

@ -2,6 +2,8 @@
namespace Tests\Feature\Sale;
use App\Domains\Attachable\Enums\AttachmentType;
use App\Domains\Attachable\Models\Attachment;
use App\Domains\Auth\Models\User;
use App\Domains\Authorization\Enums\RoleCode;
use App\Domains\Cart\Models\Cart;
@ -64,6 +66,7 @@ class AdminAppSaleControllerTest extends TestCase
PurchaseItem::query()->create([
'compra_id' => $createdPurchase->id,
'source_catalog_item_id' => $catalogItem->id,
'nombre' => $catalogItem->nombre,
'item_nombre' => $catalogItem->nombre,
'cantidad' => 3,
'precio_unitario' => '10000.00',
@ -88,6 +91,7 @@ class AdminAppSaleControllerTest extends TestCase
PurchaseItem::query()->create([
'compra_id' => $pendingPurchase->id,
'source_catalog_item_id' => $catalogItem->id,
'nombre' => $catalogItem->nombre,
'item_nombre' => $catalogItem->nombre,
'cantidad' => 4,
'precio_unitario' => '10000.00',
@ -102,6 +106,7 @@ class AdminAppSaleControllerTest extends TestCase
PurchaseItem::query()->create([
'compra_id' => $paidPurchase->id,
'source_catalog_item_id' => $catalogItem->id,
'nombre' => $catalogItem->nombre,
'item_nombre' => $catalogItem->nombre,
'cantidad' => 2,
'precio_unitario' => '10000.00',
@ -116,6 +121,7 @@ class AdminAppSaleControllerTest extends TestCase
PurchaseItem::query()->create([
'compra_id' => $supersededPurchase->id,
'source_catalog_item_id' => $catalogItem->id,
'nombre' => $catalogItem->nombre,
'item_nombre' => $catalogItem->nombre,
'cantidad' => 5,
'precio_unitario' => '10000.00',
@ -124,17 +130,25 @@ class AdminAppSaleControllerTest extends TestCase
$this->getJson('/api/v1/adminapp/tenant/sales?sort_by=id&sort_direction=asc')
->assertOk()
->assertJsonCount(3, 'data')
->assertJsonCount(4, 'data')
->assertJsonPath('data.0.id', $createdPurchase->id)
->assertJsonPath('data.0.quantity', 3)
->assertJsonPath('data.0.admin_status', Purchase::ADMIN_STATUS_INCOMPLETE)
->assertJsonPath('data.0.status_label', 'Por completar datos')
->assertJsonPath('data.1.id', $pendingPurchase->id)
->assertJsonPath('data.1.quantity', 4)
->assertJsonPath('data.1.admin_status', Purchase::ADMIN_STATUS_AWAITING_PAYMENT)
->assertJsonPath('data.1.status_label', 'Esperando pago')
->assertJsonPath('data.2.id', $paidPurchase->id)
->assertJsonPath('data.2.quantity', 2);
->assertJsonPath('data.2.quantity', 2)
->assertJsonPath('data.2.admin_status', Purchase::ADMIN_STATUS_CONFIRMED)
->assertJsonPath('data.2.status_label', 'Confirmado')
->assertJsonPath('data.3.id', $supersededPurchase->id)
->assertJsonPath('data.3.admin_status', Purchase::ADMIN_STATUS_CANCELLED)
->assertJsonPath('data.3.status_label', 'Anulado');
$this->getJson('/api/v1/adminapp/tenant/sales?status='.Purchase::STATUS_SUPERSEDED)
->assertOk()
->assertJsonCount(0, 'data');
->assertUnprocessable();
}
public function test_authentication_is_required_to_read_a_sale_detail(): void
@ -142,7 +156,7 @@ class AdminAppSaleControllerTest extends TestCase
$this->getJson('/api/v1/adminapp/tenant/sales/1')->assertUnauthorized();
}
public function test_pending_payment_filter_also_returns_purchases_in_review(): void
public function test_awaiting_payment_filter_returns_every_mapped_real_status(): void
{
$tenant = $this->createTenant('acme');
Sanctum::actingAs($this->createAdminAppUser($tenant));
@ -160,13 +174,41 @@ class AdminAppSaleControllerTest extends TestCase
'status' => Purchase::STATUS_PAID,
]);
$this->getJson('/api/v1/adminapp/tenant/sales?status=pending_payment&sort_by=id&sort_direction=asc')
$this->getJson('/api/v1/adminapp/tenant/sales?status=awaiting_payment&sort_by=id&sort_direction=asc')
->assertOk()
->assertJsonCount(2, 'data')
->assertJsonPath('data.0.id', $pendingPurchase->id)
->assertJsonPath('data.1.id', $reviewPurchase->id);
}
public function test_cancelled_filter_returns_every_visible_mapped_real_status(): void
{
$tenant = $this->createTenant('acme');
Sanctum::actingAs($this->createAdminAppUser($tenant));
foreach ([
Purchase::STATUS_CANCELLED,
Purchase::STATUS_REJECTED,
Purchase::STATUS_EXPIRED,
Purchase::STATUS_SUPERSEDED,
] as $status) {
Purchase::query()->create([
'tenant_codigo' => $tenant->codigo,
'status' => $status,
]);
}
Purchase::query()->create([
'tenant_codigo' => $tenant->codigo,
'status' => Purchase::STATUS_PAID,
]);
$this->getJson('/api/v1/adminapp/tenant/sales?status=cancelled')
->assertOk()
->assertJsonCount(4, 'data')
->assertJsonPath('data.0.admin_status', Purchase::ADMIN_STATUS_CANCELLED)
->assertJsonPath('data.0.status_label', 'Anulado');
}
public function test_an_adminapp_user_can_read_a_sale_detail_from_its_tenant(): void
{
$tenant = $this->createTenant('acme');
@ -233,6 +275,7 @@ class AdminAppSaleControllerTest extends TestCase
$purchaseItem = PurchaseItem::query()->create([
'compra_id' => $purchase->id,
'source_catalog_item_id' => $catalogItem->id,
'nombre' => $catalogItem->nombre,
'item_nombre' => $catalogItem->nombre,
'cantidad' => 2,
'precio_unitario' => '12500.00',
@ -479,6 +522,7 @@ class AdminAppSaleControllerTest extends TestCase
PurchaseItem::query()->create([
'compra_id' => $purchase->id,
'source_catalog_item_id' => $catalogItem->id,
'nombre' => $catalogItem->nombre,
'item_nombre' => $catalogItem->nombre,
'cantidad' => 2,
'precio_unitario' => '10000.00',
@ -574,14 +618,34 @@ class AdminAppSaleControllerTest extends TestCase
private function createTenant(string $code): Tenant
{
$headerLogo = $this->createAttachment("{$code}-header");
$footerLogo = $this->createAttachment("{$code}-footer");
return Tenant::query()->create([
'codigo' => $code,
'nombre' => ucfirst($code),
'dominio' => "{$code}.test",
'primary_color' => '#000000',
'secondary_color' => '#ffffff',
'danger_color' => '#dc3545',
'header_bg_color' => '#ffffff',
'footer_bg_color' => '#000000',
'header_logo_id' => $headerLogo->id,
'footer_logo_id' => $footerLogo->id,
'website_type_code' => 'onticket',
]);
}
private function createAttachment(string $name): Attachment
{
return Attachment::query()->create([
'path' => "test/{$name}.png",
'filename' => "{$name}.png",
'type' => AttachmentType::Image,
'mime_type' => 'image/png',
]);
}
private function createAdminAppUser(Tenant $tenant): User
{
return User::factory()->create([

View File

@ -3,6 +3,7 @@
namespace Tests\Unit\Sale;
use App\Domains\Logging\Models\ValueChange;
use App\Domains\Purchase\Models\Purchase;
use App\Domains\Sale\Resources\AdminApp\SaleModificationResource;
use Carbon\CarbonImmutable;
use Illuminate\Http\Request;
@ -29,5 +30,7 @@ class SaleModificationResourceTest extends TestCase
$this->assertSame('2026-08-24T17:53:00+00:00', $data['changed_at']);
$this->assertSame('2026-08-24', $data['date']);
$this->assertSame('17:53:00', $data['time']);
$this->assertSame(Purchase::ADMIN_STATUS_CANCELLED, $data['admin_status']);
$this->assertSame('Anulado', $data['status_label']);
}
}