Files
objecttothis 9ecabf6f41 fix(sales): harden unsuspend with auth, status gating, and null safety
- Require reports_sales grant on postUnsuspend; return 403 on denial
- Reject unsuspend of non-SUSPENDED sales; skip silently on invalid state
- Move clear_all() after validation so an invalid sale_id no longer wipes
  the active in-progress cart
- Null-guard get_sale_status() on missing row instead of fatal property
  access; widen return type to ?int
- Fix getSaleType null-coalescing — CI4 session default only fires when
  key is unset, not when value is null
- Rename get_sale_type → getSaleType, sale_id → saleId (PSR-12 camelCase)
- Extract SaleFixtureTrait with createSale()/createSuspendedSale(); add
  regression coverage for auth denial, status gating, and cart preservation
2026-09-07 12:18:11 +04:00

66 lines
1.9 KiB
PHP

<?php
namespace Tests\Support;
use App\Models\Sale;
use Config\Database;
trait SaleFixtureTrait
{
/**
* Inserts a minimal completed sale row directly, bypassing Sale::save_value()
* (which requires a full cart/inventory/tax pipeline unrelated to authorization
* checks). Sale::get_info() inner-joins sales_items, so a matching item/
* sales_items row is required for the sale to be found.
*/
protected function createSale(int $employeeId): int
{
$unique = uniqid();
$db = Database::connect();
$db->table('items')->insert([
'name' => "Test Item $unique",
'category' => 'Test',
'description' => 'Test item',
'cost_price' => 1,
'unit_price' => 1,
'item_number' => "TEST-$unique",
]);
$itemId = (int) $db->insertID();
$db->table('sales')->insert([
'sale_time' => date('Y-m-d H:i:s'),
'customer_id' => null,
'employee_id' => $employeeId,
'comment' => 'test sale',
'invoice_number' => null,
]);
$saleId = (int) $db->insertID();
$db->table('sales_items')->insert([
'sale_id' => $saleId,
'item_id' => $itemId,
'line' => 1,
'quantity_purchased' => 1,
'item_cost_price' => 1,
'item_unit_price' => 1,
'item_location' => 1,
]);
return $saleId;
}
/**
* Same as createSale(), but flips the sale to SUSPENDED afterward so
* postUnsuspend's status check can be exercised.
*/
protected function createSuspendedSale(int $employeeId): int
{
$saleId = $this->createSale($employeeId);
model(Sale::class)->update_sale_status($saleId, SUSPENDED);
return $saleId;
}
}