#!/usr/bin/env python # encoding: utf-8 """ @author: tx @file: drug.py @time: 2023/5/11 11:04 @desc: 药剂领用归还 """ from decimal import Decimal from fastapi import APIRouter, Request, Depends from fastapi.encoders import jsonable_encoder from pydantic import BaseModel from tortoise.queryset import Q from conf import setting from helper.drug import drugs_except_info, drugs_list_info, gram_to_milligram, milligram_to_gram, open_update_expired_at from helper import respond_to, login_required from models import Dictionary from models.drug import Drug, DrugStateEnum from models.terminal import Terminal from models.drawers import Drawer from models.cabinet import Cabinet from transfer.standard import Standard from transfer.code_standard import CodeStandard router = APIRouter(prefix='/drugs', dependencies=[Depends(login_required)]) class DrugUpdateRequest(BaseModel): open_date: str = "" # 开封日期 weight: Decimal = 0.0 # 余量 use_weight: Decimal = 0.0 # 用量 rfid: str = "" id: str = "" class DrugUpdateExpired(BaseModel): expired_at: str rfid: str = "" id: str = "" class DrugRfids(BaseModel): rfids: list class DrugTakeOut(BaseModel): terminal_id: str | None page_no: int = 1 page_size: int = 20 class UpdateDictionary(BaseModel): lack_stock_count: int = 0 expiration_alert: int = 0 class EmptyBottleDisposal(BaseModel): rfid: str weight: int | None open_date: str | None class ReturnForm(BaseModel): # barcode: str weight: Decimal = 0.0 # 余量 use_weight: Decimal = 0.0 # 用量 drawer_id: str = '' # 层id @router.post('/empty', summary='试剂置为空瓶') async def empty_bottle_disposal(request: Request, post: EmptyBottleDisposal): """ 试剂置为空瓶 :param request: Request请求头信息 :param post: 空瓶信息的请求体 :return: """ users = request.state.users standard = Standard(users) rfid = post.rfid # user_weight = post.weight open_date = post.open_date if open_date: drug = await Drug.get(rfid=rfid) drug.open_date = open_date await drug.save() await standard.empty(rfid) return respond_to() @router.post('/weight', summary="药剂用量或余量更新") async def update(keyword: DrugUpdateRequest): """ 药剂用量或余量更新 接收id或rfid都可以更新重量 余量与用量二选一,只有一个有值 :param keyword: 包含更新信息的请求体 :return: """ print("open_date", keyword.open_date) rfid = keyword.rfid if keyword.rfid else "" query_param = {"id": keyword.id} if keyword.id else {"rfid": rfid} drug_obj = await Drug.get(**query_param).prefetch_related("template") if keyword.weight: mill_weight = keyword.weight await drug_obj.update_last_weight(weight=mill_weight) elif keyword.use_weight: mill_weight = keyword.use_weight await drug_obj.update_last_use_weight(weight=mill_weight) if keyword.open_date: if not drug_obj.open_date: drug_obj.open_date = keyword.open_date template_obj = await drug_obj.template new_expired_at = open_update_expired_at(drug_obj, template_obj, keyword.open_date) if new_expired_at: drug_obj.expired_at = new_expired_at await drug_obj.save() return respond_to(code=200, desc="重量更新成功") @router.post('/except', summary='RFID药剂异常信息') async def index(request: DrugRfids): """ 获取RFID药剂异常信息 :return: """ result = list() drugs = await Drug.filter(rfid__in=request.rfids).prefetch_related('dictionary', 'template').all() terminal_obj = await Terminal.get(id=setting.TERMINAL_ID) for drug in drugs: data = await drugs_except_info(drug, terminal_obj) data["rfid"] = drug.rfid result.append(data) # 传入rfid与data所有rfid比较,差集就是非法标签 post_rfid = set(request.rfids) effective_rfid = set([i["rfid"] for i in result]) diff_rfid = post_rfid - effective_rfid if diff_rfid: result.extend([{ "rfid": rfid, "is_rfid_except": True, "sound": True, "message": "非法标签", "state": 0, } for rfid in diff_rfid]) return respond_to(code=200, desc="获取RFID药剂信息成功", data=result) @router.post('', summary='RFID药剂详情') async def index(request: DrugRfids): """ 获取RFID药剂信息 药剂信息详情 :return: """ drugs = list() for rfid in request.rfids: drug_obj = await Drug.get_or_none(rfid=rfid).prefetch_related('dictionary', 'template') if drug_obj is None: continue result = jsonable_encoder(drug_obj) template_obj = await drug_obj.template terminal_obj = await Terminal.get(id=setting.TERMINAL_ID) data = await drugs_except_info(drug_obj, terminal_obj) drug_info = await drug_obj.attribute_drug_info() parse_fill_json_content = await drug_obj.parse_fill_json_content() parse_fill_json_content.update({f'余重': f'{milligram_to_gram(drug_obj.remain_gross_weight)}'}) return_require_weigh = template_obj.archive.params.get("return_require_weigh", '') result.update({ "drugs_except_info": data, "fill_json_content": parse_fill_json_content, "rfid_drug": {drug_obj.rfid: f"[{drug_obj.rfid}]" + ",".join(list(map(lambda x:str(x), drug_info.values()))) + "," + f"{drug_obj.remain_gross_weight}"}, "return_require_weigh": return_require_weigh }) drugs.append(result) return respond_to(code=200, desc="获取RFID药剂信息成功", data=drugs) @router.get('', summary='药剂列表查询') async def index(request: Request, drug_name: str = '', page_no: int = 1, page_size: int = 20): """ 货架药剂列表查询 :return: """ query = Drug.filter() cabinets_ids = await Cabinet.filter(type=3).values_list("id", flat=True) if cabinets_ids: query = query.filter(cabinet_id__in=cabinets_ids) if drug_name: query = query.filter(Q(dictionary__k1__contains=drug_name) | Q(dictionary__k2__contains=drug_name)) total_count = await query.count() offset = (page_no - 1) * page_size drugs = await query.offset(offset).limit(page_size).all() result = await drugs_list_info(drugs) return respond_to(200, data=dict(count=total_count, drugs=result)) @router.get('/take_out', summary='药剂领用查询') async def index(request: Request, drug_name: str = '', take_out_type: int = 0, page_no: int = 1, page_size: int = 20): """ 药剂领用列表查询 本终端的排前面 :return: """ terminal = await Terminal.get(id=setting.TERMINAL_ID) query = Drug.filter(state=DrugStateEnum.IN).order_by('-drawer_board_id') if not take_out_type: query = query.filter(drawer_board__cabinet__terminal=terminal) if drug_name: query = query.filter(Q(dictionary__k1__contains=drug_name) | Q(dictionary__k2__contains=drug_name)) total_count = await query.count() offset = (page_no - 1) * page_size drugs = await query.offset(offset).limit(page_size).all() result = await drugs_list_info(drugs) return respond_to(200, data=dict(count=total_count, drugs=result)) @router.get('/put_in', summary='获取当前用户待归还清单') async def index(request: Request): """ 获取当前用户壁挂终端待归还清单 :return: """ current_user = request.state.current_user terminal = await Terminal.get(id=setting.TERMINAL_ID) # 当前柜体待归还清单 query = Drug.filter(last_receive_id=current_user.id, state=DrugStateEnum.OUT).filter(cabinet__terminal=terminal) drugs = await query.all() result = await drugs_list_info(drugs) return respond_to(data=result) @router.put('/expired_at', summary='药剂信息修改') async def update(keyword: DrugUpdateExpired): """ 药剂信息过期时间修改 :param id: 用户药剂模板条目id :param keyword: :return: """ rfid = keyword.rfid if keyword.rfid else "" query_param = {"id": keyword.id} if keyword.id else {"rfid": rfid} drug_obj = await Drug.get(**query_param) drug_obj.expired_at = keyword.expired_at await drug_obj.save() return respond_to(code=200, desc="更新成功") @router.get('/dictionaries', summary='获取药剂字典列表') async def index(keyword: str = '', page_no: int = 1, page_size: int = 10): """ 获取药剂字典列表 :param keyword: 按药剂信息搜索 :param page_no: 分页页码,默认为1 :param page_size: 分页大小,默认为10 :return: """ dictionaries = await Dictionary.all().offset((page_no - 1) * page_size).limit(page_size).values() result_list = [] for dictionary in dictionaries: drug_info = ",".join([value for key, value in dictionary.items() if key.startswith('k') and value is not None]) if keyword in drug_info: dictionary['drug_info'] = drug_info for k, v in dictionary['params'].items(): dictionary[k] = v result_list.append(dictionary) return respond_to(data={'data': result_list, 'count': len(result_list)}) @router.put('/dictionary/{id}', summary='编辑药剂字典') async def update(id: str, post: UpdateDictionary): """ 编辑药剂字典 :param id: id :param post: :return: """ dictionary = await Dictionary.get(id=id) dictionary.params = post.dict() await dictionary.save() return respond_to() @router.put('/receive/{bar_code}', summary='药剂领用') async def update(request: Request, bar_code: str): """ 药剂领用 每瓶药剂扫码即领用 :param request: request :param bar_code: 药剂条码 :return: """ drug_obj = await Drug.get_or_none(Q(barcode=bar_code) | Q(rfid=bar_code)).prefetch_related('template', 'dictionary') if not drug_obj: return respond_to(code=404, desc="药剂未找到") if drug_obj.state in [DrugStateEnum.OUT, DrugStateEnum.EMPTY]: desc = "药剂不在库" if DrugStateEnum.OUT else "药剂已空瓶" return respond_to(code=400, desc=desc) users = request.state.users standard = CodeStandard(users, drug_obj) transfer_results = await standard.take_out(barcode=bar_code) print('领用结果', transfer_results) return respond_to(desc="领用完成", data=transfer_results) @router.put('/return/{bar_code}', summary='药剂归还') async def update(request: Request, bar_code: str, keyword: ReturnForm): """ 药剂归还 药剂扫码后填入用量/余量,归还成功 :param bar_code: 药剂条码 :param post: :return: """ # 如果调用保存已经保存过用量数据,则不再进行保存 drug_obj = await Drug.get_or_none(Q(barcode=bar_code) | Q(rfid=bar_code)).prefetch_related('template', 'dictionary') if not drug_obj: return respond_to(code=404, desc="药剂未找到") if drug_obj.state == DrugStateEnum.IN: return respond_to(code=401, desc="药剂已在库,请勿再次归还!") if keyword.weight: # 余量转用量,更新用量 mill_weight = drug_obj.remain_gross_weight - keyword.weight await drug_obj.update_last_use_weight(weight=mill_weight) elif keyword.use_weight: mill_weight = keyword.use_weight await drug_obj.update_last_use_weight(weight=mill_weight) # 位置更新 if keyword.drawer_id: drawer_obj = await Drawer.get(id=keyword.drawer_id).prefetch_related("cabinet") cabinet = drawer_obj.cabinet position_str = f"{cabinet.label}-{drawer_obj.label}" drug_obj.drawer = drawer_obj drug_obj.cabinet = cabinet drug_obj.position = position_str await drug_obj.save() users = request.state.users standard = CodeStandard(users, drug_obj) transfer_results = await standard.put_in(barcode=bar_code) print('归还结果', transfer_results) return respond_to(desc="归还完成", data=transfer_results)