* 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>
* 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>
* 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>
- 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>
- 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>
- 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>
* 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>
- 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>
* 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>
- updated some files to match the official CodeIgniter 4 skeleton.
- rebuilt package.json from a clean init and modernized metadata and formatting
- rebuilt composer.json with modernized metadata and formatting
- replaced code of conduct text with markdown
- updated Dockerfile to replace deprecated instruction
- reinstated 'update-licenses' task in gulp (accidentally removed in 3e844f2f89)
- updated bootstrap, bootswatch, and various dev dependencies
- refinded text across UI
- applied consistency fixes
- added 'number' and 'tel' input types to relevant settings
- improved system info layout (still room for improvement, but better)
- updated and fixed changelog
- 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>
- Add function to correctly interpret subscription status from the API
- Error validation on customer deletion.
- Corrected PHPDoc to reflect reponse codes.
- Pass complete data to synchronize subscription function
- Rework request function to properly interpret response
- Add data to trigger
- Unsubscribe customer before deleting them from Mailchimp to prevent error.
Signed-off-by: objec <objecttothis@gmail.com>
- 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>
- Add PHPdoc including @noinspection to prevent AJAX function from causing a warning.
- Add lists array to settings retrieval in MailchimpPlugin.php
- Close modal window on Submit
- Don't check API key on empty value
Signed-off-by: objec <objecttothis@gmail.com>
- Remove unneeded keys from Config.php
- Remove unneeded lang() function override from BasePlugin.php
- Update README.md to reflect changes to language loading
- Correct language file string
- Correct lang() function calls to remove `$this->` from the call since we aren't overriding it anymore.
- Add code to correctly register namespace so that languages load.
- Fix plugin view render bug
Signed-off-by: objec <objecttothis@gmail.com>
- Fix bug causing all plugin views to be rendered on every page.
- Simplify code
- Refactor manage.php view to use bootstrap tables
Signed-off-by: objec <objecttothis@gmail.com>
- Refactor Plugins.php language keys
- Correct spacing between key and `=>`
- Replaced `"` with `'` to avoid calling the PHP string parser
- Propagated Plugins.php language string file to other languages
- removed redundant `plugin_` from key names
Signed-off-by: objec <objecttothis@gmail.com>
- Move the PluginManager creation to a service.
- Move plugin discovery to creation.
- Create static discovery and namespaces variables in the PluginManager.php library
- Refactor persistent namespace declarations
- Refactor redundant code to private function.
- Remove whitespace
- Remove enable setting from MailchimpPlugin. That is handled by the PluginManager.php
- Update Events.php to call the pluginManager service
- Correct typo in enabled setting for BasePlugin to accurately reflect the database naming.
Signed-off-by: objec <objecttothis@gmail.com>
- Move Plugins controller and rename to reflect the rest of the code.
- Lazy load event registrations.
- Autoload classes so plugins are discovered.
- Remove TODO
- Remove unneeded use statement
- Correct typo in namespace of MailchimpConnector Library
- Add class names to autoload class map
- Move Plugin discovery to post_controller_constructor event
Signed-off-by: objec <objecttothis@gmail.com>
- Removed use statements
- Refactored key name
- Refactored MailchimpLibrary.php functions
- Removed
- Refactored function name for clarity
- Removed calls to model functions
- Corrected alignment of `=>` in language files
Signed-off-by: objec <objecttothis@gmail.com>
* fix: Add missing $img_tag variable in Sales::getSendPdf()
The receipt_email.php view expects $img_tag but getSendPdf() wasn't passing it.
This caused 'Undefined variable $img_tag' error when sending receipt emails.
Closes#4514
* refactor: Extract img_tag building into helper method
Refactored duplicate img_tag building code into _build_img_tag helper method.
Both getSendPdf and getSendReceipt now use this shared method.
* refactor: Move logo-related methods to Email_lib
Moved buildLogoImgTag and getLogoMimeType methods to Email_lib library
where they logically belong alongside email-related functionality.
This removes duplicate code and centralizes email-related helpers.
Sales controller now uses email_lib->buildLogoImgTag() and
email_lib->getLogoMimeType() instead of private methods.
* fix: Address CodeRabbit review comments
- buildLogoImgTag now uses getLogoMimeType for actual MIME type instead of hardcoding image/png
- getLogoMimeType returns empty string instead of false for consistency
- Consolidated logo path/exists check logic between both methods
---------
Co-authored-by: Ollama <ollama@steganos.dev>
Root cause: In postCheckNumberLocale(), when number_locale differed from
save_number_locale (which happens during form typing/validation), the code
ignored user-provided currency values and always used locale defaults.
For example:
- User sets currency_code to "CRC" (Costa Rica Colon)
- checkNumberLocale is called with save_number_locale from hidden field
- If locale values don't match, original code overwrites with locale defaults
- This caused CRC to revert to the default currency for that locale (EUR, LAK, etc.)
Fix: Always respect user-provided currency_symbol and currency_code values
when they are non-empty, regardless of whether locale changed or not.
Fixes#4494
Co-authored-by: Ollama <ollama@steganos.dev>
- PSR compliance refactors
- Added Events trigger for customer save in Customers.php controller
- Temporarily moved code over to the Mailchimp plugin
- Replaced == with === to prevent type coercion.
Signed-off-by: objec <objecttothis@gmail.com>
- Wrap attribute definition and appconfig save in single transaction
- Capture return values from saveDefinition() and deleteDefinition()
- Only call batch_save() if attribute operation succeeds
- Combine success status with transStatus() for atomic result
- Prevents category_dropdown config persistence when attribute fails
Fixes#4461
Co-authored-by: Ollama <ollama@steganos.dev>
- 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>