// Copyright (c) Shenyang Leading Edge Intelligent Technology Co., Ltd. All rights reserved.

package implments

import (
	baseDal "LAPP_ACURA_MOM_BACKEND/dao/base"
	eolDal "LAPP_ACURA_MOM_BACKEND/dao/eol"
	meDal "LAPP_ACURA_MOM_BACKEND/dao/me"
	dal "LAPP_ACURA_MOM_BACKEND/dao/om"
	"LAPP_ACURA_MOM_BACKEND/db"
	"LAPP_ACURA_MOM_BACKEND/global"
	"LAPP_ACURA_MOM_BACKEND/grmi"
	baseMeta "LAPP_ACURA_MOM_BACKEND/meta/base"
	eolMeta "LAPP_ACURA_MOM_BACKEND/meta/eol"
	meMeta "LAPP_ACURA_MOM_BACKEND/meta/me"
	meta "LAPP_ACURA_MOM_BACKEND/meta/om"
	baseModel "LAPP_ACURA_MOM_BACKEND/models/base"
	eolModel "LAPP_ACURA_MOM_BACKEND/models/eol"
	meModel "LAPP_ACURA_MOM_BACKEND/models/me"
	model "LAPP_ACURA_MOM_BACKEND/models/om"
	"github.com/go-xorm/xorm"
	"strconv"
	"strings"
	"time"
)

/******************************************************************************
 *
 * @Description              : SerialOrder的默认查询条件
 *
 * @Author                   : 代码生成器创建
 *
 * @Date                     : 2021-10-22 14:06:37
 *
 ******************************************************************************/
var DefaultConditionOfSerialOrder = grmi.NewCondition(
	map[string]grmi.ConditionItem{
		meta.SerialOrder_SerialOrderId.UrlParameterName:      grmi.NewConditionItem(meta.SerialOrder_SerialOrderId, grmi.Equal, true),
		meta.SerialOrder_ArtId.UrlParameterName:              grmi.NewConditionItem(meta.SerialOrder_ArtId, grmi.Equal, false),
		meta.SerialOrder_ProjectId.UrlParameterName:          grmi.NewConditionItem(meta.SerialOrder_ProjectId, grmi.Equal, false),
		meta.SerialOrder_Descr.UrlParameterName:              grmi.NewConditionItem(meta.SerialOrder_Descr, grmi.Equal, false),
		meta.SerialOrder_WorkOrderId.UrlParameterName:        grmi.NewConditionItem(meta.SerialOrder_WorkOrderId, grmi.Equal, false),
		meta.SerialOrder_CustOrderId.UrlParameterName:        grmi.NewConditionItem(meta.SerialOrder_CustOrderId, grmi.Equal, false),
		meta.SerialOrder_FirstPieceInBatch.UrlParameterName:  grmi.NewConditionItem(meta.SerialOrder_FirstPieceInBatch, grmi.Equal, false),
		meta.SerialOrder_LastPieceInBatch.UrlParameterName:   grmi.NewConditionItem(meta.SerialOrder_LastPieceInBatch, grmi.Equal, false),
		meta.SerialOrder_PreSchedKey.UrlParameterName:        grmi.NewConditionItem(meta.SerialOrder_PreSchedKey, grmi.Equal, false),
		meta.SerialOrder_SchedKey.UrlParameterName:           grmi.NewConditionItem(meta.SerialOrder_SchedKey, grmi.Equal, false),
		meta.SerialOrder_Priority.UrlParameterName:           grmi.NewConditionItem(meta.SerialOrder_Priority, grmi.Equal, false),
		meta.SerialOrder_OrderType.UrlParameterName:          grmi.NewConditionItem(meta.SerialOrder_OrderType, grmi.Equal, false),
		meta.SerialOrder_OrderType1.UrlParameterName:         grmi.NewConditionItem(meta.SerialOrder_OrderType1, grmi.Equal, false),
		meta.SerialOrder_OrderType2.UrlParameterName:         grmi.NewConditionItem(meta.SerialOrder_OrderType2, grmi.Equal, false),
		meta.SerialOrder_PlanResourceId.UrlParameterName:     grmi.NewConditionItem(meta.SerialOrder_PlanResourceId, grmi.Equal, false),
		meta.SerialOrder_UsedResourceId.UrlParameterName:     grmi.NewConditionItem(meta.SerialOrder_UsedResourceId, grmi.Equal, false),
		meta.SerialOrder_PlanQty.UrlParameterName:            grmi.NewConditionItem(meta.SerialOrder_PlanQty, grmi.Equal, false),
		meta.SerialOrder_QtyUomId.UrlParameterName:           grmi.NewConditionItem(meta.SerialOrder_QtyUomId, grmi.Equal, false),
		meta.SerialOrder_OpTimePerItem.UrlParameterName:      grmi.NewConditionItem(meta.SerialOrder_OpTimePerItem, grmi.Equal, false),
		meta.SerialOrder_TimeUomId.UrlParameterName:          grmi.NewConditionItem(meta.SerialOrder_TimeUomId, grmi.Equal, false),
		meta.SerialOrder_PlanStartDate.UrlParameterName:      grmi.NewConditionItem(meta.SerialOrder_PlanStartDate, grmi.Approximate, false),
		meta.SerialOrder_PlanEndDate.UrlParameterName:        grmi.NewConditionItem(meta.SerialOrder_PlanEndDate, grmi.Approximate, false),
		meta.SerialOrder_SetupStartTime.UrlParameterName:     grmi.NewConditionItem(meta.SerialOrder_SetupStartTime, grmi.Approximate, false),
		meta.SerialOrder_SetupEndTime.UrlParameterName:       grmi.NewConditionItem(meta.SerialOrder_SetupEndTime, grmi.Approximate, false),
		meta.SerialOrder_PlanStartTime.UrlParameterName:      grmi.NewConditionItem(meta.SerialOrder_PlanStartTime, grmi.Approximate, false),
		meta.SerialOrder_PlanEndTime.UrlParameterName:        grmi.NewConditionItem(meta.SerialOrder_PlanEndTime, grmi.Approximate, false),
		meta.SerialOrder_ActStartTime.UrlParameterName:       grmi.NewConditionItem(meta.SerialOrder_ActStartTime, grmi.Approximate, false),
		meta.SerialOrder_ActEndTime.UrlParameterName:         grmi.NewConditionItem(meta.SerialOrder_ActEndTime, grmi.Approximate, false),
		meta.SerialOrder_PickingFlag.UrlParameterName:        grmi.NewConditionItem(meta.SerialOrder_PickingFlag, grmi.Equal, false),
		meta.SerialOrder_IsFirstPieceToggle.UrlParameterName: grmi.NewConditionItem(meta.SerialOrder_IsFirstPieceToggle, grmi.Equal, false),
		meta.SerialOrder_SyncKey.UrlParameterName:            grmi.NewConditionItem(meta.SerialOrder_SyncKey, grmi.Equal, false),
		meta.SerialOrder_BackFlushFlag.UrlParameterName:      grmi.NewConditionItem(meta.SerialOrder_BackFlushFlag, grmi.Equal, false),
		meta.SerialOrder_RepairFlag.UrlParameterName:         grmi.NewConditionItem(meta.SerialOrder_RepairFlag, grmi.Equal, false),
		meta.SerialOrder_CtrlPara1.UrlParameterName:          grmi.NewConditionItem(meta.SerialOrder_CtrlPara1, grmi.Equal, false),
		meta.SerialOrder_CtrlPara2.UrlParameterName:          grmi.NewConditionItem(meta.SerialOrder_CtrlPara2, grmi.Equal, false),
		meta.SerialOrder_CtrlStr1.UrlParameterName:           grmi.NewConditionItem(meta.SerialOrder_CtrlStr1, grmi.Equal, false),
		meta.SerialOrder_CtrlStr2.UrlParameterName:           grmi.NewConditionItem(meta.SerialOrder_CtrlStr2, grmi.Equal, false),
		meta.SerialOrder_QualityCheck.UrlParameterName:       grmi.NewConditionItem(meta.SerialOrder_QualityCheck, grmi.Equal, false),
		meta.SerialOrder_QualityCheckTime.UrlParameterName:   grmi.NewConditionItem(meta.SerialOrder_QualityCheckTime, grmi.Equal, false),
		meta.SerialOrder_MainOrderId.UrlParameterName:        grmi.NewConditionItem(meta.SerialOrder_MainOrderId, grmi.Equal, false),
		meta.SerialOrder_EOLCheck.UrlParameterName:           grmi.NewConditionItem(meta.SerialOrder_EOLCheck, grmi.Equal, false),
		meta.SerialOrder_CtrlTime1.UrlParameterName:          grmi.NewConditionItem(meta.SerialOrder_CtrlTime1, grmi.Approximate, false),
		meta.SerialOrder_CtrlTime2.UrlParameterName:          grmi.NewConditionItem(meta.SerialOrder_CtrlTime2, grmi.Approximate, false),
		meta.SerialOrder_LastModify.UrlParameterName:         grmi.NewConditionItem(meta.SerialOrder_LastModify, grmi.Approximate, false),
		meta.SerialOrder_LastUser.UrlParameterName:           grmi.NewConditionItem(meta.SerialOrder_LastUser, grmi.Equal, false),
		meta.SerialOrder_CreateTime.UrlParameterName:         grmi.NewConditionItem(meta.SerialOrder_CreateTime, grmi.Approximate, false),
	},
	nil,
)

/******************************************************************************
 *
 * @Description              : SerialOrder的默认分页查询条件
 *
 * @Author                   : 代码生成器创建
 *
 * @Date                     : 2021-10-22 14:06:37
 *
 ******************************************************************************/
var DefaultConditionOfSerialOrderAndPaging = grmi.NewCondition(
	DefaultConditionOfSerialOrder.Items,
	grmi.NewPaging(),
)

/******************************************************************************
 *
 * @Struct Name              : SerialOrderServiceImplement
 *-----------------------------------------------------------------------------
 *
 * @Description              : SerialOrder的服务实现
 *
 * @Author                   : 代码生成器创建
 *
 * @Date                     : 2021-10-22 14:06:37
 *
 ******************************************************************************/
type SerialOrderServiceImplement struct {
}

/******************************************************************************
 *
 * @Function Name            : NewSerialOrderDAOImplement
 *-----------------------------------------------------------------------------
 *
 * @Description              : 创建一个SerialOrderDAOImplement实例
 *
 * @Return Value             : SerialOrderDAOImplement实例
 *
 * @Author                   : 代码生成器创建
 *
 * @Date                     : 2021-10-22 14:06:37
 *
 ******************************************************************************/
func NewSerialOrderServiceImplement() *SerialOrderServiceImplement {
	return &SerialOrderServiceImplement{}
}

/******************************************************************************
 *
 * @Reference LAPP_ACURA_MOM_BACKEND/services/om/SerialOrderService.InsertOne
 *
 ******************************************************************************/
func (impl *SerialOrderServiceImplement) InsertOne(user *global.User, entity *model.SerialOrder) error {

	grmi.Log(user, "/services/om/implments/SerialOrder.service.impl.go", "InsertOneSerialOrder", "插入一个SerialOrder")

	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	if entity.OrderType != baseModel.ODER_TYPE_INTERVAL {
		return grmi.NewBusinessError("只允许创建内部订单")
	}
	entity.PlanQty = 1
	if err := session.Begin(); err != nil {
		return err
	}
	productDao := meDal.NewProductDAO(session, user.PlantNr, user.UserId)
	product, err := productDao.SelectOne(entity.ArtId, entity.ProjectId)
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("查询产品数据失败, error:" + err.Error())
	}
	if product == nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("生产的产品不存在, 产品ID:" + entity.ArtId)
	}
	projectDao := meDal.NewProjectDAO(session, user.PlantNr, user.UserId)
	project, err := projectDao.SelectOne(entity.ProjectId)
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("查询车型项目数据失败, error:" + err.Error())
	}
	if project == nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("车型项目数据不存在, 项目ID:" + entity.ProjectId)
	}
	if product.MultiWorkLineToggle {
		productWorkLineDao := meDal.NewProductWorkLineLstDAO(session, user.PlantNr, user.UserId)
		workLineLi, err := productWorkLineDao.Select([]grmi.Predicate{
			meMeta.ProductWorkLineLst_ProductId.NewPredicate(grmi.Equal, entity.ArtId),
			meMeta.ProductWorkLineLst_ProjectId.NewPredicate(grmi.Equal, entity.ProjectId),
		}, nil)
		if err != nil {
			return grmi.NewBusinessError("查询总成可生产产线失败, 错误:" + err.Error())
		}
		var canProduct bool
		for _, workLine := range workLineLi {
			if workLine.WorkLineId == entity.UsedResourceId {
				canProduct = true
				break
			}
		}
		if !canProduct {
			return grmi.NewBusinessError("该总成不能在指定的产线上生产！")
		}
	} else {
		if product.DefaultWorkLineId != entity.UsedResourceId {
			return grmi.NewBusinessError("该总成不能在指定的产线上生产！")
		}
	}
	dao := dal.NewSerialOrderDAO(session, user.PlantNr, user.UserId)
	statusDao := dal.NewSerialOrderStatusDAO(session, user.PlantNr, user.UserId)
	snrDao := baseDal.NewSnrDAO(session, user.PlantNr, user.UserId)
	//serialNumberStr, err := snrDao.GetNextSnr(project.SerialOrderSnr)
	//if err != nil {
	//	_ = session.Rollback()
	//	return grmi.NewBusinessError("生成工单流水号失败, error:" + err.Error())
	//}
	//serialNumber, err := strconv.Atoi(serialNumberStr)
	//if err != nil {
	//	_ = session.Rollback()
	//	return grmi.NewBusinessError("流水号规则错误, error:" + err.Error())
	//}
	//first := serialNumber / 34
	//if first > 34 {
	//	_ = session.Rollback()
	//	return grmi.NewBusinessError("超过流水号支持的范围")
	//}
	//second := serialNumber % 34
	//firstStr := baseModel.NUMBER_MAP[first]
	//secondStr := baseModel.NUMBER_MAP[second]
	//machineCode := product.MachineCode
	//now := time.Now()
	//year := now.Year()
	//month := int(now.Month())
	//day := now.Day()
	//yearStr := baseModel.YEAR_MAP[year]
	//monthStr := baseModel.MONTH_MAP[month]
	//dayStr := baseModel.DAY_MAP[day]
	//serialOrderId := machineCode + yearStr + monthStr + dayStr + firstStr + secondStr + "0" + product.ColorValue
	serialNumber, err := snrDao.GetNextSnr(project.INTSerialOrderSnr)
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("生成工单流水号失败, error:" + err.Error())
	}
	serialOrderId := product.MachineCode + serialNumber + product.ProductType + product.ColorValue
	entity.SerialOrderId = serialOrderId
	err = dao.InsertOne(entity)
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("写入工单数据失败, error:" + err.Error())
	}
	status := model.SerialOrderStatus{
		SerialOrderId: serialOrderId,
		Status:        baseModel.WO_STATUS_UNPLANNED,
	}
	err = statusDao.InsertOne(&status)
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("插入工单状态失败, error:" + err.Error())
	}
	_ = session.Commit()
	return nil
}

/******************************************************************************
 *
 * @Reference LAPP_ACURA_MOM_BACKEND/services/om/SerialOrderService.DeleteOne
 *
 ******************************************************************************/
func (impl *SerialOrderServiceImplement) DeleteOne(user *global.User, serialOrderId string) error {

	grmi.Log(user, "/services/om/implments/SerialOrder.service.impl.go", "DeleteOneSerialOrder", "删除一个SerialOrder")

	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	dao := dal.NewSerialOrderDAO(session, user.PlantNr, user.UserId)
	record, err := dao.SelectOne(serialOrderId)
	if err != nil {
		return err
	}
	if record == nil {
		return grmi.NewBusinessError("不存在指定记录!")
	}
	err = dao.DeleteOne(serialOrderId)
	if err != nil {
		return err
	}
	return nil
}

/******************************************************************************
 *
 * @Reference LAPP_ACURA_MOM_BACKEND/services/om/SerialOrderService.SelectOne
 *
 ******************************************************************************/
func (impl *SerialOrderServiceImplement) SelectOne(user *global.User, serialOrderId string) (*model.SerialOrder, error) {

	grmi.Log(user, "/services/om/implments/SerialOrder.service.impl.go", "SelectOneSerialOrder", "查询一个SerialOrder")

	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	dao := dal.NewSerialOrderDAO(session, user.PlantNr, user.UserId)
	result, err := dao.SelectOne(serialOrderId)
	if err != nil {
		return nil, err
	}
	return result, nil
}

/******************************************************************************
 *
 * @Reference LAPP_ACURA_MOM_BACKEND/services/om/SerialOrderService.UpdateOne
 *
 ******************************************************************************/
func (impl *SerialOrderServiceImplement) UpdateOne(user *global.User, entity *model.SerialOrder) error {

	grmi.Log(user, "/services/om/implments/SerialOrder.service.impl.go", "UpdateOneSerialOrder", "修改一个SerialOrder")

	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	if entity.OrderType != baseModel.ODER_TYPE_INTERVAL {
		return grmi.NewBusinessError("工单类型不允许修改")
	}
	dao := dal.NewSerialOrderDAO(session, user.PlantNr, user.UserId)
	record, err := dao.SelectOne(entity.SerialOrderId)
	if err != nil {
		return err
	}
	if record == nil {
		return grmi.NewBusinessError("不存在指定记录!")
	}
	if record.OrderType != baseModel.ODER_TYPE_INTERVAL {
		return grmi.NewBusinessError("当前工单不是内部工单，不允许修改, 工单ID:" + entity.SerialOrderId)
	}
	statusDao := dal.NewSerialOrderStatusDAO(session, user.PlantNr, user.UserId)
	serialOrderStatus, err := statusDao.SelectOne(entity.SerialOrderId)
	if err != nil {
		return grmi.NewBusinessError("查询工单状态失败, error:" + err.Error())
	}
	if serialOrderStatus == nil {
		return grmi.NewBusinessError("工单状态不存在, 工单ID:" + entity.SerialOrderId)
	}
	if serialOrderStatus.Status != baseModel.WO_STATUS_UNPLANNED {
		return grmi.NewBusinessError("工单状态不为未计划, 工单ID:" + entity.SerialOrderId)
	}
	err = dao.UpdateOne(entity)
	if err != nil {
		return err
	}

	return nil
}

/******************************************************************************
 *
 * @Reference LAPP_ACURA_MOM_BACKEND/services/om/SerialOrderService.Insert
 *
 ******************************************************************************/
func (impl *SerialOrderServiceImplement) Insert(user *global.User, entities *[]model.SerialOrder) error {

	grmi.Log(user, "/services/om/implments/SerialOrder.service.impl.go", "InsertSerialOrder", "插入多个SerialOrder")

	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	dao := dal.NewSerialOrderDAO(session, user.PlantNr, user.UserId)

	for _, entity := range *entities {
		record, err := dao.SelectOne(entity.SerialOrderId)
		if err != nil {
			return err
		}
		if record != nil {
			return grmi.NewBusinessError("已经存在相同主键的记录!")
		}
	}

	err := dao.Insert(entities)
	if err != nil {
		return err
	}
	return nil
}

/******************************************************************************
 *
 * @Reference LAPP_ACURA_MOM_BACKEND/services/om/SerialOrderService.Delete
 *
 ******************************************************************************/
func (impl *SerialOrderServiceImplement) Delete(user *global.User, entities *[]model.SerialOrder) error {

	grmi.Log(user, "/services/om/implments/SerialOrder.service.impl.go", "DeleteSerialOrder", "删除多个SerialOrder")

	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	dao := dal.NewSerialOrderDAO(session, user.PlantNr, user.UserId)

	for _, entity := range *entities {
		record, err := dao.SelectOne(entity.SerialOrderId)
		if err != nil {
			return err
		}
		if record == nil {
			return grmi.NewBusinessError("不存在指定记录!")
		}
	}

	err := dao.Delete(entities)
	if err != nil {
		return err
	}
	return nil
}

/******************************************************************************
 *
 * @Reference LAPP_ACURA_MOM_BACKEND/services/om/SerialOrderService.Select
 *
 ******************************************************************************/
func (impl *SerialOrderServiceImplement) Select(user *global.User, urlParameters map[string]string) ([]model.SerialOrder, error) {

	grmi.Log(user, "/services/om/implments/SerialOrder.service.impl.go", "SelectSerialOrder", "查询SerialOrder")

	condition := DefaultConditionOfSerialOrderAndPaging
	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	predicates, err := condition.BuildPredicates(urlParameters)
	if err != nil {
		return nil, err
	}
	dao := dal.NewSerialOrderDAO(session, user.PlantNr, user.UserId)
	result, err := dao.Select(predicates, condition.OrderByFields)
	if err != nil {
		return nil, err
	}
	return result, nil
}

/******************************************************************************
 *
 * @Reference LAPP_ACURA_MOM_BACKEND/services/om/SerialOrderService.SelectAndPaging
 *
 ******************************************************************************/
func (impl *SerialOrderServiceImplement) SelectAndPaging(user *global.User, urlParameters map[string]string) (grmi.PagingResult, error) {

	grmi.Log(user, "/services/om/implments/SerialOrder.service.impl.go", "SelectSerialOrder", "查询SerialOrder")

	condition := DefaultConditionOfSerialOrderAndPaging
	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	predicates, err := condition.BuildPredicates(urlParameters)
	if err != nil {
		return grmi.EmptyPagingResult, err
	}
	dao := dal.NewSerialOrderDAO(session, user.PlantNr, user.UserId)
	condition.Fill(urlParameters)
	result, err := dao.SelectAndPaging(condition.Paging, predicates, condition.OrderByFields)
	if err != nil {
		return grmi.EmptyPagingResult, err
	}
	return result, nil
}

/******************************************************************************
 *
 * @Reference LAPP_ACURA_MOM_BACKEND/services/om/SerialOrderService.Update
 *
 ******************************************************************************/
func (impl *SerialOrderServiceImplement) Update(user *global.User, entities *[]model.SerialOrder) error {

	grmi.Log(user, "/services/om/implments/SerialOrder.service.impl.go", "UpdateSerialOrder", "修改多个SerialOrder")

	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	dao := dal.NewSerialOrderDAO(session, user.PlantNr, user.UserId)

	for _, entity := range *entities {
		record, err := dao.SelectOne(entity.SerialOrderId)
		if err != nil {
			return err
		}
		if record == nil {
			return grmi.NewBusinessError("不存在指定记录!")
		}
	}

	err := dao.Update(entities)
	if err != nil {
		return err
	}
	return nil
}

/******************************************************************************
 *
 * @Reference LAPP_ACURA_MOM_BACKEND/services/om/SerialOrderService.CancelSerialOrder
 *
 ******************************************************************************/
func (impl *SerialOrderServiceImplement) CancelSerialOrder(user *global.User, serialOrderId string) error {

	grmi.Log(user, "/services/om/implments/SerialOrder.service.impl.go", "CancelSerialOrder", "取消一个SerialOrder")

	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	dao := dal.NewSerialOrderDAO(session, user.PlantNr, user.UserId)
	record, err := dao.SelectOne(serialOrderId)
	if err != nil {
		return err
	}
	if record == nil {
		return grmi.NewBusinessError("不存在指定记录!")
	}
	if record.OrderType != baseModel.ODER_TYPE_INTERVAL {
		return grmi.NewBusinessError("工单不是内部工单，不允许取消, 工单ID:" + serialOrderId)
	}
	statusDao := dal.NewSerialOrderStatusDAO(session, user.PlantNr, user.UserId)
	status, err := statusDao.SelectOne(serialOrderId)
	if err != nil {
		return grmi.NewBusinessError("查询工单状态数据失败, error:" + err.Error())
	}
	if status == nil {
		return grmi.NewBusinessError("工单状态数据不存在, 工单ID:" + serialOrderId)
	}
	originStatus := status.Status
	if status.Status != baseModel.WO_STATUS_UNPLANNED && status.Status != baseModel.WO_STATUS_RELEASED {
		return grmi.NewBusinessError("工单状态不是未计划, 不允许取消, 工单ID：" + serialOrderId)
	}
	preSerialOrderLi, err := dao.Select([]grmi.Predicate{
		meta.SerialOrder_OrderType.NewPredicate(grmi.Equal, baseModel.ORDER_TYPE_PRE),
		meta.SerialOrder_GenerateFlag.NewPredicate(grmi.Equal, 0),
		meta.SerialOrder_MainOrderId.NewPredicate(grmi.Equal, serialOrderId),
	}, nil)
	if err != nil {
		return grmi.NewBusinessError("查询工单下的预装工单失败, 错误：" + err.Error())
	}
	for _, preOrder := range preSerialOrderLi {
		if preOrder.SerialOrderStatus.Status > baseModel.WO_STATUS_RELEASED {
			return grmi.NewBusinessError("预装工单已经不允许取消, 工单ID:" + preOrder.SerialOrderId)
		}
	}
	if err := session.Begin(); err != nil {
		return err
	}
	statusRecDao := dal.NewSerialOrderStatusRecLstDAO(session, user.PlantNr, user.UserId)
	serialOrderPOLstDao := dal.NewSerialOrderPOLstDAO(session, user.PlantNr, user.UserId)
	serialOrderStepDao := dal.NewSerialOrderStepLstDAO(session, user.PlantNr, user.UserId)
	serialOrderOPDetailDao := dal.NewSerialOrderOPDetailDAO(session, user.PlantNr, user.UserId)
	statusRec := model.SerialOrderStatusRecLst{
		SerialOrderId: serialOrderId,
		Status:        baseModel.WO_STATUS_CANCELED,
		PrevStatus:    status.Status,
	}
	status.Status = baseModel.WO_STATUS_CANCELED
	err = statusRecDao.InsertOne(&statusRec)
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("插入工单状态变更记录失败, error:" + err.Error())
	}
	err = statusDao.UpdateOne(status)
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("更新工单状态失败， error:" + err.Error())
	}
	if originStatus == baseModel.WO_STATUS_RELEASED {
		poUpdateData := &model.SerialOrderPOLst{
			Status: baseModel.WO_STATUS_CANCELED,
		}
		err = serialOrderPOLstDao.UpdateWhere([]grmi.Predicate{meta.SerialOrderPOLst_SerialOrderId.NewPredicate(grmi.Equal, serialOrderId)}, poUpdateData, meta.SerialOrderPOLst_Status.Name)
		if err != nil {
			_ = session.Rollback()
			return grmi.NewBusinessError("更新工单工序状态失败, error:" + err.Error())
		}
		stepUpdateData := &model.SerialOrderStepLst{
			Status: baseModel.WO_STATUS_CANCELED,
		}
		err = serialOrderStepDao.UpdateWhere([]grmi.Predicate{meta.SerialOrderStepLst_SerialOrderId.NewPredicate(grmi.Equal, serialOrderId)}, stepUpdateData, meta.SerialOrderStepLst_Status.Name)
		if err != nil {
			_ = session.Rollback()
			return grmi.NewBusinessError("更新工单工步状态失败, error:" + err.Error())
		}
		opDetailUpdateData := &model.SerialOrderOPDetail{
			Status: baseModel.WO_STATUS_CANCELED,
		}
		err = serialOrderOPDetailDao.UpdateWhere([]grmi.Predicate{meta.SerialOrderOPDetail_SerialOrderId.NewPredicate(grmi.Equal, serialOrderId)}, opDetailUpdateData, meta.SerialOrderOPDetail_Status.Name)
		if err != nil {
			_ = session.Rollback()
			return grmi.NewBusinessError("更新工单详细操作状态失败, error:" + err.Error())
		}
	}
	// 取消预装工单
	for _, preOrder := range preSerialOrderLi {
		originStatus = preOrder.SerialOrderStatus.Status
		statusRec = model.SerialOrderStatusRecLst{
			SerialOrderId: preOrder.SerialOrderId,
			Status:        baseModel.WO_STATUS_CANCELED,
			PrevStatus:    preOrder.SerialOrderStatus.Status,
		}
		preOrder.SerialOrderStatus.Status = baseModel.WO_STATUS_CANCELED
		err = statusRecDao.InsertOne(&statusRec)
		if err != nil {
			_ = session.Rollback()
			return grmi.NewBusinessError("插入预装工单状态变更记录失败, error:" + err.Error())
		}
		err = statusDao.UpdateOne(&preOrder.SerialOrderStatus)
		if err != nil {
			_ = session.Rollback()
			return grmi.NewBusinessError("更新预装工单状态失败， error:" + err.Error())
		}
		if originStatus == baseModel.WO_STATUS_RELEASED {
			poUpdateData := &model.SerialOrderPOLst{
				Status: baseModel.WO_STATUS_CANCELED,
			}
			err = serialOrderPOLstDao.UpdateWhere([]grmi.Predicate{meta.SerialOrderPOLst_SerialOrderId.NewPredicate(grmi.Equal, preOrder.SerialOrderId)}, poUpdateData, meta.SerialOrderPOLst_Status.Name)
			if err != nil {
				_ = session.Rollback()
				return grmi.NewBusinessError("更新预装工单工序状态失败, error:" + err.Error())
			}
			stepUpdateData := &model.SerialOrderStepLst{
				Status: baseModel.WO_STATUS_CANCELED,
			}
			err = serialOrderStepDao.UpdateWhere([]grmi.Predicate{meta.SerialOrderStepLst_SerialOrderId.NewPredicate(grmi.Equal, preOrder.SerialOrderId)}, stepUpdateData, meta.SerialOrderStepLst_Status.Name)
			if err != nil {
				_ = session.Rollback()
				return grmi.NewBusinessError("更新预装工单工步状态失败, error:" + err.Error())
			}
			opDetailUpdateData := &model.SerialOrderOPDetail{
				Status: baseModel.WO_STATUS_CANCELED,
			}
			err = serialOrderOPDetailDao.UpdateWhere([]grmi.Predicate{meta.SerialOrderOPDetail_SerialOrderId.NewPredicate(grmi.Equal, preOrder.SerialOrderId)}, opDetailUpdateData, meta.SerialOrderOPDetail_Status.Name)
			if err != nil {
				_ = session.Rollback()
				return grmi.NewBusinessError("更新预装工单详细操作状态失败, error:" + err.Error())
			}
		}
	}
	_ = session.Commit()
	return nil
}

// ReleaseSerialOrder 派工已锁定的内部订单
func (impl *SerialOrderServiceImplement) ReleaseSerialOrder(user *global.User, serialOrderId string) error {
	grmi.Log(user, "/services/om/implments/WorkOrder.service.impl.go", "ReleaseSerialOrder", "派工已锁定的内部订单")
	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	if err := session.Begin(); err != nil {
		return err
	}
	serialOrderDao := dal.NewSerialOrderDAO(session, user.PlantNr, user.UserId)
	serialOrderStatusDao := dal.NewSerialOrderStatusDAO(session, user.PlantNr, user.UserId)
	statusHistoryDao := dal.NewSerialOrderStatusRecLstDAO(session, user.PlantNr, user.UserId)
	workLineProcessDao := baseDal.NewWorkLineProcessLstDAO(session, user.PlantNr, user.UserId)
	projectDao := meDal.NewProjectDAO(session, user.PlantNr, user.UserId)
	operationDao := baseDal.NewOperationDAO(session, user.PlantNr, user.UserId)
	PODao := dal.NewSerialOrderPOLstDAO(session, user.PlantNr, user.UserId)
	stepDao := dal.NewSerialOrderStepLstDAO(session, user.PlantNr, user.UserId)
	articleWithAttrDao := baseDal.NewArticleAtcodLstDAO(session, user.PlantNr, user.UserId)
	scanRuleDao := baseDal.NewScanRuleInfoDAO(session, user.PlantNr, user.UserId)
	guideDao := baseDal.NewGuideRuleInfoDAO(session, user.PlantNr, user.UserId)
	printRuleDao := baseDal.NewPrintBasicRuleInfoDAO(session, user.PlantNr, user.UserId)
	labelTemplateDetailDao := baseDal.NewLabelTemplateDetailDAO(session, user.PlantNr, user.UserId)
	articleDao := baseDal.NewArticleDAO(session, user.PlantNr, user.UserId)
	bomHeadDao := meDal.NewBomHeadDAO(session, user.PlantNr, user.UserId)
	bomLstDao := meDal.NewBomLstDAO(session, user.PlantNr, user.UserId)
	productDao := meDal.NewProductDAO(session, user.PlantNr, user.UserId)
	shipableAUConfigDao := meDal.NewShipableAUConfigDAO(session, user.PlantNr, user.UserId)
	snrDao := baseDal.NewSnrDAO(session, user.PlantNr, user.UserId)
	today := time.Now().Format("060102")
	// 组装BOM切片 获取关联的所有零件ID
	serialOrder, err := serialOrderDao.SelectOne(serialOrderId)
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("查询工单失败, error:" + err.Error())
	}
	if serialOrder == nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("不存在对应的工单, 工单ID:" + serialOrderId)
	}
	if serialOrder.OrderType != baseModel.ODER_TYPE_INTERVAL {
		_ = session.Rollback()
		return grmi.NewBusinessError("改工单不是内部工单, 工单ID:" + serialOrderId)
	}
	serialOrderStatus, err := serialOrderStatusDao.SelectOne(serialOrderId)
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("查询工单状态数据失败, error:" + err.Error())
	}
	if serialOrderStatus == nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("不存在工单状态数据, 工单ID:" + serialOrderId)
	}
	if serialOrderStatus.Status != baseModel.WO_STATUS_LOCKED {
		_ = session.Rollback()
		return grmi.NewBusinessError("工单状态不是已锁定")
	}
	serialOrderStatus.Status = baseModel.WO_STATUS_RELEASED
	err = serialOrderStatusDao.UpdateOne(serialOrderStatus)
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("修改工单状态失败, error:" + err.Error())
	}
	statusHistory := model.SerialOrderStatusRecLst{
		SerialOrderId: serialOrderId,
		PrevStatus:    baseModel.WO_STATUS_LOCKED,
		Status:        baseModel.WO_STATUS_RELEASED,
	}
	err = statusHistoryDao.InsertOne(&statusHistory)
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("写入工单状态变更历史失败, error:" + err.Error())
	}
	planDate := serialOrder.PlanStartDate.Restore().Format(grmi.DateOutFormat)
	article, err := articleDao.SelectOne(serialOrder.ArtId, serialOrder.ProjectId)
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("查询工单总成失败, error:" + err.Error())
	}
	if article == nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("工单总成不存在, 总成ID:" + serialOrder.ArtId)
	}
	product, err := productDao.SelectOne(serialOrder.ArtId, serialOrder.ProjectId)
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("查询产品总成数据失败, error:" + err.Error())
	}
	if product == nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("产品总成不存在, 总成ID:" + serialOrder.ArtId)
	}
	bomHead, err := bomHeadDao.SelectOne(serialOrder.ArtId)
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("查询物料BOM失败, error:" + err.Error() + " 工单ID: " + serialOrderId)
	}
	if bomHead == nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("不存在对应的物料BOM, 物料ID:" + serialOrder.ArtId + " 工单ID: " + serialOrderId)
	}
	var Me meModel.BomLst
	Me.PlantNr = user.PlantNr
	Me.BomId = bomHead.BomId
	bomList, err := Me.SetMenu(bomHead.MatNr)
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("获取BOM细则失败, error:" + err.Error() + " 工单ID: " + serialOrderId)
	}
	if len(bomList) == 0 {
		_ = session.Rollback()
		return grmi.NewBusinessError("未获取到BOM细则, 物料ID:" + bomHead.BomId + " 工单ID: " + serialOrderId)
	}
	bomHead.BomLstLi = bomList
	// 获取关联的所有零件ID
	artMap := impl.ExtractArtId(bomHead)
	artIdLi := make([]string, 0, len(artMap))
	for innerArtId := range artMap {
		artIdLi = append(artIdLi, innerArtId)
	}
	artIdLi = append(artIdLi, serialOrder.ArtId)
	allArticleAttributeLi, err := articleWithAttrDao.SelectWithArticleByArtIdLi(artIdLi, serialOrder.ProjectId)
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("查询半成品关联所有属性失败, 错误:" + err.Error())
	}
	// 获取项目数据 用于获取流水号
	project, err := projectDao.SelectOne(serialOrder.ProjectId)
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("获取项目数据失败, error:" + err.Error() + " 工单ID: " + serialOrderId)
	}
	if project == nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("不存在该项目数据, 项目ID: " + serialOrder.ProjectId + " 工单ID: " + serialOrderId)
	}
	// 查询所有使用到的工序
	usedPoLi, err := workLineProcessDao.SelectProcessByWorkLine(serialOrder.PlanResourceId)
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("获取产线关联的工序失败, error:" + err.Error())
	}
	if len(usedPoLi) == 0 {
		_ = session.Rollback()
		return grmi.NewBusinessError("该产线未关联工序, 产线ID:" + serialOrder.PlanResourceId)
	}

	// 拆分预装子工单
	// 1. 将所有工序数据生成MAP
	// 2. 从后往前查 如果有shipableAU标识 代表需要生成工单 需要查询shipableAUConfig数据表中该po的配置
	// 3. 生成工单有两种规则 一种是总装线 主工单 一种是预装线 子工单
	bomLstLi, err := bomLstDao.Select([]grmi.Predicate{
		meMeta.BomLst_BomId.NewPredicate(grmi.Equal, serialOrder.ArtId),
		meMeta.BomLst_ShipableAU.NewPredicate(grmi.Equal, true),
	}, nil)
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("查询BOM子项数据失败, 错误:" + err.Error())
	}
	poDataMap := make(map[string]baseModel.Process)
	allPoIdInterfaceLi := make([]interface{}, 0, len(usedPoLi))
	for _, po := range usedPoLi {
		poDataMap[po.PO] = po
		allPoIdInterfaceLi = append(allPoIdInterfaceLi, po.PO)
	}
	sfpUsedPoMap := make(map[string]interface{})
	orderPoMap := make(map[string]*model.OrderAUPoData)
	for _, bomLst := range bomLstLi {
		sfp, err := articleDao.SelectOne(bomLst.CmatNr, serialOrder.ProjectId)
		if err != nil {
			_ = session.Rollback()
			return grmi.NewBusinessError("查询物料失败, 错误:" + err.Error())
		}
		if sfp == nil {
			_ = session.Rollback()
			return grmi.NewBusinessError("半成品物料不存在, 物料ID:" + bomLst.CmatNr)
		}
		auConfig, err := shipableAUConfigDao.SelectOne(bomLst.CmatNr)
		if err != nil {
			_ = session.Rollback()
			return grmi.NewBusinessError("查询半成品配置数据失败, 错误:" + err.Error())
		}
		if auConfig == nil {
			_ = session.Rollback()
			return grmi.NewBusinessError("半成品配置不存在， 物料ID:" + bomLst.CmatNr)
		}

		sfpBomHead, err := bomHeadDao.SelectOne(bomLst.CmatNr)
		if err != nil {
			_ = session.Rollback()
			return grmi.NewBusinessError("查询物料BOM失败, error:" + err.Error())
		}
		if sfpBomHead == nil {
			_ = session.Rollback()
			return grmi.NewBusinessError("不存在对应的物料BOM, 物料ID:" + bomLst.CmatNr)
		}
		var sfpMe meModel.BomLst
		sfpMe.PlantNr = user.PlantNr
		sfpMe.BomId = sfpBomHead.BomId
		sfpBomLstLi, err := sfpMe.SetMenu(sfpBomHead.MatNr)
		if err != nil {
			_ = session.Rollback()
			return grmi.NewBusinessError("获取BOM细则失败, error:" + err.Error())
		}
		if len(sfpBomLstLi) == 0 {
			_ = session.Rollback()
			return grmi.NewBusinessError("未获取到BOM细则, 物料ID:" + bomHead.BomId)
		}
		sfpBomHead.BomLstLi = sfpBomLstLi
		// 获取关联的所有零件ID
		sfpArtMap := impl.ExtractArtId(sfpBomHead)
		sfpArtIdLi := make([]string, 0, len(sfpArtMap))
		for innerArtId := range sfpArtMap {
			sfpArtIdLi = append(sfpArtIdLi, innerArtId)
		}
		sfpArtIdLi = append(sfpArtIdLi, sfp.ArtId)
		sfpAllArticleAttributeLi, err := articleWithAttrDao.SelectWithArticleByArtIdLi(sfpArtIdLi, serialOrder.ProjectId)
		if err != nil {
			_ = session.Rollback()
			return grmi.NewBusinessError("查询半成品关联所有属性失败, 错误:" + err.Error())
		}
		auPoData, exist := poDataMap[auConfig.AuPo]
		if !exist {
			_ = session.Rollback()
			return grmi.NewBusinessError("半成品指定工序不存在于总工单序列中, 半成品ID:" + sfp.ArtId)
		}
		// 根据该工序查询所有后序
		poDataLi := impl.GetOrderPOLi(auPoData, poDataMap)
		// 该工单用到的所有生产工序
		poDataLi = append(poDataLi, auPoData)
		// 查询这个工单需要用到的所有工艺数据
		poIdLi := make([]string, 0, len(poDataLi))
		for _, innerPoData := range poDataLi {
			sfpUsedPoMap[innerPoData.PO] = nil
			poIdLi = append(poIdLi, innerPoData.PO)
		}
		opLi, err := operationDao.GetMeOperation(sfpArtIdLi, serialOrder.ProjectId, poIdLi, serialOrder.UsedResourceId)
		if err != nil {
			_ = session.Rollback()
			return grmi.NewBusinessError("获取工艺操作失败,error:" + err.Error())
		}
		if len(opLi) == 0 {
			_ = session.Rollback()
			return grmi.NewBusinessError("未获取到工艺数据")
		}
		// 所有需要生成工单工序绑定的数据
		orderPoMap[bomLst.CmatNr] = &model.OrderAUPoData{
			Config:         auConfig,
			MeOperationLi:  opLi,
			IsMainOrder:    false,
			SFP:            sfp,
			AllAttributeLi: sfpAllArticleAttributeLi,
			ArtRelMap:      sfpArtMap,
		}
	}
	mainOrderPOIdLi := make([]string, 0)
	for _, usedPoData := range usedPoLi {
		_, exist := sfpUsedPoMap[usedPoData.PO]
		if !exist {
			mainOrderPOIdLi = append(mainOrderPOIdLi, usedPoData.PO)
		}
	}
	if len(mainOrderPOIdLi) == 0 {
		_ = session.Rollback()
		return grmi.NewBusinessError("主工单无生产所需工序")
	}
	opLi, err := operationDao.GetMeOperation(artIdLi, serialOrder.ProjectId, mainOrderPOIdLi, serialOrder.UsedResourceId)
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("获取工艺操作失败,error:" + err.Error())
	}
	if len(opLi) == 0 {
		_ = session.Rollback()
		return grmi.NewBusinessError("未获取到工艺数据")
	}
	// 所有需要生成工单工序绑定的数据
	orderPoMap[serialOrder.ArtId] = &model.OrderAUPoData{
		Config:         nil,
		MeOperationLi:  opLi,
		IsMainOrder:    true,
		SFP:            nil,
		AllAttributeLi: allArticleAttributeLi,
		ArtRelMap:      artMap,
	}

	// 获取所有使用工序的扫码校验规则 组建MAP数据结构
	scanRuleLi, err := scanRuleDao.Select([]grmi.Predicate{baseMeta.ScanRuleInfo_ProjectId.NewPredicate(grmi.Equal, serialOrder.ProjectId), baseMeta.ScanRuleInfo_PO.NewPredicate(grmi.Include, allPoIdInterfaceLi...)}, nil)
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("查询扫码校验条码失败, error:" + err.Error() + " 工单ID: " + serialOrderId)
	}
	scanRuleMap := make(map[int]baseModel.ScanRuleInfo)
	for _, scanRule := range scanRuleLi {
		scanRuleMap[scanRule.OperationId] = scanRule
	}
	// 获取所有使用工序指导操作的规则 组建MAP数据结构
	guideRuleLi, err := guideDao.Select([]grmi.Predicate{baseMeta.GuideRuleInfo_ProjectId.NewPredicate(grmi.Equal, serialOrder.ProjectId), baseMeta.GuideRuleInfo_PO.NewPredicate(grmi.Include, allPoIdInterfaceLi...)}, nil)
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("查询指导操作规则失败, error:" + err.Error() + " 工单ID: " + serialOrderId)
	}
	guideRuleMap := make(map[int]baseModel.GuideRuleInfo, len(guideRuleLi))
	for _, guideRule := range guideRuleLi {
		guideRuleMap[guideRule.OperationId] = guideRule
	}
	// 获取所有使用工序扫码校验规则 组建MAP数据结构
	printRuleLi, err := printRuleDao.Select([]grmi.Predicate{baseMeta.PrintBasicRuleInfo_ProjectId.NewPredicate(grmi.Equal, serialOrder.ProjectId), baseMeta.PrintBasicRuleInfo_PO.NewPredicate(grmi.Include, allPoIdInterfaceLi...)}, []grmi.Field{baseMeta.PrintBasicRuleInfo_OperationId})
	printRuleMap := make(map[int][]baseModel.PrintBasicRuleInfo)
	for _, data := range printRuleLi {
		_, exist := printRuleMap[data.OperationId]
		if !exist {
			printRuleMap[data.OperationId] = make([]baseModel.PrintBasicRuleInfo, 0)
		}
		printRuleMap[data.OperationId] = append(printRuleMap[data.OperationId], data)
	}
	// 获取所有物料属性关联关系
	attributeLi, err := articleWithAttrDao.SelectAllWithAttrValue()
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("查询零件属性失败,error:" + err.Error())
	}
	attributeMap := make(map[int][]baseModel.ArticleWithAttrValue)
	for _, data := range attributeLi {
		_, exist := attributeMap[data.ArticleAtcodLst.AttrCode]
		if !exist {
			attributeMap[data.ArticleAtcodLst.AttrCode] = make([]baseModel.ArticleWithAttrValue, 0)
		}
		attributeMap[data.ArticleAtcodLst.AttrCode] = append(attributeMap[data.ArticleAtcodLst.AttrCode], data)
	}
	// 获取所有的标签打印模板 并组建MAP
	labelTemplateLi, err := labelTemplateDetailDao.SelectAllTemplate()
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("查询标签模板失败,error:" + err.Error())
	}
	labelTemplateMap := make(map[string]*baseModel.LabelTemplateHead)
	for _, template := range labelTemplateLi {
		innerTemplate := template
		labelTemplateMap[template.LabelTemplateId] = &innerTemplate
	}
	// 生成序列工单对应的工艺数据
	allocateMap := make(map[string][]baseModel.ArtQtyAllocate)
	checkRuleMap := make(map[string]baseModel.ArtCheckRule)
	opcBasicMap := make(map[int][]baseModel.OPCBasicComInfo)
	twMap := make(map[int][]baseModel.TightenWrenchInfo)
	for _, orderOPData := range orderPoMap {
		var innerSerialOrderId string
		var innerArticle *baseModel.Article
		if !orderOPData.IsMainOrder {
			innerArticle = orderOPData.SFP
			if orderOPData.Config.BarCodeRule == baseModel.BARCODE_RULE_ACURA_PRE {
				number, err := snrDao.GetNextSnr(project.PreOrderSnr)
				if err != nil {
					_ = session.Rollback()
					return grmi.NewBusinessError("获取流水号失败，错误:" + err.Error())
				}
				innerSerialOrderId = strings.Replace(orderOPData.SFP.ArtId, "-", "", -1) + today + number
			} else {
				_ = session.Rollback()
				return grmi.NewBusinessError("不支持的子工单条码生成规则，物料Id:" + orderOPData.SFP.ArtId)
			}
			sfpSerialOrder := model.SerialOrder{
				SerialOrderId:    innerSerialOrderId,
				ArtId:            orderOPData.SFP.ArtId,
				ProjectId:        serialOrder.ProjectId,
				Descr:            orderOPData.SFP.Descr1,
				CustOrderId:      serialOrder.CustOrderId,
				LastPieceInBatch: serialOrder.LastPieceInBatch,
				PreSchedKey:      serialOrder.SchedKey,
				SchedKey:         serialOrder.SchedKey,
				OrderType:        baseModel.ORDER_TYPE_PRE,
				OrderType1:       serialOrder.OrderType1,
				OrderType2:       serialOrder.OrderType2,
				PlanResourceId:   serialOrder.PlanResourceId,
				UsedResourceId:   serialOrder.UsedResourceId,
				PlanQty:          1,
				QtyUomId:         serialOrder.QtyUomId,
				OpTimePerItem:    serialOrder.OpTimePerItem,
				TimeUomId:        serialOrder.TimeUomId,
				PlanStartDate:    serialOrder.PlanStartDate,
				PlanEndDate:      serialOrder.PlanEndDate,
				SetupStartTime:   serialOrder.SetupStartTime,
				SetupEndTime:     serialOrder.SetupEndTime,
				PlanStartTime:    serialOrder.PlanStartTime,
				PlanEndTime:      serialOrder.PlanEndTime,
				PickingFlag:      orderOPData.SFP.PickingFlag,
				MainOrderId:      serialOrderId,
			}
			err = serialOrderDao.InsertOne(&sfpSerialOrder)
			if err != nil {
				_ = session.Rollback()
				return grmi.NewBusinessError("写入预装工单失败， 错误:" + err.Error())
			}
			sfpSerialOrderStatus := model.SerialOrderStatus{
				SerialOrderId: innerSerialOrderId,
				Status:        baseModel.WO_STATUS_RELEASED,
			}
			err = serialOrderStatusDao.InsertOne(&sfpSerialOrderStatus)
			if err != nil {
				_ = session.Rollback()
				return grmi.NewBusinessError("写入预装工单状态数据失败, 错误:" + err.Error())
			}
		} else {
			innerArticle = article
			innerSerialOrderId = serialOrder.SerialOrderId
		}
		var PO string
		processPos := 1
		opPos := 1
		for _, op := range orderOPData.MeOperationLi {
			// 生成工序操作状态表
			if PO != op.Process.PO {
				PO = op.Process.PO
				serialOrderWorkPlace := model.SerialOrderPOLst{
					SerialOrderId: innerSerialOrderId,
					PO:            PO,
					Status:        baseModel.WO_STATUS_RELEASED,
					HasDependOn:   op.Process.HasDependOn,
				}
				err = PODao.InsertOne(&serialOrderWorkPlace)
				if err != nil {
					_ = session.Rollback()
					return grmi.NewBusinessError("插入serialOrder工序数据失败,error:" + err.Error())
				}
				processPos++
			}
			serialOrderStep := model.SerialOrderStepLst{
				PO:            op.Process.PO,
				OperationId:   op.OperationId,
				SerialOrderId: innerSerialOrderId,
				StepNo:        op.Operation.StepNo,
				Pos:           opPos,
				SubArtId:      op.ArticleAtcodLst.ArtId,
				StepDesc:      op.Operation.StepDesc,
				AttrCode:      op.ArticleAtcodLst.AttrCode,
				AttrValue:     op.ArticleAtcodLst.AttrValue,
				StepType:      op.Operation.StepType,
				Status:        baseModel.WO_STATUS_RELEASED,
			}
			opPos++
			err = stepDao.InsertOne(&serialOrderStep)
			if err != nil {
				_ = session.Rollback()
				return grmi.NewBusinessError("插入序列工单工艺数据失败, error:" + err.Error())
			}
			if serialOrderStep.StepType == baseModel.STEP_CHECK_RULE { // 生成扫码校验的工艺数据
				scanRule, exist := scanRuleMap[serialOrderStep.OperationId]
				if !exist {
					return grmi.NewBusinessError("未获取到扫描校验详细规则, 工艺ID:" + strconv.Itoa(serialOrderStep.OperationId))
				}

				checkRuleMapPointer, allocateMapPointer, err := impl.ScanCodeCheck(user, session, checkRuleMap, allocateMap, orderOPData.ArtRelMap, &scanRule, &op, &serialOrderStep, op.Operation.ActivateInBKStation)
				if err != nil {
					_ = session.Rollback()
					return err
				}
				checkRuleMap = *checkRuleMapPointer
				allocateMap = *allocateMapPointer
			} else if serialOrderStep.StepType == baseModel.STEP_OPC_RULE { // 生成OPC通讯的工艺数据
				opcBasicMapPointer, err := impl.OPCCommunication(user, session, opcBasicMap, &serialOrderStep, attributeMap, artMap, op.Operation.ActivateInBKStation)
				if err != nil {
					_ = session.Rollback()
					return err
				}
				opcBasicMap = *opcBasicMapPointer
			} else if serialOrderStep.StepType == baseModel.STEP_TW_RULE { // 生成开发协议的工艺数据
				twMapPointer, err := impl.TightenWrenchInfo(user, session, twMap, &serialOrderStep, op.Operation.ActivateInBKStation, innerArticle)
				if err != nil {
					_ = session.Rollback()
					return err
				}
				twMap = *twMapPointer
			} else if serialOrderStep.StepType == baseModel.STEP_GUIDE_RULE {
				guideRule, exist := guideRuleMap[serialOrderStep.OperationId]
				if !exist {
					_ = session.Rollback()
					return grmi.NewBusinessError("未获取到指导操作的详细规则")
				}
				err = impl.GuideRuleInfo(user, session, &guideRule, &serialOrderStep, op.Operation.ActivateInBKStation)
				if err != nil {
					_ = session.Rollback()
					return err
				}
			} else if serialOrderStep.StepType == baseModel.STEP_PRINT_RULE {
				ruleLi, exist := printRuleMap[serialOrderStep.OperationId]
				if !exist {
					_ = session.Rollback()
					return grmi.NewBusinessError("未获取到打印规则, 工艺ID:" + strconv.Itoa(serialOrderStep.OperationId))
				}
				generateRuleLi := make([]baseModel.PrintBasicRuleInfo, 0)
				for _, rule := range ruleLi {
					if rule.Action == baseModel.PRINT_RULE_ACTION_UPDATE {
						generateRuleLi = append(generateRuleLi, rule)
					}
				}
				if len(generateRuleLi) > 1 {
					_ = session.Rollback()
					return grmi.NewBusinessError("改工步匹配到超过1条生成打印标识的打印规则")
				}
				var generateRule *baseModel.PrintBasicRuleInfo
				var printTemplate *baseModel.LabelTemplateHead
				if len(generateRuleLi) == 1 {
					generateRule = &generateRuleLi[0]
					var usedTemplateId string
					if generateRule.MainCodeName == "Product" {
						usedTemplateId = product.PrintTemplateId
					} else {
						usedTemplateId = generateRule.TemplateId
					}
					printTemplate, exist = labelTemplateMap[usedTemplateId]
					if !exist {
						_ = session.Rollback()
						return grmi.NewBusinessError("存在单据打印规则中配置的标签模板, 模板ID:" + generateRule.TemplateId)
					}
				}
				var printUsedArticle *baseModel.Article
				if op.Operation.AttrCode == -1 {
					printUsedArticle = innerArticle
				} else {
					for _, articleAttribute := range orderOPData.AllAttributeLi {
						if op.Operation.AttrCode == articleAttribute.AttrCode && op.Operation.AttrValue == articleAttribute.AttrValue {
							printUsedArticle = &articleAttribute.Article
							break
						}
					}
				}
				if printUsedArticle == nil {
					_ = session.Rollback()
					return grmi.NewBusinessError("单据打印规则没有获取到需要的物料数据, 工艺Id:" + strconv.Itoa(op.Operation.OperationId))
				}
				err = impl.PrintRuleInfo(user, session, &serialOrderStep, generateRule, printTemplate, printUsedArticle, op.Operation.ActivateInBKStation, planDate)
				if err != nil {
					_ = session.Rollback()
					return err
				}
			} else {
				_ = session.Rollback()
				return grmi.NewBusinessError("获取到的工艺数据系统暂不支持该操作类型:" + serialOrderStep.StepType)
			}
		}
	}
	_ = session.Commit()
	return nil
}

// ExtractArtId 从Bom数据结构中提取需要的每种物料数量
func (impl *SerialOrderServiceImplement) ExtractArtId(bomHead *meModel.BomHead) map[string]int {
	artMap := make(map[string]int)
	bomLstLi := bomHead.BomLstLi
	for _, bomLst := range bomLstLi {
		_, exist := artMap[bomLst.CmatNr]
		if exist {
			artMap[bomLst.CmatNr] += int(bomLst.CmatQty / bomLst.FmatQty)
		} else {
			artMap[bomLst.CmatNr] = int(bomLst.CmatQty / bomLst.FmatQty)
		}
	}
	return artMap
}

// ScanCodeCheck 生成扫码校验的工艺数据
func (impl *SerialOrderServiceImplement) ScanCodeCheck(user *global.User, session *xorm.Session, checkRuleMap map[string]baseModel.ArtCheckRule, allocateMap map[string][]baseModel.ArtQtyAllocate, artRelMap map[string]int, scanRule *baseModel.ScanRuleInfo, op *baseModel.MeOperation, serialOrderStep *model.SerialOrderStepLst, bkFlag bool) (resCheckRuleMap *map[string]baseModel.ArtCheckRule, resAllocateMap *map[string][]baseModel.ArtQtyAllocate, err error) {
	ruleDao := baseDal.NewArtCheckRuleDAO(session, user.PlantNr, user.UserId)
	allocateDao := baseDal.NewArtQtyAllocateDAO(session, user.PlantNr, user.UserId)
	serialOrderDetailDao := dal.NewSerialOrderOPDetailDAO(session, user.PlantNr, user.UserId)
	if scanRule.RuleType == baseModel.SCAN_RULE_TYPE_ARTICLE {
		if scanRule.SRCtrlPara1 == baseModel.SCAN_RULE_CHECK {
			// 获取零件校验规则
			rule, exist := checkRuleMap[op.ArticleAtcodLst.ArtId]
			if !exist {
				ruleLi, err := ruleDao.Select([]grmi.Predicate{baseMeta.ArtCheckRule_AttriCode.NewPredicate(grmi.Equal, op.ArticleAtcodLst.AttrCode), baseMeta.ArtCheckRule_SubObject.NewPredicate(grmi.Equal, serialOrderStep.SubArtId)}, nil)
				if err != nil {
					return nil, nil, grmi.NewBusinessError("获取零件检验规则失败, error:" + err.Error())
				}
				if len(ruleLi) == 0 {
					ruleLi, err = ruleDao.Select([]grmi.Predicate{baseMeta.ArtCheckRule_AttriCode.NewPredicate(grmi.Equal, op.ArticleAtcodLst.AttrCode), baseMeta.ArtCheckRule_SubObject.NewPredicate(grmi.Equal, baseModel.MES_MATCH_ALL)}, nil)
					if err != nil {
						return nil, nil, grmi.NewBusinessError("获取零件检验规则失败, error:" + err.Error())
					}
					if len(ruleLi) == 0 {
						return nil, nil, grmi.NewBusinessError("未获取到零件检验规则, artId:" + serialOrderStep.SubArtId)
					}
				}
				rule = ruleLi[0]
				checkRuleMap[op.ArticleAtcodLst.ArtId] = rule
			}
			// 获取零件分配数量
			var num int
			// 零件BOM中两件组成父级零件的数量
			composeQty := artRelMap[serialOrderStep.SubArtId]
			allocateLi, exist := allocateMap[op.ArticleAtcodLst.ArtId]
			if !exist {
				allocateLi, err = allocateDao.Select([]grmi.Predicate{baseMeta.ArtQtyAllocate_AttriCode.NewPredicate(grmi.Equal, op.ArticleAtcodLst.AttrCode), baseMeta.ArtQtyAllocate_TotalPlanty.NewPredicate(grmi.Equal, composeQty)}, nil)
				if err != nil {
					return nil, nil, grmi.NewBusinessError("获取特殊工位数量分配数据失败，错误:" + err.Error())
				}
				allocateMap[op.ArticleAtcodLst.ArtId] = allocateLi
			}
			// 获取特殊工位零件分配数量
			if len(allocateLi) == 0 {
				num = composeQty
			} else {
				var allNum int
				for _, allocate := range allocateLi {
					if allocate.PO == serialOrderStep.PO {
						if allocate.SubObject == baseModel.MES_MATCH_ALL {
							allNum = allocate.AssignPlanty
						} else if allocate.SubObject == serialOrderStep.SubArtId {
							num = allocate.AssignPlanty
						}
					}
				}
				if num == 0 {
					if allNum != 0 {
						num = allNum
					} else {
						num = composeQty
					}
				}
			}
			// 生成零件校验规则的工艺数据
			pos := 1
			for i := 0; i < num; i++ {
				op := model.SerialOrderOPDetail{
					SerialOrderId: serialOrderStep.SerialOrderId,
					OperationId:   serialOrderStep.OperationId,
					PO:            serialOrderStep.PO,
					SubArtId:      serialOrderStep.SubArtId,
					Pos:           pos,
					StepNo:        serialOrderStep.StepNo,
					StepType:      baseModel.STEP_CHECK_RULE,
					Status:        baseModel.WO_STATUS_RELEASED,
					// 扫码校验 AttriCodeDesc
					CharPara1: rule.AttriCodeDesc,
					// 扫码校验 SubObject
					CharPara2: rule.SubObject,
					// 扫码校验 CodeLength
					CharPara3: rule.CodeLength,
					// 扫码校验 CheckObject
					CharPara4: rule.CheckObject,
					// 条码类型
					CharPara5: rule.CodeType,
					// 条码类型校验的期望值
					CharPara6: rule.CheckValue,
					// 扫码校验 CheckStartPos
					IntPara1: rule.CheckStartPos,
					// 扫码校验 CheckLength
					IntPara2: rule.CheckLength,
					// 扫码校验 OnlyBarCodeFlag
					FlagPara1: rule.OnlyBarCodeFlag,
					// 条码类型的校验开关
					FlagPara2: rule.CodeTypeCheckToggle,
					BKFlag:    bkFlag,
				}
				pos++
				err = serialOrderDetailDao.InsertOne(&op)
				if err != nil {
					return nil, nil, grmi.NewBusinessError("插入操作明细记录失败，error:" + err.Error())
				}
			}
			return &checkRuleMap, &allocateMap, nil
		} else if scanRule.SRCtrlPara1 == baseModel.SCAN_RULE_CRWAL {
			op := model.SerialOrderOPDetail{
				SerialOrderId: serialOrderStep.SerialOrderId,
				OperationId:   serialOrderStep.OperationId,
				PO:            serialOrderStep.PO,
				SubArtId:      serialOrderStep.SubArtId,
				Pos:           1,
				StepNo:        serialOrderStep.StepNo,
				StepType:      baseModel.STEP_CHECK_RULE,
				Status:        baseModel.WO_STATUS_LOCKED,
				IntPara5:      -1,
			}
			err = serialOrderDetailDao.InsertOne(&op)
			if err != nil {
				return nil, nil, grmi.NewBusinessError("插入扫码校验操作明细记录失败，error:" + err.Error())
			} else {
				return &checkRuleMap, &allocateMap, nil
			}
		} else {
			return nil, nil, grmi.NewBusinessError("不支持的条码校验类型")
		}

	} else if scanRule.RuleType == baseModel.SCAN_RULE_TYPE_OTHER {
		//if scanRule.SRCtrlPara1 == baseModel.SCAN_RULE_SERIALORDER {
		op := model.SerialOrderOPDetail{
			SerialOrderId: serialOrderStep.SerialOrderId,
			OperationId:   serialOrderStep.OperationId,
			PO:            serialOrderStep.PO,
			SubArtId:      serialOrderStep.SubArtId,
			Pos:           1,
			StepNo:        serialOrderStep.StepNo,
			StepType:      baseModel.STEP_CHECK_RULE,
			Status:        baseModel.WO_STATUS_RELEASED,
			// 扫码校验 AttriCodeDesc
			CharPara1: serialOrderStep.SerialOrderId,
		}
		err = serialOrderDetailDao.InsertOne(&op)
		if err != nil {
			return nil, nil, grmi.NewBusinessError("插入扫码校验操作明细记录失败，error:" + err.Error())
		} else {
			return &checkRuleMap, &allocateMap, nil
		}
		//} else {
		//	return nil, nil, grmi.NewBusinessError("不支持的条码校验规则")
		//}
	} else {
		return nil, nil, grmi.NewBusinessError("不支持的条码校验类型")
	}
}

// OPCCommunication 生成OPC通信的工艺数据
func (impl *SerialOrderServiceImplement) OPCCommunication(user *global.User, session *xorm.Session, opcBasicMap map[int][]baseModel.OPCBasicComInfo, serialOrderStep *model.SerialOrderStepLst, attributeMap map[int][]baseModel.ArticleWithAttrValue, subArtMap map[string]int, bkFlag bool) (*map[int][]baseModel.OPCBasicComInfo, error) {
	var err error
	opcBasicDao := baseDal.NewOPCBasicComInfoDAO(session, user.PlantNr, user.UserId)
	opcComRuleDao := baseDal.NewOPCComRuleDAO(session, user.PlantNr, user.UserId)
	opcComDetailDao := baseDal.NewOPCComDetailRuleDAO(session, user.PlantNr, user.UserId)
	serialOrderDetailDao := dal.NewSerialOrderOPDetailDAO(session, user.PlantNr, user.UserId)
	opcBasicLi, exist := opcBasicMap[serialOrderStep.OperationId]
	if !exist {
		opcBasicLi, err = opcBasicDao.Select([]grmi.Predicate{baseMeta.OPCBasicComInfo_OperationId.NewPredicate(grmi.Equal, serialOrderStep.OperationId)}, nil)
		if err != nil {
			return nil, grmi.NewBusinessError("查询OPC基础通信规则失败，error:" + err.Error())
		}
		if len(opcBasicLi) == 0 {
			return nil, grmi.NewBusinessError("未获取到OPC基础通信规则")
		}
		for index, opcBasic := range opcBasicLi {
			opcComLi, err := opcComRuleDao.Select([]grmi.Predicate{baseMeta.OPCComRule_OperationId.NewPredicate(grmi.Equal, opcBasic.OperationId), baseMeta.OPCComRule_WorkPlaceNr.NewPredicate(grmi.Equal, opcBasic.WorkPlaceNr)}, nil)
			if err != nil {
				return nil, grmi.NewBusinessError("查询OPC通信规则失败, error:" + err.Error())
			}
			if len(opcComLi) == 0 {
				return nil, grmi.NewBusinessError("未获取到OPC通信规则")
			}
			opcDetailLi, err := opcComDetailDao.Select([]grmi.Predicate{baseMeta.OPCComDetailRule_OperationId.NewPredicate(grmi.Equal, opcBasic.OperationId), baseMeta.OPCComDetailRule_WorkPlaceNr.NewPredicate(grmi.Equal, opcBasic.WorkPlaceNr)}, nil)
			if err != nil {
				return nil, grmi.NewBusinessError("查询OPC通信详细规则失败, error:" + err.Error())
			}
			for index, opcCom := range opcComLi {
				opcCom.OPCComDetailRuleLi = make([]baseModel.OPCComDetailRule, 0)
				for _, detail := range opcDetailLi {
					if detail.OperationId == opcCom.OperationId && detail.RuleId == opcCom.RuleId {
						opcCom.OPCComDetailRuleLi = append(opcCom.OPCComDetailRuleLi, detail)
					}
				}
				opcComLi[index] = opcCom
			}
			opcBasic.OPCComRuleLi = opcComLi
			opcBasicLi[index] = opcBasic
		}
		opcBasicMap[serialOrderStep.OperationId] = make([]baseModel.OPCBasicComInfo, 0)
		opcBasicMap[serialOrderStep.OperationId] = opcBasicLi
	}
	pos := 1
	for _, opcBasic := range opcBasicLi {
		for _, opc := range opcBasic.OPCComRuleLi {
			var value string
			if opc.DBlockValueType == baseModel.OPC_DBBLOCK_VALUE_TYPE_MAP {
				for _, detail := range opc.OPCComDetailRuleLi {
					if detail.BaseOnType == baseModel.BASE_ON_TYPE_ATTRNAME {
						attrCode, err := strconv.Atoi(detail.BaseOnValue)
						if err != nil {
							return nil, grmi.NewBusinessError("OPC通讯规则映射值转换ATTRCODE失败, error:" + err.Error())
						}
						attrLi, exist := attributeMap[attrCode]
						if !exist {
							return nil, grmi.NewBusinessError("未获取到OPC通信细则中的零件属性描述对应的shortCode, 属性描述:" + detail.BaseOnValue)
						}
						if detail.DataType == baseModel.DATA_TYPE_ATTRCODE {

							value += strconv.Itoa(attrLi[0].AttributeValst.AttrCode)
						} else if detail.DataType == baseModel.DATA_TYPE_SHORTCODE {
							var sign bool
							for _, data := range attrLi {
								_, exist := subArtMap[data.ArticleAtcodLst.ArtId]
								if !exist {
									continue
								} else {
									if sign {
										return nil, grmi.NewBusinessError("OPC通讯规则映射值获取ATTRCODE下的零件多于一个")
									}
									sign = true
									value += data.AttributeValst.ShortCode
								}
							}
							if !sign {
								return nil, grmi.NewBusinessError("未获取到OPC通信细则中的零件属性描述对应的shortCode, 属性描述:" + detail.BaseOnValue)
							}
						} else if detail.DataType == baseModel.DATA_TYPE_ATTRVALUE {
							var sign bool
							for _, data := range attrLi {
								_, exist := subArtMap[data.ArticleAtcodLst.ArtId]
								if !exist {
									continue
								} else {
									if sign {
										return nil, grmi.NewBusinessError("OPC通讯规则映射值获取ATTRCODE下的零件多于一个")
									}
									sign = true
									value += data.AttributeValst.AttrValue
								}
							}
						} else {
							return nil, grmi.NewBusinessError("不支持的OPC通信细则中的DATA TYPE, DATA TYPE:" + detail.DataType)
						}
					} else if detail.BaseOnType == baseModel.BASE_ON_TYPE_ORDERINFO {
						if detail.DataType == baseModel.DATA_TYPE_PARTNO {
							value += serialOrderStep.SubArtId
						} else {
							return nil, grmi.NewBusinessError("不支持的OPC通信细则中的DATA TYPE, DATA TYPE:" + detail.DataType)
						}
					} else if detail.BaseOnType == baseModel.BASE_ON_TYPE_MAINBARCODE {
						if detail.BaseOnValue == baseModel.BASE_ON_VALUE_UNITSN {
							value += serialOrderStep.SerialOrderId
						} else {
							return nil, grmi.NewBusinessError("不支持的OPC通信细则中的BASE ON VALUE, BASE ON VALUE:" + detail.BaseOnValue)
						}
					}
				}
			} else {
				value = opc.DBlockValue
			}
			op := model.SerialOrderOPDetail{
				SerialOrderId: serialOrderStep.SerialOrderId,
				OperationId:   serialOrderStep.OperationId,
				PO:            serialOrderStep.PO,
				SubArtId:      serialOrderStep.SubArtId,
				Pos:           pos,
				StepType:      baseModel.STEP_OPC_RULE,
				Status:        baseModel.WO_STATUS_RELEASED,
				StepNo:        serialOrderStep.StepNo,
				// OPC BRAND NAME
				CharPara1: opcBasic.BrandName,
				// OPC MACHINE TYPE
				CharPara2: opcBasic.MachineType,
				// OPC IP ADDRESS
				CharPara3: opcBasic.IpAddress,
				// OPC ACTION TYPE
				CharPara4: opc.ActionType,
				// OPC DBLOCK ADDRESS
				CharPara5: opc.DBlockAddress,
				// OPC DBLOCK VALUE TYPE
				CharPara6: opc.DBlockValueType,
				CharPara7: opc.Formula,
				CharPara8: opc.OperatorSimbol,
				CharPara9: value,
				// OPC WorkPlaceNr
				IntPara1: opcBasic.WorkPlaceNr,
				IntPara5: opc.RuleId,
				BKFlag:   bkFlag,
			}
			pos++
			err = serialOrderDetailDao.InsertOne(&op)
			if err != nil {
				return nil, grmi.NewBusinessError("插入OPC通讯操作明细记录失败，error:" + err.Error())
			}
		}
	}
	return &opcBasicMap, nil
}

// TightenWrenchInfo 生成开放协议的工艺数据
func (impl *SerialOrderServiceImplement) TightenWrenchInfo(user *global.User, session *xorm.Session, twMap map[int][]baseModel.TightenWrenchInfo, serialOrderStep *model.SerialOrderStepLst, bkFlag bool, article *baseModel.Article) (*map[int][]baseModel.TightenWrenchInfo, error) {
	var err error
	twDao := baseDal.NewTightenWrenchInfoDAO(session, user.PlantNr, user.UserId)
	twrDao := baseDal.NewTightenWrenchRuleInfoDAO(session, user.PlantNr, user.UserId)
	serialOrderDetailDao := dal.NewSerialOrderOPDetailDAO(session, user.PlantNr, user.UserId)
	twLi, exist := twMap[serialOrderStep.OperationId]
	if !exist {
		twLi, err = twDao.Select([]grmi.Predicate{baseMeta.TightenWrenchInfo_OperationId.NewPredicate(grmi.Equal, serialOrderStep.OperationId)}, nil)
		if err != nil {
			return nil, grmi.NewBusinessError("查询开放协议通信规则失败，error:" + err.Error())
		}
		if len(twLi) == 0 {
			return nil, grmi.NewBusinessError("未获取到开放协议通信规则, 工艺ID:" + strconv.Itoa(serialOrderStep.OperationId))
		}
		for index, tw := range twLi {
			twrLi, err := twrDao.Select([]grmi.Predicate{baseMeta.TightenWrenchRuleInfo_OperationId.NewPredicate(grmi.Equal, tw.OperationId), baseMeta.TightenWrenchRuleInfo_WorkPlaceNr.NewPredicate(grmi.Equal, tw.WorkPlaceNr)}, nil)
			if err != nil {
				return nil, grmi.NewBusinessError("查询开放协议通信细则失败, error:" + err.Error())
			}
			if len(twrLi) == 0 {
				return nil, grmi.NewBusinessError("未获取到开放协议通信细则, 工艺ID:" + strconv.Itoa(serialOrderStep.OperationId))
			}
			tw.TightenWrenchRuleInfoLi = twrLi
			twLi[index] = tw
		}
		twMap[serialOrderStep.OperationId] = twLi
	}
	pos := 1
	for _, tw := range twLi {
		for _, twr := range tw.TightenWrenchRuleInfoLi {
			controlId := serialOrderStep.SerialOrderId
			if twr.MainControlAction == baseModel.CONTROL_ACTION_DOWNLOAD && twr.ControlId == baseModel.CONTROL_ID_PREFIX {
				controlId = article.Prefix + serialOrderStep.SerialOrderId
			}
			for i := 0; i < twr.ControlTime; i++ {
				op := model.SerialOrderOPDetail{
					SerialOrderId: serialOrderStep.SerialOrderId,
					OperationId:   serialOrderStep.OperationId,
					PO:            serialOrderStep.PO,
					SubArtId:      serialOrderStep.SubArtId,
					Pos:           pos,
					StepType:      baseModel.STEP_TW_RULE,
					Status:        baseModel.WO_STATUS_RELEASED,
					StepNo:        serialOrderStep.StepNo,
					// TightenWrenchInfo BRAND NAME
					CharPara1: tw.BrandName,
					// TightenWrenchInfo 	MACHINE TYPE
					CharPara2: tw.MachineType,
					// TightenWrenchInfo IP ADDRESS
					CharPara3: tw.IpAddress,
					// TightenWrenchInfo MainControlAction
					CharPara4: twr.MainControlAction,
					// TightenWrenchInfo ControlTime
					CharPara5: controlId,
					// TightenWrenchInfo WorkPlaceNr
					IntPara2: tw.WorkPlaceNr,
					IntPara5: twr.RuleId,
					BKFlag:   bkFlag,
				}
				pos++
				err = serialOrderDetailDao.InsertOne(&op)
				if err != nil {
					return nil, grmi.NewBusinessError("插入开放协议操作明细记录失败，error:" + err.Error())
				}
			}
		}
	}
	return &twMap, nil
}

// GuideRuleInfo 生成指导数据
func (impl *SerialOrderServiceImplement) GuideRuleInfo(user *global.User, session *xorm.Session, guideRule *baseModel.GuideRuleInfo, serialOrderStep *model.SerialOrderStepLst, bkFlag bool) error {
	var err error
	serialOrderDetailDao := dal.NewSerialOrderOPDetailDAO(session, user.PlantNr, user.UserId)

	op := model.SerialOrderOPDetail{
		SerialOrderId: serialOrderStep.SerialOrderId,
		OperationId:   serialOrderStep.OperationId,
		PO:            serialOrderStep.PO,
		SubArtId:      serialOrderStep.SubArtId,
		Pos:           1,
		StepType:      baseModel.STEP_GUIDE_RULE,
		Status:        baseModel.WO_STATUS_RELEASED,
		StepNo:        serialOrderStep.StepNo,
		IntPara1:      guideRule.DisplayTime,
		BKFlag:        bkFlag,
	}
	err = serialOrderDetailDao.InsertOne(&op)
	if err != nil {
		return grmi.NewBusinessError("插入指导操作操作明细记录失败，error:" + err.Error())
	}
	return nil
}

// PrintRuleInfo 生成单据打印数据
func (impl *SerialOrderServiceImplement) PrintRuleInfo(user *global.User, session *xorm.Session, serialOrderStep *model.SerialOrderStepLst, rule *baseModel.PrintBasicRuleInfo, printTemplate *baseModel.LabelTemplateHead, article *baseModel.Article, bkFlag bool, planDate string) error {
	var err error
	labelHeadDao := baseDal.NewLabelHeadDAO(session, user.PlantNr, user.UserId)
	labelDetailDao := baseDal.NewLabelDetailDAO(session, user.PlantNr, user.UserId)
	serialOrderDetailDao := dal.NewSerialOrderOPDetailDAO(session, user.PlantNr, user.UserId)
	var qty = 1
	var collectScanBarcode = false
	var labelDetailPos string
	var scanOperationId = 0
	var scanPo string
	if rule != nil {
		qty = rule.Qty
		collectScanBarcode = rule.CollectScanBarcode
		labelDetailPos = rule.LabelDetailPos
		scanOperationId = rule.ScanOperationId
		scanPo = rule.ScanPo

	}
	//  FlagPara1 标识是不是根据扫码更新打印数据 1为需要
	// CharPara2 插入打印模板子项索引 多个用;分割
	// IntPara2 扫码的步骤

	// 生成打印规则
	op := model.SerialOrderOPDetail{
		SerialOrderId: serialOrderStep.SerialOrderId,
		OperationId:   serialOrderStep.OperationId,
		PO:            serialOrderStep.PO,
		SubArtId:      serialOrderStep.SubArtId,
		Pos:           1,
		StepType:      baseModel.STEP_PRINT_RULE,
		Status:        baseModel.WO_STATUS_RELEASED,
		StepNo:        serialOrderStep.StepNo,
		BKFlag:        bkFlag,
		IntPara1:      qty,
		IntPara2:      scanOperationId,
		CharPara1:     scanPo,
		CharPara2:     labelDetailPos,
		FlagPara1:     collectScanBarcode,
	}
	err = serialOrderDetailDao.InsertOne(&op)
	if err != nil {
		return grmi.NewBusinessError("插入单据打印操作明细记录失败，error:" + err.Error())
	}
	// 生成打印模板数据
	if rule != nil {
		labelId := serialOrderStep.SerialOrderId + "_" + serialOrderStep.PO + strconv.Itoa(serialOrderStep.StepNo)
		labelHead := baseModel.LabelHead{
			LabelId:         labelId,
			LabelTemplateId: printTemplate.LabelTemplateId,
			PrinterId:       rule.PrinterId,
			BusinessObjType: "SerialOrderId",
			BusinessObjId:   serialOrderStep.SerialOrderId,
			Status:          -1,
			PrintQty:        1,
			OperationId:     rule.OperationId,
		}
		err = labelHeadDao.InsertOne(&labelHead)
		if err != nil {
			return grmi.NewBusinessError("写入打印标签头部数据失败, error:" + err.Error())
		}
		labelDetailLi := make([]baseModel.LabelDetail, 0, len(printTemplate.LabelTemplateDetail))
		var pos = 1
		for _, templateDetail := range printTemplate.LabelTemplateDetail {
			var labelDetail baseModel.LabelDetail
			switch templateDetail.LabelItemValue {
			case "$Descr2$":
				labelDetail = baseModel.LabelDetail{
					LabelId:       labelId,
					Pos:           pos,
					VariableName:  templateDetail.LabelItemValue,
					VariableValue: article.Descr2,
				}
				pos++
			case "$Descr1$":
				labelDetail = baseModel.LabelDetail{
					LabelId:       labelId,
					Pos:           pos,
					VariableName:  templateDetail.LabelItemValue,
					VariableValue: article.Descr1,
				}
				pos++
			case "$AlternativeArtId$":
				labelDetail = baseModel.LabelDetail{
					LabelId:       labelId,
					Pos:           pos,
					VariableName:  templateDetail.LabelItemValue,
					VariableValue: article.AlternativeArtId,
				}
				pos++
			case "$date$":
				labelDetail = baseModel.LabelDetail{
					LabelId:       labelId,
					Pos:           pos,
					VariableName:  templateDetail.LabelItemValue,
					VariableValue: planDate,
				}
				pos++
			case "$SerialOrderId$":
				labelDetail = baseModel.LabelDetail{
					LabelId:       labelId,
					Pos:           pos,
					VariableName:  templateDetail.LabelItemValue,
					VariableValue: serialOrderStep.SerialOrderId,
				}
				pos++
			case "*$SerialOrderId$*":
				labelDetail = baseModel.LabelDetail{
					LabelId:       labelId,
					Pos:           pos,
					VariableName:  templateDetail.LabelItemValue,
					VariableValue: "*" + serialOrderStep.SerialOrderId + "*",
				}
				pos++
			case "$ArtId$":
				if rule.MainCodeName == "Pre" {
					labelDetail = baseModel.LabelDetail{
						LabelId:       labelId,
						Pos:           pos,
						VariableName:  templateDetail.LabelItemValue,
						VariableValue: article.ArtId,
					}
				} else {
					if len(article.ArtId) < 7 {
						continue
					}
					labelDetail = baseModel.LabelDetail{
						LabelId:       labelId,
						Pos:           pos,
						VariableName:  templateDetail.LabelItemValue,
						VariableValue: article.ArtId[:7],
					}
				}
				pos++
			case "$CustArtId$":
				labelDetail = baseModel.LabelDetail{
					LabelId:       labelId,
					Pos:           pos,
					VariableName:  templateDetail.LabelItemValue,
					VariableValue: article.CustArtId,
				}
				pos++
			case "$OrderId$":
				labelDetail = baseModel.LabelDetail{
					LabelId:       labelId,
					Pos:           pos,
					VariableName:  templateDetail.LabelItemValue,
					VariableValue: serialOrderStep.SerialOrderId,
				}
				pos++
			case "$Barcode$":
				labelDetail = baseModel.LabelDetail{
					LabelId:       labelId,
					Pos:           pos,
					VariableName:  templateDetail.LabelItemValue,
					VariableValue: serialOrderStep.SerialOrderId,
				}
				pos++
			default:
				labelDetail = baseModel.LabelDetail{
					LabelId:       labelId,
					Pos:           pos,
					VariableName:  templateDetail.LabelItemValue,
					VariableValue: templateDetail.LabelItemValue,
				}
				pos++
			}
			labelDetailLi = append(labelDetailLi, labelDetail)
		}
		err = labelDetailDao.Insert(&labelDetailLi)
		if err != nil {
			return grmi.NewBusinessError("写入打印标签项失败, error:" + err.Error())
		}
	}
	return nil
}

// SelectSortOrder 查询排序工单页的已排序的工单
func (impl *SerialOrderServiceImplement) SelectSortOrder(user *global.User, urlParameters map[string]string) (grmi.PagingResult, error) {
	grmi.Log(user, "/services/om/implments/SerialOrder.service.impl.go", "SelectSortOrder", "查询已排序订单")
	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	var err error
	resourceId, exist := urlParameters["resourceId"]
	if !exist {
		return grmi.EmptyPagingResult, grmi.NewBusinessError("未选择查询的产线")
	}
	planDate := urlParameters["planDate"]
	statusStr := urlParameters["status"]
	seqStartStr := urlParameters["seqStart"]
	seqEndStr := urlParameters["seqEnd"]
	custOrderId := urlParameters["custOrderId"]
	serialOrderId := urlParameters["serialOrderId"]
	status, err := strconv.Atoi(statusStr)
	if err != nil {
		status = 0
	}
	var seqStart, seqEnd int
	seqStart, err = strconv.Atoi(seqStartStr)
	if err != nil {
		seqStart = 0
	}
	seqEnd, err = strconv.Atoi(seqEndStr)
	if err != nil {
		seqEnd = 0
	}
	var pageNumber, pageSize int64
	pageNumberStr, exist := urlParameters["pageNumber"]
	if !exist {
		pageNumber = 1
	} else {
		pageNumber, err = strconv.ParseInt(pageNumberStr, 0, 10)
		if err != nil {
			pageNumber = 1
		}
	}
	pageSizeStr, exist := urlParameters["pageSize"]
	if !exist {
		pageSize = 10
	} else {
		pageSize, err = strconv.ParseInt(pageSizeStr, 0, 10)
		if err != nil {
			pageSize = 10
		}
	}
	dao := dal.NewSerialOrderDAO(session, user.PlantNr, user.UserId)
	result, err := dao.SelectOrderByResource(resourceId, status, planDate, seqStart, seqEnd, custOrderId, serialOrderId, true, false, pageNumber, pageSize)
	return result, err
}

// SelectUnSortOrder 查询排序工单页的未排序的工单
func (impl *SerialOrderServiceImplement) SelectUnSortOrder(user *global.User, urlParameters map[string]string) (grmi.PagingResult, error) {
	grmi.Log(user, "/services/om/implments/SerialOrder.service.impl.go", "SelectUnSortOrder", "查询未排序订单")
	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	var err error
	resourceId, exist := urlParameters["resourceId"]
	if !exist {
		return grmi.EmptyPagingResult, grmi.NewBusinessError("未选择查询的产线")
	}
	serialOrderId := urlParameters["serialOrderId"]
	var pageNumber, pageSize int64
	pageNumberStr, exist := urlParameters["pageNumber"]
	if !exist {
		pageNumber = 1
	} else {
		pageNumber, err = strconv.ParseInt(pageNumberStr, 0, 10)
		if err != nil {
			pageNumber = 1
		}
	}
	pageSizeStr, exist := urlParameters["pageSize"]
	if !exist {
		pageSize = 10
	} else {
		pageSize, err = strconv.ParseInt(pageSizeStr, 0, 10)
		if err != nil {
			pageSize = 10
		}
	}
	dao := dal.NewSerialOrderDAO(session, user.PlantNr, user.UserId)
	result, err := dao.SelectOrderByResource(resourceId, 0, "", 0, 0, "", serialOrderId, false, false, pageNumber, pageSize)
	return result, err
}

// SelectUrgencyOrder 查询排序工单页的紧急工单
func (impl *SerialOrderServiceImplement) SelectUrgencyOrder(user *global.User, urlParameters map[string]string) (grmi.PagingResult, error) {
	grmi.Log(user, "/services/om/implments/SerialOrder.service.impl.go", "SelectUrgencyOrder", "查询紧急订单")
	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	var err error
	resourceId, exist := urlParameters["resourceId"]
	if !exist {
		return grmi.EmptyPagingResult, grmi.NewBusinessError("未选择查询的产线")
	}
	var pageNumber, pageSize int64
	pageNumberStr, exist := urlParameters["pageNumber"]
	if !exist {
		pageNumber = 1
	} else {
		pageNumber, err = strconv.ParseInt(pageNumberStr, 0, 10)
		if err != nil {
			pageNumber = 1
		}
	}
	pageSizeStr, exist := urlParameters["pageSize"]
	if !exist {
		pageSize = 10
	} else {
		pageSize, err = strconv.ParseInt(pageSizeStr, 0, 10)
		if err != nil {
			pageSize = 10
		}
	}
	dao := dal.NewSerialOrderDAO(session, user.PlantNr, user.UserId)
	result, err := dao.SelectOrderByResource(resourceId, 0, "", 0, 0, "", "", false, true, pageNumber, pageSize)
	return result, err
}

// ChangeOrderToUrgency 将工单放到紧急工单队列中
func (impl *SerialOrderServiceImplement) ChangeOrderToUrgency(user *global.User, serialOrderId string, priority int) (err error) {
	grmi.Log(user, "/services/om/implments/SerialOrder.service.impl.go", "ChangeOrderToUrgency", "将未计划的工单放到紧急工单队列中")
	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	if priority == 0 {
		return grmi.NewBusinessError("未设置优先级")
	}
	dao := dal.NewSerialOrderDAO(session, user.PlantNr, user.UserId)
	statusDao := dal.NewSerialOrderStatusDAO(session, user.PlantNr, user.UserId)
	statusRecDao := dal.NewSerialOrderStatusRecLstDAO(session, user.PlantNr, user.UserId)
	serialOrder, err := dao.SelectOne(serialOrderId)
	if err != nil {
		return grmi.NewBusinessError("查询工单数据失败, error:" + err.Error())
	}
	if serialOrder == nil {
		return grmi.NewBusinessError("不存在对应的工单， 工单ID:" + serialOrderId)
	}
	serialOrderStatus, err := statusDao.SelectOne(serialOrderId)
	if err != nil {
		return grmi.NewBusinessError("查询工单的状态失败, error:" + err.Error())
	}
	if serialOrderStatus == nil {
		return grmi.NewBusinessError("工单状态数据不存在, 工单ID:" + serialOrderId)
	}
	if serialOrderStatus.Status != baseModel.WO_STATUS_UNPLANNED {
		return grmi.NewBusinessError("工单的状态不是未计划, 工单ID:" + serialOrderId)
	}
	if err := session.Begin(); err != nil {
		return err
	}
	serialOrder.Priority = priority
	serialOrder.PlanStartDate = grmi.Date(time.Now())
	serialOrder.PlanEndDate = grmi.Date(time.Now())
	serialOrder.PlanStartTime = grmi.DateTime(time.Now())
	serialOrder.PlanEndTime = grmi.DateTime(time.Now())
	err = dao.UpdateOne(serialOrder)
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("更新工单数据失败, error:" + err.Error())
	}

	serialOrderStatus.Status = baseModel.WO_STATUS_LOCKED
	err = statusDao.UpdateOne(serialOrderStatus)
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("更新工单状态失败, error:")
	}
	statusRec := model.SerialOrderStatusRecLst{
		SerialOrderId: serialOrderId,
		Status:        baseModel.WO_STATUS_LOCKED,
		PrevStatus:    baseModel.WO_STATUS_UNPLANNED,
	}
	err = statusRecDao.InsertOne(&statusRec)
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("插入工单状态变更记录失败, error:" + err.Error())
	}
	_ = session.Commit()
	return nil
}

// WithdrawSerialOrder 撤回已计划的内部订单
func (impl *SerialOrderServiceImplement) WithdrawSerialOrder(user *global.User, serialOrderId string, source string) (err error) {
	grmi.Log(user, "/services/om/implments/SerialOrder.service.impl.go", "WithdrawSerialOrder", "撤回已计划的内部订单")
	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	if err := session.Begin(); err != nil {
		return err
	}
	serialOrderDao := dal.NewSerialOrderDAO(session, user.PlantNr, user.UserId)
	serialOrder, err := serialOrderDao.SelectOne(serialOrderId)
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("查询工单失败, error:" + err.Error())
	}
	if serialOrder == nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("工单不存在， 工单ID:" + serialOrderId)
	}
	if source == "urgency" {
		if serialOrder.Priority == 0 {
			_ = session.Rollback()
			return grmi.NewBusinessError("该工单不是紧急队列中的工单")
		}
	}
	serialOrder.Priority = 0
	serialOrder.PlanStartDate = grmi.Date{}
	serialOrder.PlanEndDate = grmi.Date{}
	serialOrder.PlanStartTime = grmi.DateTime{}
	serialOrder.PlanEndTime = grmi.DateTime{}
	serialOrder.SchedKey = 0
	err = serialOrderDao.UpdateOne(serialOrder)
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("更新工单数据失败， error:" + err.Error())
	}
	statusDao := dal.NewSerialOrderStatusDAO(session, user.PlantNr, user.UserId)
	statusRecDao := dal.NewSerialOrderStatusRecLstDAO(session, user.PlantNr, user.UserId)
	serialOrderStatus, err := statusDao.SelectOne(serialOrderId)
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("查询工单的状态失败, error:" + err.Error())
	}
	if serialOrderStatus == nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("工单状态数据不存在, 工单ID:" + serialOrderId)
	}

	if serialOrderStatus.Status != baseModel.WO_STATUS_LOCKED {
		_ = session.Rollback()
		return grmi.NewBusinessError("工单的状态不是已锁定, 工单ID:" + serialOrderId)
	}

	serialOrderStatus.Status = baseModel.WO_STATUS_UNPLANNED
	err = statusDao.UpdateOne(serialOrderStatus)
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("更新工单状态失败, error:")
	}
	statusRec := model.SerialOrderStatusRecLst{
		SerialOrderId: serialOrderId,
		Status:        baseModel.WO_STATUS_UNPLANNED,
		PrevStatus:    baseModel.WO_STATUS_LOCKED,
	}
	err = statusRecDao.InsertOne(&statusRec)
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("插入工单状态变更记录失败, error:" + err.Error())
	}
	_ = session.Commit()
	return nil
}

// PutIntSerialOrderToSeq 将未排序的内部订单放入放排序队列
func (impl *SerialOrderServiceImplement) PutIntSerialOrderToSeq(user *global.User, serialOrderId string, preSchedKey int64) error {
	grmi.Log(user, "/services/om/implments/SerialOrder.service.impl.go", "PutIntSerialOrderToSeq", "将内部订单放入到排序队列中")
	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	if err := session.Begin(); err != nil {
		return err
	}
	dao := dal.NewSerialOrderDAO(session, user.PlantNr, user.UserId)
	serialOrder, err := dao.SelectOne(serialOrderId)
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("查询工单数据失败, error:" + err.Error())
	}
	if serialOrder == nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("工单数据不存在, 工单ID:" + err.Error())
	}
	if serialOrder.OrderType != baseModel.ODER_TYPE_INTERVAL {
		_ = session.Rollback()
		return grmi.NewBusinessError("工单不是内部订单,不支持该操作")
	}
	serialOrder.PlanStartDate = grmi.Date(time.Now())
	serialOrder.PlanEndDate = grmi.Date(time.Now())
	serialOrder.PlanStartTime = grmi.DateTime(time.Now())
	serialOrder.PlanEndTime = grmi.DateTime(time.Now())
	err = dao.UpdateOne(serialOrder)
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("更新工单数据失败, error:" + err.Error())
	}
	statusDao := dal.NewSerialOrderStatusDAO(session, user.PlantNr, user.UserId)
	statusRecDao := dal.NewSerialOrderStatusRecLstDAO(session, user.PlantNr, user.UserId)
	serialOrderStatus, err := statusDao.SelectOne(serialOrderId)
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("查询工单的状态失败, error:" + err.Error())
	}
	if serialOrderStatus == nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("工单状态数据不存在, 工单ID:" + serialOrderId)
	}
	if serialOrderStatus.Status != baseModel.WO_STATUS_UNPLANNED {
		_ = session.Rollback()
		return grmi.NewBusinessError("工单的状态不是未计划, 工单ID:" + serialOrderId)
	}
	schedKey := preSchedKey + 1
	serialOrderLi, err := dao.Select([]grmi.Predicate{meta.SerialOrder_PlanResourceId.NewPredicate(grmi.Equal, serialOrder.PlanResourceId), meta.SerialOrder_SchedKey.NewPredicate(grmi.Equal, schedKey)}, nil)
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("查询工单数据失败， error:" + err.Error())
	}
	if len(serialOrderLi) != 0 {
		_ = session.Rollback()
		return grmi.NewBusinessError("该PreSchedKey之后已存在紧连接的工单, 请调整")
	}
	serialOrder.SchedKey = schedKey
	err = dao.UpdateOne(serialOrder)
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("更新工单数据失败, error:" + err.Error())
	}
	serialOrderStatus.Status = baseModel.WO_STATUS_LOCKED
	err = statusDao.UpdateOne(serialOrderStatus)
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("更新工单状态数据失败， error:" + err.Error())
	}
	statusRec := model.SerialOrderStatusRecLst{
		SerialOrderId: serialOrderId,
		PrevStatus:    baseModel.WO_STATUS_UNPLANNED,
		Status:        baseModel.WO_STATUS_LOCKED,
	}
	err = statusRecDao.InsertOne(&statusRec)
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("插入工单状态变更记录失败, error:" + err.Error())
	}
	_ = session.Commit()
	return nil
}

// ChangeOrderSchedKey 已排序的内部订单调整schedKey
func (impl *SerialOrderServiceImplement) ChangeOrderSchedKey(user *global.User, serialOrderId string, schedKey int64) error {
	grmi.Log(user, "/services/om/implments/SerialOrder.service.impl.go", "ChangeOrderSchedKey", "已排序的内部订单调整schedKey")
	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	dao := dal.NewSerialOrderDAO(session, user.PlantNr, user.UserId)
	serialOrder, err := dao.SelectOne(serialOrderId)
	if err != nil {
		return grmi.NewBusinessError("查询工单数据失败, error:" + err.Error())
	}
	if serialOrder == nil {
		return grmi.NewBusinessError("工单数据不存在, 工单ID:" + err.Error())
	}
	if serialOrder.OrderType != baseModel.ODER_TYPE_INTERVAL {
		return grmi.NewBusinessError("工单不是内部订单,不支持该操作")
	}
	statusDao := dal.NewSerialOrderStatusDAO(session, user.PlantNr, user.UserId)
	serialOrderStatus, err := statusDao.SelectOne(serialOrderId)
	if err != nil {
		return grmi.NewBusinessError("查询工单的状态失败, error:" + err.Error())
	}
	if serialOrderStatus == nil {
		return grmi.NewBusinessError("工单状态数据不存在, 工单ID:" + serialOrderId)
	}
	if serialOrderStatus.Status != baseModel.WO_STATUS_LOCKED {
		return grmi.NewBusinessError("工单的状态不是已锁定, 工单ID:" + serialOrderId)
	}
	serialOrderLi, err := dao.Select([]grmi.Predicate{meta.SerialOrder_ProjectId.NewPredicate(grmi.Equal, serialOrder.ProjectId), meta.SerialOrder_SchedKey.NewPredicate(grmi.Equal, schedKey)}, nil)
	if err != nil {
		return grmi.NewBusinessError("查询工单数据失败， error:" + err.Error())
	}
	if len(serialOrderLi) != 0 {
		return grmi.NewBusinessError("已存在该SchedKey的工单, 请调整")
	}
	serialOrder.SchedKey = schedKey
	err = dao.UpdateOne(serialOrder)
	if err != nil {
		return grmi.NewBusinessError("更新工单数据失败, error:" + err.Error())
	}
	_ = session.Commit()
	return nil
}

// GetOrderPOLi 获取工序所有的前序
func (impl *SerialOrderServiceImplement) GetOrderPOLi(poData baseModel.Process, allPoMap map[string]baseModel.Process) []baseModel.Process {
	prePoLi := strings.Split(poData.PrePO, ",")
	prePoDataLi := make([]baseModel.Process, 0, len(prePoLi))
	for _, poId := range prePoLi {
		prePoData, exist := allPoMap[poId]
		if exist {
			prePoDataLi = append(prePoDataLi, prePoData)
			if prePoData.PrePO != "" {
				innerPrePoDataLi := impl.GetOrderPOLi(prePoData, allPoMap)
				prePoDataLi = append(prePoDataLi, innerPrePoDataLi...)
			}
		}
	}
	return prePoDataLi
}

// GenerateCraftDataByCustOrder 通过custOrder生成的工单map生成工艺
func (impl *SerialOrderServiceImplement) GenerateCraftDataByCustOrder(user *global.User, session *xorm.Session, productSerialOrderMap map[string][]*model.SerialOrder) error {
	workLineProcessDao := baseDal.NewWorkLineProcessLstDAO(session, user.PlantNr, user.UserId)
	//projectDao := meDal.NewProjectDAO(session, user.PlantNr, user.UserId)
	serialOrderDao := dal.NewSerialOrderDAO(session, user.PlantNr, user.UserId)
	serialOrderStatusDao := dal.NewSerialOrderStatusDAO(session, user.PlantNr, user.UserId)
	operationDao := baseDal.NewOperationDAO(session, user.PlantNr, user.UserId)
	PODao := dal.NewSerialOrderPOLstDAO(session, user.PlantNr, user.UserId)
	stepDao := dal.NewSerialOrderStepLstDAO(session, user.PlantNr, user.UserId)
	articleWithAttrDao := baseDal.NewArticleAtcodLstDAO(session, user.PlantNr, user.UserId)
	scanRuleDao := baseDal.NewScanRuleInfoDAO(session, user.PlantNr, user.UserId)
	guideDao := baseDal.NewGuideRuleInfoDAO(session, user.PlantNr, user.UserId)
	bomHeadDao := meDal.NewBomHeadDAO(session, user.PlantNr, user.UserId)
	labelTemplateDetailDao := baseDal.NewLabelTemplateDetailDAO(session, user.PlantNr, user.UserId)
	articleDao := baseDal.NewArticleDAO(session, user.PlantNr, user.UserId)
	printRuleDao := baseDal.NewPrintBasicRuleInfoDAO(session, user.PlantNr, user.UserId)
	productDao := meDal.NewProductDAO(session, user.PlantNr, user.UserId)
	shipableAUConfigDao := meDal.NewShipableAUConfigDAO(session, user.PlantNr, user.UserId)
	snrDao := baseDal.NewSnrDAO(session, user.PlantNr, user.UserId)
	projectDao := meDal.NewProjectDAO(session, user.PlantNr, user.UserId)
	bomLstDao := meDal.NewBomLstDAO(session, user.PlantNr, user.UserId)
	// 用于子工单物料临时存储
	var project *meModel.Project
	today := time.Now().Format("060102")
	// 获取所有的标签打印模板 并组建MAP
	labelTemplateLi, err := labelTemplateDetailDao.SelectAllTemplate()
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("查询标签模板失败,error:" + err.Error())
	}
	labelTemplateMap := make(map[string]*baseModel.LabelTemplateHead)
	for _, template := range labelTemplateLi {
		innerTemplate := template
		labelTemplateMap[template.LabelTemplateId] = &innerTemplate
	}
	preSnrMap := make(map[string]string)
	for productId, serialOrderLi := range productSerialOrderMap {
		if len(serialOrderLi) == 0 {
			continue
		}
		projectId := serialOrderLi[0].ProjectId
		product, err := productDao.SelectOne(productId, projectId)
		if err != nil {
			_ = session.Rollback()
			return grmi.NewBusinessError("查询产品总成数据失败, error:" + err.Error())
		}
		if product == nil {
			_ = session.Rollback()
			return grmi.NewBusinessError("产品总成不存在, 总成ID:" + productId)
		}
		article, err := articleDao.SelectOne(productId, product.ProjectId)
		if err != nil {
			_ = session.Rollback()
			return grmi.NewBusinessError("查询工单总成失败, error:" + err.Error())
		}
		if article == nil {
			_ = session.Rollback()
			return grmi.NewBusinessError("工单总成不存在, 总成ID:" + productId)
		}
		bomHead, err := bomHeadDao.SelectOne(productId)
		if err != nil {
			_ = session.Rollback()
			return grmi.NewBusinessError("查询物料BOM失败, error:" + err.Error())
		}
		if bomHead == nil {
			_ = session.Rollback()
			return grmi.NewBusinessError("不存在对应的物料BOM, 物料ID:" + productId)
		}
		var Me meModel.BomLst
		Me.PlantNr = user.PlantNr
		Me.BomId = bomHead.BomId
		bomList, err := Me.SetMenu(bomHead.MatNr)
		if err != nil {
			_ = session.Rollback()
			return grmi.NewBusinessError("获取BOM细则失败, error:" + err.Error())
		}
		if len(bomList) == 0 {
			_ = session.Rollback()
			return grmi.NewBusinessError("未获取到BOM细则, 物料ID:" + bomHead.BomId)
		}
		bomHead.BomLstLi = bomList
		// 获取关联的所有零件ID
		artMap := impl.ExtractArtId(bomHead)
		artIdLi := make([]string, 0, len(artMap))
		for innerArtId := range artMap {
			artIdLi = append(artIdLi, innerArtId)
		}
		artIdLi = append(artIdLi, productId)
		allArticleAttributeLi, err := articleWithAttrDao.SelectWithArticleByArtIdLi(artIdLi, projectId)
		if err != nil {
			_ = session.Rollback()
			return grmi.NewBusinessError("查询半成品关联所有属性失败, 错误:" + err.Error())
		}
		workLineId := serialOrderLi[0].PlanResourceId
		// 查询所有使用到的工序
		usedPoLi, err := workLineProcessDao.SelectProcessByWorkLine(workLineId)
		if err != nil {
			_ = session.Rollback()
			return grmi.NewBusinessError("获取产线关联的工序失败, error:" + err.Error())
		}
		if len(usedPoLi) == 0 {
			_ = session.Rollback()
			return grmi.NewBusinessError("该产线未关联工序, 产线ID:" + workLineId)
		}
		// 拆分预装子工单
		// 1. 将所有工序数据生成MAP
		// 2. 从后往前查 如果有shipableAU标识 代表需要生成工单 需要查询shipableAUConfig数据表中该po的配置
		// 3. 生成工单有两种规则 一种是总装线 主工单 一种是预装线 子工单
		bomLstLi, err := bomLstDao.Select([]grmi.Predicate{
			meMeta.BomLst_BomId.NewPredicate(grmi.Equal, productId),
			meMeta.BomLst_ShipableAU.NewPredicate(grmi.Equal, true),
		}, nil)
		if err != nil {
			_ = session.Rollback()
			return grmi.NewBusinessError("查询BOM子项数据失败, 错误:" + err.Error())
		}
		poDataMap := make(map[string]baseModel.Process)
		allPoIdInterfaceLi := make([]interface{}, 0, len(usedPoLi))

		for _, po := range usedPoLi {
			poDataMap[po.PO] = po
			allPoIdInterfaceLi = append(allPoIdInterfaceLi, po.PO)
		}
		sfpUsedPoMap := make(map[string]interface{})
		orderPoMap := make(map[string]*model.OrderAUPoData)
		for _, bomLst := range bomLstLi {
			sfp, err := articleDao.SelectOne(bomLst.CmatNr, projectId)
			if err != nil {
				_ = session.Rollback()
				return grmi.NewBusinessError("查询物料失败, 错误:" + err.Error())
			}
			if sfp == nil {
				_ = session.Rollback()
				return grmi.NewBusinessError("半成品物料不存在, 物料ID:" + bomLst.CmatNr)
			}
			auConfig, err := shipableAUConfigDao.SelectOne(bomLst.CmatNr)
			if err != nil {
				_ = session.Rollback()
				return grmi.NewBusinessError("查询半成品配置数据失败, 错误:" + err.Error())
			}
			if auConfig == nil {
				_ = session.Rollback()
				return grmi.NewBusinessError("半成品配置不存在， 物料ID:" + bomLst.CmatNr)
			}

			sfpBomHead, err := bomHeadDao.SelectOne(bomLst.CmatNr)
			if err != nil {
				_ = session.Rollback()
				return grmi.NewBusinessError("查询物料BOM失败, error:" + err.Error())
			}
			if sfpBomHead == nil {
				_ = session.Rollback()
				return grmi.NewBusinessError("不存在对应的物料BOM, 物料ID:" + productId)
			}
			var sfpMe meModel.BomLst
			sfpMe.PlantNr = user.PlantNr
			sfpMe.BomId = sfpBomHead.BomId
			sfpBomLstLi, err := sfpMe.SetMenu(sfpBomHead.MatNr)
			if err != nil {
				_ = session.Rollback()
				return grmi.NewBusinessError("获取BOM细则失败, error:" + err.Error())
			}
			if len(sfpBomLstLi) == 0 {
				_ = session.Rollback()
				return grmi.NewBusinessError("未获取到BOM细则, 物料ID:" + bomHead.BomId)
			}
			sfpBomHead.BomLstLi = sfpBomLstLi
			// 获取关联的所有零件ID
			sfpArtMap := impl.ExtractArtId(sfpBomHead)
			sfpArtIdLi := make([]string, 0, len(sfpArtMap))
			for innerArtId := range sfpArtMap {
				sfpArtIdLi = append(sfpArtIdLi, innerArtId)
			}
			sfpArtIdLi = append(sfpArtIdLi, sfp.ArtId)
			sfpAllArticleAttributeLi, err := articleWithAttrDao.SelectWithArticleByArtIdLi(sfpArtIdLi, projectId)
			if err != nil {
				_ = session.Rollback()
				return grmi.NewBusinessError("查询半成品关联所有属性失败, 错误:" + err.Error())
			}
			auPoData, exist := poDataMap[auConfig.AuPo]
			if !exist {
				_ = session.Rollback()
				return grmi.NewBusinessError("半成品指定工序不存在于总工单的工序序列中, 半成品ID:" + sfp.ArtId)
			}
			// 根据该工序查询所有后序
			poDataLi := impl.GetOrderPOLi(auPoData, poDataMap)
			// 该工单用到的所有生产工序
			poDataLi = append(poDataLi, auPoData)
			// 查询这个工单需要用到的所有工艺数据
			poIdLi := make([]string, 0, len(poDataLi))
			for _, innerPoData := range poDataLi {
				sfpUsedPoMap[innerPoData.PO] = nil
				poIdLi = append(poIdLi, innerPoData.PO)
			}
			opLi, err := operationDao.GetMeOperation(sfpArtIdLi, projectId, poIdLi, workLineId)
			if err != nil {
				_ = session.Rollback()
				return grmi.NewBusinessError("获取工艺操作失败,error:" + err.Error())
			}
			if len(opLi) == 0 {
				_ = session.Rollback()
				return grmi.NewBusinessError("未获取到工艺数据")
			}
			// 所有需要生成工单工序绑定的数据
			orderPoMap[bomLst.CmatNr] = &model.OrderAUPoData{
				Config:         auConfig,
				MeOperationLi:  opLi,
				IsMainOrder:    false,
				SFP:            sfp,
				AllAttributeLi: sfpAllArticleAttributeLi,
				ArtRelMap:      sfpArtMap,
			}
		}
		mainOrderPOIdLi := make([]string, 0)
		for _, usedPoData := range usedPoLi {
			_, exist := sfpUsedPoMap[usedPoData.PO]
			if !exist {
				mainOrderPOIdLi = append(mainOrderPOIdLi, usedPoData.PO)
			}
		}
		if len(mainOrderPOIdLi) == 0 {
			_ = session.Rollback()
			return grmi.NewBusinessError("主工单无生产所需工序")
		}
		opLi, err := operationDao.GetMeOperation(artIdLi, projectId, mainOrderPOIdLi, workLineId)
		if err != nil {
			_ = session.Rollback()
			return grmi.NewBusinessError("获取工艺操作失败,error:" + err.Error())
		}
		if len(opLi) == 0 {
			_ = session.Rollback()
			return grmi.NewBusinessError("未获取到工艺数据")
		}
		// 所有需要生成工单工序绑定的数据
		orderPoMap[productId] = &model.OrderAUPoData{
			Config:         nil,
			MeOperationLi:  opLi,
			IsMainOrder:    true,
			SFP:            nil,
			AllAttributeLi: allArticleAttributeLi,
			ArtRelMap:      artMap,
		}

		// 获取所有使用工序的扫码校验规则 组建MAP数据结构
		scanRuleLi, err := scanRuleDao.Select([]grmi.Predicate{baseMeta.ScanRuleInfo_ProjectId.NewPredicate(grmi.Equal, projectId), baseMeta.ScanRuleInfo_PO.NewPredicate(grmi.Include, allPoIdInterfaceLi...)}, nil)
		if err != nil {
			_ = session.Rollback()
			return grmi.NewBusinessError("查询扫码校验条码失败, error:" + err.Error())
		}
		scanRuleMap := make(map[int]baseModel.ScanRuleInfo)
		for _, scanRule := range scanRuleLi {
			scanRuleMap[scanRule.OperationId] = scanRule
		}
		// 获取所有使用工序指导操作的规则 组建MAP数据结构
		guideRuleLi, err := guideDao.Select([]grmi.Predicate{baseMeta.GuideRuleInfo_ProjectId.NewPredicate(grmi.Equal, projectId), baseMeta.GuideRuleInfo_PO.NewPredicate(grmi.Include, allPoIdInterfaceLi...)}, nil)
		if err != nil {
			_ = session.Rollback()
			return grmi.NewBusinessError("查询指导操作规则失败, error:" + err.Error())
		}
		guideRuleMap := make(map[int]baseModel.GuideRuleInfo, len(guideRuleLi))
		for _, guideRule := range guideRuleLi {
			guideRuleMap[guideRule.OperationId] = guideRule
		}
		// 获取所有使用工序扫码校验规则 组建MAP数据结构
		printRuleLi, err := printRuleDao.Select([]grmi.Predicate{baseMeta.PrintBasicRuleInfo_ProjectId.NewPredicate(grmi.Equal, projectId), baseMeta.PrintBasicRuleInfo_PO.NewPredicate(grmi.Include, allPoIdInterfaceLi...)}, []grmi.Field{baseMeta.PrintBasicRuleInfo_OperationId})
		printRuleMap := make(map[int][]baseModel.PrintBasicRuleInfo)
		for _, data := range printRuleLi {
			_, exist := printRuleMap[data.OperationId]
			if !exist {
				printRuleMap[data.OperationId] = make([]baseModel.PrintBasicRuleInfo, 0)
			}
			printRuleMap[data.OperationId] = append(printRuleMap[data.OperationId], data)
		}
		// 获取所有物料属性关联关系
		attributeLi, err := articleWithAttrDao.SelectAllWithAttrValue()
		if err != nil {
			_ = session.Rollback()
			return grmi.NewBusinessError("查询零件属性失败,error:" + err.Error())
		}
		attributeMap := make(map[int][]baseModel.ArticleWithAttrValue)
		for _, data := range attributeLi {
			_, exist := attributeMap[data.ArticleAtcodLst.AttrCode]
			if !exist {
				attributeMap[data.ArticleAtcodLst.AttrCode] = make([]baseModel.ArticleWithAttrValue, 0)
			}
			attributeMap[data.ArticleAtcodLst.AttrCode] = append(attributeMap[data.ArticleAtcodLst.AttrCode], data)
		}
		// 生成序列工单对应的工艺数据
		allocateMap := make(map[string][]baseModel.ArtQtyAllocate)
		checkRuleMap := make(map[string]baseModel.ArtCheckRule)
		opcBasicMap := make(map[int][]baseModel.OPCBasicComInfo)
		twMap := make(map[int][]baseModel.TightenWrenchInfo)
		for _, serialOrder := range serialOrderLi {
			planDate := serialOrder.PlanStartDate.Restore().Format(grmi.DateOutFormat)
			schedKey := serialOrder.SchedKey
			var usedArticle *baseModel.Article
			for _, orderOPData := range orderPoMap {
				var serialOrderId string
				if !orderOPData.IsMainOrder {
					usedArticle = orderOPData.SFP
					if project == nil {
						project, err = projectDao.SelectOne(projectId)
						if err != nil {
							_ = session.Rollback()
							return grmi.NewBusinessError("查询项目失败, 错误:" + err.Error())
						}
					}
					if orderOPData.Config.BarCodeRule == baseModel.BARCODE_RULE_ACURA_PRE {
						number, exist := preSnrMap[serialOrder.SyncKey]
						if !exist {
							number, err = snrDao.GetNextSnr(project.PreOrderSnr)
							if err != nil {
								_ = session.Rollback()
								return grmi.NewBusinessError("获取流水号失败，错误:" + err.Error())
							}
							preSnrMap[serialOrder.SyncKey] = number
						}
						serialOrderId = strings.Replace(orderOPData.SFP.ArtId, "-", "", -1) + today + number
					} else {
						_ = session.Rollback()
						return grmi.NewBusinessError("不支持的子工单条码生成规则，物料Id:" + orderOPData.SFP.ArtId)
					}
					sfpSerialOrder := model.SerialOrder{
						SerialOrderId:    serialOrderId,
						ArtId:            orderOPData.SFP.ArtId,
						ProjectId:        projectId,
						Descr:            orderOPData.SFP.Descr1,
						CustOrderId:      serialOrder.CustOrderId,
						LastPieceInBatch: serialOrder.LastPieceInBatch,
						PreSchedKey:      schedKey,
						SchedKey:         schedKey,
						OrderType:        baseModel.ORDER_TYPE_PRE,
						OrderType1:       serialOrder.OrderType1,
						OrderType2:       serialOrder.OrderType2,
						PlanResourceId:   serialOrder.PlanResourceId,
						UsedResourceId:   serialOrder.UsedResourceId,
						PlanQty:          1,
						QtyUomId:         serialOrder.QtyUomId,
						OpTimePerItem:    serialOrder.OpTimePerItem,
						TimeUomId:        serialOrder.TimeUomId,
						PlanStartDate:    serialOrder.PlanStartDate,
						PlanEndDate:      serialOrder.PlanEndDate,
						SetupStartTime:   serialOrder.SetupStartTime,
						SetupEndTime:     serialOrder.SetupEndTime,
						PlanStartTime:    serialOrder.PlanStartTime,
						PlanEndTime:      serialOrder.PlanEndTime,
						PickingFlag:      orderOPData.SFP.PickingFlag,
						MainOrderId:      serialOrder.SerialOrderId,
					}
					err = serialOrderDao.InsertOne(&sfpSerialOrder)
					if err != nil {
						_ = session.Rollback()
						return grmi.NewBusinessError("写入预装工单失败， 错误:" + err.Error())
					}
					sfpSerialOrderStatus := model.SerialOrderStatus{
						SerialOrderId: serialOrderId,
						Status:        baseModel.WO_STATUS_RELEASED,
					}
					err = serialOrderStatusDao.InsertOne(&sfpSerialOrderStatus)
					if err != nil {
						_ = session.Rollback()
						return grmi.NewBusinessError("写入预装工单状态数据失败, 错误:" + err.Error())
					}
					//schedKey++
				} else {
					usedArticle = article
					serialOrderId = serialOrder.SerialOrderId
				}
				var PO string
				processPos := 1
				opPos := 1
				for _, op := range orderOPData.MeOperationLi {
					// 生成工序操作状态表
					if PO != op.Process.PO {
						PO = op.Process.PO
						serialOrderWorkPlace := model.SerialOrderPOLst{
							SerialOrderId: serialOrderId,
							PO:            PO,
							Status:        baseModel.WO_STATUS_RELEASED,
							HasDependOn:   op.Process.HasDependOn,
						}
						err = PODao.InsertOne(&serialOrderWorkPlace)
						if err != nil {
							_ = session.Rollback()
							return grmi.NewBusinessError("插入serialOrder工序数据失败,error:" + err.Error())
						}
						processPos++
					}
					serialOrderStep := model.SerialOrderStepLst{
						PO:            op.Process.PO,
						OperationId:   op.OperationId,
						SerialOrderId: serialOrderId,
						StepNo:        op.Operation.StepNo,
						Pos:           opPos,
						SubArtId:      op.ArticleAtcodLst.ArtId,
						StepDesc:      op.Operation.StepDesc,
						AttrCode:      op.ArticleAtcodLst.AttrCode,
						AttrValue:     op.ArticleAtcodLst.AttrValue,
						StepType:      op.Operation.StepType,
						Status:        baseModel.WO_STATUS_RELEASED,
					}
					opPos++
					err = stepDao.InsertOne(&serialOrderStep)
					if err != nil {
						_ = session.Rollback()
						return grmi.NewBusinessError("插入序列工单工艺数据失败, error:" + err.Error())
					}
					if serialOrderStep.StepType == baseModel.STEP_CHECK_RULE { // 生成扫码校验的工艺数据
						scanRule, exist := scanRuleMap[serialOrderStep.OperationId]
						if !exist {
							return grmi.NewBusinessError("未获取到扫描校验详细规则, 工艺ID:" + strconv.Itoa(serialOrderStep.OperationId))
						}

						checkRuleMapPointer, allocateMapPointer, err := impl.ScanCodeCheck(user, session, checkRuleMap, allocateMap, orderOPData.ArtRelMap, &scanRule, &op, &serialOrderStep, op.Operation.ActivateInBKStation)
						if err != nil {
							_ = session.Rollback()
							return err
						}
						checkRuleMap = *checkRuleMapPointer
						allocateMap = *allocateMapPointer
					} else if serialOrderStep.StepType == baseModel.STEP_OPC_RULE { // 生成OPC通讯的工艺数据
						opcBasicMapPointer, err := impl.OPCCommunication(user, session, opcBasicMap, &serialOrderStep, attributeMap, artMap, op.Operation.ActivateInBKStation)
						if err != nil {
							_ = session.Rollback()
							return err
						}
						opcBasicMap = *opcBasicMapPointer
					} else if serialOrderStep.StepType == baseModel.STEP_TW_RULE { // 生成开发协议的工艺数据
						twMapPointer, err := impl.TightenWrenchInfo(user, session, twMap, &serialOrderStep, op.Operation.ActivateInBKStation, usedArticle)
						if err != nil {
							_ = session.Rollback()
							return err
						}
						twMap = *twMapPointer
					} else if serialOrderStep.StepType == baseModel.STEP_GUIDE_RULE {
						guideRule, exist := guideRuleMap[serialOrderStep.OperationId]
						if !exist {
							_ = session.Rollback()
							return grmi.NewBusinessError("未获取到指导操作的详细规则")
						}
						err = impl.GuideRuleInfo(user, session, &guideRule, &serialOrderStep, op.Operation.ActivateInBKStation)
						if err != nil {
							_ = session.Rollback()
							return err
						}
					} else if serialOrderStep.StepType == baseModel.STEP_PRINT_RULE {
						ruleLi, exist := printRuleMap[serialOrderStep.OperationId]
						if !exist {
							_ = session.Rollback()
							return grmi.NewBusinessError("未获取到打印规则, 工艺ID:" + strconv.Itoa(serialOrderStep.OperationId))
						}

						generateRuleLi := make([]baseModel.PrintBasicRuleInfo, 0)
						for _, rule := range ruleLi {
							if rule.Action == baseModel.PRINT_RULE_ACTION_UPDATE {
								generateRuleLi = append(generateRuleLi, rule)
							}
						}
						if len(generateRuleLi) > 1 {
							_ = session.Rollback()
							return grmi.NewBusinessError("改工步匹配到超过1条生成打印标识的打印规则")
						}
						var generateRule *baseModel.PrintBasicRuleInfo
						var printTemplate *baseModel.LabelTemplateHead
						if len(generateRuleLi) == 1 {
							generateRule = &generateRuleLi[0]
							var templateId string
							if generateRule.MainCodeName == "Product" {
								templateId = product.PrintTemplateId
							} else {
								templateId = generateRule.TemplateId
							}
							printTemplate, exist = labelTemplateMap[templateId]
							if !exist {
								_ = session.Rollback()
								return grmi.NewBusinessError("存在单据打印规则中配置的标签模板, 模板ID:" + generateRule.TemplateId)
							}
						}
						var printUsedArticle *baseModel.Article
						if op.Operation.AttrCode == -1 {
							printUsedArticle = usedArticle
						} else {
							for _, articleAttribute := range orderOPData.AllAttributeLi {
								if op.Operation.AttrCode == articleAttribute.AttrCode && op.Operation.AttrValue == articleAttribute.AttrValue {
									printUsedArticle = &articleAttribute.Article
									break
								}
							}
						}
						if printUsedArticle == nil {
							_ = session.Rollback()
							return grmi.NewBusinessError("单据打印规则没有获取到需要的物料数据, 工艺Id:" + strconv.Itoa(op.Operation.OperationId))
						}
						err = impl.PrintRuleInfo(user, session, &serialOrderStep, generateRule, printTemplate, printUsedArticle, op.Operation.ActivateInBKStation, planDate)
						if err != nil {
							_ = session.Rollback()
							return err
						}
					} else {
						_ = session.Rollback()
						return grmi.NewBusinessError("获取到的工艺数据系统暂不支持该操作类型:" + serialOrderStep.StepType)
					}
				}
			}

		}
	}
	return nil
}

// GenerateCraftData 生成工单的工艺数据
func (impl *SerialOrderServiceImplement) GenerateCraftData(user *global.User, session *xorm.Session, serialOrderLi []model.SerialOrder) error {
	workLineProcessDao := baseDal.NewWorkLineProcessLstDAO(session, user.PlantNr, user.UserId)
	serialOrderDao := dal.NewSerialOrderDAO(session, user.PlantNr, user.UserId)
	serialOrderStatusDao := dal.NewSerialOrderStatusDAO(session, user.PlantNr, user.UserId)
	operationDao := baseDal.NewOperationDAO(session, user.PlantNr, user.UserId)
	PODao := dal.NewSerialOrderPOLstDAO(session, user.PlantNr, user.UserId)
	stepDao := dal.NewSerialOrderStepLstDAO(session, user.PlantNr, user.UserId)
	articleWithAttrDao := baseDal.NewArticleAtcodLstDAO(session, user.PlantNr, user.UserId)
	scanRuleDao := baseDal.NewScanRuleInfoDAO(session, user.PlantNr, user.UserId)
	guideDao := baseDal.NewGuideRuleInfoDAO(session, user.PlantNr, user.UserId)
	bomHeadDao := meDal.NewBomHeadDAO(session, user.PlantNr, user.UserId)
	labelTemplateDetailDao := baseDal.NewLabelTemplateDetailDAO(session, user.PlantNr, user.UserId)
	articleDao := baseDal.NewArticleDAO(session, user.PlantNr, user.UserId)
	printRuleDao := baseDal.NewPrintBasicRuleInfoDAO(session, user.PlantNr, user.UserId)
	productDao := meDal.NewProductDAO(session, user.PlantNr, user.UserId)
	shipableAUConfigDao := meDal.NewShipableAUConfigDAO(session, user.PlantNr, user.UserId)
	snrDao := baseDal.NewSnrDAO(session, user.PlantNr, user.UserId)
	projectDao := meDal.NewProjectDAO(session, user.PlantNr, user.UserId)
	bomLstDao := meDal.NewBomLstDAO(session, user.PlantNr, user.UserId)
	// 用于子工单物料临时存储
	var project *meModel.Project
	today := time.Now().Format("060102")
	// 获取所有的标签打印模板 并组建MAP
	labelTemplateLi, err := labelTemplateDetailDao.SelectAllTemplate()
	if err != nil {
		return grmi.NewBusinessError("查询标签模板失败,error:" + err.Error())
	}
	labelTemplateMap := make(map[string]*baseModel.LabelTemplateHead)
	for _, template := range labelTemplateLi {
		innerTemplate := template
		labelTemplateMap[template.LabelTemplateId] = &innerTemplate
	}
	projectId := serialOrderLi[0].ProjectId
	productId := serialOrderLi[0].ArtId
	product, err := productDao.SelectOne(productId, projectId)
	if err != nil {
		return grmi.NewBusinessError("查询产品总成数据失败, error:" + err.Error())
	}
	if product == nil {
		return grmi.NewBusinessError("产品总成不存在, 总成ID:" + productId)
	}
	article, err := articleDao.SelectOne(productId, product.ProjectId)
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("查询工单总成失败, error:" + err.Error())
	}
	if article == nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("工单总成不存在, 总成ID:" + productId)
	}
	bomHead, err := bomHeadDao.SelectOne(productId)
	if err != nil {
		return grmi.NewBusinessError("查询物料BOM失败, error:" + err.Error())
	}
	if bomHead == nil {
		return grmi.NewBusinessError("不存在对应的物料BOM, 物料ID:" + productId)
	}
	var Me meModel.BomLst
	Me.PlantNr = user.PlantNr
	Me.BomId = bomHead.BomId
	bomList, err := Me.SetMenu(bomHead.MatNr)
	if err != nil {
		return grmi.NewBusinessError("获取BOM细则失败, error:" + err.Error())
	}
	if len(bomList) == 0 {
		return grmi.NewBusinessError("未获取到BOM细则, 物料ID:" + bomHead.BomId)
	}
	bomHead.BomLstLi = bomList
	// 获取关联的所有零件ID
	artMap := impl.ExtractArtId(bomHead)
	artIdLi := make([]string, 0, len(artMap))
	for innerArtId := range artMap {
		artIdLi = append(artIdLi, innerArtId)
	}
	artIdLi = append(artIdLi, productId)
	allArticleAttributeLi, err := articleWithAttrDao.SelectWithArticleByArtIdLi(artIdLi, projectId)
	if err != nil {
		_ = session.Rollback()
		return grmi.NewBusinessError("查询半成品关联所有属性失败, 错误:" + err.Error())
	}
	workLineId := serialOrderLi[0].PlanResourceId
	// 查询所有使用到的工序
	usedPoLi, err := workLineProcessDao.SelectProcessByWorkLine(workLineId)
	if err != nil {
		return grmi.NewBusinessError("获取产线关联的工序失败, error:" + err.Error())
	}
	if len(usedPoLi) == 0 {
		return grmi.NewBusinessError("该产线未关联工序, 产线ID:" + workLineId)
	}
	// 拆分预装子工单
	// 1. 将所有工序数据生成MAP
	// 2. 从后往前查 如果有shipableAU标识 代表需要生成工单 需要查询shipableAUConfig数据表中该po的配置
	// 3. 生成工单有两种规则 一种是总装线 主工单 一种是预装线 子工单
	bomLstLi, err := bomLstDao.Select([]grmi.Predicate{
		meMeta.BomLst_BomId.NewPredicate(grmi.Equal, productId),
		meMeta.BomLst_ShipableAU.NewPredicate(grmi.Equal, true),
	}, nil)
	if err != nil {
		return grmi.NewBusinessError("查询BOM子项数据失败, 错误:" + err.Error())
	}
	poDataMap := make(map[string]baseModel.Process)
	allPoIdInterfaceLi := make([]interface{}, 0, len(usedPoLi))

	for _, po := range usedPoLi {
		poDataMap[po.PO] = po
		allPoIdInterfaceLi = append(allPoIdInterfaceLi, po.PO)
	}
	sfpUsedPoMap := make(map[string]interface{})
	orderPoMap := make(map[string]*model.OrderAUPoData)
	for _, bomLst := range bomLstLi {
		sfp, err := articleDao.SelectOne(bomLst.CmatNr, projectId)
		if err != nil {
			return grmi.NewBusinessError("查询物料失败, 错误:" + err.Error())
		}
		if sfp == nil {
			return grmi.NewBusinessError("半成品物料不存在, 物料ID:" + bomLst.CmatNr)
		}
		auConfig, err := shipableAUConfigDao.SelectOne(bomLst.CmatNr)
		if err != nil {
			return grmi.NewBusinessError("查询半成品配置数据失败, 错误:" + err.Error())
		}
		if auConfig == nil {
			return grmi.NewBusinessError("半成品配置不存在， 物料ID:" + bomLst.CmatNr)
		}

		sfpBomHead, err := bomHeadDao.SelectOne(bomLst.CmatNr)
		if err != nil {
			return grmi.NewBusinessError("查询物料BOM失败, error:" + err.Error())
		}
		if sfpBomHead == nil {
			return grmi.NewBusinessError("不存在对应的物料BOM, 物料ID:" + bomLst.CmatNr)
		}
		var sfpMe meModel.BomLst
		sfpMe.PlantNr = user.PlantNr
		sfpMe.BomId = sfpBomHead.BomId
		sfpBomLstLi, err := sfpMe.SetMenu(sfpBomHead.MatNr)
		if err != nil {
			return grmi.NewBusinessError("获取BOM细则失败, error:" + err.Error())
		}
		if len(sfpBomLstLi) == 0 {
			return grmi.NewBusinessError("未获取到BOM细则, 物料ID:" + bomHead.BomId)
		}
		sfpBomHead.BomLstLi = sfpBomLstLi
		// 获取关联的所有零件ID
		sfpArtMap := impl.ExtractArtId(sfpBomHead)
		sfpArtIdLi := make([]string, 0, len(sfpArtMap))
		for innerArtId := range sfpArtMap {
			sfpArtIdLi = append(sfpArtIdLi, innerArtId)
		}
		sfpArtIdLi = append(sfpArtIdLi, sfp.ArtId)
		sfpAllArticleAttributeLi, err := articleWithAttrDao.SelectWithArticleByArtIdLi(sfpArtIdLi, projectId)
		if err != nil {
			_ = session.Rollback()
			return grmi.NewBusinessError("查询半成品关联所有属性失败, 错误:" + err.Error())
		}
		auPoData, exist := poDataMap[auConfig.AuPo]
		if !exist {
			return grmi.NewBusinessError("半成品指定工序不存在于总工单的工序序列中, 半成品ID:" + sfp.ArtId)
		}
		// 根据该工序查询所有后序
		poDataLi := impl.GetOrderPOLi(auPoData, poDataMap)
		// 该工单用到的所有生产工序
		poDataLi = append(poDataLi, auPoData)
		// 查询这个工单需要用到的所有工艺数据
		poIdLi := make([]string, 0, len(poDataLi))
		for _, innerPoData := range poDataLi {
			sfpUsedPoMap[innerPoData.PO] = nil
			poIdLi = append(poIdLi, innerPoData.PO)
		}
		opLi, err := operationDao.GetMeOperation(sfpArtIdLi, projectId, poIdLi, workLineId)
		if err != nil {
			return grmi.NewBusinessError("获取工艺操作失败,error:" + err.Error())
		}
		if len(opLi) == 0 {
			return grmi.NewBusinessError("未获取到工艺数据")
		}
		// 所有需要生成工单工序绑定的数据
		orderPoMap[bomLst.CmatNr] = &model.OrderAUPoData{
			Config:         auConfig,
			MeOperationLi:  opLi,
			IsMainOrder:    false,
			SFP:            sfp,
			AllAttributeLi: sfpAllArticleAttributeLi,
			ArtRelMap:      sfpArtMap,
		}
	}
	mainOrderPOIdLi := make([]string, 0)
	for _, usedPoData := range usedPoLi {
		_, exist := sfpUsedPoMap[usedPoData.PO]
		if !exist {
			mainOrderPOIdLi = append(mainOrderPOIdLi, usedPoData.PO)
		}
	}
	if len(mainOrderPOIdLi) == 0 {
		return grmi.NewBusinessError("主工单无生产所需工序")
	}
	opLi, err := operationDao.GetMeOperation(artIdLi, projectId, mainOrderPOIdLi, workLineId)
	if err != nil {
		return grmi.NewBusinessError("获取工艺操作失败,error:" + err.Error())
	}
	if len(opLi) == 0 {
		return grmi.NewBusinessError("未获取到工艺数据")
	}

	// 所有需要生成工单工序绑定的数据
	orderPoMap[productId] = &model.OrderAUPoData{
		Config:         nil,
		MeOperationLi:  opLi,
		IsMainOrder:    true,
		SFP:            nil,
		AllAttributeLi: allArticleAttributeLi,
		ArtRelMap:      artMap,
	}

	// 获取所有使用工序的扫码校验规则 组建MAP数据结构
	scanRuleLi, err := scanRuleDao.Select([]grmi.Predicate{baseMeta.ScanRuleInfo_ProjectId.NewPredicate(grmi.Equal, projectId), baseMeta.ScanRuleInfo_PO.NewPredicate(grmi.Include, allPoIdInterfaceLi...)}, nil)
	if err != nil {
		return grmi.NewBusinessError("查询扫码校验条码失败, error:" + err.Error())
	}
	scanRuleMap := make(map[int]baseModel.ScanRuleInfo)
	for _, scanRule := range scanRuleLi {
		scanRuleMap[scanRule.OperationId] = scanRule
	}
	// 获取所有使用工序指导操作的规则 组建MAP数据结构
	guideRuleLi, err := guideDao.Select([]grmi.Predicate{baseMeta.GuideRuleInfo_ProjectId.NewPredicate(grmi.Equal, projectId), baseMeta.GuideRuleInfo_PO.NewPredicate(grmi.Include, allPoIdInterfaceLi...)}, nil)
	if err != nil {
		return grmi.NewBusinessError("查询指导操作规则失败, error:" + err.Error())
	}
	guideRuleMap := make(map[int]baseModel.GuideRuleInfo, len(guideRuleLi))
	for _, guideRule := range guideRuleLi {
		guideRuleMap[guideRule.OperationId] = guideRule
	}
	// 获取所有使用工序扫码校验规则 组建MAP数据结构
	printRuleLi, err := printRuleDao.Select([]grmi.Predicate{baseMeta.PrintBasicRuleInfo_ProjectId.NewPredicate(grmi.Equal, projectId), baseMeta.PrintBasicRuleInfo_PO.NewPredicate(grmi.Include, allPoIdInterfaceLi...)}, []grmi.Field{baseMeta.PrintBasicRuleInfo_OperationId})
	printRuleMap := make(map[int][]baseModel.PrintBasicRuleInfo)
	for _, data := range printRuleLi {
		_, exist := printRuleMap[data.OperationId]
		if !exist {
			printRuleMap[data.OperationId] = make([]baseModel.PrintBasicRuleInfo, 0)
		}
		printRuleMap[data.OperationId] = append(printRuleMap[data.OperationId], data)
	}
	// 获取所有物料属性关联关系
	attributeLi, err := articleWithAttrDao.SelectAllWithAttrValue()
	if err != nil {
		return grmi.NewBusinessError("查询零件属性失败,error:" + err.Error())
	}
	attributeMap := make(map[int][]baseModel.ArticleWithAttrValue)
	for _, data := range attributeLi {
		_, exist := attributeMap[data.ArticleAtcodLst.AttrCode]
		if !exist {
			attributeMap[data.ArticleAtcodLst.AttrCode] = make([]baseModel.ArticleWithAttrValue, 0)
		}
		attributeMap[data.ArticleAtcodLst.AttrCode] = append(attributeMap[data.ArticleAtcodLst.AttrCode], data)
	}
	// 生成序列工单对应的工艺数据
	allocateMap := make(map[string][]baseModel.ArtQtyAllocate)
	checkRuleMap := make(map[string]baseModel.ArtCheckRule)
	opcBasicMap := make(map[int][]baseModel.OPCBasicComInfo)
	twMap := make(map[int][]baseModel.TightenWrenchInfo)
	for _, serialOrder := range serialOrderLi {
		planDate := serialOrder.PlanStartDate.Restore().Format(grmi.DateOutFormat)
		schedKey := serialOrder.SchedKey
		var usedArticle *baseModel.Article
		for _, orderOPData := range orderPoMap {
			var serialOrderId string
			if !orderOPData.IsMainOrder {
				usedArticle = orderOPData.SFP
				if project == nil {
					project, err = projectDao.SelectOne(projectId)
					if err != nil {
						return grmi.NewBusinessError("查询项目失败, 错误:" + err.Error())
					}
				}
				if orderOPData.Config.BarCodeRule == baseModel.BARCODE_RULE_ACURA_PRE {

					number, err := snrDao.GetNextSnr(project.PreOrderSnr)
					if err != nil {
						return grmi.NewBusinessError("获取流水号失败，错误:" + err.Error())
					}

					serialOrderId = strings.Replace(orderOPData.SFP.ArtId, "-", "", -1) + today + number
				} else {
					return grmi.NewBusinessError("不支持的子工单条码生成规则，物料Id:" + orderOPData.SFP.ArtId)
				}
				sfpSerialOrder := model.SerialOrder{
					SerialOrderId:    serialOrderId,
					ArtId:            orderOPData.SFP.ArtId,
					ProjectId:        projectId,
					Descr:            orderOPData.SFP.Descr1,
					CustOrderId:      serialOrder.CustOrderId,
					LastPieceInBatch: serialOrder.LastPieceInBatch,
					PreSchedKey:      schedKey,
					SchedKey:         schedKey,
					OrderType:        baseModel.ORDER_TYPE_PRE,
					OrderType1:       serialOrder.OrderType1,
					OrderType2:       serialOrder.OrderType2,
					PlanResourceId:   serialOrder.PlanResourceId,
					UsedResourceId:   serialOrder.UsedResourceId,
					PlanQty:          1,
					QtyUomId:         serialOrder.QtyUomId,
					OpTimePerItem:    serialOrder.OpTimePerItem,
					TimeUomId:        serialOrder.TimeUomId,
					PlanStartDate:    serialOrder.PlanStartDate,
					PlanEndDate:      serialOrder.PlanEndDate,
					SetupStartTime:   serialOrder.SetupStartTime,
					SetupEndTime:     serialOrder.SetupEndTime,
					PlanStartTime:    serialOrder.PlanStartTime,
					PlanEndTime:      serialOrder.PlanEndTime,
					PickingFlag:      orderOPData.SFP.PickingFlag,
					MainOrderId:      serialOrder.SerialOrderId,
				}
				err = serialOrderDao.InsertOne(&sfpSerialOrder)
				if err != nil {
					return grmi.NewBusinessError("写入预装工单失败， 错误:" + err.Error())
				}
				sfpSerialOrderStatus := model.SerialOrderStatus{
					SerialOrderId: serialOrderId,
					Status:        baseModel.WO_STATUS_RELEASED,
				}
				err = serialOrderStatusDao.InsertOne(&sfpSerialOrderStatus)
				if err != nil {
					return grmi.NewBusinessError("写入预装工单状态数据失败, 错误:" + err.Error())
				}
				//schedKey++
			} else {
				usedArticle = article
				serialOrderId = serialOrder.SerialOrderId
			}
			var PO string
			processPos := 1
			opPos := 1
			for _, op := range orderOPData.MeOperationLi {
				// 生成工序操作状态表
				if PO != op.Process.PO {
					PO = op.Process.PO
					serialOrderWorkPlace := model.SerialOrderPOLst{
						SerialOrderId: serialOrderId,
						PO:            PO,
						Status:        baseModel.WO_STATUS_RELEASED,
						HasDependOn:   op.Process.HasDependOn,
					}
					err = PODao.InsertOne(&serialOrderWorkPlace)
					if err != nil {
						return grmi.NewBusinessError("插入serialOrder工序数据失败,error:" + err.Error())
					}
					processPos++
				}
				serialOrderStep := model.SerialOrderStepLst{
					PO:            op.Process.PO,
					OperationId:   op.OperationId,
					SerialOrderId: serialOrderId,
					StepNo:        op.Operation.StepNo,
					Pos:           opPos,
					SubArtId:      op.ArticleAtcodLst.ArtId,
					StepDesc:      op.Operation.StepDesc,
					AttrCode:      op.ArticleAtcodLst.AttrCode,
					AttrValue:     op.ArticleAtcodLst.AttrValue,
					StepType:      op.Operation.StepType,
					Status:        baseModel.WO_STATUS_RELEASED,
				}
				opPos++
				err = stepDao.InsertOne(&serialOrderStep)
				if err != nil {
					return grmi.NewBusinessError("插入序列工单工艺数据失败, error:" + err.Error())
				}
				if serialOrderStep.StepType == baseModel.STEP_CHECK_RULE { // 生成扫码校验的工艺数据
					scanRule, exist := scanRuleMap[serialOrderStep.OperationId]
					if !exist {
						return grmi.NewBusinessError("未获取到扫描校验详细规则, 工艺ID:" + strconv.Itoa(serialOrderStep.OperationId))
					}
					checkRuleMapPointer, allocateMapPointer, err := impl.ScanCodeCheck(user, session, checkRuleMap, allocateMap, orderOPData.ArtRelMap, &scanRule, &op, &serialOrderStep, op.Operation.ActivateInBKStation)
					if err != nil {
						return err
					}
					checkRuleMap = *checkRuleMapPointer
					allocateMap = *allocateMapPointer
				} else if serialOrderStep.StepType == baseModel.STEP_OPC_RULE { // 生成OPC通讯的工艺数据
					opcBasicMapPointer, err := impl.OPCCommunication(user, session, opcBasicMap, &serialOrderStep, attributeMap, artMap, op.Operation.ActivateInBKStation)
					if err != nil {
						return err
					}
					opcBasicMap = *opcBasicMapPointer
				} else if serialOrderStep.StepType == baseModel.STEP_TW_RULE { // 生成开发协议的工艺数据
					twMapPointer, err := impl.TightenWrenchInfo(user, session, twMap, &serialOrderStep, op.Operation.ActivateInBKStation, usedArticle)
					if err != nil {
						return err
					}
					twMap = *twMapPointer
				} else if serialOrderStep.StepType == baseModel.STEP_GUIDE_RULE {
					guideRule, exist := guideRuleMap[serialOrderStep.OperationId]
					if !exist {
						return grmi.NewBusinessError("未获取到指导操作的详细规则")
					}
					err = impl.GuideRuleInfo(user, session, &guideRule, &serialOrderStep, op.Operation.ActivateInBKStation)
					if err != nil {
						return err
					}
				} else if serialOrderStep.StepType == baseModel.STEP_PRINT_RULE {
					ruleLi, exist := printRuleMap[serialOrderStep.OperationId]
					if !exist {
						return grmi.NewBusinessError("未获取到打印规则, 工艺ID:" + strconv.Itoa(serialOrderStep.OperationId))
					}
					generateRuleLi := make([]baseModel.PrintBasicRuleInfo, 0)
					for _, rule := range ruleLi {
						if rule.Action == baseModel.PRINT_RULE_ACTION_UPDATE {
							generateRuleLi = append(generateRuleLi, rule)
						}
					}
					if len(generateRuleLi) > 1 {
						return grmi.NewBusinessError("改工步匹配到超过1条生成打印标识的打印规则")
					}
					var generateRule *baseModel.PrintBasicRuleInfo
					var printTemplate *baseModel.LabelTemplateHead
					if len(generateRuleLi) == 1 {
						generateRule = &generateRuleLi[0]
						var templateId string
						if generateRule.MainCodeName == "Product" {
							templateId = product.PrintTemplateId
						} else {
							templateId = generateRule.TemplateId
						}
						printTemplate, exist = labelTemplateMap[templateId]
						if !exist {
							return grmi.NewBusinessError("存在单据打印规则中配置的标签模板, 模板ID:" + generateRule.TemplateId)
						}
					}
					var printUsedArticle *baseModel.Article
					if op.Operation.AttrCode == -1 {
						printUsedArticle = usedArticle
					} else {
						for _, articleAttribute := range orderOPData.AllAttributeLi {
							if op.Operation.AttrCode == articleAttribute.AttrCode && op.Operation.AttrValue == articleAttribute.AttrValue {
								printUsedArticle = &articleAttribute.Article
								break
							}
						}
					}
					if printUsedArticle == nil {
						_ = session.Rollback()
						return grmi.NewBusinessError("单据打印规则没有获取到需要的物料数据, 工艺Id:" + strconv.Itoa(op.Operation.OperationId))
					}
					err = impl.PrintRuleInfo(user, session, &serialOrderStep, generateRule, printTemplate, printUsedArticle, op.Operation.ActivateInBKStation, planDate)
					if err != nil {
						return err
					}
				} else {
					return grmi.NewBusinessError("获取到的工艺数据系统暂不支持该操作类型:" + serialOrderStep.StepType)
				}
			}
		}
	}
	return nil
}

/******************************************************************************
 *
 * @Reference LAPP_ACURA_MOM_BACKEND/services/om/SerialOrderService.GetSerialOrderOperationData
 *
 ******************************************************************************/
func (impl *SerialOrderServiceImplement) GetSerialOrderOperationData(user *global.User, serialOrderId string) (interface{}, error) {
	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	// 1. 查询工单
	// 2. 通过工单查询生产的产线
	// 3. 通过产线查询末序之外的工序列表
	// 4. 通过工序查询当前工序生成工步结果
	serialOrderDao := dal.NewSerialOrderDAO(session, user.PlantNr, user.UserId)
	serialOrderPoDao := dal.NewSerialOrderPOLstDAO(session, user.PlantNr, user.UserId)
	serialOrder, err := serialOrderDao.SelectOne(serialOrderId)
	if err != nil {
		return nil, grmi.NewBusinessError("查询工单失败, error:" + err.Error())
	}
	if serialOrder == nil {
		return nil, grmi.NewBusinessError("工单不存在, 工单ID:" + serialOrderId)
	}
	var traceOrderId string
	if serialOrder.OriginSerialOrderId != "" {
		traceOrderId = serialOrder.OriginSerialOrderId
	} else {
		traceOrderId = serialOrder.SerialOrderId
	}
	articleDao := baseDal.NewArticleDAO(session, user.PlantNr, user.UserId)
	article, err := articleDao.SelectOne(serialOrder.ArtId, serialOrder.ProjectId)
	if err != nil {
		return nil, grmi.NewBusinessError("查询工单总成数据失败, error:" + err.Error())
	}
	if article == nil {
		return nil, grmi.NewBusinessError("工单绑定总成不存在, 总成ID:" + serialOrder.ArtId)
	}
	poDataLi, err := serialOrderPoDao.SelectWithPO(traceOrderId, serialOrder.UsedResourceId)
	if err != nil {
		return nil, grmi.NewBusinessError("查询工单工序操作数据失败, error:" + err.Error())
	}
	var result model.TraceData
	result.Status = true
	result.BarCode = serialOrderId
	result.IsFirstPiece = serialOrder.IsFirstPieceToggle
	result.Desc = article.Descr1
	displayPoLi := make([]string, 0)
	poStatusMap := make(map[string]string)
	for _, poData := range poDataLi {
		displayPoLi = append(displayPoLi, poData.Process.PO)
		if poData.Status == baseModel.WO_STATUS_FINISHED {
			poStatusMap[poData.Process.PO] = "成功"
		} else {
			poStatusMap[poData.Process.PO] = "失败"
			result.Status = false
		}
	}

	// 查询工序关联的所有工位
	processWorkPlaceDao := baseDal.NewProcessWorkPlaceLstDAO(session, user.PlantNr, user.UserId)
	relateWorkPlaceLi, err := processWorkPlaceDao.SelectByProcess(displayPoLi)
	if err != nil {
		return nil, grmi.NewBusinessError("查询工序关联的工位失败, error:" + err.Error())
	}
	workPlaceMap := make(map[int]string)
	ignorePOMap := make(map[string]interface{})
	for _, relateDate := range relateWorkPlaceLi {
		if relateDate.WorkPlace.TriggerFrom == baseModel.TRIGGER_FROM_PICKING {
			ignorePOMap[relateDate.ProcessWorkPlaceLst.PO] = nil
		}
		workPlaceMap[relateDate.WorkPlace.WorkPlaceNr] = relateDate.WorkPlace.WorkPlaceId
	}
	//stepDao := dal.NewSerialOrderStepLstDAO(session, user.PlantNr, user.UserId)
	//poIdLiInterface := make([]interface{}, 0, len(displayPoLi))
	selectPoIdLi := make([]string, 0, len(displayPoLi))
	for _, poId := range displayPoLi {
		_, exist := ignorePOMap[poId]
		if exist {
			continue
		}
		//poIdLiInterface = append(poIdLiInterface, poId)
		selectPoIdLi = append(selectPoIdLi, poId)
	}

	//stepLi, err := stepDao.Select([]grmi.Predicate{meta.SerialOrderStepLst_SerialOrderId.NewPredicate(grmi.Equal, serialOrderId), meta.SerialOrderStepLst_PO.NewPredicate(grmi.Include, poIdLiInterface...)}, nil)
	//if err != nil {
	//	return nil, grmi.NewBusinessError("查询工单工步数据失败, error:" + err.Error())
	//}

	//for _, step := range stepLi {
	//	if step.Status != baseModel.WO_STATUS_FINISHED {
	//		result.Status = false
	//	}
	//}
	//poDataLi, err := serialOrderPoDao.Select([]grmi.Predicate{meta.SerialOrderPOLst_SerialOrderId.NewPredicate(grmi.Equal, serialOrderId), meta.SerialOrderPOLst_PO.NewPredicate(grmi.Include, poIdLiInterface...)}, nil)
	//if err != nil {
	//	return nil, grmi.NewBusinessError("查询工单工序操作数据失败, error:" + err.Error())
	//}

	articleAtocdDao := baseDal.NewArticleAtcodLstDAO(session, user.PlantNr, user.UserId)
	var needEOL bool
	var needSBR bool
	attributeLi, err := articleAtocdDao.SelectByArtId(serialOrder.ArtId)
	if err != nil {
		return nil, grmi.NewBusinessError("查询总成关联属性失败, error:" + err.Error())
	}
	for _, attributeItem := range attributeLi {
		if attributeItem.Attribute.Descr == "EOL" {
			needEOL = true
		} else if attributeItem.Attribute.Descr == "SBR检测" {
			needSBR = true
		}
	}
	// 通过工序和工单ID查询工步操作结果
	opDao := dal.NewSerialOrderOPDetailRecvDataLstDAO(session, user.PlantNr, user.UserId)
	result.IsFirstPiece = serialOrder.IsFirstPieceToggle
	result.TraceDataTraceData = make([]model.POOperationData, 0)
	for _, poId := range selectPoIdLi {
		opLi, err := opDao.GetOpRecvDataByPO(traceOrderId, poId)
		if err != nil {
			return nil, grmi.NewBusinessError("查询工艺操作数据失败, error:" + err.Error())
		}
		var poOperationData model.POOperationData
		poOperationData.PO = poId
		poOperationData.Status = poStatusMap[poId]
		poOperationData.RecvDataLi = make([]model.RecvDataItem, 0)
		var preOperationId int
		var preOriginPos int
		for index, opData := range opLi {
			if index == 0 {
				preOperationId = opData.Operation.OperationId
				preOriginPos = opData.SerialOrderOPDetailRecvDataLst.OriginPos
			} else {
				if preOperationId == opData.Operation.OperationId && preOriginPos == opData.SerialOrderOPDetailRecvDataLst.OriginPos {
					poOperationData.RecvDataLi = poOperationData.RecvDataLi[:len(poOperationData.RecvDataLi)-1]
				}
			}
			preOperationId = opData.Operation.OperationId
			preOriginPos = opData.SerialOrderOPDetailRecvDataLst.OriginPos
			var operationDesc string
			if opData.SerialOrderOPDetail.StepType == baseModel.STEP_OPC_RULE {
				operationDesc = opData.OPCComRule.DBlockDesc
			} else if opData.SerialOrderOPDetail.StepType == baseModel.STEP_TW_RULE {
				operationDesc = opData.TightenWrenchRuleInfo.StepDesc
			} else {
				operationDesc = opData.Operation.StepDesc
			}
			var status string
			if opData.SerialOrderOPDetailRecvDataLst.Status == 1 {
				status = "成功"
			} else {
				status = "失败"
			}
			recvData := model.RecvDataItem{
				SerialOrderId: opData.SerialOrderOPDetailRecvDataLst.SerialOrderId,
				ArtId:         opData.SerialOrderOPDetailRecvDataLst.SubArtId,
				PO:            opData.SerialOrderOPDetailRecvDataLst.PO,
				OperationDesc: operationDesc,
				RecvData:      opData.SerialOrderOPDetailRecvDataLst.RecvData,
				Status:        status,
				Remark1:       opData.SerialOrderOPDetailRecvDataLst.Remark1,
				ExecuteTime:   opData.SerialOrderOPDetailRecvDataLst.CreateTime.ToString(),
			}
			poOperationData.RecvDataLi = append(poOperationData.RecvDataLi, recvData)
		}
		result.TraceDataTraceData = append(result.TraceDataTraceData, poOperationData)
	}
	if needEOL {
		// 电检 主驾和副驾需要查询EOLDR 和 EOLPA 两张表 后排 查询EOLBR
		eolStatus, remark, recordTime, err := impl.ExtractEOL(session, user, serialOrder)
		if err != nil {
			return nil, err
		}
		var poOperationData model.POOperationData
		poOperationData.PO = "电检"
		if eolStatus {
			poOperationData.Status = "成功"
		} else {
			poOperationData.Status = "失败"
		}
		recvData := model.RecvDataItem{
			SerialOrderId: serialOrderId,
			ArtId:         "",
			PO:            "EOL",
			OperationDesc: "电检",
			Status:        poOperationData.Status,
			RecvData:      "",
			Remark1:       remark,
			ExecuteTime:   recordTime.Format(grmi.DateTimeOutFormat),
		}

		poOperationData.RecvDataLi = append(poOperationData.RecvDataLi, recvData)
		result.TraceDataTraceData = append(result.TraceDataTraceData, poOperationData)
		if eolStatus {
			serialOrder.EOLCheck = model.EOL_STATUS_OK
		} else {
			serialOrder.EOLCheck = model.EOL_STATUS_NG
		}
		err = serialOrderDao.UpdateOne(serialOrder)
		if err != nil {
			return nil, grmi.NewBusinessError("将电检结果保存到工单上失败, 错误:" + err.Error())
		}
	}
	if needSBR {

	}
	return result, nil
}

// ExtractEOL 提取工单的电检数据
func (impl *SerialOrderServiceImplement) ExtractEOL(session *xorm.Session, user *global.User, serialOrder *model.SerialOrder) (status bool, remark string, recordT time.Time, err error) {
	eolEngine := db.Eloquent.Slaves()[0]
	eolSession := eolEngine.NewSession()
	eolDrDao := eolDal.NewEOLDRDAO(eolSession, user.UserId)
	eolPaDao := eolDal.NewEOLPADAO(eolSession, user.UserId)
	eolBrDao := eolDal.NewEOLBRDAO(eolSession, user.UserId)
	productDao := meDal.NewProductDAO(session, user.PlantNr, user.UserId)
	// 查询工单是否有返修记录 如果有返修记录 需要在返修记录后有电检数据
	var repairTime time.Time
	repairInfoDao := baseDal.NewRepairInfoDAO(session, user.UserId)
	repairInfoLi, err := repairInfoDao.Select([]grmi.Predicate{
		baseMeta.RepairInfo_SerialOrderId.NewPredicate(grmi.Equal, serialOrder.SerialOrderId),
	}, []grmi.Field{baseMeta.RepairInfo_CreateTime})
	if err != nil {
		return false, "", recordT, grmi.NewBusinessError("查询工单返修记录失败, 错误:" + err.Error())
	}
	if len(repairInfoLi) != 0 {
		repairTime = repairInfoLi[len(repairInfoLi)-1].CreateTime.Restore()
	}
	product, err := productDao.SelectOne(serialOrder.ArtId, serialOrder.ProjectId)
	if err != nil {
		return false, "", recordT, grmi.NewBusinessError("查询工单总成信息失败, 错误:" + err.Error())
	}
	if product == nil {
		return false, "", recordT, grmi.NewBusinessError("工单总成数据不存在")
	}
	if product.CtrlStr1 == meModel.PRODUCT_TYPE_MS || product.CtrlStr1 == meModel.PRODUCT_TYPE_PS {
		drLi, err := eolDrDao.Select([]grmi.Predicate{eolMeta.EOLDR_CODE.NewPredicate(grmi.Equal, serialOrder.SerialOrderId),
			eolMeta.EOLDR_TESTDATA.NewPredicate(grmi.GreaterOrEqual, repairTime)}, []grmi.Field{eolMeta.EOLDR_TESTDATA})
		if err != nil {
			return false, "", recordT, grmi.NewBusinessError("查询电检数据失败, 错误：" + err.Error())
		}
		paLi, err := eolPaDao.Select([]grmi.Predicate{eolMeta.EOLPA_CODE.NewPredicate(grmi.Equal, serialOrder.SerialOrderId),
			eolMeta.EOLPA_TESTDATA.NewPredicate(grmi.GreaterOrEqual, repairTime)}, []grmi.Field{eolMeta.EOLPA_TESTDATA})
		if err != nil {
			return false, "", recordT, grmi.NewBusinessError("查询电检数据失败, 错误:" + err.Error())
		}
		var lastDr *eolModel.EOLDR
		var lastPa *eolModel.EOLPA
		if len(drLi) > 0 {
			lastDr = &drLi[len(drLi)-1]
		}
		if len(paLi) > 0 {
			lastPa = &paLi[len(paLi)-1]
		}
		var totalResult string
		var recordTime time.Time
		if lastDr == nil && lastPa == nil {
			return false, "未获取到有效电检数据", recordT, nil
		} else if lastDr != nil && lastPa != nil {
			if lastDr.TESTDATA.Restore().After(lastPa.TESTDATA.Restore()) {
				totalResult = lastDr.TOTALRESULT
				recordTime = lastDr.TESTDATA.Restore()
			} else {
				totalResult = lastPa.TOTALRESULT
				recordTime = lastPa.TESTDATA.Restore()
			}
		} else if lastDr != nil && lastPa == nil {
			totalResult = lastDr.TOTALRESULT
			recordTime = lastDr.TESTDATA.Restore()
		} else if lastDr == nil && lastPa != nil {
			totalResult = lastPa.TOTALRESULT
			recordTime = lastPa.TESTDATA.Restore()
		}
		if totalResult == model.EOL_STATUS_OK {
			return true, "电检合格", recordTime, nil
		} else {
			return false, "电检不合格", recordTime, nil
		}
	} else {
		brLi, err := eolBrDao.Select([]grmi.Predicate{eolMeta.EOLBR_CODE.NewPredicate(grmi.Equal, serialOrder.SerialOrderId),
			eolMeta.EOLBR_TESTDATA.NewPredicate(grmi.GreaterOrEqual, repairTime)}, []grmi.Field{eolMeta.EOLBR_TESTDATA})
		if err != nil {
			return false, "", recordT, grmi.NewBusinessError("查询电检数据失败, 错误:" + err.Error())
		}
		if len(brLi) == 0 {
			return false, "未获取到有效电检数据", recordT, nil
		}
		lastBr := brLi[len(brLi)-1]
		if lastBr.TOTALRESULT == model.EOL_STATUS_OK {
			return true, "电检合格", lastBr.TESTDATA.Restore(), nil
		} else {
			return false, "电检不合格", lastBr.TESTDATA.Restore(), nil
		}
	}
}

// GetSerialOrderOperationTraceData 工单数据追溯
func (impl *SerialOrderServiceImplement) GetSerialOrderOperationTraceData(user *global.User, serialOrderId string) (interface{}, error) {
	var totalResult = &model.TraceData{
		BarCode: serialOrderId,
		Status:  true,
	}
	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	result := model.ProductTraceData{
		SerialOrderId: serialOrderId,
		POLi:          make([]model.ProductTracePOData, 0),
	}
	// 1. 查询工单
	// 2. 通过工单查询生产的产线
	// 4. 通过工序查询当前工序生成工步结果
	serialOrderDao := dal.NewSerialOrderDAO(session, user.PlantNr, user.UserId)
	serialOrderPoDao := dal.NewSerialOrderPOLstDAO(session, user.PlantNr, user.UserId)
	serialOrderStepDao := dal.NewSerialOrderStepLstDAO(session, user.PlantNr, user.UserId)
	recvDataDao := dal.NewSerialOrderOPDetailRecvDataLstDAO(session, user.PlantNr, user.UserId)
	serialOrder, err := serialOrderDao.SelectOne(serialOrderId)
	if err != nil {
		return nil, grmi.NewBusinessError("查询工单失败, error:" + err.Error())
	}
	if serialOrder == nil {
		return nil, grmi.NewBusinessError("工单不存在, 工单ID:" + serialOrderId)
	}

	articleDao := baseDal.NewArticleDAO(session, user.PlantNr, user.UserId)
	article, err := articleDao.SelectOne(serialOrder.ArtId, serialOrder.ProjectId)
	if err != nil {
		return nil, grmi.NewBusinessError("查询工单总成数据失败, error:" + err.Error())
	}
	if article == nil {
		return nil, grmi.NewBusinessError("工单绑定总成不存在, 总成ID:" + serialOrder.ArtId)
	}
	articleAtocdDao := baseDal.NewArticleAtcodLstDAO(session, user.PlantNr, user.UserId)
	var needEOL bool
	attributeLi, err := articleAtocdDao.SelectByArtId(serialOrder.ArtId)
	if err != nil {
		return nil, grmi.NewBusinessError("查询总成关联属性失败, error:" + err.Error())
	}
	for _, attributeItem := range attributeLi {
		if attributeItem.Attribute.Descr == "EOL" {
			needEOL = true
		}
	}
	totalResult.Desc = article.Descr1
	totalResult.IsFirstPiece = serialOrder.IsFirstPieceToggle
	// 所有工单ID
	allOrderIdLi := make([]interface{}, 0)
	if serialOrder.OriginSerialOrderId != "" {
		subSerialOrderLi, err := serialOrderDao.Select([]grmi.Predicate{
			meta.SerialOrder_SerialOrderId.NewPredicate(grmi.NotEqual, serialOrderId),
			meta.SerialOrder_OriginSerialOrderId.NewPredicate(grmi.Equal, serialOrder.OriginSerialOrderId),
		}, nil)
		if err != nil {
			return nil, grmi.NewBusinessError("查询工单数据失败, 错误:" + err.Error())
		}
		allOrderIdLi = append(allOrderIdLi, serialOrderId, serialOrder.OriginSerialOrderId)
		for _, subOrder := range subSerialOrderLi {
			allOrderIdLi = append(allOrderIdLi, subOrder.SerialOrderId)
		}
	} else {
		allOrderIdLi = append(allOrderIdLi, serialOrder.SerialOrderId)
	}

	allPoLi, err := serialOrderPoDao.SelectByOrderLi(allOrderIdLi, serialOrder.UsedResourceId)
	if err != nil {
		return nil, grmi.NewBusinessError("查询工序数据失败, 错误:" + err.Error())
	}
	poMap := make(map[string]int)
	for _, poItem := range allPoLi {
		var status string
		if poItem.Status == baseModel.ORDER_STATUS_CLOSED {
			status = "成功"
		} else {
			status = "失败"
		}
		_, exist := poMap[poItem.Process.PO]
		if !exist {
			poMap[poItem.Process.PO] = len(result.POLi)
			poData := model.ProductTracePOData{
				PO:     poItem.Process.PO,
				PoDesc: poItem.Process.Descr,
				PoStatus: status,
				StepLi: make([]model.ProductTraceStepData, 0),
			}
			result.POLi = append(result.POLi, poData)
		} else {
			index := poMap[poItem.Process.PO]
			originPoData := result.POLi[index]
			originPoData.PoStatus = status
			result.POLi[index] = originPoData
		}
	}
	stepLi, err := serialOrderStepDao.Select([]grmi.Predicate{
		meta.SerialOrderStepLst_SerialOrderId.NewPredicate(grmi.Include, allOrderIdLi...),
	}, []grmi.Field{meta.SerialOrderStepLst_StepNo})
	if err != nil {
		return nil, grmi.NewBusinessError("查询工步数据失败, 错误:" + err.Error())
	}
	stepMap := make(map[string]int)
	for _, stepItem := range stepLi {
		mapKey := stepItem.PO + "_" + strconv.Itoa(stepItem.StepNo)
		_, exist := stepMap[mapKey]
		if !exist {
			poIndex := poMap[stepItem.PO]
			stepMap[mapKey] = len(result.POLi[poIndex].StepLi)
			stepData := model.ProductTraceStepData{
				StepNo:     stepItem.StepNo,
				StepDesc:   stepItem.StepDesc,
				RecvDataLi: make([]model.SerialOrderOPDetailRecvDataLst, 0),
			}
			result.POLi[poIndex].StepLi = append(result.POLi[poIndex].StepLi, stepData)
		}
	}

	recvDataLi, err := recvDataDao.Select([]grmi.Predicate{
		meta.SerialOrderOPDetailRecvDataLst_SerialOrderId.NewPredicate(grmi.Include, allOrderIdLi...),
	}, []grmi.Field{
		meta.SerialOrderOPDetailRecvDataLst_SerialOrderId,
		meta.SerialOrderOPDetailRecvDataLst_PO,
		meta.SerialOrderOPDetailRecvDataLst_StepNo,
		meta.SerialOrderOPDetailRecvDataLst_OriginRuleId,
	})
	if err != nil {
		return nil, grmi.NewBusinessError("查询生产数据失败, 错误:" + err.Error())
	}
	for _, recvData := range recvDataLi {
		poIndex, exist := poMap[recvData.PO]
		if !exist {
			continue
		}
		stepKey := recvData.PO + "_" + strconv.Itoa(recvData.StepNo)
		stepIndex, exist := stepMap[stepKey]

		if !exist {
			continue
		}
		result.POLi[poIndex].StepLi[stepIndex].RecvDataLi = append(result.POLi[poIndex].StepLi[stepIndex].RecvDataLi, recvData)
	}
	for _, poData := range result.POLi {
		var poOperationData model.POOperationData
		poOperationData.PO = poData.PO
		poOperationData.Status = poData.PoStatus
		if poData.PoStatus == "失败" {
			totalResult.Status = false
		}
		for _, stepData := range poData.StepLi {
			for _, recvItem := range stepData.RecvDataLi {
				var status string
				if recvItem.Status == 1 {
					status = "成功"
				} else {
					status = "失败"
				}
				recvData := model.RecvDataItem{
					SerialOrderId: recvItem.SerialOrderId,
					ArtId:         recvItem.SubArtId,
					PO:            recvItem.PO,
					OperationDesc: stepData.StepDesc,
					RecvData:      recvItem.RecvData,
					Status:        status,
					Remark1:       recvItem.Remark1,
					ExecuteTime:   recvItem.CreateTime.ToString(),
				}
				poOperationData.RecvDataLi = append(poOperationData.RecvDataLi, recvData)
			}
		}
		totalResult.TraceDataTraceData = append(totalResult.TraceDataTraceData, poOperationData)
	}
	if needEOL {
		var poOperationData model.POOperationData
		poOperationData.PO = "电检"
		// 电检 主驾和副驾需要查询EOLDR 和 EOLPA 两张表 后排 查询EOLBR
		eolStatus, remark, recordTime, err := impl.ExtractEOL(session, user, serialOrder)
		if err != nil {
			return nil, err
		}
		if eolStatus {
			poOperationData.Status = "成功"
		} else {
			poOperationData.Status = "失败"
			totalResult.Status = false
		}
		recvData := model.RecvDataItem{
			SerialOrderId: serialOrderId,
			ArtId:         "",
			PO:            "EOL",
			OperationDesc: "电检",
			Status:        poOperationData.Status,
			RecvData:      "",
			Remark1:       remark,
			ExecuteTime:   recordTime.Format(grmi.DateTimeOutFormat),
		}

		poOperationData.RecvDataLi = append(poOperationData.RecvDataLi, recvData)
		totalResult.TraceDataTraceData = append(totalResult.TraceDataTraceData, poOperationData)
		if eolStatus {
			serialOrder.EOLCheck = model.EOL_STATUS_OK
		} else {
			serialOrder.EOLCheck = model.EOL_STATUS_NG
		}
		err = serialOrderDao.UpdateOne(serialOrder)
		if err != nil {
			return nil, grmi.NewBusinessError("将电检结果保存到工单上失败, 错误:" + err.Error())
		}
	}
	return totalResult, nil
}

/******************************************************************************
*
* @Reference LAPP_LF_MOM_BACKEND/services/om/SerialOrderService.GetSerialOrderOPTrace
*
******************************************************************************/
func (impl *SerialOrderServiceImplement) GetSerialOrderOPTrace(user *global.User, status int, workLineId, start, end, serialOrderId string, pageNumber int64, pageSize int64) (interface{}, error) {
	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	var exist bool
	serialOrderPoDao := dal.NewSerialOrderPOLstDAO(session, user.PlantNr, user.UserId)
	serialOrderDao := dal.NewSerialOrderDAO(session, user.PlantNr, user.UserId)
	poStatusRecDao := dal.NewSerialOrderPOStatusRecLstDAO(session, user.PlantNr, user.UserId)
	//roleRelDao := baseDal.NewRoleRelDAO(session, user.PlantNr, user.UserId)
	workLineDao := baseDal.NewWorkLineDAO(session, user.PlantNr, user.UserId)
	if workLineId == "" && serialOrderId == "" {
		//relLi, err := roleRelDao.Select([]grmi.Predicate{baseMeta.RoleRel_RoleId.NewPredicate(grmi.Equal, user.RoleId)}, []grmi.Field{baseMeta.RoleRel_WorkLineid})
		//if err != nil {
		//	return nil, grmi.NewBusinessError("查询角色绑定产线失败, error:" + err.Error())
		//}
		//if len(relLi) == 0 {
		//	return nil, grmi.NewBusinessError("角色未关联产线")
		//}
		//workLineId = relLi[0].WorkLineid
		workLineLi, err := workLineDao.Select([]grmi.Predicate{baseMeta.WorkLine_GroupType.NewPredicate(grmi.Equal, baseModel.WORK_LINE_TYPE_REAL)}, nil)
		if err != nil {
			return nil, grmi.NewBusinessError("查询工厂产线数据失败, error:" + err.Error())
		}
		if len(workLineLi) == 0 {
			return nil, grmi.NewBusinessError("未查询到实体产线")
		}
		workLineId = workLineLi[0].WorkLineid
	}
	pageResult, err := serialOrderDao.SelectTraceOrder(workLineId, serialOrderId, start, end, status, pageNumber, pageSize)
	if err != nil {
		return nil, grmi.NewBusinessError("查询工单数据失败, error:" + err.Error())
	}
	serialOrderDataLi := pageResult.Records.([]model.VOmSerialorder)
	// 构建响应数据
	// 标题头 用来存储最后传递给前端的抬头
	titleLi := make([]string, 0)
	// 用于存储po的map
	titleMap := make(map[string]interface{})
	// 每个工单工序格子显示的数据
	type POData struct {
		PO        string `json:"po"`
		StartTime string `json:"startTime"`
		Status    int    `json:"status"`
	}
	// 每行工单聚合的工序数据
	type SerialOrderPOLiData struct {
		SerialOrderId string   `json:"serialOrderId"`
		POLi          []POData `json:"poLi"`
	}
	// 返回给前端的结果
	type Result struct {
		TitleLi    []string              `json:"titleLi"` // 展示的标题头
		Data       []SerialOrderPOLiData `json:"data"`    // 展示的工单数据列表
		Count      int64                 `json:"count"`   // 工单总数量
		PageNumber int64                 `json:"pageNumber"`
		PageSize   int64                 `json:"pageSize"`
	}
	// 临时的工单工序数据 因为标题工序的排序不确定 先做成map
	type TempSerialOrderPOItem struct {
		SerialOrderId string
		ItemMap       map[string]POData
	}
	tempLi := make([]TempSerialOrderPOItem, 0, len(serialOrderDataLi))
	//	 遍历需要展示工单列表
	for _, serialOrderData := range serialOrderDataLi {
		var tempItem TempSerialOrderPOItem
		tempItem.SerialOrderId = serialOrderData.SerialOrder.SerialOrderId
		tempItem.ItemMap = make(map[string]POData)
		var traceOrderId string
		if serialOrderData.SerialOrder.OriginSerialOrderId != "" {
			traceOrderId = serialOrderData.SerialOrder.OriginSerialOrderId
		} else {
			traceOrderId = serialOrderData.SerialOrder.SerialOrderId
		}
		// 查询工单的所有工序状态结果
		SerialOrderPOLi, err := serialOrderPoDao.SelectBySerialOrderId(traceOrderId, serialOrderData.SerialOrder.UsedResourceId)
		if err != nil {
			return nil, grmi.NewBusinessError("查询工单工序数据失败, error:" + err.Error())
		}
		// 查询工单的所有工序状态变更记录 由于计算每个工序的开始时间
		poStatusRecLi, err := poStatusRecDao.Select([]grmi.Predicate{meta.SerialOrderPOStatusRecLst_SerialOrderId.NewPredicate(grmi.Equal, traceOrderId), meta.SerialOrderPOStatusRecLst_Status.NewPredicate(grmi.Equal, baseModel.ORDER_STATUS_RUNNING)}, []grmi.Field{meta.SerialOrderPOStatusRecLst_Pos})
		if err != nil {
			return nil, grmi.NewBusinessError("查询工序开始时间失败, error:" + err.Error())
		}
		poStatusRecMap := make(map[string][]time.Time)
		for _, statusRec := range poStatusRecLi {
			_, exist := poStatusRecMap[statusRec.PO]
			if !exist {
				poStatusRecMap[statusRec.PO] = make([]time.Time, 0)
			}
			poStatusRecMap[statusRec.PO] = append(poStatusRecMap[statusRec.PO], statusRec.CreateTime.Restore())
		}
		for _, serialOrderPO := range SerialOrderPOLi {
			_, exist = titleMap[serialOrderPO.PO]
			if !exist {
				titleMap[serialOrderPO.PO] = nil
				titleLi = append(titleLi, serialOrderPO.PO)
			}
			var poData POData
			poData.PO = serialOrderPO.PO
			poData.Status = serialOrderPO.Status
			_, exist = poStatusRecMap[serialOrderPO.PO]
			if exist {
				poData.StartTime = poStatusRecMap[serialOrderPO.PO][0].Format(grmi.DateTimeOutFormat)
			}
			tempItem.ItemMap[serialOrderPO.PO] = poData
		}
		tempLi = append(tempLi, tempItem)
	}

	var result Result
	result.TitleLi = titleLi
	result.Data = make([]SerialOrderPOLiData, 0)
	result.Count = pageResult.Count
	result.PageNumber = pageResult.PageNumber
	result.PageSize = pageResult.PageSize
	titleNum := len(titleLi)
	// 此时标题头部已经排好顺序 开始聚合数据
	for _, tempItem := range tempLi {
		var serialOrderPOData SerialOrderPOLiData
		serialOrderPOData.SerialOrderId = tempItem.SerialOrderId
		serialOrderPOData.POLi = make([]POData, 0, titleNum)
		for _, title := range titleLi {
			_, exist = tempItem.ItemMap[title]
			if exist {
				serialOrderPOData.POLi = append(serialOrderPOData.POLi, tempItem.ItemMap[title])
			} else {
				serialOrderPOData.POLi = append(serialOrderPOData.POLi, POData{PO: title})
			}
		}
		result.Data = append(result.Data, serialOrderPOData)
	}
	return result, nil
}

/******************************************************************************
*
* @Reference LAPP_LF_MOM_BACKEND/services/om/SerialOrderService.GetSerialOrderDetailTrace
*
******************************************************************************/
func (impl *SerialOrderServiceImplement) GetSerialOrderDetailTrace(user *global.User, serialOrderId string) (interface{}, error) {
	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	result := make([]model.SerialOrderOPTrace, 0)
	opDao := dal.NewSerialOrderOPDetailRecvDataLstDAO(session, user.PlantNr, user.UserId)
	workPlaceDao := baseDal.NewWorkPlaceDAO(session, user.PlantNr, user.UserId)
	serialOrderDao := dal.NewSerialOrderDAO(session, user.PlantNr, user.UserId)
	serialOrder, err := serialOrderDao.SelectOne(serialOrderId)
	if err != nil {
		return nil, grmi.NewBusinessError("查询工单失败, error:" + err.Error())
	}
	if serialOrder == nil {
		return nil, grmi.NewBusinessError("工单不存在, 工单ID:" + serialOrderId)
	}
	var traceOrderId string
	if serialOrder.OriginSerialOrderId != "" {
		traceOrderId = serialOrder.OriginSerialOrderId
	} else {
		traceOrderId = serialOrderId
	}
	opLi, err := opDao.SelectOPDetailData(traceOrderId, serialOrder.UsedResourceId)
	if err != nil {
		return nil, grmi.NewBusinessError("查询工艺操作数据失败, error:" + err.Error())
	}
	workPlaceNrMap := make(map[int]interface{})
	workPlaceNrLi := make([]interface{}, 0)
	for _, op := range opLi {
		_, exist := workPlaceNrMap[op.SerialOrderOPDetailRecvDataLst.WorkPlaceNr]
		if !exist {
			workPlaceNrMap[op.SerialOrderOPDetailRecvDataLst.WorkPlaceNr] = nil
			workPlaceNrLi = append(workPlaceNrLi, op.SerialOrderOPDetailRecvDataLst.WorkPlaceNr)
		}
	}
	workPlaceLi, err := workPlaceDao.Select([]grmi.Predicate{baseMeta.WorkPlace_WorkPlaceNr.NewPredicate(grmi.Include, workPlaceNrLi...)}, nil)
	if err != nil {
		return nil, grmi.NewBusinessError("查询工位数据失败, error:" + err.Error())
	}
	workPlaceMap := make(map[int]string)
	for _, workPlace := range workPlaceLi {
		workPlaceMap[workPlace.WorkPlaceNr] = workPlace.WorkPlaceId
	}
	for _, op := range opLi {
		if op.SerialOrderOPDetailRecvDataLst.StepType == baseModel.STEP_GUIDE_RULE {
			continue
		}
		if op.SerialOrderOPDetailRecvDataLst.StepType == baseModel.STEP_OPC_RULE && (op.OPCComRule.ActionType == baseModel.ACTION_TYPE_VERIFY || op.OPCComRule.ActionType == baseModel.ACTION_TYPE_WRITE) {
			continue
		}
		var stepDesc string
		if op.SerialOrderOPDetail.StepType == baseModel.STEP_OPC_RULE {
			stepDesc = op.OPCComRule.DBlockDesc
		} else {
			stepDesc = op.Operation.StepDesc
		}
		var status string
		if op.SerialOrderOPDetailRecvDataLst.Status == 1 {
			status = "是"
		} else {
			status = "否"
		}
		workPlaceId := workPlaceMap[op.SerialOrderOPDetailRecvDataLst.WorkPlaceNr]

		trace := model.SerialOrderOPTrace{
			ArtId:         op.SerialOrderOPDetail.SubArtId,
			SerialOrderId: op.SerialOrderOPDetailRecvDataLst.SerialOrderId,
			PO:            op.SerialOrderOPDetailRecvDataLst.PO,
			WorkPlaceId:   workPlaceId,
			StepDesc:      stepDesc,
			RecvData:      op.SerialOrderOPDetailRecvDataLst.RecvData,
			Status:        status,
			ExecuteTime:   op.SerialOrderOPDetailRecvDataLst.CreateTime.ToString(),
		}
		if status != "是" {
			trace.Remark1 = op.SerialOrderOPDetailRecvDataLst.Remark1
		}
		result = append(result, trace)
	}
	return result, nil
}

/******************************************************************************
 *
 * @Reference LAPP_ACURA_MOM_BACKEND/services/om/SerialOrderService.CreateBatchSerialOrder
 *
 ******************************************************************************/
func (impl *SerialOrderServiceImplement) CreateBatchSerialOrder(user *global.User, productId string, projectId string, workLineId string, qty int, preSchKey int, priority int) error {
	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	productDao := meDal.NewProductDAO(session, user.PlantNr, user.UserId)
	serialOrderDao := dal.NewSerialOrderDAO(session, user.PlantNr, user.UserId)
	serialOrderStatusDao := dal.NewSerialOrderStatusDAO(session, user.PlantNr, user.UserId)
	snrDao := baseDal.NewSnrDAO(session, user.PlantNr, user.UserId)
	projectDao := meDal.NewProjectDAO(session, user.PlantNr, user.UserId)
	product, err := productDao.SelectOne(productId, projectId)
	if err != nil {
		return grmi.NewBusinessError("查询总成数据失败, error:" + err.Error())
	}
	if product == nil {
		return grmi.NewBusinessError("总成不存在")
	}
	project, err := projectDao.SelectOne(projectId)
	if err != nil {
		return grmi.NewBusinessError("查询项目数据失败, error:" + err.Error())
	}
	if product.MultiWorkLineToggle {
		productWorkLineDao := meDal.NewProductWorkLineLstDAO(session, user.PlantNr, user.UserId)
		workLineLi, err := productWorkLineDao.Select([]grmi.Predicate{
			meMeta.ProductWorkLineLst_ProductId.NewPredicate(grmi.Equal, productId),
			meMeta.ProductWorkLineLst_ProjectId.NewPredicate(grmi.Equal, projectId),
		}, nil)
		if err != nil {
			return grmi.NewBusinessError("查询总成可生产产线失败, 错误:" + err.Error())
		}
		var canProduct bool
		for _, workLine := range workLineLi {
			if workLine.WorkLineId == workLineId {
				canProduct = true
				break
			}
		}
		if !canProduct {
			return grmi.NewBusinessError("该总成不能在指定的产线上生产！")
		}
	} else {
		if product.DefaultWorkLineId != workLineId {
			return grmi.NewBusinessError("该总成不能在指定的产线上生产！")
		}
	}
	insertSerialOrderLi := make([]model.SerialOrder, 0, qty)
	if err = session.Begin(); err != nil {
		return grmi.NewBusinessError("开启事务失败, 错误:" + err.Error())
	}
	if priority != 0 {
		now := time.Now()
		for i := 1; i <= qty; i++ {
			serialNumber, err := snrDao.GetNextSnr(project.INTSerialOrderSnr)
			if err != nil {
				_ = session.Rollback()
				return grmi.NewBusinessError("生成工单流水号失败, error:" + err.Error())
			}
			serialOrderId := product.MachineCode + serialNumber + product.ProductType + product.ColorValue
			insertOrder := model.SerialOrder{
				SerialOrderId:  serialOrderId,
				ArtId:          productId,
				ProjectId:      projectId,
				Descr:          product.Descr,
				PreSchedKey:    0,
				SchedKey:       0,
				Priority:       priority,
				OrderType:      baseModel.ODER_TYPE_INTERVAL,
				PlanResourceId: workLineId,
				UsedResourceId: workLineId,
				PlanQty:        1,
				PlanStartDate:  grmi.Date(now),
				PlanEndDate:    grmi.Date(now),
				PlanStartTime:  grmi.DateTime(now),
				PlanEndTime:    grmi.DateTime(now),
				PickingFlag:    product.PickingFlag,
			}
			err = serialOrderDao.InsertOne(&insertOrder)
			if err != nil {
				_ = session.Rollback()
				return grmi.NewBusinessError("写入工单数据失败, error:" + err.Error())
			}
			insertSerialOrderLi = append(insertSerialOrderLi, insertOrder)
			insertOrderStatus := model.SerialOrderStatus{
				SerialOrderId: serialOrderId,
				Status:        baseModel.WO_STATUS_RELEASED,
			}
			err = serialOrderStatusDao.InsertOne(&insertOrderStatus)
			if err != nil {
				_ = session.Rollback()
				return grmi.NewBusinessError("写入工单状态数据失败, error:" + err.Error())
			}
		}
	} else {
		serialOrder, err := serialOrderDao.Select([]grmi.Predicate{meta.SerialOrder_SchedKey.NewPredicate(grmi.Equal, preSchKey)}, nil)
		if err != nil {
			_ = session.Rollback()
			return grmi.NewBusinessError("查询工单数据失败, error:" + err.Error())
		}
		if serialOrder == nil {
			_ = session.Rollback()
			return grmi.NewBusinessError("指定前序的工单不存在")
		}
		schedKeyLi := make([]interface{}, 0, qty)
		for i := 1; i <= qty; i++ {
			schedKeyLi = append(schedKeyLi, preSchKey+i)
		}
		serialOrderLi, err := serialOrderDao.Select([]grmi.Predicate{meta.SerialOrder_SchedKey.NewPredicate(grmi.Include, schedKeyLi...)}, nil)
		if err != nil {
			_ = session.Rollback()
			return grmi.NewBusinessError("查询工单数据失败, error:" + err.Error())
		}
		if len(serialOrderLi) > 0 {
			_ = session.Rollback()
			return grmi.NewBusinessError("生成的排序Key已存在工单")
		}
		now := time.Now()
		for i := 1; i <= qty; i++ {
			serialNumber, err := snrDao.GetNextSnr(project.INTSerialOrderSnr)
			if err != nil {
				_ = session.Rollback()
				return grmi.NewBusinessError("生成工单流水号失败, error:" + err.Error())
			}
			serialOrderId := product.MachineCode + serialNumber + product.ProductType + product.ColorValue
			if int64(preSchKey+i)%100 == 0 {
				return grmi.NewBusinessError("该调度Key为序列工单预留, 调度Key:" + strconv.Itoa(preSchKey+i))
			}
			insertOrder := model.SerialOrder{
				SerialOrderId:  serialOrderId,
				ArtId:          productId,
				ProjectId:      projectId,
				Descr:          product.Descr,
				PreSchedKey:    int64(preSchKey + i - 1),
				SchedKey:       int64(preSchKey + i),
				Priority:       0,
				OrderType:      baseModel.ODER_TYPE_INTERVAL,
				PlanResourceId: workLineId,
				UsedResourceId: workLineId,
				PlanQty:        1,
				PlanStartDate:  grmi.Date(now),
				PlanEndDate:    grmi.Date(now),
				PlanStartTime:  grmi.DateTime(now),
				PlanEndTime:    grmi.DateTime(now),
				PickingFlag:    product.PickingFlag,
			}
			err = serialOrderDao.InsertOne(&insertOrder)
			if err != nil {
				_ = session.Rollback()
				return grmi.NewBusinessError("写入工单数据失败, error:" + err.Error())
			}
			insertSerialOrderLi = append(insertSerialOrderLi, insertOrder)
			insertOrderStatus := model.SerialOrderStatus{
				SerialOrderId: serialOrderId,
				Status:        baseModel.WO_STATUS_RELEASED,
			}
			err = serialOrderStatusDao.InsertOne(&insertOrderStatus)
			if err != nil {
				_ = session.Rollback()
				return grmi.NewBusinessError("写入工单状态数据失败, error:" + err.Error())
			}
		}
	}
	err = impl.GenerateCraftData(user, session, insertSerialOrderLi)
	if err != nil {
		_ = session.Rollback()
		return err
	}
	_ = session.Commit()
	return nil
}

// CancelMultiSerialOrder 批量取消工单
func (impl *SerialOrderServiceImplement) CancelMultiSerialOrder(user *global.User, serialOrderIdLi []string) error {
	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	dao := dal.NewSerialOrderDAO(session, user.PlantNr, user.UserId)
	statusRecDao := dal.NewSerialOrderStatusRecLstDAO(session, user.PlantNr, user.UserId)
	serialOrderPOLstDao := dal.NewSerialOrderPOLstDAO(session, user.PlantNr, user.UserId)
	serialOrderStepDao := dal.NewSerialOrderStepLstDAO(session, user.PlantNr, user.UserId)
	serialOrderOPDetailDao := dal.NewSerialOrderOPDetailDAO(session, user.PlantNr, user.UserId)
	for _, serialOrderId := range serialOrderIdLi {
		record, err := dao.SelectOne(serialOrderId)
		if err != nil {
			return err
		}
		if record == nil {
			return grmi.NewBusinessError("不存在指定记录!")
		}
		if record.OrderType != baseModel.ODER_TYPE_INTERVAL {
			return grmi.NewBusinessError("工单不是内部工单，不允许取消, 工单ID:" + serialOrderId)
		}
		statusDao := dal.NewSerialOrderStatusDAO(session, user.PlantNr, user.UserId)
		status, err := statusDao.SelectOne(serialOrderId)
		if err != nil {
			return grmi.NewBusinessError("查询工单状态数据失败, error:" + err.Error())
		}
		if status == nil {
			return grmi.NewBusinessError("工单状态数据不存在, 工单ID:" + serialOrderId)
		}
		originStatus := status.Status
		if status.Status > baseModel.WO_STATUS_RELEASED {
			return grmi.NewBusinessError("工单已经进行不允许取消, 工单ID：" + serialOrderId)
		}
		preSerialOrderLi, err := dao.Select([]grmi.Predicate{
			meta.SerialOrder_OrderType.NewPredicate(grmi.Equal, baseModel.ORDER_TYPE_PRE),
			meta.SerialOrder_GenerateFlag.NewPredicate(grmi.Equal, 0),
			meta.SerialOrder_MainOrderId.NewPredicate(grmi.Equal, serialOrderId),
		}, nil)
		if err != nil {
			return grmi.NewBusinessError("查询工单下的预装工单失败, 错误：" + err.Error())
		}
		for _, preOrder := range preSerialOrderLi {
			if preOrder.SerialOrderStatus.Status > baseModel.WO_STATUS_RELEASED {
				return grmi.NewBusinessError("预装工单已经不允许取消, 工单ID:" + preOrder.SerialOrderId)
			}
		}
		if err := session.Begin(); err != nil {
			return grmi.NewBusinessError("开启事务失败, 错误：" + err.Error())
		}

		statusRec := model.SerialOrderStatusRecLst{
			SerialOrderId: serialOrderId,
			Status:        baseModel.WO_STATUS_CANCELED,
			PrevStatus:    status.Status,
		}
		status.Status = baseModel.WO_STATUS_CANCELED
		err = statusRecDao.InsertOne(&statusRec)
		if err != nil {
			_ = session.Rollback()
			return grmi.NewBusinessError("插入工单状态变更记录失败, error:" + err.Error())
		}
		err = statusDao.UpdateOne(status)
		if err != nil {
			_ = session.Rollback()
			return grmi.NewBusinessError("更新工单状态失败， error:" + err.Error())
		}
		if originStatus == baseModel.WO_STATUS_RELEASED {
			poUpdateData := &model.SerialOrderPOLst{
				Status: baseModel.WO_STATUS_CANCELED,
			}
			err = serialOrderPOLstDao.UpdateWhere([]grmi.Predicate{meta.SerialOrderPOLst_SerialOrderId.NewPredicate(grmi.Equal, serialOrderId)}, poUpdateData, meta.SerialOrderPOLst_Status.Name)
			if err != nil {
				_ = session.Rollback()
				return grmi.NewBusinessError("更新工单工序状态失败, error:" + err.Error())
			}
			stepUpdateData := &model.SerialOrderStepLst{
				Status: baseModel.WO_STATUS_CANCELED,
			}
			err = serialOrderStepDao.UpdateWhere([]grmi.Predicate{meta.SerialOrderStepLst_SerialOrderId.NewPredicate(grmi.Equal, serialOrderId)}, stepUpdateData, meta.SerialOrderStepLst_Status.Name)
			if err != nil {
				_ = session.Rollback()
				return grmi.NewBusinessError("更新工单工步状态失败, error:" + err.Error())
			}
			opDetailUpdateData := &model.SerialOrderOPDetail{
				Status: baseModel.WO_STATUS_CANCELED,
			}
			err = serialOrderOPDetailDao.UpdateWhere([]grmi.Predicate{meta.SerialOrderOPDetail_SerialOrderId.NewPredicate(grmi.Equal, serialOrderId)}, opDetailUpdateData, meta.SerialOrderOPDetail_Status.Name)
			if err != nil {
				_ = session.Rollback()
				return grmi.NewBusinessError("更新工单详细操作状态失败, error:" + err.Error())
			}
		}
		// 取消预装工单
		for _, preOrder := range preSerialOrderLi {
			originStatus = preOrder.SerialOrderStatus.Status
			statusRec = model.SerialOrderStatusRecLst{
				SerialOrderId: preOrder.SerialOrderId,
				Status:        baseModel.WO_STATUS_CANCELED,
				PrevStatus:    preOrder.SerialOrderStatus.Status,
			}
			preOrder.SerialOrderStatus.Status = baseModel.WO_STATUS_CANCELED
			err = statusRecDao.InsertOne(&statusRec)
			if err != nil {
				_ = session.Rollback()
				return grmi.NewBusinessError("插入预装工单状态变更记录失败, error:" + err.Error())
			}
			err = statusDao.UpdateOne(&preOrder.SerialOrderStatus)
			if err != nil {
				_ = session.Rollback()
				return grmi.NewBusinessError("更新预装工单状态失败， error:" + err.Error())
			}
			if originStatus == baseModel.WO_STATUS_RELEASED {
				poUpdateData := &model.SerialOrderPOLst{
					Status: baseModel.WO_STATUS_CANCELED,
				}
				err = serialOrderPOLstDao.UpdateWhere([]grmi.Predicate{meta.SerialOrderPOLst_SerialOrderId.NewPredicate(grmi.Equal, preOrder.SerialOrderId)}, poUpdateData, meta.SerialOrderPOLst_Status.Name)
				if err != nil {
					_ = session.Rollback()
					return grmi.NewBusinessError("更新预装工单工序状态失败, error:" + err.Error())
				}
				stepUpdateData := &model.SerialOrderStepLst{
					Status: baseModel.WO_STATUS_CANCELED,
				}
				err = serialOrderStepDao.UpdateWhere([]grmi.Predicate{meta.SerialOrderStepLst_SerialOrderId.NewPredicate(grmi.Equal, preOrder.SerialOrderId)}, stepUpdateData, meta.SerialOrderStepLst_Status.Name)
				if err != nil {
					_ = session.Rollback()
					return grmi.NewBusinessError("更新预装工单工步状态失败, error:" + err.Error())
				}
				opDetailUpdateData := &model.SerialOrderOPDetail{
					Status: baseModel.WO_STATUS_CANCELED,
				}
				err = serialOrderOPDetailDao.UpdateWhere([]grmi.Predicate{meta.SerialOrderOPDetail_SerialOrderId.NewPredicate(grmi.Equal, preOrder.SerialOrderId)}, opDetailUpdateData, meta.SerialOrderOPDetail_Status.Name)
				if err != nil {
					_ = session.Rollback()
					return grmi.NewBusinessError("更新预装工单详细操作状态失败, error:" + err.Error())
				}
			}
		}
	}
	_ = session.Commit()
	return nil
}

// TraceSubArticleBatch 根据物料批次查询mes数据
func (impl *SerialOrderServiceImplement) TraceSubArticleBatch(user *global.User, barcode string, paging *grmi.Paging) (grmi.PagingResult, error) {
	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	recvDataDao := dal.NewSerialOrderOPDetailRecvDataLstDAO(session, user.PlantNr, user.UserId)
	result, err := recvDataDao.SelectAndPaging(paging, []grmi.Predicate{
		meta.SerialOrderOPDetailRecvDataLst_StepType.NewPredicate(grmi.Equal, baseModel.STEP_CHECK_RULE),
		meta.SerialOrderOPDetailRecvDataLst_RecvData.NewPredicate(grmi.Equal, barcode),
		meta.SerialOrderOPDetailRecvDataLst_Status.NewPredicate(grmi.Equal, 1),
	}, []grmi.Field{meta.SerialOrderOPDetailRecvDataLst_CreateTime})
	if err != nil {
		return grmi.EmptyPagingResult, grmi.NewBusinessError("查询数据失败, 错误:" + err.Error())
	}
	workplaceDao := baseDal.NewWorkPlaceDAO(session, user.PlantNr, user.UserId)
	lst := result.Records.([]model.SerialOrderOPDetailRecvDataLst)
	exportItemLi := make([]model.ExportItem, 0, len(lst))
	workplaceMap := make(map[int]string)
	for _, item := range lst {
		workplaceDesc, exist := workplaceMap[item.WorkPlaceNr]
		if !exist {
			workplace, err := workplaceDao.SelectOne(item.WorkPlaceNr)
			if err != nil {
				return grmi.EmptyPagingResult, grmi.NewBusinessError("查询工位数据失败, 错误:" + err.Error())
			}
			if workplace == nil {
				workplaceDesc = "工位系统中已不存在"
			} else {
				workplaceDesc = workplace.WorkPlaceId
			}
			workplaceMap[item.WorkPlaceNr] = workplaceDesc
		}
		exportItem := model.ExportItem{
			SerialOrderId: item.SerialOrderId,
			Po:            item.PO,
			WorkPlace:     workplaceDesc,
			ArtId:         item.SubArtId,
			BarCode:       item.RecvData,
			UsedTime:      item.CreateTime.Restore().Format(grmi.DateTimeOutFormat),
		}
		exportItemLi = append(exportItemLi, exportItem)
	}
	result.Records = exportItemLi
	return result, nil
}

// ExportTraceSubArticleBatch 导出批次零件使用记录
func (impl *SerialOrderServiceImplement) ExportTraceSubArticleBatch(user *global.User, barcode string) (string, error) {
	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	recvDataDao := dal.NewSerialOrderOPDetailRecvDataLstDAO(session, user.PlantNr, user.UserId)
	workplaceDao := baseDal.NewWorkPlaceDAO(session, user.PlantNr, user.UserId)
	workplaceMap := make(map[int]string)
	result, err := recvDataDao.Select([]grmi.Predicate{
		meta.SerialOrderOPDetailRecvDataLst_StepType.NewPredicate(grmi.Equal, baseModel.STEP_CHECK_RULE),
		meta.SerialOrderOPDetailRecvDataLst_RecvData.NewPredicate(grmi.Equal, barcode),
		meta.SerialOrderOPDetailRecvDataLst_Status.NewPredicate(grmi.Equal, 1),
	}, []grmi.Field{meta.SerialOrderOPDetailRecvDataLst_CreateTime})
	if err != nil {
		return "", grmi.NewBusinessError("查询数据失败, 错误:" + err.Error())
	}
	titleLi := []string{
		"工单ID",
		"使用工序",
		"使用工位",
		"物料ID",
		"批次",
		"使用时间",
	}

	exportLi := make([]interface{}, 0, len(result))
	for _, item := range result {
		workplaceDesc, exist := workplaceMap[item.WorkPlaceNr]
		if !exist {
			workplace, err := workplaceDao.SelectOne(item.WorkPlaceNr)
			if err != nil {
				return "", grmi.NewBusinessError("查询工位数据失败, 错误:" + err.Error())
			}
			if workplace == nil {
				workplaceDesc = "工位系统中已不存在"
			} else {
				workplaceDesc = workplace.WorkPlaceId
			}
			workplaceMap[item.WorkPlaceNr] = workplaceDesc
		}
		exportItem := model.ExportItem{
			SerialOrderId: item.SerialOrderId,
			Po:            item.PO,
			WorkPlace:     workplaceDesc,
			ArtId:         item.SubArtId,
			BarCode:       item.RecvData,
			UsedTime:      item.CreateTime.Restore().Format(grmi.DateTimeOutFormat),
		}
		exportLi = append(exportLi, exportItem)
	}
	filepath, err := grmi.SaveExcelFile(exportLi, titleLi, "Sheet1", "零件批次使用记录")
	return filepath, err

}

// GetProductTraceDataReport 工单数据追溯
func (impl *SerialOrderServiceImplement) GetProductTraceDataReport(user *global.User, serialOrderId string) (interface{}, error) {
	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	result := model.ProductTraceData{
		SerialOrderId: serialOrderId,
		POLi:          make([]model.ProductTracePOData, 0),
	}
	// 1. 查询工单
	// 2. 通过工单查询生产的产线
	// 4. 通过工序查询当前工序生成工步结果
	serialOrderDao := dal.NewSerialOrderDAO(session, user.PlantNr, user.UserId)
	serialOrderPoDao := dal.NewSerialOrderPOLstDAO(session, user.PlantNr, user.UserId)
	serialOrderStepDao := dal.NewSerialOrderStepLstDAO(session, user.PlantNr, user.UserId)
	recvDataDao := dal.NewSerialOrderOPDetailRecvDataLstDAO(session, user.PlantNr, user.UserId)
	serialOrder, err := serialOrderDao.SelectOne(serialOrderId)
	if err != nil {
		return nil, grmi.NewBusinessError("查询工单失败, error:" + err.Error())
	}
	if serialOrder == nil {
		return nil, grmi.NewBusinessError("工单不存在, 工单ID:" + serialOrderId)
	}
	// 所有工单ID
	allOrderIdLi := make([]interface{}, 0)
	if serialOrder.OriginSerialOrderId != "" {
		subSerialOrderLi, err := serialOrderDao.Select([]grmi.Predicate{
			meta.SerialOrder_SerialOrderId.NewPredicate(grmi.NotEqual, serialOrderId),
			meta.SerialOrder_OriginSerialOrderId.NewPredicate(grmi.Equal, serialOrder.OriginSerialOrderId),
		}, nil)
		if err != nil {
			return nil, grmi.NewBusinessError("查询工单数据失败, 错误:" + err.Error())
		}
		allOrderIdLi = append(allOrderIdLi, serialOrderId, serialOrder.OriginSerialOrderId)
		for _, subOrder := range subSerialOrderLi {
			allOrderIdLi = append(allOrderIdLi, subOrder.SerialOrderId)
		}
	} else {
		allOrderIdLi = append(allOrderIdLi, serialOrder.SerialOrderId)
	}

	articleDao := baseDal.NewArticleDAO(session, user.PlantNr, user.UserId)
	article, err := articleDao.SelectOne(serialOrder.ArtId, serialOrder.ProjectId)
	if err != nil {
		return nil, grmi.NewBusinessError("查询工单总成数据失败, error:" + err.Error())
	}
	if article == nil {
		return nil, grmi.NewBusinessError("工单绑定总成不存在, 总成ID:" + serialOrder.ArtId)
	}
	//articleAtocdDao := baseDal.NewArticleAtcodLstDAO(session, user.PlantNr, user.UserId)
	//var needEOL bool
	//var needSBR bool
	//attributeLi, err := articleAtocdDao.SelectByArtId(serialOrder.ArtId)
	//if err != nil {
	//	return nil, grmi.NewBusinessError("查询总成关联属性失败, error:" + err.Error())
	//}
	//for _, attributeItem := range attributeLi {
	//	if attributeItem.Attribute.Descr == "EOL" {
	//		needEOL = true
	//	} else if attributeItem.Attribute.Descr == "SBR检测" {
	//		needSBR = true
	//	}
	//}
	allPoLi, err := serialOrderPoDao.SelectByOrderLi(allOrderIdLi, serialOrder.UsedResourceId)
	if err != nil {
		return nil, grmi.NewBusinessError("查询工序数据失败, 错误:" + err.Error())
	}
	poMap := make(map[string]int)
	for _, poItem := range allPoLi {
		_, exist := poMap[poItem.Process.PO]
		if !exist {
			poMap[poItem.Process.PO] = len(result.POLi)
			poData := model.ProductTracePOData{
				PO:     poItem.Process.PO,
				PoDesc: poItem.Process.Descr,
				StepLi: make([]model.ProductTraceStepData, 0),
			}
			result.POLi = append(result.POLi, poData)
		}
	}
	stepLi, err := serialOrderStepDao.Select([]grmi.Predicate{
		meta.SerialOrderStepLst_SerialOrderId.NewPredicate(grmi.Include, allOrderIdLi...),
	}, []grmi.Field{meta.SerialOrderStepLst_StepNo})
	if err != nil {
		return nil, grmi.NewBusinessError("查询工步数据失败, 错误:" + err.Error())
	}
	stepMap := make(map[string]int)
	for _, stepItem := range stepLi {
		mapKey := stepItem.PO + "_" + strconv.Itoa(stepItem.StepNo)
		_, exist := stepMap[mapKey]
		if !exist {
			poIndex := poMap[stepItem.PO]
			stepMap[mapKey] = len(result.POLi[poIndex].StepLi)
			stepData := model.ProductTraceStepData{
				StepNo:     stepItem.StepNo,
				StepDesc:   stepItem.StepDesc,
				RecvDataLi: make([]model.SerialOrderOPDetailRecvDataLst, 0),
			}
			result.POLi[poIndex].StepLi = append(result.POLi[poIndex].StepLi, stepData)
		}
	}

	recvDataLi, err := recvDataDao.Select([]grmi.Predicate{
		meta.SerialOrderOPDetailRecvDataLst_SerialOrderId.NewPredicate(grmi.Include, allOrderIdLi...),
	}, []grmi.Field{
		meta.SerialOrderOPDetailRecvDataLst_SerialOrderId,
		meta.SerialOrderOPDetailRecvDataLst_PO,
		meta.SerialOrderOPDetailRecvDataLst_StepNo,
		meta.SerialOrderOPDetailRecvDataLst_OriginRuleId,
	})
	if err != nil {
		return nil, grmi.NewBusinessError("查询生产数据失败, 错误:" + err.Error())
	}
	for _, recvData := range recvDataLi {
		poIndex, exist := poMap[recvData.PO]
		if !exist {
			continue
		}
		stepKey := recvData.PO + "_" + strconv.Itoa(recvData.StepNo)
		stepIndex, exist := stepMap[stepKey]

		if !exist {
			continue
		}
		result.POLi[poIndex].StepLi[stepIndex].RecvDataLi = append(result.POLi[poIndex].StepLi[stepIndex].RecvDataLi, recvData)
	}
	return result, nil
}

// QuickSort 电检数据时间排序
//func (impl *SerialOrderServiceImplement) QuickSort(values []interface{}) (err error) {
//	if len(values) <= 1 {
//		return nil
//	}
//	mid, i := values[0], 1
//	var midTime time.Time
//	drData, ok := mid.(eolModel.EOLDR)
//	if ok {
//		midTime, err = time.ParseInLocation("2006/1/2 15:04:05", drData.TESTDATA, utils.TimezoneLocation)
//		if err != nil {
//			return grmi.NewBusinessError("电检数据时间格式错误")
//		}
//	} else {
//		paData, ok := mid.(eolModel.EOLPA)
//		if ok {
//			midTime, err = time.ParseInLocation("2006/1/2 15:04:05", paData.TESTDATA, utils.TimezoneLocation)
//			if err != nil {
//				return grmi.NewBusinessError("电检数据时间格式错误")
//			}
//		} else {
//			brData, ok := mid.(eolModel.EOLBR)
//			if ok {
//				midTime, err = time.ParseInLocation("2006/1/2 15:04:05", brData.TESTDATA, utils.TimezoneLocation)
//				if err != nil {
//					return grmi.NewBusinessError("电检数据时间格式错误")
//				}
//			} else {
//				return grmi.NewBusinessError("电检数据格式错误")
//			}
//		}
//	}
//
//	head, tail := 0, len(values)-1
//	for head < tail {
//		var iTime time.Time
//		drData, ok = values[i].(eolModel.EOLDR)
//		if ok {
//			midTime, err = time.ParseInLocation("2006/1/2 15:04:05", drData.TESTDATA, utils.TimezoneLocation)
//			if err != nil {
//				return grmi.NewBusinessError("电检数据时间格式错误")
//			}
//		} else {
//			paData, ok := values[i].(eolModel.EOLPA)
//			if ok {
//				midTime, err = time.ParseInLocation("2006/1/2 15:04:05", paData.TESTDATA, utils.TimezoneLocation)
//				if err != nil {
//					return grmi.NewBusinessError("电检数据时间格式错误")
//				}
//			} else {
//				brData, ok := values[i].(eolModel.EOLBR)
//				if ok {
//					midTime, err = time.ParseInLocation("2006/1/2 15:04:05", brData.TESTDATA, utils.TimezoneLocation)
//					if err != nil {
//						return grmi.NewBusinessError("电检数据时间格式错误")
//					}
//				} else {
//					return grmi.NewBusinessError("电检数据格式错误")
//				}
//			}
//		}
//		if iTime.Before(midTime) {
//			values[i], values[tail] = values[tail], values[i]
//			tail--
//		} else {
//			values[i], values[head] = values[head], values[i]
//			head++
//			i++
//		}
//	}
//	values[head] = mid
//	err = impl.QuickSort(values[:head])
//	if err != nil {
//		return err
//	}
//	err = impl.QuickSort(values[head+1:])
//	if err != nil {
//		return err
//	}
//	return nil
//}
