Commit Graph
151 Commits
Author SHA1 Message Date
objecttothis 459610cded Merge branch 'master' into plugin-system-fresh 2026-08-31 16:18:59 +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 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 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
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
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
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
objecttothisandTravis Garrison 2fbe746c95 style(models): normalize quote style in SQL GROUP_CONCAT expression (#4608)
Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>
Co-authored-by: Travis Garrison <travis@chiraqbookstore.com>
2026-07-30 15:21:29 +04:00
Travis Garrison 59aa7f1ec2 refactor(plugins): enforce plugin id naming convention and auto-cleanup on uninstall
- Add `idMatchesPluginConvention()` guard to `BasePlugin` — rejects
  module/permission ids that don't match `{plugin_id}` or `{plugin_id}_*`
- Add `unregisterPluginModules()` to `PluginManager` — called automatically
  on uninstall; removes all modules and sub-permissions by convention prefix
- Plugins no longer need to call `unregisterModule()` in `uninstall()`
- Update README to document enforced naming convention, auto-cleanup
  behavior, and corrected language key / file location guidance

Signed-off-by: Travis Garrison <travis@chiraqbookstore.com>
2026-07-29 17:48:51 +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
objecttothisandTravis Garrison 231e716c14 bugfix(reports): crash on detailed sales report when sale has multiple payments with reference codes (#4599)
* fix(sales): aggregate reference codes for multi-payment sales

Use GROUP_CONCAT to combine multiple payment reference codes into a
single comma-separated value, and group only by sale_id to correctly
handle sales with multiple payment records.

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

* docs(agents): update AGENTS.md with expanded coding standards

- Clarify PSR-12 enforcement via PHP-CS-Fixer config reference
- Bump minimum PHP version requirement from 8.1 to 8.2
- Add JavaScript const/let/var convention rule
- Reorganize coding standards section for clarity

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

* fix(sales): exclude empty strings from payment reference code aggregation

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-28 14:23:54 +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
objec 150cdfb2b8 Add plugin migration system
- Add `runPendingMigrations()` to `PluginManager` for executing plugin-specific migrations.
- Create `PluginMigrationModel` for managing plugin migration versions.
- Add migration for creating the `plugin_migrations` table to track migration states.

Signed-off-by: objec <objecttothis@gmail.com>
2026-06-26 16:19:04 +04:00
objec bf6433af3b Supplier model:
- Add optional columns array to allow functions to optionally select a subset of columns for faster queries.

Signed-off-by: objec <objecttothis@gmail.com>
2026-06-11 23:05:24 +04:00
objec 7bd9d2822a Stock Location changes
- Add TODO for pending refactor
- Add function to getUndeleted Stock Locations that don't pull in grants, people and permissions for Plugin use.
- Remove unused function that was being called by Plugins but was inefficient.

Signed-off-by: objec <objecttothis@gmail.com>
2026-06-11 23:03:51 +04:00
objec c98d2aeaec Function changes needed by Plugins
- Add getAllItemIds() to Item model
- Add optional parameter to getItems() to allow specific columns instead of all columns
- Make transactions be the authority on whether delete() succeeds to fix a bug with deletes finishing but error toast notifications.

Signed-off-by: objec <objecttothis@gmail.com>
2026-06-11 20:57:29 +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
objec f39e5ece94 Eliminate sales and receivings from attribute search
Signed-off-by: objec <objecttothis@gmail.com>
2026-06-10 14:46:37 +04:00
objec 6dea8ed710 Merge remote-tracking branch 'OpensourcePOS/master' into plugin-system-fresh
# Conflicts:
#	app/Controllers/Customers.php
2026-06-10 14:44:11 +04:00
jekkosandOllama 4173d7f350 fix: Allow searching by Sale ID in Takings/Daily Sales view (#4569)
When searching in the Takings view, entering a plain Sale ID (like '123')
did not return any results. The search only worked with customer names
or with the 'POS 123' format.

The issue was that is_valid_receipt() only recognized 'POS ####' format
or invoice numbers, so plain numeric Sale IDs fell through to the
customer name search branch which doesn't search sale_id.

This fix adds sale_id to the search conditions when the search term is
numeric (ctype_digit check), allowing direct Sale ID searches.

Fixes #4567

Co-authored-by: Ollama <ollama@steganos.dev>
2026-06-04 10:09:35 +02:00
objec 84b75825e7 Formatting and add item deletion event trigger
Signed-off-by: objec <objecttothis@gmail.com>
2026-05-29 16:34:22 +04:00
objecttothis 93713f8e4b Merge branch 'master' into plugin-system-fresh 2026-05-22 02:23:52 +04:00
objecttothis b7384296c1 Bugfix: Sale search in register not handling trailing space properly (#4557)
* Fix is_valid_receipt method bug

Strings submitted with a trailing space and no number caused an unhandled exception because Sale::exists() expects an int but a string was passed to it.

- Add guards
- Minor PSR refactor

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

* Address review comments

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

---------

Signed-off-by: objec <objecttothis@gmail.com>
2026-05-22 01:43:24 +04:00
objec 139f754a07 Refactor manage plugin configuration and settings
- Add column to indicate control setting (installed, enabled).
- Add column to indicate plugin.
- Rework business logic to read the status properly.
- Renamed the migration to properly reflect which version it's released in.

Signed-off-by: objec <objecttothis@gmail.com>
2026-05-20 17:43:42 +04:00
objec 01172fc522 Plugin related functions
- getItems() gets Item data from the item table for an array of item ids.
- getAttributeValuesBulk() front loads attribute values for an array of items.

Signed-off-by: objec <objecttothis@gmail.com>
2026-05-20 17:07:17 +04:00
objecttothis df24ef5193 Merge branch 'master' into plugin-system-fresh 2026-05-18 16:25:10 +04:00
objec 1c2112a78b Add model functions needed for plugins
- getDateAdded and getDatesAdded in Inventory.php
- GetDistinctCategories in Item.php
- GetBulkItemQuantities in Item_quantity.php
- GetBulkInfo in Item_taxes.php
- GetStockLocationsByItem in Stock_location.php

Signed-off-by: objec <objecttothis@gmail.com>
2026-05-18 16:15:24 +04:00
093ec7fb13 fix: validate attributeId > 0 in saveAttributeLink() (#4508)
- Add early validation to reject attributeId <= 0
- Ensure consistent handling of invalid attribute_id in INSERT/UPDATE paths
- Prevent foreign key constraint violations from invalid attribute references

Fixes #4460

Co-authored-by: Ollama <ollama@steganos.dev>
Co-authored-by: objecttothis <17935339+objecttothis@users.noreply.github.com>
2026-05-18 14:13:20 +02:00
objec ad097adccd Refactor get_info to getInfo
Signed-off-by: objec <objecttothis@gmail.com>
2026-05-18 16:12:38 +04:00
objec 5d608ec873 Refactoring and bugfixes
- Pass an array to QueryBuilder->whereNotIn()
- Refactor function names for PSR compliance
- Add explanatory PHPdocs and corrections
- Correct bug with items_taxes model
- Refactor local variables for PSR compliance

Signed-off-by: objec <objecttothis@gmail.com>
2026-05-18 16:06:23 +04:00
jekkosandOllama 2f51c4ef52 fix(security): SQL injection and path traversal vulnerabilities (#4539)
Security fixes for two vulnerabilities:

1. SQL Injection in Summary Sales Taxes Report (GHSA-5j9m-2f98-cjqw)
   - Fixed unsanitized user input concatenation in getData() method
   - Applied proper escaping using $this->db->escape() for start_date/end_date
   - Consistent with existing _where() method implementation

2. Path Traversal in Receipt Template (GHSA-h6wm-fhw2-m3q3)
   - Added ALLOWED_RECEIPT_TEMPLATES whitelist constant
   - Added isValidReceiptTemplate() validation method
   - Validate receipt_template before saving in Config controller
   - Validate receipt_template before rendering in receipt view
   - Default to 'receipt_default' for invalid values
   - Consistent with invoice_type fix pattern (commit 31d25e06d)

Affected files:
- app/Models/Reports/Summary_sales_taxes.php
- app/Libraries/Sale_lib.php
- app/Controllers/Config.php
- app/Views/sales/receipt.php

Co-authored-by: Ollama <ollama@steganos.dev>
2026-05-15 23:10:04 +02:00
90c981b6b7 feat: Bank transfer and wallet payment option added #4540 (#4547)
---------

Co-authored-by: Lotussoft Youngtech <lotussoftyoungtech@gmail.com>
Co-authored-by: objecttothis <17935339+objecttothis@users.noreply.github.com>
2026-05-15 20:50:34 +02:00
objec cd91ac3ff3 Fix bugs
- Add missing `MailchimpPlugin.` prefix to lang() calls.
- Do not subscribe customer if consent is not true.
- Escape output in tabular_helper.php
- Removed testConnection() as unneeded code
- Fix activity count logic
- Whitelist Sort Column Headers for Plugins.php
- Store encrypted API key as base64 instead of raw binary to prevent truncation
- Rollback on batchSave partial failure.
- Remove dead code.
- Disable plugin before uninstalling it.
- Fix getPluginSettings() internal key leak
- Add action column to plugin headers function
- Automatically add grant to all admins in case person_id 1 is not active

Signed-off-by: objec <objecttothis@gmail.com>
2026-05-05 15:06:04 +04:00
objec 478934321d Merge remote-tracking branch 'origin/master' into plugin-system-fresh
# Conflicts:
#	app/Language/th/Sales.php
2026-05-05 13:09:43 +04:00
objec 43bee7bfe4 Refactor save_customer method to saveCustomer for PSR compliance
Signed-off-by: objec <objecttothis@gmail.com>
2026-04-30 14:21:34 +04:00
objec 4246a915c4 - Correct README.md reference to views and information about renderView()
- Fix the output of pluginContent in the pluginHelper
- Register view injection events
- Correct the parameter type in getMailchimpViewData
- Correct the statusOptions creation business logic
- Removed unnecessary view injection point
- Corrected which variable was passed to the customer_saved event
- Assigned $customer_data['person_id'] on customer update
- Added renderView() function to BasePlugin.php

Signed-off-by: objec <objecttothis@gmail.com>
2026-04-30 14:11:54 +04:00
jekkosandOllama f1c6fe2981 fix: Catch mysqli_sql_exception in DB fallback handlers for fresh Docker installs (#4525)
* fix: Catch mysqli_sql_exception in DB fallback handlers for fresh Docker installs

On a fresh Docker install with an empty database, the ospos_sessions
table doesn't exist yet. The CSRF filter triggers session initialization
before the login/migration page can be reached.

The existing code in Session.php, OSPOS.php, and MY_Migration.php
catches DatabaseException, but the MySQLi driver throws
mysqli_sql_exception (which extends RuntimeException, not
DatabaseException) when the table doesn't exist. This causes an
unhandled exception resulting in HTTP 500.

Fix: Change all three catch blocks from  to
 so that mysqli_sql_exception and any other unexpected
database errors are caught, allowing the app to fall back gracefully:

- Session.php: Falls back to FileHandler so sessions work without DB
- OSPOS.php: Falls back to empty settings so config loads work
- MY_Migration.php: Falls back to version 0 / false so the migration
  check passes gracefully

This allows the login page with migration UI to be served on first
access, so the initial schema migration can run.

Fixes #4524
---------

Co-authored-by: Ollama <ollama@steganos.dev>
2026-04-22 21:13:52 +02:00
jekkosandOllama e602eddb47 fix: Scope orWhere clauses in Item::exists() and Item::get_item_id() (#4520)
In PR #4250 (commit 29c3c55), orWhere was added to match items by
either item_id or item_number, but the OR condition was not wrapped
in groupStart()/groupEnd(). This causes:

1. Wrong SQL semantics: generates
   WHERE item_id = ? OR item_number = ? AND deleted = 0
   instead of
   WHERE (item_id = ? OR item_number = ?) AND deleted = 0
   Due to AND binding tighter than OR, the deleted filter only applies
   to the item_number branch, allowing deleted items to match via item_id.

2. Performance: the unscoped OR causes MySQL to bypass the item_id
   primary key index and fall back to full table scans when item_number
   is a string column compared against a numeric parameter.

Both exists() and get_item_id() are fixed by wrapping the OR
conditions in groupStart()/groupEnd() for proper parenthesization.

Co-authored-by: Ollama <ollama@steganos.dev>
2026-04-20 06:22:42 +00:00
objec d9d93e0d9d Mailchimp PLugin
- Corrected grammar in PHPdocs
- PSR refactoring of local variables and code blocks
- Moved MailchimpPlugin.php to its own plugin folder
- Refactored out mailchimp code to the plugin
- Created customer_loaded event trigger

Signed-off-by: objec <objecttothis@gmail.com>
2026-04-16 17:29:46 +04:00