mirror of
https://github.com/opensourcepos/opensourcepos.git
synced 2025-12-24 10:07:52 -05:00
- Refactored function names for PSR-12 compliance - Programmatically cascade delete attribute_link rows when a drop-down attribute is deleted but leave attribute_link rows associated with transactions. - Added `WHERE item_id IS NOT NULL` to migration to prevent failure on MySQL databases during migration - Retroactive correction of migration to prevent MySQL databases from failing. - Refactored generic functions to helper - Reverted attribute_links foreign key to ON DELETE RESTRICT which is required for a unique constraint on this table. Cascading deletes are now handled programmatically. - Migration Session table to match Code Igniter 4.6 - Add index to attribute_links to prevent query timeout in items view on large databases - Added overridePrefix() function to the migration_helper. Any time QueryBuilder is adding a prefix to the query when we don't want it to, this query can be used to override the prefix then set it back after you're done. - Added dropAllForeignKeyConstraints() helper function. - Added deleteIndex() helper function. - Added indexExists() helper function. - Added primaryKeyExists() helper function. - Added recreateForeignKeyConstraints() helper function. - Added CRUD section headings to the Attribute model. - Replaced `==` with `===` to prevent type juggling. - Removed unused delete_value function. - Reworked deleteDefinition() and deleteDefinitionList() functions to delete rows from the attribute_links table which are associated. - Added deleteAttributeLinksByDefinitionId() function Implement Cascading Delete - Function to delete attribute links with one or more attribute definitions. - Call function to implement an effective cascading delete. - Refactor function naming to meet PSR-12 conventions Fix Migration - Add drop of Generated Column to prevent failure of migration on MySQL databases. Fix Migration - Removed blank lines - Refactored function naming for PSR compliance - Reformatted code for PSR compliance - Added logic to drop dependent foreign key constraints before deleting an index then recreating them. Migrate ospos_sessions table - DROP and CREATE session table to prevent migration problems on populated databases Fixed Bug in Migration - In the event that item_id = null (e.g., it's a dropdown) it should not be included in the results. Fixed bug in Dropdown deletes - Removed delete_value function in Attributes Controller as it is unused. - Renamed postDelete_attribute_value function for PSR-12 compliance. - Renamed delete_value Attribute model function for PSR-12 compliance. - Refactored out function to getAttributeIdByValue - Replaced == with === to prevent type juggling - Reorganized parts of model to make it easier to find CRUD functions. Refactoring - PSR-12 Compliance formatting changes - Refactored several generic functions into the migration_helper.php - First check if primary key exists before attempting to create it. - Grouped functions together in migration_helper.php - phpdoc commenting functions Optimizing Indices - There are two queries run while opening the Items view which time out on large databases with weak hardware. These indices cut the query execution in half or better. Add Unique constraint back into attribute_links - This migration reverts ospos_attribute_links_ibfk_1 and 2 to ON DELETE RESTRICT. Cascade delete is done programmatically. This is needed to have a unique column on the attribute_links table which prevents duplicate attributes from begin created with the same item_id-attribute_id-definition_id combination Correct spacing after if for PSR-12 Minor code cleanup. - Removed Comments separating sections of code in Attribute model - Removed extra log line to prevent cluttering of the log
973 lines
42 KiB
PHP
973 lines
42 KiB
PHP
<?php
|
|
|
|
namespace App\Controllers;
|
|
|
|
use App\Libraries\Barcode_lib;
|
|
use App\Libraries\Mailchimp_lib;
|
|
use App\Libraries\Receiving_lib;
|
|
use App\Libraries\Sale_lib;
|
|
use App\Libraries\Tax_lib;
|
|
use App\Models\Appconfig;
|
|
use App\Models\Attribute;
|
|
use App\Models\Customer_rewards;
|
|
use App\Models\Dinner_table;
|
|
use App\Models\Module;
|
|
use App\Models\Enums\Rounding_mode;
|
|
use App\Models\Stock_location;
|
|
use App\Models\Tax;
|
|
use CodeIgniter\Database\BaseConnection;
|
|
use CodeIgniter\Encryption\EncrypterInterface;
|
|
use Config\Database;
|
|
use Config\OSPOS;
|
|
use Config\Services;
|
|
use DirectoryIterator;
|
|
use NumberFormatter;
|
|
use ReflectionException;
|
|
|
|
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;
|
|
private Tax_lib $tax_lib;
|
|
private Appconfig $appconfig;
|
|
private Attribute $attribute;
|
|
private Customer_rewards $customer_rewards;
|
|
private Dinner_table $dinner_table;
|
|
protected Module $module;
|
|
private Stock_location $stock_location;
|
|
private Tax $tax;
|
|
private array $config;
|
|
|
|
|
|
public function __construct()
|
|
{
|
|
parent::__construct('config');
|
|
|
|
$this->barcode_lib = new Barcode_lib();
|
|
$this->sale_lib = new Sale_lib();
|
|
$this->receiving_lib = new receiving_lib();
|
|
$this->tax_lib = new Tax_lib();
|
|
$this->appconfig = model(Appconfig::class);
|
|
$this->attribute = model(Attribute::class);
|
|
$this->customer_rewards = model(Customer_rewards::class);
|
|
$this->dinner_table = model(Dinner_table::class);
|
|
$this->module = model(Module::class);
|
|
$this->stock_location = model(Stock_location::class);
|
|
$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');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This function loads all the licenses starting with the first one being OSPOS one
|
|
*/
|
|
private function _licenses(): array // TODO: remove hungarian notation. Super long function. Perhaps we need to refactor out functions?
|
|
{
|
|
$i = 0;
|
|
$composer = false;
|
|
$npmProd = false;
|
|
$npmDev = false;
|
|
$license = [];
|
|
|
|
$license[$i]['title'] = 'Open Source Point Of Sale ' . config('App')->application_version;
|
|
|
|
if (file_exists('license/LICENSE')) {
|
|
$license[$i]['text'] = file_get_contents('license/LICENSE', false, null, 0, 3000);
|
|
} else {
|
|
$license[$i]['text'] = 'LICENSE file must be in OSPOS license directory. You are not allowed to use OSPOS application until the distribution copy of LICENSE file is present.';
|
|
}
|
|
|
|
$dir = new DirectoryIterator('license'); // Read all the files in the dir license
|
|
|
|
foreach ($dir as $fileinfo) { // TODO: $fileinfo doesn't match our variable naming convention
|
|
// License files must be in couples: .version (name & version) & .license (license text)
|
|
if ($fileinfo->isFile()) {
|
|
if ($fileinfo->getExtension() == 'version') {
|
|
++$i;
|
|
|
|
$basename = 'license/' . $fileinfo->getBasename('.version');
|
|
|
|
$license[$i]['title'] = file_get_contents($basename . '.version', false, null, 0, 100);
|
|
|
|
$license_text_file = $basename . '.license';
|
|
|
|
if (file_exists($license_text_file)) {
|
|
$license[$i]['text'] = file_get_contents($license_text_file, false, null, 0, 2000);
|
|
} else {
|
|
$license[$i]['text'] = $license_text_file . ' file is missing';
|
|
}
|
|
} elseif ($fileinfo->getBasename() == 'composer.LICENSES') {
|
|
// Set a flag to indicate that the composer.LICENSES file is available and needs to be attached at the end
|
|
$composer = true;
|
|
} elseif ($fileinfo->getBasename() == 'npm-prod.LICENSES') {
|
|
// Set a flag to indicate that the npm-prod.LICENSES file is available and needs to be attached at the end
|
|
$npmProd = true;
|
|
} elseif ($fileinfo->getBasename() == 'npm-dev.LICENSES') {
|
|
// Set a flag to indicate that the npm-dev.LICENSES file is available and needs to be attached at the end
|
|
$npmDev = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Attach the licenses from the LICENSES file generated by Composer
|
|
if ($composer) {
|
|
++$i;
|
|
$license[$i]['title'] = 'Composer Libraries';
|
|
$license[$i]['text'] = '';
|
|
|
|
$file = file_get_contents('license/composer.LICENSES');
|
|
$array = json_decode($file, true);
|
|
|
|
if (isset($array['dependencies'])) {
|
|
foreach ($array['dependencies'] as $dependency => $details) {
|
|
$license[$i]['text'] .= "library: $dependency\n";
|
|
|
|
foreach ($details as $key => $value) {
|
|
if (is_array($value)) {
|
|
$license[$i]['text'] .= "$key: " . implode(' ', $value) . "\n";
|
|
} else {
|
|
$license[$i]['text'] .= "$key: $value\n";
|
|
}
|
|
}
|
|
|
|
$license[$i]['text'] .= "\n";
|
|
}
|
|
$license[$i]['text'] = rtrim($license[$i]['text'], "\n");
|
|
}
|
|
}
|
|
|
|
// Attach the licenses from the LICENSES file generated by license-report
|
|
if ($npmProd) {
|
|
++$i;
|
|
$license[$i]['title'] = 'NPM Production Libraries';
|
|
$license[$i]['text'] = '';
|
|
|
|
$file = file_get_contents('license/npm-prod.LICENSES');
|
|
$array = json_decode($file, true);
|
|
|
|
foreach ($array as $dependency) {
|
|
$license[$i]['text'] .= "library: {$dependency['name']}\n";
|
|
$license[$i]['text'] .= "authors: {$dependency['author']}\n";
|
|
$license[$i]['text'] .= "website: {$dependency['homepage']}\n";
|
|
$license[$i]['text'] .= "version: {$dependency['installedVersion']}\n";
|
|
$license[$i]['text'] .= "license: {$dependency['licenseType']}\n";
|
|
|
|
$license[$i]['text'] .= "\n";
|
|
}
|
|
$license[$i]['text'] = rtrim($license[$i]['text'], "\n");
|
|
}
|
|
|
|
if ($npmDev) {
|
|
++$i;
|
|
$license[$i]['title'] = 'NPM Development Libraries';
|
|
$license[$i]['text'] = '';
|
|
|
|
$file = file_get_contents('license/npm-dev.LICENSES');
|
|
$array = json_decode($file, true);
|
|
|
|
foreach ($array as $dependency) {
|
|
$license[$i]['text'] .= "library: {$dependency['name']}\n";
|
|
$license[$i]['text'] .= "authors: {$dependency['author']}\n";
|
|
$license[$i]['text'] .= "website: {$dependency['homepage']}\n";
|
|
$license[$i]['text'] .= "version: {$dependency['installedVersion']}\n";
|
|
$license[$i]['text'] .= "license: {$dependency['licenseType']}\n";
|
|
|
|
$license[$i]['text'] .= "\n";
|
|
}
|
|
$license[$i]['text'] = rtrim($license[$i]['text'], "\n");
|
|
}
|
|
|
|
return $license;
|
|
}
|
|
|
|
/**
|
|
* This function loads all the available themes in the dist/bootswatch directory
|
|
* @return array
|
|
*/
|
|
private function _themes(): array // TODO: Hungarian notation
|
|
{
|
|
$themes = [];
|
|
|
|
// Read all themes in the dist folder
|
|
$dir = new DirectoryIterator('resources/bootswatch');
|
|
|
|
foreach ($dir as $dirinfo) { // TODO: $dirinfo doesn't follow naming convention
|
|
if ($dirinfo->isDir() && !$dirinfo->isDot() && $dirinfo->getFileName() != 'fonts') {
|
|
$file = $dirinfo->getFileName();
|
|
$themes[$file] = ucfirst($file);
|
|
}
|
|
}
|
|
|
|
asort($themes);
|
|
|
|
return $themes;
|
|
}
|
|
|
|
/**
|
|
*/
|
|
public function getIndex(): void
|
|
{
|
|
$data['stock_locations'] = $this->stock_location->get_all()->getResultArray();
|
|
$data['dinner_tables'] = $this->dinner_table->get_all()->getResultArray();
|
|
$data['customer_rewards'] = $this->customer_rewards->get_all()->getResultArray();
|
|
$data['support_barcode'] = $this->barcode_lib->get_list_barcodes();
|
|
$data['barcode_fonts'] = $this->barcode_lib->listfonts('fonts');
|
|
$data['logo_exists'] = $this->config['company_logo'] != '';
|
|
$data['line_sequence_options'] = $this->sale_lib->get_line_sequence_options();
|
|
$data['register_mode_options'] = $this->sale_lib->get_register_mode_options();
|
|
$data['invoice_type_options'] = $this->sale_lib->get_invoice_type_options();
|
|
$data['rounding_options'] = rounding_mode::get_rounding_options();
|
|
$data['tax_code_options'] = $this->tax_lib->get_tax_code_options();
|
|
$data['tax_category_options'] = $this->tax_lib->get_tax_category_options();
|
|
$data['tax_jurisdiction_options'] = $this->tax_lib->get_tax_jurisdiction_options();
|
|
$data['show_office_group'] = $this->module->get_show_office_group();
|
|
$data['currency_code'] = $this->config['currency_code'] ?? '';
|
|
$data['dbVersion'] = mysqli_get_server_info($this->db->getConnection());
|
|
|
|
// Load all the license statements, they are already XSS cleaned in the private function
|
|
$data['licenses'] = $this->_licenses();
|
|
|
|
// Load all the themes, already XSS cleaned in the private function
|
|
$data['themes'] = $this->_themes();
|
|
|
|
// General related fields
|
|
$image_allowed_types = ['jpg', 'jpeg', 'gif', 'svg', 'webp', 'bmp', 'png', 'tif', 'tiff'];
|
|
$data['image_allowed_types'] = array_combine($image_allowed_types, $image_allowed_types);
|
|
$data['selected_image_allowed_types'] = explode(',', $this->config['image_allowed_types']);
|
|
|
|
// 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'] = (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'] = '';
|
|
}
|
|
|
|
$data['mailchimp']['lists'] = $this->_mailchimp();
|
|
|
|
echo view('configs/manage', $data);
|
|
}
|
|
|
|
/**
|
|
* Saves company information. Used in app/Views/configs/info_config.php
|
|
*
|
|
* @throws ReflectionException
|
|
* @return void
|
|
* @noinspection PhpUnused
|
|
*/
|
|
public function postSaveInfo(): void
|
|
{
|
|
$upload_data = $this->upload_logo();
|
|
$upload_success = empty($upload_data['error']);
|
|
|
|
$batch_save_data = [
|
|
'company' => $this->request->getPost('company'),
|
|
'address' => $this->request->getPost('address'),
|
|
'phone' => $this->request->getPost('phone'),
|
|
'email' => strtolower($this->request->getPost('email', FILTER_SANITIZE_EMAIL)),
|
|
'fax' => $this->request->getPost('fax'),
|
|
'website' => $this->request->getPost('website', FILTER_SANITIZE_URL),
|
|
'return_policy' => $this->request->getPost('return_policy')
|
|
];
|
|
|
|
if (!empty($upload_data['orig_name']) && $upload_data['raw_name']) {
|
|
$batch_save_data['company_logo'] = $upload_data['raw_name'] . '.' . $upload_data['file_ext'];
|
|
}
|
|
|
|
$result = $this->appconfig->batch_save($batch_save_data);
|
|
$success = $upload_success && $result;
|
|
$message = lang('Config.saved_' . ($success ? '' : 'un') . 'successfully');
|
|
$message = $upload_success ? $message : strip_tags($upload_data['error']);
|
|
|
|
echo json_encode(['success' => $success, 'message' => $message]);
|
|
}
|
|
|
|
|
|
/**
|
|
* @return array
|
|
*/
|
|
private function upload_logo(): array
|
|
{
|
|
$file = $this->request->getFile('company_logo');
|
|
if (!$file) {
|
|
return [];
|
|
}
|
|
|
|
helper(['form']);
|
|
$validation_rule = [
|
|
'company_logo' => [
|
|
'label' => 'Company logo',
|
|
'rules' => [
|
|
'uploaded[company_logo]',
|
|
'is_image[company_logo]',
|
|
'max_size[company_logo,1024]',
|
|
'mime_in[company_logo,image/png,image/jpg,image/jpeg,image/gif]',
|
|
'ext_in[company_logo,png,jpg,gif]',
|
|
'max_dims[company_logo,800,680]',
|
|
]
|
|
]
|
|
];
|
|
|
|
if (!$this->validate($validation_rule)) {
|
|
return (['error' => $this->validator->getError('company_logo')]);
|
|
}
|
|
|
|
|
|
$filename = $file->getClientName();
|
|
$info = pathinfo($filename);
|
|
|
|
$file_info = [
|
|
'orig_name' => $filename,
|
|
'raw_name' => $info['filename'],
|
|
'file_ext' => $file->guessExtension()
|
|
];
|
|
|
|
$file->move(FCPATH . 'uploads/', $file_info['raw_name'] . '.' . $file_info['file_ext'], true);
|
|
|
|
return ($file_info);
|
|
}
|
|
|
|
/**
|
|
* Saves general configuration. Used in app/Views/configs/general_config.php
|
|
*
|
|
* @throws ReflectionException
|
|
* @noinspection PhpUnused
|
|
*/
|
|
public function postSaveGeneral(): void
|
|
{
|
|
$batch_save_data = [
|
|
'theme' => $this->request->getPost('theme'),
|
|
'login_form' => $this->request->getPost('login_form'),
|
|
'default_sales_discount_type' => $this->request->getPost('default_sales_discount_type') != null,
|
|
'default_sales_discount' => parse_decimals($this->request->getPost('default_sales_discount')),
|
|
'default_receivings_discount_type' => $this->request->getPost('default_receivings_discount_type') != null,
|
|
'default_receivings_discount' => parse_decimals($this->request->getPost('default_receivings_discount')),
|
|
'enforce_privacy' => $this->request->getPost('enforce_privacy') != null,
|
|
'receiving_calculate_average_price' => $this->request->getPost('receiving_calculate_average_price') != null,
|
|
'lines_per_page' => $this->request->getPost('lines_per_page', FILTER_SANITIZE_NUMBER_INT),
|
|
'notify_horizontal_position' => $this->request->getPost('notify_horizontal_position'),
|
|
'notify_vertical_position' => $this->request->getPost('notify_vertical_position'),
|
|
'image_max_width' => $this->request->getPost('image_max_width', FILTER_SANITIZE_NUMBER_INT),
|
|
'image_max_height' => $this->request->getPost('image_max_height', FILTER_SANITIZE_NUMBER_INT),
|
|
'image_max_size' => $this->request->getPost('image_max_size', FILTER_SANITIZE_NUMBER_INT),
|
|
'image_allowed_types' => implode(',', $this->request->getPost('image_allowed_types')),
|
|
'gcaptcha_enable' => $this->request->getPost('gcaptcha_enable') != null,
|
|
'gcaptcha_secret_key' => $this->request->getPost('gcaptcha_secret_key'),
|
|
'gcaptcha_site_key' => $this->request->getPost('gcaptcha_site_key'),
|
|
'suggestions_first_column' => $this->request->getPost('suggestions_first_column'),
|
|
'suggestions_second_column' => $this->request->getPost('suggestions_second_column'),
|
|
'suggestions_third_column' => $this->request->getPost('suggestions_third_column'),
|
|
'giftcard_number' => $this->request->getPost('giftcard_number'),
|
|
'derive_sale_quantity' => $this->request->getPost('derive_sale_quantity') != null,
|
|
'multi_pack_enabled' => $this->request->getPost('multi_pack_enabled') != null,
|
|
'include_hsn' => $this->request->getPost('include_hsn') != null,
|
|
'category_dropdown' => $this->request->getPost('category_dropdown') != null
|
|
];
|
|
|
|
$this->module->set_show_office_group($this->request->getPost('show_office_group') != null);
|
|
|
|
if ($batch_save_data['category_dropdown'] == 1) {
|
|
$definition_data['definition_name'] = 'ospos_category';
|
|
$definition_data['definition_flags'] = 0;
|
|
$definition_data['definition_type'] = 'DROPDOWN';
|
|
$definition_data['definition_id'] = CATEGORY_DEFINITION_ID;
|
|
$definition_data['deleted'] = 0;
|
|
|
|
$this->attribute->save_definition($definition_data, CATEGORY_DEFINITION_ID);
|
|
} elseif ($batch_save_data['category_dropdown'] == NO_DEFINITION_ID) {
|
|
$this->attribute->deleteDefinition(CATEGORY_DEFINITION_ID);
|
|
}
|
|
|
|
$success = $this->appconfig->batch_save($batch_save_data);
|
|
|
|
echo json_encode(['success' => $success, 'message' => lang('Config.saved_' . ($success ? '' : 'un') . 'successfully')]);
|
|
}
|
|
|
|
/**
|
|
* Checks a number against the currently selected locale. Used in app/Views/configs/locale_config.php
|
|
*
|
|
* @return void
|
|
* @noinspection PhpUnused
|
|
*/
|
|
public function postCheckNumberLocale(): void
|
|
{
|
|
$number_locale = $this->request->getPost('number_locale');
|
|
$save_number_locale = $this->request->getPost('save_number_locale');
|
|
|
|
$fmt = new NumberFormatter($number_locale, NumberFormatter::CURRENCY);
|
|
if ($number_locale != $save_number_locale) {
|
|
$currency_symbol = $fmt->getSymbol(NumberFormatter::CURRENCY_SYMBOL);
|
|
$currency_code = $fmt->getTextAttribute(NumberFormatter::CURRENCY_CODE);
|
|
$save_number_locale = $number_locale;
|
|
} else {
|
|
$currency_symbol = empty($this->request->getPost('currency_symbol')) ? $fmt->getSymbol(NumberFormatter::CURRENCY_SYMBOL) : $this->request->getPost('currency_symbol');
|
|
$currency_code = empty($this->request->getPost('currency_code')) ? $fmt->getTextAttribute(NumberFormatter::CURRENCY_CODE) : $this->request->getPost('currency_code');
|
|
}
|
|
|
|
if ($this->request->getPost('thousands_separator') == 'false') {
|
|
$fmt->setTextAttribute(NumberFormatter::GROUPING_SEPARATOR_SYMBOL, '');
|
|
}
|
|
|
|
$fmt->setSymbol(NumberFormatter::CURRENCY_SYMBOL, $currency_symbol);
|
|
$number_local_example = $fmt->format(1234567890.12300);
|
|
|
|
echo json_encode([
|
|
'success' => $number_local_example != false,
|
|
'save_number_locale' => $save_number_locale,
|
|
'number_locale_example' => $number_local_example,
|
|
'currency_symbol' => $currency_symbol,
|
|
'currency_code' => $currency_code,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Saves locale configuration. Used in app/Views/configs/locale_config.php
|
|
*
|
|
* @throws ReflectionException
|
|
* @return void
|
|
* @noinspection PhpUnused
|
|
*/
|
|
public function postSaveLocale(): void
|
|
{
|
|
$exploded = explode(":", $this->request->getPost('language'));
|
|
$batch_save_data = [
|
|
'currency_symbol' => $this->request->getPost('currency_symbol'),
|
|
'currency_code' => $this->request->getPost('currency_code'),
|
|
'language_code' => $exploded[0],
|
|
'language' => $exploded[1],
|
|
'timezone' => $this->request->getPost('timezone'),
|
|
'dateformat' => $this->request->getPost('dateformat'),
|
|
'timeformat' => $this->request->getPost('timeformat'),
|
|
'thousands_separator' => $this->request->getPost('thousands_separator') != null,
|
|
'number_locale' => $this->request->getPost('number_locale'),
|
|
'currency_decimals' => $this->request->getPost('currency_decimals', FILTER_SANITIZE_NUMBER_INT),
|
|
'tax_decimals' => $this->request->getPost('tax_decimals', FILTER_SANITIZE_NUMBER_INT),
|
|
'quantity_decimals' => $this->request->getPost('quantity_decimals', FILTER_SANITIZE_NUMBER_INT),
|
|
'country_codes' => htmlspecialchars($this->request->getPost('country_codes')),
|
|
'payment_options_order' => $this->request->getPost('payment_options_order'),
|
|
'date_or_time_format' => $this->request->getPost('date_or_time_format') != null,
|
|
'cash_decimals' => $this->request->getPost('cash_decimals', FILTER_SANITIZE_NUMBER_INT),
|
|
'cash_rounding_code' => $this->request->getPost('cash_rounding_code'),
|
|
'financial_year' => $this->request->getPost('financial_year', FILTER_SANITIZE_NUMBER_INT)
|
|
];
|
|
|
|
$success = $this->appconfig->batch_save($batch_save_data);
|
|
|
|
echo json_encode(['success' => $success, 'message' => lang('Config.saved_' . ($success ? '' : 'un') . 'successfully')]);
|
|
}
|
|
|
|
/**
|
|
* Saves email configuration. Used in app/Views/configs/email_config.php
|
|
*
|
|
* @throws ReflectionException
|
|
* @return void
|
|
* @noinspection PhpUnused
|
|
*/
|
|
public function postSaveEmail(): void
|
|
{
|
|
$password = '';
|
|
|
|
if (check_encryption() && !empty($this->request->getPost('smtp_pass'))) {
|
|
$password = $this->encrypter->encrypt($this->request->getPost('smtp_pass'));
|
|
}
|
|
|
|
$batch_save_data = [
|
|
'protocol' => $this->request->getPost('protocol'),
|
|
'mailpath' => $this->request->getPost('mailpath'),
|
|
'smtp_host' => $this->request->getPost('smtp_host'),
|
|
'smtp_user' => $this->request->getPost('smtp_user'),
|
|
'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')
|
|
];
|
|
|
|
$success = $this->appconfig->batch_save($batch_save_data);
|
|
|
|
echo json_encode(['success' => $success, 'message' => lang('Config.saved_' . ($success ? '' : 'un') . 'successfully')]);
|
|
}
|
|
|
|
/**
|
|
* Saves SMS message configuration. Used in app/Views/configs/message_config.php.
|
|
*
|
|
* @throws ReflectionException
|
|
* @return void
|
|
* @noinspection PhpUnused
|
|
*/
|
|
public function postSaveMessage(): void
|
|
{
|
|
$password = '';
|
|
|
|
if (check_encryption() && !empty($this->request->getPost('msg_pwd'))) {
|
|
$password = $this->encrypter->encrypt($this->request->getPost('msg_pwd'));
|
|
}
|
|
|
|
$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')
|
|
];
|
|
|
|
$success = $this->appconfig->batch_save($batch_save_data);
|
|
|
|
echo json_encode(['success' => $success, 'message' => lang('Config.saved_' . ($success ? '' : 'un') . 'successfully')]);
|
|
}
|
|
|
|
/**
|
|
* This function fetches all the available lists from Mailchimp for the given API key
|
|
*/
|
|
private function _mailchimp(string $api_key = ''): array // TODO: Hungarian notation
|
|
{
|
|
$mailchimp_lib = new Mailchimp_lib(['api_key' => $api_key]);
|
|
|
|
$result = [];
|
|
|
|
$lists = $mailchimp_lib->getLists();
|
|
if ($lists !== false) {
|
|
if (is_array($lists) && !empty($lists['lists']) && is_array($lists['lists'])) {
|
|
foreach ($lists['lists'] as $list) {
|
|
$result[$list['id']] = $list['name'] . ' [' . $list['stats']['member_count'] . ']';
|
|
}
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Gets Mailchimp lists when a valid API key is inserted. Used in app/Views/configs/integrations_config.php
|
|
*
|
|
* @return void
|
|
* @noinspection PhpUnused
|
|
*/
|
|
public function postCheckMailchimpApiKey(): void
|
|
{
|
|
$lists = $this->_mailchimp($this->request->getPost('mailchimp_api_key'));
|
|
$success = count($lists) > 0;
|
|
|
|
echo json_encode([
|
|
'success' => $success,
|
|
'message' => lang('Config.mailchimp_key_' . ($success ? '' : 'un') . 'successfully'),
|
|
'mailchimp_lists' => $lists
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Saves Mailchimp configuration. Used in app/Views/configs/integrations_config.php
|
|
*
|
|
* @throws ReflectionException
|
|
* @return void
|
|
* @noinspection PhpUnused
|
|
*/
|
|
public function postSaveMailchimp(): void
|
|
{
|
|
$api_key = '';
|
|
$list_id = '';
|
|
|
|
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);
|
|
}
|
|
|
|
$list_id_unencrypted = $this->request->getPost('mailchimp_list_id');
|
|
if (!empty($list_id_unencrypted)) {
|
|
$list_id = $this->encrypter->encrypt($list_id_unencrypted);
|
|
}
|
|
}
|
|
|
|
$batch_save_data = ['mailchimp_api_key' => $api_key, 'mailchimp_list_id' => $list_id];
|
|
|
|
$success = $this->appconfig->batch_save($batch_save_data);
|
|
|
|
echo json_encode(['success' => $success, 'message' => lang('Config.saved_' . ($success ? '' : 'un') . 'successfully')]);
|
|
}
|
|
|
|
/**
|
|
* Gets all stock locations. Used in app/Views/configs/stock_config.php
|
|
*
|
|
* @return void
|
|
* @noinspection PhpUnused
|
|
*/
|
|
public function getStockLocations(): void
|
|
{
|
|
$stock_locations = $this->stock_location->get_all()->getResultArray();
|
|
|
|
echo view('partial/stock_locations', ['stock_locations' => $stock_locations]);
|
|
}
|
|
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function getDinnerTables(): void
|
|
{
|
|
$dinner_tables = $this->dinner_table->get_all()->getResultArray();
|
|
|
|
echo view('partial/dinner_tables', ['dinner_tables' => $dinner_tables]);
|
|
}
|
|
|
|
|
|
/**
|
|
* Gets all tax categories.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function ajax_tax_categories(): void // TODO: Is this function called anywhere in the code?
|
|
{
|
|
$tax_categories = $this->tax->get_all_tax_categories()->getResultArray();
|
|
|
|
echo view('partial/tax_categories', ['tax_categories' => $tax_categories]);
|
|
}
|
|
|
|
/**
|
|
* Gets all customer rewards. Used in app/Views/configs/reward_config.php
|
|
*
|
|
* @return void
|
|
* @noinspection PhpUnused
|
|
*/
|
|
public function getCustomerRewards(): void
|
|
{
|
|
$customer_rewards = $this->customer_rewards->get_all()->getResultArray();
|
|
|
|
echo view('partial/customer_rewards', ['customer_rewards' => $customer_rewards]);
|
|
}
|
|
|
|
/**
|
|
* @return void
|
|
*/
|
|
private function _clear_session_state(): void // TODO: Hungarian notation
|
|
{
|
|
$this->sale_lib->clear_sale_location();
|
|
$this->sale_lib->clear_table();
|
|
$this->sale_lib->clear_all();
|
|
$this->receiving_lib = new Receiving_lib();
|
|
$this->receiving_lib->clear_stock_source();
|
|
$this->receiving_lib->clear_stock_destination();
|
|
$this->receiving_lib->clear_all();
|
|
}
|
|
|
|
/**
|
|
* Saves stock locations. Used in app/Views/configs/stock_config.php
|
|
*
|
|
* @return void
|
|
* @noinspection PhpUnused
|
|
*/
|
|
public function postSaveLocations(): void
|
|
{
|
|
$this->db->transStart();
|
|
|
|
$not_to_delete = [];
|
|
foreach ($this->request->getPost() as $key => $value) {
|
|
if (str_contains($key, 'stock_location')) {
|
|
// Save or update
|
|
foreach ($value as $location_id => $location_name) {
|
|
$location_data = ['location_name' => $location_name];
|
|
if ($this->stock_location->save_value($location_data, $location_id)) {
|
|
$location_id = $this->stock_location->get_location_id($location_name);
|
|
$not_to_delete[] = $location_id;
|
|
$this->_clear_session_state();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// All locations not available in post will be deleted now
|
|
$deleted_locations = $this->stock_location->get_all()->getResultArray();
|
|
|
|
foreach ($deleted_locations as $location => $location_data) {
|
|
if (!in_array($location_data['location_id'], $not_to_delete)) {
|
|
$this->stock_location->delete($location_data['location_id']);
|
|
}
|
|
}
|
|
|
|
$this->db->transComplete();
|
|
|
|
$success = $this->db->transStatus();
|
|
|
|
echo json_encode(['success' => $success, 'message' => lang('Config.saved_' . ($success ? '' : 'un') . 'successfully')]);
|
|
}
|
|
|
|
/**
|
|
* Saves all dinner tables. Used in app/Views/configs/table_config.php
|
|
*
|
|
* @throws ReflectionException
|
|
* @return void
|
|
* @noinspection PhpUnused
|
|
*/
|
|
public function postSaveTables(): void
|
|
{
|
|
$this->db->transStart();
|
|
|
|
$dinner_table_enable = $this->request->getPost('dinner_table_enable') != null;
|
|
|
|
$this->appconfig->save(['dinner_table_enable' => $dinner_table_enable]);
|
|
|
|
if ($dinner_table_enable) {
|
|
$not_to_delete = [];
|
|
foreach ($this->request->getPost() as $key => $value) { // TODO: Not sure if this is the best way to filter the array
|
|
if (strstr($key, 'dinner_table') && $key != 'dinner_table_enable') {
|
|
$dinner_table_id = preg_replace("/.*?_(\d+)$/", "$1", $key);
|
|
$not_to_delete[] = $dinner_table_id;
|
|
|
|
// Save or update
|
|
$table_data = ['name' => $value];
|
|
if ($this->dinner_table->save_value($table_data, $dinner_table_id)) {
|
|
$this->_clear_session_state(); // TODO: Remove hungarian notation.
|
|
}
|
|
}
|
|
}
|
|
|
|
// All tables not available in post will be deleted now
|
|
$deleted_tables = $this->dinner_table->get_all()->getResultArray();
|
|
|
|
foreach ($deleted_tables as $dinner_tables => $table) {
|
|
if (!in_array($table['dinner_table_id'], $not_to_delete)) {
|
|
$this->dinner_table->delete($table['dinner_table_id']);
|
|
}
|
|
}
|
|
}
|
|
|
|
$this->db->transComplete();
|
|
|
|
$success = $this->db->transStatus();
|
|
|
|
echo json_encode(['success' => $success, 'message' => lang('Config.saved_' . ($success ? '' : 'un') . 'successfully')]);
|
|
}
|
|
|
|
/**
|
|
* Saves tax configuration. Used in app/Views/configs/tax_config.php
|
|
*
|
|
* @throws ReflectionException
|
|
* @return void
|
|
* @noinspection PhpUnused
|
|
*/
|
|
public function postSaveTax(): void
|
|
{
|
|
$default_tax_1_rate = $this->request->getPost('default_tax_1_rate');
|
|
$default_tax_2_rate = $this->request->getPost('default_tax_2_rate');
|
|
|
|
$batch_save_data = [
|
|
'default_tax_1_rate' => parse_tax(filter_var($default_tax_1_rate, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION)),
|
|
'default_tax_1_name' => $this->request->getPost('default_tax_1_name'),
|
|
'default_tax_2_rate' => parse_tax(filter_var($default_tax_2_rate, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION)),
|
|
'default_tax_2_name' => $this->request->getPost('default_tax_2_name'),
|
|
'tax_included' => $this->request->getPost('tax_included') != null,
|
|
'use_destination_based_tax' => $this->request->getPost('use_destination_based_tax') != null,
|
|
'default_tax_code' => $this->request->getPost('default_tax_code'),
|
|
'default_tax_category' => $this->request->getPost('default_tax_category'),
|
|
'default_tax_jurisdiction' => $this->request->getPost('default_tax_jurisdiction'),
|
|
'tax_id' => $this->request->getPost('tax_id', FILTER_SANITIZE_NUMBER_INT)
|
|
];
|
|
|
|
$success = $this->appconfig->batch_save($batch_save_data);
|
|
|
|
$message = lang('Config.saved_' . ($success ? '' : 'un') . 'successfully');
|
|
|
|
echo json_encode(['success' => $success, 'message' => $message]);
|
|
}
|
|
|
|
/**
|
|
* Saves customer rewards configuration. Used in app/Views/configs/reward_config.php
|
|
*
|
|
* @throws ReflectionException
|
|
* @return void
|
|
* @noinspection PhpUnused
|
|
*/
|
|
public function postSaveRewards(): void
|
|
{
|
|
$this->db->transStart();
|
|
|
|
$customer_reward_enable = $this->request->getPost('customer_reward_enable') != null;
|
|
|
|
$this->appconfig->save(['customer_reward_enable' => $customer_reward_enable]);
|
|
|
|
if ($customer_reward_enable) {
|
|
$not_to_delete = [];
|
|
$array_save = [];
|
|
foreach ($this->request->getPost() as $key => $value) {
|
|
if (strstr($key, 'customer_reward') && $key != 'customer_reward_enable') {
|
|
$customer_reward_id = preg_replace("/.*?_(\d+)$/", "$1", $key);
|
|
$not_to_delete[] = $customer_reward_id;
|
|
$array_save[$customer_reward_id]['package_name'] = $value;
|
|
} elseif (str_contains($key, 'reward_points')) {
|
|
$customer_reward_id = preg_replace("/.*?_(\d+)$/", "$1", $key);
|
|
$array_save[$customer_reward_id]['points_percent'] = $value;
|
|
}
|
|
}
|
|
|
|
if (!empty($array_save)) {
|
|
foreach ($array_save as $key => $value) {
|
|
// Save or update
|
|
$package_data = ['package_name' => $value['package_name'], 'points_percent' => $value['points_percent']];
|
|
$this->customer_rewards->save_value($package_data, $key); // TODO: reflection exception
|
|
}
|
|
}
|
|
|
|
// All packages not available in post will be deleted now
|
|
$deleted_packages = $this->customer_rewards->get_all()->getResultArray();
|
|
|
|
foreach ($deleted_packages as $customer_rewards => $reward_category) {
|
|
if (!in_array($reward_category['package_id'], $not_to_delete)) {
|
|
$this->customer_rewards->delete($reward_category['package_id']);
|
|
}
|
|
}
|
|
}
|
|
|
|
$this->db->transComplete();
|
|
|
|
$success = $this->db->transStatus();
|
|
|
|
echo json_encode(['success' => $success, 'message' => lang('Config.saved_' . ($success ? '' : 'un') . 'successfully')]);
|
|
}
|
|
|
|
/**
|
|
* Saves barcode configuration. Used in app/Views/configs/barcode_config.php
|
|
*
|
|
* @throws ReflectionException
|
|
* @return void
|
|
* @noinspection PhpUnused
|
|
*/
|
|
public function postSaveBarcode(): void
|
|
{
|
|
$batch_save_data = [
|
|
'barcode_type' => $this->request->getPost('barcode_type'),
|
|
'barcode_width' => $this->request->getPost('barcode_width', FILTER_SANITIZE_NUMBER_INT),
|
|
'barcode_height' => $this->request->getPost('barcode_height', FILTER_SANITIZE_NUMBER_INT),
|
|
'barcode_font' => $this->request->getPost('barcode_font'),
|
|
'barcode_font_size' => $this->request->getPost('barcode_font_size', FILTER_SANITIZE_NUMBER_INT),
|
|
'barcode_first_row' => $this->request->getPost('barcode_first_row'),
|
|
'barcode_second_row' => $this->request->getPost('barcode_second_row'),
|
|
'barcode_third_row' => $this->request->getPost('barcode_third_row'),
|
|
'barcode_num_in_row' => $this->request->getPost('barcode_num_in_row', FILTER_SANITIZE_NUMBER_INT),
|
|
'barcode_page_width' => $this->request->getPost('barcode_page_width', FILTER_SANITIZE_NUMBER_INT),
|
|
'barcode_page_cellspacing' => $this->request->getPost('barcode_page_cellspacing', FILTER_SANITIZE_NUMBER_INT),
|
|
'barcode_generate_if_empty' => $this->request->getPost('barcode_generate_if_empty') != null,
|
|
'allow_duplicate_barcodes' => $this->request->getPost('allow_duplicate_barcodes') != null,
|
|
'barcode_content' => $this->request->getPost('barcode_content'),
|
|
'barcode_formats' => json_encode($this->request->getPost('barcode_formats'))
|
|
];
|
|
|
|
$success = $this->appconfig->batch_save($batch_save_data);
|
|
|
|
echo json_encode(['success' => $success, 'message' => lang('Config.saved_' . ($success ? '' : 'un') . 'successfully')]);
|
|
}
|
|
|
|
/**
|
|
* Saves receipt configuration. Used in app/Views/configs/receipt_config.php.
|
|
*
|
|
* @throws ReflectionException
|
|
* @return void
|
|
* @noinspection PhpUnused
|
|
*/
|
|
public function postSaveReceipt(): void
|
|
{
|
|
$batch_save_data = [
|
|
'receipt_template' => $this->request->getPost('receipt_template'),
|
|
'receipt_font_size' => $this->request->getPost('receipt_font_size', FILTER_SANITIZE_NUMBER_INT),
|
|
'print_delay_autoreturn' => $this->request->getPost('print_delay_autoreturn', FILTER_SANITIZE_NUMBER_INT),
|
|
'email_receipt_check_behaviour' => $this->request->getPost('email_receipt_check_behaviour'),
|
|
'print_receipt_check_behaviour' => $this->request->getPost('print_receipt_check_behaviour'),
|
|
'receipt_show_company_name' => $this->request->getPost('receipt_show_company_name') != null,
|
|
'receipt_show_taxes' => $this->request->getPost('receipt_show_taxes') != null,
|
|
'receipt_show_tax_ind' => $this->request->getPost('receipt_show_tax_ind') != null,
|
|
'receipt_show_total_discount' => $this->request->getPost('receipt_show_total_discount') != null,
|
|
'receipt_show_description' => $this->request->getPost('receipt_show_description') != null,
|
|
'receipt_show_serialnumber' => $this->request->getPost('receipt_show_serialnumber') != null,
|
|
'print_silently' => $this->request->getPost('print_silently') != null,
|
|
'print_header' => $this->request->getPost('print_header') != null,
|
|
'print_footer' => $this->request->getPost('print_footer') != null,
|
|
'print_top_margin' => $this->request->getPost('print_top_margin', FILTER_SANITIZE_NUMBER_INT),
|
|
'print_left_margin' => $this->request->getPost('print_left_margin', FILTER_SANITIZE_NUMBER_INT),
|
|
'print_bottom_margin' => $this->request->getPost('print_bottom_margin', FILTER_SANITIZE_NUMBER_INT),
|
|
'print_right_margin' => $this->request->getPost('print_right_margin', FILTER_SANITIZE_NUMBER_INT)
|
|
];
|
|
|
|
$success = $this->appconfig->batch_save($batch_save_data);
|
|
|
|
echo json_encode(['success' => $success, 'message' => lang('Config.saved_' . ($success ? '' : 'un') . 'successfully')]);
|
|
}
|
|
|
|
/**
|
|
* Saves invoice configuration. Used in app/Views/configs/invoice_config.php.
|
|
*
|
|
* @throws ReflectionException
|
|
* @return void
|
|
* @noinspection PhpUnused
|
|
*/
|
|
public function postSaveInvoice(): void
|
|
{
|
|
$batch_save_data = [
|
|
'invoice_enable' => $this->request->getPost('invoice_enable') != null,
|
|
'sales_invoice_format' => $this->request->getPost('sales_invoice_format'),
|
|
'sales_quote_format' => $this->request->getPost('sales_quote_format'),
|
|
'recv_invoice_format' => $this->request->getPost('recv_invoice_format'),
|
|
'invoice_default_comments' => $this->request->getPost('invoice_default_comments'),
|
|
'invoice_email_message' => $this->request->getPost('invoice_email_message'),
|
|
'line_sequence' => $this->request->getPost('line_sequence'),
|
|
'last_used_invoice_number' => $this->request->getPost('last_used_invoice_number', FILTER_SANITIZE_NUMBER_INT),
|
|
'last_used_quote_number' => $this->request->getPost('last_used_quote_number', FILTER_SANITIZE_NUMBER_INT),
|
|
'quote_default_comments' => $this->request->getPost('quote_default_comments'),
|
|
'work_order_enable' => $this->request->getPost('work_order_enable') != null,
|
|
'work_order_format' => $this->request->getPost('work_order_format'),
|
|
'last_used_work_order_number' => $this->request->getPost('last_used_work_order_number', FILTER_SANITIZE_NUMBER_INT),
|
|
'invoice_type' => $this->request->getPost('invoice_type')
|
|
];
|
|
|
|
$success = $this->appconfig->batch_save($batch_save_data);
|
|
|
|
// Update the register mode with the latest change so that if the user
|
|
// switches immediately back to the register the mode reflects the change
|
|
if ($success) {
|
|
if ($this->config['invoice_enable']) {
|
|
$this->sale_lib->set_mode($this->config['default_register_mode']);
|
|
} else {
|
|
$this->sale_lib->set_mode('sale');
|
|
}
|
|
}
|
|
|
|
echo json_encode(['success' => $success, 'message' => lang('Config.saved_' . ($success ? '' : 'un') . 'successfully')]);
|
|
}
|
|
|
|
/**
|
|
* Removes the company logo from the database. Used in app/Views/configs/info_config.php.
|
|
*
|
|
* @return void
|
|
* @throws ReflectionException
|
|
* @noinspection PhpUnused
|
|
*/
|
|
public function postRemoveLogo(): void
|
|
{
|
|
$success = $this->appconfig->save(['company_logo' => '']);
|
|
|
|
echo json_encode(['success' => $success]);
|
|
}
|
|
}
|