Step 9:药品管理(IMP)

This commit is contained in:
Cheng Zhou
2025-12-16 19:26:45 +08:00
parent 566ebdf749
commit 882fcb0963
107 changed files with 855 additions and 1 deletions
+57
View File
@@ -0,0 +1,57 @@
import uuid
from typing import Sequence
from sqlalchemy import select, update as sa_update
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.imp_batch import ImpBatch
from app.models.imp_product import ImpProduct
from app.schemas.imp import BatchCreate, BatchUpdate
async def create_batch(db: AsyncSession, study_id: uuid.UUID, batch_in: BatchCreate) -> ImpBatch:
result = await db.execute(select(ImpProduct).where(ImpProduct.id == batch_in.product_id))
product = result.scalar_one_or_none()
if not product or product.study_id != study_id:
raise ValueError("Product not found in study")
batch = ImpBatch(
study_id=study_id,
product_id=batch_in.product_id,
batch_no=batch_in.batch_no,
expiry_date=batch_in.expiry_date,
manufacture_date=batch_in.manufacture_date,
status="ACTIVE",
)
db.add(batch)
await db.commit()
await db.refresh(batch)
return batch
async def get_batch(db: AsyncSession, batch_id: uuid.UUID) -> ImpBatch | None:
result = await db.execute(select(ImpBatch).where(ImpBatch.id == batch_id))
return result.scalar_one_or_none()
async def list_batches(db: AsyncSession, study_id: uuid.UUID, product_id: uuid.UUID | None = None, status: str | None = None) -> Sequence[ImpBatch]:
stmt = select(ImpBatch).where(ImpBatch.study_id == study_id)
if product_id:
stmt = stmt.where(ImpBatch.product_id == product_id)
if status:
stmt = stmt.where(ImpBatch.status == status)
result = await db.execute(stmt)
return result.scalars().all()
async def update_batch(db: AsyncSession, batch: ImpBatch, batch_in: BatchUpdate) -> ImpBatch:
update_data = batch_in.model_dump(exclude_unset=True)
if update_data:
await db.execute(
sa_update(ImpBatch)
.where(ImpBatch.id == batch.id)
.values(**update_data)
)
await db.commit()
await db.refresh(batch)
return batch
+46
View File
@@ -0,0 +1,46 @@
import uuid
from typing import Sequence
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.imp_inventory import ImpInventory
from app.models.imp_batch import ImpBatch
async def get_inventory_for_update(db: AsyncSession, study_id: uuid.UUID, site_id: uuid.UUID, batch_id: uuid.UUID) -> ImpInventory | None:
result = await db.execute(
select(ImpInventory)
.where(
ImpInventory.study_id == study_id,
ImpInventory.site_id == site_id,
ImpInventory.batch_id == batch_id,
)
.with_for_update()
)
return result.scalar_one_or_none()
async def get_or_create_inventory(db: AsyncSession, study_id: uuid.UUID, site_id: uuid.UUID, batch_id: uuid.UUID) -> ImpInventory:
inv = await get_inventory_for_update(db, study_id, site_id, batch_id)
if inv:
return inv
inv = ImpInventory(study_id=study_id, site_id=site_id, batch_id=batch_id, quantity_on_hand=0)
db.add(inv)
await db.flush()
return inv
async def list_inventory(
db: AsyncSession,
study_id: uuid.UUID,
site_id: uuid.UUID | None = None,
product_id: uuid.UUID | None = None,
) -> Sequence[ImpInventory]:
stmt = select(ImpInventory).where(ImpInventory.study_id == study_id)
if site_id:
stmt = stmt.where(ImpInventory.site_id == site_id)
if product_id:
stmt = stmt.join(ImpBatch, ImpBatch.id == ImpInventory.batch_id).where(ImpBatch.product_id == product_id)
result = await db.execute(stmt)
return result.scalars().all()
+50
View File
@@ -0,0 +1,50 @@
import uuid
from typing import Sequence
from sqlalchemy import select, update as sa_update
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.imp_product import ImpProduct
from app.schemas.imp import ProductCreate, ProductUpdate
async def create_product(db: AsyncSession, study_id: uuid.UUID, product_in: ProductCreate) -> ImpProduct:
product = ImpProduct(
study_id=study_id,
name=product_in.name,
form=product_in.form,
strength=product_in.strength,
unit=product_in.unit,
description=product_in.description,
is_active=product_in.is_active,
)
db.add(product)
await db.commit()
await db.refresh(product)
return product
async def get_product(db: AsyncSession, product_id: uuid.UUID) -> ImpProduct | None:
result = await db.execute(select(ImpProduct).where(ImpProduct.id == product_id))
return result.scalar_one_or_none()
async def list_products(db: AsyncSession, study_id: uuid.UUID, is_active: bool | None = None) -> Sequence[ImpProduct]:
stmt = select(ImpProduct).where(ImpProduct.study_id == study_id)
if is_active is not None:
stmt = stmt.where(ImpProduct.is_active == is_active)
result = await db.execute(stmt)
return result.scalars().all()
async def update_product(db: AsyncSession, product: ImpProduct, product_in: ProductUpdate) -> ImpProduct:
update_data = product_in.model_dump(exclude_unset=True)
if update_data:
await db.execute(
sa_update(ImpProduct)
.where(ImpProduct.id == product.id)
.values(**update_data)
)
await db.commit()
await db.refresh(product)
return product
+70
View File
@@ -0,0 +1,70 @@
import uuid
from datetime import date
from typing import Sequence
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.imp_transaction import ImpTransaction
async def create_tx(
db: AsyncSession,
*,
study_id: uuid.UUID,
site_id: uuid.UUID,
batch_id: uuid.UUID,
subject_id: uuid.UUID | None,
tx_type: str,
quantity: int,
tx_date: date,
reference: str | None,
notes: str | None,
created_by: uuid.UUID,
) -> ImpTransaction:
tx = ImpTransaction(
study_id=study_id,
site_id=site_id,
batch_id=batch_id,
subject_id=subject_id,
tx_type=tx_type,
quantity=quantity,
tx_date=tx_date,
reference=reference,
notes=notes,
created_by=created_by,
)
db.add(tx)
await db.flush()
return tx
async def list_txs(
db: AsyncSession,
study_id: uuid.UUID,
*,
site_id: uuid.UUID | None = None,
batch_id: uuid.UUID | None = None,
subject_id: uuid.UUID | None = None,
tx_type: str | None = None,
date_from: date | None = None,
date_to: date | None = None,
skip: int = 0,
limit: int = 100,
) -> Sequence[ImpTransaction]:
stmt = select(ImpTransaction).where(ImpTransaction.study_id == study_id)
if site_id:
stmt = stmt.where(ImpTransaction.site_id == site_id)
if batch_id:
stmt = stmt.where(ImpTransaction.batch_id == batch_id)
if subject_id:
stmt = stmt.where(ImpTransaction.subject_id == subject_id)
if tx_type:
stmt = stmt.where(ImpTransaction.tx_type == tx_type)
if date_from:
stmt = stmt.where(ImpTransaction.tx_date >= date_from)
if date_to:
stmt = stmt.where(ImpTransaction.tx_date <= date_to)
stmt = stmt.offset(skip).limit(limit)
result = await db.execute(stmt)
return result.scalars().all()