diff --git a/application/controllers/settings/Business_logic.php b/application/controllers/settings/Business_logic.php
new file mode 100644
index 00000000..1142e28e
--- /dev/null
+++ b/application/controllers/settings/Business_logic.php
@@ -0,0 +1,114 @@
+
+ * @copyright Copyright (c) 2013 - 2020, Alex Tselegidis
+ * @license https://opensource.org/licenses/GPL-3.0 - GPLv3
+ * @link https://easyappointments.org
+ * @since v1.5.0
+ * ---------------------------------------------------------------------------- */
+
+/**
+ * Business logic controller.
+ *
+ * Handles general settings related operations.
+ *
+ * @package Controllers
+ */
+class Business_logic extends EA_Controller {
+ /**
+ * @var array
+ */
+ protected $permissions;
+
+ /**
+ * Business_logic constructor.
+ */
+ public function __construct()
+ {
+ parent::__construct();
+
+ $this->load->model('appointments_model');
+ $this->load->model('customers_model');
+ $this->load->model('services_model');
+ $this->load->model('providers_model');
+ $this->load->model('roles_model');
+ $this->load->model('settings_model');
+
+ $this->load->library('accounts');
+ $this->load->library('google_sync');
+ $this->load->library('notifications');
+ $this->load->library('synchronization');
+ $this->load->library('timezones');
+
+ $role_slug = session('role_slug');
+
+ if ($role_slug)
+ {
+ $this->permissions = $this->roles_model->get_permissions_by_slug($role_slug);
+ }
+ }
+
+ /**
+ * Render the settings page.
+ */
+ public function index()
+ {
+ session(['dest_url' => site_url('services')]);
+
+ if (cannot('view', 'services'))
+ {
+ show_error('Forbidden', 403);
+ }
+
+ $user_id = session('user_id');
+
+ $role_slug = session('role_slug');
+
+ $this->load->view('pages/settings/business_logic/business_logic_page', [
+ 'page_title' => lang('settings'),
+ 'active_menu' => PRIV_SYSTEM_SETTINGS,
+ 'user_display_name' => $this->accounts->get_user_display_name($user_id),
+ 'timezones' => $this->timezones->to_array(),
+ 'privileges' => $this->roles_model->get_permissions_by_slug($role_slug),
+ 'system_settings' => $this->settings_model->get(),
+ ]);
+ }
+
+ /**
+ * Save general settings.
+ */
+ public function save()
+ {
+ try
+ {
+ if ($this->permissions[PRIV_SYSTEM_SETTINGS]['edit'] == FALSE)
+ {
+ throw new Exception('You do not have the required permissions for this task.');
+ }
+
+ $settings = json_decode(request('settings', FALSE), TRUE);
+
+ foreach ($settings as $setting)
+ {
+ $existing_setting = $this->settings_model->query()->where('name', $setting['name'])->get()->row_array();
+
+ if ( ! empty($existing_setting))
+ {
+ $setting['id'] = $existing_setting['id'];
+ }
+
+ $this->settings_model->save($setting);
+ }
+
+ response();
+ }
+ catch (Throwable $e)
+ {
+ json_exception($e);
+ }
+ }
+}
diff --git a/application/views/pages/settings/business_logic/business_logic_page.php b/application/views/pages/settings/business_logic/business_logic_page.php
new file mode 100755
index 00000000..c2b00a43
--- /dev/null
+++ b/application/views/pages/settings/business_logic/business_logic_page.php
@@ -0,0 +1,131 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/assets/js/backend_settings_business_logic.js b/assets/js/backend_settings_business_logic.js
new file mode 100644
index 00000000..0df56dcc
--- /dev/null
+++ b/assets/js/backend_settings_business_logic.js
@@ -0,0 +1,137 @@
+/* ----------------------------------------------------------------------------
+ * Easy!Appointments - Open Source Web Scheduler
+ *
+ * @package EasyAppointments
+ * @author A.Tselegidis
+ * @copyright Copyright (c) 2013 - 2020, Alex Tselegidis
+ * @license http://opensource.org/licenses/GPL-3.0 - GPLv3
+ * @link http://easyappointments.org
+ * @since v1.0.0
+ * ---------------------------------------------------------------------------- */
+
+window.BackendSettingsBusinessLogic = window.BackendSettingsBusinessLogic || {};
+
+/**
+ * Backend Settings
+ *
+ * Contains the functionality of the backend settings page. Can either work for system or user settings,
+ * but the actions allowed to the user are restricted to his role (only admin has full privileges).
+ *
+ * @module BackendSettingsBusinessLogic
+ */
+(function (exports) {
+ 'use strict';
+
+ // Constants
+ exports.SETTINGS_SYSTEM = 'SETTINGS_SYSTEM';
+
+ /**
+ * Use this WorkingPlan class instance to perform actions on the page's working plan tables.
+ *
+ * @type {WorkingPlan}
+ */
+ exports.wp = {};
+
+ /**
+ * Tab settings object.
+ *
+ * @type {Object}
+ */
+ var settings = {};
+
+ /**
+ * Initialize Page
+ *
+ * @param {bool} defaultEventHandlers Optional (true), determines whether to bind the default event handlers.
+ */
+ exports.initialize = function (defaultEventHandlers) {
+ defaultEventHandlers = defaultEventHandlers || true;
+
+ // Apply setting values from database.
+ var workingPlan = {};
+
+ GlobalVariables.settings.system.forEach(function (setting) {
+ $('input[data-field="' + setting.name + '"]').val(setting.value);
+ $('select[data-field="' + setting.name + '"]').val(setting.value);
+
+ if (setting.name === 'company_working_plan') {
+ workingPlan = $.parseJSON(setting.value);
+ }
+ });
+
+ exports.wp = new WorkingPlan();
+ exports.wp.setup(workingPlan);
+ exports.wp.timepickers(false);
+
+ // Set default settings helper.
+ settings = new SystemSettingsBusinessLogicHelper();
+
+ if (defaultEventHandlers) {
+ bindEventHandlers();
+ var $link = $('#settings-page .nav li').not('.d-none').first().find('a');
+ $link.tab('show');
+ }
+
+ // Apply Privileges
+ if (GlobalVariables.user.privileges.system_settings.edit === false) {
+ $('#business-logic').find('select, input, textarea').prop('readonly', true);
+ $('#business-logic').find('button').prop('disabled', true);
+ }
+
+ Backend.placeFooterToBottom();
+ };
+
+ /**
+ * Bind the backend/settings default event handlers.
+ *
+ * This method depends on the backend/settings html, so do not use this method on a different page.
+ */
+ function bindEventHandlers() {
+ exports.wp.bindEventHandlers();
+
+ /**
+ * Event: Save Settings Button "Click"
+ *
+ * Store the setting changes into the database.
+ */
+ $('.save-settings').on('click', function () {
+ var data = settings.get();
+ settings.save(data);
+ });
+
+ /**
+ * Event: Apply Global Working Plan
+ */
+ $('#apply-global-working-plan').on('click', function () {
+ var buttons = [
+ {
+ text: EALang.cancel,
+ click: function () {
+ $('#message-box').dialog('close');
+ }
+ },
+ {
+ text: 'OK',
+ click: function () {
+ var url = GlobalVariables.baseUrl + '/index.php/backend_api/ajax_apply_global_working_plan';
+
+ var data = {
+ csrfToken: GlobalVariables.csrfToken,
+ working_plan: JSON.stringify(exports.wp.get())
+ };
+
+ $.post(url, data)
+ .done(function () {
+ Backend.displayNotification(EALang.working_plans_got_updated);
+ })
+ .always(function () {
+ $('#message-box').dialog('close');
+ });
+ }
+ }
+ ];
+
+ GeneralFunctions.displayMessageBox(EALang.working_plan, EALang.overwrite_existing_working_plans, buttons);
+ });
+ }
+})(window.BackendSettingsBusinessLogic);
diff --git a/assets/js/backend_settings_business_logic_helper.js b/assets/js/backend_settings_business_logic_helper.js
new file mode 100644
index 00000000..5cf47377
--- /dev/null
+++ b/assets/js/backend_settings_business_logic_helper.js
@@ -0,0 +1,123 @@
+/* ----------------------------------------------------------------------------
+ * Easy!Appointments - Open Source Web Scheduler
+ *
+ * @package EasyAppointments
+ * @author A.Tselegidis
+ * @copyright Copyright (c) 2013 - 2020, Alex Tselegidis
+ * @license http://opensource.org/licenses/GPL-3.0 - GPLv3
+ * @link http://easyappointments.org
+ * @since v1.0.0
+ * ---------------------------------------------------------------------------- */
+
+(function () {
+ 'use strict';
+
+ /**
+ * "System Settings" Tab Helper Class
+ *
+ * @class SystemSettingsBusinessLogicHelper
+ */
+ var SystemSettingsBusinessLogicHelper = function () {};
+
+ /**
+ * Save the system settings.
+ *
+ * This method is run after changes are detected on the tab input fields.
+ *
+ * @param {Array} settings Contains the system settings data.
+ */
+ SystemSettingsBusinessLogicHelper.prototype.save = function (settings) {
+ if (!this.validate()) {
+ return; // Validation failed, do not proceed.
+ }
+
+ var url = GlobalVariables.baseUrl + '/index.php/backend_api/ajax_save_settings';
+
+ var data = {
+ csrfToken: GlobalVariables.csrfToken,
+ settings: JSON.stringify(settings),
+ type: BackendSettingsBusinessLogic.SETTINGS_SYSTEM
+ };
+
+ $.post(url, data).done(function () {
+ Backend.displayNotification(EALang.settings_saved);
+
+ // Update the logo title on the header.
+ $('#header-logo span').text($('#company-name').val());
+
+ // Update book_advance_timeout preview
+ var totalMinutes = $('#book-advance-timeout').val();
+ var hours = Math.floor(totalMinutes / 60);
+ var minutes = totalMinutes % 60;
+ $('#book-advance-timeout-helper').text(
+ EALang.book_advance_timeout_hint.replace(
+ '{$limit}',
+ ('0' + hours).slice(-2) + ':' + ('0' + minutes).slice(-2)
+ )
+ );
+
+ // We need to refresh the working plan.
+ var workingPlan = BackendSettingsBusinessLogic.wp.get();
+ BackendSettingsBusinessLogic.wp.setup(workingPlan);
+ BackendSettingsBusinessLogic.wp.timepickers(false);
+ });
+ };
+
+ /**
+ * Prepare the system settings array.
+ *
+ * This method uses the DOM elements of the backend/settings page, so it can't be used in another page.
+ *
+ * @return {Array} Returns the system settings array.
+ */
+ SystemSettingsBusinessLogicHelper.prototype.get = function () {
+ var settings = [];
+
+ // Business Logic Tab
+
+ settings.push({
+ name: 'company_working_plan',
+ value: JSON.stringify(BackendSettingsBusinessLogic.wp.get())
+ });
+
+ settings.push({
+ name: 'book_advance_timeout',
+ value: $('#book-advance-timeout').val()
+ });
+
+ return settings;
+ };
+
+ /**
+ * Validate the settings data.
+ *
+ * If the validation fails then display a message to the user.
+ *
+ * @return {Boolean} Returns the validation result.
+ */
+ SystemSettingsBusinessLogicHelper.prototype.validate = function () {
+ $('#business-logic .has-error').removeClass('has-error');
+
+ try {
+ // Validate required fields.
+ var missingRequired = false;
+ $('#business-logic .required').each(function (index, requiredField) {
+ if (!$(requiredField).val()) {
+ $(requiredField).closest('.form-group').addClass('has-error');
+ missingRequired = true;
+ }
+ });
+
+ if (missingRequired) {
+ throw new Error(EALang.fields_are_required);
+ }
+
+ return true;
+ } catch (error) {
+ Backend.displayNotification(error.message);
+ return false;
+ }
+ };
+
+ window.SystemSettingsBusinessLogicHelper = SystemSettingsBusinessLogicHelper;
+})();