package schedule

import (
	baseDal "LAPP_LF_MOM_BACKEND/dao/base"
	dal "LAPP_LF_MOM_BACKEND/dao/om"
	"LAPP_LF_MOM_BACKEND/db"
	"LAPP_LF_MOM_BACKEND/grmi"
	"LAPP_LF_MOM_BACKEND/infra/logger"
	baseMeta "LAPP_LF_MOM_BACKEND/meta/base"
	omMeta "LAPP_LF_MOM_BACKEND/meta/om"
	baseModel "LAPP_LF_MOM_BACKEND/models/base"
	common "LAPP_LF_MOM_BACKEND/models/base"
	model "LAPP_LF_MOM_BACKEND/models/om"
	"errors"
	"fmt"
	"strconv"
	"time"
)

// 调度批次任务对象
type TaskSrv struct {
	TaskId            string
	TaskType          string
	PlantNr           int
	ProjectId         string
	SortKey           int64  // 用于给MES用的排序号
	ArtId             string // 任务产品ID
	Priority          int
	Status            int
	SchedResGrpId     string        // 调度资源组ID
	SchedResId        string        // 调度资源
	FixResource       bool          // 锁定资源
	PlanQty           float64       // 计划数量
	ReleasedQty       float64       // 已下达数量 - 每个独立生成序列订单
	FinishedQty       float64       // 已完成数量
	Duration          time.Duration // 任务所需工时
	EarliestStartTime time.Time     // 最早开始时间
	LatestEndTime     time.Time     // 最迟结束时间
	SetupStartTime    time.Time     // 换型开始时间
	SetupEndTime      time.Time     // 换型结束时间
	FixStartTime      bool          // 锁定派车给开始时间
	SchedStartTime    time.Time     // 排程开始时间
	SchedEndTime      time.Time     // 排程结束时间
	ActStartTime      time.Time     // 实际开始时间
	ActEndTime        time.Time     // 实际结束时间
	Wotab             model.WorkOrder
	WorkOrderStatus   model.WorkOrderStatus
	WorkOrderQty      model.WorkOrderQty
	SerialTaskArray   []SerialTaskSrv // 批次订单的序列订单
}

// 序列任务对象
type SerialTaskSrv struct {
	TaskId               string
	TaskType             string
	ParentTaskId         string
	PlantNr              int
	ProjectId            string
	Barcode              string // 序列订单条码
	ArtId                string // 任务产品ID
	Status               int
	SchedResId           string        // 调度资源
	PlanQty              float64       // 计划数量
	Duration             time.Duration // 任务所需工时
	EarliestStartTime    time.Time     // 最早开始时间
	LatestEndTime        time.Time     // 最迟结束时间
	SetupStartTime       time.Time     // 换型开始时间
	SetupEndTime         time.Time     // 换型结束时间
	SchedStartTime       time.Time     // 排程开始时间
	SchedEndTime         time.Time     // 排程结束时间
	ActStartTime         time.Time     // 实际开始时间
	ActEndTime           time.Time     // 实际结束时间
	SerialOrderTab       model.SerialOrder
	SerialOrderStatusTab model.SerialOrderStatus
}

// 基于指定的批次订单号获取批次订单对象
func (tasksrv *TaskSrv) Get() (err error) {

	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	dao := dal.NewWorkOrderDAO(session, common.PlantNr, "scheduler")
	var wotab model.WorkOrder
	if wotab, err = dao.SelectInfo(tasksrv.TaskId); err != nil {
		err = errors.New(fmt.Sprintf("获取工厂%d批次订单%s失败！", tasksrv.PlantNr, tasksrv.TaskId))
		return
	}
	tasksrv.Init(wotab)
	return
}

// 基于给定的订单表和订单状态表进行结构初始化
func (tasksrv *TaskSrv) Init(wotab model.WorkOrder) {
	// 初始化批次任务的批次订单信息
	tasksrv.TaskId = wotab.WorkOrderId
	tasksrv.TaskType = wotab.OrderType
	tasksrv.SortKey = wotab.PlanStartTime.Restore().Unix()        // 用于给MES用的排序号
	tasksrv.ArtId = wotab.ArtId                                   // 任务产品ID
	tasksrv.Priority = wotab.Priority                             // 优先级
	tasksrv.Status = wotab.WorkOrderStatus.Status                 // 订单状态
	tasksrv.SchedResGrpId = wotab.PlanResourceGroupId             // 调度资源组ID
	tasksrv.SchedResId = wotab.PlanResourceId                     // 调度资源
	tasksrv.FixResource = false                                   // 锁定资源
	tasksrv.PlanQty = float64(wotab.PlanQty)                      // 计划数量
	tasksrv.ReleasedQty = float64(wotab.WorkOrderQty.ReleasedQty) // 已下达数量 - 每个独立生成序列订单
	tasksrv.FinishedQty = float64(wotab.WorkOrderQty.ActQty)      // 已完成数量
	tasksrv.EarliestStartTime = wotab.PlanStartDate.Restore()     // 最早开始时间
	tasksrv.LatestEndTime = wotab.PlanEndTime.Restore()           // 最迟结束时间
	tasksrv.SetupStartTime = wotab.SetupStartTime.Restore()       // 换型开始时间
	tasksrv.SetupEndTime = wotab.SetupEndTime.Restore()           // 换型结束时间
	if wotab.FixStartTimeToggle {                                 // 锁定开始时间
		tasksrv.FixStartTime = true
	} else {
		tasksrv.FixStartTime = false
	}
	tasksrv.SchedStartTime = wotab.PlanStartTime.Restore() // 排程开始时间
	tasksrv.SchedEndTime = wotab.PlanEndTime.Restore()     // 排程结束时间
	tasksrv.ActStartTime = wotab.ActStartTime.Restore()    // 实际开始时间
	tasksrv.ActEndTime = wotab.ActEndTime.Restore()        // 实际结束时间

	// 获取调度任务所需要的工时
	calQty := wotab.PlanQty - wotab.WorkOrderQty.ActQty
	if wotab.RatePerHourToggle {
		if wotab.QuantityPerHour <= 0 || calQty <= 0 {
			tasksrv.Duration = 0
		} else {
			//tasksrv.Duration = time.Duration(float64(calQty)/wotab.QuantityPerHour) * time.Hour
			tasksrv.Duration = time.Duration(float64(calQty)/wotab.QuantityPerHour*3600) * time.Second
		}
	}
	if wotab.TimePerItemToggle {
		perItemSeconds := float64(wotab.OpTimePerItem) * GetTimeUomSeconds(wotab.TimeUomId)
		tasksrv.Duration = time.Duration(float64(calQty)*perItemSeconds) * time.Second
	}
	if wotab.TimePerBatchToggle {
		if wotab.BatchTime <= 0 || wotab.BatchQuantity <= 0 || calQty <= 0 {
			tasksrv.Duration = 0
		} else {
			perBatchSeconds := float64(wotab.BatchTime) * GetTimeUomSeconds(wotab.TimeUomId)
			tasksrv.Duration = time.Duration(perBatchSeconds*float64(calQty)/float64(wotab.BatchQuantity)) * time.Hour
		}
	}
	tasksrv.Wotab = wotab
	tasksrv.WorkOrderStatus = wotab.WorkOrderStatus
	tasksrv.WorkOrderQty = wotab.WorkOrderQty
}

// 加载批次订单的序列订单
func (tasksrv *TaskSrv) LoadSerialOrders() (err error) {
	var (
		i             int
		sordertablst  []model.SerialOrder
		serialtasksrv SerialTaskSrv
	)
	// 初始化批次订单的序列订单信息
	tasksrv.SerialTaskArray = []SerialTaskSrv{}

	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	dao := dal.NewSerialOrderDAO(session, common.PlantNr, "scheduler")
	if sordertablst, err = dao.SelectByWorkOrderId(tasksrv.Wotab.WorkOrderId, common.WO_STATUS_LOCKED); err != nil {
		err = errors.New(fmt.Sprintf("查询批次订单%s的序列订单失败！", tasksrv.Wotab.WorkOrderId))
		return
	}
	for i = 0; i < len(sordertablst); i++ {
		serialtasksrv = SerialTaskSrv{}
		serialtasksrv.Init(sordertablst[i])
		tasksrv.SerialTaskArray = append(tasksrv.SerialTaskArray, serialtasksrv)
	}
	return
}

// 生成状态记录
func (tasksrv *TaskSrv) CreateStatusRecord(prevstatus int, eventsrv EventSrv) (err error) {
	var wostatusrectab model.WorkOrderStatusRecLst

	// 初始化
	wostatusrectab = model.WorkOrderStatusRecLst{}
	wostatusrectab.PlantNr = tasksrv.PlantNr
	wostatusrectab.WorkOrderId = tasksrv.TaskId
	wostatusrectab.PrevStatus = prevstatus
	wostatusrectab.Status = tasksrv.Status
	wostatusrectab.TriggerEvent = eventsrv.TriggerEvent
	wostatusrectab.TriggerObjectId = eventsrv.TriggerObjectId
	wostatusrectab.TriggerSubObjectId = eventsrv.TriggerSubObjectId
	wostatusrectab.OutputStatus = common.OUTPUT_TRIGGER_UNHANDLED // 未处理
	wostatusrectab.OutputEvent = eventsrv.OutputEvent
	wostatusrectab.OutputEventMessageId = eventsrv.OutputEventMsgId
	wostatusrectab.OutputObjectId = eventsrv.OutputObjectId
	wostatusrectab.OutputSubObjectId = eventsrv.OutputSubObjectId
	SetTableLastModify(&wostatusrectab, wostatusrectab, common.MODIFY_MODE_CREATE, eventsrv.TriggerService)

	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	dao := dal.NewWorkOrderStatusRecLstDAO(session, common.PlantNr, "scheduler")
	if err := dao.InsertOne(&wostatusrectab); err != nil {
		err = errors.New(fmt.Sprintf("无法为工单%s插入状态变更记录%v!", tasksrv.TaskId, err))
		return err
	}

	return
}

// 下达批次订单
func (tasksrv *TaskSrv) Release(workOrderStatusDao dal.WorkOrderStatusDAO, workOrderStatusRecDao dal.WorkOrderStatusRecLstDAO) (err error) {
	var (
		wostatrectab model.WorkOrderStatusRecLst
	)

	if tasksrv.WorkOrderStatus.Status != common.WO_STATUS_LOCKED {
		err = errors.New(fmt.Sprintf("批次订单%s状态%d不满足下达条件，无法下达！", tasksrv.WorkOrderStatus.WorkOrderId, tasksrv.WorkOrderStatus.Status))
		return
	}
	// 变更状态
	tasksrv.Status = common.WO_STATUS_RELEASED
	tasksrv.WorkOrderStatus.Status = common.WO_STATUS_RELEASED
	SetTableLastModify(&tasksrv.WorkOrderStatus, tasksrv.WorkOrderStatus, common.MODIFY_MODE_UPDATE, "rel_service")
	err = workOrderStatusDao.UpdateOne(&tasksrv.WorkOrderStatus)
	if err != nil {
		return err
	}
	// 新增批次订单状态变更记录
	wostatrectab = model.WorkOrderStatusRecLst{}
	wostatrectab.PlantNr = tasksrv.WorkOrderStatus.PlantNr
	wostatrectab.WorkOrderId = tasksrv.WorkOrderStatus.WorkOrderId
	wostatrectab.PrevStatus = common.WO_STATUS_LOCKED
	wostatrectab.Status = common.WO_STATUS_RELEASED
	wostatrectab.TriggerEvent = common.BATORD_EVENT_RELEASE
	wostatrectab.TriggerObjectId = tasksrv.WorkOrderStatus.WorkOrderId
	wostatrectab.OutputStatus = common.OUTPUT_TRIGGER_UNHANDLED
	wostatrectab.OutputEvent = common.BATORD_EVENT_RELEASE
	wostatrectab.OutputObjectId = tasksrv.WorkOrderStatus.WorkOrderId
	SetTableLastModify(&wostatrectab, wostatrectab, common.MODIFY_MODE_CREATE, "rel_service")
	if err = workOrderStatusRecDao.InsertOne(&wostatrectab); err != nil {
		err = errors.New(fmt.Sprintf("批次订单%s无法插入状态变更记录%v！", tasksrv.WorkOrderStatus.WorkOrderId, err))
		return
	}
	return
}

// 下达批次订单，同步生成对应的序列订单
func (tasksrv *TaskSrv) ReleaseSerialOrder(relQty int, releaseId string) (err error) {

	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	if err = session.Begin(); err != nil {
		return
	}
	var user = "scheduler"
	workOrderStatusDao := dal.NewWorkOrderStatusDAO(session, common.PlantNr, user)
	workOrderStatusRecDao := dal.NewWorkOrderStatusRecLstDAO(session, common.PlantNr, user)
	workOrderQtyDao := dal.NewWorkOrderQtyDAO(session, common.PlantNr, user)
	serialOrderStatusDao := dal.NewSerialOrderStatusDAO(session, common.PlantNr, user)
	serialOrderStatusRecDao := dal.NewSerialOrderStatusRecLstDAO(session, common.PlantNr, user)
	stepDao := dal.NewSerialOrderStepLstDAO(session, common.PlantNr, user)
	stepRecDao := dal.NewSerialOrderStepStatusRecLstDAO(session, common.PlantNr, user)
	poDao := dal.NewSerialOrderPOLstDAO(session, common.PlantNr, user)
	poRecDao := dal.NewSerialOrderPOStatusRecLstDAO(session, common.PlantNr, user)
	opDetailDao := dal.NewSerialOrderOPDetailDAO(session, common.PlantNr, user)
	opDetailRecDao := dal.NewSerialOrderOPDetailStatusRecLstDAO(session, common.PlantNr, user)
	processDao := baseDal.NewProcessDAO(session, common.PlantNr, user)
	innerLog, _ := logger.NewLogger("scheduler", "scheduler")
	innerLog.Debug("ReleaseSerialOrder begin workOrderId:"+tasksrv.Wotab.WorkOrderId+" relQty: "+strconv.Itoa(relQty), "release id:"+releaseId)
	// 判断批次订单状态是否满足下达条件 >= 24 && < 80
	if tasksrv.WorkOrderStatus.Status < common.WO_STATUS_LOCKED || tasksrv.WorkOrderStatus.Status >= common.WO_STATUS_FINISHED {
		_ = session.Rollback()
		err = errors.New(fmt.Sprintf("批次订单%s状态%d不满足下达条件，无法下达！", tasksrv.WorkOrderStatus.WorkOrderId, tasksrv.WorkOrderStatus.Status))
		return
	}
	// 获取批次订单已下达序列订单
	if err = tasksrv.LoadSerialOrders(); err != nil {
		_ = session.Rollback()
		return
	}
	innerLog.Debug("ReleaseSerialOrder LoadSerialOrders success", "release id:"+releaseId)
	// 计算实际可下达的序列订单数量
	actRelQty := len(tasksrv.SerialTaskArray)
	if actRelQty <= 0 {
		_ = session.Rollback()
		innerLog.Error("ReleaseSerialOrder actRelQty <= 0, workOrderId:"+tasksrv.Wotab.WorkOrderId, "release id:"+releaseId)
		err = errors.New(fmt.Sprintf("批次订单%s可下达数量小于0！", tasksrv.WorkOrderStatus.WorkOrderId))
		return
	}
	if relQty > actRelQty {
		_ = session.Rollback()
		innerLog.Error("ReleaseSerialOrder relQty > actRelQty, workOrderId:"+tasksrv.Wotab.WorkOrderId, "relQty:"+strconv.Itoa(relQty)+" actRelQty: "+strconv.Itoa(actRelQty), "release id:"+releaseId)
		err = errors.New(fmt.Sprintf("批次订单%s需下达数量大于锁定的任务队列！", tasksrv.WorkOrderStatus.WorkOrderId))
		return
	}
	for i := 0; i < relQty; i++ {

		serialOrderStatus := tasksrv.SerialTaskArray[i].SerialOrderStatusTab
		innerLog.Debug("ReleaseSerialOrder release serialOrder begin, serialOrderID:"+serialOrderStatus.SerialOrderId, "release id:"+releaseId)

		serialOrder := tasksrv.SerialTaskArray[i].SerialOrderTab
		recStatus := model.SerialOrderStatusRecLst{
			SerialOrderId:      serialOrderStatus.SerialOrderId,
			PrevStatus:         serialOrderStatus.Status,
			Status:             common.WO_STATUS_RELEASED,
			StartTime:          serialOrder.PlanStartTime,
			EndTime:            serialOrder.PlanEndTime,
			OutputEvent:        common.SERORD_EVENT_RELEASE,
			OutputStatus:       common.OUTPUT_TRIGGER_UNHANDLED,
			TriggerObjectId:    serialOrderStatus.TriggerObjectId,
			TriggerSubObjectId: serialOrderStatus.TriggerSubObjectId,
		}
		err = serialOrderStatusRecDao.InsertOne(&recStatus)
		if err != nil {
			innerLog.Error("ReleaseSerialOrder insert serial order status rec failed, error:"+err.Error()+" serialOrderId:"+serialOrderStatus.SerialOrderId, "release id:"+releaseId)
			_ = session.Rollback()
			return err
		}
		innerLog.Debug("ReleaseSerialOrder serialOrderStatusRecDao.InsertOne(&recStatus), serialOrderID:"+serialOrderStatus.SerialOrderId, "release id:"+releaseId)

		serialOrderStatus.Status = common.WO_STATUS_RELEASED
		err = serialOrderStatusDao.UpdateOne(&serialOrderStatus)
		if err != nil {
			innerLog.Error("ReleaseSerialOrder update serial order status failed, error:"+err.Error()+" serialOrderId:"+serialOrderStatus.SerialOrderId, "release id:"+releaseId)
			_ = session.Rollback()
			return err
		}
		innerLog.Debug("ReleaseSerialOrder serialOrderStatusDao.UpdateOne(&serialOrderStatus), serialOrderID:"+serialOrderStatus.SerialOrderId, "release id:"+releaseId)

		poLi, err := poDao.Select([]grmi.Predicate{omMeta.SerialOrderPOLst_SerialOrderId.NewPredicate(grmi.Equal, serialOrder.SerialOrderId)}, nil)
		if err != nil {
			innerLog.Error("ReleaseSerialOrder 查询工单工序数据失败, error:"+err.Error()+" serialOrderId:"+serialOrderStatus.SerialOrderId, "release id:"+releaseId)
			_ = session.Rollback()
			return err
		}

		poNumLi := make([]interface{}, 0, len(poLi))
		for _, poData := range poLi {
			poNumLi = append(poNumLi, poData.PO)
		}
		poDataLi, err := processDao.Select([]grmi.Predicate{baseMeta.Process_WorkLineId.NewPredicate(grmi.Equal, serialOrder.UsedResourceId), baseMeta.Process_PO.NewPredicate(grmi.Include, poNumLi...)}, nil)
		if err != nil {
			innerLog.Error("ReleaseSerialOrder 查询工序数据失败, error:"+err.Error()+" serialOrderId:"+serialOrderStatus.SerialOrderId, "release id:"+releaseId)
			_ = session.Rollback()
			return err
		}
		firstPOMap := make(map[int]interface{})
		releasePOLi := make([]int, 0)
		for _, po := range poDataLi {
			if po.IsFirstPO {
				firstPOMap[po.PO] = nil
				releasePOLi = append(releasePOLi, po.PO)
			}
		}
		for firstPO, _ := range firstPOMap {
			li := tasksrv.RecursionComposePO(firstPO, &poDataLi)
			releasePOLi = append(releasePOLi, li...)
		}
		releasePOMap := make(map[int]interface{}, len(releasePOLi))
		for _, po := range releasePOLi {
			releasePOMap[po] = nil
		}
		poRecLi := make([]model.SerialOrderPOStatusRecLst, 0, len(poLi))

		for index, po := range poLi {
			_, exist := releasePOMap[po.PO]
			if !exist {
				continue
			}
			poRec := model.SerialOrderPOStatusRecLst{
				SerialOrderId: serialOrder.SerialOrderId,
				PO:            po.PO,
				Pos:           1,
				PrevStatus:    po.Status,
				Status:        common.WO_STATUS_RELEASED,
			}
			poRecLi = append(poRecLi, poRec)
			po.Status = common.WO_STATUS_RELEASED
			poLi[index] = po
		}
		stepLi, err := stepDao.Select([]grmi.Predicate{omMeta.SerialOrderStepLst_SerialOrderId.NewPredicate(grmi.Equal, serialOrder.SerialOrderId)}, nil)
		if err != nil {
			innerLog.Error("ReleaseSerialOrder 查询工单工艺数据失败, error:"+err.Error()+" serialOrderId:"+serialOrderStatus.SerialOrderId, "release id:"+releaseId)
			_ = session.Rollback()
			return err
		}
		stepRecLi := make([]model.SerialOrderStepStatusRecLst, 0, len(stepLi))
		for index, step := range stepLi {
			_, exist := releasePOMap[step.PO]
			if !exist {
				continue
			}
			stepRec := model.SerialOrderStepStatusRecLst{
				SerialOrderId: serialOrder.SerialOrderId,
				OperationId:   step.OperationId,
				PO:            step.PO,
				StepNo:        step.StepNo,
				Pos:           1,
				PrevStatus:    step.Status,
				Status:        common.WO_STATUS_RELEASED,
			}
			stepRecLi = append(stepRecLi, stepRec)
			step.Status = common.WO_STATUS_RELEASED
			stepLi[index] = step
		}
		opDetailLi, err := opDetailDao.Select([]grmi.Predicate{omMeta.SerialOrderOPDetail_SerialOrderId.NewPredicate(grmi.Equal, serialOrder.SerialOrderId)}, nil)
		if err != nil {
			innerLog.Error("ReleaseSerialOrder 查询工单工艺详细步骤失败, error:"+err.Error()+" serialOrderId:"+serialOrderStatus.SerialOrderId, "release id:"+releaseId)
			_ = session.Rollback()
			return err
		}
		opDetailRecLi := make([]model.SerialOrderOPDetailStatusRecLst, 0, len(opDetailLi))
		for index, opDetail := range opDetailLi {
			_, exist := releasePOMap[opDetail.PO]
			if !exist {
				continue
			}
			opDetailRec := model.SerialOrderOPDetailStatusRecLst{
				SerialOrderId: serialOrder.SerialOrderId,
				DetailId:      opDetail.DetailId,
				PO:            opDetail.PO,
				StepNo:        opDetail.StepNo,
				OriginPos:     opDetail.Pos,
				StepType:      opDetail.StepType,
				Pos:           1,
				SubArtId:      opDetail.SubArtId,
				PrevStatus:    opDetail.Status,
				Status:        common.WO_STATUS_RELEASED,
			}
			opDetail.Status = common.WO_STATUS_RELEASED
			opDetailRecLi = append(opDetailRecLi, opDetailRec)
			opDetailLi[index] = opDetail
		}
		tempPoRecLi := make([]model.SerialOrderPOStatusRecLst, 0, 30)
		for _, poRec := range poRecLi {
			if len(tempPoRecLi) == 30 {
				err = poRecDao.Insert(&tempPoRecLi)
				if err != nil {
					innerLog.Error("ReleaseSerialOrder 插入工单工序状态变更记录失败, error:"+err.Error()+" serialOrderId:"+serialOrderStatus.SerialOrderId, "release id:"+releaseId)
					_ = session.Rollback()
					return err
				}
				tempPoRecLi = make([]model.SerialOrderPOStatusRecLst, 0, 30)
			} else {
				tempPoRecLi = append(tempPoRecLi, poRec)
			}
		}
		if len(tempPoRecLi) != 0 {
			err = poRecDao.Insert(&tempPoRecLi)
			if err != nil {
				innerLog.Error("ReleaseSerialOrder 插入工单工序状态变更记录失败, error:"+err.Error()+" serialOrderId:"+serialOrderStatus.SerialOrderId, "release id:"+releaseId)
				_ = session.Rollback()
				return err
			}
		}
		err = poDao.Update(&poLi)
		if err != nil {
			innerLog.Error("ReleaseSerialOrder 更新工单工序状态失败, error:"+err.Error()+" serialOrderId:"+serialOrderStatus.SerialOrderId, "release id:"+releaseId)
			_ = session.Rollback()
			return err
		}
		tempStepRecLi := make([]model.SerialOrderStepStatusRecLst, 0, 30)
		for _, stepRec := range stepRecLi {
			if len(tempStepRecLi) == 30 {
				err = stepRecDao.Insert(&tempStepRecLi)
				if err != nil {
					innerLog.Error("ReleaseSerialOrder 插入工单工艺状态变更记录失败, error:"+err.Error()+" serialOrderId:"+serialOrderStatus.SerialOrderId, "release id:"+releaseId)
					_ = session.Rollback()
					return err
				}
				tempStepRecLi = make([]model.SerialOrderStepStatusRecLst, 0, 30)
			} else {
				tempStepRecLi = append(tempStepRecLi, stepRec)
			}
		}
		if len(tempStepRecLi) != 0 {
			err = stepRecDao.Insert(&tempStepRecLi)
			if err != nil {
				innerLog.Error("ReleaseSerialOrder插入工单工艺状态变更记录失败, error:"+err.Error()+" serialOrderId:"+serialOrderStatus.SerialOrderId, "release id:"+releaseId)
				_ = session.Rollback()
				return err
			}
		}
		err = stepDao.Update(&stepLi)
		if err != nil {
			innerLog.Error("ReleaseSerialOrder 更新工单工艺状态失败, error:"+err.Error()+" serialOrderId:"+serialOrderStatus.SerialOrderId, "release id:"+releaseId)
			_ = session.Rollback()
			return err
		}
		tempOPDetailRecLi := make([]model.SerialOrderOPDetailStatusRecLst, 0, 30)
		for _, opDetailRec := range opDetailRecLi {
			if len(tempOPDetailRecLi) == 30 {
				err = opDetailRecDao.Insert(&tempOPDetailRecLi)
				if err != nil {
					innerLog.Error("ReleaseSerialOrder 插入工单工艺详细步骤状态变更记录失败, error:"+err.Error()+" serialOrderId:"+serialOrderStatus.SerialOrderId, "release id:"+releaseId)
					_ = session.Rollback()
					return err
				}
				tempOPDetailRecLi = make([]model.SerialOrderOPDetailStatusRecLst, 0, 30)
			} else {
				tempOPDetailRecLi = append(tempOPDetailRecLi, opDetailRec)
			}
		}
		if len(tempOPDetailRecLi) != 0 {
			err = opDetailRecDao.Insert(&tempOPDetailRecLi)
			if err != nil {
				innerLog.Error("ReleaseSerialOrder 插入工单工艺详细步骤状态变更记录失败, error:"+err.Error()+" serialOrderId:"+serialOrderStatus.SerialOrderId, "release id:"+releaseId)
				_ = session.Rollback()
				return err
			}
		}
		err = opDetailDao.Update(&opDetailLi)
		if err != nil {
			innerLog.Error("ReleaseSerialOrder 更新工单工艺详细步骤状态失败, error:"+err.Error()+" serialOrderId:"+serialOrderStatus.SerialOrderId, "release id:"+releaseId)
			_ = session.Rollback()
			return err
		}
		tasksrv.ReleasedQty++
		tasksrv.WorkOrderQty.ReleasedQty++
	}
	workOrderQty := tasksrv.WorkOrderQty
	err = workOrderQtyDao.UpdateOne(&workOrderQty)
	if err != nil {
		innerLog.Error("ReleaseSerialOrder update work order qty failed, error:"+err.Error()+" workOrderId:"+workOrderQty.WorkOrderId, "release id:"+releaseId)
		_ = session.Rollback()
		return err
	}
	innerLog.Debug("ReleaseSerialOrder workOrderQtyDao.UpdateOne(&workOrderQty), qty:"+strconv.Itoa(int(tasksrv.WorkOrderQty.ReleasedQty)), "release id:"+releaseId)

	// 置位批次订单状态

	//if tasksrv.WorkOrderStatus.Status == common.WO_STATUS_LOCKED && workOrderQty.ReleasedQty == tasksrv.Wotab.PlanQty {
	//	innerLog.Debug("ReleaseSerialOrder change WorkOrderStatus", "release id:"+ releaseId)
	//	if err = tasksrv.Release(workOrderStatusDao, workOrderStatusRecDao); err != nil {
	//		_ = session.Rollback()
	//		innerLog.Error("ReleaseSerialOrder tasksrv Release failed, error:" + err.Error() + " workOrderId:" + tasksrv.Wotab.WorkOrderId, "release id:" + releaseId)
	//		err = errors.New(fmt.Sprintf("下达批次订单%s失败！", tasksrv.TaskId))
	//		return
	//	}
	//}
	if tasksrv.WorkOrderStatus.Status == common.WO_STATUS_LOCKED && workOrderQty.ReleasedQty > 0 {
		innerLog.Debug("ReleaseSerialOrder change WorkOrderStatus", "release id:"+releaseId)
		if err = tasksrv.Release(workOrderStatusDao, workOrderStatusRecDao); err != nil {
			_ = session.Rollback()
			innerLog.Error("ReleaseSerialOrder tasksrv Release failed, error:"+err.Error()+" workOrderId:"+tasksrv.Wotab.WorkOrderId, "release id:"+releaseId)
			err = errors.New(fmt.Sprintf("下达批次订单%s失败！", tasksrv.TaskId))
			return
		}
	}
	err = session.Commit()
	if err != nil {
		innerLog.Error("ReleaseSerialOrder session commit failed, error:"+err.Error()+" workOrderId:"+tasksrv.Wotab.WorkOrderId, "release id:"+releaseId)
	}
	return
}

// 基于指定的序列订单号获取序列订单对象
func (stasksrv *SerialTaskSrv) Get() (err error) {
	var serordtab model.SerialOrder

	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	dao := dal.NewSerialOrderDAO(session, common.PlantNr, "scheduler")
	if serordtab, err = dao.SelectInfo(stasksrv.TaskId); err != nil {
		err = errors.New(fmt.Sprintf("获取工厂%d序列订单%s失败！", stasksrv.PlantNr, stasksrv.TaskId))
		return
	}
	stasksrv.Init(serordtab)
	return
}

// 初始化序列订单
func (stasksrv *SerialTaskSrv) Init(serialordertab model.SerialOrder) {

	stasksrv.SerialOrderTab = serialordertab
	stasksrv.SerialOrderStatusTab = serialordertab.SerialOrderStatus
}

// 创建序列订单
func (stasksrv *SerialTaskSrv) Create() (err error) {

	// 插入序列订单
	stasksrv.SerialOrderTab = model.SerialOrder{}
	stasksrv.SerialOrderTab.PlantNr = stasksrv.PlantNr
	stasksrv.SerialOrderTab.ProjectId = stasksrv.ProjectId
	stasksrv.SerialOrderTab.ArtId = stasksrv.ArtId
	stasksrv.SerialOrderTab.WorkOrderId = stasksrv.ParentTaskId
	stasksrv.SerialOrderTab.PlanResourceId = stasksrv.SchedResId
	stasksrv.SerialOrderTab.OrderType = stasksrv.TaskType
	stasksrv.SerialOrderTab.SerialOrderId = stasksrv.TaskId
	stasksrv.SerialOrderTab.PlanQty = float64(stasksrv.PlanQty)
	stasksrv.SerialOrderTab.PlanStartTime = grmi.DateTime(stasksrv.SchedStartTime)
	stasksrv.SerialOrderTab.PlanEndTime = grmi.DateTime(stasksrv.SchedEndTime)
	SetTableLastModify(&stasksrv.SerialOrderTab, stasksrv.SerialOrderTab, common.MODIFY_MODE_CREATE, "rel_service")

	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	dao := dal.NewSerialOrderDAO(session, common.PlantNr, "rel_service")
	if err = dao.InsertOne(&stasksrv.SerialOrderTab); err != nil {
		err = errors.New(fmt.Sprintf("工单%s的序列订单%s头创建失败%v！", stasksrv.ParentTaskId, stasksrv.TaskId, err))
		return
	}

	// 插入序列订单状态行
	stasksrv.SerialOrderStatusTab = model.SerialOrderStatus{}
	stasksrv.SerialOrderStatusTab.PlantNr = stasksrv.PlantNr
	stasksrv.SerialOrderStatusTab.SerialOrderId = stasksrv.TaskId
	stasksrv.SerialOrderStatusTab.Status = common.WO_STATUS_PLANNED
	stasksrv.SerialOrderStatusTab.TriggerObjectId = stasksrv.ParentTaskId
	stasksrv.SerialOrderStatusTab.TriggerEvent = common.BATORD_EVENT_LOCK
	SetTableLastModify(&stasksrv.SerialOrderStatusTab, stasksrv.SerialOrderStatusTab, common.MODIFY_MODE_CREATE, "rel_service")
	serial := dal.NewSerialOrderStatusDAO(session, common.PlantNr, "rel_service")
	if err = serial.InsertOne(&stasksrv.SerialOrderStatusTab); err != nil {
		err = errors.New(fmt.Sprintf("工单%s的序列订单%s状态头创建失败%v！", stasksrv.ParentTaskId, stasksrv.TaskId, err))
		return
	}

	// 插入序列订单状态记录行
	// 如果需要触发动作可在此处添加
	serordstatrectab := model.SerialOrderStatusRecLst{}
	serordstatrectab.PlantNr = stasksrv.PlantNr
	serordstatrectab.SerialOrderId = stasksrv.TaskId
	serordstatrectab.PrevStatus = 0
	serordstatrectab.Status = common.WO_STATUS_PLANNED
	serordstatrectab.OutputEvent = common.OUTPUT_EVENT_SER_NEW
	serordstatrectab.OutputObjectId = serordstatrectab.SerialOrderId
	serordstatrectab.OutputStatus = common.OUTPUT_TRIGGER_UNHANDLED
	SetTableLastModify(&serordstatrectab, serordstatrectab, common.MODIFY_MODE_CREATE, "rel_service")
	serord := dal.NewSerialOrderStatusRecLstDAO(session, common.PlantNr, "rel_service")
	if err = serord.InsertOne(&serordstatrectab); err != nil {
		err = errors.New(fmt.Sprintf("工单%s的序列订单%s状态记录行创建失败%v！", stasksrv.ParentTaskId, stasksrv.TaskId, err))
		return
	}

	return
}

// 下达序列订单
func (stasksrv *SerialTaskSrv) Release() (err error) {
	var prevstatus int

	// 判断序列订单是否可以被下达
	if stasksrv.SerialOrderStatusTab.Status >= common.WO_STATUS_RELEASED {
		return
	}

	// 获取序列订单当前状态
	prevstatus = stasksrv.SerialOrderStatusTab.Status

	// 更新序列订单状态行
	stasksrv.SerialOrderStatusTab.Status = common.WO_STATUS_RELEASED
	stasksrv.SerialOrderStatusTab.TriggerObjectId = stasksrv.ParentTaskId
	stasksrv.SerialOrderStatusTab.TriggerEvent = common.BATORD_EVENT_RELEASE
	SetTableLastModify(&stasksrv.SerialOrderStatusTab, stasksrv.SerialOrderStatusTab, common.MODIFY_MODE_UPDATE, "rel_service")
	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	serial := dal.NewSerialOrderStatusDAO(session, common.PlantNr, "rel_service")
	if err = serial.UpdateOne(&stasksrv.SerialOrderStatusTab); err != nil {
		err = errors.New(fmt.Sprintf("工单%s的序列订单%s状态头更新失败%v！", stasksrv.ParentTaskId, stasksrv.TaskId, err))
		return
	}

	// 插入序列订单状态记录行
	// 如果需要触发动作可在此处添加
	serordstatrectab := model.SerialOrderStatusRecLst{}
	serordstatrectab.PlantNr = stasksrv.PlantNr
	serordstatrectab.SerialOrderId = stasksrv.TaskId
	serordstatrectab.PrevStatus = prevstatus
	serordstatrectab.Status = common.WO_STATUS_RELEASED
	serordstatrectab.OutputEvent = common.OUTPUT_EVENT_SER_REL
	serordstatrectab.OutputObjectId = serordstatrectab.SerialOrderId
	serordstatrectab.OutputStatus = common.OUTPUT_TRIGGER_UNHANDLED
	SetTableLastModify(&serordstatrectab, serordstatrectab, common.MODIFY_MODE_UPDATE, "rel_service")
	serord := dal.NewSerialOrderStatusRecLstDAO(session, common.PlantNr, "rel_service")
	if err = serord.UpdateOne(&serordstatrectab); err != nil {
		err = errors.New(fmt.Sprintf("工单%s的序列订单%s状态记录行创建失败%v！", stasksrv.ParentTaskId, stasksrv.TaskId, err))
		return
	}
	return
}

func (tasksrv *TaskSrv) RecursionComposePO(PO int, POLi *[]baseModel.Process) []int {
	result := make([]int, 0)
	for _, poData := range *POLi {
		prePO, err := strconv.Atoi(poData.PrePO)
		if err != nil {
			prePO = 0
		}
		if prePO == PO {
			if poData.HasDependOn {
				return []int{}
			} else if poData.IsLastPO {
				return []int{poData.PO}
			}else {
				li := tasksrv.RecursionComposePO(poData.PO, POLi)
				result = append(result, poData.PO)
				result = append(result, li...)
				return result
			}
		}
	}
	return []int{}
}
