11788: Always set is_billable if timeaccount is billable
[tine20] / tine20 / Timetracker / js / TimesheetEditDialog.js
1 /**
2  * Tine 2.0
3  * 
4  * @package     Timetracker
5  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
6  * @author      Philipp Schüle <p.schuele@metaways.de>
7  * @copyright   Copyright (c) 2007-2011 Metaways Infosystems GmbH (http://www.metaways.de)
8  *
9  */
10  
11 Ext.namespace('Tine.Timetracker');
12
13 /**
14  * Timetracker Edit Dialog
15  */
16 Tine.Timetracker.TimesheetEditDialog = Ext.extend(Tine.widgets.dialog.EditDialog, {
17
18     /**
19      * @private
20      */
21     windowNamePrefix: 'TimesheetEditWindow_',
22     appName: 'Timetracker',
23     recordClass: Tine.Timetracker.Model.Timesheet,
24     recordProxy: Tine.Timetracker.timesheetBackend,
25     tbarItems: null,
26     evalGrants: false,
27     useInvoice: false,
28     displayNotes: true,
29     
30     /**
31      * overwrite update toolbars function (we don't have record grants yet)
32      */
33     updateToolbars: function(record) {
34         this.onTimeaccountUpdate();
35         Tine.Timetracker.TimesheetEditDialog.superclass.updateToolbars.call(this, record, 'timeaccount_id');
36     },
37     
38     /**
39      * this gets called when initializing and if a new timeaccount is chosen
40      * 
41      * @param {} field
42      * @param {} timeaccount
43      */
44     onTimeaccountUpdate: function(field, timeaccount) {
45         // check for manage_timeaccounts right
46         var manageRight = Tine.Tinebase.common.hasRight('manage', 'Timetracker', 'timeaccounts');
47         
48         var notBillable = false;
49         var notClearable = false;
50
51         var grants = timeaccount ? timeaccount.get('account_grants') : (this.record.get('timeaccount_id') ? this.record.get('timeaccount_id').account_grants : {});
52         
53         if (grants) {
54             var setDisabled = ! (grants.bookAllGrant || grants.adminGrant || manageRight);
55             var accountField = this.getForm().findField('account_id');
56             accountField.setDisabled(setDisabled);
57             // set account id to the current user, if he doesn't have the right to edit other users timesheets
58             if (setDisabled) {
59                 if (this.copyRecord && (this.record.get('account_id') != Tine.Tinebase.registry.get('currentAccount').accountId)) {
60                     accountField.setValue(Tine.Tinebase.registry.get('currentAccount'));
61                 }
62             }
63             notBillable = ! (grants.manageBillableGrant || grants.adminGrant || manageRight);
64             notClearable = ! (grants.adminGrant || manageRight);
65             this.getForm().findField('billed_in').setDisabled(! (grants.adminGrant || manageRight));
66         }
67
68         if (timeaccount) {
69             notBillable = notBillable || timeaccount.data.is_billable == "0" || this.record.get('timeaccount_id').is_billable == "0";
70             
71             // clearable depends on timeaccount is_billable as well (changed by ps / 2009-09-01, behaviour was inconsistent)
72             notClearable = notClearable || timeaccount.data.is_billable == "0" || this.record.get('timeaccount_id').is_billable == "0";
73         }
74         
75         this.getForm().findField('is_billable').setDisabled(notBillable);
76         this.getForm().findField('is_cleared').setDisabled(notClearable);
77         
78         if (this.record.id == 0 && timeaccount) {
79             // set is_billable for new records according to the timeaccount setting
80             this.getForm().findField('is_billable').setValue(timeaccount.data.is_billable);
81         }
82     },
83     
84     /**
85      * Always set is_billable if timeaccount is billable. This is needed for copied sheets where the
86      * original is set to not billable
87      */
88     onAfterRecordLoad: function() {
89         Tine.Timetracker.TimesheetEditDialog.superclass.onAfterRecordLoad.call(this);
90         if (this.record.id == 0 && this.record.get('timeaccount_id') && this.record.get('timeaccount_id').is_billable) {
91             this.getForm().findField('is_billable').setValue(this.record.get('timeaccount_id').is_billable);
92         }
93     },
94
95     /**
96      * this gets called when initializing and if cleared checkbox is changed
97      *
98      * @param {} field
99      * @param {} newValue
100      *
101      * @todo    add prompt later?
102      */
103     onClearedUpdate: function(field, checked) {
104         if (!this.useMultiple) {
105             this.getForm().findField('billed_in').setDisabled(! checked);
106         }
107     },
108
109     initComponent: function() {
110         var salesApp = Tine.Tinebase.appMgr.get('Sales');
111         this.useInvoice = Tine.Tinebase.appMgr.get('Sales')
112             && salesApp.featureEnabled('invoicesModule')
113             && Tine.Tinebase.common.hasRight('manage', 'Sales', 'invoices')
114             && Tine.Sales.Model.Invoice;
115         
116         Tine.Timetracker.TimesheetEditDialog.superclass.initComponent.apply(this, arguments);
117     },
118
119     /**
120      * overwrites the isValid method on multipleEdit
121      */
122     isMultipleValid: function() {
123         var valid = true;
124         var keys = ['timeaccount_id', 'description', 'account_id'];
125         Ext.each(keys, function(key) {
126             var field = this.getForm().findField(key);
127             if(field.edited && ! field.validate()) {
128                 field.markInvalid();
129                 valid = false;
130             }
131         }, this);
132         return valid;
133     },
134
135     /**
136      * returns dialog
137      * 
138      * NOTE: when this method gets called, all initialization is done.
139      */
140     getFormItems: function() {
141         var lastRow = [new Tine.Addressbook.SearchCombo({
142             allowBlank: false,
143             forceSelection: true,
144             columnWidth: 1,
145             disabled: true,
146             useAccountRecord: true,
147             userOnly: true,
148             nameField: 'n_fileas',
149             fieldLabel: this.app.i18n._('Account'),
150             name: 'account_id'
151         }), {
152             columnWidth: .25,
153             disabled: (this.useMultiple) ? false : true,
154             boxLabel: this.app.i18n._('Billable'),
155             name: 'is_billable',
156             xtype: 'checkbox'
157         }, {
158             columnWidth: .25,
159             disabled: (this.useMultiple) ? false : true,
160             boxLabel: this.app.i18n._('Cleared'),
161             name: 'is_cleared',
162             xtype: 'checkbox',
163             listeners: {
164                 scope: this,
165                 check: this.onClearedUpdate
166             }
167         }, {
168                 columnWidth: .5,
169                 disabled: true,
170                 fieldLabel: this.app.i18n._('Cleared In'),
171                 name: 'billed_in'
172             }];
173         
174         if (this.useInvoice) {
175             lastRow.push(Tine.widgets.form.RecordPickerManager.get('Sales', 'Invoice', {
176                 columnWidth: .5,
177                 disabled: true,
178                 fieldLabel: this.app.i18n._('Invoice'),
179                 name: 'invoice_id'
180             }));
181         }
182         
183         
184         return {
185             xtype: 'tabpanel',
186             border: false,
187             plain:true,
188             activeTab: 0,
189             plugins: [{
190                 ptype : 'ux.tabpanelkeyplugin'
191             }],
192             defaults: {
193                 hideMode: 'offsets'
194             },
195             items:[
196                 {
197                 title: this.app.i18n.ngettext('Timesheet', 'Timesheets', 1),
198                 autoScroll: true,
199                 border: false,
200                 frame: true,
201                 layout: 'border',
202                 items: [{
203                     region: 'center',
204                     xtype: 'columnform',
205                     labelAlign: 'top',
206                     formDefaults: {
207                         xtype:'textfield',
208                         anchor: '100%',
209                         labelSeparator: '',
210                         columnWidth: .333
211                     },
212                     items: [[Tine.widgets.form.RecordPickerManager.get('Timetracker', 'Timeaccount', {
213                         columnWidth: 1,
214                         fieldLabel: this.app.i18n.ngettext('Time Account', 'Time Accounts', 1),
215                         emptyText: this.app.i18n._('Select Time Account...'),
216                         allowBlank: false,
217                         forceSelection: true,
218                         name: 'timeaccount_id',
219                         lazyInit: false,
220                         listeners: {
221                             scope: this,
222                             render: function(field){
223                                 if(!this.useMultiple) {
224                                     field.focus(false, 250);
225                                 }
226                             },
227                             select: this.onTimeaccountUpdate
228                         }
229                     })], [{
230                         fieldLabel: this.app.i18n._('Duration'),
231                         name: 'duration',
232                         allowBlank: false,
233                         xtype: 'tinedurationspinner'
234                         }, {
235                         fieldLabel: this.app.i18n._('Date'),
236                         name: 'start_date',
237                         allowBlank: false,
238                         xtype: 'datefield'
239                         }, {
240                         fieldLabel: this.app.i18n._('Start'),
241                         emptyText: this.app.i18n._('not set'),
242                         name: 'start_time',
243                         xtype: 'timefield'
244                     }], [{
245                         columnWidth: 1,
246                         fieldLabel: this.app.i18n._('Description'),
247                         emptyText: this.app.i18n._('Enter description...'),
248                         name: 'description',
249                         allowBlank: false,
250                         xtype: 'textarea',
251                         height: 150
252                     }], lastRow] 
253                 }, {
254                     // activities and tags
255                     layout: 'accordion',
256                     animate: true,
257                     region: 'east',
258                     width: 210,
259                     split: true,
260                     collapsible: true,
261                     collapseMode: 'mini',
262                     header: false,
263                     margins: '0 5 0 5',
264                     border: true,
265                     items: [
266                         new Tine.widgets.tags.TagPanel({
267                             app: 'Timetracker',
268                             border: false,
269                             bodyStyle: 'border:1px solid #B5B8C8;'
270                         })
271                     ]
272                 }]
273             }, new Tine.widgets.activities.ActivitiesTabPanel({
274                 app: this.appName,
275                 record_id: (! this.copyRecord) ? this.record.id : null,
276                 record_model: this.appName + '_Model_' + this.recordClass.getMeta('modelName')
277             })]
278         };
279     },
280     
281     /**
282      * show error if request fails
283      * 
284      * @param {} response
285      * @param {} request
286      * @private
287      */
288     onRequestFailed: function(response, request) {
289         this.saving = false;
290         
291         if (response.code && response.code == 902) {
292             // deadline exception
293             Ext.MessageBox.alert(
294                 this.app.i18n._('Failed'), 
295                 String.format(this.app.i18n._('Could not save {0}.'), this.i18nRecordName) 
296                     + ' ( ' + this.app.i18n._('Booking deadline for this Timeaccount has been exceeded.') /* + ' ' + response.message  */ + ')'
297             );
298         } else {
299             // call default exception handler
300             Tine.Tinebase.ExceptionHandler.handleRequestException(response);
301         }
302         this.loadMask.hide();
303     }
304 });
305
306 /**
307  * Timetracker Edit Popup
308  */
309 Tine.Timetracker.TimesheetEditDialog.openWindow = function (config) {
310     var id = (config.record && config.record.id) ? config.record.id : 0;
311     var window = Tine.WindowFactory.getWindow({
312         width: 800,
313         height: 500,
314         name: Tine.Timetracker.TimesheetEditDialog.prototype.windowNamePrefix + id,
315         contentPanelConstructor: 'Tine.Timetracker.TimesheetEditDialog',
316         contentPanelConstructorConfig: config
317     });
318     return window;
319 };