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
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)
|
|
|