mirror of
https://github.com/opensourcepos/opensourcepos.git
synced 2026-05-26 17:26:37 -04:00
Compare commits
13 Commits
fix/shared
...
fix/4554-e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8735c0765b | ||
|
|
629de65e73 | ||
|
|
5450404cb2 | ||
|
|
bf5af2f2dc | ||
|
|
71c164ad18 | ||
|
|
f7280ea83e | ||
|
|
26e8d9d80c | ||
|
|
b7384296c1 | ||
|
|
403180db7b | ||
|
|
6d970953b6 | ||
|
|
b2c2d350a7 | ||
|
|
9dc58a2c1f | ||
|
|
b0dddc22a3 |
@@ -106,4 +106,54 @@ class Encryption extends BaseConfig
|
||||
* by CI3 Encryption default configuration.
|
||||
*/
|
||||
public string $cipher = 'AES-256-CTR';
|
||||
|
||||
/**
|
||||
* Constructor - loads encryption key from fallback location if not set.
|
||||
*
|
||||
* This supports Docker/container environments where ROOTPATH/.env may be
|
||||
* read-only or ephemeral. The fallback key file is stored in WRITEPATH/config/.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
// If key not set from .env or environment, try WRITEPATH fallback
|
||||
if (empty($this->key) || strlen($this->key) < 64) {
|
||||
$fallbackKey = $this->loadKeyFromWritable();
|
||||
if ($fallbackKey !== null) {
|
||||
$this->key = $fallbackKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads encryption key from WRITEPATH/config/encryption.key.
|
||||
*
|
||||
* @return string|null The encryption key if found, null otherwise
|
||||
*/
|
||||
private function loadKeyFromWritable(): ?string
|
||||
{
|
||||
$keyFile = WRITEPATH . 'config' . DIRECTORY_SEPARATOR . 'encryption.key';
|
||||
|
||||
if (!file_exists($keyFile) || !is_readable($keyFile)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$content = file_get_contents($keyFile);
|
||||
if ($content === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$data = json_decode($content, true);
|
||||
if (
|
||||
!is_array($data)
|
||||
|| !isset($data['key'])
|
||||
|| !is_string($data['key'])
|
||||
|| strlen($data['key']) < 64
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $data['key'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace Config;
|
||||
use App\Models\Appconfig;
|
||||
use CodeIgniter\Cache\CacheInterface;
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
use Config\Database;
|
||||
|
||||
/**
|
||||
* This class holds the configuration options stored from the database so that on launch those settings can be cached
|
||||
@@ -13,7 +14,7 @@ use CodeIgniter\Config\BaseConfig;
|
||||
*/
|
||||
class OSPOS extends BaseConfig
|
||||
{
|
||||
public array $settings;
|
||||
public array $settings = [];
|
||||
public string $commit_sha1 = 'dev'; // TODO: Travis scripts need to be updated to replace this with the commit hash on build
|
||||
private CacheInterface $cache;
|
||||
|
||||
@@ -33,26 +34,35 @@ class OSPOS extends BaseConfig
|
||||
|
||||
if ($cache) {
|
||||
$this->settings = decode_array($cache);
|
||||
} else {
|
||||
try {
|
||||
$appconfig = model(Appconfig::class);
|
||||
foreach ($appconfig->get_all()->getResult() as $app_config) {
|
||||
$this->settings[$app_config->key] = $app_config->value;
|
||||
}
|
||||
$this->cache->save('settings', encode_array($this->settings));
|
||||
} catch (\Exception $e) {
|
||||
// Database table doesn't exist yet (migrations haven't run)
|
||||
// or database connection failed. Return empty settings to
|
||||
// allow migration page to display. Catches mysqli_sql_exception
|
||||
// which is not a subclass of DatabaseException.
|
||||
$this->settings = [
|
||||
'language' => 'english',
|
||||
'language_code' => 'en',
|
||||
'company' => 'Home',
|
||||
'barcode_type' => 'Code39'
|
||||
];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$db = Database::connect();
|
||||
|
||||
if (!$db->tableExists('app_config')) {
|
||||
$this->settings = $this->getDefaultSettings();
|
||||
return;
|
||||
}
|
||||
|
||||
$appconfig = model(Appconfig::class);
|
||||
foreach ($appconfig->get_all()->getResult() as $app_config) {
|
||||
$this->settings[$app_config->key] = $app_config->value;
|
||||
}
|
||||
$this->cache->save('settings', encode_array($this->settings));
|
||||
} catch (\Exception $e) {
|
||||
$this->settings = $this->getDefaultSettings();
|
||||
}
|
||||
}
|
||||
|
||||
private function getDefaultSettings(): array
|
||||
{
|
||||
return [
|
||||
'language' => 'english',
|
||||
'language_code' => 'en',
|
||||
'company' => 'Home',
|
||||
'barcode_type' => 'Code39'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,4 +73,4 @@ class OSPOS extends BaseConfig
|
||||
$this->cache->delete('settings');
|
||||
$this->set_settings();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,11 +17,9 @@ use App\Models\Enums\Rounding_mode;
|
||||
use App\Models\Stock_location;
|
||||
use App\Models\Tax;
|
||||
use CodeIgniter\Database\BaseConnection;
|
||||
use CodeIgniter\Encryption\EncrypterInterface;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
use Config\Database;
|
||||
use Config\OSPOS;
|
||||
use Config\Services;
|
||||
use DirectoryIterator;
|
||||
use NumberFormatter;
|
||||
use ReflectionException;
|
||||
@@ -30,7 +28,6 @@ class Config extends Secure_Controller
|
||||
{
|
||||
protected $helpers = ['security'];
|
||||
private BaseConnection $db;
|
||||
private EncrypterInterface $encrypter;
|
||||
private Barcode_lib $barcode_lib;
|
||||
private Sale_lib $sale_lib;
|
||||
private Receiving_lib $receiving_lib;
|
||||
@@ -62,13 +59,6 @@ class Config extends Secure_Controller
|
||||
$this->tax = model(Tax::class);
|
||||
$this->config = config(OSPOS::class)->settings;
|
||||
$this->db = Database::connect();
|
||||
|
||||
helper('security');
|
||||
if (check_encryption()) {
|
||||
$this->encrypter = Services::encrypter();
|
||||
} else {
|
||||
log_message('alert', 'Error preparing encryption key');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -256,25 +246,11 @@ class Config extends Secure_Controller
|
||||
// Integrations Related fields
|
||||
$data['mailchimp'] = [];
|
||||
|
||||
if (check_encryption()) { // TODO: Hungarian notation
|
||||
if (!isset($this->encrypter)) {
|
||||
helper('security');
|
||||
$this->encrypter = Services::encrypter();
|
||||
}
|
||||
$data['mailchimp']['api_key'] = decryptValue($this->config['mailchimp_api_key'] ?? null);
|
||||
$data['mailchimp']['list_id'] = decryptValue($this->config['mailchimp_list_id'] ?? null);
|
||||
|
||||
$data['mailchimp']['api_key'] = (isset($this->config['mailchimp_api_key']) && !empty($this->config['mailchimp_api_key']))
|
||||
? $this->encrypter->decrypt($this->config['mailchimp_api_key'])
|
||||
: '';
|
||||
|
||||
$data['mailchimp']['list_id'] = (isset($this->config['mailchimp_list_id']) && !empty($this->config['mailchimp_list_id']))
|
||||
? $this->encrypter->decrypt($this->config['mailchimp_list_id'])
|
||||
: '';
|
||||
|
||||
// Remove any backup of .env created by check_encryption()
|
||||
remove_backup();
|
||||
} else {
|
||||
$data['mailchimp']['api_key'] = '';
|
||||
$data['mailchimp']['list_id'] = '';
|
||||
if (checkEncryption()) {
|
||||
removeBackup();
|
||||
}
|
||||
|
||||
$data['mailchimp']['lists'] = $this->_mailchimp();
|
||||
@@ -512,15 +488,23 @@ class Config extends Secure_Controller
|
||||
public function postSaveEmail(): ResponseInterface
|
||||
{
|
||||
$password = '';
|
||||
$passwordInput = $this->request->getPost('smtp_pass');
|
||||
|
||||
if (check_encryption() && !empty($this->request->getPost('smtp_pass'))) {
|
||||
$password = $this->encrypter->encrypt($this->request->getPost('smtp_pass'));
|
||||
if (!empty($passwordInput)) {
|
||||
$password = encryptValue($passwordInput);
|
||||
if (empty($password)) {
|
||||
log_message('error', 'SMTP password encryption failed - credentials not saved');
|
||||
|
||||
return $this->response->setJSON([
|
||||
'success' => false,
|
||||
'message' => lang('Config.encryption_failed'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$protocol = $this->request->getPost('protocol');
|
||||
$mailpath = $this->request->getPost('mailpath');
|
||||
|
||||
// Validate mailpath: required for sendmail, optional for others but must be safe if provided
|
||||
$isMailpathRequired = ($protocol === 'sendmail');
|
||||
$isMailpathProvided = !empty($mailpath);
|
||||
$isMailpathValid = $isMailpathProvided && preg_match('/^[a-zA-Z0-9_\-\/.]+$/', $mailpath);
|
||||
@@ -528,7 +512,7 @@ class Config extends Secure_Controller
|
||||
if (($isMailpathRequired && !$isMailpathProvided) || ($isMailpathProvided && !$isMailpathValid)) {
|
||||
return $this->response->setJSON([
|
||||
'success' => false,
|
||||
'message' => lang('Config.mailpath_invalid')
|
||||
'message' => lang('Config.mailpath_invalid'),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -540,7 +524,7 @@ class Config extends Secure_Controller
|
||||
'smtp_pass' => $password,
|
||||
'smtp_port' => $this->request->getPost('smtp_port', FILTER_SANITIZE_NUMBER_INT),
|
||||
'smtp_timeout' => $this->request->getPost('smtp_timeout', FILTER_SANITIZE_NUMBER_INT),
|
||||
'smtp_crypto' => $this->request->getPost('smtp_crypto')
|
||||
'smtp_crypto' => $this->request->getPost('smtp_crypto'),
|
||||
];
|
||||
|
||||
$success = $this->appconfig->batch_save($batch_save_data);
|
||||
@@ -558,16 +542,25 @@ class Config extends Secure_Controller
|
||||
public function postSaveMessage(): ResponseInterface
|
||||
{
|
||||
$password = '';
|
||||
$passwordInput = $this->request->getPost('msg_pwd');
|
||||
|
||||
if (check_encryption() && !empty($this->request->getPost('msg_pwd'))) {
|
||||
$password = $this->encrypter->encrypt($this->request->getPost('msg_pwd'));
|
||||
if (!empty($passwordInput)) {
|
||||
$password = encryptValue($passwordInput);
|
||||
if (empty($password)) {
|
||||
log_message('error', 'SMS password encryption failed');
|
||||
|
||||
return $this->response->setJSON([
|
||||
'success' => false,
|
||||
'message' => lang('Config.encryption_failed'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$batch_save_data = [
|
||||
'msg_msg' => $this->request->getPost('msg_msg'),
|
||||
'msg_uid' => $this->request->getPost('msg_uid'),
|
||||
'msg_pwd' => $password,
|
||||
'msg_src' => $this->request->getPost('msg_src')
|
||||
'msg_src' => $this->request->getPost('msg_src'),
|
||||
];
|
||||
|
||||
$success = $this->appconfig->batch_save($batch_save_data);
|
||||
@@ -623,24 +616,38 @@ class Config extends Secure_Controller
|
||||
*/
|
||||
public function postSaveMailchimp(): ResponseInterface
|
||||
{
|
||||
$api_key = '';
|
||||
$list_id = '';
|
||||
$apiKey = '';
|
||||
$listId = '';
|
||||
|
||||
if (check_encryption()) {
|
||||
$api_key_unencrypted = $this->request->getPost('mailchimp_api_key');
|
||||
if (!empty($api_key_unencrypted)) {
|
||||
$api_key = $this->encrypter->encrypt($api_key_unencrypted);
|
||||
}
|
||||
$apiKeyInput = $this->request->getPost('mailchimp_api_key');
|
||||
if (!empty($apiKeyInput)) {
|
||||
$apiKey = encryptValue($apiKeyInput);
|
||||
if (empty($apiKey)) {
|
||||
log_message('error', 'Mailchimp API key encryption failed');
|
||||
|
||||
$list_id_unencrypted = $this->request->getPost('mailchimp_list_id');
|
||||
if (!empty($list_id_unencrypted)) {
|
||||
$list_id = $this->encrypter->encrypt($list_id_unencrypted);
|
||||
return $this->response->setJSON([
|
||||
'success' => false,
|
||||
'message' => lang('Config.encryption_failed'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$batch_save_data = ['mailchimp_api_key' => $api_key, 'mailchimp_list_id' => $list_id];
|
||||
$listIdInput = $this->request->getPost('mailchimp_list_id');
|
||||
if (!empty($listIdInput)) {
|
||||
$listId = encryptValue($listIdInput);
|
||||
if (empty($listId)) {
|
||||
log_message('error', 'Mailchimp list ID encryption failed');
|
||||
|
||||
$success = $this->appconfig->batch_save($batch_save_data);
|
||||
return $this->response->setJSON([
|
||||
'success' => false,
|
||||
'message' => lang('Config.encryption_failed'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$batchSaveData = ['mailchimp_api_key' => $apiKey, 'mailchimp_list_id' => $listId];
|
||||
|
||||
$success = $this->appconfig->batch_save($batchSaveData);
|
||||
|
||||
return $this->response->setJSON(['success' => $success, 'message' => lang('Config.saved_' . ($success ? '' : 'un') . 'successfully')]);
|
||||
}
|
||||
|
||||
@@ -31,13 +31,7 @@ class Customers extends Persons
|
||||
$this->tax_code = model(Tax_code::class);
|
||||
$this->config = config(OSPOS::class)->settings;
|
||||
|
||||
$encrypter = Services::encrypter();
|
||||
|
||||
if (!empty($this->config['mailchimp_list_id'])) {
|
||||
$this->_list_id = $encrypter->decrypt($this->config['mailchimp_list_id']);
|
||||
} else {
|
||||
$this->_list_id = '';
|
||||
}
|
||||
$this->_list_id = decryptValue($this->config['mailchimp_list_id'] ?? null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -49,6 +49,13 @@ class Login extends BaseController
|
||||
return view('login', $data);
|
||||
}
|
||||
|
||||
if (!$data['is_latest'] || $data['is_new_install']) {
|
||||
set_time_limit(3600);
|
||||
|
||||
$migration->setNamespace('App')->latest();
|
||||
return redirect()->to('login');
|
||||
}
|
||||
|
||||
$rules = ['username' => 'required|login_check[data]'];
|
||||
$messages = [
|
||||
'username' => [
|
||||
@@ -62,13 +69,6 @@ class Login extends BaseController
|
||||
|
||||
return view('login', $data);
|
||||
}
|
||||
|
||||
if (!$data['is_latest']) {
|
||||
set_time_limit(3600);
|
||||
|
||||
$migration->setNamespace('App')->latest();
|
||||
return redirect()->to('login');
|
||||
}
|
||||
}
|
||||
|
||||
return redirect()->to('home');
|
||||
@@ -79,18 +79,18 @@ class Login extends BaseController
|
||||
try {
|
||||
$migration = new MY_Migration(config('Migrations'));
|
||||
$migration->migrate_to_ci4();
|
||||
|
||||
|
||||
set_time_limit(3600);
|
||||
$migration->setNamespace('App')->latest();
|
||||
|
||||
|
||||
return $this->response->setJSON([
|
||||
'success' => true,
|
||||
'message' => 'Migration completed successfully'
|
||||
]);
|
||||
|
||||
|
||||
} catch (\Exception $e) {
|
||||
log_message('error', 'Migration failed: ' . $e->getMessage());
|
||||
|
||||
|
||||
return $this->response->setJSON([
|
||||
'success' => false,
|
||||
'message' => 'Migration failed: ' . $e->getMessage()
|
||||
|
||||
@@ -161,7 +161,7 @@ class Sales extends Secure_Controller
|
||||
'only_bank_transfer'=> false,
|
||||
'only_wallet' => false,
|
||||
'only_invoices' => $this->config['invoice_enable'] && $this->request->getGet('only_invoices', FILTER_SANITIZE_NUMBER_INT),
|
||||
'is_valid_receipt' => $this->sale->is_valid_receipt($search)
|
||||
'is_valid_receipt' => $this->sale->isValidReceipt($search)
|
||||
];
|
||||
|
||||
// Check if any filter is set in the multiselect dropdown
|
||||
@@ -198,7 +198,7 @@ class Sales extends Secure_Controller
|
||||
? $this->request->getGet('term')
|
||||
: null;
|
||||
|
||||
if ($this->sale_lib->get_mode() == 'return' && $this->sale->is_valid_receipt($receipt)) {
|
||||
if ($this->sale_lib->get_mode() == 'return' && $this->sale->isValidReceipt($receipt)) {
|
||||
// If a valid receipt or invoice was found the search term will be replaced with a receipt number (POS #)
|
||||
$suggestions[] = $receipt;
|
||||
}
|
||||
@@ -525,7 +525,7 @@ class Sales extends Secure_Controller
|
||||
$quantity = ($mode == 'return') ? -$quantity : $quantity;
|
||||
$item_location = $this->sale_lib->get_sale_location();
|
||||
|
||||
if ($mode == 'return' && $this->sale->is_valid_receipt($item_id_or_number_or_item_kit_or_receipt)) {
|
||||
if ($mode == 'return' && $this->sale->isValidReceipt($item_id_or_number_or_item_kit_or_receipt)) {
|
||||
$this->sale_lib->return_entire_sale($item_id_or_number_or_item_kit_or_receipt);
|
||||
} elseif ($this->item_kit->is_valid_item_kit($item_id_or_number_or_item_kit_or_receipt)) {
|
||||
// Add kit item to order if one is assigned
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Database\Migrations;
|
||||
|
||||
use CodeIgniter\Database\Exceptions\DatabaseException;
|
||||
use CodeIgniter\Database\Migration;
|
||||
|
||||
class Migration_Upgrade_To_3_1_1 extends Migration
|
||||
@@ -17,7 +18,37 @@ class Migration_Upgrade_To_3_1_1 extends Migration
|
||||
public function up(): void
|
||||
{
|
||||
helper('migration');
|
||||
execute_script(APPPATH . 'Database/Migrations/sqlscripts/3.0.2_to_3.1.1.sql');
|
||||
|
||||
// MariaDB blocks CONVERT TO CHARACTER SET on tables with FK constraints.
|
||||
// Drop all FKs across affected tables before running the SQL script, recreate after.
|
||||
$fkColumns = [
|
||||
['modules', 'module_id'],
|
||||
['stock_locations', 'location_id'],
|
||||
['permissions', 'permission_id'],
|
||||
['people', 'person_id'],
|
||||
['suppliers', 'supplier_id'],
|
||||
['items', 'item_id'],
|
||||
['item_kits', 'item_kit_id'],
|
||||
['sales', 'sale_id'],
|
||||
['receivings', 'receiving_id'],
|
||||
['employees', 'employee_id'],
|
||||
['customers', 'person_id'],
|
||||
];
|
||||
|
||||
$constraints = [];
|
||||
foreach ($fkColumns as [$table, $column]) {
|
||||
foreach (dropAllForeignKeyConstraints($table, $column) as $c) {
|
||||
$constraints[$c['constraintName']] = $c;
|
||||
}
|
||||
}
|
||||
|
||||
if (!execute_script(APPPATH . 'Database/Migrations/sqlscripts/3.0.2_to_3.1.1.sql')) {
|
||||
throw new DatabaseException('Migration script 3.0.2_to_3.1.1.sql failed. Check logs for details.');
|
||||
}
|
||||
|
||||
$droppedTables = ['sales_suspended', 'sales_suspended_items', 'sales_suspended_items_taxes', 'sales_suspended_payments'];
|
||||
$toRecreate = array_filter($constraints, fn($c) => !in_array($c['tableName'], $droppedTables, true));
|
||||
recreateForeignKeyConstraints(array_values($toRecreate));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,10 +33,10 @@ class Convert_to_ci4 extends Migration
|
||||
if (!empty(config('Encryption')->key)) {
|
||||
$this->convert_ci3_encrypted_data();
|
||||
} else {
|
||||
check_encryption();
|
||||
checkEncryption();
|
||||
}
|
||||
|
||||
remove_backup();
|
||||
removeBackup();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,15 +66,15 @@ class Convert_to_ci4 extends Migration
|
||||
|
||||
$decrypted_data = $this->decrypt_ci3_data($ci3_encrypted_data);
|
||||
|
||||
check_encryption();
|
||||
checkEncryption();
|
||||
|
||||
try {
|
||||
$ci4_encrypted_data = $this->encrypt_data($decrypted_data);
|
||||
|
||||
$success = empty(array_diff_assoc($decrypted_data, $this->decrypt_data($ci4_encrypted_data)));
|
||||
if (!$success) {
|
||||
abort_encryption_conversion();
|
||||
remove_backup();
|
||||
abortEncryptionConversion();
|
||||
removeBackup();
|
||||
throw new RedirectException('login');
|
||||
}
|
||||
|
||||
|
||||
@@ -327,19 +327,6 @@ INSERT INTO `ospos_sales_items` (sale_id, item_id, description, serialnumber, li
|
||||
INSERT INTO `ospos_sales_payments` (sale_id, payment_type, payment_amount) SELECT sale_id, payment_type, payment_amount FROM `ospos_sales_suspended_payments`;
|
||||
INSERT INTO `ospos_sales_items_taxes` (sale_id, item_id, line, name, percent) SELECT sale_id, item_id, line, name, percent FROM `ospos_sales_suspended_items_taxes`;
|
||||
|
||||
ALTER TABLE `ospos_sales_suspended_payments` DROP FOREIGN KEY `ospos_sales_suspended_payments_ibfk_1`;
|
||||
|
||||
ALTER TABLE `ospos_sales_suspended_items_taxes` DROP FOREIGN KEY `ospos_sales_suspended_items_taxes_ibfk_1`;
|
||||
ALTER TABLE `ospos_sales_suspended_items_taxes` DROP FOREIGN KEY `ospos_sales_suspended_items_taxes_ibfk_2`;
|
||||
|
||||
ALTER TABLE `ospos_sales_suspended_items` DROP FOREIGN KEY `ospos_sales_suspended_items_ibfk_1`;
|
||||
ALTER TABLE `ospos_sales_suspended_items` DROP FOREIGN KEY `ospos_sales_suspended_items_ibfk_2`;
|
||||
ALTER TABLE `ospos_sales_suspended_items` DROP FOREIGN KEY `ospos_sales_suspended_items_ibfk_3`;
|
||||
|
||||
ALTER TABLE `ospos_sales_suspended` DROP FOREIGN KEY `ospos_sales_suspended_ibfk_1`;
|
||||
ALTER TABLE `ospos_sales_suspended` DROP FOREIGN KEY `ospos_sales_suspended_ibfk_2`;
|
||||
ALTER TABLE `ospos_sales_suspended` DROP FOREIGN KEY `ospos_sales_suspended_ibfk_3`;
|
||||
|
||||
DROP TABLE `ospos_sales_suspended_payments`, `ospos_sales_suspended_items_taxes`, `ospos_sales_suspended_items`, `ospos_sales_suspended`;
|
||||
|
||||
--
|
||||
|
||||
@@ -140,7 +140,7 @@ CREATE TABLE IF NOT EXISTS `ospos_expense_categories` (
|
||||
`category_name` varchar(255) DEFAULT NULL,
|
||||
`category_description` varchar(255) NOT NULL,
|
||||
`deleted` int(1) NOT NULL DEFAULT '0'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
|
||||
|
||||
|
||||
-- Table structure for table `ospos_expenses`
|
||||
@@ -154,7 +154,7 @@ CREATE TABLE IF NOT EXISTS `ospos_expenses` (
|
||||
`description` varchar(255) NOT NULL,
|
||||
`employee_id` int(10) NOT NULL,
|
||||
`deleted` int(1) NOT NULL DEFAULT '0'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
|
||||
|
||||
|
||||
-- Indexes for table `ospos_expense_categories`
|
||||
|
||||
@@ -75,7 +75,7 @@ CREATE TABLE `ospos_cash_up` (
|
||||
`open_employee_id` int(10) NOT NULL,
|
||||
`close_employee_id` int(10) NOT NULL,
|
||||
`deleted` int(1) NOT NULL DEFAULT '0'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
|
||||
|
||||
-- Indexes for table `ospos_cash_up`
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ CREATE TABLE IF NOT EXISTS `ospos_tax_codes` (
|
||||
`state` varchar(255) NOT NULL DEFAULT '',
|
||||
`deleted` int(1) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`tax_code_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
|
||||
|
||||
ALTER TABLE `ospos_customers`
|
||||
ADD COLUMN `tax_id` varchar(32) NOT NULL DEFAULT '' AFTER `taxable`,
|
||||
@@ -59,7 +59,7 @@ CREATE TABLE `ospos_sales_taxes` (
|
||||
`rounding_code` tinyint(2) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`sales_taxes_id`),
|
||||
KEY `print_sequence` (`sale_id`,`print_sequence`,`tax_group`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `ospos_tax_jurisdictions` (
|
||||
`jurisdiction_id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
@@ -71,7 +71,7 @@ CREATE TABLE IF NOT EXISTS `ospos_tax_jurisdictions` (
|
||||
`cascade_sequence` tinyint(2) NOT NULL DEFAULT 0,
|
||||
`deleted` int(1) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`jurisdiction_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci AUTO_INCREMENT=1;
|
||||
|
||||
ALTER TABLE `ospos_suppliers`
|
||||
ADD COLUMN `tax_id` varchar(32) DEFAULT NULL AFTER `account_number`;
|
||||
@@ -89,7 +89,7 @@ CREATE TABLE IF NOT EXISTS `ospos_tax_rates` (
|
||||
`tax_rate` decimal(15,4) NOT NULL DEFAULT 0.0000,
|
||||
`tax_rounding_code` tinyint(2) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`tax_rate_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
|
||||
|
||||
-- Add support for sales tax report
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ CREATE TABLE `ospos_sales_payments` (
|
||||
`reference_code` varchar(40) NOT NULL DEFAULT '',
|
||||
PRIMARY KEY (`payment_id`),
|
||||
KEY `payment_sale` (`sale_id`, `payment_type`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
|
||||
|
||||
INSERT INTO ospos_sales_payments (sale_id, payment_type, payment_amount, payment_user)
|
||||
SELECT payments.sale_id, payments.payment_type, payments.payment_amount, sales.employee_id
|
||||
|
||||
@@ -172,6 +172,7 @@ function dropAllForeignKeyConstraints(string $table, string $column): array {
|
||||
WHERE kcu.TABLE_SCHEMA = DATABASE()
|
||||
AND ((kcu.REFERENCED_TABLE_NAME = '" . $db->getPrefix() . "$table' AND kcu.REFERENCED_COLUMN_NAME = '$column')
|
||||
OR (kcu.TABLE_NAME = '" . $db->getPrefix() . "$table' AND kcu.COLUMN_NAME = '$column'))
|
||||
AND rc.CONSTRAINT_NAME IS NOT NULL
|
||||
");
|
||||
|
||||
$deletedConstraints = [];
|
||||
|
||||
@@ -1,96 +1,304 @@
|
||||
<?php
|
||||
|
||||
use CodeIgniter\Encryption\Encryption;
|
||||
use CodeIgniter\Encryption\Exceptions\EncryptionException;
|
||||
use Config\Services;
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* Checks and initializes encryption key.
|
||||
*
|
||||
* This function ensures a valid encryption key exists for the application.
|
||||
* It tries multiple storage locations to support different deployment scenarios:
|
||||
* 1. ROOTPATH/.env - Standard location for non-containerized deployments
|
||||
* 2. WRITEPATH/config/encryption.key - Fallback for Docker/container environments where .env is read-only
|
||||
*
|
||||
* @return bool True if encryption key is available, false if key generation/persistence failed
|
||||
*/
|
||||
function check_encryption(): bool
|
||||
function checkEncryption(): bool
|
||||
{
|
||||
$old_key = config('Encryption')->key;
|
||||
$oldKey = config('Encryption')->key;
|
||||
|
||||
if ((empty($old_key)) || (strlen($old_key) < 64)) {
|
||||
$encryption = new Encryption();
|
||||
$key = bin2hex($encryption->createKey());
|
||||
config('Encryption')->key = $key;
|
||||
if (!empty($oldKey) && strlen($oldKey) >= 64) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$config_path = ROOTPATH . '.env';
|
||||
$backup_path = WRITEPATH . '/backup/.env.bak';
|
||||
$backup_folder = WRITEPATH . '/backup';
|
||||
$encryption = new Encryption();
|
||||
$key = bin2hex($encryption->createKey());
|
||||
config('Encryption')->key = $key;
|
||||
|
||||
if (!file_exists($backup_folder)) {
|
||||
@mkdir($backup_folder, 0750, true);
|
||||
}
|
||||
$envPersisted = writeEncryptionKeyToEnv($key, $oldKey);
|
||||
$writablePersisted = writeEncryptionKeyToWritable($key, $oldKey);
|
||||
$persisted = $envPersisted || $writablePersisted;
|
||||
|
||||
if (!file_exists($config_path)) {
|
||||
$example_path = ROOTPATH . '.env.example';
|
||||
if (file_exists($example_path)) {
|
||||
@copy($example_path, $config_path);
|
||||
} else {
|
||||
@file_put_contents($config_path, "# OSPOS Configuration\n\n");
|
||||
}
|
||||
@chmod($config_path, 0640);
|
||||
}
|
||||
if ($persisted) {
|
||||
log_message('info', 'Encryption key initialized successfully');
|
||||
} else {
|
||||
log_message('error', 'Failed to persist encryption key to any location. Encryption may not survive container restarts.');
|
||||
}
|
||||
|
||||
if (file_exists($config_path)) {
|
||||
@copy($config_path, $backup_path);
|
||||
@chmod($backup_path, 0640);
|
||||
@chmod($config_path, 0640);
|
||||
return $persisted;
|
||||
}
|
||||
|
||||
$config_file = file_get_contents($config_path);
|
||||
/**
|
||||
* Writes encryption key to ROOTPATH/.env file.
|
||||
*
|
||||
* @param string $key The new encryption key (hex-encoded)
|
||||
* @param string|null $oldKey The previous key to preserve for key rotation
|
||||
*
|
||||
* @return bool True if key was written successfully, false otherwise
|
||||
*/
|
||||
function writeEncryptionKeyToEnv(string $key, ?string $oldKey = null): bool
|
||||
{
|
||||
$configPath = ROOTPATH . '.env';
|
||||
$backupPath = WRITEPATH . 'backup' . DIRECTORY_SEPARATOR . '.env.bak';
|
||||
$backupFolder = WRITEPATH . 'backup';
|
||||
|
||||
if (strpos($config_file, 'encryption.key') !== false) {
|
||||
$config_file = preg_replace("/(encryption\.key.*=.*)('.*')/", "$1'$key'", $config_file);
|
||||
} else {
|
||||
$config_file .= "\nencryption.key = '$key'\n";
|
||||
}
|
||||
|
||||
if (!empty($old_key)) {
|
||||
$old_line = "# encryption.key = '$old_key' REMOVE IF UNNEEDED\r\n";
|
||||
$insertion_point = stripos($config_file, 'encryption.key');
|
||||
if ($insertion_point !== false) {
|
||||
$config_file = substr_replace($config_file, $old_line, $insertion_point, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@file_put_contents($config_path, $config_file);
|
||||
@chmod($config_path, 0640);
|
||||
|
||||
log_message('info', "Updated encryption key in $config_path");
|
||||
if (!file_exists($backupFolder)) {
|
||||
if (!@mkdir($backupFolder, 0750, true)) {
|
||||
log_message('debug', 'Could not create backup directory');
|
||||
}
|
||||
}
|
||||
|
||||
if (!file_exists($configPath)) {
|
||||
$examplePath = ROOTPATH . '.env.example';
|
||||
if (file_exists($examplePath)) {
|
||||
if (!@copy($examplePath, $configPath)) {
|
||||
log_message('debug', 'Could not copy .env.example to .env');
|
||||
}
|
||||
} else {
|
||||
if (!@file_put_contents($configPath, "# OSPOS Configuration\n\n") !== false) {
|
||||
log_message('debug', 'Could not create .env file');
|
||||
}
|
||||
}
|
||||
@chmod($configPath, 0640);
|
||||
}
|
||||
|
||||
if (!is_writable($configPath)) {
|
||||
log_message('debug', '.env file is not writable');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file_exists($configPath)) {
|
||||
@copy($configPath, $backupPath);
|
||||
@chmod($backupPath, 0640);
|
||||
}
|
||||
|
||||
$configFile = file_get_contents($configPath);
|
||||
if ($configFile === false) {
|
||||
log_message('debug', 'Could not read .env file');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strpos($configFile, 'encryption.key') !== false) {
|
||||
$configFile = preg_replace("/(encryption\.key.*=.*)(['\"])([^'\"]*)\\2/", "$1'$key'", $configFile);
|
||||
} else {
|
||||
$configFile .= "\nencryption.key = '$key'\n";
|
||||
}
|
||||
|
||||
if (!empty($oldKey)) {
|
||||
$oldLine = "# encryption.key = '$oldKey' REMOVE IF UNNEEDED\r\n";
|
||||
$insertionPoint = stripos($configFile, 'encryption.key');
|
||||
if ($insertionPoint !== false) {
|
||||
$configFile = substr_replace($configFile, $oldLine, $insertionPoint, 0);
|
||||
}
|
||||
}
|
||||
|
||||
$result = file_put_contents($configPath, $configFile);
|
||||
if ($result === false) {
|
||||
log_message('debug', 'Could not write to .env file');
|
||||
return false;
|
||||
}
|
||||
|
||||
@chmod($configPath, 0640);
|
||||
log_message('info', "Updated encryption key in $configPath");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* Writes encryption key to WRITEPATH/config/encryption.key file.
|
||||
*
|
||||
* This is the fallback location for Docker/container environments where
|
||||
* the ROOTPATH/.env file may be read-only or ephemeral.
|
||||
*
|
||||
* @param string $key The new encryption key (hex-encoded)
|
||||
* @param string|null $oldKey The previous key to preserve for key rotation
|
||||
*
|
||||
* @return bool True if key was written successfully, false otherwise
|
||||
*/
|
||||
function abort_encryption_conversion(): void
|
||||
function writeEncryptionKeyToWritable(string $key, ?string $oldKey = null): bool
|
||||
{
|
||||
$config_path = ROOTPATH . '.env';
|
||||
$backup_path = WRITEPATH . '/backup/.env.bak';
|
||||
$keyFile = WRITEPATH . 'config' . DIRECTORY_SEPARATOR . 'encryption.key';
|
||||
$keyDir = dirname($keyFile);
|
||||
|
||||
if (!file_exists($backup_path)) {
|
||||
return;
|
||||
if (!is_dir($keyDir)) {
|
||||
if (!@mkdir($keyDir, 0750, true)) {
|
||||
log_message('error', 'Could not create config directory: ' . $keyDir);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@chmod($config_path, 0640);
|
||||
$config_file = file_get_contents($backup_path);
|
||||
@file_put_contents($config_path, $config_file);
|
||||
log_message('info', "Restored $config_path from backup");
|
||||
if (!is_writable($keyDir)) {
|
||||
log_message('error', 'Config directory is not writable: ' . $keyDir);
|
||||
return false;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'key' => $key,
|
||||
'previous_keys' => [],
|
||||
'generated_at' => date('c'),
|
||||
'generated_by' => 'checkEncryption()',
|
||||
];
|
||||
|
||||
if (!empty($oldKey)) {
|
||||
$data['previous_keys'][] = $oldKey;
|
||||
}
|
||||
|
||||
$content = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
$result = file_put_contents($keyFile, $content);
|
||||
|
||||
if ($result === false) {
|
||||
log_message('error', 'Could not write encryption key file');
|
||||
return false;
|
||||
}
|
||||
|
||||
@chmod($keyFile, 0640);
|
||||
|
||||
log_message('info', "Stored encryption key in $keyFile");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads encryption key from WRITEPATH/config/encryption.key file.
|
||||
*
|
||||
* This is the fallback key loader for Docker/container environments.
|
||||
*
|
||||
* @return string|null The encryption key if found, null otherwise
|
||||
*/
|
||||
function loadEncryptionKeyFromWritable(): ?string
|
||||
{
|
||||
$keyFile = WRITEPATH . 'config' . DIRECTORY_SEPARATOR . 'encryption.key';
|
||||
|
||||
if (!file_exists($keyFile)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!is_readable($keyFile)) {
|
||||
log_message('error', 'Encryption key file exists but is not readable: ' . $keyFile);
|
||||
return null;
|
||||
}
|
||||
|
||||
$content = file_get_contents($keyFile);
|
||||
if ($content === false) {
|
||||
log_message('error', 'Could not read encryption key file');
|
||||
return null;
|
||||
}
|
||||
|
||||
$data = json_decode($content, true);
|
||||
if (!is_array($data) || empty($data['key'])) {
|
||||
log_message('error', 'Encryption key file has invalid format');
|
||||
return null;
|
||||
}
|
||||
|
||||
log_message('info', 'Loaded encryption key from WRITEPATH config');
|
||||
|
||||
return $data['key'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores .env from backup (used by migration rollback).
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function remove_backup(): void
|
||||
function abortEncryptionConversion(): void
|
||||
{
|
||||
$backup_path = WRITEPATH . '/backup/.env.bak';
|
||||
if (!file_exists($backup_path)) {
|
||||
$configPath = ROOTPATH . '.env';
|
||||
$backupPath = WRITEPATH . '/backup/.env.bak';
|
||||
|
||||
if (!file_exists($backupPath)) {
|
||||
return;
|
||||
}
|
||||
@unlink($backup_path);
|
||||
log_message('info', "Removed $backup_path");
|
||||
|
||||
@chmod($configPath, 0640);
|
||||
$configFile = file_get_contents($backupPath);
|
||||
@file_put_contents($configPath, $configFile);
|
||||
log_message('info', "Restored $configPath from backup");
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes backup file (used after successful migration).
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function removeBackup(): void
|
||||
{
|
||||
$backupPath = WRITEPATH . '/backup/.env.bak';
|
||||
|
||||
if (file_exists($backupPath)) {
|
||||
unlink($backupPath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts an encrypted value with proper error handling.
|
||||
*
|
||||
* This function provides a consistent decryption pattern across the codebase,
|
||||
* handling cases where encryption key may not be available or decryption fails.
|
||||
*
|
||||
* @param string|null $encryptedValue The encrypted value to decrypt
|
||||
* @param string $default Default value to return if decryption fails
|
||||
*
|
||||
* @return string The decrypted value, or default if decryption fails
|
||||
*/
|
||||
function decryptValue(?string $encryptedValue, string $default = ''): string
|
||||
{
|
||||
if (empty($encryptedValue)) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
if (!checkEncryption()) {
|
||||
log_message('warning', 'Cannot decrypt value: encryption key not available');
|
||||
return $default;
|
||||
}
|
||||
|
||||
try {
|
||||
$encrypter = Services::encrypter();
|
||||
return $encrypter->decrypt($encryptedValue);
|
||||
} catch (EncryptionException $e) {
|
||||
log_message('error', 'Decryption failed: ' . $e->getMessage());
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypts a value with proper error handling.
|
||||
*
|
||||
* This function provides a consistent encryption pattern across the codebase,
|
||||
* handling cases where encryption key may not be available.
|
||||
*
|
||||
* @param string|null $value The value to encrypt
|
||||
* @param bool $require Whether encryption is required (returns empty string on failure)
|
||||
* If false, returns original value on failure
|
||||
*
|
||||
* @return string The encrypted value, or empty string/original value if encryption fails
|
||||
*/
|
||||
function encryptValue(?string $value, bool $require = true): string
|
||||
{
|
||||
if ($value === null || $value === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (!checkEncryption()) {
|
||||
log_message('error', 'Cannot encrypt value: encryption key not available');
|
||||
return $require ? '' : $value;
|
||||
}
|
||||
|
||||
try {
|
||||
$encrypter = Services::encrypter();
|
||||
return $encrypter->encrypt($value);
|
||||
} catch (EncryptionException $e) {
|
||||
log_message('error', 'Encryption failed: ' . $e->getMessage());
|
||||
return $require ? '' : $value;
|
||||
}
|
||||
}
|
||||
@@ -122,6 +122,7 @@ return [
|
||||
"email_smtp_port" => "SMTP Port",
|
||||
"email_smtp_timeout" => "SMTP Timeout (s)",
|
||||
"email_smtp_user" => "SMTP Username",
|
||||
"encryption_failed" => "Failed to encrypt data. Please check encryption configuration.",
|
||||
"enable_avatar" => "",
|
||||
"enable_avatar_tooltip" => "",
|
||||
"enable_dropdown_tooltip" => "",
|
||||
|
||||
@@ -3,11 +3,7 @@
|
||||
namespace app\Libraries;
|
||||
|
||||
use CodeIgniter\Email\Email;
|
||||
use CodeIgniter\Encryption\Encryption;
|
||||
use CodeIgniter\Encryption\EncrypterInterface;
|
||||
use CodeIgniter\Encryption\Exceptions\EncryptionException;
|
||||
use Config\OSPOS;
|
||||
use Config\Services;
|
||||
|
||||
|
||||
/**
|
||||
@@ -26,21 +22,9 @@ class Email_lib
|
||||
$this->email = new Email();
|
||||
$this->config = config(OSPOS::class)->settings;
|
||||
|
||||
$encrypter = Services::encrypter();
|
||||
$smtpPass = decryptValue($this->config['smtp_pass'] ?? null);
|
||||
|
||||
$smtp_pass = $this->config['smtp_pass'];
|
||||
if (!empty($smtp_pass) && check_encryption()) {
|
||||
try {
|
||||
$smtp_pass = $encrypter->decrypt($smtp_pass);
|
||||
} catch (\EncryptionException $e) {
|
||||
// Decryption failed, use the original value
|
||||
log_message('error', 'SMTP password decryption failed: ' . $e->getMessage());
|
||||
$smtp_pass = '';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$email_config = [
|
||||
$emailConfig = [
|
||||
'mailType' => 'html',
|
||||
'userAgent' => 'OSPOS',
|
||||
'validate' => true,
|
||||
@@ -48,12 +32,12 @@ class Email_lib
|
||||
'mailPath' => $this->config['mailpath'],
|
||||
'SMTPHost' => $this->config['smtp_host'],
|
||||
'SMTPUser' => $this->config['smtp_user'],
|
||||
'SMTPPass' => $smtp_pass,
|
||||
'SMTPPass' => $smtpPass,
|
||||
'SMTPPort' => (int)$this->config['smtp_port'],
|
||||
'SMTPTimeout' => (int)$this->config['smtp_timeout'],
|
||||
'SMTPCrypto' => $this->config['smtp_crypto']
|
||||
'SMTPCrypto' => $this->config['smtp_crypto'],
|
||||
];
|
||||
$this->email->initialize($email_config);
|
||||
$this->email->initialize($emailConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,7 +25,7 @@ class MY_Migration extends MigrationRunner
|
||||
public function get_latest_migration(): int
|
||||
{
|
||||
$migrations = $this->findMigrations();
|
||||
return basename(end($migrations)->version);
|
||||
return (int) basename(end($migrations)->version);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -41,7 +41,7 @@ class MY_Migration extends MigrationRunner
|
||||
$builder = $db->table('migrations');
|
||||
$builder->select('version')->orderBy('version', 'DESC')->limit(1);
|
||||
$result = $builder->get()->getRow();
|
||||
return $result ? $result->version : 0;
|
||||
return $result ? (int) $result->version : 0;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// Database not available yet (e.g. fresh install before schema).
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
|
||||
namespace app\Libraries;
|
||||
|
||||
use CodeIgniter\Encryption\EncrypterInterface;
|
||||
use Config\OSPOS;
|
||||
use Config\Services;
|
||||
|
||||
/**
|
||||
* MailChimp API v3 REST client Connector
|
||||
@@ -14,8 +12,6 @@ use Config\Services;
|
||||
* Inspired by the work of:
|
||||
* - Rajitha Bandara: https://github.com/rajitha-bandara/ci-mailchimp-v3-rest-client
|
||||
* - Stefan Ashwell: https://github.com/stef686/codeigniter-mailchimp-api-v3
|
||||
*
|
||||
* @property encrypterinterface encrypter
|
||||
*/
|
||||
class MailchimpConnector
|
||||
{
|
||||
@@ -40,23 +36,19 @@ class MailchimpConnector
|
||||
{
|
||||
$config = config(OSPOS::class)->settings;
|
||||
|
||||
$encrypter = Services::encrypter();
|
||||
|
||||
$mailchimp_api_key = (isset($this->config['mailchimp_api_key']) && !empty($this->config['mailchimp_api_key']))
|
||||
? $this->config['mailchimp_api_key']
|
||||
: '';
|
||||
$mailchimp_api_key = $config['mailchimp_api_key'] ?? '';
|
||||
|
||||
if (!empty($mailchimp_api_key)) {
|
||||
$this->_api_key = empty($api_key)
|
||||
? $encrypter->decrypt($mailchimp_api_key) // TODO: Hungarian notation
|
||||
: $api_key; // TODO: Hungarian notation
|
||||
? decryptValue($mailchimp_api_key)
|
||||
: $api_key;
|
||||
}
|
||||
|
||||
if (!empty($this->_api_key)) { // TODO: Hungarian notation
|
||||
if (!empty($this->_api_key)) {
|
||||
// Replace <dc> with correct datacenter obtained from the last part of the api key
|
||||
$strings = explode('-', $this->_api_key); // TODO: Hungarian notation
|
||||
$strings = explode('-', $this->_api_key);
|
||||
if (is_array($strings) && !empty($strings[1])) {
|
||||
$this->_api_endpoint = str_replace('<dc>', $strings[1], $this->_api_endpoint); // TODO: Hungarian notation
|
||||
$this->_api_endpoint = str_replace('<dc>', $strings[1], $this->_api_endpoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,7 @@
|
||||
|
||||
namespace app\Libraries;
|
||||
|
||||
use CodeIgniter\Encryption\Encryption;
|
||||
use CodeIgniter\Encryption\EncrypterInterface;
|
||||
use Config\OSPOS;
|
||||
use Config\Services;
|
||||
|
||||
|
||||
/**
|
||||
@@ -24,12 +21,7 @@ class Sms_lib
|
||||
{
|
||||
$config = config(OSPOS::class)->settings;
|
||||
|
||||
$encrypter = Services::encrypter();
|
||||
|
||||
$password = $config['msg_pwd'];
|
||||
if (!empty($password)) {
|
||||
$password = $encrypter->decrypt($password);
|
||||
}
|
||||
$password = decryptValue($config['msg_pwd'] ?? null);
|
||||
|
||||
$username = $config['msg_uid'];
|
||||
$originator = $config['msg_src'];
|
||||
|
||||
@@ -327,7 +327,7 @@ class Sale extends Model
|
||||
{
|
||||
$suggestions = [];
|
||||
|
||||
if (!$this->is_valid_receipt($search)) {
|
||||
if (!$this->isValidReceipt($search)) {
|
||||
$builder = $this->db->table('sales');
|
||||
$builder->distinct()->select('first_name, last_name');
|
||||
$builder->join('people', 'people.person_id = sales.customer_id');
|
||||
@@ -408,21 +408,21 @@ class Sale extends Model
|
||||
/**
|
||||
* Checks if valid receipt
|
||||
*/
|
||||
public function is_valid_receipt(string|null &$receipt_sale_id): bool // TODO: like the others, maybe this should be an array rather than a delimited string... either that or the parameter name needs to be changed. $receipt_sale_id implies that it's an int.
|
||||
public function isValidReceipt(string|null &$receiptSaleId): bool // TODO: like the others, maybe this should be an array rather than a delimited string... either that or the parameter name needs to be changed. $receipt_sale_id implies that it's an int.
|
||||
{
|
||||
$config = config(OSPOS::class)->settings;
|
||||
|
||||
if (!empty($receipt_sale_id)) {
|
||||
if (!empty($receiptSaleId)) {
|
||||
// POS #
|
||||
$pieces = explode(' ', $receipt_sale_id);
|
||||
$pieces = explode(' ', trim($receiptSaleId));
|
||||
|
||||
if (count($pieces) == 2 && preg_match('/(POS)/i', $pieces[0])) {
|
||||
return $this->exists($pieces[1]);
|
||||
if (count($pieces) == 2 && strtoupper($pieces[0]) === 'POS' && ctype_digit($pieces[1])) {
|
||||
return $this->exists((int)$pieces[1]);
|
||||
} elseif ($config['invoice_enable']) {
|
||||
$sale_info = $this->get_sale_by_invoice_number($receipt_sale_id);
|
||||
$saleInfo = $this->get_sale_by_invoice_number($receiptSaleId);
|
||||
|
||||
if ($sale_info->getNumRows() > 0) {
|
||||
$receipt_sale_id = 'POS ' . $sale_info->getRow()->sale_id;
|
||||
if ($saleInfo->getNumRows() > 0) {
|
||||
$receiptSaleId = 'POS ' . $saleInfo->getRow()->sale_id;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user