rattail-harvest/rattail_harvest/db/model/harvest.py
Lance Edgar 3883a8551f Add HarvestProject.deleted flag to track deletions in Harvest
set this flag instead of deleting project, so we do not lose other
info about it.  can delete manually if truly unwanted
2022-01-30 12:14:42 -06:00

306 lines
9.4 KiB
Python

# -*- coding: utf-8; -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2022 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail 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 3 of the License, or (at your option) any later
# version.
#
# Rattail 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
# Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Harvest "cache" data models
"""
import sqlalchemy as sa
from sqlalchemy import orm
from rattail.db import model
from rattail.db.util import normalize_full_name
class HarvestUser(model.Base):
"""
Represents a user record in Harvest.
https://help.getharvest.com/api-v2/users-api/users/users/#the-user-object
"""
__tablename__ = 'harvest_user'
__table_args__ = (
sa.UniqueConstraint('id', name='harvest_user_uq_id'),
)
__versioned__ = {}
uuid = model.uuid_column()
id = sa.Column(sa.Integer(), nullable=False)
first_name = sa.Column(sa.String(length=255), nullable=True)
last_name = sa.Column(sa.String(length=255), nullable=True)
name = sa.Column(sa.String(length=255), nullable=True)
email = sa.Column(sa.String(length=255), nullable=True)
telephone = sa.Column(sa.String(length=255), nullable=True)
timezone = sa.Column(sa.String(length=255), nullable=True)
has_access_to_all_future_projects = sa.Column(sa.Boolean(), nullable=True)
is_contractor = sa.Column(sa.Boolean(), nullable=True)
is_admin = sa.Column(sa.Boolean(), nullable=True)
is_project_manager = sa.Column(sa.Boolean(), nullable=True)
can_see_rates = sa.Column(sa.Boolean(), nullable=True)
can_create_projects = sa.Column(sa.Boolean(), nullable=True)
can_create_invoices = sa.Column(sa.Boolean(), nullable=True)
is_active = sa.Column(sa.Boolean(), nullable=True)
weekly_capacity = sa.Column(sa.Integer(), nullable=True)
default_hourly_rate = sa.Column(sa.Numeric(precision=6, scale=2), nullable=True)
cost_rate = sa.Column(sa.Numeric(precision=6, scale=2), nullable=True)
# TODO
# roles = sa.Column(sa.Text(), nullable=True)
avatar_url = sa.Column(sa.String(length=255), nullable=True)
created_at = sa.Column(sa.DateTime(), nullable=True)
updated_at = sa.Column(sa.DateTime(), nullable=True)
def __str__(self):
return normalize_full_name(self.first_name, self.last_name)
class HarvestClient(model.Base):
"""
Represents a client record in Harvest.
https://help.getharvest.com/api-v2/clients-api/clients/clients/#the-client-object
"""
__tablename__ = 'harvest_client'
__table_args__ = (
sa.UniqueConstraint('id', name='harvest_client_uq_id'),
)
__versioned__ = {}
uuid = model.uuid_column()
id = sa.Column(sa.Integer(), nullable=False)
name = sa.Column(sa.String(length=255), nullable=True)
is_active = sa.Column(sa.Boolean(), nullable=True)
address = sa.Column(sa.String(length=255), nullable=True)
currency = sa.Column(sa.String(length=100), nullable=True)
created_at = sa.Column(sa.DateTime(), nullable=True)
updated_at = sa.Column(sa.DateTime(), nullable=True)
def __str__(self):
return self.name or ''
class HarvestProject(model.Base):
"""
Represents a project record in Harvest.
https://help.getharvest.com/api-v2/projects-api/projects/projects/#the-project-object
"""
__tablename__ = 'harvest_project'
__table_args__ = (
sa.UniqueConstraint('id', name='harvest_project_uq_id'),
sa.ForeignKeyConstraint(['client_id'], ['harvest_client.id'], name='harvest_project_fk_client'),
)
__versioned__ = {'exclude': ['over_budget_notification_date']}
uuid = model.uuid_column()
id = sa.Column(sa.Integer(), nullable=False)
client_id = sa.Column(sa.Integer(), nullable=True) # TODO: should not allow null?
client = orm.relationship(HarvestClient, backref=orm.backref('projects'))
name = sa.Column(sa.String(length=255), nullable=True)
code = sa.Column(sa.String(length=100), nullable=True)
is_active = sa.Column(sa.Boolean(), nullable=True)
is_billable = sa.Column(sa.Boolean(), nullable=True)
is_fixed_fee = sa.Column(sa.Boolean(), nullable=True)
bill_by = sa.Column(sa.String(length=100), nullable=True)
hourly_rate = sa.Column(sa.Numeric(precision=6, scale=2), nullable=True)
budget = sa.Column(sa.Numeric(precision=6, scale=2), nullable=True)
budget_by = sa.Column(sa.String(length=100), nullable=True)
budget_is_monthly = sa.Column(sa.Boolean(), nullable=True)
notify_when_over_budget = sa.Column(sa.Boolean(), nullable=True)
over_budget_notification_percentage = sa.Column(sa.Numeric(precision=6, scale=2), nullable=True)
over_budget_notification_date = sa.Column(sa.Date(), nullable=True)
show_budget_to_all = sa.Column(sa.Boolean(), nullable=True)
cost_budget = sa.Column(sa.Numeric(precision=9, scale=2), nullable=True)
cost_budget_include_expenses = sa.Column(sa.Boolean(), nullable=True)
fee = sa.Column(sa.Numeric(precision=8, scale=2), nullable=True)
notes = sa.Column(sa.Text(), nullable=True)
starts_on = sa.Column(sa.Date(), nullable=True)
ends_on = sa.Column(sa.Date(), nullable=True)
created_at = sa.Column(sa.DateTime(), nullable=True)
updated_at = sa.Column(sa.DateTime(), nullable=True)
deleted = sa.Column(sa.Boolean(), nullable=True, doc="""
Flag indicating the record has been deleted in Harvest.
""")
def __str__(self):
return self.name or ''
class HarvestTask(model.Base):
"""
Represents a task record in Harvest.
https://help.getharvest.com/api-v2/tasks-api/tasks/tasks/#the-task-object
"""
__tablename__ = 'harvest_task'
__table_args__ = (
sa.UniqueConstraint('id', name='harvest_task_uq_id'),
)
__versioned__ = {}
uuid = model.uuid_column()
id = sa.Column(sa.Integer(), nullable=False)
name = sa.Column(sa.String(length=255), nullable=True)
billable_by_default = sa.Column(sa.Boolean(), nullable=True)
default_hourly_rate = sa.Column(sa.Numeric(precision=6, scale=2), nullable=True)
is_default = sa.Column(sa.Boolean(), nullable=True)
is_active = sa.Column(sa.Boolean(), nullable=True)
created_at = sa.Column(sa.DateTime(), nullable=True)
updated_at = sa.Column(sa.DateTime(), nullable=True)
def __str__(self):
return self.name or ''
class HarvestTimeEntry(model.Base):
"""
Represents a time entry record in Harvest.
https://help.getharvest.com/api-v2/timesheets-api/timesheets/time-entries/#the-time-entry-object
"""
__tablename__ = 'harvest_time_entry'
__table_args__ = (
sa.UniqueConstraint('id', name='harvest_time_entry_uq_id'),
sa.ForeignKeyConstraint(['user_id'], ['harvest_user.id'], name='harvest_time_entry_fk_user'),
sa.ForeignKeyConstraint(['client_id'], ['harvest_client.id'], name='harvest_time_entry_fk_client'),
sa.ForeignKeyConstraint(['project_id'], ['harvest_project.id'], name='harvest_time_entry_fk_project'),
sa.ForeignKeyConstraint(['task_id'], ['harvest_task.id'], name='harvest_time_entry_fk_task'),
)
__versioned__ = {}
model_title_plural = "Harvest Time Entries"
uuid = model.uuid_column()
id = sa.Column(sa.Integer(), nullable=False)
spent_date = sa.Column(sa.Date(), nullable=True)
user_id = sa.Column(sa.Integer(), nullable=True)
user = orm.relationship(HarvestUser, backref=orm.backref('time_entries'))
client_id = sa.Column(sa.Integer(), nullable=True)
client = orm.relationship(HarvestClient, backref=orm.backref('time_entries'))
project_id = sa.Column(sa.Integer(), nullable=True)
project = orm.relationship(HarvestProject, backref=orm.backref('time_entries'))
task_id = sa.Column(sa.Integer(), nullable=True)
task = orm.relationship(HarvestTask, backref=orm.backref('time_entries'))
invoice_id = sa.Column(sa.Integer(), nullable=True)
hours = sa.Column(sa.Numeric(precision=6, scale=2), nullable=True)
notes = sa.Column(sa.Text(), nullable=True)
is_locked = sa.Column(sa.Boolean(), nullable=True)
locked_reason = sa.Column(sa.String(length=255), nullable=True)
is_closed = sa.Column(sa.Boolean(), nullable=True)
is_billed = sa.Column(sa.Boolean(), nullable=True)
timer_started_at = sa.Column(sa.DateTime(), nullable=True)
started_time = sa.Column(sa.DateTime(), nullable=True)
ended_time = sa.Column(sa.DateTime(), nullable=True)
is_running = sa.Column(sa.Boolean(), nullable=True)
billable = sa.Column(sa.Boolean(), nullable=True)
budgeted = sa.Column(sa.Boolean(), nullable=True)
billable_rate = sa.Column(sa.Numeric(precision=6, scale=2), nullable=True)
cost_rate = sa.Column(sa.Numeric(precision=6, scale=2), nullable=True)
created_at = sa.Column(sa.DateTime(), nullable=True)
updated_at = sa.Column(sa.DateTime(), nullable=True)
def __str__(self):
return str(self.spent_date or '')