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