feat: implement complete CMT backend with API endpoints and test suite
- Add 7 core API endpoints: users, transactions, partners, products, inventory, payments, credit - Implement role-based authentication (admin/write/read-only access) - Add comprehensive database models with proper relationships - Include full test coverage for all endpoints and business logic - Set up Alembic migrations and Docker configuration - Configure FastAPI app with CORS and database integration
This commit is contained in:
@@ -0,0 +1,206 @@
|
||||
"""Remove payment_method ENUM - Complexity not worth it when CheckConstraint can serve
|
||||
|
||||
Revision ID: 997376dc1774
|
||||
Revises: e777b1b307b5
|
||||
Create Date: 2025-08-25 23:18:53.106182
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = '997376dc1774'
|
||||
down_revision: Union[str, Sequence[str], None] = 'e777b1b307b5'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
import sqlmodel.sql.sqltypes
|
||||
|
||||
def upgrade() -> None:
|
||||
"""Upgrade schema."""
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('partner',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('tin_number', sa.Integer(), nullable=False),
|
||||
sa.Column('names', sqlmodel.sql.sqltypes.AutoString(length=100), nullable=False),
|
||||
sa.Column('type', sa.Enum('CLIENT', 'SUPPLIER', name='partnertype'), nullable=False),
|
||||
sa.Column('phone_number', sqlmodel.sql.sqltypes.AutoString(length=10), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('tin_number')
|
||||
)
|
||||
op.create_table('user',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('username', sqlmodel.sql.sqltypes.AutoString(length=100), nullable=False),
|
||||
sa.Column('role', sa.Enum('ADMIN', 'WRITE', 'READ_ONLY', name='userrole'), nullable=False),
|
||||
sa.Column('password_hash', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('username')
|
||||
)
|
||||
op.create_table('inventory',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('product_id', sa.Integer(), nullable=False),
|
||||
sa.Column('total_qty', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['product_id'], ['product.id'], ),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('product_id')
|
||||
)
|
||||
op.create_table('transaction_details',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('partner_id', sa.Integer(), nullable=False),
|
||||
sa.Column('product_id', sa.Integer(), nullable=False),
|
||||
sa.Column('qty', sa.Integer(), nullable=False),
|
||||
sa.Column('selling_price', sa.Integer(), nullable=False),
|
||||
sa.Column('total_value', sa.Integer(), nullable=False),
|
||||
sa.Column('created_by', sa.Integer(), nullable=False),
|
||||
sa.Column('updated_by', sa.Integer(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True),
|
||||
sa.ForeignKeyConstraint(['created_by'], ['user.id'], ),
|
||||
sa.ForeignKeyConstraint(['partner_id'], ['partner.id'], ),
|
||||
sa.ForeignKeyConstraint(['product_id'], ['product.id'], ),
|
||||
sa.ForeignKeyConstraint(['updated_by'], ['user.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('transactions',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('partner_id', sa.Integer(), nullable=False),
|
||||
sa.Column('transcation_type', sa.Enum('SALE', 'PURCHASE', 'CREDIT', name='transactiontype'), nullable=False),
|
||||
sa.Column('transaction_status', sa.Enum('UNPAID', 'PARTIALLY_PAID', 'PAID', 'CANCELLED', name='transactionstatus'), nullable=False),
|
||||
sa.Column('total_amount', sa.Integer(), nullable=False),
|
||||
sa.Column('created_by', sa.Integer(), nullable=False),
|
||||
sa.Column('updated_by', sa.Integer(), nullable=False),
|
||||
sa.Column('created_on', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True),
|
||||
sa.Column('updated_on', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True),
|
||||
sa.ForeignKeyConstraint(['created_by'], ['user.id'], ),
|
||||
sa.ForeignKeyConstraint(['partner_id'], ['partner.id'], ),
|
||||
sa.ForeignKeyConstraint(['updated_by'], ['user.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('credit_accounts',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('partner_id', sa.Integer(), nullable=False),
|
||||
sa.Column('transaction_id', sa.Integer(), nullable=False),
|
||||
sa.Column('credit_amount', sa.Integer(), nullable=False),
|
||||
sa.Column('credit_limit', sa.Integer(), nullable=False),
|
||||
sa.Column('balance', sa.Integer(), nullable=False),
|
||||
sa.Column('created_by', sa.Integer(), nullable=False),
|
||||
sa.Column('updated_by', sa.Integer(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True),
|
||||
sa.ForeignKeyConstraint(['created_by'], ['user.id'], ),
|
||||
sa.ForeignKeyConstraint(['partner_id'], ['partner.id'], ),
|
||||
sa.ForeignKeyConstraint(['transaction_id'], ['transactions.id'], ),
|
||||
sa.ForeignKeyConstraint(['updated_by'], ['user.id'], ),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('partner_id')
|
||||
)
|
||||
|
||||
op.add_column('payment', sa.Column('transaction_id', sa.Integer(), nullable=False))
|
||||
op.add_column('payment', sa.Column('paid_amount', sa.Integer(), nullable=False))
|
||||
op.add_column('payment', sa.Column('payment_date', sa.Date(), nullable=False))
|
||||
op.add_column('payment', sa.Column('created_by', sa.Integer(), nullable=False))
|
||||
op.add_column('payment', sa.Column('updated_by', sa.Integer(), nullable=False))
|
||||
op.add_column('payment', sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True))
|
||||
op.add_column('payment', sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True))
|
||||
op.alter_column('payment', 'payment_method',
|
||||
existing_type=sa.VARCHAR(length=24),
|
||||
type_=sa.String(length=10),
|
||||
existing_nullable=False)
|
||||
op.drop_constraint(op.f('payment_client_id_fkey'), 'payment', type_='foreignkey')
|
||||
op.drop_constraint(op.f('payment_supplier_id_fkey'), 'payment', type_='foreignkey')
|
||||
op.drop_constraint(op.f('payment_product_code_fkey'), 'payment', type_='foreignkey')
|
||||
op.drop_table('credit')
|
||||
op.drop_table('supplier')
|
||||
op.drop_table('client')
|
||||
|
||||
op.create_foreign_key(None, 'payment', 'transactions', ['transaction_id'], ['id'])
|
||||
op.create_foreign_key(None, 'payment', 'user', ['created_by'], ['id'])
|
||||
op.create_foreign_key(None, 'payment', 'user', ['updated_by'], ['id'])
|
||||
op.drop_column('payment', 'product_code')
|
||||
op.drop_column('payment', 'payment_type')
|
||||
op.drop_column('payment', 'date')
|
||||
op.drop_column('payment', 'amount')
|
||||
op.drop_column('payment', 'client_id')
|
||||
op.drop_column('payment', 'supplier_id')
|
||||
op.add_column('product', sa.Column('selling_price', sa.Integer(), nullable=False))
|
||||
op.alter_column('product', 'date_modified',
|
||||
existing_type=postgresql.TIMESTAMP(),
|
||||
type_=sa.DateTime(timezone=True),
|
||||
existing_nullable=True,
|
||||
existing_server_default=sa.text('now()'))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
"""Downgrade schema."""
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column('product', 'date_modified',
|
||||
existing_type=sa.DateTime(timezone=True),
|
||||
type_=postgresql.TIMESTAMP(),
|
||||
existing_nullable=True,
|
||||
existing_server_default=sa.text('now()'))
|
||||
op.drop_column('product', 'selling_price')
|
||||
op.add_column('payment', sa.Column('supplier_id', sa.INTEGER(), autoincrement=False, nullable=False))
|
||||
op.add_column('payment', sa.Column('client_id', sa.INTEGER(), autoincrement=False, nullable=False))
|
||||
op.add_column('payment', sa.Column('amount', sa.INTEGER(), autoincrement=False, nullable=False))
|
||||
op.add_column('payment', sa.Column('date', postgresql.TIMESTAMP(), server_default=sa.text('now()'), autoincrement=False, nullable=True))
|
||||
op.add_column('payment', sa.Column('payment_type', postgresql.ENUM('BUY', 'SELL', name='tradetype'), autoincrement=False, nullable=False))
|
||||
op.add_column('payment', sa.Column('product_code', sa.VARCHAR(), autoincrement=False, nullable=False))
|
||||
op.drop_constraint(None, 'payment', type_='foreignkey')
|
||||
op.drop_constraint(None, 'payment', type_='foreignkey')
|
||||
op.drop_constraint(None, 'payment', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('payment_product_code_fkey'), 'payment', 'product', ['product_code'], ['product_code'])
|
||||
op.create_foreign_key(op.f('payment_supplier_id_fkey'), 'payment', 'supplier', ['supplier_id'], ['id'])
|
||||
op.create_foreign_key(op.f('payment_client_id_fkey'), 'payment', 'client', ['client_id'], ['id'])
|
||||
op.alter_column('payment', 'payment_method',
|
||||
existing_type=sa.String(length=10),
|
||||
type_=sa.VARCHAR(length=24),
|
||||
existing_nullable=False)
|
||||
op.drop_column('payment', 'updated_at')
|
||||
op.drop_column('payment', 'created_at')
|
||||
op.drop_column('payment', 'updated_by')
|
||||
op.drop_column('payment', 'created_by')
|
||||
op.drop_column('payment', 'payment_date')
|
||||
op.drop_column('payment', 'paid_amount')
|
||||
op.drop_column('payment', 'transaction_id')
|
||||
op.create_table('client',
|
||||
sa.Column('id', sa.INTEGER(), server_default=sa.text("nextval('client_id_seq'::regclass)"), autoincrement=True, nullable=False),
|
||||
sa.Column('tin_number', sa.INTEGER(), autoincrement=False, nullable=False),
|
||||
sa.Column('names', sa.VARCHAR(length=100), autoincrement=False, nullable=False),
|
||||
sa.Column('phone_number', sa.VARCHAR(length=10), autoincrement=False, nullable=False),
|
||||
sa.PrimaryKeyConstraint('id', name='client_pkey'),
|
||||
sa.UniqueConstraint('tin_number', name='client_tin_number_key', postgresql_include=[], postgresql_nulls_not_distinct=False),
|
||||
postgresql_ignore_search_path=False
|
||||
)
|
||||
op.create_table('credit',
|
||||
sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False),
|
||||
sa.Column('transcation_type', postgresql.ENUM('BUY', 'SELL', name='tradetype'), autoincrement=False, nullable=False),
|
||||
sa.Column('product_code', sa.VARCHAR(), autoincrement=False, nullable=False),
|
||||
sa.Column('client_id', sa.INTEGER(), autoincrement=False, nullable=False),
|
||||
sa.Column('supplier_id', sa.INTEGER(), autoincrement=False, nullable=False),
|
||||
sa.Column('qty', sa.INTEGER(), autoincrement=False, nullable=False),
|
||||
sa.Column('amount', sa.INTEGER(), autoincrement=False, nullable=False),
|
||||
sa.Column('date', postgresql.TIMESTAMP(), server_default=sa.text('now()'), autoincrement=False, nullable=True),
|
||||
sa.ForeignKeyConstraint(['client_id'], ['client.id'], name=op.f('credit_client_id_fkey')),
|
||||
sa.ForeignKeyConstraint(['product_code'], ['product.product_code'], name=op.f('credit_product_code_fkey')),
|
||||
sa.ForeignKeyConstraint(['supplier_id'], ['supplier.id'], name=op.f('credit_supplier_id_fkey')),
|
||||
sa.PrimaryKeyConstraint('id', name=op.f('credit_pkey'))
|
||||
)
|
||||
op.create_table('supplier',
|
||||
sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False),
|
||||
sa.Column('tin_number', sa.INTEGER(), autoincrement=False, nullable=False),
|
||||
sa.Column('names', sa.VARCHAR(length=100), autoincrement=False, nullable=False),
|
||||
sa.Column('phone_number', sa.VARCHAR(length=10), autoincrement=False, nullable=False),
|
||||
sa.PrimaryKeyConstraint('id', name=op.f('supplier_pkey')),
|
||||
sa.UniqueConstraint('tin_number', name=op.f('supplier_tin_number_key'), postgresql_include=[], postgresql_nulls_not_distinct=False)
|
||||
)
|
||||
op.drop_table('credit_accounts')
|
||||
op.drop_table('transactions')
|
||||
op.drop_table('transaction_details')
|
||||
op.drop_table('inventory')
|
||||
op.drop_table('user')
|
||||
op.drop_table('partner')
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,203 @@
|
||||
"""Update table models to better logic & table relationship (Transaction + payment feed into credit accounts etc...)
|
||||
|
||||
Revision ID: a4126dbcfd9e
|
||||
Revises: 4966e016dd7c
|
||||
Create Date: 2025-08-25 22:25:57.071318
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
import sqlmodel.sql.sqltypes
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = 'a4126dbcfd9e'
|
||||
down_revision: Union[str, Sequence[str], None] = '4966e016dd7c'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
"""Upgrade schema."""
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('partner',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('tin_number', sa.Integer(), nullable=False),
|
||||
sa.Column('names', sqlmodel.sql.sqltypes.AutoString(length=100), nullable=False),
|
||||
sa.Column('type', sa.Enum('CLIENT', 'SUPPLIER', name='partnertype'), nullable=False),
|
||||
sa.Column('phone_number', sqlmodel.sql.sqltypes.AutoString(length=10), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('tin_number')
|
||||
)
|
||||
op.create_table('user',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('username', sqlmodel.sql.sqltypes.AutoString(length=100), nullable=False),
|
||||
sa.Column('role', sa.Enum('ADMIN', 'WRITE', 'READ_ONLY', name='userrole'), nullable=False),
|
||||
sa.Column('password_hash', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('username')
|
||||
)
|
||||
op.create_table('inventory',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('product_id', sa.Integer(), nullable=False),
|
||||
sa.Column('total_qty', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['product_id'], ['product.id'], ),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('product_id')
|
||||
)
|
||||
op.create_table('transaction_details',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('partner_id', sa.Integer(), nullable=False),
|
||||
sa.Column('product_id', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
|
||||
sa.Column('qty', sa.Integer(), nullable=False),
|
||||
sa.Column('selling_price', sa.Integer(), nullable=False),
|
||||
sa.Column('total_value', sa.Integer(), nullable=False),
|
||||
sa.Column('created_by', sa.Integer(), nullable=False),
|
||||
sa.Column('updated_by', sa.Integer(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True),
|
||||
sa.ForeignKeyConstraint(['created_by'], ['user.id'], ),
|
||||
sa.ForeignKeyConstraint(['partner_id'], ['partner.id'], ),
|
||||
sa.ForeignKeyConstraint(['product_id'], ['product.id'], ),
|
||||
sa.ForeignKeyConstraint(['updated_by'], ['user.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('transactions',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('partner_id', sa.Integer(), nullable=False),
|
||||
sa.Column('transcation_type', sa.Enum('SALE', 'PURCHASE', 'CREDIT', name='transactiontype'), nullable=False),
|
||||
sa.Column('transaction_status', sa.Enum('UNPAID', 'PARTIALLY_PAID', 'PAID', 'CANCELLED', name='transactionstatus'), nullable=False),
|
||||
sa.Column('total_amount', sa.Integer(), nullable=False),
|
||||
sa.Column('created_by', sa.Integer(), nullable=False),
|
||||
sa.Column('updated_by', sa.Integer(), nullable=False),
|
||||
sa.Column('created_on', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True),
|
||||
sa.Column('updated_on', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True),
|
||||
sa.ForeignKeyConstraint(['created_by'], ['user.id'], ),
|
||||
sa.ForeignKeyConstraint(['partner_id'], ['partner.id'], ),
|
||||
sa.ForeignKeyConstraint(['updated_by'], ['user.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('credit_accounts',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('partner_id', sa.Integer(), nullable=False),
|
||||
sa.Column('transaction_id', sa.Integer(), nullable=False),
|
||||
sa.Column('credit_amount', sa.Integer(), nullable=False),
|
||||
sa.Column('credit_limit', sa.Integer(), nullable=False),
|
||||
sa.Column('balance', sa.Integer(), nullable=False),
|
||||
sa.Column('created_by', sa.Integer(), nullable=False),
|
||||
sa.Column('updated_by', sa.Integer(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True),
|
||||
sa.ForeignKeyConstraint(['created_by'], ['user.id'], ),
|
||||
sa.ForeignKeyConstraint(['partner_id'], ['partner.id'], ),
|
||||
sa.ForeignKeyConstraint(['transaction_id'], ['transactions.id'], ),
|
||||
sa.ForeignKeyConstraint(['updated_by'], ['user.id'], ),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('partner_id')
|
||||
)
|
||||
op.drop_table('client')
|
||||
op.drop_table('supplier')
|
||||
op.drop_table('credit')
|
||||
op.add_column('payment', sa.Column('transaction_id', sa.Integer(), nullable=False))
|
||||
op.add_column('payment', sa.Column('paid_amount', sa.Integer(), nullable=False))
|
||||
op.add_column('payment', sa.Column('payment_date', sa.Date(), nullable=False))
|
||||
op.add_column('payment', sa.Column('created_by', sa.Integer(), nullable=False))
|
||||
op.add_column('payment', sa.Column('updated_by', sa.Integer(), nullable=False))
|
||||
op.add_column('payment', sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True))
|
||||
op.add_column('payment', sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True))
|
||||
op.alter_column('payment', 'payment_method',
|
||||
existing_type=sa.VARCHAR(length=24),
|
||||
type_=sa.Enum('MOMO', 'BANK', 'CASH', name='paymentmethod'),
|
||||
existing_nullable=False)
|
||||
op.drop_constraint(op.f('payment_supplier_id_fkey'), 'payment', type_='foreignkey')
|
||||
op.drop_constraint(op.f('payment_client_id_fkey'), 'payment', type_='foreignkey')
|
||||
op.drop_constraint(op.f('payment_product_code_fkey'), 'payment', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'payment', 'transactions', ['transaction_id'], ['id'])
|
||||
op.create_foreign_key(None, 'payment', 'user', ['updated_by'], ['id'])
|
||||
op.create_foreign_key(None, 'payment', 'user', ['created_by'], ['id'])
|
||||
op.drop_column('payment', 'product_code')
|
||||
op.drop_column('payment', 'supplier_id')
|
||||
op.drop_column('payment', 'client_id')
|
||||
op.drop_column('payment', 'amount')
|
||||
op.drop_column('payment', 'date')
|
||||
op.drop_column('payment', 'payment_type')
|
||||
op.add_column('product', sa.Column('selling_price', sa.Integer(), nullable=False))
|
||||
op.alter_column('product', 'date_modified',
|
||||
existing_type=postgresql.TIMESTAMP(),
|
||||
type_=sa.DateTime(timezone=True),
|
||||
existing_nullable=True,
|
||||
existing_server_default=sa.text('now()'))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
"""Downgrade schema."""
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column('product', 'date_modified',
|
||||
existing_type=sa.DateTime(timezone=True),
|
||||
type_=postgresql.TIMESTAMP(),
|
||||
existing_nullable=True,
|
||||
existing_server_default=sa.text('now()'))
|
||||
op.drop_column('product', 'selling_price')
|
||||
op.add_column('payment', sa.Column('payment_type', postgresql.ENUM('BUY', 'SELL', name='tradetype'), autoincrement=False, nullable=False))
|
||||
op.add_column('payment', sa.Column('date', postgresql.TIMESTAMP(), server_default=sa.text('now()'), autoincrement=False, nullable=True))
|
||||
op.add_column('payment', sa.Column('amount', sa.INTEGER(), autoincrement=False, nullable=False))
|
||||
op.add_column('payment', sa.Column('client_id', sa.INTEGER(), autoincrement=False, nullable=False))
|
||||
op.add_column('payment', sa.Column('supplier_id', sa.INTEGER(), autoincrement=False, nullable=False))
|
||||
op.add_column('payment', sa.Column('product_code', sa.VARCHAR(), autoincrement=False, nullable=False))
|
||||
op.drop_constraint(None, 'payment', type_='foreignkey')
|
||||
op.drop_constraint(None, 'payment', type_='foreignkey')
|
||||
op.drop_constraint(None, 'payment', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('payment_product_code_fkey'), 'payment', 'product', ['product_code'], ['product_code'])
|
||||
op.create_foreign_key(op.f('payment_client_id_fkey'), 'payment', 'client', ['client_id'], ['id'])
|
||||
op.create_foreign_key(op.f('payment_supplier_id_fkey'), 'payment', 'supplier', ['supplier_id'], ['id'])
|
||||
op.alter_column('payment', 'payment_method',
|
||||
existing_type=sa.Enum('MOMO', 'BANK', 'CASH', name='paymentmethod'),
|
||||
type_=sa.VARCHAR(length=24),
|
||||
existing_nullable=False)
|
||||
op.drop_column('payment', 'updated_at')
|
||||
op.drop_column('payment', 'created_at')
|
||||
op.drop_column('payment', 'updated_by')
|
||||
op.drop_column('payment', 'created_by')
|
||||
op.drop_column('payment', 'payment_date')
|
||||
op.drop_column('payment', 'paid_amount')
|
||||
op.drop_column('payment', 'transaction_id')
|
||||
op.create_table('credit',
|
||||
sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False),
|
||||
sa.Column('transcation_type', postgresql.ENUM('BUY', 'SELL', name='tradetype'), autoincrement=False, nullable=False),
|
||||
sa.Column('product_code', sa.VARCHAR(), autoincrement=False, nullable=False),
|
||||
sa.Column('client_id', sa.INTEGER(), autoincrement=False, nullable=False),
|
||||
sa.Column('supplier_id', sa.INTEGER(), autoincrement=False, nullable=False),
|
||||
sa.Column('qty', sa.INTEGER(), autoincrement=False, nullable=False),
|
||||
sa.Column('amount', sa.INTEGER(), autoincrement=False, nullable=False),
|
||||
sa.Column('date', postgresql.TIMESTAMP(), server_default=sa.text('now()'), autoincrement=False, nullable=True),
|
||||
sa.ForeignKeyConstraint(['client_id'], ['client.id'], name=op.f('credit_client_id_fkey')),
|
||||
sa.ForeignKeyConstraint(['product_code'], ['product.product_code'], name=op.f('credit_product_code_fkey')),
|
||||
sa.ForeignKeyConstraint(['supplier_id'], ['supplier.id'], name=op.f('credit_supplier_id_fkey')),
|
||||
sa.PrimaryKeyConstraint('id', name=op.f('credit_pkey'))
|
||||
)
|
||||
op.create_table('supplier',
|
||||
sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False),
|
||||
sa.Column('tin_number', sa.INTEGER(), autoincrement=False, nullable=False),
|
||||
sa.Column('names', sa.VARCHAR(length=100), autoincrement=False, nullable=False),
|
||||
sa.Column('phone_number', sa.VARCHAR(length=10), autoincrement=False, nullable=False),
|
||||
sa.PrimaryKeyConstraint('id', name=op.f('supplier_pkey')),
|
||||
sa.UniqueConstraint('tin_number', name=op.f('supplier_tin_number_key'), postgresql_include=[], postgresql_nulls_not_distinct=False)
|
||||
)
|
||||
op.create_table('client',
|
||||
sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False),
|
||||
sa.Column('tin_number', sa.INTEGER(), autoincrement=False, nullable=False),
|
||||
sa.Column('names', sa.VARCHAR(length=100), autoincrement=False, nullable=False),
|
||||
sa.Column('phone_number', sa.VARCHAR(length=10), autoincrement=False, nullable=False),
|
||||
sa.PrimaryKeyConstraint('id', name=op.f('client_pkey')),
|
||||
sa.UniqueConstraint('tin_number', name=op.f('client_tin_number_key'), postgresql_include=[], postgresql_nulls_not_distinct=False)
|
||||
)
|
||||
op.drop_table('credit_accounts')
|
||||
op.drop_table('transactions')
|
||||
op.drop_table('transaction_details')
|
||||
op.drop_table('inventory')
|
||||
op.drop_table('user')
|
||||
op.drop_table('partner')
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,219 @@
|
||||
"""product.id to match types with product_id in transaction_detail
|
||||
|
||||
Revision ID: e777b1b307b5
|
||||
Revises: a4126dbcfd9e
|
||||
Create Date: 2025-08-25 22:34:19.869427
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
import sqlmodel.sql.sqltypes
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = 'e777b1b307b5'
|
||||
down_revision: Union[str, Sequence[str], None] = 'a4126dbcfd9e'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
"""Upgrade schema."""
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
|
||||
# FIRST: Remove foreign key constraints and columns from payment table
|
||||
op.drop_constraint('payment_supplier_id_fkey', 'payment', type_='foreignkey')
|
||||
op.drop_constraint('payment_client_id_fkey', 'payment', type_='foreignkey')
|
||||
op.drop_constraint('payment_product_code_fkey', 'payment', type_='foreignkey')
|
||||
|
||||
op.drop_column('payment', 'product_code')
|
||||
op.drop_column('payment', 'date')
|
||||
op.drop_column('payment', 'amount')
|
||||
op.drop_column('payment', 'payment_type')
|
||||
op.drop_column('payment', 'client_id')
|
||||
op.drop_column('payment', 'supplier_id')
|
||||
|
||||
# THEN: Drop the referenced tables (now safe)
|
||||
op.drop_table('credit')
|
||||
op.drop_table('supplier')
|
||||
op.drop_table('client')
|
||||
|
||||
# Create new tables
|
||||
op.create_table('partner',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('tin_number', sa.Integer(), nullable=False),
|
||||
sa.Column('names', sqlmodel.sql.sqltypes.AutoString(length=100), nullable=False),
|
||||
sa.Column('type', sa.Enum('CLIENT', 'SUPPLIER', name='partnertype'), nullable=False),
|
||||
sa.Column('phone_number', sqlmodel.sql.sqltypes.AutoString(length=10), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('tin_number')
|
||||
)
|
||||
op.create_table('user',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('username', sqlmodel.sql.sqltypes.AutoString(length=100), nullable=False),
|
||||
sa.Column('role', sa.Enum('ADMIN', 'WRITE', 'READ_ONLY', name='userrole'), nullable=False),
|
||||
sa.Column('password_hash', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('username')
|
||||
)
|
||||
op.create_table('inventory',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('product_id', sa.Integer(), nullable=False),
|
||||
sa.Column('total_qty', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['product_id'], ['product.id'], ),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('product_id')
|
||||
)
|
||||
op.create_table('transaction_details',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('partner_id', sa.Integer(), nullable=False),
|
||||
sa.Column('product_id', sa.Integer(), nullable=False),
|
||||
sa.Column('qty', sa.Integer(), nullable=False),
|
||||
sa.Column('selling_price', sa.Integer(), nullable=False),
|
||||
sa.Column('total_value', sa.Integer(), nullable=False),
|
||||
sa.Column('created_by', sa.Integer(), nullable=False),
|
||||
sa.Column('updated_by', sa.Integer(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True),
|
||||
sa.ForeignKeyConstraint(['created_by'], ['user.id'], ),
|
||||
sa.ForeignKeyConstraint(['partner_id'], ['partner.id'], ),
|
||||
sa.ForeignKeyConstraint(['product_id'], ['product.id'], ),
|
||||
sa.ForeignKeyConstraint(['updated_by'], ['user.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('transactions',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('partner_id', sa.Integer(), nullable=False),
|
||||
sa.Column('transcation_type', sa.Enum('SALE', 'PURCHASE', 'CREDIT', name='transactiontype'), nullable=False),
|
||||
sa.Column('transaction_status', sa.Enum('UNPAID', 'PARTIALLY_PAID', 'PAID', 'CANCELLED', name='transactionstatus'), nullable=False),
|
||||
sa.Column('total_amount', sa.Integer(), nullable=False),
|
||||
sa.Column('created_by', sa.Integer(), nullable=False),
|
||||
sa.Column('updated_by', sa.Integer(), nullable=False),
|
||||
sa.Column('created_on', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True),
|
||||
sa.Column('updated_on', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True),
|
||||
sa.ForeignKeyConstraint(['created_by'], ['user.id'], ),
|
||||
sa.ForeignKeyConstraint(['partner_id'], ['partner.id'], ),
|
||||
sa.ForeignKeyConstraint(['updated_by'], ['user.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('credit_accounts',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('partner_id', sa.Integer(), nullable=False),
|
||||
sa.Column('transaction_id', sa.Integer(), nullable=False),
|
||||
sa.Column('credit_amount', sa.Integer(), nullable=False),
|
||||
sa.Column('credit_limit', sa.Integer(), nullable=False),
|
||||
sa.Column('balance', sa.Integer(), nullable=False),
|
||||
sa.Column('created_by', sa.Integer(), nullable=False),
|
||||
sa.Column('updated_by', sa.Integer(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True),
|
||||
sa.ForeignKeyConstraint(['created_by'], ['user.id'], ),
|
||||
sa.ForeignKeyConstraint(['partner_id'], ['partner.id'], ),
|
||||
sa.ForeignKeyConstraint(['transaction_id'], ['transactions.id'], ),
|
||||
sa.ForeignKeyConstraint(['updated_by'], ['user.id'], ),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('partner_id')
|
||||
)
|
||||
|
||||
# FINALLY: Add new columns and constraints to payment table
|
||||
op.add_column('payment', sa.Column('transaction_id', sa.Integer(), nullable=False))
|
||||
op.add_column('payment', sa.Column('paid_amount', sa.Integer(), nullable=False))
|
||||
op.add_column('payment', sa.Column('payment_date', sa.Date(), nullable=False))
|
||||
op.add_column('payment', sa.Column('created_by', sa.Integer(), nullable=False))
|
||||
op.add_column('payment', sa.Column('updated_by', sa.Integer(), nullable=False))
|
||||
op.add_column('payment', sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True))
|
||||
op.add_column('payment', sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True))
|
||||
|
||||
op.execute("CREATE TYPE paymentmethod AS ENUM ('momo', 'bank', 'cash')")
|
||||
|
||||
op.alter_column('payment', 'payment_method',
|
||||
existing_type=sa.VARCHAR(length=24),
|
||||
type_=sa.Enum('momo', 'bank', 'cash', name='paymentmethod'),
|
||||
existing_nullable=False,
|
||||
postgresql_using='payment_method::paymentmethod')
|
||||
|
||||
op.create_foreign_key(None, 'payment', 'transactions', ['transaction_id'], ['id'])
|
||||
op.create_foreign_key(None, 'payment', 'user', ['created_by'], ['id'])
|
||||
op.create_foreign_key(None, 'payment', 'user', ['updated_by'], ['id'])
|
||||
|
||||
op.add_column('product', sa.Column('selling_price', sa.Integer(), nullable=False))
|
||||
op.alter_column('product', 'date_modified',
|
||||
existing_type=postgresql.TIMESTAMP(),
|
||||
type_=sa.DateTime(timezone=True),
|
||||
existing_nullable=True,
|
||||
existing_server_default=sa.text('now()'))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
"""Downgrade schema."""
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column('product', 'date_modified',
|
||||
existing_type=sa.DateTime(timezone=True),
|
||||
type_=postgresql.TIMESTAMP(),
|
||||
existing_nullable=True,
|
||||
existing_server_default=sa.text('now()'))
|
||||
op.drop_column('product', 'selling_price')
|
||||
op.add_column('payment', sa.Column('supplier_id', sa.INTEGER(), autoincrement=False, nullable=False))
|
||||
op.add_column('payment', sa.Column('client_id', sa.INTEGER(), autoincrement=False, nullable=False))
|
||||
op.add_column('payment', sa.Column('payment_type', postgresql.ENUM('BUY', 'SELL', name='tradetype'), autoincrement=False, nullable=False))
|
||||
op.add_column('payment', sa.Column('amount', sa.INTEGER(), autoincrement=False, nullable=False))
|
||||
op.add_column('payment', sa.Column('date', postgresql.TIMESTAMP(), server_default=sa.text('now()'), autoincrement=False, nullable=True))
|
||||
op.add_column('payment', sa.Column('product_code', sa.VARCHAR(), autoincrement=False, nullable=False))
|
||||
op.drop_constraint(None, 'payment', type_='foreignkey')
|
||||
op.drop_constraint(None, 'payment', type_='foreignkey')
|
||||
op.drop_constraint(None, 'payment', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('payment_product_code_fkey'), 'payment', 'product', ['product_code'], ['product_code'])
|
||||
op.create_foreign_key(op.f('payment_client_id_fkey'), 'payment', 'client', ['client_id'], ['id'])
|
||||
op.create_foreign_key(op.f('payment_supplier_id_fkey'), 'payment', 'supplier', ['supplier_id'], ['id'])
|
||||
op.alter_column('payment', 'payment_method',
|
||||
existing_type=sa.Enum('MOMO', 'BANK', 'CASH', name='paymentmethod'),
|
||||
type_=sa.VARCHAR(length=24),
|
||||
existing_nullable=False)
|
||||
op.drop_column('payment', 'updated_at')
|
||||
op.drop_column('payment', 'created_at')
|
||||
op.drop_column('payment', 'updated_by')
|
||||
op.drop_column('payment', 'created_by')
|
||||
op.drop_column('payment', 'payment_date')
|
||||
op.drop_column('payment', 'paid_amount')
|
||||
op.drop_column('payment', 'transaction_id')
|
||||
op.create_table('client',
|
||||
sa.Column('id', sa.INTEGER(), server_default=sa.text("nextval('client_id_seq'::regclass)"), autoincrement=True, nullable=False),
|
||||
sa.Column('tin_number', sa.INTEGER(), autoincrement=False, nullable=False),
|
||||
sa.Column('names', sa.VARCHAR(length=100), autoincrement=False, nullable=False),
|
||||
sa.Column('phone_number', sa.VARCHAR(length=10), autoincrement=False, nullable=False),
|
||||
sa.PrimaryKeyConstraint('id', name='client_pkey'),
|
||||
sa.UniqueConstraint('tin_number', name='client_tin_number_key', postgresql_include=[], postgresql_nulls_not_distinct=False),
|
||||
postgresql_ignore_search_path=False
|
||||
)
|
||||
op.create_table('supplier',
|
||||
sa.Column('id', sa.INTEGER(), server_default=sa.text("nextval('supplier_id_seq'::regclass)"), autoincrement=True, nullable=False),
|
||||
sa.Column('tin_number', sa.INTEGER(), autoincrement=False, nullable=False),
|
||||
sa.Column('names', sa.VARCHAR(length=100), autoincrement=False, nullable=False),
|
||||
sa.Column('phone_number', sa.VARCHAR(length=10), autoincrement=False, nullable=False),
|
||||
sa.PrimaryKeyConstraint('id', name='supplier_pkey'),
|
||||
sa.UniqueConstraint('tin_number', name='supplier_tin_number_key', postgresql_include=[], postgresql_nulls_not_distinct=False),
|
||||
postgresql_ignore_search_path=False
|
||||
)
|
||||
op.create_table('credit',
|
||||
sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False),
|
||||
sa.Column('transcation_type', postgresql.ENUM('BUY', 'SELL', name='tradetype'), autoincrement=False, nullable=False),
|
||||
sa.Column('product_code', sa.VARCHAR(), autoincrement=False, nullable=False),
|
||||
sa.Column('client_id', sa.INTEGER(), autoincrement=False, nullable=False),
|
||||
sa.Column('supplier_id', sa.INTEGER(), autoincrement=False, nullable=False),
|
||||
sa.Column('qty', sa.INTEGER(), autoincrement=False, nullable=False),
|
||||
sa.Column('amount', sa.INTEGER(), autoincrement=False, nullable=False),
|
||||
sa.Column('date', postgresql.TIMESTAMP(), server_default=sa.text('now()'), autoincrement=False, nullable=True),
|
||||
sa.ForeignKeyConstraint(['client_id'], ['client.id'], name=op.f('credit_client_id_fkey')),
|
||||
sa.ForeignKeyConstraint(['product_code'], ['product.product_code'], name=op.f('credit_product_code_fkey')),
|
||||
sa.ForeignKeyConstraint(['supplier_id'], ['supplier.id'], name=op.f('credit_supplier_id_fkey')),
|
||||
sa.PrimaryKeyConstraint('id', name=op.f('credit_pkey'))
|
||||
)
|
||||
op.drop_table('credit_accounts')
|
||||
op.drop_table('transactions')
|
||||
op.drop_table('transaction_details')
|
||||
op.drop_table('inventory')
|
||||
op.drop_table('user')
|
||||
op.drop_table('partner')
|
||||
# ### end Alembic commands ###
|
||||
Reference in New Issue
Block a user