mirror of
https://github.com/opensourcepos/opensourcepos.git
synced 2026-09-14 14:26:59 -04:00
Merge branch 'master' into feature-optimize-items-view-queries
This commit is contained in:
6 files changed
+105
-50
No files matched your search
@@ -39,6 +39,7 @@ class OSPOS extends BaseConfig
|
||||
|
||||
try {
|
||||
$db = Database::connect();
|
||||
$db->resetDataCache();
|
||||
|
||||
if (!$db->tableExists('app_config')) {
|
||||
$this->settings = $this->getDefaultSettings();
|
||||
|
||||
+19
-13
@@ -137,20 +137,26 @@ class Sales extends Secure_Controller
|
||||
}
|
||||
|
||||
$saleInfo = $this->sale->get_info($rowId)->getRow();
|
||||
$dataRow = get_sale_data_row($saleInfo);
|
||||
$dataRow = getSaleDataRow($saleInfo);
|
||||
|
||||
return $this->response->setJSON($dataRow);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function getSearch(): ResponseInterface
|
||||
{
|
||||
$personId = $this->session->get('person_id');
|
||||
|
||||
if (!$this->employee->has_grant('reports_sales', $personId)) {
|
||||
return $this->response->setStatusCode(403)->setJSON(['success' => false, 'message' => lang('Sales.not_authorized')]);
|
||||
}
|
||||
|
||||
$search = $this->request->getGet('search', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
|
||||
$limit = $this->request->getGet('limit', FILTER_SANITIZE_NUMBER_INT);
|
||||
$offset = $this->request->getGet('offset', FILTER_SANITIZE_NUMBER_INT);
|
||||
$sort = $this->sanitizeSortColumn(sales_headers(), $this->request->getGet('sort', FILTER_SANITIZE_FULL_SPECIAL_CHARS), 'sale_id');
|
||||
$sort = $this->sanitizeSortColumn(salesHeaders(), $this->request->getGet('sort', FILTER_SANITIZE_FULL_SPECIAL_CHARS), 'sale_id');
|
||||
$order = $this->request->getGet('order', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
|
||||
|
||||
$filters = [
|
||||
@@ -171,24 +177,24 @@ class Sales extends Secure_Controller
|
||||
];
|
||||
|
||||
// Check if any filter is set in the multiselect dropdown
|
||||
$request_filters = array_fill_keys($this->request->getGet('filters', FILTER_SANITIZE_FULL_SPECIAL_CHARS) ?? [], true);
|
||||
$filters = array_merge($filters, $request_filters);
|
||||
$requestFilters = array_fill_keys($this->request->getGet('filters', FILTER_SANITIZE_FULL_SPECIAL_CHARS) ?? [], true);
|
||||
$filters = array_merge($filters, $requestFilters);
|
||||
|
||||
$sales = $this->sale->search($search, $filters, $limit, $offset, $sort, $order);
|
||||
$total_rows = $this->sale->get_found_rows($search, $filters);
|
||||
$payments = $this->sale->get_payments_summary($search, $filters);
|
||||
$payment_summary = get_sales_manage_payments_summary($payments);
|
||||
$totalRows = $this->sale->get_found_rows($search, $filters);
|
||||
$payments = $this->sale->getPaymentsSummary($search, $filters);
|
||||
$paymentSummary = getSalesManagePaymentsSummary($payments);
|
||||
|
||||
$data_rows = [];
|
||||
$dataRows = [];
|
||||
foreach ($sales->getResult() as $sale) {
|
||||
$data_rows[] = get_sale_data_row($sale);
|
||||
$dataRows[] = getSaleDataRow($sale);
|
||||
}
|
||||
|
||||
if ($total_rows > 0) {
|
||||
$data_rows[] = get_sale_data_last_row($sales);
|
||||
if ($totalRows > 0) {
|
||||
$dataRows[] = getSaleDataLastRow($sales);
|
||||
}
|
||||
|
||||
return $this->response->setJSON(['total' => $total_rows, 'rows' => $data_rows, 'payment_summary' => $payment_summary]);
|
||||
return $this->response->setJSON(['total' => $totalRows, 'rows' => $dataRows, 'payment_summary' => $paymentSummary]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,7 +7,7 @@ use Config\Database;
|
||||
|
||||
class TestDatabaseBootstrapSeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
public static function reset(): void
|
||||
{
|
||||
if (ENVIRONMENT !== 'testing') {
|
||||
throw new \RuntimeException('TestDatabaseBootstrapSeeder can only run in the testing environment.');
|
||||
@@ -34,4 +34,9 @@ class TestDatabaseBootstrapSeeder extends Seeder
|
||||
$serverConn->query("DROP DATABASE IF EXISTS `{$dbName}`");
|
||||
$serverConn->query("CREATE DATABASE IF NOT EXISTS `{$dbName}`");
|
||||
}
|
||||
|
||||
public function run(): void
|
||||
{
|
||||
self::reset();
|
||||
}
|
||||
}
|
||||
@@ -59,7 +59,7 @@ function transform_headers(array $headers, bool $readonly = false, bool $editabl
|
||||
}
|
||||
|
||||
|
||||
function sales_headers(): array
|
||||
function salesHeaders(): array
|
||||
{
|
||||
return [
|
||||
['sale_id' => lang('Common.id')],
|
||||
@@ -77,7 +77,7 @@ function sales_headers(): array
|
||||
*/
|
||||
function get_sales_manage_table_headers(): string
|
||||
{
|
||||
$headers = sales_headers();
|
||||
$headers = salesHeaders();
|
||||
$config = config(OSPOS::class)->settings;
|
||||
|
||||
if ($config['invoice_enable']) {
|
||||
@@ -93,7 +93,7 @@ function get_sales_manage_table_headers(): string
|
||||
/**
|
||||
* Get the html data row for the sales
|
||||
*/
|
||||
function get_sale_data_row(object $sale): array
|
||||
function getSaleDataRow(object $sale): array
|
||||
{
|
||||
$uri = current_url(true);
|
||||
$controller = $uri->getSegment(1);
|
||||
@@ -143,7 +143,7 @@ function get_sale_data_row(object $sale): array
|
||||
/**
|
||||
* Get the html data last row for the sales
|
||||
*/
|
||||
function get_sale_data_last_row(ResultInterface $sales): array
|
||||
function getSaleDataLastRow(ResultInterface $sales): array
|
||||
{
|
||||
$sum_amount_due = 0;
|
||||
$sum_amount_tendered = 0;
|
||||
@@ -167,7 +167,7 @@ function get_sale_data_last_row(ResultInterface $sales): array
|
||||
/**
|
||||
* Get the sales payments summary
|
||||
*/
|
||||
function get_sales_manage_payments_summary(array $payments): string
|
||||
function getSalesManagePaymentsSummary(array $payments): string
|
||||
{
|
||||
$table = '<div id="report_summary">';
|
||||
$total = 0;
|
||||
|
||||
+1
-1
@@ -210,7 +210,7 @@ class Sale extends Model
|
||||
/**
|
||||
* Get the payment summary for the takings (sales/manage) view
|
||||
*/
|
||||
public function get_payments_summary(?string $search, array $filters): array
|
||||
public function getPaymentsSummary(?string $search, array $filters): array
|
||||
{
|
||||
$config = config(OSPOS::class)->settings;
|
||||
|
||||
|
||||
@@ -6,8 +6,10 @@ use CodeIgniter\Test\CIUnitTestCase;
|
||||
use CodeIgniter\Test\DatabaseTestTrait;
|
||||
use CodeIgniter\Test\FeatureTestTrait;
|
||||
use CodeIgniter\Config\Services;
|
||||
use App\Database\Seeds\TestDatabaseBootstrapSeeder;
|
||||
use App\Models\Employee;
|
||||
use App\Models\Item;
|
||||
use Config\OSPOS;
|
||||
use Tests\Support\ItemFixtureTrait;
|
||||
|
||||
/**
|
||||
* Regression tests for GHSA-3xf6-8fmq-44wg.
|
||||
@@ -21,12 +23,34 @@ class SalesControllerTest extends CIUnitTestCase
|
||||
{
|
||||
use DatabaseTestTrait;
|
||||
use FeatureTestTrait;
|
||||
use ItemFixtureTrait;
|
||||
|
||||
protected $migrate = true;
|
||||
protected $migrateOnce = true;
|
||||
protected $seedOnce = true;
|
||||
protected $refresh = false;
|
||||
protected $namespace = null;
|
||||
|
||||
private static bool $doneBootstrap = false;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
if (self::$doneBootstrap === false) {
|
||||
TestDatabaseBootstrapSeeder::reset();
|
||||
|
||||
self::$doneBootstrap = true;
|
||||
}
|
||||
|
||||
parent::setUp();
|
||||
|
||||
config(OSPOS::class)->update_settings();
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
protected function createCashierEmployee(): int
|
||||
{
|
||||
$unique = uniqid();
|
||||
@@ -35,7 +59,14 @@ class SalesControllerTest extends CIUnitTestCase
|
||||
'first_name' => 'Cashier',
|
||||
'last_name' => 'NoReports',
|
||||
'email' => "cashier.$unique@test.com",
|
||||
'phone_number' => '555-0001'
|
||||
'phone_number' => '555-0001',
|
||||
'address_1' => '',
|
||||
'address_2' => '',
|
||||
'city' => '',
|
||||
'state' => '',
|
||||
'zip' => '',
|
||||
'country' => '',
|
||||
'comments' => '',
|
||||
];
|
||||
|
||||
$employeeData = [
|
||||
@@ -69,7 +100,14 @@ class SalesControllerTest extends CIUnitTestCase
|
||||
'first_name' => 'Supervisor',
|
||||
'last_name' => 'WithReports',
|
||||
'email' => "supervisor.$unique@test.com",
|
||||
'phone_number' => '555-0002'
|
||||
'phone_number' => '555-0002',
|
||||
'address_1' => '',
|
||||
'address_2' => '',
|
||||
'city' => '',
|
||||
'state' => '',
|
||||
'zip' => '',
|
||||
'country' => '',
|
||||
'comments' => '',
|
||||
];
|
||||
|
||||
$employeeData = [
|
||||
@@ -220,30 +258,6 @@ class SalesControllerTest extends CIUnitTestCase
|
||||
return $saleId;
|
||||
}
|
||||
|
||||
protected function createTestItem(): int
|
||||
{
|
||||
$itemData = [
|
||||
'item_id' => null,
|
||||
'name' => 'Test Item',
|
||||
'description' => 'Test Item',
|
||||
'category' => 'Test Category',
|
||||
'cost_price' => 1.00,
|
||||
'unit_price' => 5.00,
|
||||
'reorder_level' => 0,
|
||||
'item_number' => 'TEST-' . uniqid(),
|
||||
'allow_alt_description' => 0,
|
||||
'is_serialized' => 0,
|
||||
'stock_type' => HAS_NO_STOCK,
|
||||
'deleted' => 0,
|
||||
];
|
||||
|
||||
$itemModel = model(Item::class);
|
||||
$itemModel->save_value($itemData);
|
||||
$this->assertTrue($itemModel->save_value($itemData));
|
||||
|
||||
return (int) $itemData['item_id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Seeds a single cart line directly in the session, mirroring the shape
|
||||
* Sale_lib::add_item() produces, so tests can target postEditItem's
|
||||
@@ -382,6 +396,35 @@ class SalesControllerTest extends CIUnitTestCase
|
||||
$this->assertSame(lang('Sales.not_authorized'), $result['message']);
|
||||
}
|
||||
|
||||
public function testCashierWithoutReportsSalesCannotGetSearch(): void
|
||||
{
|
||||
$cashierId = $this->createCashierEmployee();
|
||||
$this->createSale($cashierId);
|
||||
$this->loginAs($cashierId);
|
||||
|
||||
$response = $this->get('/sales/search');
|
||||
|
||||
$response->assertStatus(403);
|
||||
$result = json_decode($response->getJSON(), true);
|
||||
$this->assertFalse($result['success']);
|
||||
}
|
||||
|
||||
public function testEmployeeWithReportsSalesCanGetSearch(): void
|
||||
{
|
||||
$supervisorId = $this->createReportsSalesEmployee();
|
||||
$this->createSale($supervisorId);
|
||||
$this->loginAs($supervisorId);
|
||||
|
||||
$response = $this->get('/sales/search');
|
||||
|
||||
$response->assertStatus(200);
|
||||
$result = json_decode($response->getJSON(), true);
|
||||
$this->assertArrayNotHasKey('success', $result);
|
||||
$this->assertArrayHasKey('total', $result);
|
||||
$this->assertArrayHasKey('rows', $result);
|
||||
$this->assertArrayHasKey('payment_summary', $result);
|
||||
}
|
||||
|
||||
public function testEmployeeWithReportsSalesCanGetRow(): void
|
||||
{
|
||||
$supervisorId = $this->createReportsSalesEmployee();
|
||||
@@ -410,7 +453,7 @@ class SalesControllerTest extends CIUnitTestCase
|
||||
{
|
||||
$cashierId = $this->createCashierWithoutChangePriceGrant();
|
||||
$this->loginAs($cashierId);
|
||||
$itemId = $this->createTestItem();
|
||||
$itemId = $this->createTestItem(HAS_NO_STOCK);
|
||||
$this->seedCartLine(1, '5.00', $itemId);
|
||||
|
||||
$response = $this->post('/sales/editItem/1', [
|
||||
@@ -434,7 +477,7 @@ class SalesControllerTest extends CIUnitTestCase
|
||||
{
|
||||
$cashierId = $this->createCashierWithoutChangePriceGrant();
|
||||
$this->loginAs($cashierId);
|
||||
$itemId = $this->createTestItem();
|
||||
$itemId = $this->createTestItem(HAS_NO_STOCK);
|
||||
$this->seedCartLine(1, '5.00', $itemId);
|
||||
|
||||
$response = $this->post('/sales/editItem/1', [
|
||||
@@ -458,7 +501,7 @@ class SalesControllerTest extends CIUnitTestCase
|
||||
{
|
||||
$cashierId = $this->createCashierWithChangePriceGrant();
|
||||
$this->loginAs($cashierId);
|
||||
$itemId = $this->createTestItem();
|
||||
$itemId = $this->createTestItem(HAS_NO_STOCK);
|
||||
$this->seedCartLine(1, '5.00', $itemId);
|
||||
|
||||
$response = $this->post('/sales/editItem/1', [
|
||||
|
||||
Reference in new issue
Block a user