/*!
 * Tine 2.0 - Timetracker 
 * Copyright (c) 2007-2011 Metaways Infosystems GmbH (http://www.metaways.de)
 * http://www.gnu.org/licenses/agpl.html AGPL Version 3
 */
/**
 * Tine 2.0
 * 
 * @package     Timetracker
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Cornelius Weiss <c.weiss@metaways.de>
 * @copyright   Copyright (c) 2007-2015 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */
 
Ext.ns('Tine.Timetracker');

/**
 * handles minutes to time conversions
 * @class Tine.Timetracker.DurationSpinner
 * @extends Ext.ux.form.Spinner
 */
Tine.Timetracker.DurationSpinner = Ext.extend(Ext.ux.form.Spinner,  {
    
    /**
     * Set to empty value if value equals 0
     * @cfg emptyOnZero
     */
    emptyOnZero: null,
    
    initComponent: function() {
        this.preventMark = false;
        this.strategy = new Ext.ux.form.Spinner.TimeStrategy({
            incrementValue : 15
        });
        this.format = this.strategy.format;
    },

    setValue: function(value) {
        if(! value || value == '00:00') {
            value = this.emptyOnZero ? '' : '00:00';
        } else if(! value.toString().match(/:/)) {
            var hours = Math.floor(value / 60),
                minutes = value - hours * 60;

            if (hours < 10) {
               hours = '0' + hours;
            }

            if (minutes < 10) {
                minutes = '0' + minutes;
            }

            if (minutes !== 0) {
                value = hours + ':' + minutes;
            } else {
                value = hours + ':00';
            }
        }
        
        Tine.Timetracker.DurationSpinner.superclass.setValue.call(this, value);
    },
    
    validateValue: function(value) {
        if (value.search(/:/) != -1) {
            var parts = value.split(':'),
                hours = parseInt(parts[0]),
                minutes = parseInt(parts[1]);

            return 'NaN' != hours && 'NaN' != minutes;
        }
    },

    getValue: function() {
        var value = Tine.Timetracker.DurationSpinner.superclass.getValue.call(this);
        value = value.replace(',', '.');

        if(value && typeof value == 'string') {
            if (value.search(/:/) != -1) {
                var parts = value.split(':'),
                    hours = parseInt(parts[0]),
                    minutes = parseInt(parts[1]);

                if (0 > hours) {
                    hours = 0;
                }

                if (0 > minutes) {
                    minutes = 0;
                }

                if (minutes > 0) {
                    value = hours * 60 + minutes;
                } else {
                    value = hours * 60;
                }
            } else if (value > 0) {
                    value = value * 60;
            } else {
                this.markInvalid(i18n._('Not a valid time'));
                return;
            }
        }

        this.setValue(value);
        return value;
    }
});

Ext.reg('tinedurationspinner', Tine.Timetracker.DurationSpinner);
/**
 * Tine 2.0
 * 
 * @package     Timetracker
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Cornelius Weiss <c.weiss@metaways.de>
 * @copyright   Copyright (c) 2007-2016 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */

Ext.ns('Tine.Timetracker.Model');

/**
 * Model of a grant
 */
Tine.Timetracker.Model.TimeaccountGrant = Ext.data.Record.create([
    {name: 'id'},
    {name: 'account_id'},
    {name: 'account_type'},
    {name: 'account_name'},
    {name: 'bookOwnGrant',        type: 'boolean'},
    {name: 'viewAllGrant',        type: 'boolean'},
    {name: 'bookAllGrant',        type: 'boolean'},
    {name: 'manageBillableGrant', type: 'boolean'},
    {name: 'exportGrant',         type: 'boolean'},
    {name: 'adminGrant',          type: 'boolean'}
]);
/*
 * Tine 2.0
 * 
 * @package     Timetracker
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2007-2016 Metaways Infosystems GmbH (http://www.metaways.de)
 */
 
Ext.ns('Tine.Timetracker');

Tine.widgets.grid.RendererManager.register('Timetracker', 'Timesheet', 'timeaccount_closed', function(row, index, record) {
    var isopen = (record.data.timeaccount_id.is_open == '1');
    return Tine.Tinebase.common.booleanRenderer(!isopen);
});

Tine.widgets.grid.RendererManager.register('Timetracker', 'Timesheet', 'timeaccount_id', function(row, index, record) {
    var record = new Tine.Timetracker.Model.Timeaccount(record.get('timeaccount_id'));
    var closedText = record.get('is_open') ? '' : (' (' + Tine.Tinebase.appMgr.get('Timetracker').i18n._('closed') + ')');
    return record.get('number') ? (record.get('number') + ' - ' + record.get('title') + closedText) : '';
});

Tine.Tinebase.data.TitleRendererManager.register('Timetracker', 'Timeaccount', function(record) {
    var closedText = record.get('is_open') ? '' : (' (' + Tine.Tinebase.appMgr.get('Timetracker').i18n._('closed') + ')');
    return record.get('number') ? (record.get('number') + ' - ' + record.get('title') + closedText) : '';
});

Tine.Tinebase.data.TitleRendererManager.register('Timetracker', 'Timesheet', function(record) {
    var timeaccount = record.get('timeaccount_id'),
        description = Ext.util.Format.ellipsis(record.get('description'), 30, true),
        timeaccountTitle = '';

    if (timeaccount) {
        if (typeof(timeaccount.get) !== 'function') {
            timeaccount = new Tine.Timetracker.Model.Timeaccount(timeaccount);
        }
        timeaccountTitle = timeaccount.getTitle();
    }

    timeaccountTitle = timeaccountTitle ? '[' + timeaccountTitle + '] ' : '';
    return timeaccountTitle + description;
});

Tine.widgets.grid.RendererManager.register('Timetracker', 'Timeaccount', 'status', function(row, index, record) {
    return Tine.Tinebase.appMgr.get('Timetracker').i18n._hidden(record.get('status'));
});

Tine.widgets.grid.RendererManager.register('Timetracker', 'Timeaccount', 'is_open', function(row, index, record) {
    var i18n = Tine.Tinebase.appMgr.get('Timetracker').i18n;
    return record.get('is_open') ? i18n._('open') : i18n._('closed');
});

// add renderer for invoice position gridpanel
Tine.Timetracker.HumanHourRenderer = function(value) {
    return Ext.util.Format.round(value, 2);
}

Tine.Timetracker.registerRenderers = function() {
    
    if (! Tine.hasOwnProperty('Sales') || ! Tine.Sales.hasOwnProperty('InvoicePositionQuantityRendererRegistry')) {
        Tine.Timetracker.registerRenderers.defer(10);
        return false;
    }
    
    Tine.Sales.InvoicePositionQuantityRendererRegistry.register('Timetracker_Model_Timeaccount', 'hour', Tine.Timetracker.HumanHourRenderer);
};

Tine.Timetracker.registerRenderers();

Tine.Timetracker.registerAccountables = function() {
    if (! Tine.hasOwnProperty('Sales') || ! Tine.Sales.hasOwnProperty('AccountableRegistry')) {
        Tine.Timetracker.registerAccountables.defer(10);
        return false;
    }
    
    Tine.Sales.AccountableRegistry.register('Timetracker', 'Timeaccount');
};

Tine.Timetracker.registerAccountables();

// disables container tree in WestPanel
Tine.Timetracker.TimeaccountWestPanel = Ext.extend(Tine.widgets.mainscreen.WestPanel, {
    hasContainerTreePanel: false
});/* 
 * Tine 2.0
 * 
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Cornelius Weiss <c.weiss@metaways.de>
 * @copyright   Copyright (c) 2012 Metaways Infosystems GmbH (http://www.metaways.de)
 */

Ext.ns('Tine.Timetracker');

/**
 * @namespace   Tine.Timetracker
 * @author      Cornelius Weiss <c.weiss@metaways.de>
 * @class       Tine.Timetracker.TimeaccountPickerCombo
 * @extends     Tine.Tinebase.widgets.form.RecordPickerComboBox
 * 
 * adds show closed handling
 */

Tine.Timetracker.TimeaccountPickerCombo = Ext.extend(Tine.Tinebase.widgets.form.RecordPickerComboBox, {
    /**
     * @cfg {Bool} showClosed
     */
    showClosed: false,
    
    /**
     * @property showClosedBtn
     * @type Ext.Button
     */
    showClosedBtn: null,
    
    sortBy: 'number',
    
    initComponent: function() {
        this.recordProxy = Tine.Timetracker.timeaccountBackend;
        this.recordClass = Tine.Timetracker.Model.Timeaccount;
        
        Tine.Timetracker.TimeaccountPickerCombo.superclass.initComponent.apply(this, arguments);
    },
    
    initList: function() {
        Tine.Timetracker.TimeaccountPickerCombo.superclass.initList.apply(this, arguments);
        
        if (this.pageTb && ! this.showClosedBtn) {
            this.showClosedBtn = new Tine.widgets.grid.FilterButton({
                text: this.app.i18n._('Show closed'),
                iconCls: 'action_showArchived',
                field: 'is_open',
                invert: true,
                pressed: this.showClosed,
                scope: this,
                handler: function() {
                    this.showClosed = this.showClosedBtn.pressed;
                    this.store.load();
                }
                
            });
            
            this.pageTb.add('-', this.showClosedBtn);
            this.pageTb.doLayout();
        }
    },
    
    /**
     * apply showClosed value
     */
    onStoreBeforeLoadRecords: function(o, options, success, store) {
        if (Tine.Timetracker.TimeaccountPickerCombo.superclass.onStoreBeforeLoadRecords.apply(this, arguments) !== false) {
            if (this.showClosedBtn) {
                this.showClosedBtn.setValue(options.params.filter);
            }
        }
    },
    
    /**
     * append showClosed value
     */
    onBeforeLoad: function (store, options) {
        Tine.Timetracker.TimeaccountPickerCombo.superclass.onBeforeLoad.apply(this, arguments);

        if (this.showClosedBtn) {
            Ext.each(store.baseParams.filter, function(filter, idx) {
                if (filter.field == 'is_open'){
                    store.baseParams.filter.remove(filter);
                }
            }, this);
            
            if (this.showClosedBtn.getValue().value === true) {
                store.baseParams.filter.push(this.showClosedBtn.getValue());
            }
        }
    }
});

Tine.widgets.form.RecordPickerManager.register('Timetracker', 'Timeaccount', Tine.Timetracker.TimeaccountPickerCombo);/**
 * Tine 2.0
 * 
 * @package     Timetracker
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Cornelius Weiss <c.weiss@metaways.de>
 * @copyright   Copyright (c) 2007-2011 Metaways Infosystems GmbH (http://www.metaways.de)
 */
 
Ext.ns('Tine.Timetracker');

Tine.Timetracker.TimeAccountStatusFilterModel = Ext.extend(Tine.widgets.grid.FilterModel, {
    field: 'is_open',
    valueType: 'bool',
    defaultValue: 1,
    
    /**
     * @private
     */
    initComponent: function() {
        Tine.Timetracker.TimeAccountStatusFilterModel.superclass.initComponent.call(this);
        
        this.app = Tine.Tinebase.appMgr.get('Timetracker');
        this.label = this.label ? this.label : this.app.i18n._("Time Account - Status");
        this.operators = ['equals'];
    },
   
    /**
     * value renderer
     * 
     * @param {Ext.data.Record} filter line
     * @param {Ext.Element} element to render to 
     */
    valueRenderer: function(filter, el) {
        // value
        var value = new Ext.form.ComboBox({
            filter: filter,
            width: 200,
            id: 'tw-ftb-frow-valuefield-' + filter.id,
            value: filter.data.value ? filter.data.value : this.defaultValue,
            renderTo: el,
            mode: 'local',
            forceSelection: true,
            blurOnSelect: true,
            triggerAction: 'all',
            store: [[0, this.app.i18n._('closed')], [1, this.app.i18n._('open')]]
        });
        value.on('specialkey', function(field, e){
             if(e.getKey() == e.ENTER){
                 this.onFiltertrigger();
             }
        }, this);
        
        return value;
    }
});
Tine.widgets.grid.FilterToolbar.FILTERS['timetracker.timeaccountstatus'] = Tine.Timetracker.TimeAccountStatusFilterModel;
/**
 * Tine 2.0
 * 
 * @package     Timetracker
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Cornelius Weiss <c.weiss@metaways.de>
 * @copyright   Copyright (c) 2007-2011 Metaways Infosystems GmbH (http://www.metaways.de)
 */
 
Ext.ns('Tine.Timetracker');

Tine.Timetracker.TimeAccountBilledFilterModel = Ext.extend(Tine.widgets.grid.FilterModel, {
    field: 'timeaccount_status',
    valueType: 'string',
    defaultValue: 'to bill',
    
    /**
     * @private
     */
    initComponent: function() {
        Tine.Timetracker.TimeAccountBilledFilterModel.superclass.initComponent.call(this);
        
        this.app = Tine.Tinebase.appMgr.get('Timetracker');
        this.label = this.label ? this.label : this.app.i18n._("Time Account - Billed");
        this.operators = ['equals'];
    },
   
    /**
     * value renderer
     * 
     * @param {Ext.data.Record} filter line
     * @param {Ext.Element} element to render to 
     */
    valueRenderer: function(filter, el) {
        var value = new Ext.form.ComboBox({
            filter: filter,
            width: 200,
            id: 'tw-ftb-frow-valuefield-' + filter.id,
            value: filter.data.value ? filter.data.value : this.defaultValue,
            renderTo: el,
            mode: 'local',
            forceSelection: true,
            blurOnSelect: true,
            triggerAction: 'all',
            store: [
                ['not yet billed', this.app.i18n._('not yet billed')], 
                ['to bill', this.app.i18n._('to bill')],
                ['billed', this.app.i18n._('billed')]
            ]
        });
        value.on('specialkey', function(field, e){
             if(e.getKey() == e.ENTER){
                 this.onFiltertrigger();
             }
        }, this);
        
        return value;
    }
});
Tine.widgets.grid.FilterToolbar.FILTERS['timetracker.timeaccountbilled'] = Tine.Timetracker.TimeAccountBilledFilterModel;
/**
 * Tine 2.0
 * 
 * @package     Timetracker
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Alexander Stintzing <a.stintzing@metaways.de>
 * @copyright   Copyright (c) 2012 Metaways Infosystems GmbH (http://www.metaways.de)
 */
 
Ext.ns('Tine.Timetracker');

/**
 * @namespace   Tine.Timetracker
 * @class       Tine.Timetracker.TimeaccountContractFilterModel
 * @extends     Tine.widgets.grid.ForeignRecordFilter
 * 
 * @author      Alexander Stintzing <a.stintzing@metaways.de>
 */
Tine.Timetracker.TimeaccountContractFilterModel = Ext.extend(Tine.widgets.grid.ForeignRecordFilter, {
    
    // private
    field: 'contract',
    valueType: 'relation',
    
    /**
     * @private
     */
    initComponent: function() {
        this.app = Tine.Tinebase.appMgr.get('Timetracker');
        this.label = this.app.i18n._('Contract');
        this.foreignRecordClass = 'Sales.Contract',
        this.pickerConfig = {emptyText: this.app.i18n._('without contract'), allowBlank: true};

        Tine.Timetracker.TimeaccountContractFilterModel.superclass.initComponent.call(this);
    }
});

Tine.widgets.grid.FilterToolbar.FILTERS['timetracker.timeaccountcontract'] = Tine.Timetracker.TimeaccountContractFilterModel;/**
 * Tine 2.0
 *
 * @package     Timetracker
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Alexander Stintzing <a.stintzing@metaways.de>
 * @copyright   Copyright (c) 2014 Metaways Infosystems GmbH (http://www.metaways.de)
 */

Ext.ns('Tine.Timetracker');

/**
 * @namespace   Tine.Timetracker
 * @class       Tine.Timetracker.TimeaccountResponsibleFilterModel
 * @extends     Tine.widgets.grid.ForeignRecordFilter
 *
 * @author      Alexander Stintzing <a.stintzing@metaways.de>
 */
Tine.Timetracker.TimeaccountResponsibleFilterModel = Ext.extend(Tine.widgets.grid.ForeignRecordFilter, {

    // private
    field: 'responsible',
    valueType: 'relation',

    /**
     * @private
     */
    initComponent: function() {
        this.app = Tine.Tinebase.appMgr.get('Timetracker');
        this.label = this.app.i18n._('Responsible');
        this.foreignRecordClass = 'Addressbook.Contact',
            this.pickerConfig = {emptyText: this.app.i18n._('without responsible'), allowBlank: true};

        Tine.Timetracker.TimeaccountResponsibleFilterModel.superclass.initComponent.call(this);
    }
});

Tine.widgets.grid.FilterToolbar.FILTERS['timetracker.timeaccountresponsible'] = Tine.Timetracker.TimeaccountResponsibleFilterModel;/*
 * Tine 2.0
 * 
 * @package     Timetracker
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2007-2016 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */
 
Ext.namespace('Tine.Timetracker');

/**
 * Timeaccount grid panel
 * 
 * @namespace   Tine.Timetracker
 * @class       Tine.Timetracker.TimeaccountGridPanel
 * @extends     Tine.widgets.grid.GridPanel
 * 
 * <p>Timeaccount Grid Panel</p>
 * <p><pre>
 * TODO         copy action needs to copy the acl too
 * </pre></p>
 * 
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schuele <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2007-2009 Metaways Infosystems GmbH (http://www.metaways.de)
 * 
 * @param       {Object} config
 * @constructor
 * Create a new Tine.Timetracker.TimeaccountGridPanel
 */
Tine.Timetracker.TimeaccountGridPanel = Ext.extend(Tine.widgets.grid.GridPanel, {
    defaultSortInfo: {field: 'creation_time', direction: 'DESC'},
    gridConfig: {
        autoExpandColumn: 'title'
    },
    copyEditAction: true,
    defaultFilters: [{
        field: 'query',
        operator: 'contains',
        value: ''
    }, {
        field: 'is_open',
        operator: 'equals',
        value: true
    }],
    
    initComponent: function() {
        Tine.Timetracker.TimeaccountGridPanel.superclass.initComponent.call(this);
        
        this.action_addInNewWindow.setDisabled(! Tine.Tinebase.common.hasRight('manage', 'Timetracker', 'timeaccounts'));
        this.action_editInNewWindow.requiredGrant = 'editGrant';
    },

    /**
     * @private
     */
    initActions: function() {
        this.actions_exportTimeaccount = new Ext.Action({
            text: this.app.i18n._('Export Timeaccounts'),
            iconCls: 'action_export',
            scope: this,
            requiredGrant: 'readGrant',
            disabled: true,
            allowMultiple: true,
            menu: {
                items: [
                    new Tine.widgets.grid.ExportButton({
                        text: this.app.i18n._('Export as ODS'),
                        format: 'ods',
                        iconCls: 'tinebase-action-export-ods',
                        exportFunction: 'Timetracker.exportTimeaccounts',
                        gridPanel: this
                    })
                ]
            }
        });

        // register actions in updater
        this.actionUpdater.addActions([
            this.actions_exportTimeaccount
        ]);

        Tine.Timetracker.TimesheetGridPanel.superclass.initActions.call(this);
    },

    /**
     * add custom items to action toolbar
     *
     * @return {Object}
     */
    getActionToolbarItems: function() {
        return [
            Ext.apply(new Ext.Button(this.actions_exportTimeaccount), {
                scale: 'medium',
                rowspan: 2,
                iconAlign: 'top'
            })
        ];
    },

    /**
     * add custom items to context menu
     *
     * @return {Array}
     */
    getContextMenuItems: function() {
        var items = [
            '-', {
                text: Tine.Tinebase.appMgr.get('Timetracker').i18n._('Close Timeaccount'),
                iconCls: 'action_edit',
                scope: this,
                disabled: !Tine.Tinebase.common.hasRight('manage', 'Timetracker', 'timeaccounts'),
                itemId: 'closeAccount',
                handler: this.onCloseTimeaccount.createDelegate(this)
            }
        ];

        return items;
    },

    /**
     * Closes selected timeaccount
     */
    onCloseTimeaccount: function () {
        var grid = this,
            recordProxy = this.recordProxy,
            selectionModel = grid.selectionModel;

        Ext.each(selectionModel.getSelections(), function (record) {
            recordProxy.loadRecord(record, {
                success: function (record) {
                    record.set('is_open', false);
                    recordProxy.saveRecord(record, {
                        success: function () {
                            grid.store.reload();
                            grid.store.remove(record);
                        }
                    });
                }
            });
        });
    }
});
/**
 * Tine 2.0
 * 
 * @package     Timetracker
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Cornelius Weiss <c.weiss@metaways.de>
 * @copyright   Copyright (c) 2007-2016 Metaways Infosystems GmbH (http://www.metaways.de)
 */
 
Ext.ns('Tine.Timetracker');

Tine.Timetracker.TimeAccountFilterModel = Ext.extend(Tine.widgets.grid.ForeignRecordFilter, {
    
    /**
     * @cfg {Record} foreignRecordClass needed for explicit defined filters
     */
    foreignRecordClass : 'Timetracker.Timeaccount',
    
    /**
     * @cfg {String} linkType {relation|foreignId} needed for explicit defined filters
     */
    linkType: 'foreignId',
    
    /**
     * @cfg {String} filterName server side filterGroup Name, needed for explicit defined filters
     */
    filterName: 'TimeaccountFilter',
    
    /**
     * @cfg {String} ownField for explicit filterRow
     */
    ownField: 'timeaccount_id',
    
    /**
     * @private
     */
    initComponent: function() {
        this.app = Tine.Tinebase.appMgr.get('Timetracker');
        this.label = this.app.i18n.n_('Timeaccount', 'Timeaccounts', 1);
        
        this.pickerConfig = this.pickerConfig || {};
        
        Tine.Timetracker.TimeAccountFilterModel.superclass.initComponent.call(this);
    }
});

Tine.widgets.grid.FilterToolbar.FILTERS['timetracker.timeaccount'] = Tine.Timetracker.TimeAccountFilterModel;/**
 * Tine 2.0
 * 
 * @package     Timetracker
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Cornelius Weiss <c.weiss@metaways.de>
 * @copyright   Copyright (c) 2007-2016 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */
 
Ext.namespace('Tine.Timetracker');

Tine.Timetracker.TimeaccountEditDialog = Ext.extend(Tine.widgets.dialog.EditDialog, {
    
    /**
     * @private
     */
    windowNamePrefix: 'TimeaccountEditWindow_',
    appName: 'Timetracker',
    recordClass: Tine.Timetracker.Model.Timeaccount,
    recordProxy: Tine.Timetracker.timeaccountBackend,
    useInvoice: false,
    displayNotes: true,

    windowWidth: 800,
    windowHeight: 500,
    
    /**
     * overwrite update toolbars function (we don't have record grants yet)
     */
    updateToolbars: function() {
    },
    
    initComponent: function() {
        var salesApp = Tine.Tinebase.appMgr.get('Sales');
        this.useInvoice = salesApp
            && salesApp.featureEnabled('invoicesModule')
            && Tine.Tinebase.common.hasRight('manage', 'Sales', 'invoices')
            && Tine.Sales.Model.Invoice;
        Tine.Timetracker.TimeaccountEditDialog.superclass.initComponent.call(this);
    },

    isMultipleValid: function() {
        return true;
    },

    /**
     * containerProperty (all contracts in one container) exists, so overwrite creating selector here
     */
    initContainerSelector: Ext.emptyFn,
    
    onRecordLoad: function() {
        // make sure grants grid is initialized
        this.getGrantsGrid();
        
        var grants = this.record.get('grants') || [];
        this.grantsStore.loadData({results: grants});
        Tine.Timetracker.TimeaccountEditDialog.superclass.onRecordLoad.call(this);
        
        if (! this.copyRecord && ! this.record.id) {
            this.window.setTitle(this.app.i18n._('Add New Timeaccount'));
        }
    },
    
    onRecordUpdate: function() {
        Tine.Timetracker.TimeaccountEditDialog.superclass.onRecordUpdate.call(this);
        this.record.set('grants', '');
        
        var grants = [];
        this.grantsStore.each(function(_record){
            grants.push(_record.data);
        });
        
        this.record.set('grants', grants);
    },
    
    /**
     * returns dialog
     * 
     * NOTE: when this method gets called, all initalisation is done.
     */
    getFormItems: function() {
        
        var secondRow = [{
            fieldLabel: this.app.i18n._('Unit'),
            name: 'price_unit'
        }, {
            xtype: 'numberfield',
            fieldLabel: this.app.i18n._('Unit Price'),
            name: 'price',
            allowNegative: false
        }, {
            xtype: 'numberfield',
            fieldLabel: this.app.i18n._('Budget'),
            name: 'budget',
            allowNegative: false,
            value: 0
        }, {
            fieldLabel: this.app.i18n._('Status'),
            name: 'is_open',
            xtype: 'combo',
            mode: 'local',
            forceSelection: true,
            triggerAction: 'all',
            store: [[0, this.app.i18n._('closed')], [1, this.app.i18n._('open')]]
        }, {
            fieldLabel: this.app.i18n._('Billed'),
            name: 'status',
            xtype: 'combo',
            mode: 'local',
            forceSelection: true,
            triggerAction: 'all',
            value: 'not yet billed',
            store: [
                ['not yet billed', this.app.i18n._('not yet billed')], 
                ['to bill', this.app.i18n._('to bill')],
                ['billed', this.app.i18n._('billed')]
            ],
            listeners: {
                scope: this,
                select: this.onBilledChange.createDelegate(this)
            }
        }];
        
        secondRow.push({
            columnWidth: 1/3,
            disabled: false,
            fieldLabel: this.app.i18n._('Cleared In'),
            name: 'billed_in'
        });
        
        secondRow.push({
            fieldLabel: this.app.i18n._('Booking deadline'),
            name: 'deadline',
            xtype: 'combo',
            mode: 'local',
            forceSelection: true,
            triggerAction: 'all',
            value: 'none',
            store: [
                ['none', this.app.i18n._('none')], 
                ['lastweek', this.app.i18n._('last week')]
            ]
        });
        
        secondRow.push({
            xtype: 'extuxclearabledatefield',
            name: 'cleared_at',
            fieldLabel: this.app.i18n._('Cleared at')
        });
        
        if (Tine.Tinebase.appMgr.get('Sales')) {
            secondRow.push({
                xtype: 'tinerelationpickercombo',
                fieldLabel: this.app.i18n._('Cost Center'),
                editDialog: this,
                allowBlank: true,
                app: 'Sales',
                recordClass: Tine.Sales.Model.CostCenter,
                relationType: 'COST_CENTER',
                relationDegree: 'sibling',
                modelUnique: true
            });
        }
        
        var lastRow = [{
            columnWidth: 1/3,
            editDialog: this,
            xtype: 'tinerelationpickercombo',
            fieldLabel: this.app.i18n._('Responsible Person'),
            allowBlank: true,
            app: 'Addressbook',
            recordClass: Tine.Addressbook.Model.Contact,
            relationType: 'RESPONSIBLE',
            relationDegree: 'sibling',
            modelUnique: true
        }, {
            hideLabel: true,
            boxLabel: this.app.i18n._('Timesheets are billable'),
            name: 'is_billable',
            xtype: 'checkbox',
            columnWidth: 1/3
        }];
        
        if (this.useInvoice) {
            lastRow.push(Tine.widgets.form.RecordPickerManager.get('Sales', 'Invoice', {
                columnWidth: 1/3,
                disabled: true,
                fieldLabel: this.app.i18n._('Invoice'),
                name: 'invoice_id'
            }));
        }
        return {
            xtype: 'tabpanel',
            border: false,
            plain:true,
            activeTab: 0,
            plugins: [{
                ptype : 'ux.tabpanelkeyplugin'
            }],
            defaults: {
                hideMode: 'offsets'
            },
            items:[{
                title: this.app.i18n.ngettext('Time Account', 'Time Accounts', 1),
                autoScroll: true,
                border: false,
                frame: true,
                layout: 'border',
                items: [{
                    region: 'center',
                    xtype: 'columnform',
                    labelAlign: 'top',
                    formDefaults: {
                        xtype:'textfield',
                        anchor: '100%',
                        labelSeparator: '',
                        columnWidth: .333
                    },
                    items: [[{
                        fieldLabel: this.app.i18n._('Number'),
                        name: 'number',
                        allowBlank: false,
                        maxLength: 127
                        }, {
                        columnWidth: .666,
                        fieldLabel: this.app.i18n._('Title'),
                        name: 'title',
                        maxLength: 255,
                        allowBlank: false
                        }], [{
                        columnWidth: 1,
                        fieldLabel: this.app.i18n._('Description'),
                        xtype: 'textarea',
                        name: 'description',
                        height: 150
                        }], secondRow, lastRow] 
                }, {
                    // activities and tags
                    layout: 'accordion',
                    animate: true,
                    region: 'east',
                    width: 210,
                    split: true,
                    collapsible: true,
                    collapseMode: 'mini',
                    header: false,
                    margins: '0 5 0 5',
                    border: true,
                    items: [
                    new Tine.widgets.tags.TagPanel({
                        app: 'Timetracker',
                        border: false,
                        bodyStyle: 'border:1px solid #B5B8C8;'
                    })]
                }]
            },{
                title: this.app.i18n._('Access'),
                layout: 'fit',
                items: [this.getGrantsGrid()]
            }, new Tine.widgets.activities.ActivitiesTabPanel({
                app: this.appName,
                record_id: (! this.copyRecord) ? this.record.id : null,
                record_model: this.appName + '_Model_' + this.recordClass.getMeta('modelName')
            })]
        };
    },
    
    getGrantsGrid: function() {
        if (! this.grantsGrid) {
            this.grantsStore =  new Ext.data.JsonStore({
                root: 'results',
                totalProperty: 'totalcount',
                // use account_id here because that simplifies the adding of new records with the search comboboxes
                id: 'account_id',
                fields: Tine.Timetracker.Model.TimeaccountGrant
            });
            
            var columns = [
                new Ext.ux.grid.CheckColumn({
                    header: this.app.i18n._('Book Own'),
                    dataIndex: 'bookOwnGrant',
                    tooltip: i18n._('The grant to add Timesheets to this Timeaccount'),
                    width: 55
                }),
                new Ext.ux.grid.CheckColumn({
                    header: this.app.i18n._('View All'),
                    tooltip: i18n._('The grant to view Timesheets of other users'),
                    dataIndex: 'viewAllGrant',
                    width: 55
                }),
                new Ext.ux.grid.CheckColumn({
                    header: this.app.i18n._('Book All'),
                    tooltip: i18n._('The grant to add Timesheets for other users'),
                    dataIndex: 'bookAllGrant',
                    width: 55
                }),
                new Ext.ux.grid.CheckColumn({
                    header:this.app.i18n._('Manage Clearing'),
                    tooltip: i18n._('The grant to manage clearing of Timesheets'),
                    dataIndex: 'manageBillableGrant',
                    width: 55
                }),
                new Ext.ux.grid.CheckColumn({
                    header:this.app.i18n._('Export'),
                    tooltip: i18n._('The grant to export Timesheets of Timeaccount'),
                    dataIndex: 'exportGrant',
                    width: 55
                }),
                new Ext.ux.grid.CheckColumn({
                    header: this.app.i18n._('Manage All'),
                    tooltip: i18n._('Includes all other grants'),
                    dataIndex: 'adminGrant',
                    width: 55
                })
            ];
            
            this.grantsGrid = new Tine.widgets.account.PickerGridPanel({
                selectType: 'both',
                title:  this.app.i18n._('Permissions'),
                store: this.grantsStore,
                hasAccountPrefix: true,
                configColumns: columns,
                selectAnyone: false,
                selectTypeDefault: 'group',
                recordClass: Tine.Tinebase.Model.Grant
            });
        }
        return this.grantsGrid;
    },
    
    /**
     * is called is billed field changes
     */
    onBilledChange: function(combo, record, index) {
        if (combo.getValue() == 'billed') {
            if (! this.getForm().findField('cleared_at').getValue()) {
                this.getForm().findField('cleared_at').setValue(new Date());
            }
        }
    },
    
    doCopyRecord: function() {
        Tine.Timetracker.TimeaccountEditDialog.superclass.doCopyRecord.call(this);
        
        this.record.set('status', 'not yet billed');
    }
});
﻿/*
 * Tine 2.0
 * 
 * @package     Timetracker
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2007-2016 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */
 
Ext.namespace('Tine.Timetracker');

/**
 * Timesheet grid panel
 * 
 * @namespace   Tine.Timetracker
 * @class       Tine.Timetracker.TimesheetGridPanel
 * @extends     Tine.widgets.grid.GridPanel
 * 
 * <p>Timesheet Grid Panel</p>
 * <p><pre>
 * </pre></p>
 * 
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * 
 * @param       {Object} config
 * @constructor
 * Create a new Tine.Timetracker.TimesheetGridPanel
 */
Tine.Timetracker.TimesheetGridPanel = Ext.extend(Tine.widgets.grid.GridPanel, {

    /**
     * activates copy action
     */
    copyEditAction: true,

    /**
     * only allow multi edit with manage_timeaccounts right (because of timeaccount handling in edit dlg)
     */
    multipleEditRequiredRight: 'manage_timeaccounts',

    initComponent: function() {
        this.defaultFilters = [
            {field: 'start_date', operator: 'within', value: 'weekThis'},
            {field: 'account_id', operator: 'equals', value: Tine.Tinebase.registry.get('currentAccount')}
        ];

        this.initDetailsPanel();
        
        // only eval grants in action updater if user does not have the right to manage timeaccounts
        this.evalGrants = ! Tine.Tinebase.common.hasRight('manage', 'Timetracker', 'timeaccounts');
        
        Tine.Timetracker.TimesheetGridPanel.superclass.initComponent.call(this);
    },

    /**
     * @private
     */
    initDetailsPanel: function() {
        this.detailsPanel = new Tine.widgets.grid.DetailsPanel({
            gridpanel: this,
            
            // use default Tpl for default and multi view
            defaultTpl: new Ext.XTemplate(
                '<div class="preview-panel-timesheet-nobreak">',
                    '<!-- Preview timeframe -->',           
                    '<div class="preview-panel preview-panel-timesheet-left">',
                        '<div class="bordercorner_1"></div>',
                        '<div class="bordercorner_2"></div>',
                        '<div class="bordercorner_3"></div>',
                        '<div class="bordercorner_4"></div>',
                        '<div class="preview-panel-declaration">' /*+ this.app.i18n._('timeframe')*/ + '</div>',
                        '<div class="preview-panel-timesheet-leftside preview-panel-left">',
                            '<span class="preview-panel-bold">',
                            /*'First Entry'*/'<br/>',
                            /*'Last Entry*/'<br/>',
                            /*'Duration*/'<br/>',
                            '<br/>',
                            '</span>',
                        '</div>',
                        '<div class="preview-panel-timesheet-rightside preview-panel-left">',
                            '<span class="preview-panel-nonbold">',
                            '<br/>',
                            '<br/>',
                            '<br/>',
                            '<br/>',
                            '</span>',
                        '</div>',
                    '</div>',
                    '<!-- Preview summary -->',
                    '<div class="preview-panel-timesheet-right">',
                        '<div class="bordercorner_gray_1"></div>',
                        '<div class="bordercorner_gray_2"></div>',
                        '<div class="bordercorner_gray_3"></div>',
                        '<div class="bordercorner_gray_4"></div>',
                        '<div class="preview-panel-declaration">'/* + this.app.i18n._('summary')*/ + '</div>',
                        '<div class="preview-panel-timesheet-leftside preview-panel-left">',
                            '<span class="preview-panel-bold">',
                            this.app.i18n._('Total Timesheets') + '<br/>',
                            this.app.i18n._('Billable Timesheets') + '<br/>',
                            this.app.i18n._('Total Time') + '<br/>',
                            this.app.i18n._('Time of Billable Timesheets') + '<br/>',
                            '</span>',
                        '</div>',
                        '<div class="preview-panel-timesheet-rightside preview-panel-left">',
                            '<span class="preview-panel-nonbold">',
                            '{count}<br/>',
                            '{countbillable}<br/>',
                            '{sum}<br/>',
                            '{sumbillable}<br/>',
                            '</span>',
                        '</div>',
                    '</div>',
                '</div>'            
            ),
            
            showDefault: function(body) {
                
                var data = {
                    count: this.gridpanel.store.proxy.jsonReader.jsonData.totalcount,
                    countbillable: (this.gridpanel.store.proxy.jsonReader.jsonData.totalcountbillable) ? this.gridpanel.store.proxy.jsonReader.jsonData.totalcountbillable : 0,
                    sum:  Tine.Tinebase.common.minutesRenderer(this.gridpanel.store.proxy.jsonReader.jsonData.totalsum),
                    sumbillable: Tine.Tinebase.common.minutesRenderer(this.gridpanel.store.proxy.jsonReader.jsonData.totalsumbillable)
                };
                
                this.defaultTpl.overwrite(body, data);
            },
            
            showMulti: function(sm, body) {
                
                var data = {
                    count: sm.getCount(),
                    countbillable: 0,
                    sum: 0,
                    sumbillable: 0
                };
                sm.each(function(record){
                    
                    data.sum = data.sum + parseInt(record.data.duration);
                    if (record.data.is_billable_combined == '1') {
                        data.countbillable++;
                        data.sumbillable = data.sumbillable + parseInt(record.data.duration);
                    }
                });
                data.sum = Tine.Tinebase.common.minutesRenderer(data.sum);
                data.sumbillable = Tine.Tinebase.common.minutesRenderer(data.sumbillable);
                
                this.defaultTpl.overwrite(body, data);
            },
            
            tpl: new Ext.XTemplate(
                '<div class="preview-panel-timesheet-nobreak">',    
                    '<!-- Preview beschreibung -->',
                    '<div class="preview-panel preview-panel-timesheet-left">',
                        '<div class="bordercorner_1"></div>',
                        '<div class="bordercorner_2"></div>',
                        '<div class="bordercorner_3"></div>',
                        '<div class="bordercorner_4"></div>',
                        '<div class="preview-panel-declaration">' /* + this.app.i18n._('Description') */ + '</div>',
                        '<div class="preview-panel-timesheet-description preview-panel-left" ext:qtip="{[this.encode(values.description)]}">',
                            '<span class="preview-panel-nonbold">',
                             '{[this.encode(values.description, "longtext")]}',
                            '<br/>',
                            '</span>',
                        '</div>',
                    '</div>',
                    '<!-- Preview detail-->',
                    '<div class="preview-panel-timesheet-right">',
                        '<div class="bordercorner_gray_1"></div>',
                        '<div class="bordercorner_gray_2"></div>',
                        '<div class="bordercorner_gray_3"></div>',
                        '<div class="bordercorner_gray_4"></div>',
                        '<div class="preview-panel-declaration">' /* + this.app.i18n._('Detail') */ + '</div>',
                        '<div class="preview-panel-timesheet-leftside preview-panel-left">',
                        // @todo add custom fields here
                        /*
                            '<span class="preview-panel-bold">',
                            'Ansprechpartner<br/>',
                            'Newsletter<br/>',
                            'Ticketnummer<br/>',
                            'Ticketsubjekt<br/>',
                            '</span>',
                        */
                        '</div>',
                        '<div class="preview-panel-timesheet-rightside preview-panel-left">',
                            '<span class="preview-panel-nonbold">',
                            '<br/>',
                            '<br/>',
                            '<br/>',
                            '<br/>',
                            '</span>',
                        '</div>',
                    '</div>',
                '</div>',{
                encode: function(value, type, prefix) {
                    if (value) {
                        if (type) {
                            switch (type) {
                                case 'longtext':
                                    value = Ext.util.Format.ellipsis(value, 150);
                                    break;
                                default:
                                    value += type;
                            }
                        } else {
                            value = Ext.util.Format.htmlEncode(value);
                        }
                        
                        var encoded = Ext.util.Format.htmlEncode(value);
                        encoded = Ext.util.Format.nl2br(encoded);
                        
                        return encoded;
                    } else {
                        return '';
                    }
                }
            })
        });
    },

    /**
     * @private
     */
    initActions: function() {
        this.actions_exportTimesheet = new Ext.Action({
            text: this.app.i18n._('Export Timesheets'),
            iconCls: 'action_export',
            scope: this,
            requiredGrant: 'exportGrant',
            disabled: true,
            allowMultiple: true,
            actionUpdater: this.updateExportAction,
            menu: {
                items: [
                    new Tine.widgets.grid.ExportButton({
                        text: this.app.i18n._('Export as ODS'),
                        format: 'ods',
                        iconCls: 'tinebase-action-export-ods',
                        exportFunction: 'Timetracker.exportTimesheets',
                        gridPanel: this
                    }),
                    new Tine.widgets.grid.ExportButton({
                        text: this.app.i18n._('Export as CSV'),
                        format: 'csv',
                        iconCls: 'tinebase-action-export-csv',
                        exportFunction: 'Timetracker.exportTimesheets',
                        gridPanel: this
                    }),
                    new Tine.widgets.grid.ExportButton({
                        text: this.app.i18n._('Export as ...'),
                        iconCls: 'tinebase-action-export-xls',
                        exportFunction: 'Timetracker.exportTimesheets',
                        showExportDialog: true,
                        gridPanel: this
                    })
                ]
            }
        });
        
        // register actions in updater
        this.actionUpdater.addActions([
            this.actions_exportTimesheet
        ]);
        
        Tine.Timetracker.TimesheetGridPanel.superclass.initActions.call(this);
    },

    /**
     * check user exportGrant for timeaccounts
     * NOTE: manage_timeaccounts ALWAYS allows to export
     *
     * @param action
     * @param grants
     * @param records
     * @returns {boolean}
     */
    updateExportAction: function(action, grants, records) {
        var exportGrant = true;
        if (! Tine.Tinebase.common.hasRight('manage', 'Timetracker', 'timeaccounts')) {
            Ext.each(records, function (record) {
                var c = record.get('timeaccount_id').container_id;
                if (c.hasOwnProperty('account_grants')) {
                    if (!c.account_grants.exportGrant) {
                        exportGrant = false;
                        return false;
                    }
                }
            });
        }

        var disable = ! exportGrant;
        action.setDisabled(disable);
        return false;
    },

    /**
     * add custom items to action toolbar
     * 
     * @return {Object}
     */
    getActionToolbarItems: function() {
        return [
            Ext.apply(new Ext.Button(this.actions_exportTimesheet), {
                scale: 'medium',
                rowspan: 2,
                iconAlign: 'top'
            })
        ];
    },
    
    /**
     * add custom items to context menu
     * 
     * @return {Array}
     */
    getContextMenuItems: function() {
        var items = [
            '-',
            this.actions_exportTimesheet
        ];
        
        return items;
    }
});
/**
 * Tine 2.0
 * 
 * @package     Timetracker
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2007-2016 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */
 
Ext.namespace('Tine.Timetracker');

/**
 * Timetracker Edit Dialog
 */
Tine.Timetracker.TimesheetEditDialog = Ext.extend(Tine.widgets.dialog.EditDialog, {

    /**
     * @private
     */
    windowNamePrefix: 'TimesheetEditWindow_',
    appName: 'Timetracker',
    recordClass: Tine.Timetracker.Model.Timesheet,
    recordProxy: Tine.Timetracker.timesheetBackend,
    tbarItems: null,
    evalGrants: false,
    useInvoice: false,
    displayNotes: true,
    context: { 'skipClosedCheck': false },

    windowWidth: 800,
    windowHeight: 500,

    
    /**
     * overwrite update toolbars function (we don't have record grants yet)
     */
    updateToolbars: function(record) {
        this.onTimeaccountUpdate();
        Tine.Timetracker.TimesheetEditDialog.superclass.updateToolbars.call(this, record, 'timeaccount_id');
    },

    onRecordLoad: function() {
        // interrupt process flow until dialog is rendered
        if (! this.rendered) {
            this.onRecordLoad.defer(250, this);
            return;
        }

        if (! this.record.id) {
            // @todo: this should be handled by default values
            this.record.set('account_id', Tine.Tinebase.registry.get('currentAccount'));
            this.record.set('start_date', new Date());
        }

        Tine.Timetracker.TimesheetEditDialog.superclass.onRecordLoad.call(this);

        // TODO get timeaccount from filter if set
        var timeaccount = this.record.get('timeaccount_id');
        if (timeaccount) {
            this.onTimeaccountUpdate(null, new Tine.Timetracker.Model.Timeaccount(timeaccount));
        }
    },

    /**
     * this gets called when initializing and if a new timeaccount is chosen
     * 
     * @param {} field
     * @param {} timeaccount
     */
    onTimeaccountUpdate: function(field, timeaccount) {
        // check for manage_timeaccounts right
        var manageRight = Tine.Tinebase.common.hasRight('manage', 'Timetracker', 'timeaccounts');
        
        var notBillable = false;
        var notClearable = false;

        // TODO timeaccount.get('account_grants') contains [Object object] -> why is that so? this should be fixed
        var grants = this.record.get('timeaccount_id')
            ? this.record.get('timeaccount_id').account_grants
            : (timeaccount && timeaccount.get('container_id') && timeaccount.get('container_id').account_grants
                ? timeaccount.get('container_id').account_grants
                :  {});
        
        if (grants) {
            var setDisabled = ! (grants.bookAllGrant || grants.adminGrant || manageRight);
            var accountField = this.getForm().findField('account_id');
            accountField.setDisabled(setDisabled);
            // set account id to the current user, if he doesn't have the right to edit other users timesheets
            if (setDisabled) {
                if (this.copyRecord && (this.record.get('account_id') != Tine.Tinebase.registry.get('currentAccount').accountId)) {
                    accountField.setValue(Tine.Tinebase.registry.get('currentAccount'));
                }
            }
            notBillable = ! (grants.manageBillableGrant || grants.adminGrant || manageRight);
            notClearable = ! (grants.adminGrant || manageRight);
            this.getForm().findField('billed_in').setDisabled(! (grants.adminGrant || manageRight));
        }

        if (timeaccount) {
            notBillable = notBillable || timeaccount.data.is_billable == "0" || timeaccount.get('is_billable') == "0";
            
            // clearable depends on timeaccount is_billable as well (changed by ps / 2009-09-01, behaviour was inconsistent)
            notClearable = notClearable || timeaccount.data.is_billable == "0" || timeaccount.get('is_billable') == "0";
        }
        
        this.getForm().findField('is_billable').setDisabled(notBillable);
        this.getForm().findField('is_cleared').setDisabled(notClearable);
        
        if (this.record.id == 0 && timeaccount) {
            // set is_billable for new records according to the timeaccount setting
            this.getForm().findField('is_billable').setValue(timeaccount.data.is_billable);
        }
    },
    
    /**
     * Always set is_billable if timeaccount is billable. This is needed for copied sheets where the
     * original is set to not billable
     */
    onAfterRecordLoad: function() {
        Tine.Timetracker.TimesheetEditDialog.superclass.onAfterRecordLoad.call(this);
        if (this.record.id == 0 && this.record.get('timeaccount_id') && this.record.get('timeaccount_id').is_billable) {
            this.getForm().findField('is_billable').setValue(this.record.get('timeaccount_id').is_billable);
        }
    },

    /**
     * this gets called when initializing and if cleared checkbox is changed
     *
     * @param {} field
     * @param {} newValue
     *
     * @todo    add prompt later?
     */
    onClearedUpdate: function(field, checked) {
        if (!this.useMultiple) {
            this.getForm().findField('billed_in').setDisabled(! checked);
        }
    },

    initComponent: function() {
        var salesApp = Tine.Tinebase.appMgr.get('Sales');
        this.useInvoice = Tine.Tinebase.appMgr.get('Sales')
            && salesApp.featureEnabled('invoicesModule')
            && Tine.Tinebase.common.hasRight('manage', 'Sales', 'invoices')
            && Tine.Sales.Model.Invoice;
        
        Tine.Timetracker.TimesheetEditDialog.superclass.initComponent.call(this);
    },

    /**
     * overwrites the isValid method on multipleEdit
     */
    isMultipleValid: function() {
        var valid = true;
        var keys = ['timeaccount_id', 'description', 'account_id'];
        Ext.each(keys, function(key) {
            var field = this.getForm().findField(key);
            if(field.edited && ! field.validate()) {
                field.markInvalid();
                valid = false;
            }
        }, this);
        return valid;
    },

    /**
     * returns dialog
     * 
     * NOTE: when this method gets called, all initialization is done.
     */
    getFormItems: function() {
        var lastRow = [new Tine.Addressbook.SearchCombo({
            allowBlank: false,
            forceSelection: true,
            columnWidth: 1,
            disabled: true,
            useAccountRecord: true,
            userOnly: true,
            nameField: 'n_fileas',
            fieldLabel: this.app.i18n._('Account'),
            name: 'account_id'
        }), {
            columnWidth: .25,
            disabled: (this.useMultiple) ? false : true,
            boxLabel: this.app.i18n._('Billable'),
            name: 'is_billable',
            xtype: 'checkbox'
        }, {
            columnWidth: .25,
            disabled: (this.useMultiple) ? false : true,
            boxLabel: this.app.i18n._('Cleared'),
            name: 'is_cleared',
            xtype: 'checkbox',
            listeners: {
                scope: this,
                check: this.onClearedUpdate
            }
        }, {
                columnWidth: .5,
                disabled: true,
                fieldLabel: this.app.i18n._('Cleared In'),
                name: 'billed_in'
            }];
        
        if (this.useInvoice) {
            lastRow.push(Tine.widgets.form.RecordPickerManager.get('Sales', 'Invoice', {
                columnWidth: .5,
                disabled: true,
                fieldLabel: this.app.i18n._('Invoice'),
                name: 'invoice_id'
            }));
        }
        
        
        return {
            xtype: 'tabpanel',
            border: false,
            plain:true,
            activeTab: 0,
            plugins: [{
                ptype : 'ux.tabpanelkeyplugin'
            }],
            defaults: {
                hideMode: 'offsets'
            },
            items:[
                {
                title: this.app.i18n.ngettext('Timesheet', 'Timesheets', 1),
                autoScroll: true,
                border: false,
                frame: true,
                layout: 'border',
                items: [{
                    region: 'center',
                    xtype: 'columnform',
                    labelAlign: 'top',
                    formDefaults: {
                        xtype:'textfield',
                        anchor: '100%',
                        labelSeparator: '',
                        columnWidth: .333
                    },
                    items: [[Tine.widgets.form.RecordPickerManager.get('Timetracker', 'Timeaccount', {
                        columnWidth: 1,
                        fieldLabel: this.app.i18n.ngettext('Time Account', 'Time Accounts', 1),
                        emptyText: this.app.i18n._('Select Time Account...'),
                        allowBlank: false,
                        forceSelection: true,
                        name: 'timeaccount_id',
                        lazyInit: false,
                        listeners: {
                            scope: this,
                            render: function(field){
                                if(!this.useMultiple) {
                                    field.focus(false, 250);
                                }
                            },
                            select: this.onTimeaccountUpdate
                        }
                    })], [{
                        fieldLabel: this.app.i18n._('Duration'),
                        name: 'duration',
                        allowBlank: false,
                        xtype: 'tinedurationspinner'
                        }, {
                        fieldLabel: this.app.i18n._('Date'),
                        name: 'start_date',
                        allowBlank: false,
                        xtype: 'datefield'
                        }, {
                        fieldLabel: this.app.i18n._('Start'),
                        emptyText: this.app.i18n._('not set'),
                        name: 'start_time',
                        xtype: 'timefield'
                    }], [{
                        columnWidth: 1,
                        fieldLabel: this.app.i18n._('Description'),
                        emptyText: this.app.i18n._('Enter description...'),
                        name: 'description',
                        allowBlank: false,
                        xtype: 'textarea',
                        height: 150
                    }], lastRow] 
                }, {
                    // activities and tags
                    layout: 'accordion',
                    animate: true,
                    region: 'east',
                    width: 210,
                    split: true,
                    collapsible: true,
                    collapseMode: 'mini',
                    header: false,
                    margins: '0 5 0 5',
                    border: true,
                    items: [
                        new Tine.widgets.tags.TagPanel({
                            app: 'Timetracker',
                            border: false,
                            bodyStyle: 'border:1px solid #B5B8C8;'
                        })
                    ]
                }]
            }, new Tine.widgets.activities.ActivitiesTabPanel({
                app: this.appName,
                record_id: (! this.copyRecord) ? this.record.id : null,
                record_model: this.appName + '_Model_' + this.recordClass.getMeta('modelName')
            })]
        };
    },
    
    /**
     * returns additional save params
     *
     * @returns {{checkBusyConflicts: boolean}}
     */
    getAdditionalSaveParams: function() {
        return {
            context: this.context
        };
    },
    
    /**
     * show error if request fails
     * 
     * @param {} response
     * @param {} request
     * @private
     */
    onRequestFailed: function(response, request) {
        this.saving = false;
        
        if (response.code && response.code == 902) {
            // deadline exception
            Ext.MessageBox.alert(
                this.app.i18n._('Failed'), 
                String.format(this.app.i18n._('Could not save {0}.'), this.i18nRecordName) 
                    + ' ( ' + this.app.i18n._('Booking deadline for this Timeaccount has been exceeded.') /* + ' ' + response.message  */ + ')'
            );
        } else if (response.code && response.code == 444) {
            //Time Account is closed
            if(Tine.Tinebase.common.hasRight('manage', 'Timetracker', 'timeaccounts')) {
                this.onClosedWarning.apply(this, arguments);
            } else {
                Ext.MessageBox.alert(
                    this.app.i18n._('Closed Timeaccount Warning!'), 
                    String.format(this.app.i18n._('The selected Time Account is already closed.'))
                );
            }
        } else {
            // call default exception handler
            Tine.Tinebase.ExceptionHandler.handleRequestException(response);
        }
        this.loadMask.hide();
    },
    
    onClosedWarning: function() {
        Ext.Msg.confirm(this.app.i18n._('Closed Timeaccount Warning!'),
            this.app.i18n._('The selected Time Account is already closed. Do you wish to continue anyway?'),
            function(btn) {
                if (btn == 'yes') {
                    this.context = { 'skipClosedCheck': true };
                    this.onApplyChanges(true);
                }
            }, this);
    }
});
