New/obsolete files update on 2.2
authorJanusz Dobrowolski <janusz@frontaccounting.eu>
Thu, 19 Nov 2009 16:03:05 +0000 (16:03 +0000)
committerJanusz Dobrowolski <janusz@frontaccounting.eu>
Thu, 19 Nov 2009 16:03:05 +0000 (16:03 +0000)
29 files changed:
access/timeout.php [new file with mode: 0644]
admin/db/security_db.inc [new file with mode: 0644]
admin/db/tags_db.inc [new file with mode: 0644]
admin/security_roles.php [new file with mode: 0644]
admin/tags.php [new file with mode: 0644]
config.default.php [new file with mode: 0644]
config.php [deleted file]
config_db.php [deleted file]
doc/2.2_Beta.txt [new file with mode: 0644]
doc/access_levels.txt [new file with mode: 0644]
doc/calculate_price.txt [new file with mode: 0644]
doc/sales_quotations.txt [new file with mode: 0644]
gl/inquiry/journal_inquiry.php [new file with mode: 0644]
includes/access_levels.inc [new file with mode: 0644]
includes/db/audit_trail_db.inc [new file with mode: 0644]
includes/reserved.inc [deleted file]
installed_extensions.php [deleted file]
js/payalloc.js [new file with mode: 0644]
lang/installed_languages.inc [deleted file]
modules/installed_modules.php [deleted file]
reporting/rep111.php [new file with mode: 0644]
reporting/rep305.php [new file with mode: 0644]
reporting/rep409.php [new file with mode: 0644]
reporting/rep710.php [new file with mode: 0644]
sql/alter2.2.php [new file with mode: 0644]
sql/alter2.2.sql [new file with mode: 0644]
sql/alter2.2rc.sql [new file with mode: 0644]
themes/aqua/images/escape.png [new file with mode: 0644]
themes/cool/images/escape.png [new file with mode: 0644]

diff --git a/access/timeout.php b/access/timeout.php
new file mode 100644 (file)
index 0000000..c91dbc0
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+/**********************************************************************
+    Copyright (C) FrontAccounting, LLC.
+       Released under the terms of the GNU General Public License, GPL, 
+       as published by the Free Software Foundation, either version 3 
+       of the License, or (at your option) any later version.
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+    See the License here <http://www.gnu.org/licenses/gpl-3.0.html>.
+***********************************************************************/
+/*
+       User authentication page popped up after login timeout during ajax call.
+*/
+$path_to_root = '..';
+$page_security = 'SA_OPEN';
+include_once($path_to_root . "/includes/session.inc");
+
+include($path_to_root .'/access/login.php');
+
+if (get_post('SubmitUser') && $_SESSION['wa_current_user']->logged_in()) {
+       // After successfull login repeat last ajax call.
+       // Login form consists all post variables from last ajax call.
+echo "<script>
+       var o = opener;
+       if (o) {
+               o.JsHttpRequest.request(document.getElementsByName('SubmitUser')[0], o.document.forms[0]);
+               close();
+       }
+</script>";
+}
+?>
diff --git a/admin/db/security_db.inc b/admin/db/security_db.inc
new file mode 100644 (file)
index 0000000..4af9fde
--- /dev/null
@@ -0,0 +1,67 @@
+<?php
+/**********************************************************************
+    Copyright (C) FrontAccounting, LLC.
+       Released under the terms of the GNU General Public License, GPL, 
+       as published by the Free Software Foundation, either version 3 
+       of the License, or (at your option) any later version.
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+    See the License here <http://www.gnu.org/licenses/gpl-3.0.html>.
+***********************************************************************/
+//--------------------------------------------------------------------------------------------------
+
+function get_security_role($id)
+{
+       $sql = "SELECT * FROM ".TB_PREF."security_roles WHERE id='$id'";
+       $ret = db_query($sql, "could not retrieve security roles");
+       $row = db_fetch($ret);
+       if ($row != false) {
+               $row['areas'] = explode(';', $row['areas']);
+               $row['sections'] = explode(';', $row['sections']);
+       }
+       return $row;
+}
+
+//--------------------------------------------------------------------------------------------------
+
+function add_security_role($name, $description, $sections, $areas)
+{
+       $sql = "INSERT INTO ".TB_PREF."security_roles (role, description, sections, areas)
+       VALUES ("
+       .db_escape($name).","
+       .db_escape($description).","
+       .db_escape(implode(';', $sections)).","
+       .db_escape(implode(';', $areas)).")";
+
+       db_query($sql, "could not add new security role");
+}
+
+//--------------------------------------------------------------------------------------------------
+
+function update_security_role($id, $name, $description, $sections, $areas)
+{
+       $sql = "UPDATE ".TB_PREF."security_roles SET role=".db_escape($name)
+       .",description=".db_escape($description)
+       .",sections=".db_escape(implode(';', $sections))
+       .",areas=".db_escape(implode(';', $areas))
+       ." WHERE id=$id";
+       db_query($sql, "could not update role");
+}
+//--------------------------------------------------------------------------------------------------
+
+function delete_security_role($id)
+{
+       $sql = "DELETE FROM ".TB_PREF."security_roles WHERE id=$id";
+
+       db_query($sql, "could not delete role");
+}
+//--------------------------------------------------------------------------------------------------
+
+function check_role_used($id) {
+       $sql = "SELECT count(*) FROM ".TB_PREF."users WHERE role_id=$id";
+       $ret = db_query($sql, 'cannot check role usage');
+       $row = db_fetch($ret);
+       return $row[0];
+}
+?>
\ No newline at end of file
diff --git a/admin/db/tags_db.inc b/admin/db/tags_db.inc
new file mode 100644 (file)
index 0000000..6b6aec4
--- /dev/null
@@ -0,0 +1,193 @@
+<?php
+/**********************************************************************
+    Copyright (C) FrontAccounting, LLC.
+       Released under the terms of the GNU General Public License, GPL, 
+       as published by the Free Software Foundation, either version 3 
+       of the License, or (at your option) any later version.
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+    See the License here <http://www.gnu.org/licenses/gpl-3.0.html>.
+***********************************************************************/
+
+function add_tag($type, $name, $description)
+{
+       $sql = "INSERT INTO ".TB_PREF."tags (type, name, description)
+               VALUES (".db_escape($type).", ".db_escape($name).", ".db_escape($description).")";
+
+       return db_query($sql);
+}
+
+//--------------------------------------------------------------------------------------
+
+function update_tag($id, $name, $description, $type=null)
+{
+       $sql = "UPDATE ".TB_PREF."tags SET name=".db_escape($name).", 
+                                       description=".db_escape($description);
+       if ($type != null)
+               $sql .= ", type=".db_escape($type);
+
+       $sql .= " WHERE id = ".db_escape($id);
+
+       return db_query($sql);
+}
+
+//--------------------------------------------------------------------------------------
+
+function get_tags($type, $all=false)
+{
+       $sql = "SELECT * FROM ".TB_PREF."tags WHERE type=".db_escape($type);
+       
+       if (!$all) $sql .= " AND !inactive";
+       
+       $sql .= " ORDER BY name";
+
+       return db_query($sql, "could not get tags");
+}
+
+//--------------------------------------------------------------------------------------
+
+function get_tag($id)
+{
+       $sql = "SELECT * FROM ".TB_PREF."tags WHERE id = ".db_escape($id);
+
+       $result = db_query($sql, "could not get tag");
+
+       return db_fetch($result);
+}
+
+//--------------------------------------------------------------------------------------
+
+function get_tag_type($id)
+{
+       $sql = "SELECT type FROM ".TB_PREF."tags WHERE id = ".db_escape($id);
+
+       $result = db_query($sql, "could not get tag type");
+
+       $row = db_fetch_row($result);
+       return $row[0];
+}
+
+//--------------------------------------------------------------------------------------
+
+function get_tag_name($id)
+{
+       $sql = "SELECT name FROM ".TB_PREF."tags WHERE id = ".db_escape($id);
+
+       $result = db_query($sql, "could not get tag name");
+
+       $row = db_fetch_row($result);
+       return $row[0];
+}
+
+//--------------------------------------------------------------------------------------
+
+function get_tag_description($id)
+{
+       $sql = "SELECT description FROM ".TB_PREF."tags WHERE id = ".db_escape($id);
+
+       $result = db_query($sql, "could not get tag description");
+
+       $row = db_fetch_row($result);
+       return $row[0];
+}
+
+//--------------------------------------------------------------------------------------
+
+function delete_tag($id)
+{
+       $sql = "DELETE FROM ".TB_PREF."tags WHERE id = ".db_escape($id);
+
+       db_query($sql, "could not delete tag");
+}
+
+//--------------------------------------------------------------------------------------
+
+function add_tag_associations($recordid, $tagids)
+{
+       foreach($tagids as $tagid) {
+               if (!$tagid) continue;
+               $sql = "INSERT INTO ".TB_PREF."tag_associations (record_id, tag_id)
+                       VALUES (".db_escape($recordid).", ".db_escape($tagid).")";
+
+               db_query($sql, "could not add tag association");
+       }
+}
+
+//--------------------------------------------------------------------------------------
+
+function update_tag_associations($type, $recordid, $tagids)
+{
+       // Delete the old associations
+       delete_tag_associations($type, $recordid, false);
+       // Add the new associations
+       add_tag_associations($recordid, $tagids);
+}
+
+//--------------------------------------------------------------------------------------
+// To delete tag associations, we need to specify the tag type.
+// Otherwise we may inadvertantly delete records for another type of tag
+//
+function delete_tag_associations($type, $recordid, $all=false)
+{
+/* multiply table DELETE syntax available since MySQL 4.0.0:
+       $sql = "DELETE ta FROM ".TB_PREF."tag_associations ta 
+                               INNER JOIN ".TB_PREF."tags tags ON tags.id = ta.tag_id 
+                               WHERE tags.type = ".db_escape($type)." AND ta.record_id = ".db_escape($recordid);
+*/
+       // To support MySQL 3.xx we have to use multiply queries
+       $sql = "SELECT * FROM ".TB_PREF."tag_associations ta 
+                       INNER JOIN ".TB_PREF."tags tags ON tags.id = ta.tag_id 
+                       WHERE tags.type = ".db_escape($type)." AND ta.record_id = ".db_escape($recordid);
+       if (!$all)
+               $sql .= " AND tags.inactive = 0";
+       $result = db_query($sql, "could not select tag associations");
+
+       while($ta = db_fetch($result)) {
+               $sql2 = "DELETE FROM ".TB_PREF."tag_associations WHERE 
+                       record_id = '".$ta['record_id']."' AND tag_id=".$ta['tag_id'];
+               db_query($sql2, "could not delete tag associations");
+       }
+}
+
+//--------------------------------------------------------------------------------------
+
+function get_records_associated_with_tag($id)
+{
+       // Which table we query is based on the tag type
+       $type = get_tag_type($id);
+       
+       $table = $key = '';
+       switch ($type) {
+               case TAG_ACCOUNT:
+                       $table = TB_PREF."chart_master";
+                       $key = "account_code";
+                       break;
+               case TAG_DIMENSION:
+                       $table = TB_PREF."dimensions";
+                       $key = "id";
+                       break;
+       }
+       
+       $sql = "SELECT $table.* FROM $table 
+               INNER JOIN ".TB_PREF."tag_associations AS ta ON ta.record_id = $table.$key
+               INNER JOIN ".TB_PREF."tags AS tags ON ta.tag_id = tags.id
+               WHERE tags.id = ".db_escape($id);
+
+       return db_query($sql, "could not get tag associations for tag");
+}
+
+//--------------------------------------------------------------------------------------
+
+function get_tags_associated_with_record($type, $recordid)
+{
+       $sql = "SELECT tags.* FROM ".TB_PREF."tag_associations AS ta 
+                               INNER JOIN ".TB_PREF."tags AS tags ON tags.id = ta.tag_id 
+                               WHERE tags.type = $type AND ta.record_id = ".db_escape($recordid);
+
+       return db_query($sql, "could not get tags associations for record");
+}
+
+//--------------------------------------------------------------------------------------
+
+?>
\ No newline at end of file
diff --git a/admin/security_roles.php b/admin/security_roles.php
new file mode 100644 (file)
index 0000000..bce6661
--- /dev/null
@@ -0,0 +1,257 @@
+<?php
+/**********************************************************************
+    Copyright (C) FrontAccounting, LLC.
+       Released under the terms of the GNU General Public License, GPL, 
+       as published by the Free Software Foundation, either version 3 
+       of the License, or (at your option) any later version.
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+    See the License here <http://www.gnu.org/licenses/gpl-3.0.html>.
+***********************************************************************/
+$page_security = 'SA_SECROLES';
+$path_to_root = "..";
+include_once($path_to_root . "/includes/session.inc");
+
+add_access_extensions();
+
+page(_($help_context = "Access setup"));
+
+include_once($path_to_root . "/includes/ui.inc");
+include_once($path_to_root . "/includes/access_levels.inc");
+include_once($path_to_root . "/admin/db/security_db.inc");
+
+$new_role = get_post('role')=='' || get_post('cancel') || get_post('clone'); 
+//--------------------------------------------------------------------------------------------------
+// Following compare function is used for sorting areas 
+// in such a way that security areas defined by module/plugin
+// is properly placed under related section regardless of 
+// unique extension number, with order inside sections preserved.
+//
+function comp_areas($area1, $area2) 
+{
+       $sec_comp = ($area1[0]&0xff00)-($area2[0]&0xff00);
+       return $sec_comp == 0 ? ($area1[2]-$area2[2]) : $sec_comp;
+}
+
+function sort_areas($areas)
+{
+       $old_order = 0;
+       foreach($areas as $key => $area) {
+               $areas[$key][] = $old_order++;
+       }
+       uasort($areas,'comp_areas');
+       return $areas;
+}
+//--------------------------------------------------------------------------------------------------
+if (list_updated('role')) {
+       $Ajax->activate('details');
+       $Ajax->activate('controls');
+}
+
+function clear_data()
+{
+       unset($_POST);
+}
+
+if (get_post('addupdate'))
+{
+       $input_error = 0;
+       if ($_POST['description'] == '')
+       {
+       $input_error = 1;
+       display_error( _("Role description cannot be empty."));
+               set_focus('description');
+       }
+       elseif ($_POST['name'] == '')
+       {
+       $input_error = 1;
+       display_error( _("Role name cannot be empty."));
+               set_focus('name');
+       }
+               // prevent accidental editor lockup by removing SA_SECROLES
+       if (get_post('role') == $_SESSION['wa_current_user']->access) {
+               if (!isset($_POST['Area'.$security_areas['SA_SECROLES'][0]])
+                       || !isset($_POST['Section'.SS_SETUP])) {
+                       display_error(_("Access level edition in Company setup section have to be enabled for your account."));
+               $input_error = 1;
+               set_focus(!isset($_POST['Section'.SS_SETUP]) 
+                       ? 'Section'.SS_SETUP : 'Area'.$security_areas['SA_SECROLES'][0]);
+               }
+       }
+
+       if ($input_error == 0)
+       {
+               $sections = array();
+               $areas = array();
+               foreach($_POST as $p =>$val) {
+                       if (substr($p,0,4) == 'Area') {
+                               $a = substr($p, 4);
+                               if (($a&~0xffff) && (($a&0xff00)<(99<<8))) {
+                                       $sections[] = $a&~0xff; // add extended section for plugins
+                               }
+                               $areas[] = $a;
+                       }
+                       if (substr($p,0,7) == 'Section')
+                               $sections[] = substr($p, 7);
+               }
+//             $areas = sort_areas($areas);
+
+               $sections = array_values($sections);
+
+       if ($new_role) 
+               {
+                       add_security_role($_POST['name'], $_POST['description'], $sections, $areas); 
+                       display_notification(_("New security role has been added."));
+               } else
+               {
+                       update_security_role($_POST['role'], $_POST['name'], $_POST['description'], 
+                               $sections, $areas); 
+                       update_record_status($_POST['role'], get_post('inactive'),
+                               'security_roles', 'id');
+
+                       display_notification(_("Security role has been updated."));
+               }
+       $new_role = true;
+       clear_data();
+       $Ajax->activate('_page_body');
+       }
+}
+
+//--------------------------------------------------------------------------------------------------
+
+if (get_post('delete'))
+{
+       if (check_role_used(get_post('role'))) {
+               display_error(_("This role is currently assigned to some users and cannot be deleted"));
+       } else {
+               delete_security_role(get_post('role'));
+               display_notification(_("Security role has been sucessfully deleted."));
+               unset($_POST['role']);
+       }
+       $Ajax->activate('_page_body');
+}
+
+if (get_post('cancel'))
+{
+       unset($_POST['role']);
+       $Ajax->activate('_page_body');
+}
+
+if (!isset($_POST['role']) || get_post('clone') || list_updated('role')) {
+       $id = get_post('role');
+       $clone = get_post('clone');
+
+       unset($_POST);
+       if ($id) {
+               $row = get_security_role($id);
+               $_POST['description'] = $row['description'];
+               $_POST['name'] = $row['role'];
+//     if ($row['inactive']
+//             $_POST['inactive'] = 1;
+       
+               $_POST['inactive'] = $row['inactive'];
+               $access = $row['areas'];
+               $sections = $row['sections'];
+       }
+       else {
+               $_POST['description'] = $_POST['name'] = '';
+               unset($_POST['inactive']);
+               $access = $sections = array();
+       }
+       foreach($access as $a) $_POST['Area'.$a] = 1;
+       foreach($sections as $s) $_POST['Section'.$s] = 1;
+
+       if($clone) {
+               set_focus('name');
+               $Ajax->activate('_page_body');
+       } else
+               $_POST['role'] = $id;
+}
+
+//--------------------------------------------------------------------------------------------------
+
+start_form();
+
+start_table("class='tablestyle_noborder'");
+start_row();
+security_roles_list_cells(_("Role:"). "&nbsp;", 'role', null, true, true, check_value('show_inactive'));
+$new_role = get_post('role')=='';
+check_cells(_("Show inactive:"), 'show_inactive', null, true);
+end_row();
+end_table();
+echo "<hr>";
+
+if (get_post('_show_inactive_update')) {
+       $Ajax->activate('role');
+       set_focus('role');
+}
+if (find_submit('_Section')) {
+       $Ajax->activate('details');
+}
+//-----------------------------------------------------------------------------------------------
+div_start('details');
+start_table($table_style2);
+       text_row(_("Role name:"), 'name', null, 20, 22);
+       text_row(_("Role description:"), 'description', null, 50, 52);
+       record_status_list_row(_("Current status:"), 'inactive');
+end_table(1);
+
+       start_table("$table_style width=40%");
+
+       $k = $j = 0; //row colour counter
+       $ext = $sec = $m = -1;
+
+       foreach(sort_areas($security_areas) as $area =>$parms ) {
+               // system setup areas are accessable only for site admins i.e. 
+               // admins of first registered company
+               if (user_company() && (($parms[0]&0xff00) == SS_SADMIN)) continue;
+               
+               $newsec = ($parms[0]>>8)&0xff;
+               $newext  = $parms[0]>>16;
+               if ($newsec != $sec || (($newext != $ext) && ($newsec>99)))
+               { // features set selection
+                       $ext = $newext; 
+                       $sec = $newsec;
+                       $m = $parms[0] & ~0xff;
+//                     if(!isset($security_sections[$m]))
+//                      display_error(sprintf("Bad section %X:", $m));
+                       label_row($security_sections[$m].':', 
+                               checkbox( null, 'Section'.$m, null, true, 
+                                       _("On/off set of features")),
+                       "class='tableheader2'", "class='tableheader'");
+               }
+               if (check_value('Section'.$m)) {
+                               alt_table_row_color($k);
+                               check_cells($parms[1], 'Area'.$parms[0], null, 
+                                       false, '', "align='center'");
+                       end_row();
+               } else {
+                       hidden('Area'.$parms[0]);
+               }
+       }
+       end_table(1);
+div_end();
+
+div_start('controls');
+
+if ($new_role) 
+{
+       submit_center_first('Update', _("Update view"), '', null);
+       submit_center_last('addupdate', _("Insert New Role"), '', 'default');
+} 
+else 
+{
+       submit_center_first('addupdate', _("Save Role"), '', 'default');
+       submit('Update', _("Update view"), true, '', null);
+       submit('clone', _("Clone This Role"), true, '', true);
+       submit('delete', _("Delete This Role"), true, '', true);
+       submit_center_last('cancel', _("Cancel"), _("Cancel Edition"), 'cancel');
+}
+
+div_end();
+
+end_form();
+end_page();
+
+?>
diff --git a/admin/tags.php b/admin/tags.php
new file mode 100644 (file)
index 0000000..f87d98d
--- /dev/null
@@ -0,0 +1,179 @@
+<?php
+/**********************************************************************
+    Copyright (C) FrontAccounting, LLC.
+       Released under the terms of the GNU General Public License, GPL, 
+       as published by the Free Software Foundation, either version 3 
+       of the License, or (at your option) any later version.
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+    See the License here <http://www.gnu.org/licenses/gpl-3.0.html>.
+***********************************************************************/
+$path_to_root = "..";
+include($path_to_root . "/includes/session.inc");
+include_once($path_to_root . "/includes/types.inc"); // For tag constants
+include_once($path_to_root . "/admin/db/tags_db.inc");
+include($path_to_root . "/includes/ui.inc");
+
+// Set up page security based on what type of tags we're working with
+if (@$_GET['type'] == "account" || get_post('type') == TAG_ACCOUNT) {
+       $page_security = 'SA_GLACCOUNTTAGS';
+} else if(@$_GET['type'] == "dimension" || get_post('type') == TAG_DIMENSION) {
+       $page_security = 'SA_DIMTAGS';
+}
+
+// We use $_POST['type'] throughout this script, so convert $_GET vars
+// if $_POST['type'] is not set.
+if (!isset($_POST['type'])) {
+       if ($_GET['type'] == "account")
+               $_POST['type'] = TAG_ACCOUNT;
+       elseif ($_GET['type'] == "dimension")
+               $_POST['type'] = TAG_DIMENSION;
+       else
+               die(_("Unspecified tag type"));
+}
+
+// Set up page based on what type of tags we're working with
+switch ($_POST['type']) {
+       case TAG_ACCOUNT:
+               // Account tags
+               $_SESSION['page_title'] = _($help_context = "Account Tags");
+               break;
+       case TAG_DIMENSION:
+               // Dimension tags
+               $_SESSION['page_title'] = _($help_context = "Dimension Tags");
+}
+
+page($_SESSION['page_title']);
+
+simple_page_mode(true);
+
+//-----------------------------------------------------------------------------------
+
+function can_process() 
+{
+       if (strlen($_POST['name']) == 0) 
+       {
+               display_error( _("The tag name cannot be empty."));
+               set_focus('name');
+               return false;
+       }
+       return true;
+}
+
+//-----------------------------------------------------------------------------------
+
+if ($Mode=='ADD_ITEM' || $Mode=='UPDATE_ITEM') 
+{
+       if (can_process()) 
+       {
+       if ($selected_id != -1) 
+       {
+               if( $ret = update_tag($selected_id, $_POST['name'], $_POST['description']))
+                               display_notification(_('Selected tag settings have been updated'));
+       } 
+       else 
+       {
+               if( $ret = add_tag($_POST['type'], $_POST['name'], $_POST['description']))
+                               display_notification(_('New tag has been added'));
+       }
+               if ($ret) $Mode = 'RESET';
+       }
+}
+
+//-----------------------------------------------------------------------------------
+
+function can_delete($selected_id)
+{
+       if ($selected_id == -1)
+               return false;
+       $result = get_records_associated_with_tag($selected_id);
+       
+       if (db_num_rows($result) > 0)   
+       {
+               display_error(_("Cannot delete this tag because records have been created referring to it."));
+               return false;
+       }
+
+       return true;
+}
+
+
+//-----------------------------------------------------------------------------------
+
+if ($Mode == 'Delete')
+{
+       if (can_delete($selected_id))
+       {
+               delete_tag($selected_id);
+               display_notification(_('Selected tag has been deleted'));
+       }
+       $Mode = 'RESET';
+}
+
+//-----------------------------------------------------------------------------------
+
+if ($Mode == 'RESET')
+{
+       $selected_id = -1;
+       $_POST['name'] = $_POST['description'] = '';
+}
+
+//-----------------------------------------------------------------------------------
+
+$result = get_tags($_POST['type'], check_value('show_inactive'));
+
+start_form();
+start_table($table_style);
+$th = array(_("Tag Name"), _("Tag Description"), "", "");
+inactive_control_column($th);
+table_header($th);
+
+$k = 0;
+while ($myrow = db_fetch($result)) 
+{
+       alt_table_row_color($k);
+
+       label_cell($myrow['name']);
+       label_cell($myrow['description']);
+       inactive_control_cell($myrow["id"], $myrow["inactive"], 'tags', 'id');
+       edit_button_cell("Edit".$myrow["id"], _("Edit"));
+       delete_button_cell("Delete".$myrow["id"], _("Delete"));
+       end_row();
+}
+
+inactive_control_row($th);
+end_table(1);
+
+//-----------------------------------------------------------------------------------
+
+start_table($table_style2);
+
+if ($selected_id != -1) // We've selected a tag 
+{
+       if ($Mode == 'Edit') {
+               // Editing an existing tag
+               $myrow = get_tag($selected_id);
+       
+               $_POST['name'] = $myrow["name"];
+               $_POST['description'] = $myrow["description"];
+       }
+       // Note the selected tag
+       hidden('selected_id', $selected_id);
+}
+       
+text_row_ex(_("Tag Name:"), 'name', 15, 30);
+text_row_ex(_("Tag Description:"), 'description', 40, 60);
+hidden('type');
+
+end_table(1);
+
+submit_add_or_update_center($selected_id == -1, '', 'both');
+
+end_form();
+
+//------------------------------------------------------------------------------------
+
+end_page();
+
+?>
diff --git a/config.default.php b/config.default.php
new file mode 100644 (file)
index 0000000..0acd7b8
--- /dev/null
@@ -0,0 +1,214 @@
+<?php
+/**********************************************************************
+    Copyright (C) FrontAccounting, LLC.
+       Released under the terms of the GNU General Public License, GPL, 
+       as published by the Free Software Foundation, either version 3 
+       of the License, or (at your option) any later version.
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+    See the License here <http://www.gnu.org/licenses/gpl-3.0.html>.
+***********************************************************************/
+    //--------------------------------------------------
+
+       // User configurable variables
+       //---------------------------------------------------
+
+       /*Show debug messages returned from an error on the page.
+       Debugging info level also determined by settings in PHP.ini
+       if $debug=1 show debugging info, dont show if $debug=0 */
+
+if (!isset($path_to_root) || isset($_GET['path_to_root']) || isset($_POST['path_to_root']))
+       die("Restricted access");
+       // Log file for error/warning messages. Should be set to any location
+       // writable by www server. When set to empty string logging is switched off. 
+       // Special value 'syslog' can be used for system logger usage (see php manual).
+       //$error_logfile = '';
+       $error_logfile = dirname(__FILE__).'/tmp/errors.log';
+       $debug                  = 1;
+       $show_sql               = 0;
+       $go_debug               = 0;
+       $pdf_debug              = 0;
+       // set $sql_trail to 1 only if you want to perform bugtracking sql trail
+       // Warning: this produces huge amount of data in sql_trail table.
+       // Don't forget switch the option off and flush the table manually after 
+       // trail, or your future backup files are overloaded with unneeded data.
+       //
+       $sql_trail              = 0; // save all sql queries in sql_trail
+       $select_trail   = 0; // track also SELECT queries
+       if ($go_debug == 1)
+       {
+               error_reporting(E_ALL);
+               ini_set("display_errors", "On");
+       }
+       else
+       {
+               error_reporting(E_USER_WARNING|E_USER_ERROR|E_USER_NOTICE);
+               // ini_alter("error_reporting","E_COMPILE_ERROR|E_ERROR|E_CORE_ERROR|E_PARSE");
+               ini_set("display_errors", "On");
+       }
+
+       if($error_logfile != '') {
+               ini_set("error_log", $error_logfile);
+               ini_set("ignore_repeated_errors", "On");
+               ini_set("log_errors", "On");
+       }               
+       // Main Title
+       $app_title = "FrontAccounting";
+       // application version
+       $version                = "2.2";
+
+       // Build for development purposes
+       $build_version  = date("d.m.Y", filemtime("$path_to_root/CHANGELOG.txt"));
+
+       // Powered by
+       $power_by               = "FrontAccounting";
+       $power_url              = "http://frontaccounting.net";
+
+       /* use popup windows for views */
+       $use_popup_windows = 1;
+
+       /* use date picker for all date fields */
+       $use_date_picker = 1;
+
+       /* use Audit Trails in GL */
+       $use_audit_trail = 0;
+
+       /* use old style convert (income and expense in BS, PL) */
+       $use_oldstyle_convert = 0;
+
+       /* show users online discretely in the footer */
+       $show_users_online = 0;
+
+       // Wiki context help configuration
+       // If your help wiki use translated page titles uncomment next line
+       // $old_style_help = 1; // this setting is depreciated and subject to removal in next FA versions
+       //      locally installed wiki module
+       // $help_base_url = $path_to_root.'/modules/wiki/index.php?n='._('Help').'.';
+       //      context help feed from frontaccounting.net
+       // $help_base_url = 'http://frontaccounting.net/fawiki/index.php?n=Help.';
+       //      not used
+       $help_base_url = null;
+
+       /* per user data/cache directory */
+       $comp_path = $path_to_root.'/company';
+
+       /* allow alpha characters in accounts. 0 = numeric, 1 = alpha numeric, 2 = uppercase alpha numeric */
+       $accounts_alpha = 0;
+
+       /* Date systems. 0 = traditional, 1 = Jalali used by Iran, nabour countries, Afghanistan and some other Central Asian nations,
+       2 = Islamic used by other arabic nations. 3 = traditional, but where non-workday is Friday and start of week is Saturday */
+       $date_system = 0;
+
+       /* email stock location if order below reorder-level */
+       $loc_notification = 0;
+
+       /* print_invoice_no. 0 = print reference number, 1 = print invoice number */
+       $print_invoice_no = 0;
+
+       $dateformats    = array("MMDDYYYY", "DDMMYYYY", "YYYYMMDD");
+       $dateseps               = array("/", ".", "-", " ");
+       $thoseps                = array(",", ".", " ");
+       $decseps                = array(".", ",");
+       // defalt dateformats and dateseps indexes used before user login
+       $dflt_date_fmt = 0;
+       $dflt_date_sep = 0;
+
+       $pagesizes              = array("Letter", "A4"); // default PDF pagesize
+
+       /* Default border and spacing for tables */
+       /* Should be moved to CSS */
+
+       if (!isset($_SESSION['bordercolor']))
+               $_SESSION['bordercolor'] = "#8cacbb";
+       $table_style    = "cellpadding=3 border=1 bordercolor='".$_SESSION['bordercolor']."' class='tablestyle'";
+       $table_style2   = "cellpadding=3 border=1 bordercolor='#cccccc' class='tablestyle2'";
+
+       /* Accounts Payable */
+       /* System check to see if quantity charged on purchase invoices exceeds the quantity received.
+       If this parameter is checked the proportion by which the purchase invoice is an overcharge
+       referred to before reporting an error */
+
+       $check_qty_charged_vs_del_qty = true;
+
+       /* System check to see if price charged on purchase invoices exceeds the purchase order price.
+       If this parameter is checked the proportion by which the purchase invoice is an overcharge
+       referred to before reporting an error */
+
+       $check_price_charged_vs_order_price = True;
+
+       $config_allocation_settled_allowance = 0.005;
+
+       // Internal configurable variables
+       //-----------------------------------------------------------------------------------
+
+       /* Whether to display the demo login and password or not */
+
+       $allow_demo_mode = false;
+
+       /* for uploaded item pictures */
+       $pic_width              = 80;
+       $pic_height     = 50;
+       $max_image_size = 500;
+
+       /* skin for Business Graphics, 1, 2 or 3 */
+       $graph_skin     = 1;
+
+/*     
+       Before upgrade from pre-2.2 FA you have to move here your customized
+       security roles definitions. If you have used standard roles, you
+       can simply uncomment following two arrays. After upgrade both arrays need 
+       to be deleted or commented out. You may wish to change user roles to
+       new better defined in Users Setup. Old not used roles can be set inactive 
+       or deleted.
+*/
+/* Standard FA2.1 Security Group definitions
+
+       $security_headings = array(
+                       _("Inquiries"),
+                       _("Accountant"),
+                       _("System Administrator"),
+       );
+
+       $security_groups = array(
+                       array(1,2),
+                       array(1,2,3,4,5,6,7,8,9,10,11,12,13,14,16),
+                       array(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,20),
+       );
+*/
+
+       //MySQL Backup and Restore Settings
+
+if(isset($_SESSION["wa_current_user"])) {
+       define("BACKUP_PATH", $comp_path.'/'.user_company()."/backup/");
+}
+       // static js files path
+       $js_path = $path_to_root.'/js/';
+       // standard external js scripts included in all files
+       $js_static = array('JsHttpRequest.js', 'behaviour.js', 'utils.js', 'inserts.js');
+       // additional js source included in header
+       $js_lib = $js_userlib = array();
+
+if (!defined('ICON_EDIT'))
+{
+       define("ICON_EDIT", "edit.gif");        
+       define("ICON_DELETE", "delete.gif");    
+       define("ICON_ADD", "ok.gif");   
+       define("ICON_UPDATE", "ok.gif");        
+       define("ICON_OK", "ok.gif");    
+       define("ICON_CANCEL", "cancel.png");    
+       define("ICON_GL", "gl.png");    
+       define("ICON_PRINT", "print.png");      
+       define("ICON_PDF", "pdf.gif");  
+       define("ICON_DOC", "invoice.gif");      
+       define("ICON_CREDIT", "credit.gif");    
+       define("ICON_RECEIVE", "receive.gif");  
+       define("ICON_DOWN", "download.gif");    
+       define("ICON_MONEY", "money.png");      
+       define("ICON_REMOVE", "remove.png");    
+       define("ICON_REPORT", "report.png");    
+       define("ICON_VIEW", "view.gif");        
+       define("ICON_SUBMIT", "ok.gif");
+       define("ICON_ESCAPE", "escape.png");    
+}
+?>
\ No newline at end of file
diff --git a/config.php b/config.php
deleted file mode 100644 (file)
index 7ab4f10..0000000
+++ /dev/null
@@ -1,210 +0,0 @@
-<?php
-/**********************************************************************
-    Copyright (C) FrontAccounting, LLC.
-       Released under the terms of the GNU General Public License, GPL, 
-       as published by the Free Software Foundation, either version 3 
-       of the License, or (at your option) any later version.
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
-    See the License here <http://www.gnu.org/licenses/gpl-3.0.html>.
-***********************************************************************/
-    //--------------------------------------------------
-
-       // User configurable variables
-       //---------------------------------------------------
-
-       /*Show debug messages returned from an error on the page.
-       Debugging info level also determined by settings in PHP.ini
-       if $debug=1 show debugging info, dont show if $debug=0 */
-
-if (!isset($path_to_root) || isset($_GET['path_to_root']) || isset($_POST['path_to_root']))
-       die("Restricted access");
-       // Log file for error/warning messages. Should be set to any location
-       // writable by www server. When set to empty string logging is switched off. 
-       // Special value 'syslog' can be used for system logger usage (see php manual).
-       $error_logfile = '';
-       //$error_logfile = dirname(__FILE__).'/tmp/errors.log';
-       $debug                  = 1;
-       $show_sql               = 0;
-       $go_debug               = 0;
-       $pdf_debug              = 0;
-       // set $sql_trail to 1 only if you want to perform bugtracking sql trail
-       // Warning: this produces huge amount of data in sql_trail table.
-       // Don't forget switch the option off and flush the table manually after 
-       // trail, or your future backup files are overloaded with unneeded data.
-       //
-       $sql_trail              = 0; // save all sql queries in sql_trail
-       $select_trail   = 0; // track also SELECT queries
-       if ($go_debug == 1)
-       {
-               error_reporting(E_ALL);
-               ini_set("display_errors", "On");
-       }
-       else
-       {
-               error_reporting(E_USER_WARNING|E_USER_ERROR|E_USER_NOTICE);
-               // ini_alter("error_reporting","E_COMPILE_ERROR|E_ERROR|E_CORE_ERROR|E_PARSE");
-               ini_set("display_errors", "On");
-       }
-
-       if($error_logfile != '') {
-               ini_set("error_log", $error_logfile);
-               ini_set("ignore_repeated_errors", "On");
-               ini_set("log_errors", "On");
-       }               
-       // Main Title
-       $app_title = "FrontAccounting";
-       // application version
-       $version                = "2.1.7";
-
-       // Build for development purposes
-       $build_version  = date("d.m.Y", filemtime("$path_to_root/CHANGELOG.txt"));
-
-       // Powered by
-       $power_by               = "FrontAccounting";
-       $power_url              = "http://frontaccounting.net";
-
-       /* use popup windows for views */
-       $use_popup_windows = 1;
-
-       /* use date picker for all date fields */
-       $use_date_picker = 1;
-
-       /* use Audit Trails in GL */
-       $use_audit_trail = 0;
-
-       /* use old style convert (income and expense in BS, PL) */
-       $use_oldstyle_convert = 0;
-
-       /* Integrated base Wiki Help URL or null if not used */
-       //$help_base_url = $path_to_root.'/modules/wiki/index.php?n='._('Help').'.';
-       $help_base_url = null;
-
-       /* per user data/cache directory */
-       $comp_path = $path_to_root.'/company';
-
-       /* allow alpha characters in accounts. 0 = numeric, 1 = alpha numeric, 2 = uppercase alpha numeric */
-       $accounts_alpha = 0;
-
-       /* Date systems. 0 = traditional, 1 = Jalali used by Iran, nabour countries, Afghanistan and some other Central Asian nations,
-       2 = Islamic used by other arabic nations */
-       $date_system = 0;
-
-       /* email stock location if order below reorder-level */
-       $loc_notification = 0;
-
-       /* print_invoice_no. 0 = print reference number, 1 = print invoice number */
-       $print_invoice_no = 0;
-
-       $dateformats    = array("MMDDYYYY", "DDMMYYYY", "YYYYMMDD");
-       $dateseps               = array("/", ".", "-", " ");
-       $thoseps                = array(",", ".", " ");
-       $decseps                = array(".", ",");
-
-       $pagesizes              = array("Letter", "A4"); // default PDF pagesize
-
-       /* Default border and spacing for tables */
-       /* Should be moved to CSS */
-
-       $table_style    = "cellpadding=3 border=1 bordercolor='#8cacbb' style='border-collapse: collapse'";
-       $table_style2   = "cellpadding=3 border=1 bordercolor='#cccccc' style='border-collapse: collapse'";
-
-       /* Accounts Payable */
-       /* System check to see if quantity charged on purchase invoices exceeds the quantity received.
-       If this parameter is checked the proportion by which the purchase invoice is an overcharge
-       referred to before reporting an error */
-
-       $check_qty_charged_vs_del_qty = true;
-
-       /* System check to see if price charged on purchase invoices exceeds the purchase order price.
-       If this parameter is checked the proportion by which the purchase invoice is an overcharge
-       referred to before reporting an error */
-
-       $check_price_charged_vs_order_price = True;
-
-       $config_allocation_settled_allowance = 0.005;
-
-       // Internal configurable variables
-       //-----------------------------------------------------------------------------------
-
-       /* Whether to display the demo login and password or not */
-
-       $allow_demo_mode = false;
-
-       /* for uploaded item pictures */
-       $pic_width              = 80;
-       $pic_height     = 50;
-       $max_image_size = 500;
-
-       /* skin for Business Graphics, 1, 2 or 3 */
-       $graph_skin     = 1;
-
-       /*Security Group definitions - Depending on the AccessLevel of the user defined in the user set up
-       the areas of functionality accessible can be modified.
-       Each AccessLevel is associated with an array containing the security categories that the user is entitled to access
-       Each script has a particular security category associated with it.
-       If the security setting of the page is contained in the security group as determined by the access level then the user will be allowed access.
-       Each page has a $page_security = x; variable
-       This value is compared to contents of the array applicable which is based on the access level of the user.
-       Access authorisation is checked in session.inc. If you wish to add more security groups
-       with then you must add a new SecurityHeading to the security_headings array
-       and a new array of Security categories to the Security Groups _at_the_end_ of the array
-       This mechanism allows more fine grained control of access
-       security_groups is an array of arrays
-       The index is the order in which the array of allowed pages is defined new ones can be defined at will
-       or by changing the numbers in each array the security access can be tailored. These numbers need to read
-       in conjunction with the Page Security index
-       Special case is security level 20 which is reserved for admins of first
-       registered company (site admins). All potentially dangerous for whole FA
-       site operations like installing addon modules require access level 20.
-       */
-
-       $security_headings = array(
-                       _("Inquiries"),
-                       _("Accountant"),
-                       _("System Administrator"),
-       );
-
-       $security_groups = array(
-                       array(1,2),
-                       array(1,2,3,4,5,6,7,8,9,10,11,12,13,14,16),
-                       array(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,20),
-       );
-
-       /* default start-up tab (orders/AP/stock/manuf/proj/GL/system) */
-       $def_app = "orders";
-
-       //MySQL Backup and Restore Settings
-
-if(isset($_SESSION["wa_current_user"])) {
-       define("BACKUP_PATH", $comp_path.'/'.user_company()."/backup/");
-}
-       // static js files path
-       $js_path = $path_to_root.'/js/';
-       // standard external js scripts included in all files
-       $js_static = array('JsHttpRequest.js', 'behaviour.js', 'utils.js', 'inserts.js');
-       // additional js source included in header
-       $js_lib = $js_userlib = array();
-
-if (!defined('ICON_EDIT'))
-{
-       define("ICON_EDIT", "edit.gif");        
-       define("ICON_DELETE", "delete.gif");    
-       define("ICON_ADD", "ok.gif");   
-       define("ICON_UPDATE", "ok.gif");        
-       define("ICON_OK", "ok.gif");    
-       define("ICON_CANCEL", "cancel.png");    
-       define("ICON_GL", "gl.png");    
-       define("ICON_PRINT", "print.png");      
-       define("ICON_PDF", "pdf.gif");  
-       define("ICON_DOC", "invoice.gif");      
-       define("ICON_CREDIT", "credit.gif");    
-       define("ICON_RECEIVE", "receive.gif");  
-       define("ICON_DOWN", "download.gif");    
-       define("ICON_MONEY", "money.png");      
-       define("ICON_REMOVE", "remove.png");    
-       define("ICON_REPORT", "report.png");    
-       define("ICON_VIEW", "view.gif");        
-}
-?>
\ No newline at end of file
diff --git a/config_db.php b/config_db.php
deleted file mode 100644 (file)
index 1c06397..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-<?php
-
-/*Connection Information for the database
-- $def_coy is the default company that is pre-selected on login
-
-- host is the computer ip address or name where the database is the default is localhost assuming that the web server is also the sql server
-
-- user is the user name under which the database should be accessed - need to change to the mysql (or other DB) user set up for purpose
-  NB it is not secure to use root as the user with no password - a user with appropriate privileges must be set up
-
-- password is the password the user of the database requires to be sent to authorise the above database user
-
-- DatabaseName is the name of the database as defined in the RDMS being used. Typically RDMS allow many databases to be maintained under the same server.
-  The scripts for MySQL provided use the name logicworks */
-
-
-$def_coy = 0;
-
-/*--- for advanced users, manually install, uncomment this, fill in the correct information and erase the rows below
-$tb_pref_counter = 1;
-
-$db_connections = array (
-       0 => array ('name' => 'Training Co.',
-               'host' => 'localhost',
-               'dbuser' => 'your_db_user_name',
-               'dbpassword' => 'your_db_password',
-               'dbname' => 'your_db_name',
-               'tbpref' => '0_')
-       );
-*/
-
-$tb_pref_counter = 0;
-
-$db_connections = array ();
-
-?>
\ No newline at end of file
diff --git a/doc/2.2_Beta.txt b/doc/2.2_Beta.txt
new file mode 100644 (file)
index 0000000..00f5d8a
--- /dev/null
@@ -0,0 +1,64 @@
+News in FrontAccounting 2.2 Beta
+--------------------------------
+
+Common
+------
+- Quick Entries uses Tax Types instead of Item Tax Types
+- Support for default buttons with Ctrl-Enter/Escape hotkeys
+- Arrow navigations in menus
+- Option to use last document date on subsequent new documents.
+- Full support for inactive records
+- Time Zone on Reports
+- Audit Trail for all operations with Report
+- Bank Charge field in Customer/Supplier Payment.
+- New default fields in Company table for Bank Charge, Retained Earnings and 
+  Profit/Loss Year. 
+- Direct Allocation of Invoices in Customer/Supplier Payments
+- New Access Levels/Security Roles system enables fine tunning person access rights.
+- Item Tax Types moved to Setup module
+- Default Start-up Tab set by user.
+- Authorization timeout parameter set in Company preferences
+
+Sales
+-----
+- Customer/Supplier Balance Reports now with Open Balance from selected date
+- Added Sales Quotations, Inquiry and Report
+
+Purchasing
+----------
+- Better support for conversion factor in Purchasing Prices.
+- Purchasing price can have up to 6 decimals allowing fractions of hundredths in 
+  prices.
+
+Items and Inventory
+-------------------
+- Item Categories contains default parameters for new items.
+- Excluded from Sale. Non Sales Items from Categories.
+- GRN Valuation Report
+- Automatic price-calculation of Items from Std. Cost (if no prices set on items)
+- Rounding of prices to nearest XX Cent after calculations.
+
+Manufacturing
+-------------
+- Printing/Emailing of Work Orders
+
+Dimensions
+----------
+- Re-opening of Closed dimensions
+
+Banking and General Ledger
+--------------------------
+- Closing a fiscal year also closes the balances and brings forward retained earnings
+- Deleting a fiscal year removes all transactions and convert into relevant Open 
+  Balances.
+- Journal Enquiry
+- Edition/view of Journal Entries.
+- Group Sorting by Id in Account Selectors
+- Default per currency bank accounts (used in reports). The Bank Account selection on 
+  documents has been replaced by this.
+
+Bugs fixed in this release
+--------------------------
+- Fixed php 5 warnings flood in error log.
+- All bugs up to release 2.1.6 are fixed in this release too.
+
diff --git a/doc/access_levels.txt b/doc/access_levels.txt
new file mode 100644 (file)
index 0000000..4d647fc
--- /dev/null
@@ -0,0 +1,126 @@
+                       FrontAccounting access control system
+                       =====================================
+
+Since version 2.2 FrontAccounting has new, flexible access level control system.
+In contrast to previous system based on arrays stored in global config.php file, 
+the new system uses per company security_roles tables. This approach makes 
+FrontAccounting finally suitable for multicompany installation involving 
+various types of companies.
+
+1. Security schema
+------------------
+
+New access control system uses following concepts:
+
+. Security area - elementary fragment of application functionality which 
+       is placed under control of access system;
+
+. Security role - set of security areas which is accessable for user with 
+       some role in company;
+
+. Security section - a couple of security areas of similar application grouped 
+       together to make roles defining easier.
+
+Security areas stored in global $security_areas array have two properties: 
+identifier (in numeric and string form) and description. Description is used 
+only to identify area in security roles editor, while string identifiers are 
+used in various places of source code to be checked against current user 
+permissions.
+
+Every security area belongs to one security section, which can be considered 
+as upper level of access control. All defined security sections are stored 
+in global $security_sections array together with descriptions used in roles 
+editor.
+
+2. Access Setup
+---------------
+
+FrontAccounting since version 2.2 has role based access system. It means that 
+every user defined in a system has assigned role related to his position in 
+company. For any user only security areas which belong to user's role are 
+accesible. 
+
+To grant access to any security area for any role administrator first have to
+make accessible also related area's security section. Switching security section 
+off disables access to all related security areas.
+
+Security roles predefined in FrontAccounting initial database can be customized
+or completely rewritten by administrator according to company internal security 
+policy. 
+
+Some security areas crucial for overall site security are accesible only for
+administrators of the first installed company, who can be considered as 
+superadmins. All those important areas are grouped in section 0 
+(System administration), and as of FA 2.2 involve:
+       . Installing/update of companies
+       . Installing/update language message files
+       . Installing/activation system extensions
+       . System upgrades
+
+3. How all it works
+-------------------
+
+Every user defined in a system has one security role assigned. List of 
+all accesible security areas/sections codes is retrieved from security_roles 
+table on user login, and cached in user session variable for fast checking.
+Every page in a system has at least one related security area, which is 
+assigned to $page_security global variable at the beginning of the page script.
+
+Page access control is performed by check_page_security() call in 
+page() function (used for every displayed page) or by can_access_page()
+call in FrontReport constructor for all reports. When user has granted access 
+rights to checked security area, selected page is displayed or report generated. 
+Otherwise information message is displayed and page/report generation is aborted.
+
+4. Security extensions
+----------------------
+
+FrontAccounting application accepts two forms of functionality additions: 
+extension modules and extension plugins. Both types of extensions can use 
+standard security areas/sections to control access to introduced functionality,
+or define its own security areas and/or sections. 
+
+To extend access control system with additional sections/areas, extension
+need to contain a file were all new extensions are defined. The access control 
+file relative path should be entered during extension install process on 
+Install/Activate Extensions page, or as 'access' property during direct entry 
+in installed_extensions.php file.
+
+Every php script using security extensions have to call function 
+add_security_extensions() to make defined extensions active. The call should 
+be placed between session.inc inclusion and page() or FrontReport() call.
+
+5. Example access control configuration file
+--------------------------------------------
+
+This is content of sample access control file for CRM extension module:
+
+<?php
+/*
+       Define security section codes
+*/
+define('SS_CRM_C',     101<<8);
+define('SS_CRM',       102<<8);
+define('SS_CRM_A',     103<<8);
+
+/*
+       Additional security sections for CRM module
+*/
+$security_sections[SS_CRM_C] = _("CRM configuration");
+$security_sections[SS_CRM] = _("CRM transactions");
+$security_sections[SS_CRM_A] = _("CRM analytics");
+/*
+       Additional security areas for CRM module
+*/
+$security_areas['SA_CRMSETUP'] = array(SS_CRM_C|1, _("CRM module setup"));
+$security_areas['SA_CRMCONTACTENTRY'] = array(SS_CRM|1, _("Customer contact entry"));
+$security_areas['SA_CRMANALYITCS'] = array(SS_CRM|1, _("Pre-sale contact analytics"));
+
+?>
+
+The exact values used for security section codes are not very important, 
+as they are rewritten by access control system during integration of
+access extensions. Therefore numeric values of security sections/areas should 
+never be used directly in the extensions source. Use string representations
+instead when needed, or values retrieved from $security_areas array.
+
diff --git a/doc/calculate_price.txt b/doc/calculate_price.txt
new file mode 100644 (file)
index 0000000..321185c
--- /dev/null
@@ -0,0 +1,58 @@
+Calculate sales price from standard cost
+----------------------------------------
+
+You can have automatic sales price calculation for items that do not have any 
+prices in Sales Prices entered. This works independently from the Base Price 
+List setting, but if the factor is set in the other Sales Type Lists it works
+together with them.
+
+Preparing and operation
+-----------------------
+
+Go into Setup tab - Company Setup.
+
+In the field, Base for auto price calculation, set a Base Price List.
+In the field, Add Price from Std cost, enter the % that you want to increase 
+from the average standard costs for the items not listed in the Item Price List. 
+If this value is empty there are no automatic price calculations. BE aware that 
+an increase value of 0 will calculate the price to be the same as the Std cost. 
+So to remove the calculation, just empty the field. It will then get an internal
+value of -1.
+If the average standard costs is 0, the calculation would result in 0 as well.
+
+The field, Round to nearest xxx Cents, will round the calculated result to the 
+nearest Cent entered. If you have 2 decimals in the amounts the value 100 would 
+be divided by the xxx value. If you have 3 decimals in the amounts the value 
+1000 will be divided by the xxx value. If there is no fraction, the value will 
+be rounded up to the nearest xxx value. If there is a fraction, the value xxx 
+will be subtracted from the value (100-xxx) or (1000-xxx).
+
+Let us take an example:
+----------------------
+
+Item standard cost = 20.77
+Decimals = 2
+Value to increase the items with = 70 (%)
+Round to nearest value = 5
+
+the pow(10, 2) = 100. 100 divided by 5 = 20 and no fraction. Price after increase
+= 20.77 * (1 + 70 / 100) = 35.309. Rounded to nearest 5 cent value = 35.35;
+
+Let us set the Round to nearest value = 95.
+
+The value 100 / 95 = 1 and a fraction of 95. The Price will still be calculated 
+to 35.309. Now the value will be rounded to 36.00 and subtracted by (100 - 95) = 
+35.95.
+
+So you see it is very flexible. If you have larger prices you can even round up 
+to nearest 10, 100 by setting the rounding to nearest value = 1000 or 10000. 
+But take care if you have different prices, large prices and small prices. It might
+not be a good idea to round a value of 10.77 to 100.
+
+If there is a base price list set and for instance a factor 0.7 the price list in 
+foreign currencies are calculated as well. And again, if a 'Round to nearest' value
+is set to other than 1 the foreign prices are rounded in the same way as explained 
+above.
+
+This new price calculaton should work with sales kits too.
+
diff --git a/doc/sales_quotations.txt b/doc/sales_quotations.txt
new file mode 100644 (file)
index 0000000..47f6da6
--- /dev/null
@@ -0,0 +1,21 @@
+Sales Quotations as a separate menu option
+------------------------------------------
+
+You can now create Sales Quotations in a separate form. You enter the 'Valid until' 
+date. These Sales Quotations can later be used for creating a Sales Order.
+
+There is a new menu choice under Inquiries, Sales Quotation Inquiry. If the 
+'Valid until' has been passed, the Sales Quotation is not shown. But you can 
+select 'Show All' and all the Sales Quotation inside the period will be shown.
+
+In this Inquiry you have the option to look at the entire Quotation, edit the 
+Quotation, print the Quotation and make a Sales Order from the Quotation.
+
+The Sales Quotations will not be deleted, so it is possible to use it agan and 
+maybe edit it for use again.
+
+The Sales Quotations can however be deleted when editing. And when deleting a 
+fiscal year, the Quotations will be deleted.
+
+The Sales Order print out can still be selected to be printed as a Quote should 
+you prefer that.
diff --git a/gl/inquiry/journal_inquiry.php b/gl/inquiry/journal_inquiry.php
new file mode 100644 (file)
index 0000000..f2aee15
--- /dev/null
@@ -0,0 +1,180 @@
+<?php
+/**********************************************************************
+    Copyright (C) FrontAccounting, LLC.
+       Released under the terms of the GNU General Public License, GPL, 
+       as published by the Free Software Foundation, either version 3 
+       of the License, or (at your option) any later version.
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+    See the License here <http://www.gnu.org/licenses/gpl-3.0.html>.
+***********************************************************************/
+
+$page_security = 'SA_GLANALYTIC';
+$path_to_root="../..";
+
+include($path_to_root . "/includes/db_pager.inc");
+include_once($path_to_root . "/includes/session.inc");
+
+include_once($path_to_root . "/includes/date_functions.inc");
+include_once($path_to_root . "/includes/ui.inc");
+$js = "";
+if ($use_popup_windows)
+       $js .= get_js_open_window(800, 500);
+if ($use_date_picker)
+       $js .= get_js_date_picker();
+
+page(_($help_context = "Journal Inquiry"), false, false, "", $js);
+
+//-----------------------------------------------------------------------------------
+// Ajax updates
+//
+if (get_post('Search'))
+{
+       $Ajax->activate('journal_tbl');
+}
+//--------------------------------------------------------------------------------------
+if (!isset($_POST['filterType']))
+       $_POST['filterType'] = -1;
+
+start_form();
+
+start_table("class='tablestyle_noborder'");
+start_row();
+
+ref_cells(_("Reference:"), 'Ref', '',null, _('Enter reference fragment or leave empty'));
+
+journal_types_list_cells(_("Type:"), "filterType");
+date_cells(_("From:"), 'FromDate', '', null, 0, -1, 0);
+date_cells(_("To:"), 'ToDate');
+
+check_cells( _("Show closed:"), 'AlsoClosed', null);
+
+submit_cells('Search', _("Search"), '', '', 'default');
+
+end_row();
+end_table();
+
+function journal_pos($row)
+{
+       return $row['gl_seq'] ? $row['gl_seq'] : '-';
+}
+
+function systype_name($dummy, $type)
+{
+       global $systypes_array;
+       
+       return $systypes_array[$type];
+}
+
+function view_link($row) 
+{
+       return get_trans_view_str($row["type"], $row["type_no"]);
+}
+
+function gl_link($row) 
+{
+       return get_gl_view_str($row["type"], $row["type_no"]);
+}
+
+$editors = array(
+       0 => "/gl/gl_journal.php?ModifyGL=Yes&trans_no=%d&trans_type=%d",
+//     1=> Bank Payment,
+//     2=> Bank Deposit,
+//     4=> Funds Transfer,
+   ST_SALESINVOICE => "/sales/customer_invoice.php?ModifyInvoice=%d",
+//   11=>
+// free hand (debtors_trans.order_==0)
+//     "/sales/credit_note_entry.php?ModifyCredit=%d"
+// credit invoice
+//     "/sales/customer_credit_invoice.php?ModifyCredit=%d"
+//      12=> Customer Payment,
+   ST_CUSTDELIVERY => "/sales/customer_delivery.php?ModifyDelivery=%d",
+//   16=> Location Transfer,
+//   17=> Inventory Adjustment,
+//   20=> Supplier Invoice,
+//   21=> Supplier Credit Note,
+//   22=> Supplier Payment,
+//   25=> Purchase Order Delivery,
+//   28=> Work Order Issue,
+//   29=> Work Order Production",
+//   35=> Cost Update,
+);
+
+function edit_link($row)
+{
+       global $editors;
+
+       return isset($editors[$row["type"]]) && !is_closed_trans($row["type"], $row["type_no"]) ? 
+               pager_link(_("Edit"), 
+                       sprintf($editors[$row["type"]], $row["type_no"], $row["type"]),
+                       ICON_EDIT) : '';
+}
+
+// Tom Hallman 11 Nov 2009
+// IF(gl.type = 1... statement is for deposits/payments that may not actually result
+// in a deposit, such as when a fix is made.  Without that statement (and the
+// joining of the bank_trans table), the fix deposit/payment amount would show up 
+// incorrectly as only the positive side of the fix.    
+$sql = "SELECT IF(ISNULL(a.gl_seq),0,a.gl_seq) as gl_seq,
+       gl.tran_date,
+       gl.type,
+       gl.type_no,
+       refs.reference,
+       IF(gl.type = 1 OR gl.type = 2,
+         bank_trans.amount,
+         SUM(IF(gl.amount>0, gl.amount,0))) as amount,
+       com.memo_,
+       IF(ISNULL(u.user_id),'',u.user_id) as user_id
+       FROM ".TB_PREF."gl_trans as gl
+        LEFT JOIN ".TB_PREF."audit_trail as a ON 
+               (gl.type=a.type AND gl.type_no=a.trans_no)
+        LEFT JOIN ".TB_PREF."comments as com ON 
+               (gl.type=com.type AND gl.type_no=com.id)
+        LEFT JOIN ".TB_PREF."refs as refs ON 
+               (gl.type=refs.type AND gl.type_no=refs.id)
+        LEFT JOIN ".TB_PREF."users as u ON 
+               a.user=u.id
+        LEFT JOIN ".TB_PREF."bank_trans as bank_trans ON 
+               (gl.type=bank_trans.type AND gl.type_no=bank_trans.trans_no)            
+       WHERE gl.tran_date >= '" . date2sql($_POST['FromDate']) . "'
+       AND gl.tran_date <= '" . date2sql($_POST['ToDate']) . "'
+       AND gl.amount!=0";
+if (isset($_POST['Ref']) && $_POST['Ref'] != "") {
+       $sql .= " AND reference LIKE '%". $_POST['Ref'] . "%'";
+}      
+if (get_post('filterType') != -1) {
+       $sql .= " AND gl.type=".get_post('filterType');
+}      
+if (!check_value('AlsoClosed')) {
+       $sql .= " AND gl_seq=0";
+}
+$sql .= " GROUP BY gl.type, gl.type_no";
+
+$cols = array(
+       _("#") => array('fun'=>'journal_pos', 'align'=>'center'), 
+       _("Date") =>array('name'=>'tran_date','type'=>'date','ord'=>'desc'),
+       _("Type") => array('fun'=>'systype_name'), 
+       _("Trans #") => array('fun'=>'view_link'), 
+       _("Reference"), 
+       _("Amount") => array('type'=>'amount'),
+       _("Memo"),
+       _("User"),
+       _("View") => array('insert'=>true, 'fun'=>'gl_link'),
+       array('insert'=>true, 'fun'=>'edit_link')
+);
+
+if (!check_value('AlsoClosed')) {
+       $cols[_("#")] = 'skip';
+}
+
+$table =& new_db_pager('journal_tbl', $sql, $cols);
+
+$table->width = "80%";
+
+display_db_pager($table);
+
+end_form();
+end_page();
+
+?>
diff --git a/includes/access_levels.inc b/includes/access_levels.inc
new file mode 100644 (file)
index 0000000..9c502de
--- /dev/null
@@ -0,0 +1,306 @@
+<?php
+/**********************************************************************
+    Copyright (C) FrontAccounting, LLC.
+       Released under the terms of the GNU General Public License, GPL, 
+       as published by the Free Software Foundation, either version 3 
+       of the License, or (at your option) any later version.
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+    See the License here <http://www.gnu.org/licenses/gpl-3.0.html>.
+***********************************************************************/
+/*
+       Security sections groups various areas on both functionality and privilege levels.
+       Often analytic inquires are available only for management, and configuration
+       for administration or management staff. This is why we have those three
+       section type inside near every FA module.
+
+       Section codes 0-99 are reserved for core FA functionalities.
+       Every security section can contain up to 256 different areas.
+       External modules can extend security roles system by adding rows to 
+       $security_sections and $security_areas using section codes >=100.
+       Security areas and sections created by extension modules/plugins
+       have dynamically assigned 3-byte integer codes. The highest byte is zero
+       for sections/areas defined in this file, and extid+1 for those defined 
+       by extensions 
+*/
+define('SS_SADMIN',    1<<8);  // site admin
+define('SS_SETUP',     2<<8);  // company level setup
+define('SS_SPEC',      3<<8);  // special administration
+
+define('SS_SALES_C',11<<8); // configuration
+define('SS_SALES',     12<<8); // transactions
+define('SS_SALES_A',13<<8); // analytic functions/reports/inquires
+
+define('SS_PURCH_C',21<<8);
+define('SS_PURCH',     22<<8);
+define('SS_PURCH_A',23<<8);
+
+define('SS_ITEMS_C',31<<8);
+define('SS_ITEMS',     32<<8);
+define('SS_ITEMS_A',33<<8);
+
+define('SS_MANUF_C',41<<8);
+define('SS_MANUF',     42<<8);
+define('SS_MANUF_A',43<<8);
+
+define('SS_DIM_C',     51<<8);
+define('SS_DIM',       52<<8);
+define('SS_DIM_A',     53<<8);
+
+define('SS_GL_C',      61<<8);
+define('SS_GL',                62<<8);
+define('SS_GL_A',      63<<8);
+
+$security_sections = array(
+ SS_SADMIN => _("System administration"),
+ SS_SETUP => _("Company setup"),
+ SS_SPEC => _("Special maintenance"),
+ SS_SALES_C => _("Sales configuration"),
+ SS_SALES => _("Sales transactions"),
+ SS_SALES_A => _("Sales related reports"),
+ SS_PURCH_C => _("Purchase configuration"),
+ SS_PURCH => _("Purchase transactions"),
+ SS_PURCH_A => _("Purchase analytics"),
+ SS_ITEMS_C => _("Inventory configuration"),
+ SS_ITEMS => _("Inventory operations"),
+ SS_ITEMS_A => _("Inventory analytics"),
+ SS_MANUF_C => _("Manufacturing configuration"),
+ SS_MANUF => _("Manufacturing transations"),
+ SS_MANUF_A => _("Manufacturing analytics"),
+ SS_DIM_C => _("Dimensions configuration"),
+ SS_DIM => _("Dimensions"),
+ SS_GL_C => _("Banking & GL configuration"),
+ SS_GL => _("Banking & GL transactions"),
+ SS_GL_A => _("Banking & GL analytics")
+);
+
+/*
+       This table stores security areas available in FA. 
+       Key is area identifier used to check user rights, values are
+       codes stored for each role in security_roles table and description used
+       in roles editor.
+
+       Set of allowed access areas codes is retrieved during user login from
+       security_roles table, and cached in user profile.
+
+       Special value 'SA_OPEN' is used for publicly available pages like login/logout.
+*/
+$security_areas =array(
+//
+//     Site administration
+//
+       'SA_CREATECOMPANY' =>array(SS_SADMIN|1, _("Install/update companies")),
+       'SA_CREATELANGUAGE' => array(SS_SADMIN|2, _("Install/update languages")),
+       'SA_CREATEMODULES' => array(SS_SADMIN|3, _("Install/upgrade modules")),
+       'SA_SOFTWAREUPGRADE' => array(SS_SADMIN|4, _("Software upgrades")),
+//
+//     Company setup
+//
+       'SA_SETUPCOMPANY' => array(SS_SETUP|1, _("Company parameters")),
+       'SA_SECROLES' => array(SS_SETUP|2, _("Access levels edition")),
+       'SA_USERS' => array(SS_SETUP|3, _("Users setup")),
+       'SA_POSSETUP' => array(SS_SETUP|4, _("Point of sales definitions")),
+       'SA_PRINTERS' => array(SS_SETUP|5, _("Printers configuration")),
+       'SA_PRINTPROFILE' => array(SS_SETUP|6, _("Print profiles")),
+       'SA_PAYTERMS' => array(SS_SETUP|7, _("Payment terms")),
+       'SA_SHIPPING' => array(SS_SETUP|8, _("Shipping ways")),
+       'SA_CRSTATUS' => array(SS_SETUP|9, _("Credit status definitions changes")),
+       'SA_INVENTORYLOCATION' => array(SS_SETUP|10, _("Inventory locations changes")),
+       'SA_INVENTORYMOVETYPE'  => array(SS_SETUP|11, _("Inventory movement types")),
+       'SA_WORKCENTRES' => array(SS_SETUP|12, _("Manufacture work centres")),
+       'SA_FORMSETUP' => array(SS_SETUP|13, _("Forms setup")),
+//
+// Special and common functions
+//
+       'SA_VOIDTRANSACTION' => array(SS_SPEC|1, _("Voiding transactions")),
+       'SA_BACKUP' => array(SS_SPEC|2, _("Database backup/restore")),
+       'SA_VIEWPRINTTRANSACTION' => array(SS_SPEC|3, _("Common view/print transactions interface")),
+       'SA_ATTACHDOCUMENT' => array(SS_SPEC|4, _("Attaching documents")),
+       'SA_SETUPDISPLAY' => array(SS_SPEC|5, _("Display preferences")), //???
+       'SA_CHGPASSWD' => array(SS_SPEC|6, _("Password changes")), //???
+
+//
+// Sales related functionality
+//
+       'SA_SALESTYPES' => array(SS_SALES_C|1, _("Sales types")),
+       'SA_SALESPRICE' => array(SS_SALES_C|2, _("Sales prices edition")),
+       'SA_SALESMAN' => array(SS_SALES_C|3, _("Sales staff maintenance")),
+       'SA_SALESAREA' => array(SS_SALES_C|4, _("Sales areas maintenance")),
+       'SA_SALESGROUP' => array(SS_SALES_C|5, _("Sales groups changes")),
+       'SA_STEMPLATE' => array(SS_SALES_C|6, _("Sales templates")),
+       'SA_SRECURRENT' => array(SS_SALES_C|7, _("Recurrent invoices definitions")),
+
+       'SA_SALESTRANSVIEW' => array(SS_SALES|1,  _("Sales transactions view")),
+       'SA_CUSTOMER' => array(SS_SALES|2,  _("Sales customer and branches changes")),
+       'SA_SALESQUOTE' => array(SS_SALES|10, _("Sales quotations")),
+       'SA_SALESORDER' => array(SS_SALES|3, _("Sales orders edition")),
+       'SA_SALESDELIVERY' => array(SS_SALES|4, _("Sales deliveries edition")),
+       'SA_SALESINVOICE' => array(SS_SALES|5, _("Sales invoices edition")),
+       'SA_SALESCREDITINV' => array(SS_SALES|6, _("Sales credit notes against invoice")),
+       'SA_SALESCREDIT' => array(SS_SALES|7, _("Sales freehand credit notes")),
+       'SA_SALESPAYMNT' => array(SS_SALES|8, _("Customer payments entry")),
+       'SA_SALESALLOC' => array(SS_SALES|9, _("Customer payments allocation")),
+
+       'SA_SALESANALYTIC' => array(SS_SALES_A|1, _("Sales analytical reports")),
+       'SA_SALESBULKREP' => array(SS_SALES_A|2, _("Sales document bulk reports")),
+       'SA_PRICEREP' => array(SS_SALES_A|3, _("Sales prices listing")),
+       'SA_SALESMANREP' => array(SS_SALES_A|4, _("Sales staff listing")),
+       'SA_CUSTBULKREP' => array(SS_SALES_A|5, _("Customer bulk listing")),
+       'SA_CUSTSTATREP' => array(SS_SALES_A|6, _("Customer status report")),
+       'SA_CUSTPAYMREP' => array(SS_SALES_A|7, _("Customer payments report")),
+
+//
+// Purchase related functions
+//
+       'SA_PURCHASEPRICING' => array(SS_PURCH_C|1, _("Purchase price changes")),
+
+       'SA_SUPPTRANSVIEW' => array(SS_PURCH|1, _("Supplier transactions view")),
+       'SA_SUPPLIER' => array(SS_PURCH|2, _("Suppliers changes")),
+       'SA_PURCHASEORDER' => array(SS_PURCH|3, _("Purchase order entry")),
+       'SA_GRN' => array(SS_PURCH|4, _("Purchase receive")),
+       'SA_SUPPLIERINVOICE' => array(SS_PURCH|5, _("Supplier invoices")),
+       'SA_GRNDELETE' => array(SS_PURCH|9, _("Deleting GRN items during invoice entry")),
+       'SA_SUPPLIERCREDIT' => array(SS_PURCH|6, _("Supplier credit notes")),
+       'SA_SUPPLIERPAYMNT' => array(SS_PURCH|7, _("Supplier payments")),
+       'SA_SUPPLIERALLOC' => array(SS_PURCH|8, _("Supplier payments allocations")),
+
+       'SA_SUPPLIERANALYTIC' => array(SS_PURCH_A|1, _("Supplier analytical reports")),
+       'SA_SUPPBULKREP' => array(SS_PURCH_A|2, _("Supplier document bulk reports")),
+       'SA_SUPPPAYMREP' => array(SS_PURCH_A|3, _("Supplier payments report")),
+//
+// Inventory 
+//
+       'SA_ITEM' => array(SS_ITEMS_C|1, _("Stock items add/edit")),
+       'SA_SALESKIT' => array(SS_ITEMS_C|2, _("Sales kits")),
+       'SA_ITEMCATEGORY' => array(SS_ITEMS_C|3, _("Item categories")),
+       'SA_UOM' => array(SS_ITEMS_C|4, _("Units of measure")),
+
+       'SA_ITEMSSTATVIEW' => array(SS_ITEMS|1, _("Stock status view")),
+       'SA_ITEMSTRANSVIEW' => array(SS_ITEMS|2, _("Stock transactions view")),
+       'SA_FORITEMCODE' => array(SS_ITEMS|3, _("Foreign item codes entry")),
+       'SA_LOCATIONTRANSFER' => array(SS_ITEMS|4, _("Inventory location transfers")),
+       'SA_INVENTORYADJUSTMENT' => array(SS_ITEMS|5, _("Inventory adjustments")),
+
+       'SA_REORDER' => array(SS_ITEMS_A|1, _("Reorder levels")),
+       'SA_ITEMSANALYTIC' => array(SS_ITEMS_A|2, _("Items analytical reports and inquiries")),
+       'SA_ITEMSVALREP' => array(SS_ITEMS_A|3, _("Inventory valuation report")),
+
+//
+// Manufacturing module 
+//
+       'SA_BOM' => array(SS_MANUF_C|1, _("Bill of Materials")),
+
+       'SA_MANUFTRANSVIEW' => array(SS_MANUF|1, _("Manufacturing operations view")),
+       'SA_WORKORDERENTRY' => array(SS_MANUF|2, _("Work order entry")),
+       'SA_MANUFISSUE' => array(SS_MANUF|3, _("Material issues entry")),
+       'SA_MANUFRECEIVE' => array(SS_MANUF|4, _("Final product receive")),
+       'SA_MANUFRELEASE' => array(SS_MANUF|5, _("Work order releases")),
+
+       'SA_WORKORDERANALYTIC' => array(SS_MANUF_A|1, _("Work order analytical reports and inquiries")),
+       'SA_WORKORDERCOST' => array(SS_MANUF_A|2, _("Manufacturing cost inquiry")),
+       'SA_MANUFBULKREP' => array(SS_SALES_A|3, _("Work order bulk reports")),
+       'SA_BOMREP' => array(SS_MANUF_A|4, _("Bill of materials reports")),
+//
+// Dimensions
+//
+       'SA_DIMTAGS' => array(SS_DIM_C|1, _("Dimension tags")),
+
+       'SA_DIMTRANSVIEW' => array(SS_DIM|1, _("Dimension view")),
+
+       'SA_DIMENSION' => array(SS_DIM|2, _("Dimension entry")),
+
+       'SA_DIMENSIONREP' => array(SS_DIM|3, _("Dimension reports")),
+//
+// Banking and General Ledger
+//
+       'SA_ITEMTAXTYPE' => array(SS_GL_C|1, _("Item tax type definitions")),
+       'SA_GLACCOUNT' => array(SS_GL_C|2, _("GL accounts edition")),
+       'SA_GLACCOUNTGROUP' => array(SS_GL_C|3, _("GL account groups")),
+       'SA_GLACCOUNTCLASS' => array(SS_GL_C|4, _("GL account classes")),
+       'SA_QUICKENTRY' => array(SS_GL_C|5, _("Quick GL entry definitions")),
+       'SA_CURRENCY' => array(SS_GL_C|6, _("Currencies")),
+       'SA_BANKACCOUNT' => array(SS_GL_C|7, _("Bank accounts")),
+       'SA_TAXRATES' => array(SS_GL_C|8, _("Tax rates")),
+       'SA_TAXGROUPS' => array(SS_GL_C|8, _("Tax groups")),
+       'SA_FISCALYEARS' => array(SS_GL_C|9, _("Fiscal years maintenance")),
+       'SA_GLSETUP' => array(SS_GL_C|10, _("Company GL setup")),
+       'SA_GLACCOUNTTAGS' => array(SS_GL_C|11, _("GL Account tags")),
+
+       'SA_BANKTRANSVIEW' => array(SS_GL|1, _("Bank transactions view")),
+       'SA_GLTRANSVIEW' => array(SS_GL|2, _("GL postings view")),
+       'SA_EXCHANGERATE' => array(SS_GL|3, _("Exchange rate table changes")),
+       'SA_PAYMENT' => array(SS_GL|4, _("Bank payments")),
+       'SA_DEPOSIT' => array(SS_GL|5, _("Bank deposits")),
+       'SA_BANKTRANSFER' => array(SS_GL|6, _("Bank account transfers")),
+       'SA_RECONCILE' => array(SS_GL|7, _("Bank reconciliation")),
+       'SA_JOURNALENTRY' => array(SS_GL|8, _("Manual journal entries")),
+       'SA_BANKJOURNAL' => array(SS_GL|11, _("Journal entries to bank related accounts")),
+       'SA_BUDGETENTRY' => array(SS_GL|9, _("Budget edition")),
+       'SA_STANDARDCOST' => array(SS_GL|10, _("Item standard costs")),
+
+       'SA_GLANALYTIC' => array(SS_GL_A|1, _("GL analytical reports and inquiries")),
+       'SA_TAXREP' => array(SS_GL_A|2, _("Tax reports and inquiries")),
+       'SA_BANKREP' => array(SS_GL_A|3, _("Bank reports and inquiries")),
+       'SA_GLREP' => array(SS_GL_A|4, _("GL reports and inquiries")),
+);
+/*
+       This function should be called whenever we want to extend core access level system
+       with new security areas and/or sections i.e.: 
+       . on any page with non-standard security areas
+       . in security roles editor
+       The call should be placed between session.inc inclusion and page() call.
+       Up to 155 security sections and 155 security areas for any extension can be installed.
+*/
+function add_access_extensions()
+{
+       global $security_areas, $security_sections, $installed_extensions;
+
+       foreach($installed_extensions as $extid => $ext) {
+               $scode = 100;
+               $acode = 100;
+               $accext = get_access_extensions($extid);
+               $extsections = $accext[1];
+               $extareas = $accext[0];
+               $extcode = $extid<<16;
+               
+               $trans = array();
+               foreach($extsections as $code =>$name) {
+                       $trans[$code] = $scode<<8;
+                       // reassign section codes
+                       $security_sections[$trans[$code]|$extcode] = $name;
+                       $scode++;
+               }
+               foreach($extareas as $code => $area) {
+                       $section = $area[0]&0xff00;
+                       // extension modules:
+                       // if area belongs to nonstandard section
+                       // use translated section codes and
+                       // preserve lower part of area code
+                       if (isset($trans[$section])) {
+                               $section = $trans[$section];
+                       } 
+                               // otherwise assign next available
+                               // area code >99
+                       $area[0] = $extcode | $section | ($acode++);
+                       $security_areas[$code] = $area;
+               }
+       }
+}
+/*
+       Helper function to retrieve extension access definitions in isolated environment.
+*/
+function get_access_extensions($id) {
+       global $path_to_root, $installed_extensions;
+       
+       $ext = $installed_extensions[$id];
+       
+       $security_sections = $security_areas = array();
+       
+       if (isset($ext['acc_file']))
+               include($path_to_root.($ext['type'] == 'plugin' ? '/modules/':'/').$ext['path'].'/'.$ext['acc_file']);
+
+       return array($security_areas, $security_sections);
+}
+
+?>
\ No newline at end of file
diff --git a/includes/db/audit_trail_db.inc b/includes/db/audit_trail_db.inc
new file mode 100644 (file)
index 0000000..f9efe9a
--- /dev/null
@@ -0,0 +1,140 @@
+<?php
+/**********************************************************************
+       Copyright (C) FrontAccounting, LLC.
+       Released under the terms of the GNU General Public License, GPL, 
+       as published by the Free Software Foundation, either version 3 
+       of the License, or (at your option) any later version.
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+       See the License here <http://www.gnu.org/licenses/gpl-3.0.html>.
+***********************************************************************/
+
+function add_audit_trail($trans_type, $trans_no, $trans_date, $descr='')
+{
+       $sql = "INSERT INTO ".TB_PREF."audit_trail"
+               . " (type, trans_no, user, fiscal_year, gl_date, description, gl_seq)
+                       VALUES(".db_escape($trans_type).", ".db_escape($trans_no).","
+                       . $_SESSION["wa_current_user"]->user. ","
+                       . get_company_pref('f_year') .","
+                       . "'". date2sql($trans_date) ."',"
+                       . db_escape($descr). ", 0)";
+
+       db_query($sql, "Cannot add audit info");
+       
+       // all audit records beside latest one should have gl_seq set to NULL
+       // to avoid need for subqueries (not existing in MySQL 3) all over the code
+       $sql = "UPDATE ".TB_PREF."audit_trail SET gl_seq = NULL"
+               . " WHERE type=".db_escape($trans_type)." AND trans_no="
+               .db_escape($trans_no)." AND id!=".db_insert_id();
+
+       db_query($sql, "Cannot update audit gl_seq");
+}
+
+function get_audit_trail_all($trans_type, $trans_no)
+{
+       $sql = "SELECT * FROM ".TB_PREF."audit_trail"
+               ." WHERE type=".db_escape($trans_type)." AND trans_no="
+               .db_escape($trans_no);
+
+       return db_query($sql, "Cannot get all audit info for transaction");
+}
+
+function get_audit_trail_last($trans_type, $trans_no)
+{
+       $sql = "SELECT * FROM ".TB_PREF."audit_trail"
+               ." WHERE type=".db_escape($trans_type).
+                       " AND trans_no=".db_escape($trans_no)." AND NOT ISNULL(gl_seq)";
+
+       $res = db_query($sql, "Cannot get last audit info for transaction");
+       if ($res)
+               $row = db_fetch($res);
+
+       return $row;
+}
+
+/*
+       Confirm and close for edition all transactions up to date $todate, 
+       and reindex     journal.
+*/
+function close_transactions($todate) {
+       $errors = 0;
+       $sql = "SELECT DISTINCT a.id, a.gl_date, a.fiscal_year"
+               ." FROM ".TB_PREF."gl_trans gl"
+               ." LEFT JOIN ". TB_PREF."audit_trail a ON 
+                       (gl.type=a.type AND gl.type_no=a.trans_no)"
+               . " WHERE gl_date<='". date2sql($todate) ."'"
+               . " AND NOT ISNULL(gl_seq)"
+               . " ORDER BY a.fiscal_year, a.gl_date, a.id";
+
+       $result = db_query($sql, "Cannot select transactions for closing");
+
+       if (db_num_rows($result)) {
+               $last_year = 0;
+
+               while ($row = db_fetch($result)) {
+                       if ($row['fiscal_year'] == null) {
+                               $errors = 1; continue;
+                       }
+                       if ($last_year != $row['fiscal_year']) {
+                               $last_year = $row['fiscal_year'];
+                               $counter = 1; // reset counter on fiscal year change
+                       } else
+                               $counter++;
+                       $sql2 = "UPDATE ".TB_PREF."audit_trail SET"
+                               . " gl_seq=$counter"
+                               . " WHERE id=".$row['id'];
+                                                                                       
+                       db_query($sql2, "Cannot reindex journal");
+               }
+       }
+       
+       if ($errors) 
+               display_warning(_("Some transactions journal GL postings were not indexed due to lack of audit trail record."));
+}
+
+/*
+       Reopen all transactions for edition up from date $fromdate
+*/
+function open_transactions($fromdate) {
+
+       $sql = "SELECT a.id, a.gl_date, a.fiscal_year"
+               ." FROM ".TB_PREF."gl_trans gl"
+               ." LEFT JOIN ". TB_PREF."audit_trail a ON 
+                       (gl.type=a.type AND gl.type_no=a.trans_no)"
+               . " WHERE gl_date>='". date2sql($fromdate) ."'"
+               . " AND !ISNULL(gl_seq)"
+               . " ORDER BY a.fiscal_year, a.gl_date, a.id";
+
+       $result = db_query($sql, "Cannot select transactions for openning");
+
+       if (db_num_rows($result)) {
+               $last_year = 0;
+
+               while ($row = db_fetch($result)) {
+                       if ($row['fiscal_year'] == null) {
+                               continue;
+                       }
+                       $sql2 = "UPDATE ".TB_PREF."audit_trail SET"
+                               . " gl_seq=0"
+                               . " WHERE id=".$row['id'];
+                                                                                       
+                       db_query($sql2, "Cannot clear journal order");
+               }
+       }
+}
+/*
+       Closed transactions have gl_seq number assigned.
+*/
+function is_closed_trans($type, $trans_no) {
+       $sql = "SELECT  gl_seq  FROM ".TB_PREF."audit_trail"
+               . " WHERE type=".db_escape($type)
+               ." AND trans_no=".db_escape($trans_no)
+               ." AND gl_seq>0";
+
+       $res = db_query($sql, "Cannot check transaction");
+
+       return db_num_rows($res);
+}
+
+?>
diff --git a/includes/reserved.inc b/includes/reserved.inc
deleted file mode 100644 (file)
index 5cc1fcd..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-<?php
-/**********************************************************************
-    Copyright (C) FrontAccounting, LLC.
-       Released under the terms of the GNU General Public License, GPL, 
-       as published by the Free Software Foundation, either version 3 
-       of the License, or (at your option) any later version.
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
-    See the License here <http://www.gnu.org/licenses/gpl-3.0.html>.
-***********************************************************************/
-// always use capitals in reserved words (for is_reserved_word comparisons)
-
-$any_item = 'AN';
-$any_number = -1;
-$all_option = '';
-$all_option_numeric = -1;
-
-class reserved_words 
-{
-       
-       function get_any() 
-       {
-               global $any_item;
-               return $any_item;
-       } 
-       
-       function get_any_numeric() 
-       {
-               global $any_number;
-               return $any_number;
-       }
-       
-       function get_all() 
-       {
-               global $all_option;
-               return $all_option;
-       }
-       
-       function get_all_numeric() 
-       {
-               global $all_option_numeric;
-               return $all_option_numeric;
-       }
-       
-       function is_reserved_word($str) 
-       {
-               $str = strtoupper($str);
-               if ($str == get_any())
-                       return true;
-               if ($str == get_all())
-                       return true;                    
-               return false;
-       }
-       
-}
-
-?>
\ No newline at end of file
diff --git a/installed_extensions.php b/installed_extensions.php
deleted file mode 100644 (file)
index 0022561..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-
-/* How to make new entries here
-
--- if adding extensions at the beginning of the list, make sure it's index is set to 0 (it has ' 0 => ')
--- 'app_file' is the application file name to be put into folder applications
--- 'name' is the name of the extension module. Will become the index of the application
--- 'title' is the Menu Title
--- 'folder' is the folder where the extension files exist
-
-*/
-
-$installed_extensions = array ();
-
-// example
-/*
-$installed_extensions = array (
-       0 => array ('app_file' => 'organizer.php', 'name' => 'organizer', 'title' => 'Organizer', 'folder' => 'organizer'),
-       array ('app_file' => 'payroll.php', 'name' => 'payroll', 'title' => 'Payroll', 'folder' => 'payroll')
-       );
-*/     
-?>
\ No newline at end of file
diff --git a/js/payalloc.js b/js/payalloc.js
new file mode 100644 (file)
index 0000000..7bdd991
--- /dev/null
@@ -0,0 +1,64 @@
+/**********************************************************************
+    Copyright (C) FrontAccounting, LLC.
+       Released under the terms of the GNU General Public License, GPL, 
+       as published by the Free Software Foundation, either version 3 
+       of the License, or (at your option) any later version.
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+    See the License here <http://www.gnu.org/licenses/gpl-3.0.html>.
+***********************************************************************/
+function focus_alloc(i) {
+    save_focus(i);
+       i.setAttribute('_last', get_amount(i.name));
+}
+
+function blur_alloc(i) {
+       var change = get_amount(i.name);
+               price_format(i.name, change, user.pdec);                
+               if (i.name != 'amount' && i.name != 'charge') {
+                       if (change<0) change = 0;
+                       change = change-i.getAttribute('_last');
+                       if (i.name == 'discount') change = -change;
+
+                       var total = get_amount('amount')+change;
+                       price_format('amount', total, user.pdec, 0);
+               }
+}
+
+function allocate_all(doc) {
+       var amount = get_amount('amount'+doc);
+       var unallocated = get_amount('un_allocated'+doc);
+       var total = get_amount('amount');
+       var left = 0;
+       total -=  (amount-unallocated);
+       left -= (amount-unallocated);
+       amount = unallocated;
+       if(left<0) {
+               total  += left;
+               amount += left;
+               left = 0;
+       }
+       price_format('amount'+doc, amount, user.pdec);
+       price_format('amount', total, user.pdec);
+}
+
+function allocate_none(doc) {
+       amount = get_amount('amount'+doc);
+       total = get_amount('amount');
+       price_format('amount'+doc, 0, user.pdec);
+       price_format('amount', total-amount, user.pdec);
+}
+
+var allocations = {
+       '.amount': function(e) {
+               e.onblur = function() {
+                       blur_alloc(this);
+                 };
+               e.onfocus = function() {
+                       focus_alloc(this);
+               };
+       }
+}
+
+Behaviour.register(allocations);
diff --git a/lang/installed_languages.inc b/lang/installed_languages.inc
deleted file mode 100644 (file)
index 158bab0..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-<?php
-
-/* How to make new entries here
-
--- if adding languages at the beginning of the list, make sure it's index is set to 0 (it has ' 0 => ')
--- 'code' should match the name of the directory for the language under \lang
--- 'name' is the name that will be displayed in the language selection list (in Users and Display Setup)
--- 'rtl' only needs to be set for right-to-left languages like Arabic and Hebrew
-
-*/
-
-
-$installed_languages = array (
-       0 => array ('code' => 'en_GB', 'name' => 'English', 'encoding' => 'iso-8859-1'),
-               array ('code' => 'en_US', 'name' => 'English (US)', 'encoding' => 'iso-8859-1'),
-       );
-
-$dflt_lang = 'en_GB';
-?>
\ No newline at end of file
diff --git a/modules/installed_modules.php b/modules/installed_modules.php
deleted file mode 100644 (file)
index bc13fe9..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-<?php
-
-/*****************************************************************
-External modules for FrontAccounting
-******************************************************************/
-
-
-$installed_modules = array ();
-?>
\ No newline at end of file
diff --git a/reporting/rep111.php b/reporting/rep111.php
new file mode 100644 (file)
index 0000000..a16a1d7
--- /dev/null
@@ -0,0 +1,160 @@
+<?php
+/**********************************************************************
+    Copyright (C) FrontAccounting, LLC.
+       Released under the terms of the GNU General Public License, GPL, 
+       as published by the Free Software Foundation, either version 3 
+       of the License, or (at your option) any later version.
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+    See the License here <http://www.gnu.org/licenses/gpl-3.0.html>.
+***********************************************************************/
+$page_security = $_POST['PARAM_0'] == $_POST['PARAM_1'] ?
+       'SA_SALESTRANSVIEW' : 'SA_SALESBULKREP';
+// ----------------------------------------------------------------
+// $ Revision: 2.0 $
+// Creator:    Joe Hunt
+// date_:      2005-05-19
+// Title:      Print Sales Quotations
+// ----------------------------------------------------------------
+$path_to_root="..";
+
+include_once($path_to_root . "/includes/session.inc");
+include_once($path_to_root . "/includes/date_functions.inc");
+include_once($path_to_root . "/includes/data_checks.inc");
+include_once($path_to_root . "/sales/includes/sales_db.inc");
+
+//----------------------------------------------------------------------------------------------------
+
+print_sales_quotations();
+
+function print_sales_quotations()
+{
+       global $path_to_root, $print_as_quote;
+
+       include_once($path_to_root . "/reporting/includes/pdf_report.inc");
+
+       $from = $_POST['PARAM_0'];
+       $to = $_POST['PARAM_1'];
+       $currency = $_POST['PARAM_2'];
+       $email = $_POST['PARAM_3'];
+       $comments = $_POST['PARAM_4'];
+
+       if ($from == null)
+               $from = 0;
+       if ($to == null)
+               $to = 0;
+       $dec = user_price_dec();
+
+       $cols = array(4, 60, 225, 300, 325, 385, 450, 515);
+
+       // $headers in doctext.inc
+       $aligns = array('left', 'left', 'right', 'left', 'right', 'right', 'right');
+
+       $params = array('comments' => $comments);
+
+       $cur = get_company_Pref('curr_default');
+
+       if ($email == 0)
+       {
+               $rep = new FrontReport(_("SALES QUOTATION"), "SalesQuotationBulk", user_pagesize());
+               $rep->currency = $cur;
+               $rep->Font();
+               $rep->Info($params, $cols, null, $aligns);
+       }
+
+       for ($i = $from; $i <= $to; $i++)
+       {
+               $myrow = get_sales_order_header($i, ST_SALESQUOTE);
+               $baccount = get_default_bank_account($myrow['curr_code']);
+               $params['bankaccount'] = $baccount['id'];
+               $branch = get_branch($myrow["branch_code"]);
+               if ($email == 1)
+               {
+                       $rep = new FrontReport("", "", user_pagesize());
+                       $rep->currency = $cur;
+                       $rep->Font();
+                       $rep->filename = "SalesQuotation" . $i . ".pdf";
+                       $rep->Info($params, $cols, null, $aligns);
+               }
+               $rep->title = _("SALES QUOTATION");
+               $rep->Header2($myrow, $branch, $myrow, $baccount, 7);
+
+               $result = get_sales_order_details($i, ST_SALESQUOTE);
+               $SubTotal = 0;
+               while ($myrow2=db_fetch($result))
+               {
+                       $Net = round2(((1 - $myrow2["discount_percent"]) * $myrow2["unit_price"] * $myrow2["quantity"]),
+                          user_price_dec());
+                       $SubTotal += $Net;
+                       $DisplayPrice = number_format2($myrow2["unit_price"],$dec);
+                       $DisplayQty = number_format2($myrow2["quantity"],get_qty_dec($myrow2['stk_code']));
+                       $DisplayNet = number_format2($Net,$dec);
+                       if ($myrow2["discount_percent"]==0)
+                               $DisplayDiscount ="";
+                       else
+                               $DisplayDiscount = number_format2($myrow2["discount_percent"]*100,user_percent_dec()) . "%";
+                       $rep->TextCol(0, 1,     $myrow2['stk_code'], -2);
+                       $oldrow = $rep->row;
+                       $rep->TextColLines(1, 2, $myrow2['description'], -2);
+                       $newrow = $rep->row;
+                       $rep->row = $oldrow;
+                       $rep->TextCol(2, 3,     $DisplayQty, -2);
+                       $rep->TextCol(3, 4,     $myrow2['units'], -2);
+                       $rep->TextCol(4, 5,     $DisplayPrice, -2);
+                       $rep->TextCol(5, 6,     $DisplayDiscount, -2);
+                       $rep->TextCol(6, 7,     $DisplayNet, -2);
+                       $rep->row = $newrow;
+                       //$rep->NewLine(1);
+                       if ($rep->row < $rep->bottomMargin + (15 * $rep->lineHeight))
+                               $rep->Header2($myrow, $branch, $myrow, $baccount, 9);
+               }
+               if ($myrow['comments'] != "")
+               {
+                       $rep->NewLine();
+                       $rep->TextColLines(1, 5, $myrow['comments'], -2);
+               }
+               $DisplaySubTot = number_format2($SubTotal,$dec);
+               $DisplayFreight = number_format2($myrow["freight_cost"],$dec);
+
+               $rep->row = $rep->bottomMargin + (15 * $rep->lineHeight);
+               $linetype = true;
+               $doctype = 9;
+               if ($rep->currency != $myrow['curr_code'])
+               {
+                       include($path_to_root . "/reporting/includes/doctext2.inc");
+               }
+               else
+               {
+                       include($path_to_root . "/reporting/includes/doctext.inc");
+               }
+
+               $rep->TextCol(3, 6, $doc_Sub_total, -2);
+               $rep->TextCol(6, 7,     $DisplaySubTot, -2);
+               $rep->NewLine();
+               $rep->TextCol(3, 6, $doc_Shipping, -2);
+               $rep->TextCol(6, 7,     $DisplayFreight, -2);
+               $rep->NewLine();
+               $DisplayTotal = number_format2($myrow["freight_cost"] + $SubTotal, $dec);
+               $rep->Font('bold');
+               $rep->TextCol(3, 6, $doc_TOTAL_ORDER, - 2);
+               $rep->TextCol(6, 7,     $DisplayTotal, -2);
+               $rep->Font();
+               if ($email == 1)
+               {
+                       if ($myrow['contact_email'] == '')
+                       {
+                               $myrow['contact_email'] = $branch['email'];
+                               if ($myrow['contact_email'] == '')
+                                       $myrow['contact_email'] = $myrow['master_email'];
+                               $myrow['DebtorName'] = $branch['br_name'];
+                       }
+                       //$myrow['reference'] = $i;
+                       $rep->End($email, $doc_Invoice_no . " " . $i, $myrow);
+               }
+       }
+       if ($email == 0)
+               $rep->End();
+}
+
+?>
\ No newline at end of file
diff --git a/reporting/rep305.php b/reporting/rep305.php
new file mode 100644 (file)
index 0000000..b752c6e
--- /dev/null
@@ -0,0 +1,139 @@
+<?php
+/**********************************************************************
+    Copyright (C) FrontAccounting, LLC.
+       Released under the terms of the GNU General Public License, GPL, 
+       as published by the Free Software Foundation, either version 3 
+       of the License, or (at your option) any later version.
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+    See the License here <http://www.gnu.org/licenses/gpl-3.0.html>.
+***********************************************************************/
+$page_security = 'SA_SUPPLIERANALYTIC';
+// ----------------------------------------------------------------
+// $ Revision: 2.0 $
+// Creator:    Joe Hunt
+// date_:      2005-05-19
+// Title:      GRN Valuation Report
+// ----------------------------------------------------------------
+$path_to_root="..";
+
+include_once($path_to_root . "/includes/session.inc");
+include_once($path_to_root . "/includes/date_functions.inc");
+include_once($path_to_root . "/includes/data_checks.inc");
+include_once($path_to_root . "/includes/banking.inc");
+include_once($path_to_root . "/gl/includes/gl_db.inc");
+include_once($path_to_root . "/inventory/includes/db/items_category_db.inc");
+
+//----------------------------------------------------------------------------------------------------
+
+print_grn_valuation();
+
+function getTransactions($from, $to)
+{
+       $from = date2sql($from);
+       $to = date2sql($to);
+       $sql = "SELECT ".TB_PREF."grn_batch.delivery_date, ".TB_PREF."grn_batch.supplier_id, 
+                       ".TB_PREF."purch_order_details.*,
+                       ".TB_PREF."stock_master.description
+               FROM ".TB_PREF."stock_master,
+                       ".TB_PREF."purch_order_details,
+                       ".TB_PREF."grn_batch
+               WHERE ".TB_PREF."stock_master.stock_id=".TB_PREF."purch_order_details.item_code
+               AND ".TB_PREF."grn_batch.purch_order_no=".TB_PREF."purch_order_details.order_no
+               AND ".TB_PREF."purch_order_details.quantity_received>0
+               AND ".TB_PREF."grn_batch.delivery_date>='$from'
+               AND ".TB_PREF."grn_batch.delivery_date<='$to'
+               ORDER BY ".TB_PREF."stock_master.stock_id, ".TB_PREF."grn_batch.delivery_date";
+    return db_query($sql,"No transactions were returned");
+
+}
+
+//----------------------------------------------------------------------------------------------------
+
+function print_grn_valuation()
+{
+    global $path_to_root;
+
+       $from = $_POST['PARAM_0'];
+       $to = $_POST['PARAM_1'];
+       $comments = $_POST['PARAM_2'];
+       $destination = $_POST['PARAM_3'];
+       if ($destination)
+               include_once($path_to_root . "/reporting/includes/excel_report.inc");
+       else
+               include_once($path_to_root . "/reporting/includes/pdf_report.inc");
+
+    $dec = user_price_dec();
+
+       $cols = array(0, 75, 225, 275, 345, 390, 445,   515);
+       $headers = array(_('Stock ID'), _('Description'), _('PO No'), _('Qty Received'), _('Unit Price'), _('Actual Price'), _('Total'));
+
+       $aligns = array('left', 'left', 'left', 'right', 'right', 'right', 'right');
+
+    $params =   array(         0 => $comments,
+                                   1 => array('text' => _('Period'),'from' => $from, 'to' => $to));
+
+    $rep = new FrontReport(_('GRN Valuation Report'), "GRNValuationReport", user_pagesize());
+
+    $rep->Font();
+    $rep->Info($params, $cols, $headers, $aligns);
+    $rep->Header();
+
+       $res = getTransactions($from, $to);
+       $total = $qtotal = $grandtotal = 0.0;
+       $stock_id = '';
+       while ($trans=db_fetch($res))
+       {
+               if ($stock_id != $trans['item_code'])
+               {
+                       if ($stock_id != '')
+                       {
+                               $rep->Line($rep->row  - 4);
+                               $rep->NewLine(2);
+                               $rep->TextCol(0, 3, _('Total'));
+                               $rep->AmountCol(3, 4, $qtotal, $qdec);
+                               $rep->AmountCol(6, 7, $total, $dec);
+                               $rep->NewLine();
+                               $total = $qtotal = 0;
+                       }
+                       $stock_id = $trans['item_code'];
+               }
+               $curr = get_supplier_currency($trans['supplier_id']);
+               $rate = get_exchange_rate_from_home_currency($curr, sql2date($trans['delivery_date']));
+               $trans['unit_price'] *= $rate;
+               $trans['act_price'] *= $rate;
+
+               $rep->NewLine();
+               $rep->TextCol(0, 1, $trans['item_code']);
+               $rep->TextCol(1, 2, $trans['description']);
+               $rep->TextCol(2, 3, $trans['order_no']);
+               $qdec = get_qty_dec($trans['item_code']);
+               $rep->AmountCol(3, 4, $trans['quantity_received'], $qdec);
+               $rep->AmountCol(4, 5, $trans['unit_price'], $dec);
+               $rep->AmountCol(5, 6, $trans['act_price'], $dec);
+               $amt = round2($trans['quantity_received'] * $trans['act_price'], $dec);
+               $rep->AmountCol(6, 7, $amt, $dec);
+               $total += $amt;
+               $qtotal += $trans['quantity_received'];
+               $grandtotal += $amt;
+       }
+       if ($stock_id != '')
+       {
+               $rep->Line($rep->row  - 4);
+               $rep->NewLine(2);
+               $rep->TextCol(0, 3, _('Total'));
+               $rep->AmountCol(3, 4, $qtotal, $qdec);
+               $rep->AmountCol(6, 7, $total, $dec);
+               $rep->Line($rep->row  - 4);
+               $rep->NewLine(2);
+               $rep->TextCol(0, 6, _('Grand Total'));
+               $rep->AmountCol(6, 7, $grandtotal, $dec);
+       }
+
+       $rep->Line($rep->row  - 4);
+       $rep->NewLine();
+    $rep->End();
+}
+
+?>
\ No newline at end of file
diff --git a/reporting/rep409.php b/reporting/rep409.php
new file mode 100644 (file)
index 0000000..bf6bb3d
--- /dev/null
@@ -0,0 +1,146 @@
+<?php
+/**********************************************************************
+    Copyright (C) FrontAccounting, LLC.
+       Released under the terms of the GNU General Public License, GPL, 
+       as published by the Free Software Foundation, either version 3 
+       of the License, or (at your option) any later version.
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+    See the License here <http://www.gnu.org/licenses/gpl-3.0.html>.
+***********************************************************************/
+$page_security = $_POST['PARAM_0'] == $_POST['PARAM_1'] ?
+       'SA_MANUFTRANSVIEW' : 'SA_MANUFBULKREP';
+// ----------------------------------------------------------------
+// $ Revision: 2.0 $
+// Creator:    Janusz Dobrowolski
+// date_:      2008-01-14
+// Title:      Print Workorders
+// draft version!
+// ----------------------------------------------------------------
+$path_to_root="..";
+
+include_once($path_to_root . "/includes/session.inc");
+include_once($path_to_root . "/includes/date_functions.inc");
+include_once($path_to_root . "/includes/data_checks.inc");
+include_once($path_to_root . "/manufacturing/includes/manufacturing_db.inc");
+
+//----------------------------------------------------------------------------------------------------
+
+print_workorders();
+
+//----------------------------------------------------------------------------------------------------
+
+function print_workorders()
+{
+       global $path_to_root, $SysPrefs;
+
+       include_once($path_to_root . "/reporting/includes/pdf_report.inc");
+
+       $from = $_POST['PARAM_0'];
+       $to = $_POST['PARAM_1'];
+       $email = $_POST['PARAM_2'];
+       $comments = $_POST['PARAM_3'];
+
+       if ($from == null)
+               $from = 0;
+       if ($to == null)
+               $to = 0;
+       $dec = user_price_dec();
+
+       $fno = explode("-", $from);
+       $tno = explode("-", $to);
+
+       $cols = array(4, 60, 190, 255, 320, 385, 450, 515);
+
+       // $headers in doctext.inc
+       $aligns = array('left', 'left', 'left', 'left', 'right', 'right', 'right');
+
+       $params = array('comments' => $comments);
+
+       $cur = get_company_Pref('curr_default');
+
+       if ($email == 0)
+       {
+               $rep = new FrontReport(_('WORK ORDER'), "WorkOrderBulk", user_pagesize());
+               $rep->currency = $cur;
+               $rep->Font();
+               $rep->Info($params, $cols, null, $aligns);
+       }
+
+       for ($i = $fno[0]; $i <= $tno[0]; $i++)
+       {
+               $myrow = get_work_order($i);
+               if ($myrow === false)
+                       continue;
+               $date_ = sql2date($myrow["date_"]);                     
+               if ($email == 1)
+               {
+                       $rep = new FrontReport("", "", user_pagesize());
+                       $rep->currency = $cur;
+                       $rep->Font();
+                               $rep->title = _('WORK ORDER');
+                               $rep->filename = "WorkOrder" . $myrow['reference'] . ".pdf";
+                       $rep->Info($params, $cols, null, $aligns);
+               }
+               else
+                       $rep->title = _('WORK ORDER');
+               $rep->Header2($myrow, null, null, '', 26);
+
+               $result = get_wo_requirements($i);
+               $rep->TextCol(0, 5,_("Work Order Requirements"), -2);
+               $rep->NewLine(2);
+               $has_marked = false;
+               while ($myrow2=db_fetch($result))
+               {
+                       $qoh = 0;
+                       $show_qoh = true;
+                       // if it's a non-stock item (eg. service) don't show qoh
+                       if (!has_stock_holding($myrow2["mb_flag"]))
+                               $show_qoh = false;
+
+                       if ($show_qoh)
+                               $qoh = get_qoh_on_date($myrow2["stock_id"], $myrow2["loc_code"], $date_);
+
+                       if ($show_qoh && ($myrow2["units_req"] * $myrow["units_issued"] > $qoh) &&
+                               !$SysPrefs->allow_negative_stock())
+                       {
+                               // oops, we don't have enough of one of the component items
+                               $has_marked = true;
+                       }
+                       else
+                               $has_marked = false;
+                       if ($has_marked)
+                               $str = $myrow2['stock_id']." ***";
+                       else
+                               $str = $myrow2['stock_id'];
+                       $rep->TextCol(0, 1,     $str, -2);
+                       $rep->TextCol(1, 2, $myrow2['description'], -2);
+
+                       $rep->TextCol(2, 3,     $myrow2['location_name'], -2);
+                       $rep->TextCol(3, 4,     $myrow2['WorkCentreDescription'], -2);
+                       $dec = get_qty_dec($myrow2["stock_id"]);
+
+                       $rep->AmountCol(4, 5,   $myrow2['units_req'], $dec, -2);
+                       $rep->AmountCol(5, 6,   $myrow2['units_req'] * $myrow['units_issued'], $dec, -2);
+                       $rep->AmountCol(6, 7,   $myrow2['units_issued'], $dec, -2);
+                       $rep->NewLine(1);
+                       if ($rep->row < $rep->bottomMargin + (15 * $rep->lineHeight))
+                               $rep->Header2($myrow, null, null,'',26);
+               }
+               $rep->NewLine(1);
+               $rep->TextCol(0, 5," *** = "._("Insufficient stock"), -2);
+
+               $comments = get_comments(ST_WORKORDER, $i);
+               if ($comments && db_num_rows($comments))
+               {
+                       $rep->NewLine();
+                       while ($comment=db_fetch($comments))
+                               $rep->TextColLines(0, 6, $comment['memo_'], -2);
+               }
+       }
+       if ($email == 0)
+               $rep->End();
+}
+
+?>
\ No newline at end of file
diff --git a/reporting/rep710.php b/reporting/rep710.php
new file mode 100644 (file)
index 0000000..befed43
--- /dev/null
@@ -0,0 +1,119 @@
+<?php
+/**********************************************************************
+    Copyright (C) FrontAccounting, LLC.
+       Released under the terms of the GNU General Public License, GPL, 
+       as published by the Free Software Foundation, either version 3 
+       of the License, or (at your option) any later version.
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+    See the License here <http://www.gnu.org/licenses/gpl-3.0.html>.
+***********************************************************************/
+$page_security = 'SA_GLANALYTIC';
+// ----------------------------------------------------------------
+// $ Revision: 2.0 $
+// Creator:    Joe Hunt
+// date_:      2005-05-19
+// Title:      Audit Trail
+// ----------------------------------------------------------------
+$path_to_root="..";
+
+include_once($path_to_root . "/includes/session.inc");
+include_once($path_to_root . "/includes/date_functions.inc");
+include_once($path_to_root . "/includes/data_checks.inc");
+include_once($path_to_root . "/gl/includes/gl_db.inc");
+include_once($path_to_root . "/includes/ui/ui_view.inc");
+
+//----------------------------------------------------------------------------------------------------
+
+print_audit_trail();
+
+function getTransactions($from, $to, $type, $user)
+{
+       $fromdate = date2sql($from) . " 00:00:00";
+       $todate = date2sql($to). " 23:59.59";
+
+       $sql = "SELECT a.*, 
+               SUM(IF(ISNULL(g.amount), NULL, IF(g.amount > 0, g.amount, 0))) AS amount,
+               u.user_id,
+               UNIX_TIMESTAMP(a.stamp) as unix_stamp
+               FROM ".TB_PREF."audit_trail AS a JOIN ".TB_PREF."users AS u
+               LEFT JOIN ".TB_PREF."gl_trans AS g ON (g.type_no=a.trans_no
+                       AND g.type=a.type)
+               WHERE a.user = u.id ";
+       if ($type != -1)
+               $sql .= "AND a.type=$type ";
+       if ($user != -1)        
+               $sql .= "AND a.user='$user' ";
+       $sql .= "AND a.stamp >= '$fromdate'
+                       AND a.stamp <= '$todate'
+               GROUP BY a.trans_no,a.gl_seq,a.stamp    
+               ORDER BY a.stamp,a.gl_seq";
+    return db_query($sql,"No transactions were returned");
+}
+//----------------------------------------------------------------------------------------------------
+
+function print_audit_trail()
+{
+    global $path_to_root, $systypes_array;
+
+    $from = $_POST['PARAM_0'];
+    $to = $_POST['PARAM_1'];
+    $systype = $_POST['PARAM_2'];
+    $user = $_POST['PARAM_3'];
+    $comments = $_POST['PARAM_4'];
+       $destination = $_POST['PARAM_5'];
+       if ($destination)
+               include_once($path_to_root . "/reporting/includes/excel_report.inc");
+       else
+               include_once($path_to_root . "/reporting/includes/pdf_report.inc");
+
+    $dec = user_price_dec();
+
+    $cols = array(0, 60, 120, 180, 240, 340, 400, 460, 520);
+
+    $headers = array(_('Date'), _('Time'), _('User'), _('Trans Date'),
+       _('Type'), _('#'), _('Action'), _('Amount'));
+
+    $aligns = array('left', 'left', 'left', 'left', 'left', 'left', 'left', 'right');
+
+       $usr = get_user($user);
+       $user_id = $usr['user_id'];
+    $params =   array(         0 => $comments,
+                                   1 => array('text' => _('Period'), 'from' => $from,'to' => $to),
+                       2 => array('text' => _('Type'), 'from' => ($systype != -1 ? $systypes_array[$systype] : _('All')), 'to' => ''),
+                       3 => array('text' => _('User'), 'from' => ($user != -1 ? $user_id : _('All')), 'to' => ''));
+
+    $rep = new FrontReport(_('Audit Trail'), "AuditTrail", user_pagesize());
+
+    $rep->Font();
+    $rep->Info($params, $cols, $headers, $aligns);
+    $rep->Header();
+
+    $trans = getTransactions($from, $to, $systype, $user);
+
+    while ($myrow=db_fetch($trans))
+    {
+        $rep->TextCol(0, 1, sql2date(date("Y-m-d", $myrow['unix_stamp'])));
+        if (user_date_format() == 0)
+               $rep->TextCol(1, 2, date("h:i:s a", $myrow['unix_stamp']));
+        else   
+               $rep->TextCol(1, 2, date("H:i:s", $myrow['unix_stamp']));
+        $rep->TextCol(2, 3, $myrow['user_id']);
+        $rep->TextCol(3, 4, sql2date($myrow['gl_date']));
+        $rep->TextCol(4, 5, $systypes_array[$myrow['type']]);
+        $rep->TextCol(5, 6, $myrow['trans_no']);
+        if ($myrow['gl_seq'] == null)
+               $action = _('Changed');
+        else
+               $action = _('Closed');
+        $rep->TextCol(6, 7, $action);
+        if ($myrow['amount'] != null)
+               $rep->AmountCol(7, 8, $myrow['amount'], $dec);
+        $rep->NewLine(1, 2);
+    }
+    $rep->Line($rep->row  + 4);
+    $rep->End();
+}
+
+?>
\ No newline at end of file
diff --git a/sql/alter2.2.php b/sql/alter2.2.php
new file mode 100644 (file)
index 0000000..c534523
--- /dev/null
@@ -0,0 +1,340 @@
+<?php
+/**********************************************************************
+    Copyright (C) FrontAccounting, LLC.
+       Released under the terms of the GNU General Public License, GPL, 
+       as published by the Free Software Foundation, either version 3 
+       of the License, or (at your option) any later version.
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+    See the License here <http://www.gnu.org/licenses/gpl-3.0.html>.
+***********************************************************************/
+
+class fa2_2 {
+       var $version = '2.2';   // version installed
+       var $description;
+       var $sql = 'alter2.2.sql';
+       var $preconf = true;
+       var $beta = false; // upgrade from 2.1 or 2.2beta; set in pre_check
+       
+       function fa2_2() {
+               global $security_groups;
+               $this->beta = !isset($security_groups);
+               $this->description = _('Upgrade from version 2.1/2.2beta to 2.2');
+               $this->preconf = fix_extensions();
+       }
+       
+       //
+       //      Install procedure. All additional changes 
+       //      not included in sql file should go here.
+       //
+       function install($pref, $force) 
+       {
+               global $db, $systypes_array;
+               
+               if (!$this->preconf)
+                       return false;
+
+               // Until 2.2 sanitizing text input with db_escape was not
+               // consequent enough. To avoid comparision problems we have to 
+               // fix this now.
+               sanitize_database($pref);
+
+               if ($this->beta)        // nothing more to be done on upgrade from 2.2beta
+                       return true;
+
+               // set item category dflt accounts to values from company GL setup
+               $prefs = get_company_prefs();
+               $sql = "UPDATE {$pref}stock_category SET "
+                       ."dflt_sales_act = '" . $prefs['default_inv_sales_act'] . "',"
+                       ."dflt_cogs_act = '". $prefs['default_cogs_act'] . "',"
+                       ."dflt_inventory_act = '" . $prefs['default_inventory_act'] . "',"
+                       ."dflt_adjustment_act = '" . $prefs['default_adj_act'] . "',"
+                       ."dflt_assembly_act = '" . $prefs['default_assembly_act']."'";
+               if (db_query($sql)==false) {
+                       display_error("Cannot update category default GL accounts"
+                       .':<br>'. db_error_msg($db));
+                       return false;
+               }
+               // add all references to refs table for easy searching via journal interface
+               foreach($systypes_array as $typeno => $typename) {
+                       $info = get_systype_db_info($typeno);
+                       if ($info == null || $info[3] == null) continue;
+                       $tbl = str_replace(TB_PREF, $pref, $info[0]);
+                       $sql = "SELECT DISTINCT {$info[2]} as id,{$info[3]} as ref FROM $tbl";
+                       if ($info[1])
+                               $sql .= " WHERE {$info[1]}=$typeno";
+                       $result = db_query($sql);
+                       if (db_num_rows($result)) {
+                               while ($row = db_fetch($result)) {
+                                       $res2 = db_query("INSERT INTO {$pref}refs VALUES("
+                                               . $row['id'].",".$typeno.",'".$row['ref']."')");
+                                       if (!$res2) {
+                                               display_error(_("Cannot copy references from $tbl")
+                                                       .':<br>'. db_error_msg($db));
+                                               return false;
+                                       }
+                               }
+                       }
+               }
+
+               if (!($ret = db_query("SELECT MAX(`order_no`) FROM `{$pref}sales_orders`")) ||
+                       !db_num_rows($ret))
+               {
+                               display_error(_('Cannot query max sales order number.'));
+                               return false;
+               } 
+               $row = db_fetch($ret);
+               $max_order = $row[0];
+               $next_ref = $max_order+1;
+               $sql = "UPDATE `{$pref}sys_types` 
+                       SET `type_no`='$max_order',`next_reference`='$next_ref'
+                       WHERE `type_id`=30";
+               if(!db_query($sql))
+               {
+                       display_error(_('Cannot store next sales order reference.'));
+                       return false;
+               }
+               return convert_roles($pref);
+       }
+       //
+       //      Checking before install
+       //
+       function pre_check($pref, $force)
+       {       
+               global $security_groups;
+               
+               if ($this->beta && !$force)
+                       $this->sql = 'alter2.2rc.sql';
+               // return ok when security groups still defined (upgrade from 2.1)
+               // or usersonline not defined (upgrade from 2.2 beta)
+               return isset($security_groups) || (check_table($pref, 'usersonline')!=0);
+       }
+       //
+       //      Test if patch was applied before.
+       //
+       function installed($pref) {
+               $n = 1; // number of patches to be installed
+               $patchcnt = 0;
+               if (!$this->beta) {
+                       $n = 16;
+                       if (check_table($pref, 'company', 'custom1_name')) $patchcnt++;
+                       if (!check_table($pref, 'company', 'profit_loss_year_act')) $patchcnt++;
+                       if (!check_table($pref, 'company', 'login_tout')) $patchcnt++;
+                       if (!check_table($pref, 'stock_category', 'dflt_no_sale')) $patchcnt++;
+                       if (!check_table($pref, 'users', 'sticky_doc_date')) $patchcnt++;
+                       if (!check_table($pref, 'users', 'startup_tab')) $patchcnt++;
+                       if (!check_table($pref, 'cust_branch', 'inactive')) $patchcnt++;
+                       if (!check_table($pref, 'chart_class', 'ctype')) $patchcnt++;
+                       if (!check_table($pref, 'audit_trail')) $patchcnt++;
+                       if (!check_table($pref, 'currencies', 'auto_update')) $patchcnt++;
+                       if (!check_table($pref, 'stock_master','no_sale')) $patchcnt++;
+                       if (!check_table($pref, 'suppliers', 'supp_ref')) $patchcnt++;
+                       if (!check_table($pref, 'users', 'role_id')) $patchcnt++;
+                       if (!check_table($pref, 'sales_orders', 'reference')) $patchcnt++;
+                       if (!check_table($pref, 'tags')) $patchcnt++;
+               } 
+               if (!check_table($pref, 'useronline')) $patchcnt++;
+
+               $n -= $patchcnt;
+               return $n == 0 ? true : $patchcnt;
+       }
+};
+
+/*
+       Conversion of old security roles stored into $security_groups table
+*/
+function convert_roles($pref) 
+{
+               global $security_groups, $security_headings, $security_areas, $path_to_root;
+               include_once($path_to_root."/includes/access_levels.inc");
+
+       $trans_sec = array(
+               1 => array('SA_CHGPASSWD', 'SA_SETUPDISPLAY', 'SA_BANKTRANSVIEW',
+                       'SA_ITEMSTRANSVIEW','SA_SUPPTRANSVIEW', 'SA_SALESORDER',
+                       'SA_SALESALLOC', 'SA_SALESTRANSVIEW'),
+               2 => array('SA_DIMTRANSVIEW', 'SA_STANDARDCOST', 'SA_ITEMSTRANSVIEW',
+                       'SA_ITEMSSTATVIEW', 'SA_SALESPRICE', 'SA_MANUFTRANSVIEW',
+                       'SA_WORKORDERANALYTIC', 'SA_WORKORDERCOST', 'SA_SUPPTRANSVIEW',
+                       'SA_SUPPLIERALLOC', 'SA_STEMPLATE', 'SA_SALESTRANSVIEW',
+                       'SA_SALESINVOICE', 'SA_SALESDELIVERY', 'SA_CUSTPAYMREP',
+                       'SA_CUSTBULKREP', 'SA_PRICEREP', 'SA_SALESBULKREP', 'SA_SALESMANREP',
+                       'SA_SALESBULKREP', 'SA_CUSTSTATREP', 'SA_SUPPLIERANALYTIC',
+                       'SA_SUPPPAYMREP', 'SA_SUPPBULKREP', 'SA_ITEMSVALREP', 'SA_ITEMSANALYTIC',
+                       'SA_BOMREP', 'SA_MANUFBULKREP', 'SA_DIMENSIONREP', 'SA_BANKREP', 'SA_GLREP',
+                       'SA_GLANALYTIC', 'SA_TAXREP', 'SA_SALESANALYTIC', 'SA_SALESQUOTE'),
+               3 => array('SA_GLACCOUNTGROUP', 'SA_GLACCOUNTCLASS','SA_PAYMENT', 
+                       'SA_DEPOSIT', 'SA_JOURNALENTRY', 'SA_INVENTORYMOVETYPE',
+                       'SA_LOCATIONTRANSFER', 'SA_INVENTORYADJUSTMENT', 'SA_WORKCENTRES',
+                       'SA_MANUFISSUE', 'SA_SUPPLIERALLOC', 'SA_CUSTOMER', 'SA_CRSTATUS',
+                       'SA_SALESMAN', 'SA_SALESAREA', 'SA_SALESALLOC', 'SA_SALESCREDITINV',
+                       'SA_SALESPAYMNT', 'SA_SALESCREDIT', 'SA_SALESGROUP', 'SA_SRECURRENT',
+                       'SA_TAXRATES', 'SA_ITEMTAXTYPE', 'SA_TAXGROUPS', 'SA_QUICKENTRY'),
+               4 => array('SA_REORDER', 'SA_PURCHASEPRICING', 'SA_PURCHASEORDER'),
+               5 => array('SA_VIEWPRINTTRANSACTION', 'SA_BANKTRANSFER', 'SA_SUPPLIER',
+                       'SA_SUPPLIERINVOICE', 'SA_SUPPLIERPAYMNT', 'SA_SUPPLIERCREDIT'),
+               8 => array('SA_ATTACHDOCUMENT', 'SA_RECONCILE', 'SA_GLANALYTIC',
+                       'SA_TAXREP', 'SA_BANKTRANSVIEW', 'SA_GLTRANSVIEW'),
+               9 => array('SA_FISCALYEARS', 'SA_CURRENCY', 'SA_EXCHANGERATE', 
+                       'SA_BOM'),
+               10 => array('SA_PAYTERMS', 'SA_GLSETUP', 'SA_SETUPCOMPANY',
+                       'SA_FORMSETUP', 'SA_DIMTRANSVIEW', 'SA_DIMENSION', 'SA_BANKACCOUNT',
+                       'SA_GLACCOUNT', 'SA_BUDGETENTRY', 'SA_MANUFRECEIVE',
+                       'SA_MANUFRELEASE', 'SA_WORKORDERENTRY', 'SA_MANUFTRANSVIEW',
+                       'SA_WORKORDERCOST'),
+               11 => array('SA_ITEMCATEGORY', 'SA_ITEM', 'SA_UOM', 'SA_INVENTORYLOCATION',
+                        'SA_GRN', 'SA_FORITEMCODE', 'SA_SALESKIT'),
+               14 => array('SA_SHIPPING', 'SA_VOIDTRANSACTION', 'SA_SALESTYPES'),
+               15 => array('SA_PRINTERS', 'SA_PRINTPROFILE', 'SA_BACKUP', 'SA_USERS',
+                       'SA_POSSETUP'),
+               20 => array('SA_CREATECOMPANY', 'SA_CREATELANGUAGE', 'SA_CREATEMODULES',
+                       'SA_SOFTWAREUPGRADE', 'SA_SECROLES', 'SA_DIMTAGS', 'SA_GLACCOUNTTAGS')
+               );
+               $new_ids = array();
+               foreach ($security_groups as $role_id => $areas) {
+                       $area_set = array();
+                       $sections = array();
+                       foreach ($areas as $a) {
+                        if (isset($trans_sec[$a]))
+                               foreach ($trans_sec[$a] as $id) {
+                                if ($security_areas[$id][0] != 0)
+//                                     error_log('invalid area id: '.$a.':'.$id);
+                                       $area_set[] = $security_areas[$id][0];
+                                       $sections[$security_areas[$id][0]&~0xff] = 1;
+                               }
+                       }
+                       $sections  = array_keys($sections);
+                       sort($sections); sort($area_set);
+                       import_security_role($pref, $security_headings[$role_id], $sections, $area_set);
+                       $new_ids[$role_id] = db_insert_id();
+               }
+               $result = get_users(true);
+               $users = array();
+               while($row = db_fetch($result)) { // complete old user ids and roles
+                       $users[$row['role_id']][] = $row['id'];
+               }
+               foreach($users as $old_id => $uids)
+                       foreach( $uids as $id) {
+                               $sql = "UPDATE {$pref}users set role_id=".$new_ids[$old_id].
+                                       " WHERE id=$id";
+                               $ret = db_query($sql, 'cannot update users roles');
+                               if(!$ret) return false;
+                       }
+               return true;
+}
+
+function import_security_role($pref, $name, $sections, $areas)
+{
+       $sql = "INSERT INTO {$pref}security_roles (role, description, sections, areas)
+       VALUES (".db_escape('FA 2.1 '.$name).",".db_escape($name).","
+       .db_escape(implode(';',$sections)).",".db_escape(implode(';',$areas)).")";
+
+       db_query($sql, "could not add new security role");
+}
+
+/*
+       Changes in extensions system.
+       This function is executed once on first Upgrade System display.
+*/
+function fix_extensions() {
+       global $path_to_root, $db_connections;
+
+       if (!file_exists($path_to_root.'/modules/installed_modules.php'))
+               return true; // already converted
+       
+       if (!is_writable($path_to_root.'/modules/installed_modules.php')) {
+               display_error(_('Cannot upgrade extensions system: file /modules/installed_modules.php is not writeable'));
+               return false;
+       }
+       
+       $exts = array();
+       include($path_to_root.'/installed_extensions.php');
+       foreach($installed_extensions as $ext) {
+               $ext['filename'] = $ext['app_file']; unset($ext['app_file']);
+               $ext['tab'] = $ext['name'];
+               $ext['name'] = access_string($ext['title'], true); 
+               $ext['path'] = $ext['folder']; unset($ext['folder']);
+               $ext['type'] = 'module';
+               $ext['active'] = '1';
+               $exts[] = $ext;
+       }
+
+       include($path_to_root.'/modules/installed_modules.php');
+       foreach($installed_modules as $mod) {
+               $mod['title'] = $mod['name'];
+               $mod['name'] = access_string($mod['name'], true);
+               $mod['type'] = 'plugin';
+               $ext['active'] = '1';
+               $exts[] = $mod;
+       }
+       if (!write_extensions($exts))
+               return false;
+       
+       $cnt = count($db_connections);
+       for ($i = 0; $i < $cnt; $i++)
+               write_extensions($exts, $i);
+
+       unlink($path_to_root.'/modules/installed_modules.php');
+       return true;
+}
+
+/*
+       Find and update all database records with special chars in text fields 
+       to ensure all of them are changed to html entites.
+*/
+function sanitize_database($pref, $test = false) {
+
+        if ($test)
+               error_log('Sanitizing database ...');
+
+        $tsql = "SHOW TABLES LIKE '".($pref=='' ? '' : substr($pref,0,-1).'\\_')."%'";
+        $tresult = db_query($tsql, "Cannot select all tables with prefix '$pref'");
+        while($tbl = db_fetch($tresult)) {
+               $table = $tbl[0];
+               $csql = "SHOW COLUMNS FROM $table";
+               $cresult = db_query($csql, "Cannot select column names for table '$table'");
+               $textcols = $keys = array();
+               while($col = db_fetch($cresult)) {
+                       if (strpos($col['Type'], 'char')!==false 
+                                       || strpos($col['Type'], 'text')!==false)
+                               $textcols[] = '`'.$col['Field'].'`';
+                       if ($col['Key'] == 'PRI') {
+                               $keys[] = '`'.$col['Field'].'`';
+                       }
+               }
+
+               if ($test)
+                       error_log("Table $table (".implode(',',$keys)."):(".implode(',',$textcols)."):");
+
+               if (!count($textcols)) continue;
+
+               // fetch all records containing special characters in text fields
+               $sql = "SELECT ".implode(',', array_unique(array_merge($keys,$textcols)))
+                       ." FROM {$table} WHERE 
+                       CONCAT(".implode(',', $textcols).") REGEXP '[\\'\"><&]'";
+               $result = db_query($sql, "Cannot select all suspicious fields in $table");
+
+               // and fix them
+               while($rec= db_fetch($result)) {
+                       $sql = "UPDATE {$table} SET ";
+                       $val = $key = array();
+                       foreach ($textcols as $f) {
+                               $val[] = $f.'='.db_escape($rec[substr($f,1,-1)]);
+                       }
+                       $sql .= implode(',', $val). ' WHERE ';
+                       foreach ($keys as $k) {
+                               $key[] = $k.'=\''.$rec[substr($k,1,-1)].'\'';
+                       }
+                       $sql .= implode( ' AND ', $key);
+                       if ($test)
+                               error_log("\t(".implode(',',$val).") updated");
+                       else
+                               db_query($sql, 'cannot update record');
+               }
+       }
+        if ($test)
+               error_log('Sanitizing done.');
+}
+
+$install = new fa2_2;
+?>
\ No newline at end of file
diff --git a/sql/alter2.2.sql b/sql/alter2.2.sql
new file mode 100644 (file)
index 0000000..a08cc39
--- /dev/null
@@ -0,0 +1,148 @@
+ALTER TABLE `0_company` DROP COLUMN `custom1_name`;
+ALTER TABLE `0_company` DROP COLUMN `custom2_name`;
+ALTER TABLE `0_company` DROP COLUMN `custom3_name`;
+ALTER TABLE `0_company` DROP COLUMN `custom1_value`;
+ALTER TABLE `0_company` DROP COLUMN `custom2_value`;
+ALTER TABLE `0_company` DROP COLUMN `custom3_value`;
+
+ALTER TABLE `0_company` ADD COLUMN `default_delivery_required` SMALLINT(6) NULL DEFAULT '1';
+ALTER TABLE `0_company` ADD COLUMN `version_id` VARCHAR(11) NOT NULL DEFAULT '';
+ALTER TABLE `0_company` DROP COLUMN `purch_exchange_diff_act`;
+ALTER TABLE `0_company` ADD COLUMN`profit_loss_year_act` VARCHAR(11) NOT NULL DEFAULT '' AFTER `exchange_diff_act`;
+ALTER TABLE `0_company` ADD COLUMN `time_zone` TINYINT(1) NOT NULL DEFAULT '0';
+ALTER TABLE `0_company` ADD COLUMN `add_pct` INT(5) NOT NULL DEFAULT '-1';
+ALTER TABLE `0_company` ADD COLUMN `round_to` INT(5) NOT NULL DEFAULT '1';
+ALTER TABLE `0_company` CHANGE `grn_act` `bank_charge_act` VARCHAR(11) NOT NULL DEFAULT '';
+#INSERT INTO `0_chart_master` VALUES ('9990', '', 'Profit and Loss this year', '52', '0');
+UPDATE `0_company` SET `profit_loss_year_act`='9990', `version_id`='2.2' WHERE `coy_code`=1; 
+
+ALTER TABLE `0_stock_category` DROP COLUMN `stock_act`;
+ALTER TABLE `0_stock_category` DROP COLUMN `cogs_act`;
+ALTER TABLE `0_stock_category` DROP COLUMN `adj_gl_act`;
+ALTER TABLE `0_stock_category` DROP COLUMN `purch_price_var_act`;
+
+ALTER TABLE `0_stock_category` ADD COLUMN `dflt_tax_type` int(11) NOT NULL default '1';
+ALTER TABLE `0_stock_category` ADD COLUMN `dflt_units` varchar(20) NOT NULL default 'each';
+ALTER TABLE `0_stock_category` ADD COLUMN `dflt_mb_flag` char(1) NOT NULL default 'B';
+ALTER TABLE `0_stock_category` ADD COLUMN `dflt_sales_act` varchar(11) NOT NULL default '';
+ALTER TABLE `0_stock_category` ADD COLUMN `dflt_cogs_act` varchar(11) NOT NULL default '';
+ALTER TABLE `0_stock_category` ADD COLUMN `dflt_inventory_act` varchar(11) NOT NULL default '';
+ALTER TABLE `0_stock_category` ADD COLUMN `dflt_adjustment_act` varchar(11) NOT NULL default '';
+ALTER TABLE `0_stock_category` ADD COLUMN `dflt_assembly_act` varchar(11) NOT NULL default '';
+ALTER TABLE `0_stock_category` ADD COLUMN `dflt_dim1` int(11) default NULL;
+ALTER TABLE `0_stock_category` ADD COLUMN `dflt_dim2` int(11) default NULL;
+ALTER TABLE `0_stock_category` ADD COLUMN `dflt_no_sale` tinyint(1) NOT NULL default '0';
+
+ALTER TABLE `0_users` ADD COLUMN `sticky_doc_date` TINYINT(1) DEFAULT '0';
+ALTER TABLE `0_users` ADD COLUMN `startup_tab` VARCHAR(20) NOT NULL default 'orders' AFTER `sticky_doc_date`;
+
+ALTER TABLE `0_debtors_master` MODIFY COLUMN `name` varchar(100) NOT NULL default '';
+
+ALTER TABLE `0_cust_branch` ADD COLUMN `inactive` tinyint(1) NOT NULL default '0';
+
+ALTER TABLE `0_sys_types` DROP COLUMN `type_name`;
+
+ALTER TABLE `0_chart_class` CHANGE `balance_sheet` `ctype` TINYINT(1) NOT NULL DEFAULT '0';
+
+ALTER TABLE `0_chart_class` ADD COLUMN `inactive` tinyint(1) NOT NULL default '0';
+ALTER TABLE `0_chart_types` ADD COLUMN `inactive` tinyint(1) NOT NULL default '0';
+ALTER TABLE `0_movement_types` ADD COLUMN `inactive` tinyint(1) NOT NULL default '0';
+ALTER TABLE `0_item_tax_types` ADD COLUMN `inactive` tinyint(1) NOT NULL default '0';
+ALTER TABLE `0_tax_types` ADD COLUMN `inactive` tinyint(1) NOT NULL default '0';
+ALTER TABLE `0_tax_groups` ADD COLUMN `inactive` tinyint(1) NOT NULL default '0';
+
+ALTER TABLE `0_users` DROP PRIMARY KEY;
+ALTER TABLE `0_users` ADD `id` SMALLINT(6) AUTO_INCREMENT PRIMARY KEY FIRST;
+ALTER TABLE `0_users` ADD UNIQUE KEY (`user_id`);
+ALTER TABLE `0_users` ADD COLUMN `inactive` tinyint(1) NOT NULL default '0';
+
+DROP TABLE IF EXISTS `0_audit_trail`;
+# fiscal_year, gl_date, gl_seq - journal sequence data
+CREATE TABLE `0_audit_trail` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `type` smallint(6) unsigned NOT NULL default '0',
+  `trans_no` int(11) unsigned NOT NULL default '0',
+  `user` smallint(6) unsigned NOT NULL default '0',
+  `stamp` timestamp NOT NULL default CURRENT_TIMESTAMP,
+  `description` varchar(60) default NULL,
+  `fiscal_year` int(11) NOT NULL,
+  `gl_date` date NOT NULL default '0000-00-00',
+  `gl_seq` int(11) unsigned default NULL,
+   PRIMARY KEY (`id`),
+  KEY (`fiscal_year`, `gl_seq`)
+) TYPE=InnoDB  ;
+
+ALTER TABLE `0_stock_master` ADD COLUMN `no_sale` tinyint(1) NOT NULL default '0';
+ALTER TABLE `0_currencies` ADD COLUMN `auto_update` tinyint(1) NOT NULL default '1';
+
+ALTER TABLE `0_debtors_master` ADD COLUMN `debtor_ref` varchar(30) NOT NULL;
+UPDATE `0_debtors_master` SET `debtor_ref`=`name` WHERE 1; 
+ALTER TABLE `0_suppliers` ADD COLUMN `supp_ref` varchar(30) NOT NULL;
+UPDATE `0_suppliers` SET `supp_ref`=`supp_name` WHERE 1; 
+ALTER TABLE `0_cust_branch` ADD COLUMN `branch_ref`    varchar(30) NOT NULL;
+UPDATE `0_cust_branch` SET `branch_ref`=`br_name` WHERE 1; 
+
+DROP TABLE IF EXISTS `0_security_roles`;
+
+CREATE TABLE `0_security_roles` (
+  `id` int(11) NOT NULL auto_increment,
+  `role` varchar(30) NOT NULL,
+  `description` varchar(50) default NULL,
+  `sections` text,
+  `areas` text,
+  `inactive` tinyint(1) NOT NULL default '0',
+  PRIMARY KEY  (`id`),
+  UNIQUE KEY `role` (`role`)
+) TYPE=MyISAM AUTO_INCREMENT=1;
+
+ALTER TABLE `0_company` ADD COLUMN `login_tout` SMALLINT(6) NOT NULL DEFAULT '600';
+ALTER TABLE `0_users` CHANGE COLUMN `full_access` `role_id` int(11) NOT NULL default '1';
+
+ALTER TABLE `0_sales_order_details` ADD COLUMN `trans_type` SMALLINT(6) NOT NULL DEFAULT '30' AFTER `order_no`;
+ALTER TABLE `0_sales_orders` CHANGE COLUMN `order_no` `order_no` int(11) NOT NULL;
+ALTER TABLE `0_sales_orders` ADD COLUMN `trans_type` SMALLINT(6) NOT NULL DEFAULT '30' AFTER `order_no`;
+ALTER TABLE `0_sales_orders` ADD COLUMN `reference` varchar(100) NOT NULL DEFAULT '' AFTER `branch_code`;
+ALTER TABLE `0_sales_orders` DROP PRIMARY KEY;
+ALTER TABLE `0_sales_orders` ADD PRIMARY KEY ( `trans_type` , `order_no` ); 
+UPDATE `0_sales_orders`        SET `reference`=`order_no` WHERE 1;
+INSERT INTO `0_sys_types` (`type_id`, `type_no`, `next_reference`) VALUES (32, 0, '1');
+
+ALTER TABLE `0_bank_accounts` ADD COLUMN `dflt_curr_act` TINYINT(1) NOT NULL default '0' AFTER `bank_curr_code`;
+
+DROP TABLE IF EXISTS `0_tags`;
+
+CREATE TABLE `0_tags` (
+  `id` int(11) NOT NULL auto_increment,
+  `type` smallint(6) NOT NULL,
+  `name` varchar(30) NOT NULL,
+  `description` varchar(60) default NULL,
+  `inactive` tinyint(1) NOT NULL default '0',
+  PRIMARY KEY  (`id`),
+  UNIQUE KEY(`type`,`name`)
+) TYPE=MyISAM AUTO_INCREMENT=1;
+
+DROP TABLE IF EXISTS `0_tag_associations`;
+
+CREATE TABLE `0_tag_associations` (
+  `record_id` varchar(11) NOT NULL,
+  `tag_id` int(11) NOT NULL,
+  UNIQUE KEY(`record_id`,`tag_id`)
+) TYPE=MyISAM;
+
+DROP TABLE IF EXISTS `0_useronline` ;
+
+CREATE TABLE `0_useronline` (
+       `id` int(11) NOT NULL AUTO_INCREMENT ,
+       `timestamp` int(15) NOT NULL default '0',
+       `ip` varchar(40) NOT NULL default '',
+       `file` varchar(100) NOT NULL default '',
+       PRIMARY KEY `id` (`id`) ,
+       KEY (`timestamp`) 
+) TYPE=MYISAM AUTO_INCREMENT=1;
+
+ALTER TABLE `0_suppliers` ADD COLUMN `phone2` varchar(30) NOT NULL default '' AFTER `phone`;
+ALTER TABLE `0_cust_branch` ADD COLUMN `phone2` varchar(30) NOT NULL default '' AFTER `phone`;
+ALTER TABLE `0_shippers` ADD COLUMN `phone2` varchar(30) NOT NULL default '' AFTER `phone`;
+ALTER TABLE `0_locations` ADD COLUMN `phone2` varchar(30) NOT NULL default '' AFTER `phone`;
+ALTER TABLE `0_debtors_master` ADD COLUMN `notes` tinytext NULL default '' AFTER `credit_limit`;
+ALTER TABLE `0_cust_branch` ADD COLUMN `notes` tinytext NULL default '' AFTER `group_no`;
diff --git a/sql/alter2.2rc.sql b/sql/alter2.2rc.sql
new file mode 100644 (file)
index 0000000..aff4c0a
--- /dev/null
@@ -0,0 +1,15 @@
+# Patch for upgrade from 2.2beta to 2.2RC/final
+
+ALTER TABLE `0_tag_associations` DROP COLUMN `id`;
+ALTER TABLE `0_tag_associations` ADD  UNIQUE KEY(`record_id`,`tag_id`);
+
+DROP TABLE IF EXISTS `0_useronline` ;
+
+CREATE TABLE `0_useronline` (
+       `id` int(11) NOT NULL AUTO_INCREMENT ,
+       `timestamp` int(15) NOT NULL default '0',
+       `ip` varchar(40) NOT NULL default '',
+       `file` varchar(100) NOT NULL default '',
+       PRIMARY KEY `id` (`id`) ,
+       KEY (`timestamp`) 
+) TYPE=MYISAM AUTO_INCREMENT=1;
diff --git a/themes/aqua/images/escape.png b/themes/aqua/images/escape.png
new file mode 100644 (file)
index 0000000..4d84554
Binary files /dev/null and b/themes/aqua/images/escape.png differ
diff --git a/themes/cool/images/escape.png b/themes/cool/images/escape.png
new file mode 100644 (file)
index 0000000..4d84554
Binary files /dev/null and b/themes/cool/images/escape.png differ