Commit Graph
6921 Commits
Author SHA1 Message Date
objecttothis aa3a8d80d9 Merge branch 'master' into fix/reports-auth 2026-11-09 00:47:31 +04:00
jekkosandobjecttothis 260483d436 fix(config): reject spaces in valid_path_strict to prevent command injection (#4797)
The valid_path_strict() regex previously allowed space characters,
permitting values like '/bin/sh -c id' to pass validation. When passed
to CodeIgniter's popen() sink, the shell interprets the space as an
argument separator, enabling arbitrary command execution.

Remove space from the allowed character set. Legitimate sendmail paths
(/usr/sbin/sendmail, C:\wamp64\bin\sendmail\sendmail.exe) do not
contain spaces.

Update existing tests and add regression cases for shell -c payloads.
---------

Co-authored-by: objecttothis <17935339+objecttothis@users.noreply.github.com>
2026-11-08 19:38:18 +01:00
jekkos 8e39c4e6d9 merge: resolve conflict with master — keep explicit permission map 2026-11-08 11:45:54 +00:00
jekkos 4fbfada5f4 fix: address CodeRabbit — No_access::getIndex route, add auto-route permission aliases 2026-11-07 22:33:24 +00:00
GMO 603b2c7c8f Translated using Weblate (Spanish)
Currently translated at 100.0% (47 of 47 strings)

Translation: opensourcepos/expenses
Translate-URL: https://translate.opensourcepos.org/projects/opensourcepos/expenses/es/
2026-11-07 06:51:41 +01:00
GMO 4fb2a4fc67 Translated using Weblate (Spanish)
Currently translated at 100.0% (81 of 81 strings)

Translation: opensourcepos/taxes
Translate-URL: https://translate.opensourcepos.org/projects/opensourcepos/taxes/es/
2026-11-07 06:51:40 +01:00
GMO 89b9deaf1f Translated using Weblate (Spanish)
Currently translated at 100.0% (47 of 47 strings)

Translation: opensourcepos/employees
Translate-URL: https://translate.opensourcepos.org/projects/opensourcepos/employees/es/
2026-11-07 06:51:40 +01:00
GMO 2d58fd7458 Translated using Weblate (Spanish)
Currently translated at 97.9% (144 of 147 strings)

Translation: opensourcepos/reports
Translate-URL: https://translate.opensourcepos.org/projects/opensourcepos/reports/es/
2026-11-07 06:51:40 +01:00
GMO c19d76b446 Translated using Weblate (Spanish)
Currently translated at 95.9% (141 of 147 strings)

Translation: opensourcepos/reports
Translate-URL: https://translate.opensourcepos.org/projects/opensourcepos/reports/es/
2026-11-07 06:07:57 +01:00
GMO d73c8db2ef Translated using Weblate (Spanish)
Currently translated at 100.0% (32 of 32 strings)

Translation: opensourcepos/attributes
Translate-URL: https://translate.opensourcepos.org/projects/opensourcepos/attributes/es/
2026-11-07 05:47:14 +01:00
GMO d7c5ede1f1 Translated using Weblate (Spanish)
Currently translated at 100.0% (32 of 32 strings)

Translation: opensourcepos/attributes
Translate-URL: https://translate.opensourcepos.org/projects/opensourcepos/attributes/es/
2026-11-07 05:45:18 +01:00
GMO 48f95293ab Translated using Weblate (Spanish)
Currently translated at 100.0% (32 of 32 strings)

Translation: opensourcepos/attributes
Translate-URL: https://translate.opensourcepos.org/projects/opensourcepos/attributes/es/
2026-11-07 05:45:01 +01:00
GMO 63d9d3359f Translated using Weblate (Spanish)
Currently translated at 100.0% (32 of 32 strings)

Translation: opensourcepos/attributes
Translate-URL: https://translate.opensourcepos.org/projects/opensourcepos/attributes/es/
2026-11-07 05:43:23 +01:00
GMO 06269175e5 Translated using Weblate (Spanish)
Currently translated at 96.8% (31 of 32 strings)

Translation: opensourcepos/attributes
Translate-URL: https://translate.opensourcepos.org/projects/opensourcepos/attributes/es/
2026-11-07 05:42:33 +01:00
BudsieBuds 9a304c0183 refactor(i18n): standardize language catalogs, enforce dynamic fallback, and add test coverage
- Replace untranslated English strings with empty fallback ('') across all non-English catalogs and sort keys alphabetically (closes #4783, closes #4660)
- Restore missing Sales.no_sales_to_display in English while preserving existing translations (closes #4794, references #4637, #4795)
- Register Georgian (ka) and Swahili (sw-KE, sw-TZ) locales, normalize Croatian to hr per BCP 47, and fix datepicker long month keys (partially addresses #4476, references #3891)
- Hardcode invariant brand constants (OSPOS, website, copyrights) in login/footer views, remove from catalogs, and standardize Latin OSPOS branding
- Fix English catalog typos and maintain consistent British English variants in en-GB
- Add LanguageFilesTest with 4,418 automated tests for syntax, key parity, sorting, and bidirectional locale registrations
- Update AGENTS.md with localization guidelines and development best practices
2026-11-04 15:23:50 +01:00
BudsieBuds 72a75152ad fix(security): harden container permissions, validate platform reqs, and use rolling base image (#4789)
- Dockerfile: use rolling php:8.2-apache with expose_php = Off.
- Dockerfile: validate runtime against composer.lock at build boundary via BuildKit composer check-platform-reqs.
- Dockerfile: enforce least-privilege permissions (root:www-data 0640 files, 0750 dirs), restricting write access to writable/ and public/uploads.
- dockerignore: include composer.json and composer.lock in build context for platform validation.
- CI: remove redundant bash version checks across workflows; enforce fresh upstream base via pull: true in build-release.yml.
- Docs & Changelog: remove security advisory identifiers while preserving minimum patch levels.
2026-11-03 20:06:39 +01:00
jekkos 7d05be9473 fix(reports): fix permission map and default-deny exclusions
1. Fix get_detailed_sales/get_detailed_receivings -> getGet_detailed_sales/
   getGet_detailed_receivings (actual normalized method names)
2. Add detailedItemSales (camelCase) to permission map
3. Add all helper/UI methods (date_input, *_input, get_payment_type, etc.)
   to default-deny exclusion list so they don't get blocked
2026-11-02 23:45:33 +00:00
jekkos 3a1ddc69f8 fix(receivings): gate per-record endpoints behind reports_receivings grant (#4798)
getEdit, postDelete, getReceipt, and postSave all accept a
user-controlled receiving_id and previously only checked the class-level
receivings module grant. This allowed any employee with basic receiving
permissions to read, modify, or delete historical receiving records
belonging to other employees.

Add reports_receivings permission checks to all four per-record
endpoints, following the same pattern as Sales (PR #4627).
2026-11-03 00:00:43 +01:00
jekkos 7d64363124 fix(reports): use no_access/reports/... route pattern for access-denied redirects
Match the codebase convention (Secure_Controller, Employees, Sales)
which uses no_access/{module_id}/{submodule_id} rather than
no_access/index/{module_id}.
2026-11-02 21:57:53 +00:00
jekkos d33578d650 Merge remote-tracking branch 'origin/master' into HEAD 2026-11-02 20:13:39 +00:00
jekkosandcoderabbitai[bot] fa0fa534c3 fix(sales): allow gift card credit for returns + surface errors (#4781)
* fix(sales): allow gift card credit for returns + surface errors

- Add incrementGiftcardValue() to Giftcard model for crediting cards
- In Sale::save_value(), use incrementGiftcardValue() for returns
  with negative gift card payments (credits the card)
- Add error_message rendering to register view so failed completions
  show the error instead of silently reloading

Fixes #4779

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-11-02 21:03:52 +01:00
jekkos 86d3e8a609 fix(migrations): prevent concurrent runs with pessimistic DB lock (#4769)
* fix(migrations): prevent concurrent runs with pessimistic DB lock

Adds GET_LOCK/RELEASE_LOCK around the migration sequence so a second
request (page refresh, double-click, supervisor retry) cannot re-trigger
the same migration while the first is still executing. Also adds
ignore_user_abort(true) so a client disconnect does not tear down the
PHP process mid-migration.

Fixes #4763
2026-11-02 15:21:02 +01:00
jekkos f8e3ac8771 fix(reports): address CodeRabbit review comments
1. Add get_detailed_sales and get_detailed_receivings to the permission
   map (auto-routed detail-row endpoints that were missing).
2. Add default-deny for unmapped report names to prevent open access.
3. Fix no_access redirect URIs to use no_access/index/... to match
   the defined routes.
2026-11-02 15:20:05 +01:00
jekkos a163fd0b6a fix(reports): replace regex heuristic with explicit report permission map
The Reports controller constructor derived the required permission from
the URL segment using a two-step regex heuristic that truncated multi-word
report names. For example, summary_expenses_categories was reduced to
'categories', enforcing reports_categories instead of the correct
reports_expenses_categories. Similarly, summary_sales_taxes was reduced to
'taxes', enforcing reports_taxes instead of reports_sales_taxes.

This inverted authorization: employees with the wrong sibling grant were
allowed into restricted financial reports, while employees with the correct
grant were denied.

Replace the heuristic with an explicit route-to-permission map covering all
20 report types. Normalize the report name by stripping the graphical_
prefix and _graph/_row suffixes before lookup.
2026-11-02 15:20:05 +01:00
objecttothis f5233b20e6 refactor(cashups): extract value object and focused methods in Cashups controller (#4786)
Introduce a CashupAmounts transfer object and break down large Cashups
controller methods into focused private helpers. Includes a breaking
change to constructor parameter order.

- Add CashupAmounts (TransferObjects/Cashups) to bundle open/transfer/
  closed cash, due, card, and check amounts; reorder constructor params
  to cash-before-due sequence
- Replace positional _calculate_total() with calculateTotal(CashupAmounts),
  eliminating argument-order errors
- Decompose getView() into buildEmployeeLookup(), normalizeCashupInfo(),
  initializeNewCashup(), isPendingClose(), closeCashup(),
  buildReportDateRangeInputs(), applyPaymentTotals(), subtractCashExpenses()
- Decompose postSave() with parseCashupDate() and getCashupAmountsFromPost()
  helpers; update both CashupAmounts call sites to new param order
- Rename snake_case vars/params to camelCase throughout Cashups controller
- Drop stale TODO comments in OSPOS.php and Cashups::getIndex filter block
- Scope escape => false to cashup_headers() note column rather than a
  broad regex exclusion in transform_headers()
- Add CashupFixtureTrait and ExpenseFixtureTrait under tests/Support;
  tighten cashup view test to assertSeeInField on specific form fields

BREAKING CHANGE: CashupAmounts constructor positional argument order
changed — closedAmountCash now precedes closedAmountDue; any callers
passing positional args must swap those two arguments.

Signed-off-by: objecttothis <17935339+objecttothis@users.noreply.github.com>
2026-11-02 03:59:07 +04:00
jekkos 90495a508f fix(sales): set discount_type on cart lines in apply_customer_discount (#4780)
When a customer is selected after items are already in the cart,
apply_customer_discount() set the discount value and recomputed totals
using the customer's discount type, but never updated the line's
discount_type field. get_totals() later recomputes from the line's
discount_type (still the store default), producing wrong totals.

Fixes #4778
2026-10-29 23:31:35 +01:00
BudsieBuds 386f976dcc feat(php): add PHP 8.5 support, resolve deprecations, and optimize CI workflows (#4776)
- Extend composer.json and composer.lock PHP requirement to include PHP 8.5
- Add PHP 8.5 to test matrix in phpunit.yml and main.yml workflows
- Modernize php-linter.yml to use setup-php@v2 with parallel matrix for PHP 8.2–8.5
- Propagate PHP 8.2 lint failures via shell loop in find -exec + while retaining batching for 8.3–8.5
- Add permissions: contents: read and cancel-in-progress concurrency to php-linter.yml
- Remove obsolete Node.js and npm install steps from phpunit.yml
- Update phpunit.yml runner to ubuntu-latest and composer cache action to v4
- Address PHP 8.5 deprecations and syntax:
  - Replace deprecated switch-case semicolon with standard colon in app/Config/Database.php
  - Replace non-standard (double) cast with (float) in app/Libraries/Token_lib.php
  - Remove obsolete curl_close() calls in app/Config/Validation/OSPOSRules.php and app/Libraries/Mailchimp_lib.php
- Update INSTALL.md, workflows README, and bug report issue template to include PHP 8.5
2026-10-28 15:58:14 +04:00
jekkos 8a8736ad0d fix(i18n): migrate powered_by to named placeholders across all locales (#4661, #4727)
Replace hardcoded string concatenation of the app name and version in the
document <title> with named-placeholder interpolation, consistent with the
existing Common.migration_needed pattern.

- header.php passes ['OSPOS', application_version] as the lang() array
  argument instead of concatenating values around the translation
- All 46 Common.php locale files convert powered_by to a named-placeholder
  template, preserving current rendered output while allowing each locale
  to reorder the app name and version as needed
- hy, ml, nb, and ur leave powered_by empty so MY_Language::getLine()
  falls back to English rather than rendering bare placeholder strings
- de-CH powered_by translated to German
- Codify named-placeholder + ICU MessageFormat as the localization standard
  in the agents documentation
2026-10-28 14:10:27 +04:00
objecttothisandClaude Sonnet 5 600417bfdf refactor: extract PSR-12 renames and independent bugfixes from plugin system PR (#4784)
build/refactor/fix: tighten tooling, rename accessors to camelCase, and correct model bugs

* build: tighten gitignore, lockfile, and script config
- gitignore /build and /.php-cs-fixer.cache
- allow core-js@3.50.0 postinstall script in package.json

* refactor: rename snake_case data accessors to camelCase
Rename get_info, get_multiple_info, get_sale_items, get_sale_payments,
and save_customer across Customer, Employee, Item, Sale, and Supplier
models to PSR-12 camelCase, updating every call site in controllers,
libraries, and views.

* test: update calls to match renamed camelCase accessors
Update HomeTest, ItemsControllerTest, and ItemTest to call
getInfo()/getMultipleInfo() instead of the old snake_case model methods.

* fix: correct independent bugs in Item, Item_taxes, and Attribute models
- Item::delete()/delete_list(): let $this->db->transStatus() alone decide
  success instead of accumulating a bitwise-AND over intermediate results,
  which could report failure (and trigger error toasts) even when the
  delete had actually completed
- Item_taxes: correct $table property from 'item_taxes' to 'items_taxes'
- Attribute::getDefinitionNames(): pass an array to
  QueryBuilder->whereNotIn(), which requires one

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-28 13:00:30 +04:00
richardmillesandobjecttothis b07f5e3ebb chore(i18n): remove unused Sales language keys (#4637)
Drop verified-unused keys from Sales.php across all locales to reduce
translation burden. Retained:
- invoice_type_* keys (referenced by Sale_lib)
- rewards_package key (built dynamically by register.php)

Fixes #4586

Co-authored-by: objecttothis <17935339+objecttothis@users.noreply.github.com>
2026-10-27 15:24:21 +04:00
objecttothisandClaude Sonnet 4.6 74e825ba6f feat(stock-locations): add default location and sort order support (#4643)
Refactor Stock_location model to camelCase naming and introduce
is_default and sort_order fields with full migration, UI, and
item-sync support. Multiple follow-up fixes consolidated.

Database
- Add is_default (TINYINT, backfilled to oldest non-deleted row) and
  sort_order (nullable unsigned TINYINT unique, dense 0..N-1) columns
  via new migration; MySQL 5.7-compatible backfill using user variables
  instead of ROW_NUMBER(); down() drops both columns

Model
- Rename show_locations → showLocations (camelCase convention)
- Add getDefaultLocationId(), setDefaultLocation(), saveSortOrder();
  validate location existence before setting as default
- delete() compacts sort_order via two-pass CASE update to avoid
  collisions after prior reorders
- Atomic sort_order assignment via explicit SQL COUNT query

Controller / Config
- Modularize save logic into sanitizeSubmittedLocations(),
  deleteRemovedLocations(), saveSubmittedLocations(),
  saveLocationOrderAndDefault(); wrap in single transaction
- Support stock_location_new inputs for new-row creation
- Resolve new-* ID placeholders to real IDs post-save
- Abort loop on save failure; default missing POST arrays to []
- Validate that at least one location array is non-empty

Views
- Add Default column with center-aligned radio buttons; disable for
  unsaved rows; use attr() instead of data() for location IDs
- Escape dynamic location IDs against XSS in all affected partials
- form_count_details.php: fall back to default location when current
  is undefined; guard quantity input similarly
- Add aria-label to default radio inputs

Libraries / Items
- Widen Item_lib::get_item_location() and get_stock_destination() to
  nullable return types
- Guard null location_id in Items::getRow() and getGenerateBarcodes()
- Item::get_multiple_info(): accept ?int $location_id; short-circuit
  with where('item_id', -1) instead of always-false where('1','0')

i18n
- Add stock_location_default, stock_location_default_header,
  stock_location_name_header across all locale files

Tests
- Stock_locationTest: getDefaultLocationId, setDefaultLocation,
  saveSortOrder, delete compaction, saveValue; rename to
  testMethod_Scenario convention
- Add ConfigControllerTest / StockLocationsControllerTest with
  AdminAuthTrait covering CRUD, reorder, default assignment, and
  malformed/empty/blank payload validation
- DatabaseBootstrapTrait-based setup for ItemTest and Stock_locationTest

Signed-off-by: objecttothis <17935339+objecttothis@users.noreply.github.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-10-27 14:36:34 +04:00
objecttothis 9253cfe836 test(config): add coverage for email/locale/general security fixes (#4782)
Add ConfigControllerTest covering validation added to protect
config save endpoints from injection and bypass attacks.

- Mailpath validation: accept legit sendmail paths (with dots,
  trailing args), require mailpath when protocol is sendmail,
  reject command injection via shell metacharacters (e.g.
  semicolon) for both sendmail and non-sendmail protocols
- postSaveLocale: verify payment_reference_code_min/max bounds
  (reject non-numeric, zero, negative, max < min; accept valid
  and min==max cases)
- postSaveLocale: verify language_code allow-list enforcement
  against path traversal (forward/backslash variants), unknown
  languages, and case-mismatched codes (GHSA-related hardening)
- postSaveGeneral: begin coverage of theme validation with a
  reusable base payload builder

Signed-off-by: objecttothis <17935339+objecttothis@users.noreply.github.com>
2026-10-26 18:12:52 +04:00
jekkosandcoderabbitai[bot] 42025e7817 fix(items): allow empty tax rows on item save (#4774)
* fix(items): allow empty tax rows on item save

- Remove 'required' from tax_names.* rule; use permit_empty instead
- Add permit_empty before nonNegativeDecimal for tax_percents.*
- Add empty-string guard in nonNegativeDecimal() rule

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-10-25 22:50:33 +01:00
khao_lek 1f5c8de4a8 Translated using Weblate (Thai)
Currently translated at 100.0% (238 of 238 strings)

Translation: opensourcepos/sales
Translate-URL: https://translate.opensourcepos.org/projects/opensourcepos/sales/th/
2026-10-25 21:33:52 +01:00
khao_lek e2203e1580 Translated using Weblate (Thai)
Currently translated at 100.0% (80 of 80 strings)

Translation: opensourcepos/taxes
Translate-URL: https://translate.opensourcepos.org/projects/opensourcepos/taxes/th/
2026-10-25 21:33:51 +01:00
khao_lek fe97de45a6 Translated using Weblate (Thai)
Currently translated at 100.0% (10 of 10 strings)

Translation: opensourcepos/error
Translate-URL: https://translate.opensourcepos.org/projects/opensourcepos/error/th/
2026-10-25 21:33:51 +01:00
khao_lek 0a1cf0c3fb Translated using Weblate (Thai)
Currently translated at 100.0% (147 of 147 strings)

Translation: opensourcepos/reports
Translate-URL: https://translate.opensourcepos.org/projects/opensourcepos/reports/th/
2026-10-25 21:33:51 +01:00
khao_lek d4b96f5928 Translated using Weblate (Thai)
Currently translated at 100.0% (32 of 32 strings)

Translation: opensourcepos/attributes
Translate-URL: https://translate.opensourcepos.org/projects/opensourcepos/attributes/th/
2026-10-25 14:17:45 +01:00
khao_lek 84be6a3369 Translated using Weblate (Thai)
Currently translated at 100.0% (53 of 53 strings)

Translation: opensourcepos/customers
Translate-URL: https://translate.opensourcepos.org/projects/opensourcepos/customers/th/
2026-10-25 14:17:45 +01:00
khao_lek 4be632dac6 Translated using Weblate (Thai)
Currently translated at 100.0% (341 of 341 strings)

Translation: opensourcepos/config
Translate-URL: https://translate.opensourcepos.org/projects/opensourcepos/config/th/
2026-10-25 14:17:44 +01:00
khao_lek 1b988b0346 Translated using Weblate (Thai)
Currently translated at 99.3% (146 of 147 strings)

Translation: opensourcepos/reports
Translate-URL: https://translate.opensourcepos.org/projects/opensourcepos/reports/th/
2026-10-25 14:17:43 +01:00
khao_lek 91e49cf049 Translated using Weblate (Thai)
Currently translated at 80.0% (8 of 10 strings)

Translation: opensourcepos/error
Translate-URL: https://translate.opensourcepos.org/projects/opensourcepos/error/th/
2026-10-25 14:17:43 +01:00
khao_lek b063373d23 Translated using Weblate (Thai)
Currently translated at 96.6% (230 of 238 strings)

Translation: opensourcepos/sales
Translate-URL: https://translate.opensourcepos.org/projects/opensourcepos/sales/th/
2026-10-25 14:17:42 +01:00
khao_lek 28d99c18cd Translated using Weblate (Thai)
Currently translated at 100.0% (121 of 121 strings)

Translation: opensourcepos/items
Translate-URL: https://translate.opensourcepos.org/projects/opensourcepos/items/th/
2026-10-25 14:17:42 +01:00
khao_lek 8de93daf2e Translated using Weblate (Thai)
Currently translated at 100.0% (27 of 27 strings)

Translation: opensourcepos/login
Translate-URL: https://translate.opensourcepos.org/projects/opensourcepos/login/th/
2026-10-25 14:17:41 +01:00
JisooMx 82468e6665 Translated using Weblate (Spanish (Mexico))
Currently translated at 77.9% (53 of 68 strings)

Translation: opensourcepos/giftcards
Translate-URL: https://translate.opensourcepos.org/projects/opensourcepos/giftcards/es_MX/
2026-10-25 02:33:44 +01:00
JisooMx 317d1431e5 Translated using Weblate (Spanish (Mexico))
Currently translated at 99.7% (340 of 341 strings)

Translation: opensourcepos/config
Translate-URL: https://translate.opensourcepos.org/projects/opensourcepos/config/es_MX/
2026-10-25 02:33:44 +01:00
JisooMx 08ecb15f0f Translated using Weblate (Spanish)
Currently translated at 100.0% (27 of 27 strings)

Translation: opensourcepos/login
Translate-URL: https://translate.opensourcepos.org/projects/opensourcepos/login/es/
2026-10-25 02:33:44 +01:00
JisooMx b3a95840c3 Translated using Weblate (Spanish (Mexico))
Currently translated at 90.3% (215 of 238 strings)

Translation: opensourcepos/sales
Translate-URL: https://translate.opensourcepos.org/projects/opensourcepos/sales/es_MX/
2026-10-25 02:33:43 +01:00
JisooMx d71e7df04e Translated using Weblate (Spanish (Mexico))
Currently translated at 99.3% (146 of 147 strings)

Translation: opensourcepos/reports
Translate-URL: https://translate.opensourcepos.org/projects/opensourcepos/reports/es_MX/
2026-10-25 00:41:50 +02:00