DBGroup)->call('App\Database\Seeds\TestDatabaseBootstrapSeeder'); Config::connect($this->DBGroup)->close(); self::$doneBootstrap = true; } parent::setUp(); } protected function tearDown(): void { parent::tearDown(); } protected function createGiftcard(float $value): int { $giftcardNumber = random_int(1000000, 9999999); \Config\Database::connect()->table('giftcards')->insert([ 'giftcard_number' => $giftcardNumber, 'value' => $value, 'deleted' => 0, 'person_id' => null, ]); return $giftcardNumber; } protected function createCustomerWithPoints(int $points): int { $db = \Config\Database::connect(); $db->table('people')->insert([ 'first_name' => 'Reward', 'last_name' => 'Customer', 'phone_number' => '555-0300', 'email' => 'customer-' . uniqid() . '@test.com', 'address_1' => '', 'address_2' => '', 'city' => '', 'state' => '', 'zip' => '', 'country' => '', 'comments' => '', ]); $personId = (int) $db->insertID(); $db->table('customers')->insert([ 'person_id' => $personId, 'employee_id' => 1, 'points' => $points, ]); return $personId; } protected function buildCartLine(int $itemId, float $quantity, float $price): array { return [ 0 => [ 'item_id' => $itemId, 'line' => 0, 'description' => 'Test Item', 'serialnumber' => '', 'quantity' => $quantity, 'discount' => 0, 'discount_type' => 0, 'cost_price' => 1.00, 'price' => $price, 'item_location' => self::LOCATION_ID, 'print_option' => 0, ], ]; } protected function countSalesRows(): int { return \Config\Database::connect()->table('sales')->countAllResults(); } protected function getGiftcardValue(int $giftcardNumber): float { $row = \Config\Database::connect()->table('giftcards') ->where('giftcard_number', $giftcardNumber) ->get() ->getRow(); return (float) $row->value; } protected function getCustomerPoints(int $personId): int { $row = \Config\Database::connect()->table('customers') ->where('person_id', $personId) ->get() ->getRow(); return (int) $row->points; } public function testSaveValueSucceedsWithSufficientGiftcardBalance(): void { $employeeId = $this->createEmployee(); $giftcardNumber = $this->createGiftcard(100.00); $itemId = $this->createTestItem(HAS_NO_STOCK); $saleModel = model(Sale::class); $saleStatus = COMPLETED; $items = $this->buildCartLine($itemId, 1, 60.00); $payments = [ 0 => [ 'payment_type' => lang('Sales.giftcard') . ':' . $giftcardNumber, 'payment_amount' => 60.00, 'cash_refund' => 0, 'cash_adjustment' => 0, 'reference_code' => null, ], ]; $salesTaxes = [[], []]; $result = $saleModel->save_value( NEW_ENTRY, $saleStatus, $items, NEW_ENTRY, $employeeId, 'test sale', null, null, null, SALE_TYPE_POS, $payments, null, $salesTaxes ); $this->assertGreaterThan(0, $result); $this->assertEqualsWithDelta(40.00, $this->getGiftcardValue($giftcardNumber), 0.001); } public function testSaveValueDoesNotMatchGiftcardSinkOnSubstringOnly(): void { $employeeId = $this->createEmployee(); $giftcardNumber = $this->createGiftcard(100.00); $itemId = $this->createTestItem(HAS_NO_STOCK); $saleModel = model(Sale::class); $saleStatus = COMPLETED; $items = $this->buildCartLine($itemId, 1, 60.00); $payments = [ 0 => [ 'payment_type' => 'Foo ' . lang('Sales.giftcard') . ':' . $giftcardNumber, 'payment_amount' => 60.00, 'cash_refund' => 0, 'cash_adjustment' => 0, 'reference_code' => null, ], ]; $salesTaxes = [[], []]; $result = $saleModel->save_value( NEW_ENTRY, $saleStatus, $items, NEW_ENTRY, $employeeId, 'test sale', null, null, null, SALE_TYPE_POS, $payments, null, $salesTaxes ); $this->assertGreaterThan(0, $result); $this->assertEqualsWithDelta(100.00, $this->getGiftcardValue($giftcardNumber), 0.001); } public function testSaveValueReturnsInsufficientGiftcardBalanceSentinel(): void { $employeeId = $this->createEmployee(); $giftcardNumber = $this->createGiftcard(50.00); $itemId = $this->createTestItem(HAS_NO_STOCK); $salesCountBefore = $this->countSalesRows(); $saleModel = model(Sale::class); $saleStatus = COMPLETED; $items = $this->buildCartLine($itemId, 1, 60.00); $payments = [ 0 => [ 'payment_type' => lang('Sales.giftcard') . ':' . $giftcardNumber, 'payment_amount' => 60.00, 'cash_refund' => 0, 'cash_adjustment' => 0, 'reference_code' => null, ], ]; $salesTaxes = [[], []]; $result = $saleModel->save_value( NEW_ENTRY, $saleStatus, $items, NEW_ENTRY, $employeeId, 'test sale', null, null, null, SALE_TYPE_POS, $payments, null, $salesTaxes ); $this->assertSame(INSUFFICIENT_GIFTCARD_BALANCE, $result); $this->assertEqualsWithDelta(50.00, $this->getGiftcardValue($giftcardNumber), 0.001); $this->assertSame($salesCountBefore, $this->countSalesRows()); } public function testSaveValueReturnsInsufficientRewardPointsSentinel(): void { $employeeId = $this->createEmployee(); $customerId = $this->createCustomerWithPoints(20); $itemId = $this->createTestItem(HAS_NO_STOCK); $salesCountBefore = $this->countSalesRows(); $saleModel = model(Sale::class); $saleStatus = COMPLETED; $items = $this->buildCartLine($itemId, 1, 60.00); $payments = [ 0 => [ 'payment_type' => lang('Sales.rewards'), 'payment_amount' => 40.00, 'cash_refund' => 0, 'cash_adjustment' => 0, 'reference_code' => null, ], ]; $salesTaxes = [[], []]; $result = $saleModel->save_value( NEW_ENTRY, $saleStatus, $items, $customerId, $employeeId, 'test sale', null, null, null, SALE_TYPE_POS, $payments, null, $salesTaxes ); $this->assertSame(INSUFFICIENT_REWARD_POINTS, $result); $this->assertSame(20, $this->getCustomerPoints($customerId)); $this->assertSame($salesCountBefore, $this->countSalesRows()); } public function testSaveValueDecrementsStockOnCompletedSale(): void { $employeeId = $this->createEmployee(); $itemId = $this->createTestItem(HAS_STOCK); \Config\Database::connect()->table('item_quantities')->insert([ 'item_id' => $itemId, 'location_id' => self::LOCATION_ID, 'quantity' => 10, ]); $saleModel = model(Sale::class); $saleStatus = COMPLETED; $items = $this->buildCartLine($itemId, 3, 5.00); $payments = [ 0 => [ 'payment_type' => lang('Sales.cash'), 'payment_amount' => 15.00, 'cash_refund' => 0, 'cash_adjustment' => 0, 'reference_code' => null, ], ]; $salesTaxes = [[], []]; $result = $saleModel->save_value( NEW_ENTRY, $saleStatus, $items, NEW_ENTRY, $employeeId, 'test sale', null, null, null, SALE_TYPE_POS, $payments, null, $salesTaxes ); $this->assertGreaterThan(0, $result); $this->assertEqualsWithDelta(7.0, $this->getItemQuantity($itemId, self::LOCATION_ID), 0.001); } public function testSaveValueDecrementsStockOnCompletedSaleWhenNoQuantityRowExists(): void { $employeeId = $this->createEmployee(); $itemId = $this->createTestItem(HAS_STOCK); // Deliberately no item_quantities row inserted for this item/location. $saleModel = model(Sale::class); $saleStatus = COMPLETED; $items = $this->buildCartLine($itemId, 3, 5.00); $payments = [ 0 => [ 'payment_type' => lang('Sales.cash'), 'payment_amount' => 15.00, 'cash_refund' => 0, 'cash_adjustment' => 0, 'reference_code' => null, ], ]; $salesTaxes = [[], []]; $result = $saleModel->save_value( NEW_ENTRY, $saleStatus, $items, NEW_ENTRY, $employeeId, 'test sale', null, null, null, SALE_TYPE_POS, $payments, null, $salesTaxes ); $this->assertGreaterThan(0, $result); $this->assertEqualsWithDelta(-3.0, $this->getItemQuantity($itemId, self::LOCATION_ID), 0.001); } public function testDeleteRestoresStockOnCanceledSale(): void { $employeeId = $this->createEmployee(); $itemId = $this->createTestItem(HAS_STOCK); \Config\Database::connect()->table('item_quantities')->insert([ 'item_id' => $itemId, 'location_id' => self::LOCATION_ID, 'quantity' => 10, ]); $saleModel = model(Sale::class); $saleStatus = COMPLETED; $items = $this->buildCartLine($itemId, 3, 5.00); $payments = [ 0 => [ 'payment_type' => lang('Sales.cash'), 'payment_amount' => 15.00, 'cash_refund' => 0, 'cash_adjustment' => 0, 'reference_code' => null, ], ]; $salesTaxes = [[], []]; $saleId = $saleModel->save_value( NEW_ENTRY, $saleStatus, $items, NEW_ENTRY, $employeeId, 'test sale', null, null, null, SALE_TYPE_POS, $payments, null, $salesTaxes ); $this->assertGreaterThan(0, $saleId); $this->assertEqualsWithDelta(7.0, $this->getItemQuantity($itemId, self::LOCATION_ID), 0.001); $deleteResult = $saleModel->delete($saleId, false, true, $employeeId); $this->assertTrue($deleteResult); $this->assertEqualsWithDelta(10.0, $this->getItemQuantity($itemId, self::LOCATION_ID), 0.001); } }