Commit Graph

6852 Commits

Author SHA1 Message Date
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>
unstable
2026-06-10 23:16:25 +04:00
objec
1153666ae4 Correct Event trigger to send array, not nested array
Signed-off-by: objec <objecttothis@gmail.com>
2026-06-10 15:20:11 +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
jekkos
84aeeb52fe fix(security): Fix DOMPDF RCE and customer email sanitization (#4568)
* fix(security): Fix DOMPDF RCE and customer email sanitization

- Disable isPhpEnabled in DOMPDF to prevent RCE via embedded PHP in HTML
- Disable isRemoteEnabled to prevent SSRF attacks
- Add email validation and sanitization in CSV import (FILTER_SANITIZE_EMAIL, FILTER_VALIDATE_EMAIL)
- Reject invalid email formats during customer import

* fix(security): Escape email addresses in mailto() to prevent XSS

Email columns in bootstrap tables had escaping disabled (line 52) and
mailto() function doesn't escape its parameters. This fix escapes email
addresses before passing to mailto() in:
- get_person_data_row() (employees)
- get_customer_data_row() (customers)
- get_supplier_data_row() (suppliers)

Attack vector: Malicious email via CSV import renders XSS in table view.

* test(security): Add tests for customer CSV import email validation

Tests cover:
- Valid email acceptance
- Invalid email rejection with row-specific error
- XSS payload sanitization in email field
- Mixed valid/invalid email handling
- Email with special characters sanitization

Verifies fixes for customer email import vulnerability.

* fix(security): Allow empty email addresses in customer import

- Empty emails are now allowed (customers may not have email addresses)
- Validation only applies when email is non-empty
- Added test case for empty email acceptance

This fixes a regression where FILTER_VALIDATE_EMAIL rejected empty
strings, breaking imports for customers without email addresses.

---------

Co-authored-by: Ollama <ollama@steganos.dev>
2026-06-09 17:58:52 +02:00
jekkos
4d6ebbafdd fix: tax rate inputs blank with comma-decimal locales (#4555)
* fix: tax rate inputs blank with comma-decimal locales

The to_tax_decimals() function returns locale-formatted values
(e.g. "18,00" for comma-decimal locales like fr_FR, de_DE).
Browsers reject comma-decimal values in <input type="number">
and render the field blank.

Use raw float value instead - PHP serializes floats with period
decimal regardless of locale. The parse_tax() on the save side
already handles locale-aware parsing, so round-tripping works
correctly.

Fixes #4553
Regression from commit 42ba39d29

* fix: tax rate input locale handling - save path

The display fix (using (float) instead of to_tax_decimals()) was
correct but incomplete. The save path in Config.php also needed
fixing because parse_tax() misinterprets dot-decimal values from
type="number" inputs when locale uses comma as decimal separator.

Root cause: Browsers submit type="number" inputs as dot-decimal
(e.g., "5.5") regardless of locale. With comma-decimal locales
like de_DE, parse_tax() treats the dot as thousands separator,
causing 5.5 to be saved as 5.

Fix: Replace parse_tax() with direct (float) cast for these
inputs since type="number" already guarantees dot-decimal format.

Includes tests for tax rate handling with various decimal values.

Fixes #4553

* revert: remove type=number from tax rate inputs

Resolution from PR #4555 review: Revert to text inputs for locale-specific
tax rate fields.

The type='number' attribute was added in commit 42ba39d29, but it caused
issues with locale-specific decimal separators. Browsers submit type='number'
inputs as dot-decimal regardless of locale, which breaks comma-decimal locales.

Solution: Revert to text inputs which use to_tax_decimals() for display
and parse_tax() for saving, correctly handling locale-specific formatting.

Changes:
- tax_config.php: Remove type='number', step, min, max attributes
- tax_config.php: Restore to_tax_decimals() for value display
- Config.php: Restore parse_tax() for tax rate parsing
- ConfigTest.php: Remove tests added for the type='number' approach

Fixes #4553

---------

Co-authored-by: Ollama <ollama@steganos.dev>
2026-06-06 22:50:51 +02:00
objecttothis
450c0866b5 Add Guards to Database Migration (#4571)
* Properly replace key in config file when encryption key is updated

This fixes a break caused if there is a commented out key in the .env. It's a more robust replacement.

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

* Guard against dropping constraint that doesn't exist

- Updated wording in migration_helper.php PHPdoc
- Use migration_helper function to drop key which only drops the constraint if it exists.

The core logic was not changed here. It only adds a safety mechanism.

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

* Remove duplicate call to getResultArray in attribute_links loop

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

* PSR refactoring

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

* Remove dead parameter from reassignDuplicateAttributeValues method

The attribute value was not needed and is never used since we have the attribute_ids and those are unique.

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

* Create potentially missing primary keys before attempting to add constraints.

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

* Guard datetime creation

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

* Update regex pattern to handle double-quoted and non-quoted encryption keys

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

* Issue warning and fallback on unparseable attribute_date during

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

---------

Signed-off-by: objec <objecttothis@gmail.com>
2026-06-06 02:02:42 +04:00
jekkos
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
27680b07c9 Merge remote-tracking branch 'OpensourcePOS/master' into plugin-system-fresh 2026-06-04 10:38:59 +04:00
objecttothis
9509a97164 Add fallback for allowedHostnames environment variable (#4565)
* Add fallback for allowedHostnames environment variable

- In some cases allowedHostnames is set in env but not loaded at the time of check, yet available in other sources. This adds fallback checks.
- Add UnitTest

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

* Improve the fallback logic for allowedHostnames environment variable

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

---------

Signed-off-by: objec <objecttothis@gmail.com>
2026-06-03 22:00:30 +04: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
objec
07d1d15ade Add missing helper for migration execution
Signed-off-by: objec <objecttothis@gmail.com>
2026-05-29 16:33:54 +04:00
objec
0e10a85248 Change tense on trigger name for consistency
Signed-off-by: objec <objecttothis@gmail.com>
2026-05-29 15:15:50 +04:00
objec
b18e9ea785 Merge remote-tracking branch 'OpensourcePOS/master' into plugin-system-fresh 2026-05-29 14:01:57 +04:00
jekkos
5450404cb2 fix: cast string returns to int in MY_Migration (#4560)
basename() returns string and database column values are strings,
but get_latest_migration() and get_current_version() declare int
return types. PHP 8.0+ enforces strict return types and no longer
silently coerces strings to int, causing a TypeError on fresh
installs.

Fixes #4559

Co-authored-by: Ollama <ollama@steganos.dev>
2026-05-22 16:07:21 +02: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
ad901f9c2d Add Receiving type to receiving_complete event trigger
- The type isn't found in the db, so send it to plugins.
- Update documentation

Signed-off-by: objec <objecttothis@gmail.com>
2026-05-21 13:13:48 +04:00
objec
388c8ad631 Add Receivings event trigger
Signed-off-by: objec <objecttothis@gmail.com>
2026-05-21 12:47:53 +04:00
objec
705c61b48c Update documentation
Signed-off-by: objec <objecttothis@gmail.com>
2026-05-20 23:09:58 +04:00
objec
d39067e2e1 Add event trigger for sale completion
Signed-off-by: objec <objecttothis@gmail.com>
2026-05-20 21:32:00 +04:00
objec
50eead4da4 Updating customers save triggers to pass an array
- Customer CSV import will potentially have many customerIds to send to.
- Rework mailchimp onCustomerSaved() to receive an array of ids instead of a single ID

Signed-off-by: objec <objecttothis@gmail.com>
2026-05-20 19:41:38 +04:00
objec
4c7ac7b5d0 Thin contract triggers
- send only bare required data to trigger callbacks.
- Plugins for now access model, library and helpers but in the future access REST APIs only for data.

Signed-off-by: objec <objecttothis@gmail.com>
2026-05-20 19:27:36 +04:00
objec
bed8a1c34d Error checking and validation
Signed-off-by: objec <objecttothis@gmail.com>
2026-05-20 19:05:23 +04:00
objec
10588867c4 Update configuration form to improve the UI
Signed-off-by: objec <objecttothis@gmail.com>
2026-05-20 17:57:34 +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
c08872f83e Controller function updates for plugins
- Refactor get_multiple_info() to getMultipleInfo() in call
- Change data passed in customer event trigger to just the customerId.

Signed-off-by: objec <objecttothis@gmail.com>
2026-05-20 17:09:06 +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
objec
f8fd12c5de Unify CLAUDE.md and AGENTS.md
Signed-off-by: objec <objecttothis@gmail.com>
2026-05-20 17:04:46 +04:00
objec
a15a6516a6 Update AGENTS.md and CLAUDE.md to reflect unified instructions
Signed-off-by: objec <objecttothis@gmail.com>
2026-05-20 17:03:04 +04:00
objec
84a10ec218 chore: untrack PHPUnit build artifacts
/build is already gitignored but these four files were previously
committed, causing them to be tracked despite the ignore rule.
2026-05-19 16:57:44 +04:00
objec
f650f17181 Push missing language strings file changes and HomeTest
Signed-off-by: objec <objecttothis@gmail.com>
2026-05-19 16:16:27 +04:00
objec
d699d82388 Merge remote-tracking branch 'OpensourcePOS/master' into plugin-system-fresh 2026-05-19 16:07:31 +04:00
objecttothis
b0dddc22a3 Bugfixes to get Migration working on MySQL and MariaDB (#4551)
* Bugfixes to get Migration working on MySQL

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

* MariaDB compatibility fixes

- Drop foreign key constraints before making charset changes
- Fix dropAllForeignKeyConstraints helper function.
- Added `IF EXISTS` to DROP statements
- Do not try to readd FK constraints for tables which were dropped.
- MariaDB 11.8.x changes the default implicit collation to uca1400 which breaks the IndiaGST migration, et. al. Explicitly declare utf8_general_ci in affected migrations.

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

* Fix changes which break MySQL migrations

- MySQL does not support IF EXISTS in foreign key constraints. Since the PHP is now handling dropping those constraints, these lines are redundant. Remove them.

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

* Resolve code review recommendations

- Add try/catch around DB connect statement
- Heed result of execute_script function and throw an exception on failure.

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

* Refactor out duplicate code

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

* Initialize array variable causing potential issues

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

---------

Signed-off-by: objec <objecttothis@gmail.com>
2026-05-19 16:02:05 +04:00
objec
7afaeef6a3 Merge remote-tracking branch 'OpensourcePOS/master' into plugin-system-fresh 2026-05-19 11:28:29 +04:00
jekkos
8d6b166673 feat: Add deployment workflow with approval gates (#4522)
* feat: Add deployment workflows with approval gates

Add GitHub Actions workflows for controlled deployments:

deploy.yml - Manual Deploy:
- Triggered via Actions UI (workflow_dispatch)
- Select environment (production/staging)
- Select Docker image tag
- Reusable via workflow_call for other workflows
- Creates GitHub deployment records with status tracking
- Sends Docker Hub compatible webhook payload
- Environment input validation for workflow_call

deploy-pr.yml - PR Deploy:
- Auto-triggers when PR is approved (same-repo only)
- Deploys to staging environment
- Image tag format: pr-{number}-{short-sha}
- Posts deployment status as PR comment
- Fork PR protection: only runs for same-repo PRs

Security:
- jq-based JSON payload construction (prevents script injection)
- HMAC-SHA256 signature verification for webhook
- Untrusted inputs via env: blocks (not inline interpolation)
- Environment validation before deployment
- Fork detection guard for PR deployments

Fixes CodeRabbit review comments:
- Invalid jq string filter syntax (missing quotes)
- Unvalidated environment input in workflow_call
- Fork PR deployments blocked by pull_request_review restrictions

* refactor: Limit deployment to staging only

- Remove environment input choice (was production/staging)
- Hardcode environment to 'staging' throughout
- Simplify workflow - no environment validation needed
- Update concurrency group to deploy-staging

* refactor: Extract deployment logic to reusable deploy-core.yml

Restructure workflows to eliminate code duplication:

deploy-core.yml (new):
- Reusable workflow with all deployment logic
- Creates GitHub deployment record
- Sends webhook payload to external service
- Handles status updates
- Accepts image_tag, sha, description, pr_number inputs
- Outputs deployment_id and status

deploy.yml (simplified):
- Manual trigger only
- Calls deploy-core with user-provided image_tag
- 18 lines (was 175)

deploy-pr.yml (simplified):
- PR approval trigger with fork guard
- Prepare job: checkout, generate PR image tag
- Deploy job: calls deploy-core
- Comment job: post status to PR
- 70 lines (was 204)

---------

Co-authored-by: Ollama <ollama@steganos.dev>
2026-05-18 21:48:02 +02: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
jekkos
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
796657118a Feature to pass data to config view modals
- Add getConfigViewData() to BasePlugin.php
- Add function to plugin interface

Signed-off-by: objec <objecttothis@gmail.com>
2026-05-18 16:11:12 +04:00
objec
445a506ea8 Plugin Changes
- Remove unneeded language line from MailchimpPlugin
- Update README.md
- Normalized fields_required_message

Signed-off-by: objec <objecttothis@gmail.com>
2026-05-18 16:08:33 +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
jekkos
9c89a2e2cb fix: Capture CSV import failures in save_tax_data and save_inventory_quantities (#4507)
* fix: capture CSV import failures in save_tax_data and save_inventory_quantities

- Change save_tax_data() return type from void to bool
- Change save_inventory_quantities() return type from void to bool
- Accumulate failure status with &= operator in save_inventory_quantities
- Update postImportCsvFile() to capture return values and set isFailedRow
- Properly propagate failures to failCodes array

Fixes #4475

* fix: Change isset to !empty for items_taxes_data check

- isset was always true since array was initialized
- Use !empty to properly check if there are tax items to save

Address CodeRabbit review feedback

* fix: Capture inventory insert result in save_inventory_quantities

- Combine inventory insert result with success tracking
- Use &= operator to accumulate failures from both operations
- Ensure failures from inventory inserts are propagated

Address CodeRabbit review feedback

---------

Co-authored-by: Ollama <ollama@steganos.dev>
2026-05-17 22:23:43 +02:00
jekkos
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
jekkos
def0c27a0e fix(security): Path traversal vulnerability in getPicThumb (#4545)
Security impact:
- Authenticated attackers could read arbitrary files on the server
- Path traversal via unsanitized pic_filename parameter
- Could read .env, config files, encryption keys

Fix:
- Apply basename() to strip directory components
- Validate file extension to allowlist image types only
- Add explicit error response for invalid file types

CVE: Pending
Affected: <= 3.4.2
Reported by: Kamran Saifullah (VulDB)

Co-authored-by: Ollama <ollama@steganos.dev>
2026-05-15 22:04:29 +02:00