You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
228 lines
8.7 KiB
228 lines
8.7 KiB
#!/usr/bin/env python
|
|
# encoding: utf-8
|
|
"""
|
|
@author: tx
|
|
@file: warehouse.py
|
|
@time: 2023/5/8 17:53
|
|
@desc:
|
|
"""
|
|
import re
|
|
from datetime import datetime, timedelta
|
|
|
|
from fastapi import APIRouter, Depends, Request
|
|
from pydantic import BaseModel
|
|
from tortoise.expressions import Q
|
|
|
|
from helper import login_required, respond_to
|
|
from helper.logger import logger
|
|
from helper.utils import timezone_now
|
|
from transfer.code_standard import CodeStandard
|
|
from transfer.standard import Standard
|
|
from models import Template
|
|
from models.archive import Archive
|
|
from models.dictionary import Dictionary
|
|
from models.drawers import Drawer
|
|
from models.drug import Drug
|
|
from models.storage_form import StorageForm
|
|
from models.cabinet import Cabinet
|
|
|
|
router = APIRouter(prefix="/warehousing", dependencies=[Depends(login_required)])
|
|
|
|
class WarehouseingDrugBinding(BaseModel):
|
|
drug_list: list
|
|
|
|
@router.get('/item/{storageId}', summary='获取绑定入库可选条目')
|
|
async def index(request: Request, storageId: str):
|
|
"""
|
|
用户入库模板条目
|
|
:param storageId: 模板id
|
|
:return:
|
|
"""
|
|
storage_form = await StorageForm.get_or_none(id=storageId).prefetch_related('template')
|
|
template = await Template.get(id=storage_form.template.id)
|
|
if not storage_form:
|
|
return respond_to(404, desc="导入目标条目未查询到")
|
|
if not template.xlsx:
|
|
result = {
|
|
"template_xlsx": {},
|
|
"data": [],
|
|
}
|
|
return result
|
|
template_xlsx = template.parse_xlsx_transfer()
|
|
archive_obj = await Archive.get(id=request.state.archive_id)
|
|
return_require_weigh = archive_obj.params.get("return_require_weigh", '')
|
|
resContent = list()
|
|
for item in storage_form.fill_json_content:
|
|
itemContent = item.get("fill_json_content")
|
|
if not itemContent:
|
|
continue
|
|
itemNum = itemContent.get("import_count")
|
|
if not itemNum:
|
|
continue
|
|
for i in range(int(itemNum)):
|
|
resContent.append(item)
|
|
data = {
|
|
"id": storage_form.id,
|
|
"return_require_weigh": return_require_weigh,
|
|
"storage_fill_json_content": resContent
|
|
}
|
|
result = {
|
|
"template_xlsx": template_xlsx,
|
|
"data": data
|
|
}
|
|
return respond_to(data=result)
|
|
|
|
|
|
@router.post('/binding', summary="入库绑定上传")
|
|
async def binding_warehousing_drug(request: Request, body: WarehouseingDrugBinding):
|
|
"""
|
|
绑定入库上传 already_import_count
|
|
:param request: Request
|
|
:param drug_list: [{"storage_item_id", str, "rfid": str, "gross_weight": float}]
|
|
:return: {"abnormal_drugs": List[Dict], "normal_drugs": List[Dict]}
|
|
"""
|
|
drug_list = body.drug_list
|
|
if not drug_list:
|
|
return respond_to(code=400, desc="绑定药剂为空,上传数据有误")
|
|
current_user = request.state.current_user
|
|
normal_drugs, abnormal_drugs = list(), list()
|
|
drug_data = list()
|
|
archive_id = request.state.archive_id
|
|
if not archive_id:
|
|
return respond_to(code=400, desc="未获取大类信息")
|
|
template_obj = await Template.get(archive_id=archive_id).prefetch_related('archive')
|
|
is_in_weight = template_obj.archive.params.get("storage_require_weigh")
|
|
print(drug_list)
|
|
|
|
for drug in drug_list:
|
|
if not drug.get("rfid") or not drug.get("fill_json_content"):
|
|
abnormal_drugs.append(drug)
|
|
continue
|
|
|
|
fill_json_content = drug["fill_json_content"]
|
|
|
|
drug_type = fill_json_content.get("drug_type", 'normal')
|
|
# 如果字典不存在改试剂条目,则新增字典条目信息
|
|
if drug_type == "box":
|
|
k_args = {"k1": fill_json_content.get("box_name")}
|
|
query = Q(**k_args)
|
|
else:
|
|
k_args = {f'k{i}': fill_json_content.get(f'k{i}') for i in range(1, 7) if fill_json_content.get(f'k{i}')}
|
|
k_args_with_null = {k: None for k, v in k_args.items() if v is None}
|
|
query = Q(**k_args) & Q(**k_args_with_null)
|
|
dictionary_obj = await Dictionary.filter(query).first()
|
|
if not dictionary_obj:
|
|
k_args["archive"] = template_obj.archive
|
|
k_args["params"] = {"lack_stock_count": 10, "expiration_alert": 30}
|
|
dictionary_obj = await Dictionary.create(**k_args)
|
|
|
|
if is_in_weight and not drug.get("gross_weight"):
|
|
abnormal_drugs.append(drug)
|
|
logger.warning("[入库需称重]入库药剂条目未称重:{0}".format(drug))
|
|
continue
|
|
rfid = drug.get("rfid").upper()
|
|
|
|
# 过期日期如果有填写(生产日期保质期),则添加过期日期字段
|
|
if fill_json_content.get("expire_date"):
|
|
expired_at = fill_json_content.get("expire_date")
|
|
elif fill_json_content.get("lateDate"):
|
|
expired_at = fill_json_content.get("lateDate")
|
|
elif not fill_json_content.get("produce_date") or not fill_json_content.get("shelf_life"):
|
|
expired_at = ""
|
|
else:
|
|
produce_date = fill_json_content.get("produce_date")
|
|
shelf_life = fill_json_content.get("shelf_life")
|
|
production_date = datetime.strptime(produce_date, '%Y-%m-%d')
|
|
shelf_life_date = timedelta(days=int(shelf_life))
|
|
expired_at = production_date + shelf_life_date
|
|
expired_at = expired_at.strftime('%Y-%m-%d')
|
|
|
|
mill_gross_weight = drug.get("gross_weight")
|
|
if mill_gross_weight is None and fill_json_content.get("gross_weight"):
|
|
mill_gross_weight = fill_json_content.get("gross_weight")
|
|
# 位置信息
|
|
cabinet = await Cabinet.get(id=drug.get("cabinet_id"))
|
|
position_str = f"{cabinet.label}"
|
|
if drug.get("drawer_id"):
|
|
drawer_obj = await Drawer.get(id=drug.get("drawer_id"))
|
|
position_str += f"-{drawer_obj.label}"
|
|
|
|
now = timezone_now()
|
|
drug_dict = {
|
|
"dictionary_id": dictionary_obj.id,
|
|
"rfid": rfid,
|
|
"fill_json_content": fill_json_content,
|
|
"bind_id": current_user.id,
|
|
"bind_at": now,
|
|
"storage_id": current_user.id,
|
|
"storage_at": now,
|
|
"template": template_obj,
|
|
"cabinet_id": drug.get("cabinet_id"),
|
|
"position": position_str,
|
|
"state": 0 if len(rfid) >= 16 else 1,
|
|
"barcode": rfid if len(rfid) < 16 else '',
|
|
"taboo_species": fill_json_content.get("taboo_species", ""), # 药剂禁忌种类
|
|
"drug_type": drug_type, # 盒装药剂 普通药剂
|
|
}
|
|
if mill_gross_weight:
|
|
drug_dict.update({
|
|
"remain_gross_weight": convert_to_mg(mill_gross_weight)
|
|
})
|
|
if drug.get("drawer_id"):
|
|
drug_dict.update({
|
|
"drawer_id": drug.get("drawer_id"),
|
|
})
|
|
if expired_at:
|
|
drug_dict.setdefault("expired_at", expired_at)
|
|
drug_data.append(drug_dict)
|
|
|
|
# 药剂批量保存
|
|
except_info = ""
|
|
for data in drug_data:
|
|
try:
|
|
print(data)
|
|
drug_obj = await Drug.create(**data)
|
|
normal_drugs.append(data)
|
|
|
|
# 生成流转记录
|
|
if len(drug_obj.rfid) < 16:
|
|
standard = CodeStandard(request.state.users, drug_obj)
|
|
await standard.put_in(barcode=drug_obj.barcode)
|
|
|
|
# 盒装药剂明细保存
|
|
if data.get("drug_type") == "box":
|
|
await drug_obj.box_item_generate(drug_items=data.get("fill_json_content").get("drug_items"))
|
|
except Exception as e:
|
|
logger.error("药剂导入绑定异常:{0}".format(e))
|
|
except_info += "药剂导入绑定异常:{0}".format(e)
|
|
abnormal_drugs.append(data)
|
|
|
|
desc = except_info if abnormal_drugs else "药剂保存成功"
|
|
return respond_to(code=200, desc=desc, data={"abnormal_drugs": abnormal_drugs, "normal_drugs": normal_drugs})
|
|
def convert_to_mg(value):
|
|
# 如果是纯数字,则直接返回该数字
|
|
if isinstance(value, (int, float)):
|
|
return value
|
|
# 定义正则表达式,匹配以数字开头,可带小数点,后跟 g 或 mg 结尾的模式
|
|
pattern = r'^(\d+(\.\d+)?)(\s*)(g|mg)?$'
|
|
|
|
# 使用正则表达式进行匹配
|
|
match = re.match(pattern, value)
|
|
|
|
if match:
|
|
# 获取匹配到的数值部分和单位
|
|
numeric_part = match.group(1)
|
|
unit = match.group(4)
|
|
|
|
# 如果有单位,并且单位是 'g',则转换为毫克 'mg'
|
|
if unit == 'g':
|
|
numeric_value = float(numeric_part) * 1000 # 将克转换为毫克
|
|
else:
|
|
numeric_value = float(numeric_part)
|
|
|
|
return int(numeric_value) # 返回整数型的毫克值
|
|
|
|
|
|
|
|
return None # 如果没有匹配成功,返回 None 或者你认为合适的默认值
|