Initial commit, w/ cache tables and importers
This commit is contained in:
		
						commit
						66864e6f67
					
				
					 19 changed files with 1368 additions and 0 deletions
				
			
		
							
								
								
									
										0
									
								
								rattail_harvest/db/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								rattail_harvest/db/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								rattail_harvest/db/alembic/versions/.keepme
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								rattail_harvest/db/alembic/versions/.keepme
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,315 @@ | |||
| # -*- coding: utf-8; -*- | ||||
| """initial Harvest tables | ||||
| 
 | ||||
| Revision ID: d59ce24c2f9f | ||||
| Revises: d8b0ba4fa795 | ||||
| Create Date: 2022-01-29 11:54:34.940773 | ||||
| 
 | ||||
| """ | ||||
| 
 | ||||
| # revision identifiers, used by Alembic. | ||||
| revision = 'd59ce24c2f9f' | ||||
| down_revision = None | ||||
| branch_labels = ('rattail_harvest',) | ||||
| depends_on = None | ||||
| 
 | ||||
| from alembic import op | ||||
| import sqlalchemy as sa | ||||
| import rattail.db.types | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| def upgrade(): | ||||
| 
 | ||||
|     # harvest_user | ||||
|     op.create_table('harvest_user', | ||||
|                     sa.Column('uuid', sa.String(length=32), nullable=False), | ||||
|                     sa.Column('id', sa.Integer(), nullable=False), | ||||
|                     sa.Column('first_name', sa.String(length=255), nullable=True), | ||||
|                     sa.Column('last_name', sa.String(length=255), nullable=True), | ||||
|                     sa.Column('name', sa.String(length=255), nullable=True), | ||||
|                     sa.Column('email', sa.String(length=255), nullable=True), | ||||
|                     sa.Column('telephone', sa.String(length=255), nullable=True), | ||||
|                     sa.Column('timezone', sa.String(length=255), nullable=True), | ||||
|                     sa.Column('has_access_to_all_future_projects', sa.Boolean(), nullable=True), | ||||
|                     sa.Column('is_contractor', sa.Boolean(), nullable=True), | ||||
|                     sa.Column('is_admin', sa.Boolean(), nullable=True), | ||||
|                     sa.Column('is_project_manager', sa.Boolean(), nullable=True), | ||||
|                     sa.Column('can_see_rates', sa.Boolean(), nullable=True), | ||||
|                     sa.Column('can_create_projects', sa.Boolean(), nullable=True), | ||||
|                     sa.Column('can_create_invoices', sa.Boolean(), nullable=True), | ||||
|                     sa.Column('is_active', sa.Boolean(), nullable=True), | ||||
|                     sa.Column('weekly_capacity', sa.Integer(), nullable=True), | ||||
|                     sa.Column('default_hourly_rate', sa.Numeric(precision=6, scale=2), nullable=True), | ||||
|                     sa.Column('cost_rate', sa.Numeric(precision=6, scale=2), nullable=True), | ||||
|                     sa.Column('avatar_url', sa.String(length=255), nullable=True), | ||||
|                     sa.Column('created_at', sa.DateTime(), nullable=True), | ||||
|                     sa.Column('updated_at', sa.DateTime(), nullable=True), | ||||
|                     sa.PrimaryKeyConstraint('uuid'), | ||||
|                     sa.UniqueConstraint('id', name='harvest_user_uq_id') | ||||
|     ) | ||||
|     op.create_table('harvest_user_version', | ||||
|                     sa.Column('uuid', sa.String(length=32), autoincrement=False, nullable=False), | ||||
|                     sa.Column('id', sa.Integer(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('first_name', sa.String(length=255), autoincrement=False, nullable=True), | ||||
|                     sa.Column('last_name', sa.String(length=255), autoincrement=False, nullable=True), | ||||
|                     sa.Column('name', sa.String(length=255), autoincrement=False, nullable=True), | ||||
|                     sa.Column('email', sa.String(length=255), autoincrement=False, nullable=True), | ||||
|                     sa.Column('telephone', sa.String(length=255), autoincrement=False, nullable=True), | ||||
|                     sa.Column('timezone', sa.String(length=255), autoincrement=False, nullable=True), | ||||
|                     sa.Column('has_access_to_all_future_projects', sa.Boolean(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('is_contractor', sa.Boolean(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('is_admin', sa.Boolean(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('is_project_manager', sa.Boolean(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('can_see_rates', sa.Boolean(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('can_create_projects', sa.Boolean(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('can_create_invoices', sa.Boolean(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('is_active', sa.Boolean(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('weekly_capacity', sa.Integer(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('default_hourly_rate', sa.Numeric(precision=6, scale=2), autoincrement=False, nullable=True), | ||||
|                     sa.Column('cost_rate', sa.Numeric(precision=6, scale=2), autoincrement=False, nullable=True), | ||||
|                     sa.Column('avatar_url', sa.String(length=255), autoincrement=False, nullable=True), | ||||
|                     sa.Column('created_at', sa.DateTime(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('updated_at', sa.DateTime(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('transaction_id', sa.BigInteger(), autoincrement=False, nullable=False), | ||||
|                     sa.Column('end_transaction_id', sa.BigInteger(), nullable=True), | ||||
|                     sa.Column('operation_type', sa.SmallInteger(), nullable=False), | ||||
|                     sa.PrimaryKeyConstraint('uuid', 'transaction_id') | ||||
|     ) | ||||
|     op.create_index(op.f('ix_harvest_user_version_end_transaction_id'), 'harvest_user_version', ['end_transaction_id'], unique=False) | ||||
|     op.create_index(op.f('ix_harvest_user_version_operation_type'), 'harvest_user_version', ['operation_type'], unique=False) | ||||
|     op.create_index(op.f('ix_harvest_user_version_transaction_id'), 'harvest_user_version', ['transaction_id'], unique=False) | ||||
| 
 | ||||
|     # harvest_client | ||||
|     op.create_table('harvest_client', | ||||
|                     sa.Column('uuid', sa.String(length=32), nullable=False), | ||||
|                     sa.Column('id', sa.Integer(), nullable=False), | ||||
|                     sa.Column('name', sa.String(length=255), nullable=True), | ||||
|                     sa.Column('is_active', sa.Boolean(), nullable=True), | ||||
|                     sa.Column('address', sa.String(length=255), nullable=True), | ||||
|                     sa.Column('currency', sa.String(length=100), nullable=True), | ||||
|                     sa.Column('created_at', sa.DateTime(), nullable=True), | ||||
|                     sa.Column('updated_at', sa.DateTime(), nullable=True), | ||||
|                     sa.PrimaryKeyConstraint('uuid'), | ||||
|                     sa.UniqueConstraint('id', name='harvest_client_uq_id') | ||||
|     ) | ||||
|     op.create_table('harvest_client_version', | ||||
|                     sa.Column('uuid', sa.String(length=32), autoincrement=False, nullable=False), | ||||
|                     sa.Column('id', sa.Integer(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('name', sa.String(length=255), autoincrement=False, nullable=True), | ||||
|                     sa.Column('is_active', sa.Boolean(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('address', sa.String(length=255), autoincrement=False, nullable=True), | ||||
|                     sa.Column('currency', sa.String(length=100), autoincrement=False, nullable=True), | ||||
|                     sa.Column('created_at', sa.DateTime(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('updated_at', sa.DateTime(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('transaction_id', sa.BigInteger(), autoincrement=False, nullable=False), | ||||
|                     sa.Column('end_transaction_id', sa.BigInteger(), nullable=True), | ||||
|                     sa.Column('operation_type', sa.SmallInteger(), nullable=False), | ||||
|                     sa.PrimaryKeyConstraint('uuid', 'transaction_id') | ||||
|     ) | ||||
|     op.create_index(op.f('ix_harvest_client_version_end_transaction_id'), 'harvest_client_version', ['end_transaction_id'], unique=False) | ||||
|     op.create_index(op.f('ix_harvest_client_version_operation_type'), 'harvest_client_version', ['operation_type'], unique=False) | ||||
|     op.create_index(op.f('ix_harvest_client_version_transaction_id'), 'harvest_client_version', ['transaction_id'], unique=False) | ||||
| 
 | ||||
|     # harvest_project | ||||
|     op.create_table('harvest_project', | ||||
|                     sa.Column('uuid', sa.String(length=32), nullable=False), | ||||
|                     sa.Column('id', sa.Integer(), nullable=False), | ||||
|                     sa.Column('client_id', sa.Integer(), nullable=True), | ||||
|                     sa.Column('name', sa.String(length=255), nullable=True), | ||||
|                     sa.Column('code', sa.String(length=100), nullable=True), | ||||
|                     sa.Column('is_active', sa.Boolean(), nullable=True), | ||||
|                     sa.Column('is_billable', sa.Boolean(), nullable=True), | ||||
|                     sa.Column('is_fixed_fee', sa.Boolean(), nullable=True), | ||||
|                     sa.Column('bill_by', sa.String(length=100), nullable=True), | ||||
|                     sa.Column('hourly_rate', sa.Numeric(precision=6, scale=2), nullable=True), | ||||
|                     sa.Column('budget', sa.Numeric(precision=6, scale=2), nullable=True), | ||||
|                     sa.Column('budget_by', sa.String(length=100), nullable=True), | ||||
|                     sa.Column('budget_is_monthly', sa.Boolean(), nullable=True), | ||||
|                     sa.Column('notify_when_over_budget', sa.Boolean(), nullable=True), | ||||
|                     sa.Column('over_budget_notification_percentage', sa.Numeric(precision=6, scale=2), nullable=True), | ||||
|                     sa.Column('over_budget_notification_date', sa.Date(), nullable=True), | ||||
|                     sa.Column('show_budget_to_all', sa.Boolean(), nullable=True), | ||||
|                     sa.Column('cost_budget', sa.Numeric(precision=9, scale=2), nullable=True), | ||||
|                     sa.Column('cost_budget_include_expenses', sa.Boolean(), nullable=True), | ||||
|                     sa.Column('fee', sa.Numeric(precision=8, scale=2), nullable=True), | ||||
|                     sa.Column('notes', sa.Text(), nullable=True), | ||||
|                     sa.Column('starts_on', sa.Date(), nullable=True), | ||||
|                     sa.Column('ends_on', sa.Date(), nullable=True), | ||||
|                     sa.Column('created_at', sa.DateTime(), nullable=True), | ||||
|                     sa.Column('updated_at', sa.DateTime(), nullable=True), | ||||
|                     sa.ForeignKeyConstraint(['client_id'], ['harvest_client.id'], name='harvest_project_fk_client'), | ||||
|                     sa.PrimaryKeyConstraint('uuid'), | ||||
|                     sa.UniqueConstraint('id', name='harvest_project_uq_id') | ||||
|     ) | ||||
|     op.create_table('harvest_project_version', | ||||
|                     sa.Column('uuid', sa.String(length=32), autoincrement=False, nullable=False), | ||||
|                     sa.Column('id', sa.Integer(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('client_id', sa.Integer(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('name', sa.String(length=255), autoincrement=False, nullable=True), | ||||
|                     sa.Column('code', sa.String(length=100), autoincrement=False, nullable=True), | ||||
|                     sa.Column('is_active', sa.Boolean(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('is_billable', sa.Boolean(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('is_fixed_fee', sa.Boolean(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('bill_by', sa.String(length=100), autoincrement=False, nullable=True), | ||||
|                     sa.Column('hourly_rate', sa.Numeric(precision=6, scale=2), autoincrement=False, nullable=True), | ||||
|                     sa.Column('budget', sa.Numeric(precision=6, scale=2), autoincrement=False, nullable=True), | ||||
|                     sa.Column('budget_by', sa.String(length=100), autoincrement=False, nullable=True), | ||||
|                     sa.Column('budget_is_monthly', sa.Boolean(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('notify_when_over_budget', sa.Boolean(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('over_budget_notification_percentage', sa.Numeric(precision=6, scale=2), autoincrement=False, nullable=True), | ||||
|                     sa.Column('show_budget_to_all', sa.Boolean(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('cost_budget', sa.Numeric(precision=9, scale=2), autoincrement=False, nullable=True), | ||||
|                     sa.Column('cost_budget_include_expenses', sa.Boolean(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('fee', sa.Numeric(precision=8, scale=2), autoincrement=False, nullable=True), | ||||
|                     sa.Column('notes', sa.Text(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('starts_on', sa.Date(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('ends_on', sa.Date(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('created_at', sa.DateTime(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('updated_at', sa.DateTime(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('transaction_id', sa.BigInteger(), autoincrement=False, nullable=False), | ||||
|                     sa.Column('end_transaction_id', sa.BigInteger(), nullable=True), | ||||
|                     sa.Column('operation_type', sa.SmallInteger(), nullable=False), | ||||
|                     sa.PrimaryKeyConstraint('uuid', 'transaction_id') | ||||
|     ) | ||||
|     op.create_index(op.f('ix_harvest_project_version_end_transaction_id'), 'harvest_project_version', ['end_transaction_id'], unique=False) | ||||
|     op.create_index(op.f('ix_harvest_project_version_operation_type'), 'harvest_project_version', ['operation_type'], unique=False) | ||||
|     op.create_index(op.f('ix_harvest_project_version_transaction_id'), 'harvest_project_version', ['transaction_id'], unique=False) | ||||
| 
 | ||||
|     # harvest_task | ||||
|     op.create_table('harvest_task', | ||||
|                     sa.Column('uuid', sa.String(length=32), nullable=False), | ||||
|                     sa.Column('id', sa.Integer(), nullable=False), | ||||
|                     sa.Column('name', sa.String(length=255), nullable=True), | ||||
|                     sa.Column('billable_by_default', sa.Boolean(), nullable=True), | ||||
|                     sa.Column('default_hourly_rate', sa.Numeric(precision=6, scale=2), nullable=True), | ||||
|                     sa.Column('is_default', sa.Boolean(), nullable=True), | ||||
|                     sa.Column('is_active', sa.Boolean(), nullable=True), | ||||
|                     sa.Column('created_at', sa.DateTime(), nullable=True), | ||||
|                     sa.Column('updated_at', sa.DateTime(), nullable=True), | ||||
|                     sa.PrimaryKeyConstraint('uuid'), | ||||
|                     sa.UniqueConstraint('id', name='harvest_task_uq_id') | ||||
|     ) | ||||
|     op.create_table('harvest_task_version', | ||||
|                     sa.Column('uuid', sa.String(length=32), autoincrement=False, nullable=False), | ||||
|                     sa.Column('id', sa.Integer(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('name', sa.String(length=255), autoincrement=False, nullable=True), | ||||
|                     sa.Column('billable_by_default', sa.Boolean(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('default_hourly_rate', sa.Numeric(precision=6, scale=2), autoincrement=False, nullable=True), | ||||
|                     sa.Column('is_default', sa.Boolean(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('is_active', sa.Boolean(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('created_at', sa.DateTime(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('updated_at', sa.DateTime(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('transaction_id', sa.BigInteger(), autoincrement=False, nullable=False), | ||||
|                     sa.Column('end_transaction_id', sa.BigInteger(), nullable=True), | ||||
|                     sa.Column('operation_type', sa.SmallInteger(), nullable=False), | ||||
|                     sa.PrimaryKeyConstraint('uuid', 'transaction_id') | ||||
|     ) | ||||
|     op.create_index(op.f('ix_harvest_task_version_end_transaction_id'), 'harvest_task_version', ['end_transaction_id'], unique=False) | ||||
|     op.create_index(op.f('ix_harvest_task_version_operation_type'), 'harvest_task_version', ['operation_type'], unique=False) | ||||
|     op.create_index(op.f('ix_harvest_task_version_transaction_id'), 'harvest_task_version', ['transaction_id'], unique=False) | ||||
| 
 | ||||
|     # harvest_time_entry | ||||
|     op.create_table('harvest_time_entry', | ||||
|                     sa.Column('uuid', sa.String(length=32), nullable=False), | ||||
|                     sa.Column('id', sa.Integer(), nullable=False), | ||||
|                     sa.Column('spent_date', sa.Date(), nullable=True), | ||||
|                     sa.Column('user_id', sa.Integer(), nullable=True), | ||||
|                     sa.Column('client_id', sa.Integer(), nullable=True), | ||||
|                     sa.Column('project_id', sa.Integer(), nullable=True), | ||||
|                     sa.Column('task_id', sa.Integer(), nullable=True), | ||||
|                     sa.Column('invoice_id', sa.Integer(), nullable=True), | ||||
|                     sa.Column('hours', sa.Numeric(precision=6, scale=2), nullable=True), | ||||
|                     sa.Column('notes', sa.Text(), nullable=True), | ||||
|                     sa.Column('is_locked', sa.Boolean(), nullable=True), | ||||
|                     sa.Column('locked_reason', sa.String(length=255), nullable=True), | ||||
|                     sa.Column('is_closed', sa.Boolean(), nullable=True), | ||||
|                     sa.Column('is_billed', sa.Boolean(), nullable=True), | ||||
|                     sa.Column('timer_started_at', sa.DateTime(), nullable=True), | ||||
|                     sa.Column('started_time', sa.DateTime(), nullable=True), | ||||
|                     sa.Column('ended_time', sa.DateTime(), nullable=True), | ||||
|                     sa.Column('is_running', sa.Boolean(), nullable=True), | ||||
|                     sa.Column('billable', sa.Boolean(), nullable=True), | ||||
|                     sa.Column('budgeted', sa.Boolean(), nullable=True), | ||||
|                     sa.Column('billable_rate', sa.Numeric(precision=6, scale=2), nullable=True), | ||||
|                     sa.Column('cost_rate', sa.Numeric(precision=6, scale=2), nullable=True), | ||||
|                     sa.Column('created_at', sa.DateTime(), nullable=True), | ||||
|                     sa.Column('updated_at', sa.DateTime(), nullable=True), | ||||
|                     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'), | ||||
|                     sa.ForeignKeyConstraint(['user_id'], ['harvest_user.id'], name='harvest_time_entry_fk_user'), | ||||
|                     sa.PrimaryKeyConstraint('uuid'), | ||||
|                     sa.UniqueConstraint('id', name='harvest_time_entry_uq_id') | ||||
|     ) | ||||
|     op.create_table('harvest_time_entry_version', | ||||
|                     sa.Column('uuid', sa.String(length=32), autoincrement=False, nullable=False), | ||||
|                     sa.Column('id', sa.Integer(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('spent_date', sa.Date(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('user_id', sa.Integer(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('client_id', sa.Integer(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('project_id', sa.Integer(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('task_id', sa.Integer(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('invoice_id', sa.Integer(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('hours', sa.Numeric(precision=6, scale=2), autoincrement=False, nullable=True), | ||||
|                     sa.Column('notes', sa.Text(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('is_locked', sa.Boolean(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('locked_reason', sa.String(length=255), autoincrement=False, nullable=True), | ||||
|                     sa.Column('is_closed', sa.Boolean(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('is_billed', sa.Boolean(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('timer_started_at', sa.DateTime(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('started_time', sa.DateTime(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('ended_time', sa.DateTime(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('is_running', sa.Boolean(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('billable', sa.Boolean(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('budgeted', sa.Boolean(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('billable_rate', sa.Numeric(precision=6, scale=2), autoincrement=False, nullable=True), | ||||
|                     sa.Column('cost_rate', sa.Numeric(precision=6, scale=2), autoincrement=False, nullable=True), | ||||
|                     sa.Column('created_at', sa.DateTime(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('updated_at', sa.DateTime(), autoincrement=False, nullable=True), | ||||
|                     sa.Column('transaction_id', sa.BigInteger(), autoincrement=False, nullable=False), | ||||
|                     sa.Column('end_transaction_id', sa.BigInteger(), nullable=True), | ||||
|                     sa.Column('operation_type', sa.SmallInteger(), nullable=False), | ||||
|                     sa.PrimaryKeyConstraint('uuid', 'transaction_id') | ||||
|     ) | ||||
|     op.create_index(op.f('ix_harvest_time_entry_version_end_transaction_id'), 'harvest_time_entry_version', ['end_transaction_id'], unique=False) | ||||
|     op.create_index(op.f('ix_harvest_time_entry_version_operation_type'), 'harvest_time_entry_version', ['operation_type'], unique=False) | ||||
|     op.create_index(op.f('ix_harvest_time_entry_version_transaction_id'), 'harvest_time_entry_version', ['transaction_id'], unique=False) | ||||
| 
 | ||||
| 
 | ||||
| def downgrade(): | ||||
| 
 | ||||
|     # harvest_time_entry | ||||
|     op.drop_index(op.f('ix_harvest_time_entry_version_transaction_id'), table_name='harvest_time_entry_version') | ||||
|     op.drop_index(op.f('ix_harvest_time_entry_version_operation_type'), table_name='harvest_time_entry_version') | ||||
|     op.drop_index(op.f('ix_harvest_time_entry_version_end_transaction_id'), table_name='harvest_time_entry_version') | ||||
|     op.drop_table('harvest_time_entry_version') | ||||
|     op.drop_table('harvest_time_entry') | ||||
| 
 | ||||
|     # harvest_task | ||||
|     op.drop_index(op.f('ix_harvest_task_version_transaction_id'), table_name='harvest_task_version') | ||||
|     op.drop_index(op.f('ix_harvest_task_version_operation_type'), table_name='harvest_task_version') | ||||
|     op.drop_index(op.f('ix_harvest_task_version_end_transaction_id'), table_name='harvest_task_version') | ||||
|     op.drop_table('harvest_task_version') | ||||
|     op.drop_table('harvest_task') | ||||
| 
 | ||||
|     # harvest_project | ||||
|     op.drop_index(op.f('ix_harvest_project_version_transaction_id'), table_name='harvest_project_version') | ||||
|     op.drop_index(op.f('ix_harvest_project_version_operation_type'), table_name='harvest_project_version') | ||||
|     op.drop_index(op.f('ix_harvest_project_version_end_transaction_id'), table_name='harvest_project_version') | ||||
|     op.drop_table('harvest_project_version') | ||||
|     op.drop_table('harvest_project') | ||||
| 
 | ||||
|     # harvest_client | ||||
|     op.drop_index(op.f('ix_harvest_client_version_transaction_id'), table_name='harvest_client_version') | ||||
|     op.drop_index(op.f('ix_harvest_client_version_operation_type'), table_name='harvest_client_version') | ||||
|     op.drop_index(op.f('ix_harvest_client_version_end_transaction_id'), table_name='harvest_client_version') | ||||
|     op.drop_table('harvest_client_version') | ||||
|     op.drop_table('harvest_client') | ||||
| 
 | ||||
|     # harvest_user | ||||
|     op.drop_index(op.f('ix_harvest_user_version_transaction_id'), table_name='harvest_user_version') | ||||
|     op.drop_index(op.f('ix_harvest_user_version_operation_type'), table_name='harvest_user_version') | ||||
|     op.drop_index(op.f('ix_harvest_user_version_end_transaction_id'), table_name='harvest_user_version') | ||||
|     op.drop_table('harvest_user_version') | ||||
|     op.drop_table('harvest_user') | ||||
							
								
								
									
										28
									
								
								rattail_harvest/db/model/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								rattail_harvest/db/model/__init__.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,28 @@ | |||
| # -*- 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 integration data models | ||||
| """ | ||||
| 
 | ||||
| from .harvest import (HarvestUser, HarvestClient, HarvestProject, | ||||
|                       HarvestTask, HarvestTimeEntry) | ||||
							
								
								
									
										301
									
								
								rattail_harvest/db/model/harvest.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										301
									
								
								rattail_harvest/db/model/harvest.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,301 @@ | |||
| # -*- 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) | ||||
| 
 | ||||
|     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 '') | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Lance Edgar
						Lance Edgar