source: contrib/ProjectManager/inc/class.bopricelist.inc.php @ 3594

Revision 3594, 12.7 KB checked in by afernandes, 13 years ago (diff)

Ticket #1416 - Disponibilizado o módulo ProjectManager? para a comunidade

  • Property svn:executable set to *
Line 
1<?php
2/**
3 * ProjectManager - Pricelist buisness object
4 *
5 * @link http://www.egroupware.org
6 * @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
7 * @package projectmanager
8 * @copyright (c) 2005 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
9 * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
10 * @version $Id: class.bopricelist.inc.php 23379 2007-02-05 18:50:53Z nelius_weiss $
11 */
12
13include_once(PHPGW_INCLUDE_ROOT.'/projectmanager/inc/class.sopricelist.inc.php');
14
15/**
16 * Pricelist buisness object of the projectmanager
17 */
18class bopricelist extends sopricelist
19{
20        /**
21         * @var array $timestamps timestaps that need to be adjusted to user-time on reading or saving
22         */
23        var $timestamps = array(
24                'pl_modified','pl_validsince',
25        );
26        /**
27         * @var int $tz_offset_s offset in secconds between user and server-time,
28         *      it need to be add to a server-time to get the user-time or substracted from a user-time to get the server-time
29         */
30        var $tz_offset_s;
31        /**
32         * @var int $now actual USER time
33         */
34        var $now;
35
36        /**
37         * Constructor, calls the constructor of the extended class
38         *
39         * @param int $pm_id=0 pm_id of the project to use, default 0 (project independent / standard prices)
40         */
41        function bopricelist($pm_id=0)
42        {
43                $this->sopricelist($pm_id);
44
45                if (!is_object($GLOBALS['phpgw']->datetime))
46                {
47                        $GLOBALS['phpgw']->datetime =& CreateObject('phpgwapi.datetime');
48                }
49                $this->tz_offset_s = $GLOBALS['phpgw']->datetime->tz_offset;
50                $this->now = time() + $this->tz_offset_s;
51               
52                if (!is_object($GLOBALS['boprojectmanager']))
53                {
54                        CreateObject('projectmanager.boprojectmanager',$pm_id);
55                }
56                $this->project =& $GLOBALS['boprojectmanager'];
57        }
58       
59        /**
60         * saves the content of data to the db, also checks acl and deletes not longer set prices!
61         *
62         * @param array $keys=null if given $keys are copied to data before saveing => allows a save as
63         * @return int/boolean 0 on success, true on missing acl-rights and errno != 0 else
64         */
65        function save($keys=null)
66        {
67                if (is_array($keys) && count($keys)) $this->data_merge($keys);
68               
69                if ((int)$this->debug >= 2)
70                {
71                        echo "<p>sopricelist::save(".print_r($keys,true).") data=";
72                        _debug_array($this->data);
73                }
74                if ($this->data['pl_id'])
75                {
76                        $backup =& $this->data;         // would get overwritten by read
77                        unset($this->data);
78                        $old = $this->read(array(
79                                'pl_id' => $backup['pl_id'],
80                                'pm_id' => $backup['pm_id'] ? array($backup['pm_id'],0) : 0
81                        ));
82                        $this->data =& $backup;
83                        unset($backup);
84                }
85                $need_general = count($old['prices']) > 0 || count($this->data['prices']) > 0;
86                if (!($pricelist_need_save = !$this->data['pl_id']))
87                {
88                        $this->data['cat_id'] = (int) $this->data['cat_id'];
89                        foreach($this->db_cols as $col => $data)
90                        {
91                                if (!$old || $this->data[$col] != $old[$col])
92                                {
93                                        $pricelist_need_save = true;
94                                        break;
95                                }
96                        }
97                }
98                if ($pricelist_need_save)
99                {
100                        // check acl
101                        if (!$this->check_acl(PHPGW_ACL_EDIT,$need_general ? 0 : $this->data['pm_id']))
102                        {
103                                return lang('permission denied !!!').' need_general='.(int)$need_general;
104                        }
105                        if (($err = parent::save($this->data)))
106                        {
107                                return $err;
108                        }
109                }
110                $prices = array();
111                foreach($this->data['prices'] as $key => $nul)
112                {
113                        $price =& $this->data['prices'][$key];
114                        $price['pm_id'] = 0;
115                        $price['pl_billable'] = $price['pl_customertitle'] = null;
116                        if (count($this->data['prices']) == 1) $price['pl_validsince'] = 0;     // no date for first price
117                        $prices[] =& $price;
118                }
119                foreach($this->data['project_prices'] as $key => $nul)
120                {
121                        $price =& $this->data['project_prices'][$key];
122                        foreach(array('pm_id','pl_billable','pl_customertitle') as $key)
123                        {
124                                if (!isset($price[$key])) $price[$key] = $this->data[$key];
125                        }
126                        if (count($this->data['project_prices']) == 1) $price['pl_validsince'] = 0;     // no date for first price
127                        $prices[] =& $price;
128                }
129               
130                // index prices in old by pm_id and date (!) of validsince
131                $old_prices = array();
132                if ($old)
133                {
134                        foreach(array_merge($old['prices'],$old['project_prices']) as $old_price)
135                        {
136                                $old_prices[(int)$old_price['pm_id']][date('Y-m-d',(int)$old_price['pl_validsince'])] = $old_price;
137                        }
138                }
139                foreach($prices as $key => $nul)
140                {
141                        $price =& $prices[$key];
142                        if (!isset($price['pl_id'])) $price['pl_id'] = $this->data['pl_id'];
143                        $old_price = $old_prices[(int)$price['pm_id']][date('Y-m-d',(int)$price['pl_validsince'])];
144                        if (!$this->prices_equal($price,$old_price))
145                        {
146                                // price needs saving, checking acl now
147                                if (!$this->check_acl(PHPGW_ACL_EDIT,$price['pm_id']))
148                                {
149                                        return lang('permission denied !!!').' check_acl(PHPGW_ACL_EDIT(pm_id='.(int)$price[pm_id].')';
150                                }
151                                // maintain time of old price, to not create doublets with different times by users operating in different TZ's
152                                if ($old_price) $price['pl_validsince'] = $old_price['pl_validsince'];
153
154                                if (($err = parent::save_price($price)))
155                                {
156                                        return $err;
157                                }
158                        }
159                        unset($old_prices[(int)$price['pm_id']][date('Y-m-d',(int)$old_price['pl_validsince'])]);
160                }
161                // check if there are old prices not longer set ==> delete them
162                foreach($old_prices as $pm_id => $prices)
163                {
164                        foreach($prices as $price)
165                        {
166                                if (!$this->check_acl(PHPGW_ACL_DELETE,$price['pm_id']))
167                                {
168                                        return lang('permission denied !!!').' check_acl(PHPGW_ACL_DELETE(pm_id='.(int)$price[pm_id].')';
169                                }
170                                if (!parent::delete($price))
171                                {
172                                        return lang('Error: deleting price !!!');
173                                }
174                        }
175                }
176                return 0;
177        }
178       
179        /**
180         * search elements, reimplemented to use $this->pm_id, if no pm_id given in criteria or filter and join with the prices table
181         *
182         * @param array/string $criteria array of key and data cols, OR a SQL query (content for WHERE), fully quoted (!)
183         * @param boolean $only_keys True returns only keys, False returns all cols
184         * @param string $order_by fieldnames + {ASC|DESC} separated by colons ','
185         * @param string/array $extra_cols string or array of strings to be added to the SELECT, eg. "count(*) as num"
186         * @param string $wildcard appended befor and after each criteria
187         * @param boolean $empty False=empty criteria are ignored in query, True=empty have to be empty in row
188         * @param string $op defaults to 'AND', can be set to 'OR' too, then criteria's are OR'ed together
189         * @param int/boolean $start if != false, return only maxmatch rows begining with start
190         * @param array $filter if set (!=null) col-data pairs, to be and-ed (!) into the query without wildcards
191         * @param string/boolean $join=true default join with prices-table or string as in so_sql
192         * @return array of matching rows (the row is an array of the cols) or False
193         */
194        function search($criteria,$only_keys=false,$order_by='',$extra_cols='',$wildcard='',$empty=False,$op='AND',$start=false,$filter=null,$join=true)
195        {
196                if (!$this->check_acl(PHPGW_ACL_READ,(int)($criteria['pm_id'] ? $criteria['pm_id'] : $this->pm_id)))
197                {
198                        return false;
199                }
200                return parent::search($criteria,$only_keys,$order_by,$extra_cols,$wildcard,$empty,$op,$start,$filter,$join);
201        }
202       
203        /**
204         * return priceslist of a given project (only bookable or billable price, no general pricelist)
205         *
206         * @param int $pm_id project
207         * @return array/boolean array with pl_id => pl_unit: pl_tiltle (pl_price) pairs or false on error (eg. no ACL)
208         */
209        function pricelist($pm_id)
210        {
211                //echo "<p>bopricelist::pricelist($pm_id)</p>\n";
212                if (!($prices =& $this->search(array('pm_id' => $pm_id))))
213                {
214                        return false;
215                }
216                $options = array();
217                foreach($prices as $price)
218                {
219                        $options[$price['pl_id']] = $price['pl_unit'].' '.$price['pl_title'].
220                                ($price['pl_customertitle'] ? ': '.$price['pl_customertitle'] : '').
221                                ' ('.$price['pl_price'].')';
222                }
223                return $options;
224        }
225
226        /**
227         * reads one pricelist-itme specified by $keys, reimplemented to use $this->pm_id, if no pm_id given
228         *
229         * @param array $keys array with keys in form internalName => value, may be a scalar value if only one key
230         * @param string/array $extra_cols string or array of strings to be added to the SELECT, eg. "count(*) as num"
231         * @param string/boolean $join=true default join with links-table or string as in so_sql
232         * @return array/boolean data if row could be retrived else False
233        */
234        function read($keys,$extra_cols='',$join=true)
235        {
236                // check if we have the requested access to all given pricelists
237                foreach(!is_array($keys) || !isset($keys['pm_id']) ? array($this->pm_id) :
238                        (is_array($keys['pm_id']) ? $keys['pm_id'] : array($keys['pm_id'])) as $pm_id)
239                {
240                        if (!$this->check_acl(PHPGW_ACL_READ,(int)$pm_id)) return false;
241                }
242                return parent::read($keys,$extra_cols,$join);
243        }
244
245        /**
246         * delete pricelist-entries and price(s) specified by keys pl_id, pm_id and/or pl_validsince
247         *
248         * If the last price of a pricelist-entry gets deleted, the pricelist entry is automatic deleted too!
249         *
250         * @param array/int $keys array with keys pm_id, pl_id and/or pl_validsince to delete or integer pm_id
251         * @return int/boolean number of deleted prices or false if permission denied
252         */
253        function delete($keys)
254        {
255                if (!$this->check_acl(PHPGW_ACL_EDIT,(int)(is_array($keys) ? $keys['pm_id'] : $keys)))
256                {
257                        return false;
258                }
259                return parent::delete($keys);
260        }
261
262        /**
263         * checks if the user has sufficent rights for a certain action
264         *
265         * For project-spez. prices/data you need a PHPGW_ACL_BUDGET right of the project for read or
266         * PHPGW_ACL_EDIT_BUDGET for write or delete.
267         * For general pricelist data you need atm. no extra read rights, but is_admin to write/delete.
268         *
269         * @param int $required PHPGW_ACL_{READ|WRITE|DELETE}
270         * @param int $pm_id=0 project-id for project-spez. prices/data to check, default 0 = general pricelist
271         * @param array/int $data=null data/id of pricelist-entry, default null = use $this->data ($pm_id is ignored)
272         * @return boolean true if the user has the rights, false otherwise
273         */
274        function check_acl($required,$pm_id=0,$data=null)
275        {
276/*              not used atm.
277                if (is_null($data))
278                {
279                        $data =& $this->data;
280                }
281                elseif (!is_array($data))
282                {
283                        if ((int) $data)
284                        {
285                                $backup = $this->data;
286                                $data = $this->read(array('pm_id'=>(int)$pm_id,'pl_id' => (int)$data));
287                                $this->data = $backup;
288                        }
289                        else
290                        {
291                                return false;
292                        }
293                }
294*/
295                if (!$pm_id)
296                {
297                        return $required == PHPGW_ACL_READ || $this->project->is_admin;
298                }
299                return $this->project->check_acl($required == PHPGW_ACL_READ ? PHPGW_ACL_BUDGET : PHPGW_ACL_EDIT_BUDGET,$pm_id);
300        }
301
302        /**
303         * Compares two prices to check if they are equal
304         *
305         * The compared fields depend on the price being project-specific or not
306         *
307         * @param array $price
308         * @param array $price2
309         * @return boolean true if the two prices are identical, false otherwise or if they are no arrays!
310         */
311        function prices_equal($price,$price2)
312        {
313                if (!is_array($price) || !is_array($price2)) return false;
314               
315                $to_compare = array('pl_id','pm_id','pl_price','pl_validsince','pl_modified','pl_modifier');
316               
317                if ($price['pm_id'])
318                {
319                        $to_compare[] = 'pl_customertitle';
320                        $to_compare[] = 'pl_billable';
321                }
322                $equal = true;
323                foreach($to_compare as $key)
324                {
325                        switch($key)
326                        {
327                                case 'pm_id':
328                                        $equal = (int) $price['pm_id'] == (int) $price2['pm_id'];
329                                        break;
330                                case 'pl_validsince':
331                                        $equal = date('Y-m-d',(int)$price['pl_validsince']) == date('Y-m-d',(int)$price2['pl_validsince']);
332                                        break;
333                                default:
334                                        $equal = $price[$key] == $price2[$key];
335                                        break;
336                        }
337                        if (!$equal) break;
338                }
339                if ((int)$this->debug >= 3) echo "<p>bopricelist::prices_equal(".print_r($price,true).','.print_r($price2,true).') = '.($equal ? 'true' : "differ in $key: {$price[$key]} != {$price2[$key]}")."</p>\n";
340
341                return $equal;
342        }
343
344        /**
345         * changes the data from the db-format to your work-format
346         *
347         * reimplemented to adjust the timezone of the timestamps (adding $this->tz_offset_s to get user-time)
348         * Please note, we do NOT call the method of the parent or so_sql !!!
349         *
350         * @param array $data if given works on that array and returns result, else works on internal data-array
351         * @return array with changed data
352         */
353        function db2data($data=null)
354        {
355                if (!is_array($data))
356                {
357                        $data = &$this->data;
358                }
359                foreach($this->timestamps as $name)
360                {
361                        if (isset($data[$name]) && $data[$name]) $data[$name] += $this->tz_offset_s;
362                }
363                return $data;
364        }
365
366        /**
367         * changes the data from your work-format to the db-format
368         *
369         * reimplemented to adjust the timezone of the timestamps (subtraction $this->tz_offset_s to get server-time)
370         * Please note, we do NOT call the method of the parent or so_sql !!!
371         *
372         * @param array $data if given works on that array and returns result, else works on internal data-array
373         * @return array with changed data
374         */
375        function data2db($data=null)
376        {
377                if ($intern = !is_array($data))
378                {
379                        $data = &$this->data;
380                }
381                foreach($this->timestamps as $name)
382                {
383                        if (isset($data[$name]) && $data[$name]) $data[$name] -= $this->tz_offset_s;
384                }
385                return $data;
386        }
387}
Note: See TracBrowser for help on using the repository browser.