Extended payment delete fix (#4274)

* Create a  Base64 URL-Safe encoding and decoding helper

* Rename web_helper to url_helper

---------

Co-authored-by: El_Coloso <diegoramosp@gmail.com>
This commit is contained in:
jekkos
2025-07-07 13:57:03 +02:00
committed by GitHub
parent b9e17daac7
commit 0d1f4efe3c
16 changed files with 71 additions and 187 deletions

View File

@@ -22,6 +22,8 @@ script:
- sed -i 's/opensourcepos.tar.gz/opensourcepos.$version.tgz/g' package.json
- npm ci && npm install -g gulp && npm run build
- docker build . --target ospos -t ospos
- docker build . --target ospos_test -t ospos_test
- docker run --rm ospos_test /app/vendor/bin/phpunit --testdox
- docker build app/Database/ -t "jekkos/opensourcepos:sql-$TAG"
env:
global:

View File

@@ -22,7 +22,7 @@ RUN composer install -d/app
#RUN sed -i 's/backupGlobals="true"/backupGlobals="false"/g' /app/tests/phpunit.xml
WORKDIR /app/tests
CMD ["/app/vendor/phpunit/phpunit/phpunit"]
CMD ["/app/vendor/phpunit/phpunit/phpunit", "/app/test/helpers"]
FROM ospos AS ospos_dev

View File

@@ -466,7 +466,9 @@ class Sales extends Secure_Controller
*/
public function getDeletePayment(string $payment_id): void
{
$this->sale_lib->delete_payment(base64_decode($payment_id));
helper('url');
$this->sale_lib->delete_payment(base64url_decode($payment_id));
$this->_reload(); // TODO: Hungarian notation
}

View File

@@ -0,0 +1,31 @@
<?php
if (!function_exists('base64url_encode')) {
/**
* Encode data to Base64 URL-safe string.
*
* @param string $data
* @return string
*/
function base64url_encode($data)
{
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
}
if (!function_exists('base64url_decode')) {
/**
* Decode Base64 URL-safe string to original data.
*
* @param string $data
* @return string|false
*/
function base64url_decode($data)
{
$remainder = strlen($data) % 4;
if ($remainder) {
$data .= str_repeat('=', 4 - $remainder);
}
return base64_decode(strtr($data, '-_', '+/'));
}
}

View File

@@ -57,6 +57,8 @@ if (!empty($warning)) {
if (isset($success)) {
echo '<div class="alert alert-dismissible alert-success">' . esc($success) . '</div>';
}
helper('url');
?>
<div id="register_wrapper">
@@ -478,7 +480,7 @@ if (isset($success)) {
<tbody id="payment_contents">
<?php foreach ($payments as $payment_id => $payment) { ?>
<tr>
<td><?= anchor("$controller_name/deletePayment/". base64_encode($payment_id), '<span class="glyphicon glyphicon-trash"></span>') ?></td>
<td><?= anchor("$controller_name/deletePayment/". base64url_encode($payment_id), '<span class="glyphicon glyphicon-trash"></span>') ?></td>
<td><?= $payment['payment_type'] ?></td>
<td style="text-align: right;"><?= to_currency($payment['payment_amount']) ?></td>
</tr>

View File

@@ -31,6 +31,7 @@
"matrix": "https://matrix.to/#/#opensourcepos_Lobby:gitter.im"
},
"require": {
"ext-intl": "*",
"php": "^8.1",
"codeigniter4/framework": "4.6.0",
"dompdf/dompdf": "^2.0.3",

View File

@@ -1,5 +1,3 @@
version: '2.2'
include:
- docker/docker-mysql.yml
@@ -18,6 +16,6 @@ services:
- MYSQL_DATABASE=ospos
- MYSQL_USERNAME=admin
- MYSQL_PASSWORD=pointofsale
command: [ "/bin/wait-for-it.sh", "mysql:3306", "--", "/app/vendor/bin/phpunit", "/app/app/tests" ]
command: [ "/bin/wait-for-it.sh", "mysql:3306", "--", "/app/vendor/bin/phpunit", "/app/tests" ]
ports:
- "80:80"

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "@opensourcepos/opensourcepos",
"version": "3.4.0",
"version": "3.4.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@opensourcepos/opensourcepos",
"version": "3.4.0",
"version": "3.4.1",
"license": "MIT",
"dependencies": {
"bootstrap": "^3.4.1",

View File

@@ -1,22 +0,0 @@
var assert = require("assert"); // Node.js core module
var ospos = require("./ospos");
describe("giftcard numbering test", function () {
this.timeout(25000);
it("should be able to login", function (done) {
return ospos.login(this.browser, done);
});
it.skip("issue #65: giftcard numbering should add properly", function (done) {
return this.browser.get(ospos.url("/index.php/giftcards")).elementByCssSelector(".modal-dlg").click()
.elementByName("value", 10000).type("100").elementById('giftcard_number').clear().type("10")
.elementById("submit").click().elementByXPath("//table/tbody/tr[td/text()='10']", 2000).text().then(function (value) {
assert.ok(value, "giftcard failed to be added properly!");
}).elementByCssSelector(".modal-dlg").click().elementByName("value", 4000).type("100").elementById("submit").click()
.elementByXPath("//table/tbody/tr[td/text()='11']").text().then(function (value) {
assert.equal(value, "11", "giftcard number not incrementing properly!!");
}).then(done, done);
});
});

View File

@@ -0,0 +1,25 @@
<?php
use PHPUnit\Framework\TestCase;
class UrlHelperTest extends TestCase
{
protected function setUp(): void
{
// Include the url_helper.php file
require_once __DIR__ . '/../../app/Helpers/url_helper.php';
}
public function testBase64urlEncode(): void
{
$data = 'Test data';
$encoded = base64url_encode($data);
// Assert that the encoded string is URL-safe
$this->assertMatchesRegularExpression('/^[A-Za-z0-9\-_]+$/', $encoded);
// Assert that decoding the encoded string returns the original data
$decoded = base64url_decode($encoded);
$this->assertEquals($data, $decoded);
}
}

View File

@@ -1,11 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>403 Forbidden</title>
</head>
<body>
<p>Directory access is forbidden.</p>
</body>
</html>

View File

@@ -1,43 +0,0 @@
var assert = require('assert');
var ospos = require('./ospos');
require('wd');
describe("create item and make sale", function () {
this.timeout(25000);
var def_timeout = 3000;
var item = { name: "anItem", category: "aCategory", cost_price: 10, unit_price: 20 };
it("should be able to add item", function (done) {
return ospos.create_item(this.browser, item).then(done, done);
});
it("should be able to make sale", function (done) {
return this.browser.get(ospos.url("/index.php/sales"))
.elementById("item", def_timeout).clear().type("1\uE007")
.elementByName("quantity", def_timeout).clear().type("2").elementByName("discount", def_timeout).type(item.cost_price).elementByCssSelector(".glyphicon.glyphicon-refresh").click()
.elementById("add_payment_button", def_timeout).click().elementByCssSelector("tbody#payment_contents tr td:last-child", def_timeout).text().then(function (value) {
assert.equal(value, "$43.56", "price " + value + " in sale register is not correct!!");
}).elementById("finish_sale_button", def_timeout).click().elementByCssSelector("#receipt_items tbody tr:nth-child(8) td:last-child", def_timeout).text().then(function (value) {
assert.equal(value, "$43.56", "price " + value + " on sale receipt is not correct!!");
}).elementByCssSelector("#receipt_items tbody tr:nth-child(10) td:last-child", def_timeout).text().then(function (value) {
assert.equal(value, "-$43.56", "payment amount " + value + " on sale receipt is not correct!!")
}).then(done, done);
});
it("should be able to make receiving", function (done) {
return this.browser.get(ospos.url("/index.php/receivings"))
.elementById("item", def_timeout).clear().type("1\uE007")
.elementByName("quantity", def_timeout).clear().type("2").elementByCssSelector("a[title='Update']").click()
.elementByCssSelector("td:nth-last-child(2)").text().then(function (value) {
assert.equal(value, "$20.00", "price " + value + " in receiving register is not correct!!");
}).elementById("finish_receiving_button").click().elementByCssSelector("#receipt_items tbody tr:nth-last-child(2) td:nth-child(2) div.total-value").text().then(function (value) {
assert.equal(value, "$20.00", "price " + value + " on receiving receipt is not correct!!");
})
.then(done, done);
});
});

View File

@@ -1,47 +0,0 @@
var assert = require('assert');
var ospos = function () {
var server = "http://localhost";
return {
url: function (suffix) {
return server + suffix;
}
,
login: function (browser, done) {
return browser.get(this.url("/index.php"))
.elementByName('username').type("admin").getValue()
.then(function (value) {
assert.equal(value, "admin");
})
.elementByName('password').type("pointofsale").getValue()
.then(function (value) {
assert.ok(value, "pointofsale");
})
.elementByName('loginButton').click()
.elementById('home_module_list').then(function (value) {
assert.ok(value, "Login failed!!")
})
.then(done, done);
},
create_item: function (browser, item) {
return browser.get(this.url("/index.php/items")).elementByCssSelector("button[title*='New Item']", 5000).click()
.elementById('cost_price', 2000).clear().type(item.cost_price)
.elementById("unit_price", 2000).type(item.unit_price)
.elementById('tax_name_1', 2000).type('VAT').elementById("tax_percent_name_1", 2000).type("21")
.elementById("name", 10000).type(item.name)
.elementById("category", 2000).clear().type(item.category)
.elementById('receiving_quantity', 2000).type(item.receiving_quantity || 1)
.elementById("quantity_1", 2000).type("1").elementById("reorder_level", 2000).type("0").elementById("submit", 2000).click()
.elementByXPath("//table/tbody/tr[td/text()='anItem']", 5000).text().then(function (value) {
assert.equal(value, "1 - anItem aCategory - $10.00 $20.00 1 21.00%");
});
}
}
};
module.exports = ospos();

View File

@@ -1,26 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap = "../../vendor/autoload.php"
<phpunit bootstrap = "../vendor/autoload.php"
backupGlobals = "false"
backupStaticAttributes = "false"
colors = "true"
convertErrorsToExceptions = "true"
convertNoticesToExceptions = "true"
convertWarningsToExceptions = "true"
processIsolation = "false"
stopOnFailure = "false"
syntaxCheck = "false">
stopOnFailure = "false">
<testsuites>
<testsuite name="Helpers">
<directory>helpers</directory>
</testsuite>
<testsuite name="Libraries">
<directory>libraries</directory>
</testsuite>
<testsuite name="Models">
<directory>models</directory>
</testsuite>
</testsuites>
</phpunit>

View File

@@ -1,32 +0,0 @@
var assert = require('assert');
var ospos = require('./ospos');
describe("test receiving quantity", function () {
var def_timeout = 10000;
var item = {
name: "recvQty",
category: "aCategory",
cost_price: 10,
unit_price: 20,
receiving_quantity: 2
};
it("should be able to create item with receiving quantity", function (done) {
return ospos.create_item(this.browser, item).then(done, done);
});
it("should be able to receive quantities with multiplier", function (done) {
this.browser.get(ospos.url('/index.php/receivings')).elementById("item").clear().type(item.name)
.elementByCssSelector(".ui-autocomplete .ui-menu-item", def_timeout).click()
.elementByName("quantity")
.elementByCssSelector("#cart_contents tr td:nth-child(5)").text().then(function (value) {
assert(value, "x " + item.receiving_quantity, "receiving quantity " + item.receiving_quantity + " is not displayed correctly in receivings module!");
}).elementById("finish_receiving_button", def_timeout).submit()
.elementByCssSelector("#receipt_items tr:nth-child(2) td:nth-last-child(2)", def_timeout).text().then(function (value) {
assert(value, "1 x " + item.receiving_quantity, "receiving quantity " + item.receiving_quantity + " is not displayed correctly on receipt!!");
}).then(done, done);
});
});

View File

@@ -1,11 +0,0 @@
var assert = require("assert"); // Node.js core module
var wd = require('wd');
describe('A Mocha test run by grunt-mocha-webdriver', function () {
it('has a browser injected into it', function () {
assert.ok(this.browser);
});
it('has wd injected into it for customizing', function () {
assert.notEqual(this.wd, undefined);
});
});