MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the License here <http://www.gnu.org/licenses/gpl-3.0.html>.
***********************************************************************/
-class Hook{
+//
+// FrontAccounting extension modules integration.
+// This file is included in session.inc even before session is started,
+// and includes hooks.php connector files from all installed extensions.
+// To make hooks active install_hooks() have to be called after interface
+// language is set.
+//
+// To find how various hooks are processed look into respective hook_* functions below.
+//
+class hooks {
+ var $module_name; // extension module name.
+
+ //
+ // Helper for updating databases with extension scheme
+ //
+ // $comp can be company number, -1 for all,
+ // $updates - table of filename => array(table, field, property)
+ // $check_only - don't update database, check table/field/property existence only
+ //
+ function update_databases($comp, $updates, $check_only=false)
+ {
+ global $db_connections, $path_to_root;
+
+ if ($comp == -1)
+ $conn = $db_connections;
+ else
+ $conn = array( $comp => $db_connections[$comp]);
+ $result = true;
+
+ foreach($conn as $comp => $con) {
+ set_global_connection($comp);
+ foreach($updates as $file => $update) {
+ $table = @$update[0];
+ $field = @$update[1];
+ $properties = @$update[2];
+
+ $ok = check_table($con['tbpref'], $table, $field, $properties) == 0;
+
+ if (!$check_only && !$ok) {
+ $ok = db_import($path_to_root.'/modules/'.$this->module_name.'/sql/'.$file,
+ $con);
+ }
+ $result &= $ok;
+ if (!$result)
+ break;
+ }
+ db_close();
+ if (!$result)
+ break;
+ }
+ set_global_connection(0); // return to siteadmin account
+
+ return $result;
+ }
+ //
+ // Install additional tabs provided by extension
+ //
+ function install_tabs($app)
+ {
+// $app->add_application(new example_class); // add menu tab defined by example_class
+ }
+ //
+ // Install additonal menu options provided by extension
+ //
+ function install_options($app)
+ {
+// global $path_to_root;
+//
+// switch($app->id) {
+// case 'orders':
+// $app->add_rapp_function( 0, _("&Example option"),
+// $path_to_root.'/modules/example/example.php?', 'SA_OPEN');
+// }
+ }
+
+ function install_access()
+ {
+// $security_areas['SA_EXAMPLE'] = array(SS_EXAMPLE|100, _("Example security area."));
+//
+// $security_sections = array(SS_EXAMPLE => _("Example module implementation"));
+//
+// return array($security_areas, $security_sections);
+ }
+
+ //
+ // Invoked for all modules before page header is displayed
+ //
+ function pre_header($fun_args)
+ {
+ }
+ //
+ // Invoked for all modules before page footer is displayed
+ //
+ function pre_footer($fun_args)
+ {
+ }
+
//
// Price in words. $doc_type is set to document type and can be used to suppress
// price in words printing for selected document types.
//
// Returns: amount in words as string.
-// function price_in_words($amount, $doc_type)
-// {
-// }
+ function price_in_words($amount, $doc_type)
+ {
+ }
//
// Exchange rate currency $curr as on date $date.
// with apprioprate provider set, otherwise implement your own.
// Returns: $curr value in home currency units as a real number.
-// function retrieve_ex_rate($curr, $date)
-// {
+ function retrieve_exrate($curr, $date)
+ {
// $provider = 'ECB'; // 'ECB', 'YAHOO' or 'GOOGLE'
// return get_extern_rate($curr, $provider, $date);
-// }
+ return null;
+ }
+ // External authentication
+ // If used should return true after successfull athentication, false otherwise.
+ function authenticate($login, $password)
+ {
+ return null;
+ }
// Generic function called at the end of Tax Report (report 709)
// Can be used e.g. for special database updates on every report printing
// or to print special tax report footer
//
// Returns: nothing
-// function tax_report_done()
-// {
-// }
+ function tax_report_done()
+ {
+ }
// Following database transaction hooks akcepts array of parameters:
// 'cart' => transaction data
// 'trans_type' => transaction type
-// function db_prewrite(&$cart, $trans_type)
-// {
-// }
-// function db_postwrite(&$cart, $trans_type)
-// {
-// }
-// function db_prevoid($trans_type, $trans_no)
-// {
-// }
+ function db_prewrite(&$cart, $trans_type)
+ {
+ return true;
+ }
+
+ function db_postwrite(&$cart, $trans_type)
+ {
+ return true;
+ }
+
+ function db_prevoid($trans_type, $trans_no)
+ {
+ return true;
+ }
+ /*
+ This method is called after module install.
+ */
+ function install_extension($check_only=true)
+ {
+ return true;
+ }
+ /*
+ This method is called after module uninstall.
+ */
+ function uninstall_extension($check_only=true)
+ {
+ return true;
+ }
+ /*
+ This method is called on extension activation for company.
+ */
+ function activate_extension($company, $check_only=true)
+ {
+ return true;
+ }
+ /*
+ This method is called when extension is deactivated for company.
+ */
+ function deactivate_extension($company, $check_only=true)
+ {
+ return true;
+ }
+
+ /*
+ * Returns the quantity allowed to be dispatched for a particular item
+ * and a status (which can be used to style the row).
+ * This quantity would be the default value on the delivery note.
+ * The usual use case for this is when a item is in stock,
+ * but has been reserved by someone else.
+ * This allows extensions to implements its own priority algorithm.
+ * This function is by detail_id and not item in case the item is present
+ * more than one in the cart.
+ */
+ /* Default behavior check if there is enough quantity on hand and change the css
+ * class if needed */
+ static function default_get_dispatchable_quantity($line_item, $location, $date, $qoh) {
+ global $SysPrefs;
+
+ if ($SysPrefs->allow_negative_stock() || ($line_item->qty_dispatched <= $qoh)) {
+ return true;
+ }
+ return array($qoh, 'stockmankobg');
+ return array($line_item->qty_dispatched, 'stockmankobg');
+ }
+
}
+/*
+ Installs hooks provided by extension modules
+*/
+function install_hooks()
+{
+ global $path_to_root, $Hooks, $installed_extensions;
+
+ $Hooks = array();
+
+ // include current language related $Hooks object if locale file exists
+ $lang_code = clean_file_name($_SESSION['language']->code);
+ if (file_exists($path_to_root . "/lang/" . $lang_code . "/locale.inc"))
+ {
+ include_once($path_to_root . "/lang/" . $lang_code . "/locale.inc");
+ $code = $_SESSION['language']->code;
+ $hook_class = 'hooks_'.$code;
+ $Hooks[$code] = new $hook_class;
+ unset($code, $hook_class);
+ }
+ // install hooks provided by active extensions
+ foreach($installed_extensions as $ext) {
+ $hook_class = 'hooks_'.$ext['package'];
+ if ($ext['active'] && class_exists($hook_class)) {
+ $Hooks[$ext['package']] = new $hook_class;
+ }
+ }
+}
+/*
+ Non active hooks are not included in $Hooks array, so we can use special function to
+ activate.
+*/
+function activate_hooks($ext, $comp)
+{
+ global $Hooks;
+
+ $hooks = @$Hooks[$ext];
+ if (!$hooks) {
+ $hookclass = 'hooks_'.$ext;
+ if (class_exists($hookclass))
+ $hooks = new $hookclass;
+ else
+ return true; // extension does not have hooks file
+ }
+ if (!$hooks)
+ return false;
+ else
+ return $hooks->activate_extension($comp, false);
+}
/*
Calls hook $method defined in extension $ext (if any)
*/
-function hook_invoke($ext, &$data, $opts=null) {
+function hook_invoke($ext, $method, &$data, $opts=null)
+{
global $Hooks;
-
+
+ $ret = null;
if (isset($Hooks[$ext]) && method_exists($Hooks[$ext], $method)) {
- $Hooks[$ext]->$method($data, $opts);
- }
+ set_ext_domain('modules/'.$ext);
+ $ret = $Hooks[$ext]->$method($data, $opts);
+ set_ext_domain();
+ }
+ return $ret;
}
/*
Calls hook $methods defined in all extensions (if any)
*/
-function hook_invoke_all($method, &$data, $opts=null) {
+function hook_invoke_all($method, &$data, $opts=null)
+{
global $Hooks;
- $result = array();
- foreach($Hooks as $ext => $hook)
+ $return = array();
+ if (isset($Hooks))
+ {
+ foreach($Hooks as $ext => $hook)
+ if (method_exists($hook, $method)) {
+ set_ext_domain('modules/'.$ext);
+ $result = $hook->$method($data, $opts);
+ if (isset($result) && is_array($result)) {
+ $return = array_merge_recursive($return, $result);
+ } else if (isset($result)) {
+ $return[] = $result;
+ }
+ set_ext_domain();
+ }
+ }
+ return $return;
+}
+/*
+ Returns first non-null result returned from hook.
+*/
+function hook_invoke_first($method, &$data, $opts=null)
+{
+
+ global $Hooks;
+
+ $result = null;
+ foreach($Hooks as $ext => $hook) {
if (method_exists($hook, $method)) {
- $result = $Hooks[$ext]->$method($data, $opts);
- if (isset($result) && is_array($result)) {
- $return = array_merge_recursive($return, $result);
- } else if (isset($result)) {
- $return[] = $result;
- }
+ set_ext_domain('modules/'.$ext);
+ $result = $hook->$method($data, $opts);
+ set_ext_domain();
+ if (isset($result))
+ break;
}
+ }
return $result;
}
/*
- Returns first non-null result returned from hook.
+ Returns last non-null result returned from modules method. Helps implement hooks overriding by
+ extensions installed later.
*/
-function hook_invoke_first($method, &$data, $opts=null) {
+function hook_invoke_last($method, &$data, $opts=null)
+{
global $Hooks;
-
- $result = array();
- foreach($Hooks as $ext => $hook) {
+
+ $result = null;
+ $Reverse = array_reverse($Hooks);
+ foreach($Reverse as $ext => $hook) {
if (method_exists($hook, $method)) {
- $result = $Hooks[$ext]->$method($data, $opts);
+ set_ext_domain('modules/'.$ext);
+ $result = $hook->$method($data, $opts);
+ set_ext_domain();
if (isset($result))
- return $result;
+ break;
}
}
- return null;
+ return $result;
}
//------------------------------------------------------------------------------------------
// Database transaction hooks.
*/
function hook_db_prewrite(&$cart, $type)
{
- hook_invoke_all('db_prewrite', $cart, $type);
+ return hook_invoke_all('db_prewrite', $cart, $type);
}
/*
*/
function hook_db_postwrite(&$cart, $type)
{
- hook_invoke_all('db_postwrite', $cart, $type);
+ return hook_invoke_all('db_postwrite', $cart, $type);
}
/*
Invoked before transaction is voided
*/
function hook_db_prevoid($type, $type_no)
{
- hook_invoke_all('db_prevoid', $type, $type_no);
+ return hook_invoke_all('db_prevoid', $type, $type_no);
}
//-------------------------------------------------------------------------------------------
//
function hook_retrieve_exrate($currency, $date)
{
- return hook_invoke_first('retrieve_exrate', $currency, $date);
+ return hook_invoke_last('retrieve_exrate', $currency, $date);
}
//
// Generic function called at the end of Tax Report (report 709)
//
function hook_tax_report_done()
{
- hook_invoke_all('tax_report_done', $dummy);
+ return hook_invoke_all('tax_report_done', $dummy);
}
//
// Amount in words displayed on various documents (especially sales invoice)
//
function hook_price_in_words($amount, $document)
{
- return hook_invoke_first('price_in_words', $amount, $document);
+ return hook_invoke_last('price_in_words', $amount, $document);
+}
+//
+// Session handling hook. This is special case of hook class which have to be run before session is started.
+// If fa_session_manager class is defined in any installed extension, this class provides session handling
+// for application, otherwise standard php session handling is used.
+//
+function hook_session_start($company)
+{
+ if (class_exists('fa_session_manager')) {
+ global $SessionManager;
+ $SessionManager = new fa_session_manager($company);
+ return $SessionManager->installed;
+ }
+ return false;
+}
+//
+// Third party authentication modules.
+// Returns true after successfull authentication, false otherwise, null if no login hook is defined.
+//
+function hook_authenticate($login, $password)
+{
+ return hook_invoke_last('authenticate', $login, $password);
+}
+
+ /*
+ * Returns the quantity allowed to be dispatched for a particular item
+ * and a "reason" (css classes).
+ * This quantity would be the default value on the delivery note.
+ * The usual use case for this is when a item is in stock,
+ * but has been reserved by someone else.
+ * This allows extensions to implements its own priority algorithm.
+ * This function is by detail_id and not item in case the item is present
+ * more than one in the cart.
+ * If 'skip' is returned, the line will be skipped and not displayed
+ */
+function hook_get_dispatchable_quantity($line_item, $location, $date, $qoh) {
+ $result = hook_invoke_first('get_dispatchable_quantity', $line_item, array($location, $date, $qoh));
+ return $result !== null ? $result : hooks::default_get_dispatchable_quantity($line_item, $location, $date, $qoh);
}