Additional dev related exclusions in gitignore, small cleanups in old upgrade classes.
[fa-stable.git] / sql / alter2.4.php
1 <?php
2 /**********************************************************************
3     Copyright (C) FrontAccounting, LLC.
4         Released under the terms of the GNU General Public License, GPL, 
5         as published by the Free Software Foundation, either version 3 
6         of the License, or (at your option) any later version.
7     This program is distributed in the hope that it will be useful,
8     but WITHOUT ANY WARRANTY; without even the implied warranty of
9     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
10     See the License here <http://www.gnu.org/licenses/gpl-3.0.html>.
11 ***********************************************************************/
12
13 class fa2_4 extends fa_patch {
14         var $previous = '2.3rc';                // applicable database version
15         var $version = '2.4.1'; // version installed
16         var $description;
17         var $sql = 'alter2.4.sql';
18         var     $max_upgrade_time = 900;        // table recoding is really long process
19         
20         function __construct() {
21                 parent::__construct();
22                 $this->description = _('Upgrade from version 2.3 to 2.4');
23         }
24         
25     /*
26             Shows parameters to be selected before upgrade (if any)
27         */
28     function show_params($comp)
29         {
30           display_note(_('Set optimal parameters and start upgrade:'));
31           start_table(TABLESTYLE);
32           start_row();
33                 collations_list_row(_('Text collation optimization:'), 'collation', substr($_SESSION['language']->code, 0, 2));
34           end_row();
35           end_table();
36           br();
37     }
38
39         /*
40             Fetches selected upgrade parameters.
41     */
42         function prepare()
43     {
44                 $this->collation = get_post('collation');
45                 return true;
46         }
47
48         //
49         //      Install procedure. All additional changes 
50         //      not included in sql file should go here.
51         //
52         function install($company, $force=false)
53         {
54                 global $db_version, $db_connections;
55
56                 $pref = $db_connections[$company]['tbpref'];
57
58                 if (get_company_pref('grn_clearing_act') === null) { // available form 2.3.1, can be not defined on pre-2.4 installations
59                         set_company_pref('grn_clearing_act', 'glsetup.purchase', 'varchar', 15, 0);
60                 }
61                 if (get_company_pref('default_quote_valid_days') === null) { // new in 2.3.23 installations
62                         set_company_pref('default_quote_valid_days', 'glsetup.sales', 'smallint', 6, 30);
63                 }
64                 if (get_company_pref('bcc_email') === null) { // available from 2.3.14, can be not defined on pre-2.4 installations
65                         set_company_pref('bcc_email', 'setup.company', 'varchar', 100, '');
66                 }
67                 if (get_company_pref('alternative_tax_include_on_docs') === null) { // available from 2.3.14, can be not defined on pre-2.4 installations
68                         set_company_pref('alternative_tax_include_on_docs', 'setup.company', 'tinyint', 1, '0');
69                 }
70                 if (get_company_pref('suppress_tax_rates') === null) { // available from 2.3.14, can be not defined on pre-2.4 installations
71                         set_company_pref('suppress_tax_rates', 'setup.company', 'tinyint', 1, '0');
72                 }
73                 if (get_company_pref('company_logo_report') === null) { // available from 2.4.2, during updates
74                         set_company_pref('company_logo_report', 'setup.company', 'tinyint', 1, '0');
75                 }
76                 if (get_company_pref('print_dialog_direct') === null) { // available from 2.4.5, during updates
77                         set_company_pref('print_dialog_direct', 'setup.company', 'tinyint', 1, '0');
78                 }
79                 if (get_company_pref('barcodes_on_stock') === null) { // available from 2.4.3, during updates
80                         set_company_pref('barcodes_on_stock', 'setup.company', 'tinyint', 1, '0');
81                 }
82                 if (get_company_pref('ref_no_auto_increase') === null) { // available from 2.4.4, during updates
83                         set_company_pref('ref_no_auto_increase', 'setup.company', 'tinyint', 1, '0');
84                 }
85                 if (get_company_pref('shortname_name_in_list') === null) { // available from 2.4.2, during updates
86                         set_company_pref('shortname_name_in_list', 'setup.company', 'tinyint', 1, '0');
87                 }
88
89                 $result = $this->update_workorders()  && $this->update_grn_rates() && $this->switch_database_to_utf($pref);
90
91                 if ($result)
92                         $result = $this->do_cleanup();
93                 if ($result)
94                 {
95                         $db_connections[$company]['collation'] = $this->collation;
96                         if (write_config_db())
97                                 return $this->log_error(_("Cannot update config_db.php file."));
98                 }
99
100                 $sec_updates = array(
101                         'SA_SETUPCOMPANY' => array(
102                                 'SA_ASSET', 'SA_ASSETCATEGORY', 'SA_ASSETCLASS',
103                                 'SA_ASSETSTRANSVIEW','SA_ASSETTRANSFER', 'SA_ASSETDISPOSAL',
104                                 'SA_DEPRECIATION', 'SA_ASSETSANALYTIC'),
105                 );
106                 $result = $this->update_security_roles($sec_updates);
107
108                 return $result;
109         }
110
111         //
112         // optional procedure done after upgrade fail, before backup is restored
113         //
114         function post_fail($company)
115         {
116                 $pref = $this->companies[$company]['tbpref'];
117                 db_query("DROP TABLE IF EXISTS " . $pref . 'wo_costing');
118                 db_query("DROP TABLE IF EXISTS " . $pref . 'stock_fa_class');
119                 db_query("DELETE FROM ".$pref."sys_prefs
120                         WHERE `name` in (
121                                 'gl_closing_date', 'deferred_income_act', 'no_zero_lines_amount', 'accounts_alpha',
122                                 'tax_algorithm', 'grn_clearing_act', 'default_receival_required',
123                                 'default_quote_valid_days',     'no_zero_lines_amount', 'show_po_item_codes', 'accounts_alpha',
124                                 'loc_notification', 'print_invoice_no', 'allow_negative_prices', 'print_item_images_on_quote',
125                                 'bcc_email', 'alternative_tax_include_on_docs', 'suppress_tax_rates', 'company_logo_report',
126                                 'barcodes_on_stock', print_dialog_direct', 'ref_no_auto_increase')");
127         }
128
129         function update_workorders()
130         {
131                 global $db;
132
133                 $sql = "SELECT DISTINCT type, type_no, tran_date, person_id FROM ".TB_PREF."gl_trans WHERE `type`=".ST_WORKORDER
134                         ." AND person_type_id=1";
135                 $res = db_query($sql);
136                 if (!$res)
137                         return $this->log_error(sprintf(_("Cannot update work orders costs:\n%s"), db_error_msg($db)));
138
139                 while ($row = db_fetch($res))
140                 {
141                         $journal_id = get_next_trans_no(ST_JOURNAL);
142
143                         $sql1 = "UPDATE ".TB_PREF."gl_trans SET `type`=".ST_JOURNAL.", type_no={$journal_id},
144                                 person_type_id=NULL, person_id=0
145                                 WHERE `type`=".ST_WORKORDER." AND type_no={$row['type_no']} AND tran_date='{$row['tran_date']}'
146                                 AND person_id='{$row['person_id']}'";
147                         if (!db_query($sql1)) return false;
148
149                         $sql2 = "INSERT INTO ".TB_PREF."wo_costing (workorder_id, cost_type, trans_no) 
150                                 VALUES ({$row['type_no']}, {$row['person_id']}, {$journal_id})";
151                         if (!db_query($sql2)) return false;
152                 }
153                 return true;
154         }
155
156 /*
157         In previous versions FA ignored encoding settings on database/tables, so it depended on server settings,
158         but data stored is encoded in user language encoding. Now we switch to utf8 internal database encoding, while
159         user encoding can be selected independently.
160
161         To perform safe FA database switch to utf-8 encoding we have to first ensure that all text/char columns 
162         have properly set encoding (the same as its content), so the algorithm performed on every table is as follows:
163         . set default table encoding for the table to currently used on client side;
164         . for all text/char column:
165          - suppress autorecoding by change of the type to related binary/blob type
166          - change column to utf8 encodding and selected collation.
167         . change default table encoding to utf8 and selected collation
168 */
169         function switch_database_to_utf($pref, $dbg = false) {
170
171                 global $installed_languages, $dflt_lang;
172
173                 $old_encoding = 'latin1'; // default client encoding
174
175                 // uncomment in case of 1071 errors (requires SUPER privileges)
176                 // db_query("SET @@global.innodb_large_prefix=1", "Cannot set large prefix");
177
178                  // site default encoding is presumed as encoding for all databases!
179                 $lang = array_search_value($dflt_lang, $installed_languages, 'code');
180                 $new_encoding = get_mysql_encoding_name(strtoupper($lang['encoding']));
181
182                 $this->log_error(sprintf('Switching database to utf8 encoding from %s', $old_encoding), 'Info');
183                 $collation = get_mysql_collation($this->collation);
184                 $tsql = "SHOW TABLES LIKE '".($pref=='' ? '' : substr($pref, 0, -1).'\\_')."%'";
185                 $tresult = db_query($tsql, "Cannot select all tables with prefix '$pref'");
186                 while($tbl = db_fetch($tresult)) {
187                         $table = $tbl[0];
188
189                         db_query("ALTER TABLE `$table` CONVERT TO CHARACTER SET $old_encoding"); // convert encoding on utf-8 tables
190
191                         // set proper default table encoding for current user language (used on binary->text conversion)
192                         db_query("ALTER TABLE `$table` CHARSET $new_encoding");
193                         $csql = "SHOW COLUMNS FROM $table";
194                         $cresult = db_query($csql, "Cannot select column names for table '$table'");
195                         $convert = false;
196
197                         $to_binary = $to_default = $to_utf = array();
198                         while($col = db_fetch($cresult)) {
199
200                                 $bintype = strtr($col['Type'], array('varchar' => 'varbinary', 'char'=>'varbinary', 'text'=>'blob', 'tinytext'=>'tinyblob'));
201
202                                 if ($bintype != $col['Type'])
203                                 { // this is char/text column, so change encoding to proper encoding
204                                         if ($dbg)
205                                                 $this->log_error(sprintf('%s switched to uft8.', $table.'.'.$col['Field']), 'Debug');
206
207                                         $null = $col['Null'] === 'YES' ? ' NULL ' : ' NOT NULL ';
208                                         $default = $col['Null'] !== 'YES' && isset($col['Default']) ? ' DEFAULT '.db_escape($col['Default']) : '';
209
210                                         // to avoid column width multiplication x3 we old->binary->ui->utf column type change instead of column CONVERT
211
212                                         $to_binary[] = "CHANGE `".$col['Field']."` `".$col['Field']."` ".$bintype;
213                                         $to_default[] = "CHANGE `".$col['Field']."` `".$col['Field']."` ".$col['Type'].$null.$default;
214                                         $to_utf[] = "MODIFY COLUMN `".$col['Field']."` ".$col['Type']." COLLATE ".$collation.$null.$default;
215                                         $convert = true;
216                                 }
217                         }
218                         if(count($to_binary))
219                         {
220                                 $sql = "ALTER TABLE `$table` ".implode(',',$to_binary);
221                                 db_query($sql);
222                                 $sql = "ALTER TABLE `$table` ".implode(',',$to_default);
223                                 db_query($sql);
224                                 $sql = "ALTER TABLE `$table` ".implode(',',$to_utf);
225                                 db_query($sql);
226                         }
227                         db_query("ALTER TABLE `$table` COLLATE $collation");
228                 }
229                 db_query("ALTER DATABASE COLLATE $collation");
230                 $this->log_error(_('Convertion to utf8 done.'), 'Info');
231
232                 return true;
233         }
234
235         function update_grn_rates()
236         {
237                 $sql = "SELECT grn.id, grn.delivery_date, supp.curr_code 
238                         FROM ".TB_PREF."grn_batch grn, ".TB_PREF."suppliers supp
239                         WHERE supp.supplier_id=grn.supplier_id AND supp.curr_code!='".get_company_pref('curr_default')."'";
240                 $result = db_query($sql);
241
242                 if (!$result)
243                         return false;
244
245                 $sql = "UPDATE ".TB_PREF."grn_batch SET rate=%s WHERE id=%d";
246                 while ($grn = db_fetch($result))
247                         db_query(sprintf($sql, get_exchange_rate_from_home_currency($grn['curr_code'], sql2date($grn['delivery_date'])), $grn['id']));
248
249                 return true;
250         }
251
252         function do_cleanup()
253         {
254                 global $db;
255
256                 //remove obsolete and temporary columns.
257                 // this have to be done here as db_import rearranges alter query order
258                 $dropcol = array(
259                                 'tax_groups' => array('tax_shipping'),
260                                 'tax_group_items' => array('rate'),
261                                 'budget_trans' => array('type', 'type_no', 'person_id', 'person_type_id', 'memo_'),
262                                 'cust_branch' => array('contact_name', 'disable_trans'),
263                                 'stock_moves' => array('discount_percent', 'visible', 'person_id'),
264                 );
265
266                 foreach($dropcol as $table => $columns)
267                         foreach($columns as $col) {
268                                 if (db_query("ALTER TABLE `".TB_PREF."{$table}` DROP `$col`") == false) {
269                                         return $this->log_error(sprintf(_("Cannot drop column in %s table: %s"), $table, db_error_msg($db)));
270                                 }
271                         }
272                 return true;
273   }
274 }
275
276 $install = new fa2_4;