Fixed language modules locale.inc integration.
[fa-stable.git] / includes / hooks.inc
1 <?php
2 /**********************************************************************
3     Copyright (C) FrontAccounting, LLC.
4         Released under the terms of the GNU General Public License, GPL, 
5         as published by the Free Software Foundation, either version 3 
6         of the License, or (at your option) any later version.
7     This program is distributed in the hope that it will be useful,
8     but WITHOUT ANY WARRANTY; without even the implied warranty of
9     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
10     See the License here <http://www.gnu.org/licenses/gpl-3.0.html>.
11 ***********************************************************************/
12 //
13 // FrontAccounting extension modules integration.
14 // This file is included in session.inc even before session is started,
15 // and includes hooks.php connector files from all installed extensions.
16 // To make hooks active install_hooks() have to be called after interface
17 // language is set.
18 //
19 // To find how various hooks are processed look into respective hook_* functions below.
20 //
21 class hooks {
22         var $module_name; // extension module name.
23         var $path = null;
24         // 
25         // Helper for updating databases with extension scheme
26         //
27         // $comp can be company number, -1 for all, 
28         // $updates - table of filename => array(table, field, property)
29         // $check_only - don't update database, check table/field/property existence only
30         //
31         function update_databases($comp, $updates, $check_only=false)
32         {
33                 global $db_connections, $path_to_root;
34         
35                 if ($comp == -1) 
36                         $conn = $db_connections;
37                 else
38                         $conn = array( $comp => $db_connections[$comp]);
39                 $result = true;
40
41                 foreach($conn as $comp => $con) {
42                         set_global_connection($comp);
43                         foreach($updates as $file => $update) {
44                                 $table = @$update[0];
45                                 $field = @$update[1];
46                                 $properties = @$update[2];
47
48                                 $ok = check_table($con['tbpref'], $table, $field, $properties) == 0;
49
50                                 if (!$check_only && !$ok) {
51                                         $ok = db_import($path_to_root.'/modules/'.$this->module_name.'/sql/'.$file,
52                                                 $con);
53                                 }
54                                 $result &= $ok;
55                                 if (!$result)
56                                         break;
57                         }
58                         db_close();
59                         if (!$result)
60                                 break;
61                 }
62                 set_global_connection(0); // return to siteadmin account
63
64                 return $result;
65         }
66         //
67         //      Install additional tabs provided by extension
68         //
69         function install_tabs($app)
70         {
71 //              $app->add_application(new example_class); // add menu tab defined by example_class
72         }
73         //
74         //      Install additonal menu options provided by extension
75         //
76         function install_options($app)
77         {
78 //              global $path_to_root;
79 //
80 //              switch($app->id) {
81 //                      case 'orders':
82 //                              $app->add_rapp_function( 0, _("&Example option"), 
83 //                                      $path_to_root.'/modules/example/example.php?', 'SA_OPEN');
84 //              }
85         }
86         
87         function install_access()
88         {
89 //              $security_areas['SA_EXAMPLE'] = array(SS_EXAMPLE|100, _("Example security area."));
90 //
91 //              $security_sections = array(SS_EXAMPLE => _("Example module implementation"));
92 //
93 //              return array($security_areas, $security_sections);
94         }
95
96         //
97         //      Invoked for all modules before page header is displayed
98         //
99         function pre_header($fun_args)
100         {
101         }
102         //
103         //      Invoked for all modules before page footer is displayed
104         //
105         function pre_footer($fun_args)
106         {
107         }
108
109         //
110         // Price in words. $doc_type is set to document type and can be used to suppress 
111         // price in words printing for selected document types.
112         // Used instead of built in simple english price_in_words() function.
113         //
114         //      Returns: amount in words as string.
115
116         function price_in_words($amount, $doc_type)
117         {
118         }
119
120         //
121         // Exchange rate currency $curr as on date $date.
122         // Keep in mind FA has internally implemented 3 exrate providers
123         // If any of them supports your currency, you can simply use function below
124         // with apprioprate provider set, otherwise implement your own.
125         // Returns: $curr value in home currency units as a real number.
126
127         function retrieve_exrate($curr, $date)
128         {
129 //              $provider = 'ECB'; // 'ECB', 'YAHOO' or 'GOOGLE'
130 //              return get_extern_rate($curr, $provider, $date);
131                 return null;
132         }
133
134         // External authentication
135         // If used should return true after successfull athentication, false otherwise.
136         function authenticate($login, $password)
137         {
138                 return null;
139         }
140         // Generic function called at the end of Tax Report (report 709)
141         // Can be used e.g. for special database updates on every report printing
142         // or to print special tax report footer 
143         //
144         // Returns: nothing
145         function tax_report_done()
146         {
147         }
148         // Following database transaction hooks akcepts array of parameters:
149         // 'cart' => transaction data
150         // 'trans_type' => transaction type
151
152         function db_prewrite(&$cart, $trans_type)
153         {
154                 return true;
155         }
156
157         function db_postwrite(&$cart, $trans_type)
158         {
159                 return true;
160         }
161
162         function db_prevoid($trans_type, $trans_no)
163         {
164                 return true;
165         }
166         /*
167                 This method is called after module install.
168         */
169         function install_extension($check_only=true)
170         {
171                 return true;
172         }
173         /*
174                 This method is called after module uninstall.
175         */
176         function uninstall_extension($check_only=true)
177         {
178                 return true;
179         }
180         /*
181                 This method is called on extension activation for company.
182         */
183         function activate_extension($company, $check_only=true)
184         {
185                 return true;
186         }
187         /*
188                 This method is called when extension is deactivated for company.
189         */
190         function deactivate_extension($company, $check_only=true)
191         {
192                 return true;
193         }
194
195         /*
196          * Returns the quantity allowed to be dispatched for a particular item 
197          * and a status (which can be used to style the row).
198          * This quantity would be the default value on the delivery note.
199          * The usual use case for this is when a item is in stock,
200          * but has been reserved by someone else.
201          * This allows extensions to implements its own priority algorithm.
202          * This function is by detail_id and not item in case the item is present
203          * more than one in  the cart.
204          */
205         /* Default behavior check if there is enough quantity on hand and change the css
206          * class if needed */
207         static function  default_get_dispatchable_quantity($line_item, $location, $date, $qoh) {
208         global $SysPrefs;
209
210                 if ($SysPrefs->allow_negative_stock() || ($line_item->qty_dispatched <= $qoh)) {
211                         return true;
212                 }
213                 return array($qoh, 'stockmankobg');
214         }
215
216 }
217
218 /*
219         Installs hooks provided by extension modules
220 */
221 function install_hooks($lang_code=null)
222 {
223         global $path_to_root, $Hooks, $installed_extensions;
224
225         $Hooks = array();
226         // include current language related $Hooks object if locale file exists
227         if (!$lang_code)
228                 $lang_code = $_SESSION['language']->code;
229         $lang_code  = clean_file_name($lang_code);
230
231         if (file_exists($path_to_root . "/lang/" . $lang_code . "/locale.inc"))
232         {
233                 include_once($path_to_root . "/lang/" . $lang_code . "/locale.inc");
234                 $code = $lang_code;
235                 $hook_class = 'hooks_'.$code;
236                 $Hooks[$code] = new $hook_class;
237                 $Hooks[$code]->path = "/lang/" . $lang_code . "/locale.inc";
238                 unset($code, $hook_class);
239         }
240         // install hooks provided by active extensions
241         if (isset($installed_extensions) && !empty($installed_extensions)) {
242                 foreach($installed_extensions as $ext) {
243                         $hook_class = 'hooks_'.$ext['package'];
244                         if ($ext['active'] && class_exists($hook_class)) {
245                                 $Hooks[$ext['package']] = new $hook_class;
246                                 $Hooks[$ext['package']]->path = $ext['path'];
247                         }
248                 }       
249         }
250 }
251 /*
252         Non active hooks are not included in $Hooks array, so we can use special function to 
253         activate.
254 */
255 function activate_hooks($ext, $comp, $on=true)
256 {
257         global $Hooks;
258
259         $hooks = @$Hooks[$ext];
260         if (!$hooks) {
261                 $hookclass = 'hooks_'.$ext;
262                 if (class_exists($hookclass))
263                         $hooks = new $hookclass;
264                 else
265                         return true; // extension does not have hooks file
266         }
267         if (!$hooks)
268                 return false;
269         elseif ($on)
270                 return $hooks->activate_extension($comp, false);
271         else
272                 return $hooks->deactivate_extension($comp, false);
273 }
274 /*
275         Calls hook $method defined in extension $ext (if any)
276 */
277 function hook_invoke($ext, $method, &$data, $opts=null)
278 {
279
280         global $Hooks;
281
282         $ret = null;
283         if (isset($Hooks[$ext]) && method_exists($Hooks[$ext], $method)) {
284                 set_ext_domain($Hooks[$ext]->path);
285                 $ret = $Hooks[$ext]->$method($data, $opts);
286                 set_ext_domain();
287         } 
288         return $ret;
289 }
290
291 /*
292         Calls hook $methods defined in all extensions (if any)
293 */
294 function hook_invoke_all($method, &$data, $opts=null)
295 {
296
297         global $Hooks;
298         
299         $return = array();
300         if (isset($Hooks))
301         {
302                 foreach($Hooks as $ext => $hook)
303                         if (method_exists($hook, $method)) {
304                                 set_ext_domain($hook->path);
305                                 $result = $hook->$method($data, $opts);
306                                 if (isset($result) && is_array($result)) {
307                                         $return = array_merge_recursive($return, $result);
308                                 } else if (isset($result)) {
309                                         $return[] = $result;
310                                         }
311                                 set_ext_domain();
312                         }
313         }
314         return $return;
315 }
316 /*
317         Returns first non-null result returned from hook.
318 */
319 function hook_invoke_first($method, &$data, $opts=null)
320 {
321
322         global $Hooks;
323
324         $result = null;
325         foreach($Hooks as $ext => $hook) {
326                 if (method_exists($hook, $method)) {
327                         set_ext_domain($hook->path);
328                         $result = $hook->$method($data, $opts);
329                         set_ext_domain();
330                         if (isset($result))
331                                 break;
332                 }
333         }
334         return $result;
335 }
336 /*
337         Returns last non-null result returned from modules method. Helps implement hooks overriding by 
338         extensions installed later.
339 */
340 function hook_invoke_last($method, &$data, $opts=null)
341 {
342
343         global $Hooks;
344
345         $result = null;
346         $Reverse = array_reverse($Hooks);
347         foreach($Reverse as $ext => $hook) {
348                 if (method_exists($hook, $method)) {
349                         set_ext_domain($hook->path);
350                         $result = $hook->$method($data, $opts);
351                         set_ext_domain();
352                         if (isset($result))
353                                 break;
354                 }
355         }
356         return $result;
357 }
358 //------------------------------------------------------------------------------------------
359 //      Database transaction hooks.
360 //      $type - type of transaction (simplifies cart processing)
361 //      $cart - transaction cart
362 //      $args is optional array of parameters
363 //
364 // For FA 2.3 prewrite, postwrite and prevoid hooks are implemented for following transaction types:
365 //
366 // ST_BANKPAYMENT, ST_BANKDEPOSIT, ST_BANKTRANSFER,
367 // ST_SALESORDER, ST_SALESQUOTE, ST_SALESINVOICE, ST_CUSTCREDIT, ST_CUSTPAYMENT, ST_CUSTDELIVERY,
368 // ST_LOCTRANSFER, ST_INVADJUST, 
369 // ST_PURCHORDER, ST_SUPPINVOICE, ST_SUPPCREDIT, ST_SUPPAYMENT, ST_SUPPRECEIVE,
370 // ST_WORKORDER, ST_MANUISSUE, ST_MANURECEIVE, 
371
372 /*
373         Invoked after transaction has been read from database to cart.
374         Not implemented yet.
375 */
376 //function hook_db_postread(&$cart, $type)
377 //{
378 //      hook_invoke_all('db_postread', $cart, $type);
379 //}
380
381 /*
382         Invoked before transaction is written to database.
383 */
384 function hook_db_prewrite(&$cart, $type)
385 {
386         return hook_invoke_all('db_prewrite', $cart, $type);
387 }
388
389 /*
390         Invoked after transaction has been written to database.
391 */
392 function hook_db_postwrite(&$cart, $type)
393 {
394         return hook_invoke_all('db_postwrite', $cart, $type);
395 }
396 /*
397         Invoked before transaction is voided
398 */
399 function hook_db_prevoid($type, $type_no)
400 {
401         return hook_invoke_all('db_prevoid', $type, $type_no);
402 }
403
404 //-------------------------------------------------------------------------------------------
405 //
406 //      Various hooks
407 //
408 //      Alternative exchange rates feeds.
409 //
410 function hook_retrieve_exrate($currency, $date)
411 {
412         return hook_invoke_last('retrieve_exrate', $currency, $date);
413 }
414 //
415 // Generic function called at the end of Tax Report (report 709)
416 //
417 function hook_tax_report_done()
418 {
419         return hook_invoke_all('tax_report_done', $dummy);
420 }
421 //
422 //      Amount in words displayed on various documents (especially sales invoice)
423 //
424 function hook_price_in_words($amount, $document)
425 {
426         return hook_invoke_last('price_in_words', $amount, $document);
427 }
428 //
429 //      Session handling hook. This is special case of hook class which have to be run before session is started.
430 //      If fa_session_manager class is defined in any installed extension, this class provides session handling
431 //      for application, otherwise standard php session handling is used.
432 //
433 function hook_session_start($company)
434 {
435         if (class_exists('fa_session_manager')) {
436                 global $SessionManager;
437                 $SessionManager = new fa_session_manager($company);
438                 return $SessionManager->installed;
439         }
440         return false;
441 }
442 //
443 //      Third party authentication modules.
444 //      Returns true after successfull authentication, false otherwise, null if no login hook is defined.
445 //
446 function hook_authenticate($login, $password)
447 {
448         return hook_invoke_last('authenticate', $login, $password);
449 }
450
451         /*
452          * Returns the quantity allowed to be dispatched for a particular item 
453          * and a "reason" (css classes).
454          * This quantity would be the default value on the delivery note.
455          * The usual use case for this is when a item is in stock,
456          * but has been reserved by someone else.
457          * This allows extensions to implements its own priority algorithm.
458          * This function is by detail_id and not item in case the item is present
459          * more than one in  the cart.
460          * If 'skip' is returned, the line will be skipped and not displayed
461          */
462 function hook_get_dispatchable_quantity($line_item, $location, $date, $qoh) {
463                 $result =  hook_invoke_first('get_dispatchable_quantity', $line_item, array($location, $date, $qoh));
464                 return $result !== null ? $result : hooks::default_get_dispatchable_quantity($line_item, $location, $date, $qoh);
465 }