feat(purchase,sale,ticket): expose refunded total summary in admin sales and tickets
This commit is contained in:
parent
4bb4f526e4
commit
4f7ede1072
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Purchase\Services;
|
||||||
|
|
||||||
|
use App\Domains\Purchase\Models\PurchaseItem;
|
||||||
|
use App\Domains\Tenant\Models\Tenant;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
|
||||||
|
class PurchaseRefundSummaryService
|
||||||
|
{
|
||||||
|
public function totalForTenant(Tenant $tenant): string
|
||||||
|
{
|
||||||
|
$total = PurchaseItem::query()
|
||||||
|
->whereHas(
|
||||||
|
'purchase',
|
||||||
|
fn (Builder $query): Builder => $query->where('tenant_codigo', $tenant->codigo)
|
||||||
|
)
|
||||||
|
->sum('refunded_amount');
|
||||||
|
|
||||||
|
return number_format((float) $total, 2, '.', '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -35,6 +35,7 @@ class SaleController extends Controller
|
||||||
$this->saleService->sales($tenant, $request->validated())
|
$this->saleService->sales($tenant, $request->validated())
|
||||||
)->additional([
|
)->additional([
|
||||||
'confirmed_sales_total' => $this->saleService->confirmedSalesTotal($tenant),
|
'confirmed_sales_total' => $this->saleService->confirmedSalesTotal($tenant),
|
||||||
|
'refunded_total' => $this->saleService->refundedTotal($tenant),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ namespace App\Domains\Sale\Services;
|
||||||
use App\Domains\Logging\Models\ValueChange;
|
use App\Domains\Logging\Models\ValueChange;
|
||||||
use App\Domains\Purchase\Models\Purchase;
|
use App\Domains\Purchase\Models\Purchase;
|
||||||
use App\Domains\Purchase\Services\CheckoutService;
|
use App\Domains\Purchase\Services\CheckoutService;
|
||||||
|
use App\Domains\Purchase\Services\PurchaseRefundSummaryService;
|
||||||
use App\Domains\Tenant\Models\Tenant;
|
use App\Domains\Tenant\Models\Tenant;
|
||||||
use App\Domains\Ticket\Models\Ticket;
|
use App\Domains\Ticket\Models\Ticket;
|
||||||
use App\Domains\Ticket\Services\TicketPresentationResolver;
|
use App\Domains\Ticket\Services\TicketPresentationResolver;
|
||||||
|
|
@ -17,6 +18,7 @@ class AdminAppSaleService
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
protected CheckoutService $checkoutService,
|
protected CheckoutService $checkoutService,
|
||||||
|
protected PurchaseRefundSummaryService $refundSummaryService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function confirmedSalesTotal(Tenant $tenant): string
|
public function confirmedSalesTotal(Tenant $tenant): string
|
||||||
|
|
@ -29,6 +31,11 @@ class AdminAppSaleService
|
||||||
return number_format((float) $total, 2, '.', '');
|
return number_format((float) $total, 2, '.', '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function refundedTotal(Tenant $tenant): string
|
||||||
|
{
|
||||||
|
return $this->refundSummaryService->totalForTenant($tenant);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array{
|
* @param array{
|
||||||
* q?: string|null,
|
* q?: string|null,
|
||||||
|
|
|
||||||
|
|
@ -15,20 +15,24 @@ class AdminAppTicketCollection extends ResourceCollection
|
||||||
|
|
||||||
private readonly int $totalTickets;
|
private readonly int $totalTickets;
|
||||||
|
|
||||||
|
private readonly string $refundedTotal;
|
||||||
|
|
||||||
public function __construct(AdminAppTicketResult $result)
|
public function __construct(AdminAppTicketResult $result)
|
||||||
{
|
{
|
||||||
parent::__construct($result->tickets);
|
parent::__construct($result->tickets);
|
||||||
|
|
||||||
$this->scannedTickets = $result->scannedTickets;
|
$this->scannedTickets = $result->scannedTickets;
|
||||||
$this->totalTickets = $result->totalTickets;
|
$this->totalTickets = $result->totalTickets;
|
||||||
|
$this->refundedTotal = $result->refundedTotal;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return array{scanned_tickets: int, total_tickets: int} */
|
/** @return array{scanned_tickets: int, total_tickets: int, refunded_total: string} */
|
||||||
public function with(Request $request): array
|
public function with(Request $request): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'scanned_tickets' => $this->scannedTickets,
|
'scanned_tickets' => $this->scannedTickets,
|
||||||
'total_tickets' => $this->totalTickets,
|
'total_tickets' => $this->totalTickets,
|
||||||
|
'refunded_total' => $this->refundedTotal,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,5 +12,6 @@ final readonly class AdminAppTicketResult
|
||||||
public LengthAwarePaginator $tickets,
|
public LengthAwarePaginator $tickets,
|
||||||
public int $scannedTickets,
|
public int $scannedTickets,
|
||||||
public int $totalTickets,
|
public int $totalTickets,
|
||||||
|
public string $refundedTotal,
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ namespace App\Domains\Ticket\Services;
|
||||||
|
|
||||||
use App\Domains\Auth\Models\User;
|
use App\Domains\Auth\Models\User;
|
||||||
use App\Domains\Purchase\Models\PurchaseItem;
|
use App\Domains\Purchase\Models\PurchaseItem;
|
||||||
|
use App\Domains\Purchase\Services\PurchaseRefundSummaryService;
|
||||||
use App\Domains\Tenant\Models\Tenant;
|
use App\Domains\Tenant\Models\Tenant;
|
||||||
use App\Domains\Ticket\Models\Ticket;
|
use App\Domains\Ticket\Models\Ticket;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
|
@ -27,6 +28,7 @@ class AdminAppTicketService
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly AdminAppTicketColumnService $columnService,
|
private readonly AdminAppTicketColumnService $columnService,
|
||||||
private readonly AdminAppTicketRowService $rowService,
|
private readonly AdminAppTicketRowService $rowService,
|
||||||
|
private readonly PurchaseRefundSummaryService $refundSummaryService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -68,6 +70,7 @@ class AdminAppTicketService
|
||||||
tickets: $tickets,
|
tickets: $tickets,
|
||||||
scannedTickets: $scannedTickets,
|
scannedTickets: $scannedTickets,
|
||||||
totalTickets: $totalTickets,
|
totalTickets: $totalTickets,
|
||||||
|
refundedTotal: $this->refundSummaryService->totalForTenant($tenant),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -163,6 +163,7 @@ class AdminAppSaleControllerTest extends TestCase
|
||||||
'cantidad' => 3,
|
'cantidad' => 3,
|
||||||
'precio_unitario' => '10000.00',
|
'precio_unitario' => '10000.00',
|
||||||
'total' => '30000.00',
|
'total' => '30000.00',
|
||||||
|
'refunded_amount' => '1250.00',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$pendingCart = Cart::query()->create([
|
$pendingCart = Cart::query()->create([
|
||||||
|
|
@ -203,6 +204,7 @@ class AdminAppSaleControllerTest extends TestCase
|
||||||
'cantidad' => 2,
|
'cantidad' => 2,
|
||||||
'precio_unitario' => '10000.00',
|
'precio_unitario' => '10000.00',
|
||||||
'total' => '20000.00',
|
'total' => '20000.00',
|
||||||
|
'refunded_amount' => '2500.00',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$supersededPurchase = Purchase::query()->create([
|
$supersededPurchase = Purchase::query()->create([
|
||||||
|
|
@ -237,7 +239,8 @@ class AdminAppSaleControllerTest extends TestCase
|
||||||
->assertJsonPath('data.2.status_label', 'Confirmado')
|
->assertJsonPath('data.2.status_label', 'Confirmado')
|
||||||
->assertJsonPath('data.3.id', $supersededPurchase->id)
|
->assertJsonPath('data.3.id', $supersededPurchase->id)
|
||||||
->assertJsonPath('data.3.admin_status', Purchase::ADMIN_STATUS_CANCELLED)
|
->assertJsonPath('data.3.admin_status', Purchase::ADMIN_STATUS_CANCELLED)
|
||||||
->assertJsonPath('data.3.status_label', 'Anulado');
|
->assertJsonPath('data.3.status_label', 'Anulado')
|
||||||
|
->assertJsonPath('refunded_total', '3750.00');
|
||||||
|
|
||||||
$this->getJson('/api/v1/adminapp/tenant/sales?status='.Purchase::STATUS_SUPERSEDED)
|
$this->getJson('/api/v1/adminapp/tenant/sales?status='.Purchase::STATUS_SUPERSEDED)
|
||||||
->assertUnprocessable();
|
->assertUnprocessable();
|
||||||
|
|
|
||||||
|
|
@ -541,17 +541,56 @@ class AdminAppTicketControllerTest extends TestCase
|
||||||
$this->createTicket($tenant, $admin)->update(['disabled_at' => now()]);
|
$this->createTicket($tenant, $admin)->update(['disabled_at' => now()]);
|
||||||
$this->createTicket($otherTenant, $otherUser)->update(['used_at' => now()]);
|
$this->createTicket($otherTenant, $otherUser)->update(['used_at' => now()]);
|
||||||
|
|
||||||
|
$catalogItem = CatalogItem::query()->create([
|
||||||
|
'tenant_code' => $tenant->codigo,
|
||||||
|
'slug' => 'refund-summary-item',
|
||||||
|
'nombre' => 'Entrada',
|
||||||
|
'precio' => '1000.00',
|
||||||
|
]);
|
||||||
|
$purchase = Purchase::query()->create([
|
||||||
|
'tenant_codigo' => $tenant->codigo,
|
||||||
|
'status' => Purchase::STATUS_PAID,
|
||||||
|
'total' => '1000.00',
|
||||||
|
]);
|
||||||
|
PurchaseItem::query()->create([
|
||||||
|
'compra_id' => $purchase->id,
|
||||||
|
'source_catalog_item_id' => $catalogItem->id,
|
||||||
|
'nombre' => 'Entrada',
|
||||||
|
'item_nombre' => 'Entrada',
|
||||||
|
'cantidad' => 1,
|
||||||
|
'precio_unitario' => '1000.00',
|
||||||
|
'total' => '1000.00',
|
||||||
|
'refunded_amount' => '250.00',
|
||||||
|
]);
|
||||||
|
$otherPurchase = Purchase::query()->create([
|
||||||
|
'tenant_codigo' => $otherTenant->codigo,
|
||||||
|
'status' => Purchase::STATUS_PAID,
|
||||||
|
'total' => '2000.00',
|
||||||
|
]);
|
||||||
|
PurchaseItem::query()->create([
|
||||||
|
'compra_id' => $otherPurchase->id,
|
||||||
|
'source_catalog_item_id' => $catalogItem->id,
|
||||||
|
'nombre' => 'Otra entrada',
|
||||||
|
'item_nombre' => 'Otra entrada',
|
||||||
|
'cantidad' => 1,
|
||||||
|
'precio_unitario' => '2000.00',
|
||||||
|
'total' => '2000.00',
|
||||||
|
'refunded_amount' => '2000.00',
|
||||||
|
]);
|
||||||
|
|
||||||
$this->getJson('/api/v1/adminapp/tenant/tickets?q=does-not-match')
|
$this->getJson('/api/v1/adminapp/tenant/tickets?q=does-not-match')
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertJsonCount(0, 'data')
|
->assertJsonCount(0, 'data')
|
||||||
->assertJsonPath('scanned_tickets', 0)
|
->assertJsonPath('scanned_tickets', 0)
|
||||||
->assertJsonPath('total_tickets', 0);
|
->assertJsonPath('total_tickets', 0)
|
||||||
|
->assertJsonPath('refunded_total', '250.00');
|
||||||
|
|
||||||
$this->getJson('/api/v1/adminapp/tenant/tickets')
|
$this->getJson('/api/v1/adminapp/tenant/tickets')
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertJsonCount(5, 'data')
|
->assertJsonCount(5, 'data')
|
||||||
->assertJsonPath('scanned_tickets', 1)
|
->assertJsonPath('scanned_tickets', 1)
|
||||||
->assertJsonPath('total_tickets', 2);
|
->assertJsonPath('total_tickets', 2)
|
||||||
|
->assertJsonPath('refunded_total', '250.00');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_it_returns_structured_variant_properties(): void
|
public function test_it_returns_structured_variant_properties(): void
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue