package implments

import (
	baseDAL "LAPP_ACURA_MOM_BACKEND/dao/base"
	"LAPP_ACURA_MOM_BACKEND/db"
	"LAPP_ACURA_MOM_BACKEND/global"
	"LAPP_ACURA_MOM_BACKEND/grmi"
	baseMeta "LAPP_ACURA_MOM_BACKEND/meta/base"
	jitMeta "LAPP_ACURA_MOM_BACKEND/meta/jit"
	omMeta "LAPP_ACURA_MOM_BACKEND/meta/om"
	baseModel "LAPP_ACURA_MOM_BACKEND/models/base"
	jitModel "LAPP_ACURA_MOM_BACKEND/models/jit"
	omModel "LAPP_ACURA_MOM_BACKEND/models/om"
	"LAPP_ACURA_MOM_BACKEND/utils"
	"errors"
	"fmt"
	"github.com/360EntSecGroup-Skylar/excelize/v2"
	"github.com/go-xorm/xorm"
	"sort"
	"strconv"
	"strings"
	"time"
)

type PackingRecordQuery_PackOrderSummary struct {
	PackOrderId string         `json:"PackingRecordQuery-PackOrderId"` // 包装单号
	Status      int            `json:"PackingRecordQuery-Status"`      // 状态
	PackTime    *grmi.DateTime `json:"PackingRecordQuery-PackTime"`    // 打包时间
	UnpackTime  *grmi.DateTime `json:"PackingRecordQuery-UnpackTime"`  // 拆包时间
	LastUser    string         `json:"PackingRecordQuery-LastUser"`    // 操作人
}

type SeatQuery_SeatSummary struct {
	SerialOrderId    string         `json:"SeatQuery-SerialOrderId"`    // 座椅条码
	PackOrderId      string         `json:"SeatQuery-PackOrderId"`      // 包装单号
	QualityCheckTime *grmi.DateTime `json:"SeatQuery-QualityCheckTime"` // 目检时间
	PackTime         *grmi.DateTime `json:"SeatQuery-PackTime"`         // 打包时间
	ShipOrderId      string         `json:"SeatQuery-ShipOrderId"`      // 发运单号
	ShipTime         *grmi.DateTime `json:"SeatQuery-ShipTime"`         // 发运时间
	ShipType         string         `json:"SeatQuery-ShipType`          // 发运模式
	ProductFamilies  string         `json:"SeatQuery-ProductFamilies"`  // 座椅关联的派生
}

type ProductFamilyInventoryQuery_ProductFamilyStatistics struct {
	ProductFamilyId   string                                         `json:"ProductFamilyInventoryQuery-ProductFamilyId"`   // 派生号
	InventoryQuantity float64                                        `json:"ProductFamilyInventoryQuery-InventoryQuantity"` // 库存数量
	PackOrders        []ProductFamilyInventoryQuery_PackOrderSummary `json:"ProductFamilyInventoryQuery-PackOrders"`        // 派生关联的包装单
}

type ProductFamilyInventoryQuery_PackOrderSummary struct {
	InventoryQuantity float64                                   `json:"ProductFamilyInventoryQuery-InventoryQuantity"` // 库存数量
	PackOrderId       string                                    `json:"ProductFamilyInventoryQuery-PackOrderId"`       // 包装单号
	PackStatus        int                                       `json:"ProductFamilyInventoryQuery-PackStatus"`        // 包装状态
	PackTime          *grmi.DateTime                            `json:"ProductFamilyInventoryQuery-PackTime"`          // 包装时间
	ShipOrderId       string                                    `json:"ProductFamilyInventoryQuery-ShipOrderId"`       // 发运单号
	ShipTime          *grmi.DateTime                            `json:"ProductFamilyInventoryQuery-ShipTime"`          // 发运时间
	ShipType          string                                    `json:"ProductFamilyInventoryQuery-ShipType"`          // 发运模式
	Seats             []ProductFamilyInventoryQuery_SeatSummary `json:"ProductFamilyInventoryQuery-Seats"`             // 包装单关联的座椅
}

type ProductFamilyInventoryQuery_SeatSummary struct {
	SerialOrderId string `json:"ProductFamilyInventoryQuery-SerialOrderId"` // 座椅条码
}

type wrapper_SerialOrder struct {
	omModel.SerialOrder       `xorm:"extends"`
	omModel.SerialOrderStatus `xorm:"extends"`
	jitModel.PackOrderItemLst `xorm:"extends"`
}

func (serialOrder *wrapper_SerialOrder) QualityCheckTime() *grmi.DateTime {
	if serialOrder.QualityCheck != "" {
		return &serialOrder.SerialOrder.QualityCheckTime
	}
	return nil
}

type wrapper_PackOrder struct {
	jitModel.PackOrder        `xorm:"extends"`
	jitModel.PackOrderStatus  `xorm:"extends"`
	jitModel.ShipOrderDataLst `xorm:"extends"`
	ProductFamilyKey          string `xorm:"-"`
}

type RepairInfoSummary struct {
	baseModel.RepairInfo `xorm:"extends"`
	ArtId                string `json:"OM_SerialOrder-ArtId"`
}

func (packOrder *wrapper_PackOrder) PackStatus() int {

	if packOrder.PackOrderStatus.Status == 80 {
		if packOrder.PackOrderStatus.ShipStatus < 80 {
			return 1
		} else if packOrder.PackOrderStatus.ShipStatus == 80 {
			return 2
		} else {
			return 3
		}
	}
	return 0
}

func (packOrder *wrapper_PackOrder) PackTime() *grmi.DateTime {

	if packOrder.PackOrderStatus.Status == 80 {
		return &packOrder.PackOrder.CloseTime
	}
	return nil
}

func (packOrder *wrapper_PackOrder) IsInventory() bool {

	return packOrder.PackOrderStatus.Status == 80 && packOrder.PackOrderStatus.ShipStatus < 80
}

type wrapper_ShipOrder struct {
	jitModel.ShipOrder       `xorm:"extends"`
	jitModel.ShipOrderStatus `xorm:"extends"`
}

func (shipOrder *wrapper_ShipOrder) ShipTime() *grmi.DateTime {

	if shipOrder.ShipOrderStatus.Status == 80 {
		return &shipOrder.ShipOrder.CloseTime
	}
	return nil
}

type wrapper_PackOrder_Lite struct {
	jitModel.PackOrder       `xorm:"extends"`
	jitModel.PackOrderStatus `xorm:"extends"`
}

func (packOrder *wrapper_PackOrder_Lite) PackTime() *grmi.DateTime {

	if packOrder.PackOrderStatus.Status == 80 {
		return &packOrder.PackOrder.CloseTime
	}
	return nil
}

func (packOrder *wrapper_PackOrder_Lite) UnpackTime() *grmi.DateTime {

	if packOrder.PackOrderStatus.Status == 98 {
		return &packOrder.PackOrder.LastModify
	}
	return nil
}

/******************************************************************************
 *
 * @Struct Name              : CustomizedQueryServiceImplement
 *-----------------------------------------------------------------------------
 *
 * @Description              : CustomizedQuery的服务实现
 *
 *
 * @Author                   : chenglei.wei@le-it.com.cn
 *
 * @Date                     : 2022-03-09 10:44
 *
 ******************************************************************************/
type CustomizedQueryServiceImplement struct {
}

/******************************************************************************
 *
 * @Function Name            : NewCustomizedQueryServiceImplement
 *-----------------------------------------------------------------------------
 *
 * @Description              : 创建一个CustomizedQueryServiceImplement实例
 *
 * @Return Value             : CustomizedQueryServiceImplement实例
 *
 * @Author                   : chenglei.wei@le-it.com.cn
 *
 * @Date                     : 2022-03-09 11:02
 *
 ******************************************************************************/
func NewCustomizedQueryServiceImplement() *CustomizedQueryServiceImplement {
	return &CustomizedQueryServiceImplement{}
}

/******************************************************************************
 *
 * @Function Name            : querySerialOrdersWithMapping
 *-----------------------------------------------------------------------------
 *
 * @Description              : 向Excel写入一行
 *
 * @Function Parameters      : 当前访问人员信息
 *
 * @Function Parameters      : 数据访问会话
 *
 * @Function Parameters      : 项目
 *
 * @Return Value             : 发运单列表
 *
 * @Return Value             : 包装单分组
 *
 * @Return Value             : 工单分组
 *
 * @Return Value             : 派生分组
 *
 * @Return Value             : 执行时发生的错误
 *
 * @Author                   : chenglei.wei@le-it.com.cn
 *
 * @Date                     : 2022-03-16 11:15
 *
 ******************************************************************************/
func (impl *CustomizedQueryServiceImplement) querySerialOrdersWithMapping(user *global.User, session *xorm.Session, projectId string) (shipOrders []wrapper_ShipOrder, packOrderGroups map[string][]*wrapper_PackOrder, serialOrderGroups map[string][]*wrapper_SerialOrder, productFamilyKeyMapping map[string][]string, err error) {

	// 查询座椅-座椅状态-包装单项
	serialOrders := make([]wrapper_SerialOrder, 0, 1000)
	{
		query := session.Table(omMeta.SerialOrder.TableName)
		query = query.Join("INNER", omMeta.SerialOrderStatus.TableName, fmt.Sprintf("%s = %s and %s = %s", omMeta.SerialOrder_PlantNr.ColumnName, omMeta.SerialOrderStatus_PlantNr.ColumnName, omMeta.SerialOrder_SerialOrderId.ColumnName, omMeta.SerialOrderStatus_SerialOrderId.ColumnName))
		query = query.Join("LEFT OUTER", jitMeta.PackOrderItemLst.TableName, fmt.Sprintf("%s = %s and %s = %s", omMeta.SerialOrder_PlantNr.ColumnName, jitMeta.PackOrderItemLst_PlantNr.ColumnName, omMeta.SerialOrder_SerialOrderId.ColumnName, jitMeta.PackOrderItemLst_SerialOrderId.ColumnName))
		query = query.Where(fmt.Sprintf("%s = ?", omMeta.SerialOrder_PlantNr.ColumnName), user.PlantNr)
		query = query.Where(fmt.Sprintf("%s <> 'PRE'", omMeta.SerialOrder_OrderType.ColumnName))
		query = query.Where(fmt.Sprintf("%s <> 1", omMeta.SerialOrder_GenerateFlag.ColumnName))
		if projectId != "" {
			query = query.Where(fmt.Sprintf("%s = ?", omMeta.SerialOrder_ProjectId.ColumnName), projectId)
		}
		err = query.Find(&serialOrders)
		if err != nil {
			return
		}
	}
	// 查询包装单-包装单状态-发运单项
	packOrders := make([]wrapper_PackOrder, 0, 1000)
	{
		query := session.Table(jitMeta.PackOrder.TableName)
		query = query.Join("INNER", jitMeta.PackOrderStatus.TableName, fmt.Sprintf("%s = %s and %s = %s", jitMeta.PackOrder_PlantNr.ColumnName, jitMeta.PackOrderStatus_PlantNr.ColumnName, jitMeta.PackOrder_PackOrderId.ColumnName, jitMeta.PackOrderStatus_PackOrderId.ColumnName))
		query = query.Join("LEFT OUTER", jitMeta.ShipOrderDataLst.TableName, fmt.Sprintf("%s = %s and %s = %s", jitMeta.PackOrder_PlantNr.ColumnName, jitMeta.ShipOrderDataLst_PlantNr.ColumnName, jitMeta.PackOrder_PackOrderId.ColumnName, jitMeta.ShipOrderDataLst_PackOrderId.ColumnName))
		query = query.Where(fmt.Sprintf("%s = ?", jitMeta.PackOrder_PlantNr.ColumnName), user.PlantNr)
		query = query.Where(fmt.Sprintf("%s = ?", jitMeta.PackOrderStatus_Status.ColumnName), 80) // 不显示拆包数据
		if projectId != "" {
			query = query.Where(fmt.Sprintf("%s = ?", jitMeta.PackOrder_ProjectId.ColumnName), projectId)
		}
		err = query.Find(&packOrders)
		if err != nil {
			return
		}
	}
	// 查询发运单-发运单状态
	allShipOrders := make([]wrapper_ShipOrder, 0, 1000)
	{
		query := session.Table(jitMeta.ShipOrder.TableName)
		query = query.Join("INNER", jitMeta.ShipOrderStatus.TableName, fmt.Sprintf("%s = %s and %s = %s", jitMeta.ShipOrder_PlantNr.ColumnName, jitMeta.ShipOrderStatus_PlantNr.ColumnName, jitMeta.ShipOrder_ShipOrderId.ColumnName, jitMeta.ShipOrderStatus_ShipOrderId.ColumnName))
		query = query.Where(fmt.Sprintf("%s = ?", jitMeta.ShipOrder_PlantNr.ColumnName), user.PlantNr)
		if projectId != "" {
			query = query.Where(fmt.Sprintf("%s = ?", jitMeta.ShipOrder_ProjectId.ColumnName), projectId)
		}
		err = query.Find(&allShipOrders)
		if err != nil {
			return
		}
	}

	// 合并正式发运单和预发运单
	shipOrderMapping := make(map[string]wrapper_ShipOrder)

	for _, shipOrder := range allShipOrders {
		if shipOrder.ShipType == "ADVANCE" {
			shipOrderMapping[shipOrder.ShipOrder.ShipOrderId] = shipOrder
		}
	}

	for _, shipOrder := range allShipOrders {
		if shipOrder.ShipType == "FORMAL" {
			if _, ok := shipOrderMapping[shipOrder.ShipOrder.SourceId]; ok {
				delete(shipOrderMapping, shipOrder.ShipOrder.SourceId)
			}
			shipOrderMapping[shipOrder.ShipOrder.ShipOrderId] = shipOrder
		}
	}

	for _, shipOrder := range shipOrderMapping {
		shipOrders = append(shipOrders, shipOrder)
	}

	// 查询包装单模板项
	templateItems := make([]jitModel.PackTemplateItemLst, 0, 10)
	{
		query := session.Table(jitMeta.PackTemplateItemLst.TableName)
		query = query.Select(fmt.Sprintf("%s.*", jitMeta.PackTemplateItemLst.TableName))
		query = query.Join("INNER", jitMeta.PackTemplate.TableName, fmt.Sprintf("%s = %s and %s = %s", jitMeta.PackTemplateItemLst_PlantNr.ColumnName, jitMeta.PackTemplate_PlantNr.ColumnName, jitMeta.PackTemplateItemLst_PackTemplateId.ColumnName, jitMeta.PackTemplate_PackTemplateId.ColumnName))
		query = query.Where(fmt.Sprintf("%s = ?", jitMeta.PackTemplate_PlantNr.ColumnName), user.PlantNr)
		query = query.Where(fmt.Sprintf("%s = 1", jitMeta.PackTemplate_EnabledToggle.ColumnName))
		if projectId != "" {
			query = query.Where(fmt.Sprintf("%s = ?", jitMeta.PackTemplate_ProjectId.ColumnName), projectId)
		}
		err = query.Find(&templateItems)
		if err != nil {
			return
		}
	}
	// 按包装单模板分组包装单模板项
	templateItemGroups := make(map[string][]*jitModel.PackTemplateItemLst)
	for i := 0; i < len(templateItems); i++ {
		templateItem := templateItems[i]
		group, ok := templateItemGroups[templateItem.PackTemplateId]
		if !ok {
			group = make([]*jitModel.PackTemplateItemLst, 0, 10)
		}
		templateItemGroups[templateItem.PackTemplateId] = append(group, &templateItems[i])
	}
	// 计算系统中现有派生
	productFamilyKeyMapping = make(map[string][]string)
	for packTemplateId, group := range templateItemGroups {
		parts := make([]string, 0, 10)
		for _, templateItem := range group {
			parts = append(parts, templateItem.PartId)
		}
		sort.Strings(parts)
		productFamilyKey := strings.Join(parts, ",")
		packTemplateIdGroup, ok := productFamilyKeyMapping[productFamilyKey]
		if !ok {
			packTemplateIdGroup = make([]string, 0, 10)
		}
		productFamilyKeyMapping[productFamilyKey] = append(packTemplateIdGroup, packTemplateId)
	}
	// 按发运单分组包装单, key为"" 为没有关联到发运单的包装单(不应该出现)
	packOrderGroups = make(map[string][]*wrapper_PackOrder)
	for i := 0; i < len(packOrders); i++ {
		packOrder := packOrders[i]
		group, ok := packOrderGroups[packOrder.ShipOrderId]
		if !ok {
			group = make([]*wrapper_PackOrder, 0, 10)
		}
		group = append(group, &packOrders[i])
		packOrderGroups[packOrder.ShipOrderId] = group
	}
	// 按包装单分组座椅, key为"" 为没有关联到包装单的座椅
	serialOrderGroups = make(map[string][]*wrapper_SerialOrder)
	for i := 0; i < len(serialOrders); i++ {
		serialOrder := serialOrders[i]
		group, ok := serialOrderGroups[serialOrder.PackOrderId]
		if !ok {
			group = make([]*wrapper_SerialOrder, 0, 10)
		}
		group = append(group, &serialOrders[i])
		serialOrderGroups[serialOrder.PackOrderId] = group
	}
	// 计算包装单的派生
	for i := 0; i < len(packOrders); i++ {
		if serialOrderGroup, ok := serialOrderGroups[packOrders[i].PackOrder.PackOrderId]; ok {
			parts := make([]string, 0, 10)
			for _, serialOrder := range serialOrderGroup {
				parts = append(parts, serialOrder.PartId)
			}
			sort.Strings(parts)
			packOrders[i].ProductFamilyKey = strings.Join(parts, ",")
		}
	}
	return
}

/******************************************************************************
 *
 * @Reference LAPP_ACURA_MOM_BACKEND/services/report/CustomizedQueryService.ProductFamilyInventoryQuery
 *
 ******************************************************************************/
func (impl *CustomizedQueryServiceImplement) ProductFamilyInventoryQuery(user *global.User, productFamilyId string, projectId string, serialOrderId string) (result []ProductFamilyInventoryQuery_ProductFamilyStatistics, err error) {

	productFamilyId = strings.TrimSpace(productFamilyId)
	projectId = strings.TrimSpace(projectId)
	serialOrderId = strings.TrimSpace(serialOrderId)

	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()

	shipOrders, packOrderGroups, serialOrderGroups, productFamilyKeyMapping, err := impl.querySerialOrdersWithMapping(user, session, projectId)
	if err != nil {
		return
	}

	productFamilyStatisticsMapping := make(map[string]*ProductFamilyInventoryQuery_ProductFamilyStatistics)

	for _, packTemplateIdGroup := range productFamilyKeyMapping {
		for _, packTemplateId := range packTemplateIdGroup {
			if productFamilyId != "" && packTemplateId != productFamilyId {
				continue
			}
			productFamilyStatistics := ProductFamilyInventoryQuery_ProductFamilyStatistics{
				ProductFamilyId: packTemplateId,
				PackOrders:      make([]ProductFamilyInventoryQuery_PackOrderSummary, 0, 10),
			}
			productFamilyStatisticsMapping[packTemplateId] = &productFamilyStatistics
		}
	}

	for _, shipOrder := range shipOrders {
		packOrderGroup, ok := packOrderGroups[shipOrder.ShipOrder.ShipOrderId]
		if !ok {
			continue
		}
		delete(packOrderGroups, shipOrder.ShipOrder.ShipOrderId)
		for _, packOrder := range packOrderGroup {
			// 没找到座椅
			serialOrderGroup, ok := serialOrderGroups[packOrder.PackOrder.PackOrderId]
			if !ok {
				continue
			}
			delete(serialOrderGroups, packOrder.PackOrder.PackOrderId)
			seats := make([]ProductFamilyInventoryQuery_SeatSummary, 0, 10)
			for _, serialOrder := range serialOrderGroup {
				if serialOrderId == "" || serialOrderId == serialOrder.SerialOrder.SerialOrderId {
					seats = append(seats, ProductFamilyInventoryQuery_SeatSummary{
						SerialOrderId: serialOrder.SerialOrder.SerialOrderId,
					})
				}
			}
			// 没有匹配的座椅
			if len(seats) <= 0 {
				continue
			}
			var inventoryQuantity float64 = 0
			if packOrder.IsInventory() {
				inventoryQuantity = 1
			}
			packTemplateIdLst, ok := productFamilyKeyMapping[packOrder.ProductFamilyKey]
			// 没找到派生
			if !ok {
				continue
			}
			packOrderSummary := ProductFamilyInventoryQuery_PackOrderSummary{
				PackOrderId:       packOrder.PackOrder.PackOrderId,
				InventoryQuantity: inventoryQuantity,
				PackStatus:        packOrder.PackStatus(),
				PackTime:          packOrder.PackTime(),
				ShipOrderId:       shipOrder.ShipOrder.ShipOrderId,
				ShipTime:          shipOrder.ShipTime(),
				ShipType:          shipOrder.ShipType,
				Seats:             seats,
			}
			for _, packTemplateId := range packTemplateIdLst {
				// 没找到模板
				productFamilyStatistics, ok := productFamilyStatisticsMapping[packTemplateId]
				if !ok {
					continue
				}
				productFamilyStatisticsMapping[packTemplateId].InventoryQuantity += inventoryQuantity
				productFamilyStatisticsMapping[packTemplateId].PackOrders = append(productFamilyStatistics.PackOrders, packOrderSummary)
			}
		}
	}
	packOrderGroup, ok := packOrderGroups[""] // 未关联发运单的包装单
	if ok {
		for _, packOrder := range packOrderGroup {
			// 没找到座椅
			serialOrderGroup, ok := serialOrderGroups[packOrder.PackOrder.PackOrderId]
			if !ok {
				continue
			}
			delete(serialOrderGroups, packOrder.PackOrder.PackOrderId)
			seats := make([]ProductFamilyInventoryQuery_SeatSummary, 0, 10)
			for _, serialOrder := range serialOrderGroup {
				if serialOrderId == "" || serialOrderId == serialOrder.SerialOrder.SerialOrderId {
					seats = append(seats, ProductFamilyInventoryQuery_SeatSummary{
						SerialOrderId: serialOrder.SerialOrder.SerialOrderId,
					})
				}
			}
			// 没有匹配的座椅
			if len(seats) <= 0 {
				continue
			}
			var inventoryQuantity float64 = 0
			if packOrder.IsInventory() {
				inventoryQuantity = 1
			}
			packTemplateIdLst, ok := productFamilyKeyMapping[packOrder.ProductFamilyKey]
			// 没找到派生
			if !ok {
				continue
			}
			packOrderSummary := ProductFamilyInventoryQuery_PackOrderSummary{
				PackOrderId:       packOrder.PackOrder.PackOrderId,
				InventoryQuantity: inventoryQuantity,
				PackStatus:        packOrder.PackStatus(),
				PackTime:          packOrder.PackTime(),
				Seats:             seats,
			}
			for _, packTemplateId := range packTemplateIdLst {
				// 没找到模板
				productFamilyStatistics, ok := productFamilyStatisticsMapping[packTemplateId]
				if !ok {
					continue
				}
				productFamilyStatisticsMapping[packTemplateId].InventoryQuantity += inventoryQuantity
				productFamilyStatisticsMapping[packTemplateId].PackOrders = append(productFamilyStatistics.PackOrders, packOrderSummary)
			}
		}
	}

	result = make([]ProductFamilyInventoryQuery_ProductFamilyStatistics, 0, 1)
	for _, productFamilyStatistics := range productFamilyStatisticsMapping {
		// 如果按座椅查询,则需要过滤到没有座椅的空的派生
		if serialOrderId == "" || len(productFamilyStatistics.PackOrders) > 0 {
			result = append(result, *productFamilyStatistics)
		}
	}

	return
}

/******************************************************************************
 *
 * @Reference LAPP_ACURA_MOM_BACKEND/services/report/CustomizedQueryService.ProductFamilyInventoryExportExcel
 *
 ******************************************************************************/
func (impl *CustomizedQueryServiceImplement) ProductFamilyInventoryExportExcel(user *global.User, productFamilyId string, projectId string, serialOrderId string) (filePathh string, err error) {

	result, err := impl.ProductFamilyInventoryQuery(user, productFamilyId, projectId, serialOrderId)
	if err != nil {
		return
	}

	file := excelize.NewFile()
	sheetName := "派生库存"
	file.NewSheet(sheetName)
	file.DeleteSheet("Sheet1")
	streamWriter, err := file.NewStreamWriter(sheetName)
	if err != nil {
		return
	}

	rowIndex := 1
	if err = WriteExcelRow(streamWriter, &rowIndex, "派生号", "库存数量"); err != nil {
		return
	}

	for _, productFamily := range result {
		if err = WriteExcelRow(streamWriter, &rowIndex, productFamily.ProductFamilyId, productFamily.InventoryQuantity); err != nil {
			return
		}

		if len(productFamily.PackOrders) > 0 {
			if err = WriteExcelRow(streamWriter, &rowIndex, "", "库存数量", "包装单号", "包装状态", "包装时间", "发运单号", "发运时间", "发运模式"); err != nil {
				return
			}
			for _, packOrder := range productFamily.PackOrders {
				if err = WriteExcelRow(streamWriter, &rowIndex, "", packOrder.InventoryQuantity, packOrder.PackOrderId, packOrder.PackStatus, packOrder.PackTime, packOrder.ShipOrderId, packOrder.ShipTime, packOrder.ShipType); err != nil {
					return
				}
				if len(packOrder.Seats) > 0 {
					if err = WriteExcelRow(streamWriter, &rowIndex, "", "", "座椅条码"); err != nil {
						return
					}
					for _, seat := range packOrder.Seats {
						if err = WriteExcelRow(streamWriter, &rowIndex, "", "", seat.SerialOrderId); err != nil {
							return
						}
					}
				}
			}
		}
	}
	if err = streamWriter.Flush(); err != nil {
		return
	}
	return SaveExcelFile(file, "派生库存查询")
}

/******************************************************************************
 *
 * @Function Name            : WriteExcelRow
 *-----------------------------------------------------------------------------
 *
 * @Description              : 向Excel写入一行
 *
 * @Function Parameters      : excel写入器
 *
 * @Function Parameters      : 行索引/位置
 *
 * @Function Parameters      : 行的值列表
 *
 * @Return Value             : 执行时发生的错误
 *
 * @Author                   : chenglei.wei@le-it.com.cn
 *
 * @Date                     : 2022-03-16 11:15
 *
 ******************************************************************************/
func WriteExcelRow(streamWriter *excelize.StreamWriter, rowIndex *int, rowValues ...interface{}) (err error) {

	cell, _ := excelize.CoordinatesToCellName(1, *rowIndex)
	if err = streamWriter.SetRow(cell, rowValues); err != nil {
		return
	}
	*rowIndex++
	return
}

/******************************************************************************
 *
 * @Function Name            : SaveExcelFile
 *-----------------------------------------------------------------------------
 *
 * @Description              : 保存Excel文件
 *
 * @Function Parameters      : excel句柄
 *
 * @Function Parameters      : 文件名前缀
 *
 * @Return Value             : 文件路径
 *
 * @Return Value             : 执行时发生的错误
 *
 * @Author                   : chenglei.wei@le-it.com.cn
 *
 * @Date                     : 2022-03-16 11:15
 *
 ******************************************************************************/
func SaveExcelFile(file *excelize.File, prefixFilename string) (filePathh string, err error) {

	filename := utils.MakeOrderSn(prefixFilename) + ".xlsx"
	dirname, err := utils.GetCurrentPath("web/public/uploadxlsx")
	if err != nil {
		return "", err
	}
	filePathh = dirname + "/" + filename
	err = file.SaveAs(filePathh)
	if err != nil {
		return "", err
	}
	pathLi := strings.Split(filePathh, "web\\")
	if len(pathLi) != 2 {
		return "", errors.New("导出失败,请重试")
	}
	return pathLi[1], nil
}

/******************************************************************************
 *
 * @Reference LAPP_ACURA_MOM_BACKEND/services/report/CustomizedQueryService.SeatQuery
 *
 ******************************************************************************/
func (impl *CustomizedQueryServiceImplement) SeatQuery(user *global.User, serialOrderId string, status string) (result []SeatQuery_SeatSummary, err error) {

	serialOrderId = strings.TrimSpace(serialOrderId)
	status = strings.ToLower(strings.TrimSpace(status))

	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	switch status {
	case "":
		break
	case "packed":
		break
	case "ok":
		break
	default:
		err = errors.New("状态查询条件错误")
		return
	}

	shipOrders, packOrderGroups, serialOrderGroups, productFamilyKeyMapping, err := impl.querySerialOrdersWithMapping(user, session, "")
	if err != nil {
		return
	}

	productFamilyStatisticsMapping := make(map[string]*ProductFamilyInventoryQuery_ProductFamilyStatistics)

	result = make([]SeatQuery_SeatSummary, 0, 1000)
	for _, packTemplateIdGroup := range productFamilyKeyMapping {
		for _, packTemplateId := range packTemplateIdGroup {
			productFamilyStatistics := ProductFamilyInventoryQuery_ProductFamilyStatistics{
				ProductFamilyId: packTemplateId,
				PackOrders:      make([]ProductFamilyInventoryQuery_PackOrderSummary, 0, 10),
			}
			productFamilyStatisticsMapping[packTemplateId] = &productFamilyStatistics
		}
	}

	for _, shipOrder := range shipOrders {
		if shipOrder.ShipOrder.ShipOrderId == "C20220306238108J01" {
			fmt.Printf("-0")
		}
		packOrderGroup, ok := packOrderGroups[shipOrder.ShipOrder.ShipOrderId]
		if !ok {
			continue
		}
		delete(packOrderGroups, shipOrder.ShipOrder.ShipOrderId)
		if shipOrder.ShipOrder.ShipOrderId == "C20220306238108J01" {
			fmt.Printf("-1")
		}
		for _, packOrder := range packOrderGroup {
			if status == "packed" && packOrder.PackOrderStatus.Status != 80 {
				continue
			}
			// 没找到座椅
			serialOrderGroup, ok := serialOrderGroups[packOrder.PackOrder.PackOrderId]
			if !ok {
				continue
			}
			delete(serialOrderGroups, packOrder.PackOrder.PackOrderId)
			if shipOrder.ShipOrder.ShipOrderId == "C20220306238108J01" {
				fmt.Printf("-2")
			}
			var packTemplateIds string
			packTemplateIdLst, ok := productFamilyKeyMapping[packOrder.ProductFamilyKey]
			if ok {
				packTemplateIds = strings.Join(packTemplateIdLst, ",")
			}
			for _, serialOrder := range serialOrderGroup {
				if (serialOrderId == "" || serialOrderId == serialOrder.SerialOrder.SerialOrderId) && (status != "ok" || serialOrder.SerialOrder.QualityCheck == "OK") {
					result = append(result, SeatQuery_SeatSummary{
						SerialOrderId:    serialOrder.SerialOrder.SerialOrderId,
						QualityCheckTime: serialOrder.QualityCheckTime(),
						PackOrderId:      packOrder.PackOrder.PackOrderId,
						PackTime:         packOrder.PackTime(),
						ProductFamilies:  packTemplateIds,
						ShipOrderId:      shipOrder.ShipOrder.ShipOrderId,
						ShipTime:         shipOrder.ShipTime(),
						ShipType:         shipOrder.ShipType,
					})
				}
			}
		}
	}
	packOrderGroup, ok := packOrderGroups[""] // 未关联发运单的包装单
	if ok {
		for _, packOrder := range packOrderGroup {
			if status == "packed" && packOrder.PackOrderStatus.Status != 80 {
				continue
			}
			// 没找到座椅
			serialOrderGroup, ok := serialOrderGroups[packOrder.PackOrder.PackOrderId]
			if !ok {
				continue
			}
			delete(serialOrderGroups, packOrder.PackOrder.PackOrderId)

			var packTemplateIds string
			packTemplateIdLst, ok := productFamilyKeyMapping[packOrder.ProductFamilyKey]
			if ok {
				packTemplateIds = strings.Join(packTemplateIdLst, ",")
			}
			for _, serialOrder := range serialOrderGroup {
				if (serialOrderId == "" || serialOrderId == serialOrder.SerialOrder.SerialOrderId) && (status != "ok" || serialOrder.SerialOrder.QualityCheck == "OK") {
					result = append(result, SeatQuery_SeatSummary{
						SerialOrderId:    serialOrder.SerialOrder.SerialOrderId,
						QualityCheckTime: serialOrder.QualityCheckTime(),
						PackOrderId:      packOrder.PackOrder.PackOrderId,
						PackTime:         packOrder.PackTime(),
						ProductFamilies:  packTemplateIds,
					})
				}
			}
		}
	}
	if status != "packed" {
		serialOrderGroup, ok := serialOrderGroups[""] // 未关联发运单的包装单
		if ok {
			for _, serialOrder := range serialOrderGroup {
				if (serialOrderId == "" || serialOrderId == serialOrder.SerialOrder.SerialOrderId) && (status != "ok" || serialOrder.SerialOrder.QualityCheck == "OK") {
					result = append(result, SeatQuery_SeatSummary{
						SerialOrderId:    serialOrder.SerialOrder.SerialOrderId,
						QualityCheckTime: serialOrder.QualityCheckTime(),
					})
				}
			}
		}
	}

	return
}

/******************************************************************************
 *
 * @Reference LAPP_ACURA_MOM_BACKEND/services/report/CustomizedQueryService.SeatExportExcel
 *
 ******************************************************************************/
func (impl *CustomizedQueryServiceImplement) SeatExportExcel(user *global.User, serialOrderId string, status string) (filePathh string, err error) {

	result, err := impl.SeatQuery(user, serialOrderId, status)
	if err != nil {
		return
	}

	data := make([]interface{}, len(result))
	for i, item := range result {
		data[i] = item
	}

	return grmi.SaveExcelFile(data, []string{"座椅条码", "包装单号", "目检时间", "打包时间", "发运单号", "发运时间", "发运模式", "座椅关联的派生"}, "座椅", "座椅查询")
}

/******************************************************************************
 *
 * @Reference LAPP_ACURA_MOM_BACKEND/services/report/CustomizedQueryService.PackingRecordQuery
 *
 ******************************************************************************/
func (impl *CustomizedQueryServiceImplement) PackingRecordQuery(user *global.User, serialOrderId string) (result []PackingRecordQuery_PackOrderSummary, err error) {

	serialOrderId = strings.TrimSpace(serialOrderId)

	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()

	packOrders := make([]wrapper_PackOrder_Lite, 0, 1000)
	{
		query := session.Table(omMeta.SerialOrder.TableName)
		query = query.Select(fmt.Sprintf("%s.*, %s.*", jitMeta.PackOrder.TableName, jitMeta.PackOrderStatus.TableName))
		query = query.Join("INNER", omMeta.SerialOrderStatus.TableName, fmt.Sprintf("%s = %s and %s = %s", omMeta.SerialOrder_PlantNr.ColumnName, omMeta.SerialOrderStatus_PlantNr.ColumnName, omMeta.SerialOrder_SerialOrderId.ColumnName, omMeta.SerialOrderStatus_SerialOrderId.ColumnName))
		query = query.Join("LEFT OUTER", jitMeta.PackOrderItemLst.TableName, fmt.Sprintf("%s = %s and %s = %s", omMeta.SerialOrder_PlantNr.ColumnName, jitMeta.PackOrderItemLst_PlantNr.ColumnName, omMeta.SerialOrder_SerialOrderId.ColumnName, jitMeta.PackOrderItemLst_SerialOrderId.ColumnName))
		query = query.Join("LEFT OUTER", jitMeta.PackOrder.TableName, fmt.Sprintf("%s = %s and %s = %s", jitMeta.PackOrderItemLst_PlantNr.ColumnName, jitMeta.PackOrder_PlantNr.ColumnName, jitMeta.PackOrderItemLst_PackOrderId.ColumnName, jitMeta.PackOrder_PackOrderId.ColumnName))
		query = query.Join("LEFT OUTER", jitMeta.PackOrderStatus.TableName, fmt.Sprintf("%s = %s and %s = %s", jitMeta.PackOrderItemLst_PlantNr.ColumnName, jitMeta.PackOrderStatus_PlantNr.ColumnName, jitMeta.PackOrderItemLst_PackOrderId.ColumnName, jitMeta.PackOrderStatus_PackOrderId.ColumnName))
		query = query.Where(fmt.Sprintf("%s = ?", omMeta.SerialOrder_PlantNr.ColumnName), user.PlantNr)
		query = query.Where(fmt.Sprintf("%s <> 'PRE'", omMeta.SerialOrder_OrderType.ColumnName))
		query = query.Where(fmt.Sprintf("%s <> 1", omMeta.SerialOrder_GenerateFlag.ColumnName))
		query = query.Where(fmt.Sprintf("%s = ?", omMeta.SerialOrder_SerialOrderId.ColumnName), serialOrderId)
		err = query.Find(&packOrders)
		if err != nil {
			return
		}
	}
	result = make([]PackingRecordQuery_PackOrderSummary, 0, 1000)
	for _, packOrder := range packOrders {
		result = append(result, PackingRecordQuery_PackOrderSummary{
			PackOrderId: packOrder.PackOrder.PackOrderId,
			Status:      packOrder.PackOrderStatus.Status,
			PackTime:    packOrder.PackTime(),
			UnpackTime:  packOrder.UnpackTime(),
			LastUser:    packOrder.PackOrder.LastUser,
		})
	}
	return
}

/******************************************************************************
 *
 * @Reference LAPP_ACURA_MOM_BACKEND/services/report/CustomizedQueryService.PackingRecordExportExcel
 *
 ******************************************************************************/
func (impl *CustomizedQueryServiceImplement) PackingRecordExportExcel(user *global.User, serialOrderId string) (filePathh string, err error) {

	result, err := impl.PackingRecordQuery(user, serialOrderId)
	if err != nil {
		return
	}

	data := make([]interface{}, len(result))
	for i, item := range result {
		data[i] = item
	}

	return grmi.SaveExcelFile(data, []string{"包装单号", "状态", "打包时间", "拆包时间", "操作人"}, "打包记录", "打包记录查询")
}

/******************************************************************************
 *
 * @Reference LAPP_ACURA_MOM_BACKEND/services/report/CustomizedQueryService.RepairQuery
 *
 ******************************************************************************/
func (impl *CustomizedQueryServiceImplement) RepairQuery(user *global.User, paging *grmi.Paging, workLineId string, artId string, repairReason string, beginTime *time.Time, endTime *time.Time) (result grmi.PagingResult, err error) {

	workLineId = strings.TrimSpace(workLineId)
	repairReason = strings.TrimSpace(repairReason)

	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()

	query := session.Table("RepairInfo")
	on := fmt.Sprintf("%s = %d and RepairInfo.SerialOrderId = %s", omMeta.SerialOrder_PlantNr.ColumnName, user.PlantNr, omMeta.SerialOrder_SerialOrderId.ColumnName)
	query = query.Join("LEFT OUTER", omMeta.SerialOrder.TableName, on)
	if workLineId != "" {
		query.Where("RepairInfo.WorkLineId = ?", workLineId)
	}
	if artId != "" {
		query.Where(fmt.Sprintf("%s = ?", omMeta.SerialOrder_ArtId.ColumnName), artId)
	}
	if repairReason != "" {
		query.Where("RepairInfo.RepairReason like concat('%', ?, '%')", repairReason)
	}
	if beginTime != nil {
		query.Where("RepairInfo.LastModify >= ?", *beginTime)
	}
	if endTime != nil {
		query.Where("RepairInfo.LastModify <= ?", *endTime)
	}

	count, err := query.Count(new(RepairInfoSummary))
	if err != nil {
		return grmi.EmptyPagingResult, err
	}

	var parameters []interface{}
	where := " where 1=1"
	if workLineId != "" {
		where += " and RepairInfo.WorkLineId = ?"
		parameters = append(parameters, workLineId)
	}
	if artId != "" {
		where += fmt.Sprintf(" and %s = ?", omMeta.SerialOrder_ArtId.ColumnName)
		parameters = append(parameters, artId)
	}
	if repairReason != "" {
		where += " and RepairInfo.RepairReason like concat('%', ?, '%')"
		parameters = append(parameters, repairReason)
	}
	if beginTime != nil {
		where += " and RepairInfo.LastModify >= ?"
		parameters = append(parameters, *beginTime)
	}
	if endTime != nil {
		where += " and RepairInfo.LastModify <= ?"
		parameters = append(parameters, *endTime)
	}
	parameters = append(parameters, paging.Offset(), paging.Size)
	data := make([]RepairInfoSummary, 0, 10)
	err = session.SQL("select RepairInfo.*, "+omMeta.SerialOrder.TableName+".ArtId from RepairInfo left join "+omMeta.SerialOrder.TableName+" on "+on+where+" order by RepairInfo.SerialOrderId offset ? row fetch next ? row only", parameters...).Find(&data)
	if err != nil {
		return grmi.EmptyPagingResult, err
	}
	return grmi.PagingResult{Records: data, Count: count, PageNumber: paging.Number, PageSize: paging.Size}, nil
}

type RepairCountByDate struct {
	Reason string
	Date   string
	Amount int
}

type TableValues struct {
	XAxisHanders []string
	YAxisHanders []string
	Cells        [][]int
}

/******************************************************************************
 *
 * @Reference LAPP_ACURA_MOM_BACKEND/services/report/CustomizedQueryService.RepairAnalysis_CountByDate
 *
 ******************************************************************************/
func (impl *CustomizedQueryServiceImplement) RepairAnalysis_CountByDate(_ *global.User, beginDate *time.Time, endDate *time.Time) (result TableValues, err error) {

	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()

	var parameters []interface{}
	where := " where 1=1"
	if beginDate != nil {
		where += " and RepairAnalysis_CountByDate.Date >= ?"
		parameters = append(parameters, *beginDate)
	}
	if endDate != nil {
		var oneDay time.Duration
		oneDay, err = time.ParseDuration("24h")
		if err != nil {
			return
		}
		where += " and RepairAnalysis_CountByDate.Date < ?"
		parameters = append(parameters, (*endDate).Add(oneDay))
	}
	items := make([]RepairCountByDate, 0, 10)
	err = session.SQL("select * from RepairAnalysis_CountByDate"+where, parameters...).Find(&items)
	if err != nil {
		return
	}
	reasonMapping := make(map[string]map[string]int, 10)
	dateMapping := make(map[string][]int, 10)
	for _, item := range items {
		if _, ok := dateMapping[item.Date]; !ok {
			dateMapping[item.Date] = make([]int, 0, 10)
		}
		if _, ok := reasonMapping[item.Reason]; !ok {
			reasonMapping[item.Reason] = make(map[string]int, 10)
		}
		if _, ok := reasonMapping[item.Reason][item.Date]; !ok {
			reasonMapping[item.Reason][item.Date] = item.Amount
		}
	}
	xHeaders := make([]string, 0, len(reasonMapping))
	for reason := range reasonMapping {
		xHeaders = append(xHeaders, reason)
	}
	sort.Strings(xHeaders)
	yHeaders := make([]string, 0, len(dateMapping))
	for date := range dateMapping {
		yHeaders = append(yHeaders, date)
	}
	sort.Strings(yHeaders)
	valuess := make([][]int, len(xHeaders))
	for x, reason := range xHeaders {
		values := make([]int, len(yHeaders))
		for y, date := range yHeaders {
			values[y] = 0
			if group, ok := reasonMapping[reason]; ok {
				if value, ok := group[date]; ok {
					values[y] = value
				}
			}
		}
		valuess[x] = values
	}
	result.XAxisHanders = xHeaders
	result.YAxisHanders = yHeaders
	result.Cells = valuess
	return
}

type serialOrderWithArticle struct {
	omModel.SerialOrder       `xorm:"extends"`
	omModel.SerialOrderStatus `xorm:"extends"`
	baseModel.Article         `xorm:"extends"`
}

type WorkLineDateReportItem struct {
	Date           grmi.Date `json:"WorkLineDateReportItem-Date"`
	WorkLineId     string    `json:"WorkLineDateReportItem-WorkLineId"`
	ArtId          string    `json:"OM_SerialOrder-ArtId"`
	Descr          string    `json:"Article-Descr1"`
	PlanAmount     int       `json:"WorkLineDateReportItem-PlanAmount"`
	FinishedAmount int       `json:"WorkLineDateReportItem-FinishedAmount"`
	RepairAmount   int       `json:"WorkLineDateReportItem-RepairAmount"`
	TotalAmount    int       `json:"WorkLineDateReportItem-TotalAmount"`
}

/******************************************************************************
 *
 * @Function Name            : getPlanSerialOrders
 *-----------------------------------------------------------------------------
 *
 * @Description              : 获取计划的导航
 *
 * @Function Parameters      : 数据访问会话
 *
 * @Function Parameters      : 当前访问人员信息
 *
 * @Function Parameters      : 产线
 *
 * @Function Parameters      : 日期范围-开始
 *
 * @Function Parameters      : 日期范围-结束
 *
 * @Return Value             : 查询结果
 *
 * @Return Value             : 执行时发生的错误
 *
 * @Author                   : chenglei.wei@le-it.com.cn
 *
 * @Date                     : 2022-03-16 11:15
 *
 ******************************************************************************/
func (impl *CustomizedQueryServiceImplement) getPlanSerialOrders(session *xorm.Session, user *global.User, workLineId string, beginDate *time.Time, endDate *time.Time) ([]serialOrderWithArticle, error) {

	planSerialOrders := make([]serialOrderWithArticle, 0, 1000)
	{
		query := session.Table(omMeta.SerialOrder.TableName)
		query = query.Join("INNER", omMeta.SerialOrderStatus.TableName, fmt.Sprintf("%s = %s and %s = %s", omMeta.SerialOrder_PlantNr.ColumnName, omMeta.SerialOrderStatus_PlantNr.ColumnName, omMeta.SerialOrder_SerialOrderId.ColumnName, omMeta.SerialOrderStatus_SerialOrderId.ColumnName))
		query = query.Join("LEFT OUTER", jitMeta.PackOrderItemLst.TableName, fmt.Sprintf("%s = %s and %s = %s", omMeta.SerialOrder_PlantNr.ColumnName, jitMeta.PackOrderItemLst_PlantNr.ColumnName, omMeta.SerialOrder_SerialOrderId.ColumnName, jitMeta.PackOrderItemLst_SerialOrderId.ColumnName))
		query = query.Where(fmt.Sprintf("%s = ?", omMeta.SerialOrder_PlantNr.ColumnName), user.PlantNr)

		if workLineId != "" {
			query = query.Where(fmt.Sprintf("%s = ?", omMeta.SerialOrder_UsedResourceId.ColumnName), workLineId)
		}
		if beginDate != nil {
			query = query.Where(fmt.Sprintf("%s >= ?", omMeta.SerialOrder_PlanEndDate.ColumnName), *beginDate)
		}
		if endDate != nil {
			oneDay, err := time.ParseDuration("24h")
			if err != nil {
				return nil, err
			}
			query = query.Where(fmt.Sprintf("%s < ?", omMeta.SerialOrder_PlanEndDate.ColumnName), (*endDate).Add(oneDay))
		}
		err := query.Find(&planSerialOrders)
		if err != nil {
			return nil, err
		}
	}
	return planSerialOrders, nil
}

/******************************************************************************
 *
 * @Function Name            : getEndSerialOrders
 *-----------------------------------------------------------------------------
 *
 * @Description              : 获取完成的工单
 *
 * @Function Parameters      : 数据访问会话
 *
 * @Function Parameters      : 当前访问人员信息
 *
 * @Function Parameters      : 产线
 *
 * @Function Parameters      : 日期范围-开始
 *
 * @Function Parameters      : 日期范围-结束
 *
 * @Return Value             : 查询结果
 *
 * @Return Value             : 执行时发生的错误
 *
 * @Author                   : chenglei.wei@le-it.com.cn
 *
 * @Date                     : 2022-03-16 11:15
 *
 ******************************************************************************/
func (impl *CustomizedQueryServiceImplement) getEndSerialOrders(session *xorm.Session, user *global.User, workLineId string, beginDate *time.Time, endDate *time.Time) ([]serialOrderWithArticle, error) {

	endSerialOrders := make([]serialOrderWithArticle, 0, 1000)
	{
		query := session.Table(omMeta.SerialOrder.TableName)
		query = query.Join("INNER", omMeta.SerialOrderStatus.TableName, fmt.Sprintf("%s = %s and %s = %s", omMeta.SerialOrder_PlantNr.ColumnName, omMeta.SerialOrderStatus_PlantNr.ColumnName, omMeta.SerialOrder_SerialOrderId.ColumnName, omMeta.SerialOrderStatus_SerialOrderId.ColumnName))
		query = query.Join("LEFT OUTER", jitMeta.PackOrderItemLst.TableName, fmt.Sprintf("%s = %s and %s = %s", omMeta.SerialOrder_PlantNr.ColumnName, jitMeta.PackOrderItemLst_PlantNr.ColumnName, omMeta.SerialOrder_SerialOrderId.ColumnName, jitMeta.PackOrderItemLst_SerialOrderId.ColumnName))
		query = query.Where(fmt.Sprintf("%s = ?", omMeta.SerialOrder_PlantNr.ColumnName), user.PlantNr)

		if workLineId != "" {
			query = query.Where(fmt.Sprintf("%s = ?", omMeta.SerialOrder_UsedResourceId.ColumnName), workLineId)
		}
		if beginDate != nil {
			query = query.Where(fmt.Sprintf("FORMAT(%s, 'yyyy-MM-dd') >= ?", omMeta.SerialOrder_ActEndTime.ColumnName), *beginDate)
		}
		if endDate != nil {
			oneDay, err := time.ParseDuration("24h")
			if err != nil {
				return nil, err
			}
			query = query.Where(fmt.Sprintf("FORMAT(%s, 'yyyy-MM-dd') < ?", omMeta.SerialOrder_ActEndTime.ColumnName), (*endDate).Add(oneDay))
		}
		query = query.Where(fmt.Sprintf("%s IN (39, 79, 80)", omMeta.SerialOrderStatus_Status.ColumnName))
		err := query.Find(&endSerialOrders)
		if err != nil {
			return nil, err
		}
	}
	return endSerialOrders, nil
}

/******************************************************************************
 *
 * @Reference LAPP_ACURA_MOM_BACKEND/services/report/CustomizedQueryService.WorkLineDateReport
 *
 ******************************************************************************/
func (impl *CustomizedQueryServiceImplement) WorkLineDateReport(user *global.User, workLineId string, beginDate *time.Time, endDate *time.Time) (result []WorkLineDateReportItem, err error) {

	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()

	planSerialOrders, err := impl.getPlanSerialOrders(session, user, workLineId, beginDate, endDate)
	if err != nil {
		return
	}

	endSerialOrders, err := impl.getEndSerialOrders(session, user, workLineId, beginDate, endDate)
	if err != nil {
		return
	}

	itemMapping := make(map[string]WorkLineDateReportItem)

	for _, planSerialOrder := range planSerialOrders {
		date := planSerialOrder.PlanEndDate.ToString()
		item, ok := itemMapping[date]
		if !ok {
			item = WorkLineDateReportItem{
				Date:       planSerialOrder.PlanEndDate,
				WorkLineId: planSerialOrder.SerialOrder.UsedResourceId,
				ArtId:      planSerialOrder.SerialOrder.ArtId,
				Descr:      planSerialOrder.Descr1,
			}
		}
		item.PlanAmount += 1
		itemMapping[date] = item
	}

	for _, endSerialOrder := range endSerialOrders {
		endDate := grmi.Date(endSerialOrder.ActEndTime.Restore())
		date := endDate.ToString()
		item, ok := itemMapping[date]
		if !ok {
			item = WorkLineDateReportItem{
				Date:       endDate,
				WorkLineId: endSerialOrder.SerialOrder.UsedResourceId,
				ArtId:      endSerialOrder.SerialOrder.ArtId,
				Descr:      endSerialOrder.Descr1,
			}
		}
		if endSerialOrder.Status == 80 && strings.ToUpper(endSerialOrder.QualityCheck) == "OK" {
			item.FinishedAmount += 1
		} else {
			item.RepairAmount += 1
		}
		item.TotalAmount += 1
		itemMapping[date] = item
	}

	dates := make([]string, 0, len(itemMapping))
	for date := range itemMapping {
		dates = append(dates, date)
	}
	sort.Strings(dates)

	for _, date := range dates {
		if item, ok := itemMapping[date]; ok {
			result = append(result, item)
		}
	}

	return
}

/******************************************************************************
 *
 * @Reference LAPP_ACURA_MOM_BACKEND/services/report/CustomizedQueryService.WorkLineDateReportExportExcel
 *
 ******************************************************************************/
func (impl *CustomizedQueryServiceImplement) WorkLineDateReportExportExcel(user *global.User, workLineId string, beginDate *time.Time, endDate *time.Time) (filePathh string, err error) {

	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()

	planSerialOrders, err := impl.getPlanSerialOrders(session, user, workLineId, beginDate, endDate)
	if err != nil {
		return
	}

	_, err = impl.getEndSerialOrders(session, user, workLineId, beginDate, endDate)
	if err != nil {
		return
	}

	file := excelize.NewFile()
	{
		list := make([][]interface{}, len(planSerialOrders))
		for i, planSerialOrder := range planSerialOrders {
			list[i] = []interface{}{
				planSerialOrder.SerialOrder.SerialOrderId,
				planSerialOrder.SerialOrder.ProjectId,
				planSerialOrder.SerialOrder.ArtId,
				planSerialOrder.Article.Descr1,
				planSerialOrder.SerialOrderStatus.Status,
				planSerialOrder.SerialOrder.QualityCheck,
				planSerialOrder.SerialOrder.OrderType,
				planSerialOrder.SerialOrder.WorkOrderId,
				strconv.Itoa(int(planSerialOrder.SerialOrder.PreSchedKey)),
				strconv.Itoa(int(planSerialOrder.SerialOrder.SchedKey)),
				planSerialOrder.SerialOrder.OpTimePerItem,
				planSerialOrder.SerialOrder.QtyUomId,
				planSerialOrder.SerialOrder.TimeUomId,
				planSerialOrder.SerialOrder.PlanStartDate.ToString(),
				planSerialOrder.SerialOrder.PlanStartTime.ToString(),
				planSerialOrder.SerialOrder.PlanEndDate.ToString(),
				planSerialOrder.SerialOrder.PlanEndTime.ToString(),
				planSerialOrder.SerialOrder.PlanResourceId,
				planSerialOrder.SerialOrder.PlanQty,
				planSerialOrder.SerialOrder.SetupStartTime.ToString(),
				planSerialOrder.SerialOrder.SetupEndTime.ToString(),
				planSerialOrder.SerialOrder.ActStartTime.ToString(),
				planSerialOrder.SerialOrder.ActEndTime.ToString(),
				planSerialOrder.SerialOrder.UsedResourceId,
			}
		}

		err = WriteSheet(file, "实际生产工单", []interface{}{
			"序列工单ID",
			"车型项目ID",
			"物料号",
			"产品描述",
			"状态",
			"目检结果",
			"订单类型",
			"生产工单ID",
			"前一个调度Key",
			"调度排序Key",
			"单件工时",
			"数量单位",
			"时间单位",
			"计划开始日期",
			"计划开始时间",
			"计划结束日期",
			"计划结束时间",
			"计划资源ID",
			"计划数量",
			"换型开始时间",
			"换型结束时间",
			"实际开始时间",
			"实际结束时间",
			"使用资源ID",
		}, list)
		if err != nil {
			return
		}
	}
	{
		list := make([][]interface{}, len(planSerialOrders))
		for i, planSerialOrder := range planSerialOrders {
			list[i] = []interface{}{
				planSerialOrder.SerialOrder.SerialOrderId,
				planSerialOrder.SerialOrder.ProjectId,
				planSerialOrder.SerialOrder.ArtId,
				planSerialOrder.SerialOrder.OrderType,
				planSerialOrder.SerialOrder.WorkOrderId,
				strconv.Itoa(int(planSerialOrder.SerialOrder.PreSchedKey)),
				strconv.Itoa(int(planSerialOrder.SerialOrder.SchedKey)),
				planSerialOrder.SerialOrder.OpTimePerItem,
				planSerialOrder.SerialOrder.QtyUomId,
				planSerialOrder.SerialOrder.TimeUomId,
				planSerialOrder.SerialOrder.PlanStartDate.ToString(),
				planSerialOrder.SerialOrder.PlanStartTime.ToString(),
				planSerialOrder.SerialOrder.PlanEndDate.ToString(),
				planSerialOrder.SerialOrder.PlanEndTime.ToString(),
				planSerialOrder.SerialOrder.PlanResourceId,
				planSerialOrder.SerialOrder.PlanQty,
				planSerialOrder.SerialOrder.SetupStartTime.ToString(),
				planSerialOrder.SerialOrder.SetupEndTime.ToString(),
				planSerialOrder.SerialOrder.ActStartTime.ToString(),
				planSerialOrder.SerialOrder.ActEndTime.ToString(),
				planSerialOrder.SerialOrder.UsedResourceId,
			}
		}

		err = WriteSheet(file, "计划生产工单", []interface{}{
			"序列工单ID",
			"车型项目ID",
			"物料号",
			"订单类型",
			"生产工单ID",
			"前一个调度Key",
			"调度排序Key",
			"单件工时",
			"数量单位",
			"时间单位",
			"计划开始日期",
			"计划开始时间",
			"计划结束日期",
			"计划结束时间",
			"计划资源ID",
			"计划数量",
			"换型开始时间",
			"换型结束时间",
			"实际开始时间",
			"实际结束时间",
			"使用资源ID",
		}, list)
		if err != nil {
			return
		}
	}
	return SaveExcelFile(file, "产线生产日报")
}

/******************************************************************************
 *
 * @Function Name            : WriteSheet
 *-----------------------------------------------------------------------------
 *
 * @Description              : 将数据写入一个Excel工作表
 *
 * @Function Parameters      : excel句柄
 *
 * @Function Parameters      : 工作表名称
 *
 * @Function Parameters      : 标题行
 *
 * @Function Parameters      : 单元格的值矩阵
 *
 * @Return Value             : 执行时发生的错误
 *
 * @Author                   : chenglei.wei@le-it.com.cn
 *
 * @Date                     : 2022-03-16 11:15
 *
 ******************************************************************************/
func WriteSheet(file *excelize.File, sheetName string, titles []interface{}, cells [][]interface{}) (err error) {
	file.NewSheet(sheetName)
	file.DeleteSheet("Sheet1")
	streamWriter, err := file.NewStreamWriter(sheetName)
	if err != nil {
		return
	}

	rowIndex := 1
	if err = WriteExcelRow(streamWriter, &rowIndex, titles...); err != nil {
		return
	}

	for _, row := range cells {
		if err = WriteExcelRow(streamWriter, &rowIndex, row...); err != nil {
			return
		}
	}
	if err = streamWriter.Flush(); err != nil {
		return
	}
	return
}

/******************************************************************************
 *
 * @Reference LAPP_ACURA_MOM_BACKEND/services/report/CustomizedQueryService.AndonAnalysis_CountByDate
 *
 ******************************************************************************/
func (impl *CustomizedQueryServiceImplement) AndonAnalysis_CountByDate(user *global.User, workLineId string, workPlaceIds []string, ruleDesc string, beginDate *time.Time, endDate *time.Time) (result TableValues, err error) {

	workLineId = strings.TrimSpace(workLineId)
	ruleDesc = strings.TrimSpace(ruleDesc)

	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()

	andonInfoDAO := baseDAL.NewAndonInfoDAO(session, user.UserId)
	var predicates []grmi.Predicate
	if workLineId != "" {
		predicates = append(predicates, baseMeta.AndonInfo_WorkLineId.NewPredicate(grmi.Equal, workLineId))
	}
	if len(workPlaceIds) > 0 {
		values := make([]interface{}, len(workPlaceIds))
		for i, workPlaceId := range workPlaceIds {
			values[i] = workPlaceId
		}
		predicates = append(predicates, baseMeta.AndonInfo_WorkPlaceId.NewPredicate(grmi.Include, values...))
	}
	if ruleDesc != "" {
		predicates = append(predicates, baseMeta.AndonInfo_RuleDesc.NewPredicate(grmi.Like, ruleDesc))
	}
	if beginDate != nil {
		predicates = append(predicates, baseMeta.AndonInfo_CallTime.NewPredicate(grmi.GreaterOrEqual, *beginDate))
	}
	if endDate != nil {
		var oneDay time.Duration
		oneDay, err = time.ParseDuration("24h")
		if err != nil {
			return
		}
		predicates = append(predicates, baseMeta.AndonInfo_CallTime.NewPredicate(grmi.LessThen, (*endDate).Add(oneDay)))
	}
	andonInfos, err := andonInfoDAO.Select(predicates, []grmi.Field{baseMeta.AndonInfo_CallTime})
	if err != nil {
		return
	}

	reasonMapping := make(map[string]map[string]int, 10)
	dateMapping := make(map[string][]int, 10)
	for _, andonInfo := range andonInfos {
		reason := andonInfo.RuleDesc
		callTime := grmi.Date(andonInfo.CallTime.Restore())
		date := callTime.ToString()
		if _, ok := dateMapping[date]; !ok {
			dateMapping[date] = make([]int, 0, 10)
		}
		if _, ok := reasonMapping[reason]; !ok {
			reasonMapping[reason] = make(map[string]int, 10)
		}
		amount, ok := reasonMapping[reason][date]
		if ok {
			reasonMapping[reason][date] = amount + 1
		} else {
			reasonMapping[reason][date] = 1
		}
	}
	xHeaders := make([]string, 0, len(reasonMapping))
	for reason := range reasonMapping {
		xHeaders = append(xHeaders, reason)
	}
	sort.Strings(xHeaders)
	yHeaders := make([]string, 0, len(dateMapping))
	for date := range dateMapping {
		yHeaders = append(yHeaders, date)
	}
	sort.Strings(yHeaders)
	valuess := make([][]int, len(xHeaders))
	for x, reason := range xHeaders {
		values := make([]int, len(yHeaders))
		for y, date := range yHeaders {
			values[y] = 0
			if group, ok := reasonMapping[reason]; ok {
				if value, ok := group[date]; ok {
					values[y] = value
				}
			}
		}
		valuess[x] = values
	}
	result.XAxisHanders = xHeaders
	result.YAxisHanders = yHeaders
	result.Cells = valuess
	return
}
