2025-12-19 10:12:59 +08:00
|
|
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
|
|
from module_admin.dao.warehouse_receipt_dao import WarehouseReceiptDao
|
|
|
|
|
|
from module_admin.dao.warehouse_sample_dao import WarehouseSampleDao
|
|
|
|
|
|
from module_admin.entity.do.warehouse_receipt_do import WarehouseReceipt
|
|
|
|
|
|
from module_admin.entity.do.warehouse_sample_do import WarehouseSample
|
|
|
|
|
|
from module_admin.entity.vo.warehouse_receipt_vo import (
|
|
|
|
|
|
WarehouseReceiptModel, WarehouseReceiptPageQueryModel,
|
|
|
|
|
|
AddWarehouseReceiptModel, EditWarehouseReceiptModel, DeleteWarehouseReceiptModel
|
|
|
|
|
|
)
|
|
|
|
|
|
from module_admin.entity.vo.common_vo import CrudResponseModel
|
|
|
|
|
|
from exceptions.exception import ServiceException
|
|
|
|
|
|
from utils.common_util import CamelCaseUtil
|
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class WarehouseReceiptService:
|
|
|
|
|
|
"""
|
|
|
|
|
|
入库单业务逻辑层
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
|
async def get_receipt_list(cls, db: AsyncSession, query_object: WarehouseReceiptPageQueryModel, is_page: bool = False):
|
|
|
|
|
|
"""
|
|
|
|
|
|
获取入库单列表
|
|
|
|
|
|
"""
|
2025-12-29 22:22:13 +08:00
|
|
|
|
try:
|
|
|
|
|
|
receipt_list = await WarehouseReceiptDao.get_receipt_list(db, query_object, is_page)
|
|
|
|
|
|
|
|
|
|
|
|
# 转换为字典并添加样品数量
|
|
|
|
|
|
result_list = []
|
|
|
|
|
|
for receipt in receipt_list:
|
|
|
|
|
|
receipt_dict = CamelCaseUtil.transform_result(receipt)
|
|
|
|
|
|
# 获取样品数量
|
|
|
|
|
|
sample_count = await WarehouseSampleDao.get_sample_count_by_receipt(db, receipt.receipt_id)
|
|
|
|
|
|
receipt_dict['sample_count'] = sample_count if sample_count else 0
|
|
|
|
|
|
result_list.append(receipt_dict)
|
|
|
|
|
|
|
|
|
|
|
|
if is_page:
|
|
|
|
|
|
total = await WarehouseReceiptDao.get_receipt_count(db, query_object)
|
|
|
|
|
|
return {'rows': result_list, 'total': total if total else 0}
|
|
|
|
|
|
else:
|
|
|
|
|
|
return result_list
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
raise ServiceException(message=f'获取入库单列表失败: {str(e)}')
|
2025-12-19 10:12:59 +08:00
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
|
async def get_receipt_detail(cls, db: AsyncSession, receipt_id: int):
|
|
|
|
|
|
"""
|
|
|
|
|
|
获取入库单详情
|
|
|
|
|
|
"""
|
2026-01-04 23:56:11 +08:00
|
|
|
|
import sys
|
|
|
|
|
|
print(f"DEBUG Service START: get_receipt_detail called with receipt_id={receipt_id}", flush=True)
|
|
|
|
|
|
sys.stdout.flush()
|
2025-12-29 22:22:13 +08:00
|
|
|
|
try:
|
|
|
|
|
|
receipt = await WarehouseReceiptDao.get_receipt_by_id(db, receipt_id)
|
|
|
|
|
|
if not receipt:
|
|
|
|
|
|
raise ServiceException(message='入库单不存在')
|
|
|
|
|
|
|
2026-01-04 23:56:11 +08:00
|
|
|
|
print(f"DEBUG Service: receipt found, receipt.receipt_id={receipt.receipt_id}, receipt_no={receipt.receipt_no}", flush=True)
|
|
|
|
|
|
sys.stdout.flush()
|
|
|
|
|
|
|
2025-12-29 22:22:13 +08:00
|
|
|
|
receipt_dict = CamelCaseUtil.transform_result(receipt)
|
|
|
|
|
|
|
|
|
|
|
|
# 获取样品列表
|
|
|
|
|
|
from module_admin.entity.vo.warehouse_sample_vo import WarehouseSamplePageQueryModel
|
2026-01-04 23:56:11 +08:00
|
|
|
|
# 使用 camelCase 字段名来构造查询对象
|
|
|
|
|
|
sample_query = WarehouseSamplePageQueryModel(receiptId=receipt.receipt_id, pageNum=1, pageSize=1000)
|
2026-01-04 16:16:03 +08:00
|
|
|
|
|
|
|
|
|
|
# 添加日志
|
2026-01-04 23:56:11 +08:00
|
|
|
|
print(f"DEBUG: 查询入库单 {receipt_id} 的样品,receipt.receipt_id={receipt.receipt_id}", flush=True)
|
|
|
|
|
|
print(f"DEBUG: sample_query.receipt_id={sample_query.receipt_id}", flush=True)
|
|
|
|
|
|
sys.stdout.flush()
|
2026-01-04 16:16:03 +08:00
|
|
|
|
|
2025-12-29 22:22:13 +08:00
|
|
|
|
samples = await WarehouseSampleDao.get_sample_list(db, sample_query, is_page=False)
|
|
|
|
|
|
|
2026-01-04 16:16:03 +08:00
|
|
|
|
# 添加日志
|
2026-01-04 23:56:11 +08:00
|
|
|
|
print(f"DEBUG: 查询到 {len(samples) if samples else 0} 个样品", flush=True)
|
2026-01-04 16:16:03 +08:00
|
|
|
|
if samples:
|
|
|
|
|
|
for sample in samples[:3]: # 只打印前3个
|
2026-01-04 23:56:11 +08:00
|
|
|
|
print(f"DEBUG: 样品 ID={sample.sample_id}, receipt_id={sample.receipt_id}, SN={sample.sample_sn}", flush=True)
|
|
|
|
|
|
sys.stdout.flush()
|
2026-01-04 16:16:03 +08:00
|
|
|
|
|
2025-12-29 22:22:13 +08:00
|
|
|
|
# 转换样品列表
|
|
|
|
|
|
samples_list = [CamelCaseUtil.transform_result(sample) for sample in samples] if samples else []
|
|
|
|
|
|
receipt_dict['samples'] = samples_list
|
|
|
|
|
|
receipt_dict['sample_count'] = len(samples_list)
|
|
|
|
|
|
|
|
|
|
|
|
return receipt_dict
|
|
|
|
|
|
except ServiceException:
|
|
|
|
|
|
raise
|
|
|
|
|
|
except Exception as e:
|
2026-01-04 23:56:11 +08:00
|
|
|
|
print(f"DEBUG Service ERROR: {str(e)}", flush=True)
|
2025-12-29 22:22:13 +08:00
|
|
|
|
raise ServiceException(message=f'获取入库单详情失败: {str(e)}')
|
2025-12-19 10:12:59 +08:00
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
|
async def add_receipt(cls, db: AsyncSession, receipt_model: AddWarehouseReceiptModel):
|
|
|
|
|
|
"""
|
|
|
|
|
|
新增入库单(同时创建关联的样品)
|
|
|
|
|
|
"""
|
2025-12-29 22:22:13 +08:00
|
|
|
|
try:
|
|
|
|
|
|
# 检查入库单号是否已存在
|
|
|
|
|
|
existing = await WarehouseReceiptDao.get_receipt_by_no(db, receipt_model.receipt_no)
|
|
|
|
|
|
if existing:
|
|
|
|
|
|
raise ServiceException(message=f'入库单号【{receipt_model.receipt_no}】已存在')
|
|
|
|
|
|
|
|
|
|
|
|
# 获取样品数据
|
|
|
|
|
|
samples_data = receipt_model.samples if receipt_model.samples else []
|
|
|
|
|
|
|
|
|
|
|
|
# 创建入库单对象(排除samples字段)
|
|
|
|
|
|
receipt_dict = receipt_model.model_dump(exclude_unset=True, exclude={'samples'})
|
|
|
|
|
|
receipt = WarehouseReceipt(**receipt_dict)
|
|
|
|
|
|
receipt.create_time = datetime.now()
|
|
|
|
|
|
receipt.update_time = datetime.now()
|
|
|
|
|
|
|
|
|
|
|
|
# 保存入库单
|
|
|
|
|
|
await WarehouseReceiptDao.add_receipt(db, receipt)
|
|
|
|
|
|
await db.flush() # 刷新以获取receipt_id
|
|
|
|
|
|
|
|
|
|
|
|
# 创建样品对象并关联到入库单
|
|
|
|
|
|
for sample_data in samples_data:
|
|
|
|
|
|
sample_dict = sample_data.model_dump(exclude_unset=True)
|
|
|
|
|
|
sample = WarehouseSample(**sample_dict)
|
|
|
|
|
|
sample.receipt_id = receipt.receipt_id
|
|
|
|
|
|
sample.receipt_no = receipt.receipt_no
|
|
|
|
|
|
sample.create_by = receipt.create_by
|
|
|
|
|
|
sample.create_time = datetime.now()
|
|
|
|
|
|
sample.update_time = datetime.now()
|
|
|
|
|
|
await WarehouseSampleDao.add_sample(db, sample)
|
|
|
|
|
|
|
|
|
|
|
|
await db.commit()
|
|
|
|
|
|
|
|
|
|
|
|
return CrudResponseModel(is_success=True, message='新增成功')
|
|
|
|
|
|
except ServiceException:
|
|
|
|
|
|
await db.rollback()
|
|
|
|
|
|
raise
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
await db.rollback()
|
|
|
|
|
|
raise ServiceException(message=f'新增入库单失败: {str(e)}')
|
2025-12-19 10:12:59 +08:00
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
|
async def edit_receipt(cls, db: AsyncSession, receipt_model: EditWarehouseReceiptModel):
|
|
|
|
|
|
"""
|
|
|
|
|
|
编辑入库单(同时更新关联的样品)
|
|
|
|
|
|
"""
|
2025-12-29 22:22:13 +08:00
|
|
|
|
try:
|
|
|
|
|
|
# 检查入库单是否存在
|
|
|
|
|
|
receipt = await WarehouseReceiptDao.get_receipt_by_id(db, receipt_model.receipt_id)
|
|
|
|
|
|
if not receipt:
|
|
|
|
|
|
raise ServiceException(message='入库单不存在')
|
2025-12-19 10:12:59 +08:00
|
|
|
|
|
2025-12-29 22:22:13 +08:00
|
|
|
|
# 如果修改了入库单号,检查新单号是否已存在
|
|
|
|
|
|
if receipt_model.receipt_no and receipt_model.receipt_no != receipt.receipt_no:
|
|
|
|
|
|
existing = await WarehouseReceiptDao.get_receipt_by_no(db, receipt_model.receipt_no)
|
|
|
|
|
|
if existing:
|
|
|
|
|
|
raise ServiceException(message=f'入库单号【{receipt_model.receipt_no}】已存在')
|
2025-12-19 10:12:59 +08:00
|
|
|
|
|
2025-12-29 22:22:13 +08:00
|
|
|
|
# 更新入库单基本信息
|
|
|
|
|
|
receipt_model.update_time = datetime.now()
|
|
|
|
|
|
await WarehouseReceiptDao.edit_receipt(db, receipt_model)
|
2025-12-19 10:12:59 +08:00
|
|
|
|
|
2025-12-29 22:22:13 +08:00
|
|
|
|
# 如果提供了样品列表,则同步更新样品
|
|
|
|
|
|
if receipt_model.samples is not None:
|
|
|
|
|
|
# 获取现有样品列表
|
|
|
|
|
|
from module_admin.entity.vo.warehouse_sample_vo import WarehouseSamplePageQueryModel
|
2026-01-04 23:56:11 +08:00
|
|
|
|
# 使用 camelCase 字段名来构造查询对象
|
|
|
|
|
|
sample_query = WarehouseSamplePageQueryModel(receiptId=receipt_model.receipt_id, pageNum=1, pageSize=1000)
|
2025-12-29 22:22:13 +08:00
|
|
|
|
existing_samples = await WarehouseSampleDao.get_sample_list(db, sample_query, is_page=False)
|
|
|
|
|
|
existing_sample_ids = {sample.sample_id for sample in existing_samples} if existing_samples else set()
|
2025-12-19 10:12:59 +08:00
|
|
|
|
|
2025-12-29 22:22:13 +08:00
|
|
|
|
# 处理前端传来的样品
|
|
|
|
|
|
submitted_sample_ids = set()
|
|
|
|
|
|
for sample_data in receipt_model.samples:
|
2026-01-05 15:22:58 +08:00
|
|
|
|
sample_dict = sample_data.model_dump(exclude_unset=True, by_alias=True)
|
2025-12-29 22:22:13 +08:00
|
|
|
|
|
|
|
|
|
|
# 如果有sample_id,说明是更新现有样品
|
2026-01-05 15:22:58 +08:00
|
|
|
|
if 'sampleId' in sample_dict and sample_dict['sampleId']:
|
|
|
|
|
|
sample_id = sample_dict['sampleId']
|
2025-12-29 22:22:13 +08:00
|
|
|
|
submitted_sample_ids.add(sample_id)
|
|
|
|
|
|
# 更新样品
|
|
|
|
|
|
from module_admin.entity.vo.warehouse_sample_vo import EditWarehouseSampleModel
|
|
|
|
|
|
edit_sample = EditWarehouseSampleModel(**sample_dict)
|
|
|
|
|
|
edit_sample.update_time = datetime.now()
|
|
|
|
|
|
edit_sample.update_by = receipt_model.update_by
|
|
|
|
|
|
await WarehouseSampleDao.edit_sample(db, edit_sample)
|
|
|
|
|
|
else:
|
2026-01-05 15:22:58 +08:00
|
|
|
|
# 新增样品 - 转换为snake_case用于创建DO对象
|
|
|
|
|
|
sample_dict_snake = sample_data.model_dump(exclude_unset=True)
|
|
|
|
|
|
sample = WarehouseSample(**sample_dict_snake)
|
2025-12-29 22:22:13 +08:00
|
|
|
|
sample.receipt_id = receipt_model.receipt_id
|
|
|
|
|
|
sample.receipt_no = receipt_model.receipt_no or receipt.receipt_no
|
|
|
|
|
|
sample.create_by = receipt_model.update_by
|
|
|
|
|
|
sample.create_time = datetime.now()
|
|
|
|
|
|
sample.update_time = datetime.now()
|
|
|
|
|
|
await WarehouseSampleDao.add_sample(db, sample)
|
|
|
|
|
|
|
|
|
|
|
|
# 删除前端没有提交的样品(逻辑删除)
|
|
|
|
|
|
samples_to_delete = existing_sample_ids - submitted_sample_ids
|
|
|
|
|
|
if samples_to_delete:
|
|
|
|
|
|
await WarehouseSampleDao.delete_sample(db, list(samples_to_delete))
|
2025-12-19 10:12:59 +08:00
|
|
|
|
|
2025-12-29 22:22:13 +08:00
|
|
|
|
await db.commit()
|
2025-12-19 10:12:59 +08:00
|
|
|
|
|
2025-12-29 22:22:13 +08:00
|
|
|
|
return CrudResponseModel(is_success=True, message='更新成功')
|
|
|
|
|
|
except ServiceException:
|
|
|
|
|
|
await db.rollback()
|
|
|
|
|
|
raise
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
await db.rollback()
|
|
|
|
|
|
raise ServiceException(message=f'更新入库单失败: {str(e)}')
|
2025-12-19 10:12:59 +08:00
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
|
async def delete_receipt(cls, db: AsyncSession, delete_model: DeleteWarehouseReceiptModel):
|
|
|
|
|
|
"""
|
|
|
|
|
|
删除入库单(同时删除关联的样品)
|
|
|
|
|
|
"""
|
2025-12-29 22:22:13 +08:00
|
|
|
|
try:
|
|
|
|
|
|
receipt_ids = [int(id_str) for id_str in delete_model.receipt_ids.split(',')]
|
|
|
|
|
|
|
|
|
|
|
|
# 检查入库单是否存在
|
|
|
|
|
|
for receipt_id in receipt_ids:
|
|
|
|
|
|
receipt = await WarehouseReceiptDao.get_receipt_by_id(db, receipt_id)
|
|
|
|
|
|
if not receipt:
|
|
|
|
|
|
raise ServiceException(message=f'入库单ID【{receipt_id}】不存在')
|
|
|
|
|
|
|
|
|
|
|
|
# 删除入库单(级联删除样品)
|
|
|
|
|
|
await WarehouseReceiptDao.delete_receipt(db, receipt_ids)
|
|
|
|
|
|
await db.commit()
|
|
|
|
|
|
|
|
|
|
|
|
return CrudResponseModel(is_success=True, message='删除成功')
|
|
|
|
|
|
except ServiceException:
|
|
|
|
|
|
await db.rollback()
|
|
|
|
|
|
raise
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
await db.rollback()
|
|
|
|
|
|
raise ServiceException(message=f'删除入库单失败: {str(e)}')
|
2025-12-19 10:12:59 +08:00
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
|
async def generate_receipt_no(cls, db: AsyncSession):
|
|
|
|
|
|
"""
|
|
|
|
|
|
生成入库单号
|
|
|
|
|
|
"""
|
2025-12-29 22:22:13 +08:00
|
|
|
|
try:
|
|
|
|
|
|
year = datetime.now().year
|
|
|
|
|
|
receipt_no = await WarehouseReceiptDao.generate_receipt_no(db, year)
|
|
|
|
|
|
return {'receiptNo': receipt_no}
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
raise ServiceException(message=f'生成入库单号失败: {str(e)}')
|
2025-12-19 10:12:59 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|