Ejemplo n.º 1
0
class Workspace(ProfileMixin, BaseNameMixin, db.Model):
    """
    Workspaces contain expense reports, budgets and categories. Workspaces
    correspond to organizations in LastUser.
    """
    __tablename__ = 'workspace'
    userid = db.Column(db.Unicode(22), nullable=False, unique=True)
    currency = db.Column(db.Unicode(3), nullable=False)
    timezone = db.Column(db.Unicode(32), nullable=False)
    description = db.Column(db.UnicodeText, nullable=False, default=u'')

    admin_teams = db.relationship('Team', secondary=workspace_admin_teams, backref='workspaces_admin')
    review_teams = db.relationship('Team', secondary=workspace_review_teams, backref='workspaces_reviewer')
    access_teams = db.relationship('Team', secondary=workspace_access_teams, backref='workspaces_access')

    def __init__(self, *args, **kwargs):
        super(Workspace, self).__init__(*args, **kwargs)

        # Add owners to admin and review teams automatically
        if self.owners and self.owners not in self.admin_teams:
            self.admin_teams.insert(0, self.owners)
        if self.owners and self.owners not in self.review_teams:
            self.review_teams.append(self.owners)

    @cached_property
    def tz(self):
        return timezone(self.timezone)

    @cached_property
    def owners(self):
        return Team.query.filter_by(orgid=self.userid, owners=True).first()

    def permissions(self, user, inherited=None):
        perms = super(Workspace, self).permissions(user, inherited)
        # No access without explicit tests
        perms.discard('view')
        perms.discard('edit')
        perms.discard('delete')

        if user:
            userteams = set(user.teams)
            if len(userteams & set(self.admin_teams)) > 0 or self.userid in user.organizations_owned_ids():
                # The user is in a team that has admin access
                perms.add('edit')
                perms.add('delete')
                perms.add('admin')
                perms.add('view')
                perms.add('new-report')
                perms.add('new-budget')
                perms.add('new-category')
            if len(userteams & set(self.review_teams)) > 0:
                # The user is in a team that has review access
                perms.add('review')
                perms.add('view')
            if len(userteams & set(self.access_teams)) > 0:
                # The user is in a team that has view/submit access
                perms.add('view')
                perms.add('new-report')
        return perms
Ejemplo n.º 2
0
class Budget(BaseScopedNameMixin, db.Model):
    """
    Budget to which expense reports can be assigned.
    """
    __tablename__ = 'budget'
    workspace_id = db.Column(db.Integer,
                             db.ForeignKey('workspace.id'),
                             nullable=False)
    workspace = db.relation(Workspace,
                            backref=db.backref('budgets',
                                               cascade='all, delete-orphan'))
    parent = db.synonym('workspace')
    #: Description of the budget. HTML field.
    description = db.Column(db.Text, nullable=False, default='')
    __table_args__ = (db.UniqueConstraint('name', 'workspace_id'), )
Ejemplo n.º 3
0
class Budget(db.Model, BaseNameMixin):
    """
    Budget to which expense reports can be assigned.
    """
    __tablename__ = 'budget'
    #: Description of the budget. HTML field.
    description = db.Column(db.Text, nullable=False, default=u'')
Ejemplo n.º 4
0
class Payment(BaseMixin, db.Model):
    __tablename__ = 'payment'
    workspace_id = db.Column(db.Integer,
                             db.ForeignKey('workspace.id'),
                             nullable=False)
    workspace = db.relation(Workspace,
                            backref=db.backref('payments',
                                               cascade='all, delete-orphan'))
    #: User who made the payment
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    user = db.relationship(User,
                           primaryjoin=user_id == User.id,
                           backref=db.backref('payouts',
                                              cascade='all, delete-orphan'))
    #: User-reported date when payment was made
    date = db.Column(db.Date, nullable=False)
    #: Currency for payment
    currency = db.Column(db.Unicode(3), nullable=False, default=u'INR')
    #: Amount of payment
    amount = db.Column(db.Numeric(10, 2), default=0, nullable=False)
Ejemplo n.º 5
0
class Expense(db.Model, BaseMixin):
    """
    Expense line item.
    """
    __tablename__ = 'expense'
    #: Id of report in which this expense is recorded
    report_id = db.Column(db.Integer,
                          db.ForeignKey('expense_report.id'),
                          nullable=False)
    #: Sequence number for sorting
    seq = db.Column(db.Integer, nullable=False, default=0)
    #: Date of expense
    date = db.Column(db.Date, nullable=False)
    #: Category of expense
    category_id = db.Column(db.Integer,
                            db.ForeignKey('category.id'),
                            nullable=False)
    category = db.relationship(Category,
                               primaryjoin=category_id == Category.id)
    #: Description
    description = db.Column(db.Unicode(250), nullable=False)
    #: Amount of expense
    amount = db.Column(db.Numeric(scale=2), default=0, nullable=False)
    #: Report in which this expense is recorded
    report = db.relationship(ExpenseReport,
                             primaryjoin=report_id == ExpenseReport.id,
                             backref=db.backref('expenses',
                                                cascade='all, delete-orphan',
                                                order_by=seq))
Ejemplo n.º 6
0
class Category(BaseScopedNameMixin, db.Model):
    """
    Expense categories.
    """
    __tablename__ = 'category'
    workspace_id = db.Column(db.Integer,
                             db.ForeignKey('workspace.id'),
                             nullable=False)
    workspace = db.relation(Workspace,
                            backref=db.backref('categories',
                                               cascade='all, delete-orphan'))
    parent = db.synonym('workspace')
    __table_args__ = (db.UniqueConstraint('name', 'workspace_id'), )
Ejemplo n.º 7
0
class Expense(BaseMixin, db.Model):
    """
    Expense line item.
    """
    __tablename__ = 'expense'
    #: Id of report in which this expense is recorded
    report_id = db.Column(db.Integer,
                          db.ForeignKey('expense_report.id'),
                          nullable=False)
    #: Sequence number for sorting
    seq = db.Column(db.Integer, nullable=False, default=0)
    #: Date of expense
    date = db.Column(db.Date, nullable=False)
    #: Category of expense
    category_id = db.Column(db.Integer,
                            db.ForeignKey('category.id'),
                            nullable=False)
    category = db.relationship(Category,
                               primaryjoin=category_id == Category.id)
    #: Description
    description = db.Column(db.Unicode(250), nullable=False)
    #: Amount of expense
    amount = db.Column(db.Numeric(10, 2), default=0, nullable=False)
    #: Report in which this expense is recorded
    report = db.relationship(ExpenseReport,
                             primaryjoin=report_id == ExpenseReport.id,
                             backref=db.backref('expenses',
                                                cascade='all, delete-orphan',
                                                order_by=seq))

    def permissions(self, user, inherited=None):
        perms = super(Expense, self).permissions(user, inherited)
        # Expenses can be deleted only if the report can be edited
        perms.discard('delete')
        if 'edit' in perms:
            perms.add('delete')
        return perms
Ejemplo n.º 8
0
class ExpenseReport(db.Model, BaseMixin):
    """
    Collection of expenses.
    """
    __tablename__ = 'expense_report'
    #: User who submitted the report
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    user = db.relationship(User,
                           primaryjoin=user_id == User.id,
                           backref=db.backref('expensereports',
                                              cascade='all, delete-orphan'))
    #: Date of report submission
    datetime = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
    title = db.Column(db.Unicode(250), nullable=False)
    #: Budget to which this report is assigned
    budget_id = db.Column(db.Integer,
                          db.ForeignKey('budget.id'),
                          nullable=True)
    budget = db.relationship(Budget, primaryjoin=budget_id == Budget.id)
    #: Currency for expenses in this report
    currency = db.Column(db.Unicode(3), nullable=False, default=u'INR')
    #: Optional description of expenses
    description = db.Column(db.Text, nullable=False, default=u'')
    #: Total value in the report's currency
    total_value = db.Column(db.Numeric(scale=2),
                            nullable=False,
                            default=Decimal('0.0'))
    #: Total value in the organization's preferred currency
    total_converted = db.Column(db.Numeric(scale=2),
                                nullable=False,
                                default=Decimal('0.0'))
    #: Reviewer
    reviewer_id = db.Column(db.Integer,
                            db.ForeignKey('user.id'),
                            nullable=True)
    reviewer = db.relationship(
        User,
        primaryjoin=reviewer_id == User.id,
        backref=db.backref('reviewed_reports',
                           cascade='all'))  # No delete-orphan
    #: Reviewer notes
    notes = db.Column(db.Text, nullable=False, default=u'')  # HTML notes
    #: Status
    status = db.Column(db.Integer, nullable=False, default=REPORT_STATUS.DRAFT)

    def update_total(self):
        self.total_value = sum([e.amount for e in self.expenses])
Ejemplo n.º 9
0
class ExpenseReport(BaseScopedIdNameMixin, db.Model):
    """
    Collection of expenses.
    """
    __tablename__ = 'expense_report'
    workspace_id = db.Column(db.Integer,
                             db.ForeignKey('workspace.id'),
                             nullable=False)
    workspace = db.relation(Workspace,
                            backref=db.backref('reports',
                                               cascade='all, delete-orphan'))
    parent = db.synonym('workspace')
    #: User who submitted the report
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    user = db.relationship(User,
                           primaryjoin=user_id == User.id,
                           backref=db.backref('expensereports',
                                              cascade='all, delete-orphan'))
    #: Date of report submission
    datetime = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
    #: Budget to which this report is assigned
    budget_id = db.Column(db.Integer,
                          db.ForeignKey('budget.id'),
                          nullable=True)
    budget = db.relationship(Budget, primaryjoin=budget_id == Budget.id)
    #: Currency for expenses in this report
    currency = db.Column(db.Unicode(3), nullable=False, default='INR')
    #: Optional description of expenses
    description = db.Column(db.Text, nullable=False, default='')
    #: Total value in the report's currency
    total_value = db.Column(db.Numeric(10, 2),
                            nullable=False,
                            default=Decimal('0.0'))
    #: Total value in the organization's preferred currency
    total_converted = db.Column(db.Numeric(10, 2),
                                nullable=False,
                                default=Decimal('0.0'))
    #: Reviewer
    reviewer_id = db.Column(db.Integer,
                            db.ForeignKey('user.id'),
                            nullable=True)
    reviewer = db.relationship(
        User,
        primaryjoin=reviewer_id == User.id,
        backref=db.backref('reviewed_reports',
                           cascade='all'))  # No delete-orphan
    #: Reviewer notes
    notes = db.Column(db.Text, nullable=False, default='')  # HTML notes
    #: Status
    status = db.Column(db.Integer, nullable=False, default=REPORT_STATUS.DRAFT)

    __table_args__ = (db.UniqueConstraint('url_id', 'workspace_id'), )

    def update_total(self):
        self.total_value = sum([e.amount for e in self.expenses])
        # self.total_value = db.session.query(
        #     db.func.sum(Expense.amount).label('sum')).filter_by(
        #         report_id = self.id).first().sum

    def update_sequence_numbers(self):
        # self.expenses is ordered by seq. See the relation defined at Expense.report
        for i, expense in enumerate(self.expenses):
            # Only edit the object if its sequence position has changed
            if expense.seq != i + 1:
                expense.seq = i + 1

    def permissions(self, user, inherited=None):
        perms = super(ExpenseReport, self).permissions(user, inherited)

        workflow = self.workflow()
        perms.discard('view')
        perms.discard('edit')
        perms.discard('delete')
        perms.discard('new-expense')

        if 'review' in perms or 'admin' in perms:
            if not workflow.draft():
                perms.add('view')

        if self.user == user:
            perms.add('owner')
            perms.add('view')
            if workflow.draft():
                perms.add('delete')
            if workflow.editable():
                perms.add('edit')
                perms.add('new-expense')
            if workflow.review():
                perms.add('withdraw')

        return perms
Ejemplo n.º 10
0
# -*- coding: utf-8 -*-

from pytz import timezone
from werkzeug.utils import cached_property
from flask_lastuser.sqlalchemy import ProfileMixin
from kharcha.models import db, BaseNameMixin, Team

__all__ = ['Workspace']

workspace_admin_teams = db.Table(
    'workspace_admin_teams', db.Model.metadata,
    db.Column('workspace_id', db.Integer, db.ForeignKey('workspace.id')),
    db.Column('team_id', db.Integer, db.ForeignKey('team.id')))

workspace_review_teams = db.Table(
    'workspace_review_teams', db.Model.metadata,
    db.Column('workspace_id', db.Integer, db.ForeignKey('workspace.id')),
    db.Column('team_id', db.Integer, db.ForeignKey('team.id')))

workspace_access_teams = db.Table(
    'workspace_access_teams', db.Model.metadata,
    db.Column('workspace_id', db.Integer, db.ForeignKey('workspace.id')),
    db.Column('team_id', db.Integer, db.ForeignKey('team.id')))


class Workspace(ProfileMixin, BaseNameMixin, db.Model):
    """
    Workspaces contain expense reports, budgets and categories. Workspaces
    correspond to organizations in LastUser.
    """
    __tablename__ = 'workspace'