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.

486 lines
18 KiB

# -*- coding:utf-8 -*-
"""
@Created on : 2023/7/3 19:41
@Author: hxl
@Des:
"""
import asyncio
import datetime
import subprocess
from io import BytesIO
import json
import urllib.parse
from openpyxl.utils import get_column_letter
from fastapi import APIRouter, Request, UploadFile, File, Depends, Response
from openpyxl import Workbook
from tortoise.expressions import Q
from conf import setting
from endpoints.cab.warehouse import StorageFormItemEdit
from helper import respond_to, login_required
from helper.barcode_builders import get_barcode_index, update_barcode_index
from helper.create_barcode import CreateBarcode
from helper.utils import timezone_now
from models import StorageForm, Template, Terminal, StorageFormItem, Drug, User, Archive
from models import Role
from pydantic import BaseModel
from models.barcode_builders import BarcodeBuilders
router = APIRouter(prefix='/uploads', dependencies=[Depends(login_required)])
class TemplateUploadItemSaveModel(BaseModel):
storage_id: str
templateName: str
fill_json_content: list
drug_type: str = "normal" # 药剂模板类型 normal普通药剂 box盒装药剂
class TemplateUploadItemModel(BaseModel):
fill_json_content: dict
@router.post('', summary='导入入库模板')
async def create(request: Request, file: UploadFile = File(...)):
"""
:param request: 请求
:param id: 药剂模板ID
:param file: 模板文件
"""
archive_id = request.state.archive_id
if not (file.filename and file.filename.rsplit('.', 1)[1].lower() in ['xlsx', 'xls']):
return respond_to(code=400, desc='请上传正确的文件格式')
template = await Template.get(archive_id=archive_id)
current_user = request.state.current_user
file_contents = await file.read()
try:
data = await upload(user=current_user, template=template, file_contents=file_contents,
filename=file.filename)
except Exception as e:
print("导入有误e", e)
return respond_to(code=400, desc='导入模版有误')
return respond_to(data=data)
async def upload(user: User, template: Template, file_contents: bytes, filename: str):
# 文件
bytes_file = BytesIO(file_contents)
# 条目上传
xlsx = list(filter(lambda i: i.get("checked"), template.xlsx))
xlsx_transfer = {f"{item['key']}": f"{item['text']}" for item in xlsx}
required_keys = [item['key'] for item in xlsx if item.get('required')]
un_required_keys = [item['key'] for item in xlsx if not item.get('required')]
barcode_index = await BarcodeBuilders.get_barcode_index()
data, drug_info = [], []
for row in StorageForm.read_xlsx(bytes_file):
# row {'药剂名称': '硫酸', '药剂规格': '500ml', '药剂纯度': '80%', '生产厂家': '广西硫酸厂'}
# required_item {'k1': '硫酸', 'k2': '500ml', 'k3': '80%'}
required_item = {item["key"]: row[item["text"]] for item in xlsx if item["key"] in required_keys}
# un_required_item {'manufacturer': '广西硫酸厂'}
un_required_item = {item["key"]: row[item["text"]] for item in xlsx if item["key"] in un_required_keys}
keys = {**required_item, **un_required_item}
keys = StorageForm.check_parse_datetime(keys)
if 'import_count' not in keys or keys['import_count'] == '':
keys['import_count'] = 1
barcode_use_num = keys.get("import_count")
keys.setdefault("barcode_from", barcode_index)
barcode_index = int(barcode_index) + int(barcode_use_num)
keys.setdefault("barcode_to", barcode_index - 1)
validate, validate_msg = StorageForm.row_check(required_item, keys, xlsx)
data.append({
"fill_json_content": keys,
"validate": validate,
"validate_msg": validate_msg,
"gross_weight": keys.get("gross_weight") if keys.get("gross_weight") else "",
"import_count": int(keys.get("import_count")) if keys.get("import_count") else 1,
})
drug_info.append(required_item.get('k1', ''))
result = {
"xlsx_transfer": xlsx_transfer,
"data": data
}
await BarcodeBuilders.update_barcode_index(barcode_index)
await StorageForm.create(user=user, template=template, name=filename, fill_json_content=data, drug_info=",".join(drug_info))
return result
@router.post('/drug/{id}', summary='上传试剂附件路径')
async def update(request: Request, id: str, file_path: list):
"""
药剂附件上传
:param request:
:param id: 药剂ID
:param file_path:
:return:
"""
drug_obj = await Drug.get(id=id)
drug_obj.files = json.dumps(file_path)
await drug_obj.save()
return respond_to()
@router.get('/index', summary='获取用户导入模板')
async def index(request: Request, page_no: int = 1, page_size: int = 10, name: str = None, created_at_from: str = '', created_at_to: str = ''):
"""
获取用户导入模板
:param request: 请求
:return:
"""
query = StorageForm.filter()
# 管理员看所有,非管理员看自己模板
current_user = request.state.current_user
role_id = request.state.current_user.role_id
current_role = await Role.get(id=role_id)
if current_role.grade == 0:
query = query.filter(user_id=current_user.id)
if name:
query = query.filter(Q(name__contains=name) | Q(drug_info__contains=name))
if created_at_from:
query = query.filter(created_at__gte=f"{created_at_from} 00:00:00")
if created_at_to:
query = query.filter(created_at__lte=f"{created_at_to} 23:59:59")
query = query.filter(Q(template__archive_id=request.state.archive_id)).prefetch_related("template")
offset = (page_no - 1) * page_size
total_count = await query.count()
storage_form_objs = await query.offset(offset).limit(page_size).order_by('-created_at')
res_list = []
for obj in storage_form_objs:
item = dict(obj)
count = 0
for i in item.get("fill_json_content"):
if i.get("fill_json_content").get("import_count",0):
count = count + int(i.get("fill_json_content").get("import_count",0))
item['import_count'] = count
if item.get("fill_json_content") and item.get("fill_json_content")[0].get("fill_json_content"):
item['drug_type'] = item.get("fill_json_content")[0].get("fill_json_content").get("drug_type", "normal")
res_list.append(item)
return respond_to(200, data={"total_count": total_count, "list": res_list})
@router.get('/detail/{storage_form_id}', summary='获取模板内容')
async def index(storage_form_id: str):
"""
用户入库模板条目
:param storage_form_id: 导入模板id
:return:
"""
storage_form = await StorageForm.get_or_none(id=storage_form_id).prefetch_related('template')
if not storage_form:
return respond_to(404, desc="模板未查询到")
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()
data = {
"id": storage_form.id,
"fill_json_content": storage_form.fill_json_content
}
result = {
"template_xlsx": template_xlsx,
"data": data,
}
return respond_to(data=result)
@router.post('/item', summary='单品入库')
async def create(request: Request, body: TemplateUploadItemModel):
"""
药剂模板条目新增,单品入库
:param request: Request
:param body: fill_json_content {"k1": xx, "k2": xx}
:return:
"""
current_user = request.state.current_user
template_obj = await Template.get(archive_id=request.state.archive_id)
if 'import_count' not in body.fill_json_content or body.fill_json_content['import_count'] == '':
body.fill_json_content['import_count'] = 1
fill_json_content = {'fill_json_content': body.fill_json_content}
barcode_use_num = body.fill_json_content.get("import_count")
barcode_index = await BarcodeBuilders.get_barcode_index()
fill_json_content.get("fill_json_content").setdefault("barcode_from", barcode_index)
barcode_index = int(barcode_index) + int(barcode_use_num)
fill_json_content.get("fill_json_content").setdefault("barcode_to", barcode_index - 1)
await BarcodeBuilders.update_barcode_index(barcode_index)
now = datetime.datetime.now()
name = f"{now.strftime('%Y%m%d%H%M%S')}-单品入库模板"
storage_form_obj = await StorageForm.get_or_none(user=current_user, template=template_obj, name=name)
drug_info = body.fill_json_content.get("k1", "")
if not storage_form_obj:
await StorageForm.create(
user=current_user,
template=template_obj,
name=name,
fill_json_content=[fill_json_content],
drug_info=drug_info
)
return respond_to(200, desc="单品入库成功")
storage_form_content = storage_form_obj.fill_json_content
storage_form_content.append(fill_json_content)
storage_form_obj.fill_json_content = storage_form_content
storage_form_obj.drug_info = drug_info
await storage_form_obj.save()
return respond_to(200, desc="单品入库成功")
@router.post('/template/save', summary='模板保存')
async def create(request: Request, body: TemplateUploadItemSaveModel):
"""
药剂模板条目新增,模板保存
盒装药剂在fill_json_content中增加明细 drug_items: [{"k1": xx, "k2": xx},{}]
:param request: Request
:param body: fill_json_content [{"k1": xx, "k2": xx, "import_count": 3, "drug_items": [{"k1": xx, "k2": xx},{}]}, {"k1": xx, "k2": xx, "import_count": 3}]
:盒装药剂 fill_json_content [{"box_name": xx, "box_name_list": "xx,xx","box_ph": "xx", "import_count": 3, "drug_items": [{"k1": xx, "k2": xx},{}]}, {"k1": xx, "k2": xx, "import_count": 3}]
:return:
"""
current_user = request.state.current_user
archive_id = request.state.archive_id
template_obj = await Template.get(archive_id=archive_id)
data = list()
barcode_index = await BarcodeBuilders.get_barcode_index()
print(barcode_index)
drug_info = []
for content in body.fill_json_content:
cdata = dict(content)
if 'import_count' not in cdata or cdata['import_count'] == '':
cdata['import_count'] = 1
per_count = cdata.get("import_count")
cdata["barcode_from"] = (barcode_index)
barcode_index = int(barcode_index) + int(per_count)
cdata["barcode_to"] = (barcode_index - 1)
cdata["drug_type"] = body.drug_type
data.append({"fill_json_content": cdata})
# 模板药剂详情 盒装药剂 普通药剂
if body.drug_type == "box":
drug_info.append(content.get("box_name_list"))
else:
drug_info.append(content.get("k1"))
await BarcodeBuilders.update_barcode_index(barcode_index)
print(f'after: {data}')
storage_obj = await StorageForm.get_or_none(id=body.storage_id)
# print(f'before: {storage_obj.fill_json_content}')
count = await StorageForm.filter(user=current_user, template=template_obj).filter(name__contains = body.templateName.split("_")[0]).count()
after_fill_json_content = data
if (not storage_obj or body.templateName != storage_obj.name
or not compare_storage_json_content(storage_obj.fill_json_content, after_fill_json_content)):
await StorageForm.create(
user=current_user,
template=template_obj,
name=body.templateName,
fill_json_content=data,
drug_info=",".join(drug_info) if drug_info else ""
)
return respond_to(200, desc="模板保存成功")
else:
return respond_to()
def compare_storage_json_content(before_fill_json_content, after_fill_json_content):
if len(before_fill_json_content) != len(after_fill_json_content):
return False
for i in range(len(before_fill_json_content)):
obj1 = before_fill_json_content[i].copy()
obj2 = after_fill_json_content[i].copy()
obj1.pop('id', None)
obj2.pop('id', None)
obj1.pop('barcode_before', None)
obj2.pop('barcode_before', None)
obj1.pop('barcode_after', None)
obj2.pop('barcode_after', None)
if obj1 != obj2:
return False
return True
@router.delete('/delete/{storageId}', summary='删除模板')
async def delete_role(storageId: str):
"""
删除模板
:param id
:return
"""
storage_form_obj = await StorageForm.get_or_none(id=storageId)
if not storage_form_obj:
return respond_to(code=400, desc='删除失败,该模板不存在')
await storage_form_obj.delete()
return respond_to(200, desc='模板删除成功')
@router.get('/download/{storageId}', summary='用户模板内容导出')
async def download_storage_fill_content(request: Request, storageId: str, visit_type: int = 2):
"""
用户模板内容导出
"""
# current_user = request.state.current_user
storage_form_obj = await StorageForm.get_or_none(id=storageId)
if not storage_form_obj:
return respond_to(code=400, desc='导出失败,模板不存在')
template_obj = await Template.get(id=storage_form_obj.template_id)
template_map = dict()
for item in template_obj.xlsx:
if not item.get("checked"):
continue
template_map.setdefault(item.get("key"), item)
data = list()
for item in storage_form_obj.fill_json_content:
content = item.get("fill_json_content")
cdata = dict(content)
if 'id' in cdata:
cdata.pop("id")
if '$index' in cdata:
cdata.pop("$index")
if cdata:
data.append(cdata)
workbook = Workbook()
sheet = workbook.active
has_header = 0
header_col = 0
key_list = list()
for index, item in enumerate(data, 1):
if not has_header:
header_col = len(template_map)
key_list = list(template_map.keys())
for col in range(len(template_map)):
chr = get_column_letter(col + 1)
template_title = template_map.get(key_list[col], None)
sheet[f'{chr}1'] = template_title.get("text")
has_header = 1
row = index + 1
for col in range(len(template_map)):
if col > header_col:
break
chr = get_column_letter(col + 1)
sheet[f'{chr}{row}'] = item.get(key_list[col], None)
# 设置每列为文本格式
for row in sheet.iter_rows(min_row=1, max_row=1000, min_col=1, max_col=header_col + 1):
for cell in row:
cell.number_format = '@'
binary = BytesIO()
workbook.save(binary)
binary.seek(0)
filename = f"{timezone_now().strftime('%Y%m%d%H%M')}-{storage_form_obj.name}-入库模板.xlsx"
encoded_filename = urllib.parse.quote(filename)
return Response(content=binary.getvalue(),
media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
headers={'Content-Disposition': f'attachment; filename={encoded_filename}'})
# @router.get('/barcode/print/{storageId}', summary='打印模板条码')
# async def barcode_print(storageId: str):
# """
# 打印模板条码
# :param id: 用户药剂模板id
# :return:
# """
# storage_form_obj = await StorageForm.get(id=storageId)
# if not storage_form_obj.fill_json_content:
# return respond_to(500, desc="传入参数有误")
#
# for item in storage_form_obj.fill_json_content:
# json_content = item.get("fill_json_content")
# for code in range(json_content.get("barcode_from"), json_content.get("barcode_to")):
# # 生成 Code 128 条码对象
# barcode = Code128(str(code), writer=ImageWriter())
# # 将条码保存为临时图片文件
# filename = f'barcode_{code}.png'
# barcode.save(filename)
# printer_name = "TSC_TTP-244_Pro"
# # 使用操作系统的命令将图片发送到打印机打印
# subprocess.run(['lpr', '-P', printer_name, filename])
# return respond_to()
@router.get('/barcode/print/{storageId}', summary='打印条码')
async def barcode_print(request: Request, storageId: str):
"""
打印模板条码
:param storageId: 用户药剂模板id
:return:
"""
storage_form_obj = await StorageForm.get(id=storageId)
if not storage_form_obj.fill_json_content:
return respond_to(500, desc="传入参数有误")
key=''
try:
parse_fill_json_content = await storage_form_obj.parse_fill_json_content()
if '厂商' in parse_fill_json_content.keys():
key =parse_fill_json_content['厂商']
except Exception as e:
print(e)
archive_id = request.state.archive_id
archive_obj = await Archive.get(id=archive_id)
for item in storage_form_obj.fill_json_content:
json_content = item.get("fill_json_content")
for code in range(int(json_content.get("barcode_from")), int(json_content.get("barcode_to") + 1)):
obj = CreateBarcode()
# 耗材纸打印标签
if archive_obj.name == "耗材":
obj.archive_name = "耗材"
if key:
obj.create_Code128_img(str(code),json_content.get("k1"),json_content.get(key))
else:
obj.create_Code128_img(str(code),json_content.get("k1"),'')
return respond_to()
@router.get('/print_count/{storageId}', summary='条码最大最小值')
async def print_count(storageId: str):
"""
条码最大最小值
:param storageId: 用户药剂模板id
:return:
"""
storage_form_obj = await StorageForm.get(id=storageId)
if not storage_form_obj.fill_json_content:
return respond_to(500, desc="传入参数有误")
min_value = float('inf')
max_value = float('-inf')
for item in storage_form_obj.fill_json_content:
json_content = item.get("fill_json_content")
try:
json_content = dict(json_content)
barcode_from = int(json_content["barcode_from"])
barcode_to = int(json_content["barcode_to"])
min_value = min(min_value, barcode_from)
max_value = max(max_value, barcode_to)
except (ValueError, KeyError):
return respond_to(500, desc="条码异常")
data = {
"min": min_value,
"max": max_value
}
return respond_to(200, desc='查询成功', data=data)