Commit Graph
305 Commits
Author SHA1 Message Date
objecttothis 7a82da5b66 fix(config): harden sendmail path validation and license page rendering
- Broaden OSPOSRules filesystem path regex to allow space, colon,
  and backslash for Windows sendmail paths while still excluding
  shell metacharacters used unescaped in popen(); update docblock
  accordingly.
- Restore/translate mailpath_invalid message across all language
  files so users get a real error string instead of empty text.
- Guard Config controller license rendering against malformed or
  non-array npm-prod/npm-dev LICENSES JSON, skipping entries
  missing required keys instead of raising warnings.
- Document Node.js 20+ requirement in BUILD.md for license
  reporting dependency.
- Remove security advisory IDs/URLs from INSTALL.md and code
  comments per new AGENTS.md policy against referencing CVE/GHSA
  identifiers anywhere in the repo.

Signed-off-by: objecttothis <17935339+objecttothis@users.noreply.github.com>
2026-09-10 18:07:18 +04:00
objecttothis 6a36cdc3e9 fix(licenses): guard malformed data, parallelize gulp tasks, require Node 20
fix(config): guard against non-array and incomplete license data

- Wrap npm-prod/npm-dev license parsing in is_array() checks to avoid
  foreach errors when JSON decodes to null or non-array
- Skip dependency entries missing required keys (name, author, homepage,
  installedVersion, licenseType) in open-source and license-key loops

fix(gulp): correctly await all async tasks

- Parallelize update-licenses, copy-bootswatch, copy-bootswatch5, and
  copy-bootstrap sub-tasks via Promise.all
- Wrap exec() calls with finished(execStream.resume()) so composer and
  npm license-report commands fully write output files before task resolves;
  .resume() drains stdout so streams can emit close/finish events

build(package): require Node.js >=20

- Add engines field to package.json
- Regenerate package-lock.json with matching constraint
- Document prerequisite in BUILD.md; license-reporting dep needs regex
  features unavailable in Node 18 and earlier

Signed-off-by: objecttothis <17935339+objecttothis@users.noreply.github.com>
2026-09-10 17:32:52 +04:00
objecttothis cc68b75b26 fix(plugins): block install completion until plugin migrations run
Previously, plugin migrations only ran once during PluginManager
initialization for already-enabled plugins, which caused the login
"latest version" check to report the system as up to date even when
plugin migrations were still pending. Newly enabled plugins also had
no path to run their migrations at enable time.

- Login controller: factor pending-migration check into
  isLatest determination, and explicitly run pending plugin
  migrations after core migrations complete
- PluginManager: remove implicit migration run from init(); add
  public hasPendingMigrations() and runPendingMigrations() so
  callers can check/trigger migrations explicitly
- PluginManager: extract getPendingMigrationFiles() helper shared
  by hasPendingMigrations() and per-plugin migration runner
- PluginManager: run pending migrations immediately when a plugin
  is enabled, instead of waiting for next request/init
- PluginManager: throw RuntimeException on missing migration class
  or failed migration instead of silently breaking the loop, so
  failures propagate to callers

Signed-off-by: objecttothis <17935339+objecttothis@users.noreply.github.com>
2026-09-10 12:26:51 +04:00
objecttothis ca87840170 Merge remote-tracking branch 'OpensourcePOS/master' into plugin-system-fresh 2026-09-09 12:26:08 +04:00
jekkos 28755dfd50 fix(tests): resolve all phpunit failures — clean-DB suite green (#4626) (#4691)
* fix(tests): resolve all phpunit failures (#4626)

Bring the phpunit suite from 153 failures to 0 (281 tests passing):

- Employee: decouple grants block from save_value success; restructure
  save_employee new-employee + disallowed-grants early return
- Sale: unify sales_payments_temp schema (add sale_cash_refund,
  reference_code) so both creators produce an identical superset table
- Employees controller: provide placeholder password/hash in testing env
  so new-employee insert succeeds and grant logic is testable
- TestDatabaseBootstrapSeeder: reset shared connection table-name cache
  after bootstrap reset to avoid stale listTables()/tableExists() results
- Config: fix postSaveLocale validation rule syntax
- Test data: use unique employee usernames to avoid UNIQUE constraint
  collisions latching strict-mode transStatus=false on the shared conn
- Various test-file and language-string corrections

* test: consolidate employee fixtures in shared trait

Route test employee creation through a single EmployeeFixtureTrait
that delegates to Employee::save_employee(), so fixtures exercise the
same production code path instead of raw DB inserts. Removes six
near-duplicate helpers across EmployeeTest, SalesControllerTest, and
EmployeesControllerTest while preserving each test's specific grant
set.

Closes a piece of the fixture-scattering flagged in #4626.

Closes #4626

* test: add global DROP/CREATE grant and commit theme fixtures

* fix(ci): remove redundant symlink step, set working encryption key

* fix(ci): run phpunit with --no-coverage to avoid no-driver warning

* fix: address code review findings

- Config: restore strict locale validation (min required|integer|>0) and
  fix max cross-field check with a new gte_field rule (CI4's
  greater_than_equal_to[field] does not resolve the field value)
- Tests: assert rejection for non-numeric/zero/negative/min>max limits
- .env.example: remove shared hard-coded encryption.key (auto-generates);
  document Docker env-var usage
- phpunit.yml: scope CREATE/DROP grant to ospos_test.* and provision a
  per-run encryption key as an env var

* feat: support ENCRYPTION_KEY env var for encryption key

Read ENCRYPTION_KEY as a fallback for the encryption key when the
config value is empty. This is a supported, reliable path for Docker /
container deploys and CI, avoiding reliance on the raw dotted
encryption.key env var.

* fix: align Summary_report temp tables with Sale temp table schema

Summary_report created sales_items_taxes_temp and sales_payments_temp with fewer columns than the canonical create_temp_table() in Sale.php. A later reader expecting those columns hit a schema-mismatch SQL error on the shared temp tables. Add internal_tax/sales_tax (sales_items_taxes_temp) and reference_code (sales_payments_temp) so all creators emit the identical column set.
2026-09-08 21:49:28 +02:00
objecttothis 3b7b41e119 fix(bootstrap-table): resolve lang file mismatch and improve locale handling
- Add `editable` override support in `bootstrap_tables_locale.php` to fix misaligned language file lookups for plugins.
- Update documentation explaining the issue and providing a resolution.
- Adjust `Sales` and `Attribute` logic for clearer naming (`sale_type` to `saleType`) and better attribute mapping (`attribute_id` added).

Signed-off-by: objecttothis <17935339+objecttothis@users.noreply.github.com>
2026-09-08 13:47:14 +04:00
objecttothis e581cecdd8 Merge remote-tracking branch 'OpensourcePOS/master' into plugin-system-fresh 2026-09-07 12:29:40 +04:00
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
jekkosandobjecttothis 839821e2eb bugfix(sales): reject non-negative gift-card amount_tendered (#4674)
* Validate gift-card payment amounts (GHSA-9847)

Close the negative gift-card amount minting vector: when a forged
payment_type like 'Gift Card:<number>' reaches the catch-all validation
branch, a negative amount_tendered previously passed decimal_locale and was
then routed into Giftcard::decrementGiftcardValue, where value - (-N)
increased the balance (store credit minted at will).

- Add nonNegativeDecimal rule + 'Sales.negative_amount_tendered' message to
  the catch-all amount_tendered rules in Sales::postAddPayment(); add the
  language key to all 46 locale files (populated in en, empty elsewhere).
- Guard Giftcard::decrementGiftcardValue() against non-positive amounts so
  the sink itself can no longer add balance from an inverted subtraction.
- Regression tests: controller-level rejection of negative amount_tendered
  and model-level rejection of negative/zero decrements.

* Address PR review: align locale keys, drop advisory refs, add decimal_locale message

- Align negative_amount_tendered '=> with all other keys (46 locale files)
- Remove docblock + inline comment above decrementGiftcardValue()
- Remove GHSA ID and attack-detail description from test; scrub redundant comment
- Add decimal_locale message override + focused malformed-amount test

* Fix formatting and spacing in SalesControllerTest

* fix(lang): remove duplicate negative amount tendered key

Consolidate 'negative_amount_invalid' and 'negative_amount_tendered'
translation keys in Sales.php across all locale files. Both keys held
identical messages, causing redundant translation maintenance.

- Drop 'negative_amount_invalid' key, keep 'negative_amount_tendered'
- Move existing translated text into 'negative_amount_tendered' where
  it was previously empty
- Applied across all app/Language/*/Sales.php locale files

Signed-off-by: objecttothis <17935339+objecttothis@users.noreply.github.com>

* fix(sales): allow negative amount_tendered in return mode

Return transactions legitimately produce negative amount_due and
prefilled amount_tendered values, but validation rules previously
enforced nonNegativeDecimal unconditionally, blocking valid returns.

- Detect return mode via sale_lib->get_mode() in Sales::process
- Build amount_tendered rule conditionally: skip nonNegativeDecimal
  check when in return mode, keep it for sale/giftcard flows
- Apply the conditional rule to both giftcard and standard payment
  branches

Signed-off-by: objecttothis <17935339+objecttothis@users.noreply.github.com>

* test: update expected error message in negative payment test

Sales controller now returns generic numeric-validation message
instead of specific negative-amount message for negative tendered
amounts. Update test assertion to match new lang key.

- tests/Controllers/SalesControllerTest.php: assert
  Sales.must_enter_numeric instead of
  Sales.negative_amount_tendered

Signed-off-by: objecttothis <17935339+objecttothis@users.noreply.github.com>

* test: remove regression tests for GHSA-9847 negative amount fix

Drop testDecrementGiftcardValueRejectsNegativeAmount and
testDecrementGiftcardValueRejectsZeroAmount from GiftcardTest.

- Remove coverage for decrementGiftcardValue() rejecting
  non-positive amounts (negative/zero) in tests/Models/GiftcardTest.php

Signed-off-by: objecttothis <17935339+objecttothis@users.noreply.github.com>

---------

Signed-off-by: objecttothis <17935339+objecttothis@users.noreply.github.com>
Co-authored-by: objecttothis <17935339+objecttothis@users.noreply.github.com>
2026-09-07 11:47:02 +04:00
objecttothis 5e4632d55d Merge remote-tracking branch 'OpensourcePOS/master' into plugin-system-fresh
# Conflicts:
#	app/Controllers/Sales.php
#	app/Models/Sale.php
2026-09-02 11:35:39 +04:00
objecttothis f5f9052de1 fix(sales): harden payment validation and gift card handling
- Validate paymentType is a non-empty string before processing
- Reject negative or zero amounts for all payment types
- Enforce full payment coverage before completing a sale
- Bypass coverage check for invoice and quote mode sales
- Require a valid gift card number before decrementing value;
  rollback and return insufficient balance error on missing input
- Add "amount_due_not_covered" and "negative_amount_invalid"
  translations across 40+ locales
- Add test coverage for gift card validation, negative amounts,
  and quote/invoice zero-payment completion
- Rename snake_case locals to camelCase in postComplete (no behavior change)
2026-09-02 01:21:46 +04:00
objecttothis fdc1c38b43 feat(validation, tests): add valid_path_strict rule and integrate into mailpath validation (#4684)
- Introduce `valid_path_strict` rule in `OSPOSRules` to enforce stricter path validation, preventing security issues like injection attempts with newline or special characters.
- Update mail configuration validation in `Config` controller to use the new rule for the `mailpath` field.
- Add unit tests in `OSPOSRulesTest` to cover edge cases for `valid_path_strict`.

Signed-off-by: objecttothis <17935339+objecttothis@users.noreply.github.com>
2026-09-01 19:41:41 +04:00
objecttothis 6db3dde491 Bugfix tax names (#4677)
bugfix(items, validation): reject unsafe tax names and fix payments temp table collision

- Add unicode_alpha_numeric_punct rule (OSPOSRules) to allow accented/CJK
  chars in text fields while blocking HTML-unsafe chars (<, >) as
  defense-in-depth against injection
- Items controller: extract validateItemFields/validateBulkUpdateFields,
  validate tax_names on save and bulk update using new rule; add shared
  validateFields helper in Secure_Controller to DRY up validation +
  JSON error response
- Escape tax_group output in sales/quote.php and receipt_email.php views
  to harden output encoding at render time
- Rename sales_payments_temp -> sales_report_payments_temp (Summary_report)
  and -> sales_search_payments_temp (Sale model) to avoid name collision
  between concurrently-created temp tables
- AGENTS.md: document alignment rule for => columns when inserting new
  language keys

Tests:
- Add ItemsControllerTest covering postSave/bulkupdate tax_names validation
- Reject <, > in tax_names on /items/save and /items/bulkupdate
- Verify unicode and apostrophe-containing tax names are accepted
- Cover CSV import helpers: header generation (basic, multiple locations,
  attributes), stock-location/attribute header builders, get_csv_file
  parsing (plain, BOM-prefixed, multi-row)
- Validate required-header detection for import templates
- Remove outdated tax name test from SalesControllerTest
- Simplify Database class references in SalesControllerTest

i18n:
- Add tax_name_invalid translation to Items.php for 20+ locales, inserted
  alphabetically after tax_category in each file
- Normalize quote style in ar-EG/Items.php to single quotes
- Add ka/Items.php Georgian locale scaffold

Signed-off-by: objecttothis <17935339+objecttothis@users.noreply.github.com>
2026-09-01 10:59:29 +04:00
objecttothis 459610cded Merge branch 'master' into plugin-system-fresh 2026-08-31 16:18:59 +04:00
objecttothis 905a447e55 fix(reports, home): resolve double-URL-decoding bypass for method grants (#4660) (#4666)
fix(reports, home): strengthen method grant validation and URI decoding (#4660)

- Reports, Home: fix URI decoding in hasGrant() method checks to use
  urldecode consistently, preventing malformed URI segments from
  bypassing access controls
- Reports: rename snake_case variables to camelCase for PSR-12 compliance
- Reports: adjust access checks to accurately handle null submodule IDs

Tests:
- Add grant check tests for encoded URI inputs across Reports and Home
- Add test case for employee access with base reports grant
- Add secondary grant check for reports_customers in relevant test cases
- Confirm logout bypass remains functional and properly controlled
- Refactor TestDatabaseBootstrapSeeder to expose static reset() for
  per-class DB re-initialization instead of only via seeder run()
- Standardize session handling, setup logic, and boolean declarations
- Use unique data in test helpers to avoid collisions
- Add docblocks to ReportsControllerTest and HomeTest for PSR-5 compliance
- Add exception handling for failed employee creation in test setup
- Wrap password validation test in try-finally to guarantee state cleanup

Signed-off-by: objecttothis <17935339+objecttothis@users.noreply.github.com>
2026-08-31 13:33:09 +04:00
objecttothis 8cfd1a4b1d fix(sales): enforce reports_sales grant on search endpoint (#4673)
- Sales::getSearch now enforces reports_sales grant before running
  search, returns 403 with lang message when missing
- Rename snake_case helpers/methods to camelCase across
  Sales controller, Sale model, and tabular_helper
  (get_sale_data_row -> getSaleDataRow, get_payments_summary ->
  getPaymentsSummary, sales_headers -> salesHeaders, etc.)
- Config/OSPOS: reset DB data cache before checking app_config
  table existence to avoid stale schema cache in tests
- TestDatabaseBootstrapSeeder: expose static reset() so tests can
  rebuild schema once per class instead of only via seeder run()
- SalesControllerTest: bootstrap DB once per class, seed once,
  refresh app settings each setUp, add tests for search endpoint
  authorization (cashier denied, supervisor allowed), move
  createTestItem into shared ItemFixtureTrait

Signed-off-by: objecttothis <17935339+objecttothis@users.noreply.github.com>
2026-08-31 13:08:33 +04:00
objecttothis d63d31700d Ensure payload data is escaped to prevent XSS (#4664)
fix(barcode): escape payload fields to prevent XSS; PSR-12 refactor

- Apply `esc()` to name, ID, item number, category, and company name in `Barcode_lib` payloads
- Remove redundant `urldecode()` in `Item_kitsController` to prevent triple decoding
- Rename variables and methods to camelCase across barcode, item_kits, and tests
- Add type hint for `$layoutType` parameter in `manageDisplayLayout`
- Add/update unit tests covering escaping and HTTP response assertions

Signed-off-by: objecttothis <17935339+objecttothis@users.noreply.github.com>
2026-08-28 10:08:31 +04:00
objecttothis 84bddcdd88 Feature admin account safeguards (#4657)
fix(employees): harden permissions UI and access control

- Prevent admins from removing their own minimum module grants (employees, home, office)
- Add session_status check before session regeneration
- Disable submit button and return no_access view for AJAX requests
- Replace fade class with active-only for Bootstrap compatibility
- Update permission toggle selectors to .module-toggle
- Add error_cannot_remove_own_minimum_grant translations for Armenian, Bulgarian, Georgian, Swedish, and Ukrainian

---------

Signed-off-by: objecttothis <17935339+objecttothis@users.noreply.github.com>
2026-08-27 11:29:32 +04:00
objecttothis c701d21886 fix(sales): gate per-record endpoints behind reports_sales grant (#4627)
fix(sales): gate per-record endpoints behind reports_sales grant (REDACTED)

Cashiers holding only the base `sales` grant could reach per-sale endpoints
(getRow, getEdit, postSave, getReceipt, getInvoice, getSendPdf, getSendReceipt)
that require `reports_sales`. getManage() enforced this at the list level, but
individual endpoints did not re-check. Regression tests added.

Auth:
- Introduce `IsLoggedIn` filter to centralize login checks across controllers
- Replace custom `AccessDeniedRedirectException` with built-in `RedirectException`

Employees:
- Add `DISALLOW_PASSWORD_CHANGE` and `DISALLOW_GRANT_CHANGE` env vars to restrict
  credential and permission changes in locked-down environments
- Extract `hasGrantsChanged()` to streamline `postSave`

Refactor:
- Rename snake_case variables to camelCase in Sales, Items, and Employees
  controllers for PSR-12 compliance
- Use explicit `db_connect()` for transaction clarity in Items controller

Fixes:
- SMTP config entries fall back to defaults via null coalescing
- Migration uses `DROP FOREIGN KEY` instead of `DROP CONSTRAINT`
- Password hash upgrade only sets session on successful `hash_version` update
- Correct lang key for unknown error in Module model

Language:
- Translate `error_grant_change_disallowed` / `error_password_change_disallowed`
  across all 44 supported locales with => alignment matching en reference
- Fix "cannot be deleted" messages and misc typos across ~15 language files

Tests:
- Bootstrap seeder only once in ItemsCsvImportTest; close connection after
- Restore `DISALLOW_GRANT_CHANGE` in teardown to prevent side effects
- Use `uniqid()` for test user data to avoid collisions

Signed-off-by: 17935339+objecttothis@users.noreply.github.com
2026-08-23 17:40:06 +04:00
objecttothis 9ee530e454 Hotfix: Fix CI3 database migration caused by regression (#4649)
* refactor: standardize function and variable names to camelCase and improve naming consistency across files

* refactor(config): remove spaces around `=` in configuration files for improved consistency and formatting as is required by .env formatting rules.

* refactor(security): extract `.env` key management logic into reusable `writeEnvKey` helper, add throttle key provisioning logic, and streamline encryption key updates

* fix(migration): improve error handling in CI3 to CI4 encryption data migration
- Secure `up` and `convertCI3EncryptedData` methods with detailed exception handling for script execution and data saving.

* fix(migration): ensure empty string is correctly handled in CI3 to CI4 encryption data conversion

* refactor(security): enhance `.env` management with durable writes, better locking, and helper abstraction
- Update `writeEnvKey` to return a success flag and handle file locks robustly.
- Introduce `atomicWriteFile` for atomic writes to prevent partial file updates.
- Add `applyEnvKeyReplacement` to streamline `.env` key insertion and updates.
- Improve throttle key provisioning with validation and runtime persistence safeguards.

* refactor(security): implement dedicated `.env` file locking for robust and cross-platform safe write operations
- Add `lockEnvFile` and `unlockEnvFile` helpers to manage `.env` mutex files.
- Refactor `.env` write logic to use lock helpers, improving reliability and preventing race conditions.
- Enhance `atomicWriteFile` for better handling of file overwrites on Windows and POSIX systems.

* fix(migration): improve encryption error handling during CI3 to CI4 data conversion
- Add conditional checks for `checkEncryption` to prevent failed key persistence.
- Introduce `abortEncryptionConversion` for cleanup on failure.
- Update `writeEnvKey` to handle and return errors gracefully.

* refactor(security): improve `atomicWriteFile` for better file locking and cross-platform durability
- Replace `uniqid` with `bin2hex(random_bytes())` for more secure temp file naming.
- Add explicit file permissions and locking for safe concurrent writes.
- Enhance error handling to ensure atomicity on both Windows and POSIX systems.

* Add env temp files to gitignore so they don't get tracked.

---------

Signed-off-by: objecttothis <17935339+objecttothis@users.noreply.github.com>
2026-08-21 21:00:48 +04:00
objecttothis 1cbdae2b89 Merge remote-tracking branch 'OpensourcePOS/master' into plugin-system-fresh 2026-08-20 11:27:14 +04:00
objecttothis cc93c31355 fix(items): add explicit sentinel value for clearing supplier in bulk edit (#4617)
* fix(items): add explicit sentinel value for clearing supplier in bulk edit

This fixes a regression introduced in the fix for [REDACTED]
Introduce `Item::CLEAR_SUPPLIER_OPTION = 'NONE'` to distinguish between
\"leave supplier_id unchanged\" (empty string) and \"clear supplier_id\"
(sentinel). Previously, empty string was ambiguous.

- Add `CLEAR_SUPPLIER_OPTION` constant with doc comment explaining intent
- Update supplier dropdown to include sentinel as first real option
- Shift empty string to mean \"do nothing\" across all bulk edit fields

* test(items): add regression tests for mass assignment in bulk edit

Cover [REDACTED]: Item::update_multiple() bypasses model
$allowedFields via Query Builder, allowing unintended field writes
during bulk edit operations.

* fix(items): add type validation to bulk-edit field filter

filterBulkEditFields now validates field values before accepting them:
- Non-scalar values are rejected (array injection guard)
- Price/quantity fields are locale-parsed to floats, invalid strings skipped
- Boolean fields must be 0 or 1, other values skipped
- supplier_id must be numeric; CLEAR_SUPPLIER_OPTION still nulls it

Update tests to assert parsed types (float for prices, int for
supplier_id) and replace the fill-all-fields fixture with a realistic
input that only covers fields a form would actually submit.

* test(items): add supplier cleanup and helper methods to bulk update tests

- Track created supplier person IDs for teardown cleanup
- Delete supplier records in tearDown to prevent test pollution
- Extract item/supplier creation into reusable helper methods

* style(tests): rename variables to camelCase in ItemBulkUpdateTest

* refactor(items): rename snake_case variables to camelCase

Convert Item model, Items controller, and bulk update tests to
PSR-compliant camelCase naming per project conventions.

- Rename update_multiple to updateMultiple in Item model
- Rename local variables (item_data, items_to_update, tax_names, etc.)
  to camelCase across Items controller and Item model
- Update ItemBulkUpdateTest to use new updateMultiple method name
- Reorder and update AGENTS.md naming conventions

* style(tests): convert snake_case variables to camelCase in ItemBulkUpdateTest

Rename local variables and property names to camelCase for PSR-12
consistency, matching convention used elsewhere in new test code.
2026-08-20 11:24:50 +04:00
objecttothis a9526a6334 Merge remote-tracking branch 'OpensourcePOS/master' into plugin-system-fresh
# Conflicts:
#	app/Controllers/Items.php
#	app/Models/Item_quantity.php
#	app/Models/Sale.php
2026-08-20 11:18:18 +04:00
objecttothis 29a9b1a7e7 Bugfix: Resolve Race Condition in Rewards and Gift Card Spending (#4640)
* Implement atomic updates for gift card and reward point decrements, enhance error handling for insufficient balances, and add regression tests for concurrency safety.

* Add translations for insufficient gift card balance and reward points error messages across all supported languages.

* Reorder `clear_suspended_sale_detail` call to ensure transactional consistency.

* Reorder `clear_all` call to align with success and error handling logic.

* Ensure soft-deleted gift cards are excluded in balance updates.

* Refactor change_quantity logic with atomic upserts, improve error handling for insufficient stock, and update related tests and constants.

* Added check for NEW_ENTRY

* Added unit tests to test changes.

* Fix class name casing in ItemQuantityTest for consistency.

* Fix Bulgarian translations for insufficient balance error messages in Sales module.

* Fix Greek translations for insufficient balance error messages in Sales module.

* Fix Armenian translations for insufficient balance error messages in Sales module.

* Fix Tamil translations for insufficient balance error messages in Sales module.

* Implement race condition testing for database methods with concurrent process support.

* Fix class name casing in ItemTest for consistency.

* Improve concurrent process handling in race condition tests; add readiness and synchronization barriers.

* Improve handling of process I/O streams and timeout management in race condition tests.

* Add test for decrementing gift card value when marked as deleted

* Add `finally` block to ensure proper cleanup in async database race condition tests

* Improve error handling and timeout management in async database race condition tests.

* Refactor test utilities to use shared `EmployeeFixtureTrait` and `ItemFixtureTrait`.

* Track process exit codes explicitly in race condition tests for improved error detection and debugging.

* Improve error handling in `ConcurrentDbRaceTrait` by adding exceptions for `mysqli_poll` and `mysqli_reap_async_query`.

Signed-off-by: objecttothis <17935339+objecttothis@users.noreply.github.com>

---------

Signed-off-by: objecttothis <17935339+objecttothis@users.noreply.github.com>
2026-08-20 02:24:23 +04:00
objecttothis a2b91493c7 Feature: CodeIgniter Throttler (#4619)
* fix(auth): throttle login attempts to prevent brute-force attacks

Add Throttle filter and wire into login route to mitigate
credential-stuffing/brute-force risk (redacted).

- Register App\\Filters\\Throttle in Filters config
- Add \"too_many_attempts\" language string for throttled responses
- Add tests for Throttle filter and Login controller throttling

* Correct bug causing error to not display.


* i18n(login): add too_many_attempts translation for login throttling

Add localized \"too many attempts\" message across all language files
to support login throttling feature. Message informs users to wait
before retrying after exceeding attempt limit.

* fix(auth): rate limit login attempts to prevent brute-force attacks

Add Throttle filter that rate limits login/migrate POST requests,
keyed by IP and submitted username, using CodeIgniter's cache-based
Throttler (redacted).

- Wire filter into login/migrate routes
- Show localized error message when rate limit exceeded
- Broaden writable/cache ignore pattern to cover throttler cache file

* Update app/Language/ar-EG/Login.php

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* style(i18n): use single quotes for translation array keys and values

Convert double-quoted array keys and string values to single quotes
across all Login.php language files for style consistency.

* test(auth): update LoginTest to use throttler service directly

Replace clearThrottleState's Services::resetSingle('throttler') call
with Services::throttler() to reset state via the service instance.
Add usedKeys property to track throttle keys used across test cases.

* test(auth): skip login test assertion when migration required

LoginTest now check migration-required response state before
asserting HTTP 200. Prevent false failures when app force
pending-migration redirect during test run.

* Added missing return statements
2026-08-19 00:25:22 +04:00
objecttothis 8bd2a51cb5 fix(items): validate item_number and skip receiving quantity default for temp items (#4621)
* fix(items): validate item_number and skip receiving quantity default for temp items

- Validate item_number against alpha_numeric_punct rule, return JSON error on failure
- Add item_number_invalid language string

* i18n: reorder Items language keys and add item_number_invalid string

Add item_number_invalid translation across all locale files and
resort surrounding keys alphabetically to match key ordering
convention.

* PSR-12 refactoring.

- Change local variable to camelCase.
- Use single quote in language files.

* i18n: translate item_number_invalid string in ta, th, tl, zh-Hans

Item_number_invalid key had English placeholder text in Tamil,
Thai, Tagalog, Chinese Simplified language files. Translate to
match each locale.

* fix(items): use FormatRules for item_number validation

* refactor(items): use camelCase for variable names

* refactor(items): use camelCase for variable names in Items controller
2026-08-18 02:51:42 +04:00
Sai Asish Yandobjecttothis 0e8fc0963a Reject item CSV imports whose header row is missing required columns (#4597)
* Reject item CSV imports whose header row is missing required columns

Signed-off-by: Sai Asish Y <say.apm35@gmail.com>

* Require all template columns when validating CSV import headers

Signed-off-by: Sai Asish Y <say.apm35@gmail.com>

* fix: derive required CSV import headers from the template generator

---------

Signed-off-by: Sai Asish Y <say.apm35@gmail.com>
Co-authored-by: objecttothis <17935339+objecttothis@users.noreply.github.com>
2026-08-14 14:05:43 +04:00
objecttothisandTravis Garrison 5c9b1b81e6 fix(xss): remove redundant escaping that double-encoded item attribute values (#4628)
* fix(xss): remove redundant escaping that double-encoded item attribute values

- Remove esc()/html_entity_decode() calls now that output is escaped
  at render time by the framework, preventing double-encoding of
  special characters in attribute names, units, and definition values
- Fix employee_name form_input value fields to stop pre-escaping
  before form_input applies its own escaping
- Reorder Items.php use statements and add missing BaseConnection import
- Change items/manage.php start_date from let to plain assignment for
  proper reassignment scope

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

* test(sales): add regression tests for permission checks on sales endpoints

- Ensure role-based permissions correctly restrict access to sensitive actions like price edits, receipt/invoice views, and report generation.
- Add tests for both granted and restricted user scenarios to validate the behavior.

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

* fix(attributes): validate `attribute_value` before processing

- Add checks to ensure `attribute_value` is a non-empty string in `postSaveAttributeValue` and `postDeleteDropdownAttributeValue` methods.
- Return error response if validation fails to prevent invalid data handling.

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

* fix(attributes): improve error handling and optimize affected items processing

- Use `array_column` for extracting item IDs to streamline logic.
- Add JSON validation with `JSON_THROW_ON_ERROR` and return proper error response for invalid `definition_values`.

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

* test(sales): enable database refresh for consistent test state

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

* test(sales): assert unauthorized message is displayed on restricted access

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

* refactor(attributes): use camelCase for `attributeValue` in controller methods

- Standardize variable naming in `postSaveAttributeValue` and `postDeleteDropdownAttributeValue` methods by switching to camelCase.

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

---------

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>
Co-authored-by: Travis Garrison <travis@chiraqbookstore.com>
2026-08-09 11:37:38 +04:00
richardmilles 08b0951a84 fix: use db_connect() for item save transactions (#4636)
postSave() called $this->db which is not set on the Items controller, causing Undefined property errors when saving items. Match the CSV import path and obtain the connection via db_connect().

Fixes #4623
2026-08-09 00:10:53 +02:00
objecttothisandTravis Garrison f5ba1709eb fix(security): sanitize filenames and escape logo path in config (#4630)
* fix(security): sanitize filenames and escape logo path in config

- Sanitize uploaded filename in Config.php via preg_replace, strip
  chars outside [a-zA-Z0-9_-] before storing raw_name
- Escape $logo_src with esc(..., 'attr') in info_config.php view to
  prevent XSS via crafted logo path/filename

Prevents stored XSS and path traversal from unsanitized filenames
used in config uploads.

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

* fix(config): sanitize uploaded config filenames

Replace inline regex filename sanitization with sanitize_filename()
helper to prevent path traversal via crafted upload filenames.

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

---------

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>
Co-authored-by: Travis Garrison <travis@chiraqbookstore.com>
2026-08-07 20:30:02 +04:00
Rayan Abdul Caderandobjecttothis aa96ad0284 refactor: apply PSR-12 naming to Attribute definition methods (#4624)
Renames the Attribute-specific definition methods from snake_case to
camelCase and updates every call site:

  get_definition_by_name   -> getDefinitionByName
  get_definition_names     -> getDefinitionNames
  get_definition_values    -> getDefinitionValues
  get_definitions_by_type  -> getDefinitionsByType
  get_definitions_by_flags -> getDefinitionsByFlags
  get_definition_flags     -> getDefinitionFlags

Also documents getDefinitionByName()'s return contract: a single
definition row as an associative array, or [] when none matches,
matching the getRowArray() behaviour introduced in #4464.

get_found_rows() and get_total_rows() are deliberately left alone -
they are declared across 15 models and renaming them only here would
break that shared convention.

Refs #4622

Co-authored-by: objecttothis <17935339+objecttothis@users.noreply.github.com>
2026-08-07 13:13:05 +04:00
objecttothisandTravis Garrison 3bafa07e04 fix(sales): enforce server-side authorization for price changes (#4631)
Client-side \"change_price\" flag is UI-only, not trustworthy. Compare
submitted price against current cart price server-side and require
sales_change_price grant when they differ, else reject with
not_authorized error.

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>
Co-authored-by: Travis Garrison <travis@chiraqbookstore.com>
2026-08-07 02:20:01 +04:00
2c69dc0c34 fix(config): validate theme param to prevent XSS via invalid theme (#4620)
* fix(config): validate theme param to prevent XSS via invalid theme

Add validation rule for theme field before batch save, rejecting
requests with unrecognized theme values. Add test coverage for
theme validation in postSaveGeneral.

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

* Update app/Config/Validation/OSPOSRules.php

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

---------

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>
Co-authored-by: Travis Garrison <travis@chiraqbookstore.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-08-07 02:18:44 +04:00
0a4c418690 fix: get_definition_by_name() returns single row instead of multi-dimensional array (#4452) (#4464)
Co-authored-by: jekkos <jekkos@users.noreply.github.com>
Co-authored-by: objecttothis <17935339+objecttothis@users.noreply.github.com>
2026-08-06 12:27:13 +04:00
c0abf80c86 fix: Wrap postSave() in single transaction for atomicity (#4506)
* fix: wrap postSave() in single transaction for atomicity

- Remove internal transaction from Item_taxes->save_value() to allow controller-level transaction
- Wrap entire save sequence (item, taxes, quantities, inventory, attributes) in single transaction
- Ensure all operations succeed or all fail together
- Prevents partial writes when saveItemAttributes() fails after item/tax/quantity saves succeed

Fixes #4474

* fix: Use explicit transBegin/transCommit/transRollback for atomicity

- Replace transStart/transComplete with transBegin/transCommit/transRollback
- Check all success conditions before committing
- Explicit rollback on failure

Address CodeRabbit review feedback

* refactor(items): rename postSave locals to camelCase per PSR-12

Convert snake_case variables to camelCase in postSave() and related
item-save logic to match project naming convention for new methods.

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

---------

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>
Co-authored-by: Ollama <ollama@steganos.dev>
Co-authored-by: objecttothis <17935339+objecttothis@users.noreply.github.com>
Co-authored-by: Travis Garrison <travis@chiraqbookstore.com>
2026-08-06 12:04:16 +04:00
Travis Garrison 2a8e82a050 Merge remote-tracking branch 'OpensourcePOS/master' into plugin-system-fresh 2026-08-05 12:30:42 +04:00
cdf477f823 fix(login): skip auth validation on new install to allow migration (#4609)
* fix(login): skip auth validation on new install to allow migration

On fresh installs with no DB version, login validation would fail
before migrations could run. Check MY_Migration::getCurrentVersion()
and bypass credential check when no version exists.

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

* fix(login): distinguish DB unavailable from empty install

`getCurrentVersion()` previously returned `int` with 0 used for both
\"empty database\" and \"DB connection failure\". This conflated two
distinct states, causing login to skip auth when DB was unreachable.

Changes:
- Return type widened to `?int`: null = DB unavailable, 0 = confirmed
  empty DB (new install), positive int = migrated
- Login controller now returns 503 on null (DB error) before checking
  isNewInstall
- isNewInstall gate now uses `=== 0` instead of falsy check

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

* fix(login): return 503 JSON response when DB connection unavailable

Extract getCurrentVersion() before building data array to distinguish
null (connection failure) from 0 (new install). Return early with JSON
503 when DB is unreachable instead of rendering broken view.

Update return type hint to include ResponseInterface.

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

---------

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>
Co-authored-by: Travis Garrison <travis@chiraqbookstore.com>
Co-authored-by: jekkos <jekkos@users.noreply.github.com>
2026-08-05 11:48:05 +04:00
Travis Garrison f950829a8d Merge remote-tracking branch 'OpensourcePOS/master' into plugin-system-fresh
# Conflicts:
#	AGENTS.md
2026-07-29 13:49:39 +04:00
Travis Garrison b5ce31397b feat(plugins): add user_logged_in event and return_completed event
- Fire user_logged_in event on successful login authentication
- Add return_completed event distinct from sale_completed for return mode
- Update README with new events and clarify sale_completed fires non-return only
- Import CodeIgniter\\Events\\Events in Login controller

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>
2026-07-27 23:48:15 +04:00
objecttothisandTravis Garrison eb4c3d8764 fix(giftcard): correct return type and rename getGiftcardId method (#4600)
- Rename get_giftcard_id to getGiftcardId (PSR-12 camelCase)
- Fix return type from bool to int|false
- Replace loose == with strict === comparison
- Update all call sites in Giftcards controller and Sale model

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>
Co-authored-by: Travis Garrison <travis@chiraqbookstore.com>
2026-07-24 01:46:52 +04:00
Travis Garrison 1a03706c05 Merge remote-tracking branch 'OpensourcePOS/master' into plugin-system-fresh
Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

# Conflicts:
#	app/Controllers/Sales.php
#	app/Language/ar-EG/Config.php
#	app/Language/ar-EG/Sales.php
#	app/Language/ar-LB/Config.php
#	app/Language/ar-LB/Sales.php
#	app/Language/az/Config.php
#	app/Language/az/Sales.php
#	app/Language/bg/Config.php
#	app/Language/bg/Sales.php
#	app/Language/bs/Config.php
#	app/Language/bs/Sales.php
#	app/Language/ckb/Config.php
#	app/Language/ckb/Sales.php
#	app/Language/cs/Config.php
#	app/Language/cs/Sales.php
#	app/Language/da/Config.php
#	app/Language/da/Sales.php
#	app/Language/de-CH/Config.php
#	app/Language/de-CH/Sales.php
#	app/Language/de-DE/Config.php
#	app/Language/de-DE/Sales.php
#	app/Language/el/Config.php
#	app/Language/el/Sales.php
#	app/Language/en-GB/Config.php
#	app/Language/en-GB/Sales.php
#	app/Language/en/Config.php
#	app/Language/en/Sales.php
#	app/Language/es-ES/Config.php
#	app/Language/es-ES/Sales.php
#	app/Language/es-MX/Config.php
#	app/Language/es-MX/Sales.php
#	app/Language/fa/Config.php
#	app/Language/fa/Sales.php
#	app/Language/fr/Config.php
#	app/Language/fr/Sales.php
#	app/Language/he/Config.php
#	app/Language/he/Sales.php
#	app/Language/hr-HR/Config.php
#	app/Language/hr-HR/Sales.php
#	app/Language/hu/Config.php
#	app/Language/hu/Sales.php
#	app/Language/hy/Config.php
#	app/Language/hy/Sales.php
#	app/Language/id/Config.php
#	app/Language/id/Sales.php
#	app/Language/it/Config.php
#	app/Language/it/Sales.php
#	app/Language/km/Config.php
#	app/Language/km/Sales.php
#	app/Language/lo/Config.php
#	app/Language/lo/Sales.php
#	app/Language/ml/Config.php
#	app/Language/ml/Sales.php
#	app/Language/nb/Config.php
#	app/Language/nb/Sales.php
#	app/Language/nl-BE/Config.php
#	app/Language/nl-BE/Sales.php
#	app/Language/nl-NL/Config.php
#	app/Language/nl-NL/Sales.php
#	app/Language/pl/Config.php
#	app/Language/pl/Sales.php
#	app/Language/pt-BR/Config.php
#	app/Language/pt-BR/Sales.php
#	app/Language/ro/Config.php
#	app/Language/ro/Sales.php
#	app/Language/ru/Config.php
#	app/Language/ru/Sales.php
#	app/Language/sv/Config.php
#	app/Language/sv/Sales.php
#	app/Language/sw-KE/Config.php
#	app/Language/sw-KE/Sales.php
#	app/Language/sw-TZ/Config.php
#	app/Language/sw-TZ/Sales.php
#	app/Language/ta/Config.php
#	app/Language/ta/Sales.php
#	app/Language/th/Config.php
#	app/Language/th/Sales.php
#	app/Language/tl/Config.php
#	app/Language/tl/Sales.php
#	app/Language/tr/Config.php
#	app/Language/tr/Sales.php
#	app/Language/uk/Config.php
#	app/Language/uk/Sales.php
#	app/Language/ur/Config.php
#	app/Language/ur/Sales.php
#	app/Language/vi/Config.php
#	app/Language/vi/Sales.php
#	app/Language/zh-Hans/Config.php
#	app/Language/zh-Hans/Sales.php
#	app/Language/zh-Hant/Config.php
#	app/Language/zh-Hant/Sales.php
#	app/Libraries/Sale_lib.php
#	app/Models/Sale.php
2026-07-10 14:28:45 +04:00
objecttothisandTravis Garrison 9c542efaf6 Feature: Payment reference code (#4587)
* Add `reference_code` to sale payment queries and group by statements

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

* Refactor `Sales` controller to improve payment handling readability and replace snake_case with camelCase variables

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

* Add missing translations for `Sales` language file and include new keys like `must_enter_rrn` and `reference_code`

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

* Refactor `Sales` payment handling to use camelCase and extend `addPayment` with `referenceCode` support

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

* Refactor `Sales` models, controllers, and libraries to adopt camelCase naming conventions and improve readability

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

* Add translations and updates for `must_enter_reference_code` and `reference_code` across language files and update `Sales` controller to replace `must_enter_rrn` with the new key

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

* feat(sales): add reference code input and payment type helper

- Add `get_reference_code_payment_types()` to locale_helper as single
  source of truth for card-requiring payment types
- Add reference code row to register view, shown/hidden via JS based
  on selected payment type
- Fix payment type dropdown width to 100% for consistent layout
- Add min-width to payment buttons and right-padding to button group

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

* feat(config): add payment reference code length configuration

- Add payment_reference_code_min and payment_reference_code_max fields
  to Config controller save logic
- Add translation keys for reference code length limits across all
  language files (min/max label + section header)
- Align array key formatting in Config controller for readability

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

* style(lang): normalize string delimiters to single quotes across all language files

Convert double-quoted array keys and values to single quotes in all
app/Language/*/Config.php and app/Language/*/Sales.php variants.
No translation content changed — formatting only.

Also add Localization section to AGENTS.md documenting language file
conventions for new keys and fallback behavior.

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

* feat(lang): add payment reference code length translations

Add localized strings for payment_reference_code_length_limits,
payment_reference_code_length_max_label, and
payment_reference_code_length_min_label across all supported locales.

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

* test(config,sales): add payment reference code min/max validation tests

- Add baseLocalePayload() helper in ConfigTest for postSaveLocale tests
- Add testSaveLocale_AcceptsValidReferenceCodeMinMax and related boundary tests
- Add Sale_libPaymentTest for payment reference code validation in Sale_lib

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

* fix(sales): add type-specific validation for amount_tendered

Gift card payments use amount_tendered as giftcard number (integer);
cash/other payments require decimal_locale format. Apply correct
validation rule per payment type instead of generic required.

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

* fix(lang): replace self-closing </br> with <br> in all locales

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

* fix(sales): use configurable precision in discount comparison

Replace hardcoded precision 2 with totals_decimals() when comparing
discount against item total via bccomp/bcmul, so discount validation
respects the configured decimal precision setting.

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

* fix(lang): correct Azerbaijani translations in Config.php

Replace placeholder/mismatched strings with accurate translations:
- email_mailpath, email_smtp_pass, invoice_email_message
- number_locale_invalid/required, receipt_template
- reward_configuration, right, tax_decimals, theme

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

* feat(sales): add reference code support to payment edit flow

- Add reference_code field to new payment row in sale edit form
- Persist reference_code on insert in Sale model
- Validate reference_code_new in Sales controller using configurable min/max length rules

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

* feat(lang): add Georgian (ka) language stubs for Config and Sales

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

* Match the fallback maximum reference_code length to the maximum of the field in the db

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

* Bug fixes

- Add validation of UI settings to prevent overridden values from being passed.
- Correct maximum value in JS for payment_reference_code maximum length to 40.
- Fix bug causing copy_entire_sale() to incorrectly copy the reference code and cash_adjustment

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

---------

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>
Co-authored-by: Travis Garrison <travis@chiraqbookstore.com>
2026-07-10 13:49:05 +04:00
Travis Garrison 1159d7d572 feat(plugins): add $pluginData support for events and form submissions
- Add `$pluginData` parameter to `Events::trigger` calls for key events (e.g., `sale_completed`, `customer_saved`)
- Update core views to include `data-plugin-form` attribute for plugin-field serialization
- Bundle `plugin_data_helper.js` to handle form field extraction for plugins
- Extend README.md with comprehensive documentation on using `$pluginData`
- Refactor language files to support `install` translations and streamline UI labels
- Add new view hooks for sales receipt and register UI extension

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>
2026-07-09 15:32:27 +04:00
Travis Garrison 584d38426c feat(plugins): add uninstall confirmation modal and soft-delete uninstall
- Intercept uninstall action to show confirmation modal before AJAX call
- Change uninstall to soft-delete: set installed=0 instead of purging all config
- Add deleteAllNonControlForPlugin to preserve control rows on uninstall
- Add isPluginInstalled helper to PluginManager for installed state checks
- Fix isInstalled guard to treat installed=0 as not installed

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>
2026-07-07 17:54:18 +04:00
Travis Garrison 9aec82927c refactor(models): rename snake_case methods to camelCase per PSR-12
Rename get_info, get_sale_items, get_sale_payments across Item, Sale
models and all call sites in controllers, libraries, and tests.

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>
2026-07-07 16:26:06 +04:00
Travis Garrison 00490d2ff2 Merge remote-tracking branch 'OpensourcePOS/master' into plugin-system-fresh 2026-07-07 12:00:43 +04:00
objecttothisandTravis Garrison dec736ad6c Bugfix: Fix problems with migration UI in login (#4589)
* refactor(login): rename methods and keys to camelCase, add initialization keys

- Rename snake_case methods to camelCase in MY_Migration and callers:
  is_latest() → isLatest(), migrate_to_ci4() → migrateToCI4(),
  get_latest_migration() → getLatestMigration(),
  get_current_version() → getCurrentVersion()
- Rename snake_case array keys to camelCase in Login controller:
  has_errors → hasErrors, is_new_install → isNewInstall,
  is_latest → isLatest, latest_version → latestVersion,
  gcaptcha_enabled → gcaptchaEnabled
- Add initialization_required, initialization_message, initialize keys
  to all language files to support new install flow messaging

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

* refactor(login): move auth validation before migration, reorder language keys alphabetically

- Validate credentials with login_check rule before attempting migration
- Return 401 with invalid credentials message on validation failure
- Alphabetically reorder and align language file keys across all locales
- Add new migration status keys: migrating_database, migration_complete,
  migration_complete_login, migration_complete_migrate
- Remove deprecated keys: migration_needed, migration_initializing,
  migration_running, migration_required

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>

---------

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>
Co-authored-by: Travis Garrison <travis@chiraqbookstore.com>
2026-07-07 10:35:25 +04:00
objec 5cd66d76c3 Correct the verb tense for consistency
Signed-off-by: objec <objecttothis@gmail.com>
2026-06-12 16:02:27 +04:00
objec 4ce51f0d09 Merge remote-tracking branch 'OpensourcePOS/master' into plugin-system-fresh 2026-06-11 08:40:48 +04:00
objecttothis e6388deed8 Fix overly lenient date validation (#4574)
* Fix overly lenient date validation

- In the past date validation would roll over dates that didn't exist into the next month.  Now they return an error.

Signed-off-by: objec <objecttothis@gmail.com>

* Refactor naming

- Refactor parameter
- Refactor function name.

Signed-off-by: objec <objecttothis@gmail.com>

* Add unit tests for LocaleHelper

Signed-off-by: objec <objecttothis@gmail.com>

* Remove files from being tracked.

Signed-off-by: objec <objecttothis@gmail.com>

---------

Signed-off-by: objec <objecttothis@gmail.com>
2026-06-10 23:16:25 +04:00