费用管理内容优化-初步
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
import uuid
|
||||
from datetime import date
|
||||
from typing import Sequence
|
||||
|
||||
from sqlalchemy import case, func, or_, select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.models.contract_fee import ContractFee
|
||||
from app.models.contract_fee_payment import ContractFeePayment
|
||||
from app.models.site import Site
|
||||
from app.schemas.contract_fee import ContractFeeCreate, ContractFeeUpdate
|
||||
|
||||
|
||||
async def create_contract_fee(
|
||||
db: AsyncSession,
|
||||
contract_in: ContractFeeCreate,
|
||||
) -> ContractFee:
|
||||
contract = ContractFee(
|
||||
project_id=contract_in.project_id,
|
||||
center_id=contract_in.center_id,
|
||||
contract_amount=contract_in.contract_amount,
|
||||
contract_cases=contract_in.contract_cases,
|
||||
actual_cases=contract_in.actual_cases,
|
||||
settlement_amount=contract_in.settlement_amount,
|
||||
final_payment_amount=contract_in.final_payment_amount,
|
||||
)
|
||||
db.add(contract)
|
||||
await db.commit()
|
||||
await db.refresh(contract)
|
||||
return contract
|
||||
|
||||
|
||||
async def get_contract_fee(db: AsyncSession, contract_id: uuid.UUID) -> ContractFee | None:
|
||||
result = await db.execute(select(ContractFee).where(ContractFee.id == contract_id))
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
|
||||
async def get_contract_fee_by_project_center(
|
||||
db: AsyncSession, project_id: uuid.UUID, center_id: uuid.UUID
|
||||
) -> ContractFee | None:
|
||||
result = await db.execute(
|
||||
select(ContractFee).where(ContractFee.project_id == project_id, ContractFee.center_id == center_id)
|
||||
)
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
|
||||
async def list_contract_fees(
|
||||
db: AsyncSession,
|
||||
project_id: uuid.UUID,
|
||||
center_id: uuid.UUID | None = None,
|
||||
q: str | None = None,
|
||||
) -> Sequence[tuple[ContractFee, str, float, float, date | None, date | None]]:
|
||||
paid_total = func.coalesce(
|
||||
func.sum(case((ContractFeePayment.is_paid.is_(True), ContractFeePayment.amount), else_=0)),
|
||||
0,
|
||||
).label("paid_total")
|
||||
verified_total = func.coalesce(
|
||||
func.sum(case((ContractFeePayment.is_verified.is_(True), ContractFeePayment.amount), else_=0)),
|
||||
0,
|
||||
).label("verified_total")
|
||||
last_paid_date = func.max(
|
||||
case((ContractFeePayment.is_paid.is_(True), ContractFeePayment.paid_date), else_=None)
|
||||
).label("last_paid_date")
|
||||
last_verified_date = func.max(
|
||||
case((ContractFeePayment.is_verified.is_(True), ContractFeePayment.verified_date), else_=None)
|
||||
).label("last_verified_date")
|
||||
|
||||
stmt = (
|
||||
select(
|
||||
ContractFee,
|
||||
Site.name.label("center_name"),
|
||||
paid_total,
|
||||
verified_total,
|
||||
last_paid_date,
|
||||
last_verified_date,
|
||||
)
|
||||
.join(Site, Site.id == ContractFee.center_id)
|
||||
.outerjoin(ContractFeePayment, ContractFeePayment.contract_fee_id == ContractFee.id)
|
||||
.where(ContractFee.project_id == project_id)
|
||||
.group_by(ContractFee.id, Site.name)
|
||||
)
|
||||
|
||||
if center_id:
|
||||
stmt = stmt.where(ContractFee.center_id == center_id)
|
||||
|
||||
if q:
|
||||
conditions = [Site.name.ilike(f"%{q}%")]
|
||||
try:
|
||||
conditions.append(Site.id == uuid.UUID(q))
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
stmt = stmt.where(or_(*conditions))
|
||||
|
||||
stmt = stmt.order_by(Site.name.asc())
|
||||
result = await db.execute(stmt)
|
||||
return result.all()
|
||||
|
||||
|
||||
async def update_contract_fee(
|
||||
db: AsyncSession, contract: ContractFee, contract_in: ContractFeeUpdate
|
||||
) -> ContractFee:
|
||||
update_data = contract_in.model_dump(exclude_unset=True)
|
||||
for key, value in update_data.items():
|
||||
setattr(contract, key, value)
|
||||
await db.commit()
|
||||
await db.refresh(contract)
|
||||
return contract
|
||||
|
||||
|
||||
async def delete_contract_fee(db: AsyncSession, contract: ContractFee) -> None:
|
||||
await db.delete(contract)
|
||||
await db.commit()
|
||||
@@ -0,0 +1,74 @@
|
||||
import uuid
|
||||
from typing import Sequence
|
||||
|
||||
from sqlalchemy import func, select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.models.contract_fee_payment import ContractFeePayment
|
||||
from app.schemas.contract_fee_payment import ContractFeePaymentCreate, ContractFeePaymentUpdate
|
||||
|
||||
|
||||
async def create_payment(
|
||||
db: AsyncSession,
|
||||
contract_fee_id: uuid.UUID,
|
||||
payment_in: ContractFeePaymentCreate,
|
||||
) -> ContractFeePayment:
|
||||
result = await db.execute(
|
||||
select(func.coalesce(func.max(ContractFeePayment.seq), 0)).where(
|
||||
ContractFeePayment.contract_fee_id == contract_fee_id
|
||||
)
|
||||
)
|
||||
next_seq = (result.scalar_one_or_none() or 0) + 1
|
||||
payment = ContractFeePayment(
|
||||
contract_fee_id=contract_fee_id,
|
||||
seq=next_seq,
|
||||
amount=payment_in.amount,
|
||||
paid_date=payment_in.paid_date,
|
||||
verified_date=payment_in.verified_date,
|
||||
is_paid=payment_in.is_paid,
|
||||
is_verified=payment_in.is_verified,
|
||||
remark=payment_in.remark,
|
||||
)
|
||||
db.add(payment)
|
||||
await db.commit()
|
||||
await db.refresh(payment)
|
||||
return payment
|
||||
|
||||
|
||||
async def get_payment(db: AsyncSession, payment_id: uuid.UUID) -> ContractFeePayment | None:
|
||||
result = await db.execute(select(ContractFeePayment).where(ContractFeePayment.id == payment_id))
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
|
||||
async def list_payments(db: AsyncSession, contract_fee_id: uuid.UUID) -> Sequence[ContractFeePayment]:
|
||||
result = await db.execute(
|
||||
select(ContractFeePayment)
|
||||
.where(ContractFeePayment.contract_fee_id == contract_fee_id)
|
||||
.order_by(ContractFeePayment.seq.asc(), ContractFeePayment.created_at.asc())
|
||||
)
|
||||
return result.scalars().all()
|
||||
|
||||
|
||||
async def update_payment(
|
||||
db: AsyncSession, payment: ContractFeePayment, payment_in: ContractFeePaymentUpdate
|
||||
) -> ContractFeePayment:
|
||||
update_data = payment_in.model_dump(exclude_unset=True)
|
||||
for key, value in update_data.items():
|
||||
setattr(payment, key, value)
|
||||
await db.commit()
|
||||
await db.refresh(payment)
|
||||
return payment
|
||||
|
||||
|
||||
async def delete_payment(db: AsyncSession, payment: ContractFeePayment) -> None:
|
||||
await db.delete(payment)
|
||||
await db.commit()
|
||||
|
||||
|
||||
async def resequence_payments(db: AsyncSession, contract_fee_id: uuid.UUID) -> None:
|
||||
payments = await list_payments(db, contract_fee_id)
|
||||
for idx, payment in enumerate(payments, start=1):
|
||||
if payment.seq != idx:
|
||||
payment.seq = idx
|
||||
db.add(payment)
|
||||
await db.commit()
|
||||
@@ -0,0 +1,70 @@
|
||||
import uuid
|
||||
from typing import Sequence
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.models.fee_attachment import FeeAttachment
|
||||
|
||||
|
||||
async def create_attachment(
|
||||
db: AsyncSession,
|
||||
*,
|
||||
entity_type: str,
|
||||
entity_id: uuid.UUID,
|
||||
file_type: str,
|
||||
filename: str,
|
||||
mime_type: str | None,
|
||||
size: int,
|
||||
storage_key: str | None,
|
||||
url: str | None,
|
||||
uploaded_by: uuid.UUID,
|
||||
) -> FeeAttachment:
|
||||
attachment = FeeAttachment(
|
||||
entity_type=entity_type,
|
||||
entity_id=entity_id,
|
||||
file_type=file_type,
|
||||
filename=filename,
|
||||
mime_type=mime_type,
|
||||
size=size,
|
||||
storage_key=storage_key,
|
||||
url=url,
|
||||
uploaded_by=uploaded_by,
|
||||
)
|
||||
db.add(attachment)
|
||||
await db.commit()
|
||||
await db.refresh(attachment)
|
||||
return attachment
|
||||
|
||||
|
||||
async def list_attachments(
|
||||
db: AsyncSession,
|
||||
*,
|
||||
entity_type: str,
|
||||
entity_id: uuid.UUID,
|
||||
) -> Sequence[FeeAttachment]:
|
||||
result = await db.execute(
|
||||
select(FeeAttachment)
|
||||
.where(
|
||||
FeeAttachment.entity_type == entity_type,
|
||||
FeeAttachment.entity_id == entity_id,
|
||||
FeeAttachment.is_deleted.is_(False),
|
||||
)
|
||||
.order_by(FeeAttachment.uploaded_at.desc())
|
||||
)
|
||||
return result.scalars().all()
|
||||
|
||||
|
||||
async def get_attachment(db: AsyncSession, attachment_id: uuid.UUID) -> FeeAttachment | None:
|
||||
result = await db.execute(
|
||||
select(FeeAttachment).where(FeeAttachment.id == attachment_id, FeeAttachment.is_deleted.is_(False))
|
||||
)
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
|
||||
async def soft_delete_attachment(db: AsyncSession, attachment: FeeAttachment) -> FeeAttachment:
|
||||
attachment.is_deleted = True
|
||||
db.add(attachment)
|
||||
await db.commit()
|
||||
await db.refresh(attachment)
|
||||
return attachment
|
||||
@@ -0,0 +1,98 @@
|
||||
import uuid
|
||||
from datetime import date
|
||||
from typing import Sequence
|
||||
|
||||
from sqlalchemy import and_, func, select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.models.fee_attachment import FeeAttachment
|
||||
from app.models.special_expense import SpecialExpense
|
||||
from app.models.site import Site
|
||||
from app.schemas.special_expense import SpecialExpenseCreate, SpecialExpenseUpdate
|
||||
|
||||
|
||||
async def create_special_expense(
|
||||
db: AsyncSession,
|
||||
expense_in: SpecialExpenseCreate,
|
||||
created_by: uuid.UUID | None,
|
||||
) -> SpecialExpense:
|
||||
expense = SpecialExpense(
|
||||
project_id=expense_in.project_id,
|
||||
center_id=expense_in.center_id,
|
||||
category=expense_in.category,
|
||||
amount=expense_in.amount,
|
||||
happen_date=expense_in.happen_date,
|
||||
description=expense_in.description,
|
||||
is_paid=expense_in.is_paid,
|
||||
paid_date=expense_in.paid_date,
|
||||
is_verified=expense_in.is_verified,
|
||||
verified_date=expense_in.verified_date,
|
||||
created_by=created_by,
|
||||
)
|
||||
db.add(expense)
|
||||
await db.commit()
|
||||
await db.refresh(expense)
|
||||
return expense
|
||||
|
||||
|
||||
async def get_special_expense(db: AsyncSession, expense_id: uuid.UUID) -> SpecialExpense | None:
|
||||
result = await db.execute(select(SpecialExpense).where(SpecialExpense.id == expense_id))
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
|
||||
async def list_special_expenses(
|
||||
db: AsyncSession,
|
||||
project_id: uuid.UUID,
|
||||
center_id: uuid.UUID | None = None,
|
||||
category: str | None = None,
|
||||
date_from: date | None = None,
|
||||
date_to: date | None = None,
|
||||
) -> Sequence[tuple[SpecialExpense, str | None, int]]:
|
||||
attachment_count = func.count(FeeAttachment.id).label("attachments_count")
|
||||
stmt = (
|
||||
select(
|
||||
SpecialExpense,
|
||||
Site.name.label("center_name"),
|
||||
attachment_count,
|
||||
)
|
||||
.outerjoin(Site, Site.id == SpecialExpense.center_id)
|
||||
.outerjoin(
|
||||
FeeAttachment,
|
||||
and_(
|
||||
FeeAttachment.entity_type == "special_expense",
|
||||
FeeAttachment.entity_id == SpecialExpense.id,
|
||||
FeeAttachment.is_deleted.is_(False),
|
||||
),
|
||||
)
|
||||
.where(SpecialExpense.project_id == project_id)
|
||||
.group_by(SpecialExpense.id, Site.name)
|
||||
.order_by(SpecialExpense.happen_date.desc().nullslast(), SpecialExpense.created_at.desc())
|
||||
)
|
||||
|
||||
if center_id:
|
||||
stmt = stmt.where(SpecialExpense.center_id == center_id)
|
||||
if category:
|
||||
stmt = stmt.where(SpecialExpense.category == category)
|
||||
if date_from:
|
||||
stmt = stmt.where(SpecialExpense.happen_date >= date_from)
|
||||
if date_to:
|
||||
stmt = stmt.where(SpecialExpense.happen_date <= date_to)
|
||||
|
||||
result = await db.execute(stmt)
|
||||
return result.all()
|
||||
|
||||
|
||||
async def update_special_expense(
|
||||
db: AsyncSession, expense: SpecialExpense, expense_in: SpecialExpenseUpdate
|
||||
) -> SpecialExpense:
|
||||
update_data = expense_in.model_dump(exclude_unset=True)
|
||||
for key, value in update_data.items():
|
||||
setattr(expense, key, value)
|
||||
await db.commit()
|
||||
await db.refresh(expense)
|
||||
return expense
|
||||
|
||||
|
||||
async def delete_special_expense(db: AsyncSession, expense: SpecialExpense) -> None:
|
||||
await db.delete(expense)
|
||||
await db.commit()
|
||||
Reference in New Issue
Block a user