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.

282 lines
12 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#!/usr/bin/env python
# encoding: utf-8
"""
@author: tx
@file: drug.py
@time: 2023/5/9 9:14
@desc:
"""
from decimal import Decimal
from enum import IntEnum
import re
from tortoise.expressions import Q
from tortoise import fields, models
from tortoise.queryset import QuerySet
from models.user import User
from models.drug_item import DrugItem, DrugItemStateEnum
from models.dictionary import Dictionary
from conf.setting import setting
class DrugStateEnum(IntEnum):
# 0 = 待入库, 1 = 在库, 2 = 出库, 3 = 空瓶, 4 = 报废
INITIAL = 0
IN = 1
OUT = 2
EMPTY = 3
SCRAP = 4
class Drug(models.Model):
id = fields.UUIDField(pk=True)
dictionary = fields.ForeignKeyField('rms.Dictionary', null=True, to_field='id')
rfid = fields.CharField(max_length=255, unique=True, description='电子标签')
# barcode = fields.CharField(null=True, max_length=255, description='标签条码')
state = fields.IntEnumField(DrugStateEnum, default=DrugStateEnum.INITIAL,
description='0=待入库,1=在库,2=出库,3=空瓶,4=报废')
# hole = fields.SmallIntField(null=True, description='芯片序号')
position = fields.CharField(null=True, max_length=255, description='位置描述')
# remain_gross_weight = fields.IntField(null=True, description='试剂余重')
# last_use_weight = fields.IntField(null=True, description='最后使用重量')
# total_use_weight = fields.IntField(null=True, default=0, description='总用量 耗材为总使用数量')
fill_json_content = fields.JSONField(null=True, description='扩展字段')
# last_return_odd = fields.BooleanField(default=False, description='归还是否异常')
bind = fields.ForeignKeyField('rms.User', related_name="bind", on_delete=fields.SET_NULL, null=True, description='绑定人')
bind_at = fields.DatetimeField(null=True, description='绑定时间')
storage = fields.ForeignKeyField('rms.User', related_name="storage", on_delete=fields.SET_NULL, null=True, description='入库人')
storage_at = fields.DatetimeField(null=True, description='入库时间')
last_receive = fields.ForeignKeyField('rms.User',related_name="last_receive", on_delete=fields.SET_NULL, null=True, description='最后领用人')
last_receive_at = fields.DatetimeField(null=True, description='最后领用时间')
last_return = fields.ForeignKeyField('rms.User',related_name="last_return", on_delete=fields.SET_NULL, null=True, description='最后归还人')
last_return_at = fields.DatetimeField(null=True, description='最后归还时间')
# expired_at = fields.DatetimeField(null=True, description='过期时间')
# open_date = fields.DatetimeField(null=True, description='开封日期')
# board = fields.ForeignKeyField('rms.Board', null=True, on_delete=fields.SET_NULL, description='定位板规格ID')
# drawer = fields.ForeignKeyField('rms.Drawer', null=True, on_delete=fields.SET_NULL, description='抽屉ID')
# drawer_board = fields.ForeignKeyField('rms.DrawerBoard', null=True, on_delete=fields.SET_NULL, description='所属柜体')
cabinet = fields.ForeignKeyField('rms.Cabinet', null=True, on_delete=fields.SET_NULL, description='所属柜体')
template = fields.ForeignKeyField('rms.Template', null=True, description='所属模版')
# taboo_species = fields.CharField(null=True, max_length=50, description="药剂禁忌种类")
# drug_type = fields.CharField(null=True, max_length=50, default='normal',description="药剂类型 普通药剂normal 盒装药剂box")
# files = fields.JSONField(null=True, default="[]")
# near = fields.BooleanField(default=False, description='是否临期')
created_at = fields.DatetimeField(auto_now_add=True)
updated_at = fields.DatetimeField(auto_now=True)
class Meta:
table = 'drugs'
table_description = '药剂表'
@classmethod
async def conflict(cls, rfid) -> bool:
'''药剂标签是否已存在'''
drug = await Drug.get_or_none(rfid=rfid).values_list('id')
return True if drug else False
async def update_last_weight(self, weight: Decimal):
"""
称重更新重量
:param weight: 重量值
:return:
"""
self.last_weight = weight
await self.save()
async def update_last_use_weight(self, weight: Decimal):
"""
更新用量
:param weight: 用量值
:return:
"""
self.last_use_weight = weight
await self.save()
def attribute(self, key, default=None):
return self.fill_json_content.get(key, default)
async def parse_fill_json_content(self, pop_count: bool = True):
"""
解析药剂信息
:return: {"药剂名称": "xx", "药剂规格": "xx"}
"""
fill_json_content = self.fill_json_content
template_obj = await self.template
xlsx_transfer = template_obj.parse_xlsx_transfer().get("xlsx_transfer")
if pop_count and 'import_count' in fill_json_content:
fill_json_content.pop('import_count')
if fill_json_content.get("gross_weight") is not None:
fill_json_content.pop('gross_weight')
result = {
xlsx_transfer.get(key): fill_json_content.get(key)
for key in fill_json_content.keys() if xlsx_transfer.get(key)}
return result
async def attribute_cabinet_id(self):
# drawer_board_obj = await DrawerBoard.get(id = self.drawer_board_id).prefetch_related("cabinet")
if self.drawer_board_id:
drawer_board_obj = await self.drawer_board.prefetch_related("cabinet")
else:
return self.cabinet_id
return drawer_board_obj.cabinet.id
async def attribute_drug_info(self):
"""
full_json_content内部k1-k6数据
:return:
"""
template_obj = await self.template
xlsx = list(filter(lambda i: i.get("checked"), template_obj.xlsx))
xlsx_transfer = {f"{item['key']}": f"{item['text']}" for item in xlsx}
if self.drug_type == 'box':
box_dict = setting.BOX_TRANSFER_DICT
result = {box_dict.get(key): self.fill_json_content.get(key) for key in box_dict}
else:
result = {xlsx_transfer.get(f"k{i}"): self.fill_json_content.get(f"k{i}")
for i in range(1, 7) if self.fill_json_content.get(f"k{i}")}
return result
async def attribute_last_user(self):
"""
最后使用人
判断最后领用人,最后归还人
:return:
"""
if self.last_return_at and not self.last_receive_at:
last_user_id = self.last_return_id
elif self.last_receive_id and self.last_return_id:
last_user_id = self.last_receive_id if self.last_receive_at < self.last_return_at else self.last_return_id
else:
last_user_id = self.last_receive_id or self.last_return_id or self.storage_id
user_obj = await User.get_or_none(id=last_user_id)
if user_obj is None:
return ""
return user_obj.name
async def format_weight(self):
"""
余重
:return:
"""
remain_gross_weight = self.remain_gross_weight
if self.remain_gross_weight is None:
return '-'
net_weight = self.fill_json_content.get('net_weight') # 净含量
density = self.fill_json_content.get('density') # 密度
total_use_weight = self.total_use_weight # 总用量
if self.state == DrugStateEnum.EMPTY:
if net_weight is None or net_weight == '':
return '0g'
else:
unit = re.findall('.*?(mg|g|ml)$', net_weight)[0]
return "0{}".format(unit)
else:
if net_weight is None or net_weight == '':
return "%.1fg" % (remain_gross_weight / 1000) if remain_gross_weight > 1000 else "{}mg".format(
remain_gross_weight)
else:
if net_weight.endswith('mg'):
# 总用量大于净含量则余量显示为0
if total_use_weight >= float(net_weight[:-2]):
return "{}mg".format(0)
return "{}mg".format(float(net_weight[:-2]) - total_use_weight)
if net_weight.endswith('g'):
return "%.1fg" % ((float(net_weight[:-1]) * 1000 - total_use_weight) / 1000)
# 如下的代码不是BUG
# 如果是液体单位ml有密度前端入参是重量需要容量=重量/密度,获取容量
# 如果是液体,没有密度,则返回余重
if net_weight.endswith('ml'):
if density:
density = density[:-4] if density.endswith("g/ml") else density
return "%.1fml" % (float(net_weight[:-2]) - total_use_weight / (float(density) * 1000))
else:
return "%.1fg" % (remain_gross_weight / 1000) if remain_gross_weight > 1000 else "{}mg".format(
remain_gross_weight)
async def parse_archive_params(self, key):
"""
解析获取大类参数
drug_obj -> template ->archive params
:param key:
:return:
"""
archive_obj = self.template.archive
archive_params = archive_obj.params.get(key)
return archive_params
async def box_item_generate(self, *args, **kwargs):
"""
盒装药剂明细条目生成
:param args:
:param kwargs: drug_items=[{xx}]
:return:
"""
drug_items = kwargs.get("drug_items")
if not drug_items:
return False
for drug_item in drug_items:
k_args = {f'k{i}': drug_item.get(f'k{i}') for i in range(1, 7) if drug_item.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"] = self.template.archive
k_args["params"] = {"lack_stock_count": 10, "expiration_alert": 30}
dictionary_obj = await Dictionary.create(**k_args)
# 根据导入数量生成对应数量条目
if not drug_item.get("import_count"):
import_count = 1
else:
import_count = int(drug_item.get("import_count", 1))
drug_item["import_count"] = 1
data = {
"drug_id": self.id,
"name": drug_item.get("k1"),
"fill_json_content": drug_item,
"dictionary": dictionary_obj,
"rfid": self.rfid,
"barcode": self.barcode,
"state": DrugItemStateEnum.IN if len(self.rfid) < 10 else DrugItemStateEnum.INITIAL,
"bind_id": self.bind_id,
"bind_at": self.bind_at,
}
if drug_item.get("expire_date"):
data.update({
"expired_at": drug_item.get("expire_date")
})
for i in range(import_count):
await DrugItem.create(**data)
return True
async def attribute_box_item_info(self, *args, **kwargs):
"""
盒内药剂明细信息
:param args:
:param kwargs:
:return: {”box_count“: 10, "box_name_items": ""}
"""
if self.drug_type != "box":
return {"box_count": 0, "box_name_items": ""}
query = QuerySet(DrugItem).filter(drug_id=self.id)
total_count = await query.count()
drug_item_objs = await query.all()
name_list = []
# 相同名称药剂,只需要显示一次
for drug_item_obj in drug_item_objs:
name = drug_item_obj.fill_json_content.get("k1")
if name and name not in name_list:
name_list.append(name)
name_str = ",".join(name_list) if name_list else ""
return {"box_count": total_count, "box_name_items": name_str}