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