← Back to team overview

openerp-community-reviewer team mailing list archive

[Merge] lp:~dreis-pt/project-service/7.0-project_sla-dr into lp:project-service

 

Daniel Reis has proposed merging lp:~dreis-pt/project-service/7.0-project_sla-dr into lp:project-service.

Requested reviews:
  Project Core Editors (project-core-editors)

For more details, see:
https://code.launchpad.net/~dreis-pt/project-service/7.0-project_sla-dr/+merge/196960

Service Level Agreements for Contracts and Project Issues
-- 
https://code.launchpad.net/~dreis-pt/project-service/7.0-project_sla-dr/+merge/196960
Your team Project Core Editors is requested to review the proposed merge of lp:~dreis-pt/project-service/7.0-project_sla-dr into lp:project-service.
=== added directory 'project_sla'
=== added file 'project_sla/__init__.py'
--- project_sla/__init__.py	1970-01-01 00:00:00 +0000
+++ project_sla/__init__.py	2013-11-27 18:10:23 +0000
@@ -0,0 +1,4 @@
+# -*- coding: utf-8 -*-
+import project_sla
+import analytic_account
+import project_issue

=== added file 'project_sla/__openerp__.py'
--- project_sla/__openerp__.py	1970-01-01 00:00:00 +0000
+++ project_sla/__openerp__.py	2013-11-27 18:10:23 +0000
@@ -0,0 +1,68 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    Copyright (C) 2013 Daniel Reis
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+{
+    'name': 'Service Level Agreements',
+    'summary': 'Define SLAs for your Contracts',
+    'version': '1.0',
+    "category": "Project Management",
+    'description': """\
+SLA Defintions are setup from the Analytic Account form, SLA Definition field.
+Also adds SLA control to Project Issue documents.
+
+Each definition can have one or more Rules.
+The rule to apply for a particular document is decided using by a condition.
+In the simplest cases, you would have a single rule with no condition.
+
+An SLA Rule defines the working hours interval from creation date to limit
+date. It also defines a warning date, that can be used to generate alerts
+before the limit date.
+
+The working calendar used is set on the Project definitions, Other Info tab.
+If none is defined, working days from 8-12 13-17 are used.
+For time calculations, also uses the assigned user's (`user_id`) timezone and
+leaves.
+
+
+Configuration steps summary:
+
+  * Set Project's Working Calendar, at Project definitions, "Other Info" tab
+  * Set Project's SLA, in it's Analytic Account form
+  * See Project Issue's calculated SLA in the "Extra Info" tab
+
+Reports or alerts, such as emails or escalation procedures, can then be
+icreated based on the additional SLA information added to Project Issues
+or other models using SLA Control.
+
+(Icon by David Vignoni, Nuvola icon theme for KDE 3.x)
+""",
+    'author': 'Daniel Reis',
+    'website': '',
+    'depends': [
+        'project_issue',
+    ],
+    'data': [
+        'project_sla_view.xml',
+        'analytic_account_view.xml',
+        'project_view.xml',
+        'project_issue_view.xml',
+        'security/ir.model.access.csv',
+    ],
+    'installable': True,
+}

=== added file 'project_sla/analytic_account.py'
--- project_sla/analytic_account.py	1970-01-01 00:00:00 +0000
+++ project_sla/analytic_account.py	2013-11-27 18:10:23 +0000
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    Copyright (C) 2013 Daniel Reis
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+from openerp.osv import fields, orm
+
+
+class AnalyticAccount(orm.Model):
+    """Add SLA to Analytic Accounts"""
+    _inherit = 'account.analytic.account'
+    _columns = {
+        'sla_id': fields.many2one('project.sla', 'Service Level Agreement'),
+        }

=== added file 'project_sla/analytic_account_view.xml'
--- project_sla/analytic_account_view.xml	1970-01-01 00:00:00 +0000
+++ project_sla/analytic_account_view.xml	2013-11-27 18:10:23 +0000
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+
+        <record id="view_account_analytic_account_form_sla" model="ir.ui.view">
+            <field name="name">view_account_analytic_account_form_sla</field>
+            <field name="model">account.analytic.account</field>
+            <field name="inherit_id" ref="analytic.view_account_analytic_account_form"/>
+            <field name="arch" type="xml">
+
+                <field name="description" position="before">
+                  <group>
+                    <field name="sla_id"/>
+                  </group>
+                </field>
+
+           </field>
+        </record>
+
+    </data>
+</openerp>

=== added directory 'project_sla/i18n'
=== added file 'project_sla/i18n/project_sla.pot'
--- project_sla/i18n/project_sla.pot	1970-01-01 00:00:00 +0000
+++ project_sla/i18n/project_sla.pot	2013-11-27 18:10:23 +0000
@@ -0,0 +1,138 @@
+# Translation of OpenERP Server.
+# This file contains the translation of the following modules:
+#	* project_sla
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: OpenERP Server 7.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-11-27 17:50+0000\n"
+"PO-Revision-Date: 2013-11-27 17:50+0000\n"
+"Last-Translator: <>\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: \n"
+
+#. module: project_sla
+#: model:ir.model,name:project_sla.model_project_issue
+msgid "Project Issue"
+msgstr ""
+
+#. module: project_sla
+#: field:project.sla.line,limit_qty:0
+msgid "Limit threshold"
+msgstr ""
+
+#. module: project_sla
+#: view:project.project:0
+msgid "Administration"
+msgstr ""
+
+#. module: project_sla
+#: view:project.sla:0
+#: field:project.sla.line,sla_id:0
+msgid "SLA Definition"
+msgstr ""
+
+#. module: project_sla
+#: field:project.sla,model_id:0
+msgid "For model"
+msgstr ""
+
+#. module: project_sla
+#: field:project.issue,sla_line_id:0
+#: field:project.sla.controlled,sla_line_id:0
+msgid "SLA Rule"
+msgstr ""
+
+#. module: project_sla
+#: view:project.sla:0
+msgid "Reapply SLAs"
+msgstr ""
+
+#. module: project_sla
+#: view:project.sla:0
+msgid "Rules"
+msgstr ""
+
+#. module: project_sla
+#: field:project.sla,name:0
+#: field:project.sla.line,name:0
+msgid "Title"
+msgstr ""
+
+#. module: project_sla
+#: model:ir.model,name:project_sla.model_project_sla_controlled
+msgid "SLA Controlled Document"
+msgstr ""
+
+#. module: project_sla
+#: field:project.sla.line,condition:0
+msgid "Condition"
+msgstr ""
+
+#. module: project_sla
+#: help:project.sla.line,condition:0
+msgid "Apply only if this expression is evaluated to True. The document can be accesses using either `o`, `obj` or `object`. Example: `obj.priority <= '2'`"
+msgstr ""
+
+#. module: project_sla
+#: field:project.issue,sla_warn_date:0
+#: field:project.sla.controlled,sla_warn_date:0
+msgid "SLA Warning Date"
+msgstr ""
+
+#. module: project_sla
+#: field:project.sla,active:0
+msgid "Active"
+msgstr ""
+
+#. module: project_sla
+#: field:project.sla,sla_line_ids:0
+#: view:project.sla.line:0
+msgid "Definitions"
+msgstr ""
+
+#. module: project_sla
+#: help:project.sla,model_id:0
+msgid "Only this model will be affected by the recalculation button. The model should have a 'state' field."
+msgstr ""
+
+#. module: project_sla
+#: model:ir.model,name:project_sla.model_project_sla_line
+msgid "project.sla.line"
+msgstr ""
+
+#. module: project_sla
+#: model:ir.model,name:project_sla.model_project_sla
+msgid "project.sla"
+msgstr ""
+
+#. module: project_sla
+#: field:project.sla.line,sequence:0
+msgid "Sequence"
+msgstr ""
+
+#. module: project_sla
+#: field:account.analytic.account,sla_id:0
+msgid "Service Level Agreement"
+msgstr ""
+
+#. module: project_sla
+#: field:project.issue,sla_limit_date:0
+#: field:project.sla.controlled,sla_limit_date:0
+msgid "SLA Limit Date"
+msgstr ""
+
+#. module: project_sla
+#: model:ir.model,name:project_sla.model_account_analytic_account
+msgid "Analytic Account"
+msgstr ""
+
+#. module: project_sla
+#: field:project.sla.line,warn_qty:0
+msgid "Warning threshold"
+msgstr ""
+

=== added file 'project_sla/project_issue.py'
--- project_sla/project_issue.py	1970-01-01 00:00:00 +0000
+++ project_sla/project_issue.py	2013-11-27 18:10:23 +0000
@@ -0,0 +1,68 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    Copyright (C) 2013 Daniel Reis
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+from openerp.osv import fields, orm
+
+
+class ProjectIssue(orm.Model):
+    _name = 'project.issue'
+    _inherit = ['project.issue', 'project.sla.controlled']
+
+    #def _compute_sla(self, cr, uid, ids, fields, args, context=None):
+    #    """
+    #    Compute Issue's SLA function fields
+    #    """
+    #    # print '_compute_sla:', ids, fields, args, context
+    #    return self.pool.get('project.sla').compute_sla_fields(
+    #        cr, uid, ids, 'project.issue', context)
+
+    #def _get_ids(self, cr, uid, ids, context=None):
+    #    """ Avoid the use of a longer lambda expression """
+    #    return ids
+
+    #def _get_ids_slaline(self, cr, uid, sla_line_ids, context=None):
+    #    """
+    #    Return IDs for Issues to recalc because of SLA Line changes
+    #    """
+    #    return self.pool.get('project.issue').search(
+    #        cr, uid, [('sla_line_id', 'in', sla_line_ids)], context=context)
+
+    #_columns = {
+    #    'sla_line_id': fields.function(
+    #        _compute_sla, string='SLA Rule', type='many2one',
+    #        relation='project.sla.line', multi='SLA', store={
+    #            'project.issue': (_get_ids, ['write_date'], 10)}),
+    #    'sla_warn_date': fields.function(
+    #        _compute_sla, string='SLA Warning Date', type='datetime',
+    #        multi='SLA', store={
+    #            'project.issue': (
+    #                _get_ids, ['write_date'], 20),
+    #            'project.sla.lines': (
+    #                _get_ids_slaline, ['limit_qty', 'warn_qty', 'sla_id'], 20),
+    #        }),
+    #    'sla_limit_date': fields.function(
+    #        _compute_sla, string='SLA Limit  Date', type='datetime',
+    #        multi='SLA', store={
+    #            'project.issue': (
+    #                _get_ids, ['write_date'], 20),
+    #            'project.sla.lines': (
+    #                _get_ids_slaline, ['limit_qty', 'warn_qty', 'sla_id'], 20),
+    #        }),
+    #    }

=== added file 'project_sla/project_issue_view.xml'
--- project_sla/project_issue_view.xml	1970-01-01 00:00:00 +0000
+++ project_sla/project_issue_view.xml	2013-11-27 18:10:23 +0000
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+
+        <record id="project_issue_form_view_sla" model="ir.ui.view">
+            <field name="name">project_issue_form_view_sla</field>
+            <field name="model">project.issue</field>
+            <field name="inherit_id" ref="project_issue.project_issue_form_view"/>
+            <field name="arch" type="xml">
+
+                <field name="days_since_creation" position="after">
+                    <field name="sla_line_id"/>
+                    <field name="sla_warn_date"
+                           attrs="{'invisible': [('sla_line_id','=',False)]}"/>
+                    <field name="sla_limit_date"
+                           attrs="{'invisible': [('sla_line_id','=',False)]}"/>
+                </field>
+
+           </field>
+        </record>
+
+    </data>
+</openerp>

=== added file 'project_sla/project_sla.py'
--- project_sla/project_sla.py	1970-01-01 00:00:00 +0000
+++ project_sla/project_sla.py	2013-11-27 18:10:23 +0000
@@ -0,0 +1,217 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    Copyright (C) 2013 Daniel Reis
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+from openerp.osv import fields, orm
+from openerp.tools.safe_eval import safe_eval
+from openerp.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT as DT_FMT
+from datetime import datetime, timedelta
+
+
+def safe_getattr(obj, dotattr, default=False):
+    """
+    Follow an object attribute dot-notation chain to find the leaf value.
+    If any attribute doesn't exist or has no value, just return False.
+    Checks hasattr ahead, to avoid ORM browse warnings.
+    """
+    attrs = dotattr.split('.')
+    while attrs:
+        attr = attrs.pop(0)
+        if not hasattr(obj, attr):
+            return default
+        else:
+            try:
+                obj = getattr(obj, attr)
+            except AttributeError:
+                return default
+    return obj
+
+
+class SLADefinition(orm.Model):
+    """
+    SLA Definition
+    """
+    _name = 'project.sla'
+    _columns = {
+        'name': fields.char('Title', size=64, required=True, translate=True),
+        'model_id': fields.many2one(
+            'ir.model', 'For model', required=1, help="Only this model will "
+            "be affected by the recalculation button. The model should have "
+            "a 'state' field."),
+        'active': fields.boolean('Active'),
+        'sla_line_ids': fields.one2many(
+            'project.sla.line', 'sla_id', 'Definitions'),
+        }
+    _defaults = {
+        'active': True,
+        }
+
+    def _compute_sla_date(self, cr, uid, working_hours, res_uid,
+                          start_date, hours, context=None):
+        """
+        Return a limit datetime by adding hours to a start_date, honoring
+        a working_time calendar and a resource's (res_uid) timezone and
+        availability (leaves)
+
+        Currently implemented using a binary search using
+        _interval_hours_get() from resource.calendar. This is
+        resource.calendar agnostic, but could be more efficient if
+        implemented based on it's logic.
+
+        Known issue: the end date can be a non-working time; it would be
+        best for it to be the latest working time possible. Example:
+        if working time is 08:00 - 16:00 and start_date is 19:00, the +8h
+        end date will be 19:00 of the next day, and it should rather be
+        16:00 of the next day.
+        """
+        assert isinstance(start_date, datetime)
+        assert isinstance(hours, int) and hours >= 0
+
+        cal_obj = self.pool.get('resource.calendar')
+        target, step = hours * 3600,  16 * 3600
+        lo, hi = start_date, start_date
+        while target > 0 and step > 60:
+            hi = lo + timedelta(seconds=step)
+            check = int(3600 * cal_obj._interval_hours_get(
+                cr, uid, working_hours, lo, hi,
+                timezone_from_uid=res_uid, exclude_leaves=False,
+                context=context))
+            if check <= target:
+                target -= check
+                lo = hi
+            else:
+                step = int(step / 4.0)
+        return hi
+
+    def compute_sla(self, cr, uid, sla_id, browse_doc, worktime, dt_start,
+                    res_uid,  context=None):
+        """
+        Computes SLA dates given the necessary information:
+            * browse_doc: used for rule conditions evaluation
+            * worktime: a resource.calendar id
+            * dt_start: a datetime to star counting the SLA dates
+            * res_uid: a resource user id to get timezone and leaves
+        Returns a dict with the camputed fields.
+        """
+        assert isinstance(sla_id, int)
+        assert isinstance(dt_start, datetime)
+
+        for l in self.browse(cr, uid, sla_id, context=context).sla_line_ids:
+            eval_ctx = {k: browse_doc for k in ['object', 'obj', 'o']}
+            if not l.condition or safe_eval(l.condition, eval_ctx):
+                warn_date = self._compute_sla_date(
+                    cr, uid, worktime, res_uid, dt_start,
+                    l.warn_qty, context=context)
+                limit_date = self._compute_sla_date(
+                    cr, uid,  worktime, res_uid, warn_date,
+                    l.limit_qty - l.warn_qty, context=context)
+                return {
+                    'sla_line_id': l.id,
+                    'sla_warn_date': datetime.strftime(warn_date, DT_FMT),
+                    'sla_limit_date': datetime.strftime(limit_date, DT_FMT)}
+        return False
+
+    def reapply_slas(self, cr, uid, ids, context=None):
+        """
+        Force SLA recalculation on open  documents that already are subject to
+        this SLA Definition.
+        To use after SLA Definition rule modification.
+        """
+        for sla in self.browse(cr, uid, ids, context=context):
+            model = self.pool.get(sla.model_id.model)
+            query = [('state', 'not in', ['done', 'cancelled']),
+                     ('sla_line_id', 'in', [x.id for x in sla.sla_line_ids])]
+            docs = model.search(cr, uid, query, context=context)
+            # innocuous write to force stored fields recalculation
+            model.write(cr, uid, docs, {'active': 1}, context=context)
+        return True
+
+
+class SLARules(orm.Model):
+    """ SLA Definition Rule Lines """
+    _name = 'project.sla.line'
+    _order = 'sequence'
+    _columns = {
+        'name': fields.char('Title', size=64, required=True, translate=True),
+        'sla_id': fields.many2one('project.sla', 'SLA Definition'),
+        'sequence': fields.integer('Sequence'),
+        'limit_qty': fields.integer('Limit threshold'),
+        'warn_qty': fields.integer('Warning threshold'),
+        'condition': fields.char(
+            'Condition', size=256, help="Apply only if this expression is "
+            "evaluated to True. The document can be accesses using either "
+            "`o`, `obj` or `object`. Example: `obj.priority <= '2'`"),
+        }
+    _defaults = {
+        'sequence': 10,
+        }
+
+
+class SLAControlled(orm.AbstractModel):
+    """
+    SLA Controlled documents:
+    AbstractModel to easilly apply SLA Control on a model.
+    """
+    _name = 'project.sla.controlled'
+    _description = 'SLA Controlled Documents'
+
+    def _sla_funcflds(self, cr, uid, ids, fields, args, context=None):
+        """
+        Computes SLA for a list of documents and returns a dict with the
+        structure expected for funtion fields:
+        {id0: {'sla_line_id': ..., 'sla_warn_date': ..., 'sla_warn_date': ...},
+         ...}
+        The SLA used is either from a related analytic_account_id or
+        project_id, whatever is found first.
+        """
+        sla_model = self.pool.get('project.sla')
+        res = {}
+        for doc in self.browse(cr, uid, ids, context=context):
+            # Find document's SLA Id
+            sla_id = (safe_getattr(doc, 'analytic_account_id.sla_id.id') or
+                      safe_getattr(doc, 'project_id.sla_id.id'))
+            if not sla_id:
+                res[doc.id] = {
+                    'sla_line_id': None,
+                    'sla_warn_date': None,
+                    'sla_limit_date': None}
+            else:
+                # Compute document's SLA
+                # if the working hours on the project are not defined, use
+                # default ones (8 -> 12 and 13 -> 17 * 5), represented by None
+                worktime = safe_getattr(
+                    doc, 'project_id.resource_calendar_id.id')
+                dt_start = datetime.strptime(doc.create_date, DT_FMT)
+                res[doc.id] = sla_model.compute_sla(
+                    cr, uid, sla_id, doc,
+                    worktime, dt_start, doc.user_id.id or uid,
+                    context=context)
+        return res
+
+    _columns = {
+        'sla_line_id': fields.function(
+            _sla_funcflds, string='SLA Rule', type='many2one',
+            relation='project.sla.line', multi='SLA', store=True),
+        'sla_warn_date': fields.function(
+            _sla_funcflds, string='SLA Warning Date', type='datetime',
+            multi='SLA', store=True),
+        'sla_limit_date': fields.function(
+            _sla_funcflds, string='SLA Limit Date', type='datetime',
+            multi='SLA', store=True),
+        }

=== added file 'project_sla/project_sla_view.xml'
--- project_sla/project_sla_view.xml	1970-01-01 00:00:00 +0000
+++ project_sla/project_sla_view.xml	2013-11-27 18:10:23 +0000
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+
+        <record id="view_sla_lines_tree" model="ir.ui.view">
+            <field name="name">view_sla_lines_tree</field>
+            <field name="model">project.sla.line</field>
+            <field name="arch" type="xml">
+
+                <tree string="Definitions">
+                    <field name="sequence"/>
+                    <field name="name"/>
+                    <field name="condition"/>
+                    <field name="limit_qty"/>
+                    <field name="warn_qty"/>
+                </tree>
+
+            </field>
+        </record>
+
+        <record id="view_sla_form" model="ir.ui.view">
+            <field name="name">view_sla_form</field>
+            <field name="model">project.sla</field>
+            <field name="arch" type="xml">
+
+                <form string="SLA Definition">
+                  <field name="name"/>
+                  <field name="active"/>
+                  <field name="model_id"/>
+                  <button name="reapply_slas" string="Reapply SLAs"
+                          type="object" />
+                  <notebook colspan="4">
+                    <page string="Rules">
+                      <field name="sla_line_ids" nolabel="1"/>
+                    </page>
+                  </notebook>
+                </form>
+
+            </field>
+        </record>
+
+    </data>
+</openerp>

=== added file 'project_sla/project_view.xml'
--- project_sla/project_view.xml	1970-01-01 00:00:00 +0000
+++ project_sla/project_view.xml	2013-11-27 18:10:23 +0000
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+
+        <record id="edit_project_sla" model="ir.ui.view">
+            <field name="name">edit_projec_sla</field>
+            <field name="model">project.project</field>
+            <field name="inherit_id" ref="project.edit_project"/>
+            <field name="arch" type="xml">
+
+                <!-- make resource calendar always visible -->
+                <group string="Administration" position="attributes">
+                    <attribute name="groups"/>
+                </group>
+
+           </field>
+        </record>
+
+    </data>
+</openerp>

=== added directory 'project_sla/security'
=== added file 'project_sla/security/ir.model.access.csv'
--- project_sla/security/ir.model.access.csv	1970-01-01 00:00:00 +0000
+++ project_sla/security/ir.model.access.csv	2013-11-27 18:10:23 +0000
@@ -0,0 +1,5 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_sla_manager,access_sla_manager,model_project_sla,project.group_project_manager,1,1,1,1
+access_sla_user,access_sla_user,model_project_sla,base.group_user,1,0,0,0
+access_sla_lines_manager,access_sla_lines_manager,model_project_sla_line,project.group_project_manager,1,1,1,1
+access_sla_lines_user,access_sla_lines_user,model_project_sla_line,base.group_user,1,0,0,0

=== added directory 'project_sla/static'
=== added directory 'project_sla/static/src'
=== added directory 'project_sla/static/src/img'
=== added file 'project_sla/static/src/img/icon.png'
Binary files project_sla/static/src/img/icon.png	1970-01-01 00:00:00 +0000 and project_sla/static/src/img/icon.png	2013-11-27 18:10:23 +0000 differ