Removed sparse code line in hooks.inc
[fa-stable.git] / includes / hooks.inc
index 079d834ae186ca291d4d1c7d51f464f553137c73..501d4a0f7eb1ca441a103b2d4efc85773e75899d 100644 (file)
@@ -9,7 +9,103 @@
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
     See the License here <http://www.gnu.org/licenses/gpl-3.0.html>.
 ***********************************************************************/
+//
+// 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.
@@ -17,9 +113,9 @@ class hooks {
        //
        //      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.
@@ -28,82 +124,230 @@ class hooks {
        // 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');
+       }
+
 }
 
+/*
+       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, $on=true)
+{
+       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;
+       elseif ($on)
+               return $hooks->activate_extension($comp, false);
+       else
+               return $hooks->deactivate_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.
@@ -133,7 +377,7 @@ function hook_invoke_first($method, &$data, $opts=null) {
 */
 function hook_db_prewrite(&$cart, $type)
 {
-       hook_invoke_all('db_prewrite', $cart, $type);
+       return hook_invoke_all('db_prewrite', $cart, $type);
 }
 
 /*
@@ -141,14 +385,14 @@ function hook_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);
 }
 
 //-------------------------------------------------------------------------------------------
@@ -159,19 +403,57 @@ function hook_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);
 }