<commit_before># coding: utf-8
"""
    A parser for the genie (Age of Empires, ...) DRS container file format.

    Based on the excellent documentation at http://artho.com/age/files/drs.html -
    thank you!
"""

import struct

import construct as cons

class TableAdapter(cons.Adapter):
    def _decode(self, obj, context):
        return Table(context['_']['drs_file'],
                        obj['resource_type'],
                        obj['offset'],
                        obj['number_of_files'],
                        dict((f.resource_id, f) for f in obj['embedded_files']))

EMBEDDED_FILE = cons.Struct('embedded_files',
    cons.ULInt32('resource_id'),
    cons.ULInt32('offset'),
    cons.ULInt32('size'),
#    cons.OnDemand(
#        cons.Pointer(lambda ctx: ctx['offset'],
#            cons.MetaField('data', lambda ctx: ctx['size'])
#        )
#    )
    # We're not parsing it on demand anymore cause we don't want
    # construct to keep a reference to the file stream forever.
)

TABLE = cons.Struct('tables',
    cons.ULInt32('resource_type'),
    cons.ULInt32('offset'),
    cons.ULInt32('number_of_files'),
    cons.Pointer(lambda ctx: ctx['offset'],
        cons.Array(lambda ctx: ctx['number_of_files'],
            EMBEDDED_FILE
        )
    )
)

AOE_HEADER = cons.Struct('header',
    cons.String('copyright', 40, padchar='\0'),
    cons.String('version', 4),
    cons.String('file_type', 12, padchar='\0'),
    cons.ULInt32('number_of_tables'),
    cons.ULInt32('offset'),
    cons.Array(lambda ctx: ctx['number_of_tables'], TableAdapter(TABLE)),
)

SWGB_HEADER = cons.Struct(
    cons.String('copyright', 60, padchar='\0'),
    cons.String('version', 4),
    cons.String('file_type', 12, padchar='\0'),
    cons.ULInt32('number_of_tables'),
    cons.ULInt32('offset'),
    cons.Array(lambda ctx: ctx['number_of_tables'], TableAdapter(TABLE)),
)

def get_file_extension(resource_type):
    """
        get the embedded file extension from the genie resource type (a 4-byte number).
    """
    return ''.join(reversed(struct.pack('=I', resource_type)[1:]))

class Table(object):
    """
        A DRS table. Holds multiple embedded files of the same type.

        Caution: The actual file data is read *lazily*. That means, they are
        construct `OnDemand` instances. You probably don't want *all*
        resource files to be read at once since they're just chilling in your
        memory then. If you want to access the actual data, use `get_data`;
        but of course you need to do it before closing the stream.

        If you really want to read all the embedded files into memory, use
        the `read_all` method.
    """
    def __init__(self, drs_file, resource_type, offset, number_of_files, embedded_files):
        self.drs_file = drs_file
        self.resource_type = resource_type
        self.offset = offset
        self.number_of_files = number_of_files
        self.embedded_files = embedded_files

    @property
    def file_extension(self):
        return get_file_extension(self.resource_type)

    def get_data(self, resource_id):
        """ get the binary data of a specific file. """
        stream = self.drs_file.stream
        embedded = self.embedded_files[resource_id]
        old_offset = stream.tell()
        stream.seek(embedded.offset)
        data = stream.read(embedded.size)
        stream.seek(old_offset)
        return data

    def read_all(self):
        """ read ALL THE THINGS """
        for f in self.embedded_files.itervalues():
            self.get_data(f.resource_id)

class DRSFile(object):
    """
        A representation of a DRS container file. Can read
        data from a stream, but be careful: The stream position
        WILL NEVER BE THE SAME AGAIN!!1

        Has multiple `Table` objects.
    """
    def __init__(self, stream):
        self.stream = stream
        pos = stream.tell()
        stream.seek(64)
        maybe_swgb_header = stream.read(4)
        stream.seek(pos)

        if maybe_swgb_header == "swbg":
            self.header = SWGB_HEADER._parse(stream, cons.Container(drs_file=self))
        else:
            self.header = AOE_HEADER._parse(stream, cons.Container(drs_file=self))

    @property
    def tables(self):
        return self.header.tables

    def get_data(self, resource_id):
        """
            get the binary data of a specific file. Raise KeyError
            if it couldn't be found.
        """
        for table in self.tables:
            try:
                return table.get_data(resource_id)
            except KeyError:
                pass
        raise KeyError(resource_id)

def get_all_files(stream):
    """
        get all embedded files from a DRS file. This yields
        (resource type, resource id, data) tuples.
    """
    drs = DRSFile(stream)
    for table in drs.tables:
        ext = table.file_extension
        for embedded in table.embedded_files.itervalues():
            yield (table.resource_type, embedded.resource_id, table.get_data(embedded.resource_id))

<commit_msg>Fix SWGB header parsing<commit_after># coding: utf-8
"""
    A parser for the genie (Age of Empires, ...) DRS container file format.

    Based on the excellent documentation at http://artho.com/age/files/drs.html -
    thank you!
"""

import struct

import construct as cons

class TableAdapter(cons.Adapter):
    def _decode(self, obj, context):
        return Table(context['_']['drs_file'],
                        obj['resource_type'],
                        obj['offset'],
                        obj['number_of_files'],
                        dict((f.resource_id, f) for f in obj['embedded_files']))

EMBEDDED_FILE = cons.Struct('embedded_files',
    cons.ULInt32('resource_id'),
    cons.ULInt32('offset'),
    cons.ULInt32('size'),
#    cons.OnDemand(
#        cons.Pointer(lambda ctx: ctx['offset'],
#            cons.MetaField('data', lambda ctx: ctx['size'])
#        )
#    )
    # We're not parsing it on demand anymore cause we don't want
    # construct to keep a reference to the file stream forever.
)

TABLE = cons.Struct('tables',
    cons.ULInt32('resource_type'),
    cons.ULInt32('offset'),
    cons.ULInt32('number_of_files'),
    cons.Pointer(lambda ctx: ctx['offset'],
        cons.Array(lambda ctx: ctx['number_of_files'],
            EMBEDDED_FILE
        )
    )
)

AOE_HEADER = cons.Struct('header',
    cons.String('copyright', 40, padchar='\0'),
    cons.String('version', 4),
    cons.String('file_type', 12, padchar='\0'),
    cons.ULInt32('number_of_tables'),
    cons.ULInt32('offset'),
    cons.Array(lambda ctx: ctx['number_of_tables'], TableAdapter(TABLE)),
)

SWGB_HEADER = cons.Struct('header',
    cons.String('copyright', 60, padchar='\0'),
    cons.String('version', 4),
    cons.String('file_type', 12, padchar='\0'),
    cons.ULInt32('number_of_tables'),
    cons.ULInt32('offset'),
    cons.Array(lambda ctx: ctx['number_of_tables'], TableAdapter(TABLE)),
)

def get_file_extension(resource_type):
    """
        get the embedded file extension from the genie resource type (a 4-byte number).
    """
    return ''.join(reversed(struct.pack('=I', resource_type)[1:]))

class Table(object):
    """
        A DRS table. Holds multiple embedded files of the same type.

        Caution: The actual file data is read *lazily*. That means, they are
        construct `OnDemand` instances. You probably don't want *all*
        resource files to be read at once since they're just chilling in your
        memory then. If you want to access the actual data, use `get_data`;
        but of course you need to do it before closing the stream.

        If you really want to read all the embedded files into memory, use
        the `read_all` method.
    """
    def __init__(self, drs_file, resource_type, offset, number_of_files, embedded_files):
        self.drs_file = drs_file
        self.resource_type = resource_type
        self.offset = offset
        self.number_of_files = number_of_files
        self.embedded_files = embedded_files

    @property
    def file_extension(self):
        return get_file_extension(self.resource_type)

    def get_data(self, resource_id):
        """ get the binary data of a specific file. """
        stream = self.drs_file.stream
        embedded = self.embedded_files[resource_id]
        old_offset = stream.tell()
        stream.seek(embedded.offset)
        data = stream.read(embedded.size)
        stream.seek(old_offset)
        return data

    def read_all(self):
        """ read ALL THE THINGS """
        for f in self.embedded_files.itervalues():
            self.get_data(f.resource_id)

class DRSFile(object):
    """
        A representation of a DRS container file. Can read
        data from a stream, but be careful: The stream position
        WILL NEVER BE THE SAME AGAIN!!1

        Has multiple `Table` objects.
    """
    def __init__(self, stream):
        self.stream = stream
        pos = stream.tell()
        stream.seek(64, 1)
        maybe_swgb_header = stream.read(4)
        stream.seek(pos)

        if maybe_swgb_header == "swbg":
            self.header = SWGB_HEADER._parse(stream, cons.Container(drs_file=self))
        else:
            self.header = AOE_HEADER._parse(stream, cons.Container(drs_file=self))

    @property
    def tables(self):
        return self.header.tables

    def get_data(self, resource_id):
        """
            get the binary data of a specific file. Raise KeyError
            if it couldn't be found.
        """
        for table in self.tables:
            try:
                return table.get_data(resource_id)
            except KeyError:
                pass
        raise KeyError(resource_id)

def get_all_files(stream):
    """
        get all embedded files from a DRS file. This yields
        (resource type, resource id, data) tuples.
    """
    drs = DRSFile(stream)
    for table in drs.tables:
        ext = table.file_extension
        for embedded in table.embedded_files.itervalues():
            yield (table.resource_type, embedded.resource_id, table.get_data(embedded.resource_id))

<|endoftext|>


<commit_before>import datetime
from django.http import JsonResponse
from django.shortcuts import get_object_or_404
from django.db.models import Q
from opencivicdata.core.models import Division

DATE_FORMAT = "%Y-%m-%d"

def division_list(request):
    today = datetime.datetime.strftime(datetime.datetime.now(), DATE_FORMAT)

    lat = request.GET.get('lat')
    lon = request.GET.get('lon')
    date = datetime.datetime.strptime(
        request.GET.get('date', today), DATE_FORMAT
    ).date()

    if not (lat and lon):
        return JsonResponse({"error": "Must provide lat & lon"}, status_code=400)

    divisions = Division.objects.filter(
        Q(geometries__boundary__set__start_date__lte=date) |
        Q(geometries__boundary__set__start_date=None),
        Q(geometries__boundary__set__end_date__gte=date) |
        Q(geometries__boundary__set__end_date=None),
        geometries__boundary__shape__contains='POINT({} {})'.format(lon, lat)
    )

    return JsonResponse({
        'results': [
            {'id': d.id} for d in divisions
        ]
    })

def serialize_geometry(geometry):
    return {
        'boundary_set': {
            'name': geometry.boundary.set.name,
            'start_date': geometry.boundary.set.start_date,
            'end_date': geometry.boundary.set.end_date,
        },
        'metadata': geometry.boundary.metadata,
        'extent': geometry.boundary.extent,
        'external_id': geometry.boundary.external_id,
        'name': geometry.boundary.name,
        'centroid': tuple(geometry.boundary.centroid),
    }

def division_detail(request, pk):
    division = get_object_or_404(Division, pk=pk)
    data = {
        'id': division.id,
        'country': division.country,
        'name': division.name,
        'geometries': [
            serialize_geometry(g) for g in
            division.geometries.all()
        ],
        # 'jurisdictions':
        # 'children':
    }
    return JsonResponse(data)
<commit_msg>add name to divisions response<commit_after>import datetime
from django.http import JsonResponse
from django.shortcuts import get_object_or_404
from django.db.models import Q
from opencivicdata.core.models import Division

DATE_FORMAT = "%Y-%m-%d"

def division_list(request):
    today = datetime.datetime.strftime(datetime.datetime.now(), DATE_FORMAT)

    lat = request.GET.get('lat')
    lon = request.GET.get('lon')
    date = datetime.datetime.strptime(
        request.GET.get('date', today), DATE_FORMAT
    ).date()

    if not (lat and lon):
        return JsonResponse({"error": "Must provide lat & lon"}, status_code=400)

    divisions = Division.objects.filter(
        Q(geometries__boundary__set__start_date__lte=date) |
        Q(geometries__boundary__set__start_date=None),
        Q(geometries__boundary__set__end_date__gte=date) |
        Q(geometries__boundary__set__end_date=None),
        geometries__boundary__shape__contains='POINT({} {})'.format(lon, lat)
    )
    divisions = divisions[:2]

    return JsonResponse({
        'results': [
            {'id': d.id,
             'name': d.name,
             } for d in divisions
        ]
    })

def serialize_geometry(geometry):
    return {
        'boundary_set': {
            'name': geometry.boundary.set.name,
            'start_date': geometry.boundary.set.start_date,
            'end_date': geometry.boundary.set.end_date,
        },
        'metadata': geometry.boundary.metadata,
        'extent': geometry.boundary.extent,
        'external_id': geometry.boundary.external_id,
        'name': geometry.boundary.name,
        'centroid': tuple(geometry.boundary.centroid),
    }

def division_detail(request, pk):
    division = get_object_or_404(Division, pk=pk)
    data = {
        'id': division.id,
        'country': division.country,
        'name': division.name,
        'geometries': [
            serialize_geometry(g) for g in
            division.geometries.all()
        ],
    }
    return JsonResponse(data)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
#
# This file is part of Invenio.
# Copyright (C) 2014, 2015 CERN.
#
# Invenio is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# Invenio 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
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Invenio; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.

"""Database models for OAuth2 server."""

from __future__ import absolute_import

from flask import current_app

from flask_login import current_user

from invenio_base.i18n import _
from invenio.config import SECRET_KEY as secret_key
from invenio_ext.login.legacy_user import UserInfo
from invenio_ext.sqlalchemy import db
from invenio_accounts.models import User

import six

from sqlalchemy_utils.types import URLType
from sqlalchemy_utils.types.encrypted import AesEngine, EncryptedType

from werkzeug.security import gen_salt

from wtforms import validators

from .errors import ScopeDoesNotExists
from .validators import validate_redirect_uri, validate_scopes

class NoneAesEngine(AesEngine):

    """Filter None values from encrypting."""

    def encrypt(self, value):
        """Encrypt a value on the way in."""
        if value is not None:
            return super(NoneAesEngine, self).encrypt(value)

    def decrypt(self, value):
        """Decrypt value on the way out."""
        if value is not None:
            return super(NoneAesEngine, self).decrypt(value)

class String255EncryptedType(EncryptedType):

    """String encrypted type."""

    impl = db.String(255)

class OAuthUserProxy(object):

    """Proxy object to an Invenio User."""

    def __init__(self, user):
        """Initialize proxy object with user instance."""
        self._user = user

    def __getattr__(self, name):
        """Pass any undefined attribute to the underlying object."""
        return getattr(self._user, name)

    def __getstate__(self):
        """Return the id."""
        return self.id

    def __setstate__(self, state):
        """Set user info."""
        self._user = UserInfo(state)

    @property
    def id(self):
        """Return user identifier."""
        return self._user.get_id()

    def check_password(self, password):
        """Check user password."""
        return self.password == password

    @classmethod
    def get_current_user(cls):
        """Return an instance of current user object."""
        return cls(current_user._get_current_object())

class Scope(object):

    """OAuth scope definition."""

    def __init__(self, id_, help_text='', group='', internal=False):
        """Initialize scope values."""
        self.id = id_
        self.group = group
        self.help_text = help_text
        self.is_internal = internal

class Client(db.Model):

    """A client is the app which want to use the resource of a user.

    It is suggested that the client is registered by a user on your site, but
    it is not required.

    The client should contain at least these information:

        client_id: A random string
        client_secret: A random string
        client_type: A string represents if it is confidential
        redirect_uris: A list of redirect uris
        default_redirect_uri: One of the redirect uris
        default_scopes: Default scopes of the client

    But it could be better, if you implemented:

        allowed_grant_types: A list of grant types
        allowed_response_types: A list of response types
        validate_scopes: A function to validate scopes
    """

    __tablename__ = 'oauth2CLIENT'

    name = db.Column(
        db.String(40),
        info=dict(
            label=_('Name'),
            description=_('Name of application (displayed to users).'),
            validators=[validators.DataRequired()]
        )
    )
    """Human readable name of the application."""

    description = db.Column(
        db.Text(),
        default=u'',
        info=dict(
            label=_('Description'),
            description=_('Optional. Description of the application'
                          ' (displayed to users).'),
        )
    )
    """Human readable description."""

    website = db.Column(
        URLType(),
        info=dict(
            label=_('Website URL'),
            description=_('URL of your application (displayed to users).'),
        ),
        default=u'',
    )

    user_id = db.Column(db.ForeignKey('user.id'), nullable=True)
    """Creator of the client application."""

    client_id = db.Column(db.String(255), primary_key=True)
    """Client application ID."""

    client_secret = db.Column(
        db.String(255), unique=True, index=True, nullable=False
    )
    """Client application secret."""

    is_confidential = db.Column(db.Boolean, default=True)
    """Determine if client application is public or not."""

    is_internal = db.Column(db.Boolean, default=False)
    """Determins if client application is an internal application."""

    _redirect_uris = db.Column(db.Text)
    """A newline-separated list of redirect URIs. First is the default URI."""

    _default_scopes = db.Column(db.Text)
    """A space-separated list of default scopes of the client.

    The value of the scope parameter is expressed as a list of space-delimited,
    case-sensitive strings.
    """

    user = db.relationship(
        User,
        backref=db.backref(
            "oauth2clients",
            cascade="all, delete-orphan",
        )
    )
    """Relationship to user."""

    @property
    def allowed_grant_types(self):
        """Return allowed grant types."""
        return current_app.config['OAUTH2_ALLOWED_GRANT_TYPES']

    @property
    def allowed_response_types(self):
        """Return allowed response types."""
        return current_app.config['OAUTH2_ALLOWED_RESPONSE_TYPES']

    # def validate_scopes(self, scopes):
    #     return self._validate_scopes

    @property
    def client_type(self):
        """Return client type."""
        if self.is_confidential:
            return 'confidential'
        return 'public'

    @property
    def redirect_uris(self):
        """Return redirect uris."""
        if self._redirect_uris:
            return self._redirect_uris.splitlines()
        return []

    @redirect_uris.setter
    def redirect_uris(self, value):
        """Validate and store redirect URIs for client."""
        if isinstance(value, six.text_type):
            value = value.split("\n")

        value = [v.strip() for v in value]

        for v in value:
            validate_redirect_uri(v)

        self._redirect_uris = "\n".join(value) or ""

    @property
    def default_redirect_uri(self):
        """Return default redirect uri."""
        try:
            return self.redirect_uris[0]
        except IndexError:
            pass

    @property
    def default_scopes(self):
        """List of default scopes for client."""
        if self._default_scopes:
            return self._default_scopes.split(" ")
        return []

    @default_scopes.setter
    def default_scopes(self, scopes):
        """Set default scopes for client."""
        validate_scopes(scopes)
        self._default_scopes = " ".join(set(scopes)) if scopes else ""

    def validate_scopes(self, scopes):
        """Validate if client is allowed to access scopes."""
        try:
            validate_scopes(scopes)
            return True
        except ScopeDoesNotExists:
            return False

    def gen_salt(self):
        """Generate salt."""
        self.reset_client_id()
        self.reset_client_secret()

    def reset_client_id(self):
        """Reset client id."""
        self.client_id = gen_salt(
            current_app.config.get('OAUTH2_CLIENT_ID_SALT_LEN')
        )

    def reset_client_secret(self):
        """Reset client secret."""
        self.client_secret = gen_salt(
            current_app.config.get('OAUTH2_CLIENT_SECRET_SALT_LEN')
        )

class Token(db.Model):

    """A bearer token is the final token that can be used by the client."""

    __tablename__ = 'oauth2TOKEN'

    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    """Object ID."""

    client_id = db.Column(
        db.String(255), db.ForeignKey('oauth2CLIENT.client_id'),
        nullable=False,
    )
    """Foreign key to client application."""

    client = db.relationship(
        'Client',
        backref=db.backref(
            'oauth2tokens',
            cascade="all, delete-orphan"
        ))
    """SQLAlchemy relationship to client application."""

    user_id = db.Column(
        db.Integer(15, unsigned=True), db.ForeignKey('user.id'), nullable=True
    )
    """Foreign key to user."""

    user = db.relationship(
        User,
        backref=db.backref(
            "oauth2tokens",
            cascade="all, delete-orphan",
        )
    )
    """SQLAlchemy relationship to user."""

    token_type = db.Column(db.String(255), default='bearer')
    """Token type - only bearer is supported at the moment."""

    access_token = db.Column(String255EncryptedType(
        type_in=db.String(255),
        key=secret_key),
        unique=True
    )

    refresh_token = db.Column(String255EncryptedType(
        type_in=db.String(255),
        key=secret_key,
        engine=NoneAesEngine),
        unique=True, nullable=True
    )

    expires = db.Column(db.DateTime, nullable=True)

    _scopes = db.Column(db.Text)

    is_personal = db.Column(db.Boolean, default=False)
    """Personal accesss token."""

    is_internal = db.Column(db.Boolean, default=False)
    """Determines if token is an internally generated token."""

    @property
    def scopes(self):
        """Return all scopes."""
        if self._scopes:
            return self._scopes.split()
        return []

    @scopes.setter
    def scopes(self, scopes):
        """Set scopes."""
        validate_scopes(scopes)
        self._scopes = " ".join(set(scopes)) if scopes else ""

    def get_visible_scopes(self):
        """Get list of non-internal scopes for token."""
        from .registry import scopes as scopes_registry
        return [k for k, s in scopes_registry.choices() if k in self.scopes]

    @classmethod
    def create_personal(cls, name, user_id, scopes=None, is_internal=False):
        """Create a personal access token.

        A token that is bound to a specific user and which doesn't expire, i.e.
        similar to the concept of an API key.
        """
        scopes = " ".join(scopes) if scopes else ""

        c = Client(
            name=name,
            user_id=user_id,
            is_internal=True,
            is_confidential=False,
            _default_scopes=scopes
        )
        c.gen_salt()

        t = Token(
            client_id=c.client_id,
            user_id=user_id,
            access_token=gen_salt(
                current_app.config.get('OAUTH2_TOKEN_PERSONAL_SALT_LEN')
            ),
            expires=None,
            _scopes=scopes,
            is_personal=True,
            is_internal=is_internal,
        )

        db.session.add(c)
        db.session.add(t)
        db.session.commit()

        return t
<commit_msg>models: fix for SECRET_KEY loading<commit_after># -*- coding: utf-8 -*-
#
# This file is part of Invenio.
# Copyright (C) 2014, 2015 CERN.
#
# Invenio is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# Invenio 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
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Invenio; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.

"""Database models for OAuth2 server."""

from __future__ import absolute_import

from flask import current_app

from flask_login import current_user

from invenio_base.i18n import _
from invenio_ext.login.legacy_user import UserInfo
from invenio_ext.sqlalchemy import db
from invenio_accounts.models import User

import six

from sqlalchemy_utils.types import URLType
from sqlalchemy_utils.types.encrypted import AesEngine, EncryptedType

from werkzeug.security import gen_salt

from wtforms import validators

from .errors import ScopeDoesNotExists
from .validators import validate_redirect_uri, validate_scopes

def secret_key():
    """Return secret key as bytearray."""
    return current_app.config['SECRET_KEY'].encode('utf-8')

class NoneAesEngine(AesEngine):

    """Filter None values from encrypting."""

    def encrypt(self, value):
        """Encrypt a value on the way in."""
        if value is not None:
            return super(NoneAesEngine, self).encrypt(value)

    def decrypt(self, value):
        """Decrypt value on the way out."""
        if value is not None:
            return super(NoneAesEngine, self).decrypt(value)

class String255EncryptedType(EncryptedType):

    """String encrypted type."""

    impl = db.String(255)

class OAuthUserProxy(object):

    """Proxy object to an Invenio User."""

    def __init__(self, user):
        """Initialize proxy object with user instance."""
        self._user = user

    def __getattr__(self, name):
        """Pass any undefined attribute to the underlying object."""
        return getattr(self._user, name)

    def __getstate__(self):
        """Return the id."""
        return self.id

    def __setstate__(self, state):
        """Set user info."""
        self._user = UserInfo(state)

    @property
    def id(self):
        """Return user identifier."""
        return self._user.get_id()

    def check_password(self, password):
        """Check user password."""
        return self.password == password

    @classmethod
    def get_current_user(cls):
        """Return an instance of current user object."""
        return cls(current_user._get_current_object())

class Scope(object):

    """OAuth scope definition."""

    def __init__(self, id_, help_text='', group='', internal=False):
        """Initialize scope values."""
        self.id = id_
        self.group = group
        self.help_text = help_text
        self.is_internal = internal

class Client(db.Model):

    """A client is the app which want to use the resource of a user.

    It is suggested that the client is registered by a user on your site, but
    it is not required.

    The client should contain at least these information:

        client_id: A random string
        client_secret: A random string
        client_type: A string represents if it is confidential
        redirect_uris: A list of redirect uris
        default_redirect_uri: One of the redirect uris
        default_scopes: Default scopes of the client

    But it could be better, if you implemented:

        allowed_grant_types: A list of grant types
        allowed_response_types: A list of response types
        validate_scopes: A function to validate scopes
    """

    __tablename__ = 'oauth2CLIENT'

    name = db.Column(
        db.String(40),
        info=dict(
            label=_('Name'),
            description=_('Name of application (displayed to users).'),
            validators=[validators.DataRequired()]
        )
    )
    """Human readable name of the application."""

    description = db.Column(
        db.Text(),
        default=u'',
        info=dict(
            label=_('Description'),
            description=_('Optional. Description of the application'
                          ' (displayed to users).'),
        )
    )
    """Human readable description."""

    website = db.Column(
        URLType(),
        info=dict(
            label=_('Website URL'),
            description=_('URL of your application (displayed to users).'),
        ),
        default=u'',
    )

    user_id = db.Column(db.ForeignKey('user.id'), nullable=True)
    """Creator of the client application."""

    client_id = db.Column(db.String(255), primary_key=True)
    """Client application ID."""

    client_secret = db.Column(
        db.String(255), unique=True, index=True, nullable=False
    )
    """Client application secret."""

    is_confidential = db.Column(db.Boolean, default=True)
    """Determine if client application is public or not."""

    is_internal = db.Column(db.Boolean, default=False)
    """Determins if client application is an internal application."""

    _redirect_uris = db.Column(db.Text)
    """A newline-separated list of redirect URIs. First is the default URI."""

    _default_scopes = db.Column(db.Text)
    """A space-separated list of default scopes of the client.

    The value of the scope parameter is expressed as a list of space-delimited,
    case-sensitive strings.
    """

    user = db.relationship(
        User,
        backref=db.backref(
            "oauth2clients",
            cascade="all, delete-orphan",
        )
    )
    """Relationship to user."""

    @property
    def allowed_grant_types(self):
        """Return allowed grant types."""
        return current_app.config['OAUTH2_ALLOWED_GRANT_TYPES']

    @property
    def allowed_response_types(self):
        """Return allowed response types."""
        return current_app.config['OAUTH2_ALLOWED_RESPONSE_TYPES']

    # def validate_scopes(self, scopes):
    #     return self._validate_scopes

    @property
    def client_type(self):
        """Return client type."""
        if self.is_confidential:
            return 'confidential'
        return 'public'

    @property
    def redirect_uris(self):
        """Return redirect uris."""
        if self._redirect_uris:
            return self._redirect_uris.splitlines()
        return []

    @redirect_uris.setter
    def redirect_uris(self, value):
        """Validate and store redirect URIs for client."""
        if isinstance(value, six.text_type):
            value = value.split("\n")

        value = [v.strip() for v in value]

        for v in value:
            validate_redirect_uri(v)

        self._redirect_uris = "\n".join(value) or ""

    @property
    def default_redirect_uri(self):
        """Return default redirect uri."""
        try:
            return self.redirect_uris[0]
        except IndexError:
            pass

    @property
    def default_scopes(self):
        """List of default scopes for client."""
        if self._default_scopes:
            return self._default_scopes.split(" ")
        return []

    @default_scopes.setter
    def default_scopes(self, scopes):
        """Set default scopes for client."""
        validate_scopes(scopes)
        self._default_scopes = " ".join(set(scopes)) if scopes else ""

    def validate_scopes(self, scopes):
        """Validate if client is allowed to access scopes."""
        try:
            validate_scopes(scopes)
            return True
        except ScopeDoesNotExists:
            return False

    def gen_salt(self):
        """Generate salt."""
        self.reset_client_id()
        self.reset_client_secret()

    def reset_client_id(self):
        """Reset client id."""
        self.client_id = gen_salt(
            current_app.config.get('OAUTH2_CLIENT_ID_SALT_LEN')
        )

    def reset_client_secret(self):
        """Reset client secret."""
        self.client_secret = gen_salt(
            current_app.config.get('OAUTH2_CLIENT_SECRET_SALT_LEN')
        )

class Token(db.Model):

    """A bearer token is the final token that can be used by the client."""

    __tablename__ = 'oauth2TOKEN'

    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    """Object ID."""

    client_id = db.Column(
        db.String(255), db.ForeignKey('oauth2CLIENT.client_id'),
        nullable=False,
    )
    """Foreign key to client application."""

    client = db.relationship(
        'Client',
        backref=db.backref(
            'oauth2tokens',
            cascade="all, delete-orphan"
        ))
    """SQLAlchemy relationship to client application."""

    user_id = db.Column(
        db.Integer(15, unsigned=True), db.ForeignKey('user.id'), nullable=True
    )
    """Foreign key to user."""

    user = db.relationship(
        User,
        backref=db.backref(
            "oauth2tokens",
            cascade="all, delete-orphan",
        )
    )
    """SQLAlchemy relationship to user."""

    token_type = db.Column(db.String(255), default='bearer')
    """Token type - only bearer is supported at the moment."""

    access_token = db.Column(String255EncryptedType(
        type_in=db.String(255),
        key=secret_key),
        unique=True
    )

    refresh_token = db.Column(String255EncryptedType(
        type_in=db.String(255),
        key=secret_key,
        engine=NoneAesEngine),
        unique=True, nullable=True
    )

    expires = db.Column(db.DateTime, nullable=True)

    _scopes = db.Column(db.Text)

    is_personal = db.Column(db.Boolean, default=False)
    """Personal accesss token."""

    is_internal = db.Column(db.Boolean, default=False)
    """Determines if token is an internally generated token."""

    @property
    def scopes(self):
        """Return all scopes."""
        if self._scopes:
            return self._scopes.split()
        return []

    @scopes.setter
    def scopes(self, scopes):
        """Set scopes."""
        validate_scopes(scopes)
        self._scopes = " ".join(set(scopes)) if scopes else ""

    def get_visible_scopes(self):
        """Get list of non-internal scopes for token."""
        from .registry import scopes as scopes_registry
        return [k for k, s in scopes_registry.choices() if k in self.scopes]

    @classmethod
    def create_personal(cls, name, user_id, scopes=None, is_internal=False):
        """Create a personal access token.

        A token that is bound to a specific user and which doesn't expire, i.e.
        similar to the concept of an API key.
        """
        scopes = " ".join(scopes) if scopes else ""

        c = Client(
            name=name,
            user_id=user_id,
            is_internal=True,
            is_confidential=False,
            _default_scopes=scopes
        )
        c.gen_salt()

        t = Token(
            client_id=c.client_id,
            user_id=user_id,
            access_token=gen_salt(
                current_app.config.get('OAUTH2_TOKEN_PERSONAL_SALT_LEN')
            ),
            expires=None,
            _scopes=scopes,
            is_personal=True,
            is_internal=is_internal,
        )

        db.session.add(c)
        db.session.add(t)
        db.session.commit()

        return t
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
##############################################################################
#
#    OpenERP, Open Source Management Solution
#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
#    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 osv import fields, osv

class res_company(osv.osv):
    _inherit = "res.company"
    _columns = {
        'overdue_msg': fields.text('Overdue Payments Message', translate=True),
        'property_reserve_and_surplus_account': fields.property(
            'account.account',
            type='many2one',
            relation='account.account',
            string="Reserve and Profit/Loss Account",
            view_load=True,
            domain="[('type', '=', 'other')]",
            help="This Account is used for transferring Profit/Loss(If It is Profit: Amount will be added, Loss : Amount will be deducted.), Which is calculated from Profit & Loss Report"),
    }

    _defaults = {
        'overdue_msg': 'Please note that the following payments are now due. If your payment \
                        has been sent, kindly forward your payment details. If payment will be \
                        delayed further, please contact us to discuss. \
                        \nWould your payment have been carried out after this mail was sent, please consider the present one as void.'
    }

res_company()

# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:<commit_msg>[MERGE] account company default overdue_msg<commit_after># -*- coding: utf-8 -*-
##############################################################################
#
#    OpenERP, Open Source Management Solution
#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
#    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 osv import fields, osv

class res_company(osv.osv):
    _inherit = "res.company"
    _columns = {
        'overdue_msg': fields.text('Overdue Payments Message', translate=True),
        'property_reserve_and_surplus_account': fields.property(
            'account.account',
            type='many2one',
            relation='account.account',
            string="Reserve and Profit/Loss Account",
            view_load=True,
            domain="[('type', '=', 'other')]",
            help="This Account is used for transferring Profit/Loss(If It is Profit: Amount will be added, Loss : Amount will be deducted.), Which is calculated from Profit & Loss Report"),
    }

    _defaults = {
        'overdue_msg': '''Please note that the following payments are now due.
If your payment has been sent, kindly forward your payment details.
If payment will be delayed further, please contact us to discuss.
Would your payment have been carried out after this mail was sent, please consider the present one as void.
'''
    }

res_company()

# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
##############################################################################
#
#    OpenERP, Open Source Management Solution
#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
#    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': 'Customer & Supplier Relationship Management',
    'version': '1.0',
    'category': 'Customer Relationship Management',
    'complexity': "easy",
    'description': """
The generic OpenERP Customer Relationship Management.
=====================================================

This system enables a group of people to intelligently and efficiently manage
leads, opportunities, meeting, phonecall etc.
It manages key tasks such as communication, identification, prioritization,
assignment, resolution and notification.

OpenERP ensures that all cases are successfully tracked by users, customers and
suppliers. It can automatically send reminders, escalate the request, trigger
specific methods and lots of other actions based on your own enterprise rules.

The greatest thing about this system is that users don't need to do anything
special. They can just send email to the request tracker. OpenERP will take
care of thanking them for their message, automatically routing it to the
appropriate staff, and make sure all future correspondence gets to the right
place.

The CRM module has a email gateway for the synchronisation interface
between mails and OpenERP.

Creates a dashboard for CRM that includes:
    * Opportunities by Categories (graph)
    * Opportunities by Stage (graph)
    * Planned Revenue by Stage and User (graph)
""",
    'author': 'OpenERP SA',
    'website': 'http://www.openerp.com',
    'depends': [
        'base',
        'base_action_rule',
        'base_setup',
        'process',
        'mail',
        'base_calendar',
        'resource',
        'board'
    ],
    'init_xml': [
        'crm_data.xml',
        'crm_meeting_data.xml',
        'crm_lead_data.xml',
        'crm_meeting_data.xml',
        'crm_phonecall_data.xml',
    ],
    'update_xml': [
        'security/crm_security.xml',
        'security/ir.model.access.csv',

        'wizard/crm_lead_to_partner_view.xml',
        'wizard/crm_lead_to_opportunity_view.xml',

        'wizard/crm_phonecall_to_phonecall_view.xml',
        'wizard/crm_phonecall_to_partner_view.xml',
        'wizard/crm_phonecall_to_opportunity_view.xml',

        'wizard/crm_opportunity_to_phonecall_view.xml',
        'wizard/crm_partner_to_opportunity_view.xml',

        'wizard/crm_add_note_view.xml',
        'wizard/crm_merge_opportunities_view.xml',

        'crm_view.xml',

        'crm_action_rule_view.xml',
        'crm_lead_view.xml',
        'crm_lead_menu.xml',

        'crm_meeting_view.xml',
        'crm_meeting_menu.xml',
        'crm_meeting_shortcut_data.xml',

        'crm_phonecall_view.xml',
        'crm_phonecall_menu.xml',

        'report/crm_lead_report_view.xml',
        'report/crm_phonecall_report_view.xml',

        'process/crm_configuration_process.xml',
        'crm_installer_view.xml',

        'res_partner_view.xml',
        'board_crm_view.xml',
        'board_crm_statistical_view.xml',

    ],
    'demo_xml': [
        'crm_demo.xml',
        'crm_lead_demo.xml',
        'crm_meeting_demo.xml',
        'crm_phonecall_demo.xml',
    ],
    'test': [
            'test/process/crm_lead_open.yml',
            'test/process/crm_lead_to_opportunity.yml',
            'test/process/cancle_crm_lead.yml',
            'test/process/crm_meeting.yml',
            'test/ui/lead_form.yml',
            'test/test_crm_opportunity.yml',
            'test/process/crm_phonecall.yml',
            'test/test_crm_recurrent_meeting.yml',
            'test/test_crm_stage_changes.yml',
            'test/test_crm_recurrent_meeting_case2.yml',
            'test/test_crm_lead_case2.yml',
            'test/test_crm_opportunity_case2.yml',
            'test/test_crm_phonecall_case2.yml',
            'test/test_crm_partner2opportunity.yml',
            'test/test_crm_segmentation.yml',
             ],
    'installable': True,
    'active': False,
    'certificate': '0079056041421',
    'images': ['images/sale_crm_crm_dashboard.png', 'images/crm_dashboard.jpeg','images/leads.jpeg','images/meetings.jpeg','images/opportunities.jpeg','images/outbound_calls.jpeg','images/stages.jpeg'],
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
<commit_msg>[IMP] crm : All renamed file name Updated on __opnerp.py file<commit_after># -*- coding: utf-8 -*-
##############################################################################
#
#    OpenERP, Open Source Management Solution
#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
#    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': 'Customer & Supplier Relationship Management',
    'version': '1.0',
    'category': 'Customer Relationship Management',
    'complexity': "easy",
    'description': """
The generic OpenERP Customer Relationship Management.
=====================================================

This system enables a group of people to intelligently and efficiently manage
leads, opportunities, meeting, phonecall etc.
It manages key tasks such as communication, identification, prioritization,
assignment, resolution and notification.

OpenERP ensures that all cases are successfully tracked by users, customers and
suppliers. It can automatically send reminders, escalate the request, trigger
specific methods and lots of other actions based on your own enterprise rules.

The greatest thing about this system is that users don't need to do anything
special. They can just send email to the request tracker. OpenERP will take
care of thanking them for their message, automatically routing it to the
appropriate staff, and make sure all future correspondence gets to the right
place.

The CRM module has a email gateway for the synchronisation interface
between mails and OpenERP.

Creates a dashboard for CRM that includes:
    * Opportunities by Categories (graph)
    * Opportunities by Stage (graph)
    * Planned Revenue by Stage and User (graph)
""",
    'author': 'OpenERP SA',
    'website': 'http://www.openerp.com',
    'depends': [
        'base',
        'base_action_rule',
        'base_setup',
        'process',
        'mail',
        'base_calendar',
        'resource',
        'board'
    ],
    'init_xml': [
        'crm_data.xml',
        'crm_meeting_data.xml',
        'crm_lead_data.xml',
        'crm_meeting_data.xml',
        'crm_phonecall_data.xml',
    ],
    'update_xml': [
        'security/crm_security.xml',
        'security/ir.model.access.csv',

        'wizard/crm_lead_to_partner_view.xml',
        'wizard/crm_lead_to_opportunity_view.xml',

        'wizard/crm_phonecall_to_phonecall_view.xml',
        'wizard/crm_phonecall_to_partner_view.xml',
        'wizard/crm_phonecall_to_opportunity_view.xml',

        'wizard/crm_opportunity_to_phonecall_view.xml',
        'wizard/crm_partner_to_opportunity_view.xml',

        'wizard/crm_add_note_view.xml',
        'wizard/crm_merge_opportunities_view.xml',

        'crm_view.xml',

        'crm_action_rule_view.xml',
        'crm_lead_view.xml',
        'crm_lead_menu.xml',

        'crm_meeting_view.xml',
        'crm_meeting_menu.xml',
        'crm_meeting_shortcut_data.xml',

        'crm_phonecall_view.xml',
        'crm_phonecall_menu.xml',

        'report/crm_lead_report_view.xml',
        'report/crm_phonecall_report_view.xml',

        'process/crm_configuration_process.xml',
        'crm_installer_view.xml',

        'res_partner_view.xml',
        'board_crm_view.xml',
        'board_crm_statistical_view.xml',

    ],
    'demo_xml': [
        'crm_demo.xml',
        'crm_lead_demo.xml',
        'crm_meeting_demo.xml',
        'crm_phonecall_demo.xml',
    ],
    'test': [
            'test/process/lead_open.yml',
            'test/process/lead2opportunity.yml',
            'test/process/cancel_lead.yml',
            'test/process/meeting.yml',
            'test/ui/lead_form.yml',
            'test/test_crm_opportunity.yml',
            'test/process/phonecall2opportunity.yml',
            'test/test_crm_recurrent_meeting.yml',
            'test/test_crm_stage_changes.yml',
            'test/test_crm_recurrent_meeting_case2.yml',
            'test/test_crm_lead_case2.yml',
            'test/test_crm_opportunity_case2.yml',
            'test/test_crm_phonecall_case2.yml',
            'test/test_crm_partner2opportunity.yml',
            'test/test_crm_segmentation.yml',
             ],
    'installable': True,
    'active': False,
    'certificate': '0079056041421',
    'images': ['images/sale_crm_crm_dashboard.png', 'images/crm_dashboard.jpeg','images/leads.jpeg','images/meetings.jpeg','images/opportunities.jpeg','images/outbound_calls.jpeg','images/stages.jpeg'],
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
##############################################################################
#
#    OpenERP, Open Source Management Solution
#    Copyright (C) 2010-today OpenERP SA (<http://www.openerp.com>)
#
#    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/>
#
##############################################################################

import openerp
import openerp.tools as tools
from openerp.osv import osv
from openerp.osv import fields
from openerp import SUPERUSER_ID

class mail_group(osv.Model):
    """ A mail_group is a collection of users sharing messages in a discussion
        group. The group mechanics are based on the followers. """
    _description = 'Discussion group'
    _name = 'mail.group'
    _mail_flat_thread = False
    _inherit = ['mail.thread']
    _inherits = {'mail.alias': 'alias_id'}

    def _get_image(self, cr, uid, ids, name, args, context=None):
        result = dict.fromkeys(ids, False)
        for obj in self.browse(cr, uid, ids, context=context):
            result[obj.id] = tools.image_get_resized_images(obj.image)
        return result

    def _set_image(self, cr, uid, id, name, value, args, context=None):
        return self.write(cr, uid, [id], {'image': tools.image_resize_image_big(value)}, context=context)

    _columns = {
        'name': fields.char('Name', size=64, required=True, translate=True),
        'description': fields.text('Description'),
        'menu_id': fields.many2one('ir.ui.menu', string='Related Menu', required=True, ondelete="cascade"),
        'public': fields.selection([('public', 'Public'), ('private', 'Private'), ('groups', 'Selected Group Only')], 'Privacy', required=True,
            help='This group is visible by non members. \
            Invisible groups can add members through the invite button.'),
        'group_public_id': fields.many2one('res.groups', string='Authorized Group'),
        'group_ids': fields.many2many('res.groups', rel='mail_group_res_group_rel',
            id1='mail_group_id', id2='groups_id', string='Auto Subscription',
            help="Members of those groups will automatically added as followers. "\
                 "Note that they will be able to manage their subscription manually "\
                 "if necessary."),
        # image: all image fields are base64 encoded and PIL-supported
        'image': fields.binary("Photo",
            help="This field holds the image used as photo for the group, limited to 1024x1024px."),
        'image_medium': fields.function(_get_image, fnct_inv=_set_image,
            string="Medium-sized photo", type="binary", multi="_get_image",
            store={
                'mail.group': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10),
            },
            help="Medium-sized photo of the group. It is automatically "\
                 "resized as a 128x128px image, with aspect ratio preserved. "\
                 "Use this field in form views or some kanban views."),
        'image_small': fields.function(_get_image, fnct_inv=_set_image,
            string="Small-sized photo", type="binary", multi="_get_image",
            store={
                'mail.group': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10),
            },
            help="Small-sized photo of the group. It is automatically "\
                 "resized as a 64x64px image, with aspect ratio preserved. "\
                 "Use this field anywhere a small image is required."),
        'alias_id': fields.many2one('mail.alias', 'Alias', ondelete="cascade", required=True,
            help="The email address associated with this group. New emails received will automatically "
                 "create new topics."),
    }

    def _get_default_employee_group(self, cr, uid, context=None):
        ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'base', 'group_user')
        return ref and ref[1] or False

    def _get_default_image(self, cr, uid, context=None):
        image_path = openerp.modules.get_module_resource('mail', 'static/src/img', 'groupdefault.png')
        return tools.image_resize_image_big(open(image_path, 'rb').read().encode('base64'))

    _defaults = {
        'public': 'groups',
        'group_public_id': _get_default_employee_group,
        'image': _get_default_image,
    }

    def _generate_header_description(self, cr, uid, group, context=None):
        header = ''
        if group.description:
            header = '%s' % group.description
        if group.alias_id and group.alias_name and group.alias_domain:
            if header:
                header = '%s
' % header
            return '%sGroup email gateway: %s@%s' % (header, group.alias_name, group.alias_domain)
        return header

    def _subscribe_users(self, cr, uid, ids, context=None):
        for mail_group in self.browse(cr, uid, ids, context=context):
            partner_ids = []
            for group in mail_group.group_ids:
                partner_ids += [user.partner_id.id for user in group.users]
            self.message_subscribe(cr, uid, ids, partner_ids, context=context)

    def create(self, cr, uid, vals, context=None):
        if context is None:
            context = {}

        # get parent menu
        menu_parent = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'mail', 'mail_group_root')
        menu_parent = menu_parent and menu_parent[1] or False

        # Create menu id
        mobj = self.pool.get('ir.ui.menu')
        menu_id = mobj.create(cr, SUPERUSER_ID, {'name': vals['name'], 'parent_id': menu_parent}, context=context)
        vals['menu_id'] = menu_id

        # Create group and alias
        create_context = dict(context, alias_model_name=self._name, alias_parent_model_name=self._name)
        mail_group_id = super(mail_group, self).create(cr, uid, vals, context=create_context)
        group = self.browse(cr, uid, mail_group_id, context=context)
        self.pool.get('mail.alias').write(cr, uid, [group.alias_id.id], {"alias_force_thread_id": mail_group_id, 'alias_parent_thread_id': mail_group_id}, context)
        group = self.browse(cr, uid, mail_group_id, context=context)

        # Create client action for this group and link the menu to it
        ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'mail', 'action_mail_group_feeds')
        if ref:
            search_ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'mail', 'view_message_search')
            params = {
                'search_view_id': search_ref and search_ref[1] or False,
                'domain': [
                    ('model', '=', 'mail.group'),
                    ('res_id', '=', mail_group_id),
                ],
                'context': {
                    'default_model': 'mail.group',
                    'default_res_id': mail_group_id,
                },
                'res_model': 'mail.message',
                'thread_level': 1,
                'header_description': self._generate_header_description(cr, uid, group, context=context),
                'view_mailbox': True,
                'compose_placeholder': 'Send a message to the group',
            }
            cobj = self.pool.get('ir.actions.client')
            newref = cobj.copy(cr, SUPERUSER_ID, ref[1], default={'params': str(params), 'name': vals['name']}, context=context)
            mobj.write(cr, SUPERUSER_ID, menu_id, {'action': 'ir.actions.client,' + str(newref), 'mail_group_id': mail_group_id}, context=context)

        if vals.get('group_ids'):
            self._subscribe_users(cr, uid, [mail_group_id], context=context)
        return mail_group_id

    def unlink(self, cr, uid, ids, context=None):
        groups = self.browse(cr, uid, ids, context=context)
        # Cascade-delete mail aliases as well, as they should not exist without the mail group.
        mail_alias = self.pool.get('mail.alias')
        alias_ids = [group.alias_id.id for group in groups if group.alias_id]
        # Delete mail_group
        res = super(mail_group, self).unlink(cr, uid, ids, context=context)
        # Delete alias
        mail_alias.unlink(cr, SUPERUSER_ID, alias_ids, context=context)
        # Cascade-delete menu entries as well
        self.pool.get('ir.ui.menu').unlink(cr, SUPERUSER_ID, [group.menu_id.id for group in groups if group.menu_id], context=context)
        return res

    def write(self, cr, uid, ids, vals, context=None):
        result = super(mail_group, self).write(cr, uid, ids, vals, context=context)
        if vals.get('group_ids'):
            self._subscribe_users(cr, uid, ids, context=context)
        # if description, name or alias is changed: update client action
        if vals.get('description') or vals.get('name') or vals.get('alias_id') or vals.get('alias_name'):
            cobj = self.pool.get('ir.actions.client')
            for action in [group.menu_id.action for group in self.browse(cr, uid, ids, context=context)]:
                new_params = action.params
                new_params['header_description'] = self._generate_header_description(cr, uid, group, context=context)
                cobj.write(cr, SUPERUSER_ID, [action.id], {'params': str(new_params)}, context=context)
        # if name is changed: update menu
        if vals.get('name'):
            mobj = self.pool.get('ir.ui.menu')
            mobj.write(cr, SUPERUSER_ID,
                [group.menu_id.id for group in self.browse(cr, uid, ids, context=context)],
                {'name': vals.get('name')}, context=context)

        return result

    def action_follow(self, cr, uid, ids, context=None):
        """ Wrapper because message_subscribe_users take a user_ids=None
            that receive the context without the wrapper. """
        return self.message_subscribe_users(cr, uid, ids, context=context)

    def action_unfollow(self, cr, uid, ids, context=None):
        """ Wrapper because message_unsubscribe_users take a user_ids=None
            that receive the context without the wrapper. """
        return self.message_unsubscribe_users(cr, uid, ids, context=context)

    def get_suggested_thread(self, cr, uid, removed_suggested_threads=None, context=None):
        """Show the suggestion of groups if display_groups_suggestions if the
        user perference allows it."""
        user = self.pool.get('res.users').browse(cr, uid, uid, context)
        if not user.display_groups_suggestions:
            return []
        else:
            return super(mail_group, self).get_suggested_thread(cr, uid, removed_suggested_threads, context)
<commit_msg>[IMP] mail.group: can post if can read<commit_after># -*- coding: utf-8 -*-
##############################################################################
#
#    OpenERP, Open Source Management Solution
#    Copyright (C) 2010-today OpenERP SA (<http://www.openerp.com>)
#
#    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/>
#
##############################################################################

import openerp
import openerp.tools as tools
from openerp.osv import osv
from openerp.osv import fields
from openerp import SUPERUSER_ID

class mail_group(osv.Model):
    """ A mail_group is a collection of users sharing messages in a discussion
        group. The group mechanics are based on the followers. """
    _description = 'Discussion group'
    _name = 'mail.group'
    _mail_flat_thread = False
    _inherit = ['mail.thread']
    _inherits = {'mail.alias': 'alias_id'}
    _mail_post_access = 'read'

    def _get_image(self, cr, uid, ids, name, args, context=None):
        result = dict.fromkeys(ids, False)
        for obj in self.browse(cr, uid, ids, context=context):
            result[obj.id] = tools.image_get_resized_images(obj.image)
        return result

    def _set_image(self, cr, uid, id, name, value, args, context=None):
        return self.write(cr, uid, [id], {'image': tools.image_resize_image_big(value)}, context=context)

    _columns = {
        'name': fields.char('Name', size=64, required=True, translate=True),
        'description': fields.text('Description'),
        'menu_id': fields.many2one('ir.ui.menu', string='Related Menu', required=True, ondelete="cascade"),
        'public': fields.selection([('public', 'Public'), ('private', 'Private'), ('groups', 'Selected Group Only')], 'Privacy', required=True,
            help='This group is visible by non members. \
            Invisible groups can add members through the invite button.'),
        'group_public_id': fields.many2one('res.groups', string='Authorized Group'),
        'group_ids': fields.many2many('res.groups', rel='mail_group_res_group_rel',
            id1='mail_group_id', id2='groups_id', string='Auto Subscription',
            help="Members of those groups will automatically added as followers. "\
                 "Note that they will be able to manage their subscription manually "\
                 "if necessary."),
        # image: all image fields are base64 encoded and PIL-supported
        'image': fields.binary("Photo",
            help="This field holds the image used as photo for the group, limited to 1024x1024px."),
        'image_medium': fields.function(_get_image, fnct_inv=_set_image,
            string="Medium-sized photo", type="binary", multi="_get_image",
            store={
                'mail.group': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10),
            },
            help="Medium-sized photo of the group. It is automatically "\
                 "resized as a 128x128px image, with aspect ratio preserved. "\
                 "Use this field in form views or some kanban views."),
        'image_small': fields.function(_get_image, fnct_inv=_set_image,
            string="Small-sized photo", type="binary", multi="_get_image",
            store={
                'mail.group': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10),
            },
            help="Small-sized photo of the group. It is automatically "\
                 "resized as a 64x64px image, with aspect ratio preserved. "\
                 "Use this field anywhere a small image is required."),
        'alias_id': fields.many2one('mail.alias', 'Alias', ondelete="cascade", required=True,
            help="The email address associated with this group. New emails received will automatically "
                 "create new topics."),
    }

    def _get_default_employee_group(self, cr, uid, context=None):
        ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'base', 'group_user')
        return ref and ref[1] or False

    def _get_default_image(self, cr, uid, context=None):
        image_path = openerp.modules.get_module_resource('mail', 'static/src/img', 'groupdefault.png')
        return tools.image_resize_image_big(open(image_path, 'rb').read().encode('base64'))

    _defaults = {
        'public': 'groups',
        'group_public_id': _get_default_employee_group,
        'image': _get_default_image,
    }

    def _generate_header_description(self, cr, uid, group, context=None):
        header = ''
        if group.description:
            header = '%s' % group.description
        if group.alias_id and group.alias_name and group.alias_domain:
            if header:
                header = '%s
' % header
            return '%sGroup email gateway: %s@%s' % (header, group.alias_name, group.alias_domain)
        return header

    def _subscribe_users(self, cr, uid, ids, context=None):
        for mail_group in self.browse(cr, uid, ids, context=context):
            partner_ids = []
            for group in mail_group.group_ids:
                partner_ids += [user.partner_id.id for user in group.users]
            self.message_subscribe(cr, uid, ids, partner_ids, context=context)

    def create(self, cr, uid, vals, context=None):
        if context is None:
            context = {}

        # get parent menu
        menu_parent = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'mail', 'mail_group_root')
        menu_parent = menu_parent and menu_parent[1] or False

        # Create menu id
        mobj = self.pool.get('ir.ui.menu')
        menu_id = mobj.create(cr, SUPERUSER_ID, {'name': vals['name'], 'parent_id': menu_parent}, context=context)
        vals['menu_id'] = menu_id

        # Create group and alias
        create_context = dict(context, alias_model_name=self._name, alias_parent_model_name=self._name)
        mail_group_id = super(mail_group, self).create(cr, uid, vals, context=create_context)
        group = self.browse(cr, uid, mail_group_id, context=context)
        self.pool.get('mail.alias').write(cr, uid, [group.alias_id.id], {"alias_force_thread_id": mail_group_id, 'alias_parent_thread_id': mail_group_id}, context)
        group = self.browse(cr, uid, mail_group_id, context=context)

        # Create client action for this group and link the menu to it
        ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'mail', 'action_mail_group_feeds')
        if ref:
            search_ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'mail', 'view_message_search')
            params = {
                'search_view_id': search_ref and search_ref[1] or False,
                'domain': [
                    ('model', '=', 'mail.group'),
                    ('res_id', '=', mail_group_id),
                ],
                'context': {
                    'default_model': 'mail.group',
                    'default_res_id': mail_group_id,
                },
                'res_model': 'mail.message',
                'thread_level': 1,
                'header_description': self._generate_header_description(cr, uid, group, context=context),
                'view_mailbox': True,
                'compose_placeholder': 'Send a message to the group',
            }
            cobj = self.pool.get('ir.actions.client')
            newref = cobj.copy(cr, SUPERUSER_ID, ref[1], default={'params': str(params), 'name': vals['name']}, context=context)
            mobj.write(cr, SUPERUSER_ID, menu_id, {'action': 'ir.actions.client,' + str(newref), 'mail_group_id': mail_group_id}, context=context)

        if vals.get('group_ids'):
            self._subscribe_users(cr, uid, [mail_group_id], context=context)
        return mail_group_id

    def unlink(self, cr, uid, ids, context=None):
        groups = self.browse(cr, uid, ids, context=context)
        # Cascade-delete mail aliases as well, as they should not exist without the mail group.
        mail_alias = self.pool.get('mail.alias')
        alias_ids = [group.alias_id.id for group in groups if group.alias_id]
        # Delete mail_group
        res = super(mail_group, self).unlink(cr, uid, ids, context=context)
        # Delete alias
        mail_alias.unlink(cr, SUPERUSER_ID, alias_ids, context=context)
        # Cascade-delete menu entries as well
        self.pool.get('ir.ui.menu').unlink(cr, SUPERUSER_ID, [group.menu_id.id for group in groups if group.menu_id], context=context)
        return res

    def write(self, cr, uid, ids, vals, context=None):
        result = super(mail_group, self).write(cr, uid, ids, vals, context=context)
        if vals.get('group_ids'):
            self._subscribe_users(cr, uid, ids, context=context)
        # if description, name or alias is changed: update client action
        if vals.get('description') or vals.get('name') or vals.get('alias_id') or vals.get('alias_name'):
            cobj = self.pool.get('ir.actions.client')
            for action in [group.menu_id.action for group in self.browse(cr, uid, ids, context=context)]:
                new_params = action.params
                new_params['header_description'] = self._generate_header_description(cr, uid, group, context=context)
                cobj.write(cr, SUPERUSER_ID, [action.id], {'params': str(new_params)}, context=context)
        # if name is changed: update menu
        if vals.get('name'):
            mobj = self.pool.get('ir.ui.menu')
            mobj.write(cr, SUPERUSER_ID,
                [group.menu_id.id for group in self.browse(cr, uid, ids, context=context)],
                {'name': vals.get('name')}, context=context)

        return result

    def action_follow(self, cr, uid, ids, context=None):
        """ Wrapper because message_subscribe_users take a user_ids=None
            that receive the context without the wrapper. """
        return self.message_subscribe_users(cr, uid, ids, context=context)

    def action_unfollow(self, cr, uid, ids, context=None):
        """ Wrapper because message_unsubscribe_users take a user_ids=None
            that receive the context without the wrapper. """
        return self.message_unsubscribe_users(cr, uid, ids, context=context)

    def get_suggested_thread(self, cr, uid, removed_suggested_threads=None, context=None):
        """Show the suggestion of groups if display_groups_suggestions if the
        user perference allows it."""
        user = self.pool.get('res.users').browse(cr, uid, uid, context)
        if not user.display_groups_suggestions:
            return []
        else:
            return super(mail_group, self).get_suggested_thread(cr, uid, removed_suggested_threads, context)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-

import calendar
from datetime import date
from dateutil import relativedelta
import json

from openerp import tools
from openerp.osv import fields, osv
from openerp.tools.float_utils import float_repr

class crm_team(osv.Model):
    _inherit = 'crm.team'

    def _get_sales_to_invoice_amount(self, cr, uid, ids, field_name, arg, context=None):
        obj = self.pool['sale.order']

        res = dict.fromkeys(ids, 0)
        domain = [
            ('team_id', 'in', ids),
            ('state', '=', 'manual'),
        ]
        amounts = obj.read_group(cr, uid, domain, ['amount_total', 'team_id'], ['team_id'], context=context)
        for rec in amounts:
            res[rec['team_id'][0]] = rec['amount_total']
        return res

    def _get_monthly_invoiced(self, cr, uid, ids, field_name, arg, context=None):
        obj_inv = self.pool['account.invoice']
        res = dict.fromkeys(ids, 0)

        # Cannot use read_group because amount_untaxed_signed is an unstored computed field
        for team in ids:
            domain = [
                ('state', 'in', ['open', 'paid']),
                ('team_id', '=', team),
                ('date', '<=', date.today()),
                ('date', '>=', date.today().replace(day=1))
            ]
            invoices = obj_inv.search_read(cr, uid, domain, ['amount_untaxed_signed'], context=context)
            res[team] = sum([inv['amount_untaxed_signed'] for inv in invoices])
        return res

    _columns = {
        'use_quotations': fields.boolean('Quotations', help="Check this box to manage quotations in this sales team."),
        'use_invoices': fields.boolean('Invoices', help="Check this box to manage invoices in this sales team."),
        'invoiced': fields.function(_get_monthly_invoiced, type='integer', readonly=True, string='Invoiced This Month',
            help="Invoice revenue for the current month. This is the amount the sales "
                    "team has invoiced this month. It is used to compute the progression ratio "
                    "of the current and target revenue on the kanban view."),
        'invoiced_target': fields.integer(string='Invoice Target',
            help="Target of invoice revenue for the current month. This is the amount the sales "
                    "team estimates to be able to invoice this month."),
        'sales_to_invoice_amount': fields.function(_get_sales_to_invoice_amount,
            type='integer', readonly=True,
            string='Amount of sales to invoice'),
        'currency_id': fields.related('company_id', 'currency_id', type="many2one", relation="res.currency", string="Currency", readonly=True, required=True),
    }

    _defaults = {
        'use_quotations': True,
    }

    def update_invoiced_target(self, cr, uid, id, value, context=None):
        return self.write(cr, uid, [id], {'invoiced_target': round(float(value or 0))}, context=context)
<commit_msg>[FIX] sale: Teams dashboard, sales to invoice count<commit_after># -*- coding: utf-8 -*-

import calendar
from datetime import date
from dateutil import relativedelta
import json

from openerp import tools
from openerp.osv import fields, osv
from openerp.tools.float_utils import float_repr

class crm_team(osv.Model):
    _inherit = 'crm.team'

    def _get_sales_to_invoice_amount(self, cr, uid, ids, field_name, arg, context=None):
        obj = self.pool['sale.order']

        res = dict.fromkeys(ids, 0)
        domain = [
            ('team_id', 'in', ids),
            ('invoice_status', '=', 'to invoice'),
        ]
        amounts = obj.read_group(cr, uid, domain, ['amount_total', 'team_id'], ['team_id'], context=context)
        for rec in amounts:
            res[rec['team_id'][0]] = rec['amount_total']
        return res

    def _get_monthly_invoiced(self, cr, uid, ids, field_name, arg, context=None):
        obj_inv = self.pool['account.invoice']
        res = dict.fromkeys(ids, 0)

        # Cannot use read_group because amount_untaxed_signed is an unstored computed field
        for team in ids:
            domain = [
                ('state', 'in', ['open', 'paid']),
                ('team_id', '=', team),
                ('date', '<=', date.today()),
                ('date', '>=', date.today().replace(day=1))
            ]
            invoices = obj_inv.search_read(cr, uid, domain, ['amount_untaxed_signed'], context=context)
            res[team] = sum([inv['amount_untaxed_signed'] for inv in invoices])
        return res

    _columns = {
        'use_quotations': fields.boolean('Quotations', help="Check this box to manage quotations in this sales team."),
        'use_invoices': fields.boolean('Invoices', help="Check this box to manage invoices in this sales team."),
        'invoiced': fields.function(_get_monthly_invoiced, type='integer', readonly=True, string='Invoiced This Month',
            help="Invoice revenue for the current month. This is the amount the sales "
                    "team has invoiced this month. It is used to compute the progression ratio "
                    "of the current and target revenue on the kanban view."),
        'invoiced_target': fields.integer(string='Invoice Target',
            help="Target of invoice revenue for the current month. This is the amount the sales "
                    "team estimates to be able to invoice this month."),
        'sales_to_invoice_amount': fields.function(_get_sales_to_invoice_amount,
            type='integer', readonly=True,
            string='Amount of sales to invoice'),
        'currency_id': fields.related('company_id', 'currency_id', type="many2one", relation="res.currency", string="Currency", readonly=True, required=True),
    }

    _defaults = {
        'use_quotations': True,
    }

    def update_invoiced_target(self, cr, uid, id, value, context=None):
        return self.write(cr, uid, [id], {'invoiced_target': round(float(value or 0))}, context=context)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-

import calendar
from datetime import date
from dateutil import relativedelta

from openerp import tools
from openerp.osv import fields, osv

class crm_case_section(osv.osv):
    _inherit = 'crm.case.section'

    def _get_sale_orders_data(self, cr, uid, ids, field_name, arg, context=None):
        obj = self.pool.get('sale.order')
        res = dict.fromkeys(ids, False)
        month_begin = date.today().replace(day=1)
        date_begin = (month_begin - relativedelta.relativedelta(months=self._period_number - 1)).strftime(tools.DEFAULT_SERVER_DATE_FORMAT)
        date_end = month_begin.replace(day=calendar.monthrange(month_begin.year, month_begin.month)[1]).strftime(tools.DEFAULT_SERVER_DATE_FORMAT)
        for id in ids:
            res[id] = dict()
            created_domain = [('section_id', '=', id), ('state', '=', 'draft'), ('date_order', '>=', date_begin), ('date_order', '<=', date_end)]
            res[id]['monthly_quoted'] = self.__get_bar_values(cr, uid, obj, created_domain, ['amount_total', 'date_order'], 'amount_total', 'date_order', context=context)
            validated_domain = [('section_id', '=', id), ('state', 'not in', ['draft', 'sent', 'cancel']), ('date_order', '>=', date_begin), ('date_order', '<=', date_end)]
            res[id]['monthly_confirmed'] = self.__get_bar_values(cr, uid, obj, validated_domain, ['amount_total', 'date_order'], 'amount_total', 'date_order', context=context)
        return res

    def _get_invoices_data(self, cr, uid, ids, field_name, arg, context=None):
        obj = self.pool.get('account.invoice.report')
        res = dict.fromkeys(ids, False)
        month_begin = date.today().replace(day=1)
        date_begin = (month_begin - relativedelta.relativedelta(months=self._period_number - 1)).strftime(tools.DEFAULT_SERVER_DATE_FORMAT)
        date_end = month_begin.replace(day=calendar.monthrange(month_begin.year, month_begin.month)[1]).strftime(tools.DEFAULT_SERVER_DATE_FORMAT)
        for id in ids:
            created_domain = [('section_id', '=', id), ('state', 'not in', ['draft', 'cancel']), ('date', '>=', date_begin), ('date', '<=', date_end)]
            res[id] = self.__get_bar_values(cr, uid, obj, created_domain, ['price_total', 'date'], 'price_total', 'date', context=context)
        return res

    _columns = {
        'use_quotations': fields.boolean('Opportunities', help="Check this box to manage quotations in this sales team."),
        'invoiced_forecast': fields.integer(string='Invoice Forecast',
            help="Forecast of the invoice revenue for the current month. This is the amount the sales \n"
                    "team should invoice this month. It is used to compute the progression ratio \n"
                    " of the current and forecast revenue on the kanban view."),
        'invoiced_target': fields.integer(string='Invoice Target',
            help="Target of invoice revenue for the current month. This is the amount the sales \n"
                    "team estimates to be able to invoice this month."),
        'monthly_quoted': fields.function(_get_sale_orders_data,
            type='string', readonly=True, multi='_get_sale_orders_data',
            string='Rate of created quotation per duration'),
        'monthly_confirmed': fields.function(_get_sale_orders_data,
            type='string', readonly=True, multi='_get_sale_orders_data',
            string='Rate of validate sales orders per duration'),
        'monthly_invoiced': fields.function(_get_invoices_data,
            type='string', readonly=True,
            string='Rate of sent invoices per duration'),
    }

    _defaults = {
        'use_quotations': True,
    }

    def action_forecast(self, cr, uid, id, value, context=None):
        return self.write(cr, uid, [id], {'invoiced_forecast': round(float(value))}, context=context)

# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
<commit_msg>[FIX] sale: sales_team: fixed use_quotations field named 'opportunities' instead of 'quotations', copy and paste error probably<commit_after># -*- coding: utf-8 -*-

import calendar
from datetime import date
from dateutil import relativedelta

from openerp import tools
from openerp.osv import fields, osv

class crm_case_section(osv.osv):
    _inherit = 'crm.case.section'

    def _get_sale_orders_data(self, cr, uid, ids, field_name, arg, context=None):
        obj = self.pool.get('sale.order')
        res = dict.fromkeys(ids, False)
        month_begin = date.today().replace(day=1)
        date_begin = (month_begin - relativedelta.relativedelta(months=self._period_number - 1)).strftime(tools.DEFAULT_SERVER_DATE_FORMAT)
        date_end = month_begin.replace(day=calendar.monthrange(month_begin.year, month_begin.month)[1]).strftime(tools.DEFAULT_SERVER_DATE_FORMAT)
        for id in ids:
            res[id] = dict()
            created_domain = [('section_id', '=', id), ('state', '=', 'draft'), ('date_order', '>=', date_begin), ('date_order', '<=', date_end)]
            res[id]['monthly_quoted'] = self.__get_bar_values(cr, uid, obj, created_domain, ['amount_total', 'date_order'], 'amount_total', 'date_order', context=context)
            validated_domain = [('section_id', '=', id), ('state', 'not in', ['draft', 'sent', 'cancel']), ('date_order', '>=', date_begin), ('date_order', '<=', date_end)]
            res[id]['monthly_confirmed'] = self.__get_bar_values(cr, uid, obj, validated_domain, ['amount_total', 'date_order'], 'amount_total', 'date_order', context=context)
        return res

    def _get_invoices_data(self, cr, uid, ids, field_name, arg, context=None):
        obj = self.pool.get('account.invoice.report')
        res = dict.fromkeys(ids, False)
        month_begin = date.today().replace(day=1)
        date_begin = (month_begin - relativedelta.relativedelta(months=self._period_number - 1)).strftime(tools.DEFAULT_SERVER_DATE_FORMAT)
        date_end = month_begin.replace(day=calendar.monthrange(month_begin.year, month_begin.month)[1]).strftime(tools.DEFAULT_SERVER_DATE_FORMAT)
        for id in ids:
            created_domain = [('section_id', '=', id), ('state', 'not in', ['draft', 'cancel']), ('date', '>=', date_begin), ('date', '<=', date_end)]
            res[id] = self.__get_bar_values(cr, uid, obj, created_domain, ['price_total', 'date'], 'price_total', 'date', context=context)
        return res

    _columns = {
        'use_quotations': fields.boolean('Quotations', help="Check this box to manage quotations in this sales team."),
        'invoiced_forecast': fields.integer(string='Invoice Forecast',
            help="Forecast of the invoice revenue for the current month. This is the amount the sales \n"
                    "team should invoice this month. It is used to compute the progression ratio \n"
                    " of the current and forecast revenue on the kanban view."),
        'invoiced_target': fields.integer(string='Invoice Target',
            help="Target of invoice revenue for the current month. This is the amount the sales \n"
                    "team estimates to be able to invoice this month."),
        'monthly_quoted': fields.function(_get_sale_orders_data,
            type='string', readonly=True, multi='_get_sale_orders_data',
            string='Rate of created quotation per duration'),
        'monthly_confirmed': fields.function(_get_sale_orders_data,
            type='string', readonly=True, multi='_get_sale_orders_data',
            string='Rate of validate sales orders per duration'),
        'monthly_invoiced': fields.function(_get_invoices_data,
            type='string', readonly=True,
            string='Rate of sent invoices per duration'),
    }

    _defaults = {
        'use_quotations': True,
    }

    def action_forecast(self, cr, uid, id, value, context=None):
        return self.write(cr, uid, [id], {'invoiced_forecast': round(float(value))}, context=context)

# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
<|endoftext|>