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

#!/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 或者你认为合适的默认值