Browse Source

添加固定价格&批量下载excel按组件归类

animeic 1 year ago
parent
commit
bc0f346ffc

+ 1 - 1
boxcost/Dockerfile

@@ -11,7 +11,7 @@
 # # 由于alpine镜像使用的是musl libc而不是gnu libc,/lib64/ 是不存在的。但他们是兼容的,可以创建个软连接
 #   && mkdir /lib64 \
 #   && ln -s /lib/libc.musl-x86_64.so.1 /lib64/ld-linux-x86-64.so.2 \
-FROM hub.afina.studio/afina-studio/alpine
+FROM registry.cn-chengdu.aliyuncs.com/infish/alpine
 # https://github.com/Docker-Hub-frolvlad/docker-alpine-glibc
 
 RUN echo -e https://mirrors.ustc.edu.cn/alpine/v3.15/main > /etc/apk/repositories \

+ 23 - 13
boxcost/api/plan-cost-excel.go

@@ -187,8 +187,19 @@ func (b *PlanCostExcel) drawAllContent() error {
 						perBudgetPrice += stage.OrderPrice * float64(stage.OrderCount)
 						b.FormatToEmpty(&budgetPrice)
 						// 实际金额
-						perRealPrice += stage.OrderPrice * float64(stage.ConfirmCount)
+						// !10.27 如果是固定价格
 						realPrice := fmt.Sprintf("%.3f", stage.OrderPrice*float64(stage.ConfirmCount))
+						if stage.IsFix == nil {
+							_fix := false
+							stage.IsFix = &_fix
+						}
+						if *stage.IsFix {
+							realPrice = budgetPrice
+							perRealPrice += stage.OrderPrice * float64(stage.OrderCount)
+						} else {
+							perRealPrice += stage.OrderPrice * float64(stage.ConfirmCount)
+						}
+
 						b.FormatToEmpty(&realPrice)
 						unit := stage.Unit
 						if stage.Unit == "吨" || stage.Unit == "平方米" {
@@ -362,8 +373,18 @@ func (b *PlanCostExcel) drawSupplierContent() error {
 								perBudgetPrice += stage.OrderPrice * float64(stage.OrderCount)
 								b.FormatToEmpty(&budgetPrice)
 								// 实际金额
-								perRealPrice += stage.OrderPrice * float64(stage.ConfirmCount)
+								// !10.27 如果是固定价格
 								realPrice := fmt.Sprintf("%.3f", stage.OrderPrice*float64(stage.ConfirmCount))
+								if stage.IsFix == nil {
+									_fix := false
+									stage.IsFix = &_fix
+								}
+								if *stage.IsFix {
+									realPrice = budgetPrice
+									perRealPrice += stage.OrderPrice * float64(stage.OrderCount)
+								} else {
+									perRealPrice += stage.OrderPrice * float64(stage.ConfirmCount)
+								}
 								b.FormatToEmpty(&realPrice)
 								unit := stage.Unit
 								if stage.Unit == "吨" || stage.Unit == "平方米" {
@@ -423,23 +444,12 @@ func (b *PlanCostExcel) drawSupplierContent() error {
 								}
 
 								b.drawRow(b.Row, "", stageType, stage.Name, orderCount, realCount, stageStatus, supplierName, fmt.Sprintf("%.3f元/%s", stage.Price, stage.Unit), stage.Norm, matHeigth, matWidth, unit, price, budgetPrice, realPrice)
-								// if stage.SupplierInfo != nil {
-								// 	cates[stage.SupplierInfo.Name] = append(cates[stage.SupplierInfo.Name], b.Row)
-
-								// }
 								b.Row++
 							}
 
 						}
 					}
 
-					// todo 有bug 合并同一供应商名
-					// for supplierName, cate := range cates {
-					// 	mergeStartRow := cate[0]
-					// 	mergeEndRow := cate[len(cate)-1]
-					// 	b.Excel.MergeCell(b.SheetName, fmt.Sprintf("G%d", mergeStartRow), fmt.Sprintf("G%d", mergeEndRow))
-					// 	b.Excel.SetCellValue(b.SheetName, fmt.Sprintf("G%d", mergeEndRow), supplierName)
-					// }
 					if startRow != 0 {
 						endRow := b.Row - 1
 						// 组件名字

+ 11 - 1
boxcost/api/plan-summary-excel.go

@@ -403,8 +403,18 @@ func (b *PlanSummaryExcel) drawSupplierContent() error {
 								perBudgetPrice += stage.OrderPrice * float64(stage.OrderCount)
 								b.FormatToEmpty(&budgetPrice)
 								// 实际金额
-								perRealPrice += stage.OrderPrice * float64(stage.ConfirmCount)
+								// !10.27 如果是固定价格
 								realPrice := fmt.Sprintf("%.3f", stage.OrderPrice*float64(stage.ConfirmCount))
+								if stage.IsFix == nil {
+									_fix := false
+									stage.IsFix = &_fix
+								}
+								if *stage.IsFix {
+									realPrice = budgetPrice
+									perRealPrice += stage.OrderPrice * float64(stage.OrderCount)
+								} else {
+									perRealPrice += stage.OrderPrice * float64(stage.ConfirmCount)
+								}
 								b.FormatToEmpty(&realPrice)
 								unit := stage.Unit
 								if stage.Unit == "吨" || stage.Unit == "平方米" {

+ 435 - 79
boxcost/api/plan.go

@@ -22,6 +22,8 @@ import (
 	"go.mongodb.org/mongo-driver/bson/primitive"
 )
 
+// TODO 下载代码重复提取
+
 // 生产计划管理
 func ProductPlan(r *GinRouter) {
 
@@ -44,6 +46,9 @@ func ProductPlan(r *GinRouter) {
 	// r.GET("/bill/plan/download", DownLoadCompBills)
 	r.GET("/bill/plan/download", DownLoadPlanBills)
 
+	r.GET("/bill/comp/download", DownLoadCompBills)
+	r.GET("/bill/comp/downloadPdf", DownLoadCompBillsPdf)
+
 	r.GET("/bill/plan/downloadPdf", DownLoadPlanBillsPdf)
 
 	// 生产成本表
@@ -230,12 +235,17 @@ func DownLoadPlanCost(c *gin.Context, apictx *ApiSession) (interface{}, error) {
 
 }
 
-func DownLoadPlanBills(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+func DownLoadCompBills(c *gin.Context, apictx *ApiSession) (interface{}, error) {
 	_planId := c.Query("id")
-	planId, err := primitive.ObjectIDFromHex(_planId)
-	if err != nil {
+	compId := c.Query("compId")
+	planId, _ := primitive.ObjectIDFromHex(_planId)
+	if planId.IsZero() {
 		return nil, errors.New("planId错误")
 	}
+	if len(compId) < 1 {
+		return nil, errors.New("planId错误")
+	}
+
 	plan := model.ProductPlan{}
 	found, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
 		CollectName: repo.CollectionProductPlan,
@@ -244,30 +254,35 @@ func DownLoadPlanBills(c *gin.Context, apictx *ApiSession) (interface{}, error)
 	if !found || err != nil {
 		return nil, errors.New("数据未找到")
 	}
-	// 获取所有stages单据id
-	billIds := make([]string, 0)
+	compBills := make([]string, 0)
+	compNameId := ""
 	for _, comp := range plan.Pack.Components {
 		if comp.Id == "" || len(comp.Stages) == 0 {
 			continue
 		}
-		for _, stage := range comp.Stages {
-			billId, _ := primitive.ObjectIDFromHex(stage.BillId)
-			if !billId.IsZero() {
-				billIds = append(billIds, fmt.Sprintf("%d_%s", stage.BillType, stage.BillId))
+		if compId == comp.Id {
+			compNameId = fmt.Sprintf("%s_%s", comp.Name, compId)
+			for _, stage := range comp.Stages {
+				billId, _ := primitive.ObjectIDFromHex(stage.BillId)
+				if !billId.IsZero() {
+					compBills = append(compBills, fmt.Sprintf("%d_%s", stage.BillType, stage.BillId))
+				}
 			}
-		}
 
+		}
 	}
-	// 去重单据号
-	typeBillIds := removeDuplicationSort(billIds)
+	if len(compBills) < 1 {
+		return nil, errors.New("数据未找到")
+	}
+	companyName := getCompanyName(apictx)
+
+	typeBillIds := removeDuplicationSort(compBills)
 	if len(typeBillIds) < 1 {
 		return nil, errors.New("未找到单据信息")
 	}
 	f := excelize.NewFile()
 	f.SetDefaultFont("宋体")
 	flagIndex := -1
-	companyName := getCompanyName(apictx)
-
 	// 采购 加工 加工-印刷 加工-覆膜 成品采购
 	typeRows := []int{0, 0, 0, 0, 0}
 	for _, tId := range typeBillIds {
@@ -404,25 +419,23 @@ func DownLoadPlanBills(c *gin.Context, apictx *ApiSession) (interface{}, error)
 		}
 
 	}
+
 	// 设置活跃sheet
 	f.SetActiveSheet(flagIndex)
 	// 删除默认Sheet1
 	f.DeleteSheet("Sheet1")
 
 	c.Header("Content-Type", "application/octet-stream")
-	c.Header("Content-Disposition", "attachment; filename="+"bill.xlsx")
+	c.Header("Content-Disposition", "attachment; filename="+fmt.Sprintf("%s.xlsx", compNameId))
 	c.Header("Content-Transfer-Encoding", "binary")
 
-	err = f.Write(c.Writer)
-	if err != nil {
-		return nil, err
-	}
+	f.Write(c.Writer)
 
 	return nil, nil
+
 }
 
-// todo old 功能确定后删除
-func DownLoadPlanBillsBack(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+func DownLoadPlanBills(c *gin.Context, apictx *ApiSession) (interface{}, error) {
 	_planId := c.Query("id")
 	planId, err := primitive.ObjectIDFromHex(_planId)
 	if err != nil {
@@ -436,46 +449,78 @@ func DownLoadPlanBillsBack(c *gin.Context, apictx *ApiSession) (interface{}, err
 	if !found || err != nil {
 		return nil, errors.New("数据未找到")
 	}
+	// 获取组件中的单据并分工序排列
+
 	// 获取所有stages单据id
-	billIds := make([]string, 0)
+	// billIds := make([]string, 0)
+	type billIds []string
+	compBillsMap := make(map[string]billIds, 0)
 	for _, comp := range plan.Pack.Components {
 		if comp.Id == "" || len(comp.Stages) == 0 {
 			continue
 		}
+		// 唯一组价名,拼上id是因为防止可能有多个相同组件名
+		compNameId := fmt.Sprintf("%s_%s", comp.Name, comp.Id)
 		for _, stage := range comp.Stages {
 			billId, _ := primitive.ObjectIDFromHex(stage.BillId)
 			if !billId.IsZero() {
-				billIds = append(billIds, fmt.Sprintf("%d_%s", stage.BillType, stage.BillId))
+				compBillsMap[compNameId] = append(compBillsMap[compNameId], fmt.Sprintf("%d_%s", stage.BillType, stage.BillId))
+				// billIds = append(billIds, fmt.Sprintf("%d_%s", stage.BillType, stage.BillId))
 			}
 		}
 
 	}
-	// 去重单据号
-	typeBillIds := removeDuplicationSort(billIds)
-	if len(typeBillIds) < 1 {
-		return nil, errors.New("未找到单据信息")
+	if len(compBillsMap) < 1 {
+		return nil, errors.New("数据未找到")
 	}
-	f := excelize.NewFile()
-	index := f.NewSheet("Sheet1")
-	f.SetActiveSheet(index)
-	f.SetDefaultFont("宋体")
+
 	companyName := getCompanyName(apictx)
 
-	row := 0
-	for _, tId := range typeBillIds {
-		tidArr := strings.Split(tId, "_")
+	_planName := plan.Name
+	r := regexp.MustCompile(`/`)
+	planName := r.ReplaceAllString(_planName, `&`)
+	// fmt.Println(planName)
+	// 打包pdf的缓存目录
+	saveTmpDir := fmt.Sprintf("tmp1/excel/%s", planName)
 
-		var billExcel IExcel
-		// 采购
-		billId, _ := primitive.ObjectIDFromHex(tidArr[1])
-		if tidArr[0] == "1" {
-			purchase := model.PurchaseBill{}
-			found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
-				CollectName: repo.CollectionBillPurchase,
-				Query:       repo.Map{"_id": billId},
-			}, &purchase)
-			if found {
+	if isExistDir(saveTmpDir) {
+		os.RemoveAll(saveTmpDir)
+	}
+
+	for compNameId, billIds := range compBillsMap {
+		// 去重单据号
+		typeBillIds := removeDuplicationSort(billIds)
+		if len(typeBillIds) < 1 {
+			return nil, errors.New("未找到单据信息")
+		}
+		f := excelize.NewFile()
+		f.SetDefaultFont("宋体")
+		flagIndex := -1
+		// 采购 加工 加工-印刷 加工-覆膜 成品采购
+		typeRows := []int{0, 0, 0, 0, 0}
+		for _, tId := range typeBillIds {
+			tidArr := strings.Split(tId, "_")
+			var billExcel IExcel
+			// 采购
+			billId, _ := primitive.ObjectIDFromHex(tidArr[1])
+			if tidArr[0] == "1" {
+				purchase := model.PurchaseBill{}
+				found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
+					CollectName: repo.CollectionBillPurchase,
+					Query:       repo.Map{"_id": billId},
+				}, &purchase)
+				if !found {
+					continue
+				}
+				sheetName := "采购单"
+				index := f.NewSheet(sheetName)
+				if flagIndex < 0 {
+					flagIndex = index
+				}
+				// index := f.NewSheet("采购单")
+				// f.SetActiveSheet(index)
 				billExcel = NewPurchaseBill(f)
+				billExcel.SetSheetName(sheetName)
 				if purchase.Reviewed == 1 {
 					if len(purchase.SignUsers) > 0 {
 						signs := []*model.Signature{}
@@ -490,18 +535,34 @@ func DownLoadPlanBillsBack(c *gin.Context, apictx *ApiSession) (interface{}, err
 				}
 				billExcel.SetContent(&purchase)
 				billExcel.SetTitle(fmt.Sprintf("%s原材料采购单", companyName))
-			}
 
-		}
-		// 工艺
-		if tidArr[0] == "2" {
-			produce := model.ProduceBill{}
-			found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
-				CollectName: repo.CollectionBillProduce,
-				Query:       repo.Map{"_id": billId},
-			}, &produce)
-			if found {
+				billExcel.SetRow(typeRows[0])
+				billExcel.Draws()
+				typeRows[0] = billExcel.GetRow() + 5
+
+			}
+			// 工艺
+			if tidArr[0] == "2" {
+				produce := model.ProduceBill{}
+				found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
+					CollectName: repo.CollectionBillProduce,
+					Query:       repo.Map{"_id": billId},
+				}, &produce)
+				if !found {
+					continue
+				}
+				sheetName := "加工单"
+				if produce.IsPrint {
+					sheetName = "加工单-印刷"
+				} else if produce.IsLam {
+					sheetName = "加工单-覆膜"
+				}
+				index := f.NewSheet(sheetName)
+				if flagIndex < 0 {
+					flagIndex = index
+				}
 				billExcel = NewProduceBill(f)
+				billExcel.SetSheetName(sheetName)
 				if produce.Reviewed == 1 {
 					if len(produce.SignUsers) > 0 {
 						signs := []*model.Signature{}
@@ -516,18 +577,40 @@ func DownLoadPlanBillsBack(c *gin.Context, apictx *ApiSession) (interface{}, err
 				}
 				billExcel.SetContent(&produce)
 				billExcel.SetTitle(fmt.Sprintf("%s加工单", companyName))
-			}
 
-		}
-		// 成品采购
-		if tidArr[0] == "3" {
-			product := model.ProductBill{}
-			found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
-				CollectName: repo.CollectionBillProduct,
-				Query:       repo.Map{"_id": billId},
-			}, &product)
-			if found {
+				if produce.IsPrint {
+					billExcel.SetRow(typeRows[2])
+					billExcel.Draws()
+					typeRows[2] = billExcel.GetRow() + 5
+				} else if produce.IsLam {
+					billExcel.SetRow(typeRows[3])
+					billExcel.Draws()
+					typeRows[3] = billExcel.GetRow() + 5
+				} else {
+					billExcel.SetRow(typeRows[1])
+					billExcel.Draws()
+					typeRows[1] = billExcel.GetRow() + 5
+
+				}
+
+			}
+			// 成品采购
+			if tidArr[0] == "3" {
+				product := model.ProductBill{}
+				found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
+					CollectName: repo.CollectionBillProduct,
+					Query:       repo.Map{"_id": billId},
+				}, &product)
+				if !found {
+					continue
+				}
+				sheetName := "成品采购单"
+				index := f.NewSheet(sheetName)
+				if flagIndex < 0 {
+					flagIndex = index
+				}
 				billExcel = NewProductBill(f)
+				billExcel.SetSheetName(sheetName)
 				if product.Reviewed == 1 {
 					if len(product.SignUsers) > 0 {
 						signs := []*model.Signature{}
@@ -542,26 +625,73 @@ func DownLoadPlanBillsBack(c *gin.Context, apictx *ApiSession) (interface{}, err
 				}
 				billExcel.SetContent(&product)
 				billExcel.SetTitle(companyName)
+
+				billExcel.SetRow(typeRows[4])
+				billExcel.Draws()
+				typeRows[4] = billExcel.GetRow() + 5
 			}
+
 		}
-		if billExcel == nil {
-			continue
+
+		// 设置活跃sheet
+		f.SetActiveSheet(flagIndex)
+		// 删除默认Sheet1
+		f.DeleteSheet("Sheet1")
+
+		buf, _ := f.WriteToBuffer()
+
+		targeEXcelName := fmt.Sprintf("%s.xlsx", compNameId)
+
+		err := savePdfToTmp(saveTmpDir, targeEXcelName, buf.Bytes())
+		if err != nil {
+			fmt.Println("保存文件失败!")
+			return nil, err
 		}
-		billExcel.SetRow(row)
-		billExcel.Draws()
-		row = billExcel.GetRow() + 5
-	}
 
+	}
 	c.Header("Content-Type", "application/octet-stream")
-	c.Header("Content-Disposition", "attachment; filename="+"bill.xlsx")
+	c.Header("Content-Disposition", "attachment; filename="+fmt.Sprintf("%s-execl.zip", planName))
 	c.Header("Content-Transfer-Encoding", "binary")
 
-	err = f.Write(c.Writer)
-	if err != nil {
-		return nil, err
-	}
+	archive := zip.NewWriter(c.Writer)
+	defer archive.Close()
+	// 遍历路径信息
+	filepath.Walk(saveTmpDir, func(path string, info os.FileInfo, _ error) error {
+
+		// 如果是源路径,提前进行下一个遍历
+		if path == saveTmpDir {
+			return nil
+		}
+
+		// 获取:文件头信息
+		header, _ := zip.FileInfoHeader(info)
+		header.Name = strings.TrimPrefix(path, saveTmpDir+`/`)
+
+		// 判断:文件是不是文件夹
+		if info.IsDir() {
+			header.Name += `/`
+		} else {
+			// 设置:zip的文件压缩算法
+			header.Method = zip.Deflate
+		}
+
+		// 创建:压缩包头部信息
+		writer, _ := archive.CreateHeader(header)
+		if !info.IsDir() {
+			file, _ := os.Open(path)
+			defer file.Close()
+			io.Copy(writer, file)
+		}
+		return nil
+	})
+
+	// 删除缓存目录
+	os.RemoveAll(saveTmpDir)
 
 	return nil, nil
+	// http://127.0.0.1:8888/boxcost/bill/plan/download?id=652206412617b328da71655e
+	// http://127.0.0.1:8888/boxcost/bill/comp/download?id=652206412617b328da71655e&compId=1677034398070
+	// http://127.0.0.1:8888/boxcost/bill/comp/downloadPdf?id=652206412617b328da71655e&compId=1677034398070
 }
 
 func DownLoadPlanBillsPdf(c *gin.Context, apictx *ApiSession) (interface{}, error) {
@@ -592,12 +722,11 @@ func DownLoadPlanBillsPdf(c *gin.Context, apictx *ApiSession) (interface{}, erro
 		}
 
 	}
+	if len(billIds) < 1 {
+		return nil, errors.New("数据未找到")
+	}
 	// 去重单据号
 	typeBillIds := removeDuplicationSort(billIds)
-	if len(typeBillIds) < 1 {
-		return nil, errors.New("未找到单据信息")
-	}
-
 	companyName := getCompanyName(apictx)
 	_planName := plan.Name
 	r := regexp.MustCompile(`/`)
@@ -855,6 +984,233 @@ func toPdfAndSaveTask(buf *bytes.Buffer, toPdfAddr, saveTmpDir, targetPdfName st
 	wg.Done()
 }
 
+func DownLoadCompBillsPdf(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	_planId := c.Query("id")
+	compId := c.Query("compId")
+	planId, _ := primitive.ObjectIDFromHex(_planId)
+	if planId.IsZero() {
+		return nil, errors.New("planId错误")
+	}
+	if len(compId) < 1 {
+		return nil, errors.New("compId错误")
+	}
+	plan := model.ProductPlan{}
+	found, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
+		CollectName: repo.CollectionProductPlan,
+		Query:       repo.Map{"_id": planId},
+	}, &plan)
+	if !found || err != nil {
+		return nil, errors.New("数据未找到")
+	}
+	// 获取所有stages单据id
+	billIds := make([]string, 0)
+	compNameId := ""
+	for _, comp := range plan.Pack.Components {
+		if comp.Id == "" || len(comp.Stages) == 0 {
+			continue
+		}
+		if comp.Id == compId {
+			compNameId = fmt.Sprintf("%s_%s", comp.Name, comp.Id)
+			for _, stage := range comp.Stages {
+				billId, _ := primitive.ObjectIDFromHex(stage.BillId)
+				if !billId.IsZero() {
+					billIds = append(billIds, fmt.Sprintf("%d_%s", stage.BillType, stage.BillId))
+				}
+			}
+
+		}
+
+	}
+	if len(billIds) < 1 {
+		return nil, errors.New("数据未找到")
+	}
+	// 去重单据号
+	typeBillIds := removeDuplicationSort(billIds)
+	companyName := getCompanyName(apictx)
+	_planName := plan.Name
+	r := regexp.MustCompile(`/`)
+	planName := r.ReplaceAllString(_planName, `&`)
+	// fmt.Println(planName)
+	// 打包pdf的缓存目录
+	saveTmpDir := fmt.Sprintf("tmp1/%s/%s", planName, compNameId)
+
+	if isExistDir(saveTmpDir) {
+		os.RemoveAll(saveTmpDir)
+	}
+	// 记录文件数量
+	var wg sync.WaitGroup
+	c1 := make(chan int)
+	for _, tId := range typeBillIds {
+		productName := ""
+		supplierName := ""
+		serialNumber := ""
+		f := excelize.NewFile()
+		index := f.NewSheet("Sheet1")
+		f.SetActiveSheet(index)
+		f.SetDefaultFont("宋体")
+
+		tidArr := strings.Split(tId, "_")
+		var billExcel IExcel
+		// 采购
+		billId, _ := primitive.ObjectIDFromHex(tidArr[1])
+		if tidArr[0] == "1" {
+			purchase := model.PurchaseBill{}
+			found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
+				CollectName: repo.CollectionBillPurchase,
+				Query:       repo.Map{"_id": billId},
+			}, &purchase)
+			if !found {
+				continue
+			}
+			billExcel = NewPurchaseBill(f)
+			if purchase.Reviewed == 1 {
+				if len(purchase.SignUsers) > 0 {
+					signs := []*model.Signature{}
+					repo.RepoDocsSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{
+						CollectName: repo.CollectionSignature,
+						Query:       repo.Map{"_id": bson.M{"$in": purchase.SignUsers}},
+						Sort:        bson.M{"sort": 1},
+					}, &signs)
+					billExcel.SetSignatures(signs)
+
+				}
+
+			}
+			productName = purchase.ProductName
+			supplierName = purchase.Supplier
+			serialNumber = purchase.SerialNumber
+			billExcel.SetContent(&purchase)
+			billExcel.SetTitle(fmt.Sprintf("%s原材料采购单", companyName))
+
+		}
+		// 工艺
+		if tidArr[0] == "2" {
+			produce := model.ProduceBill{}
+			found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
+				CollectName: repo.CollectionBillProduce,
+				Query:       repo.Map{"_id": billId},
+			}, &produce)
+			if !found {
+				continue
+			}
+			billExcel = NewProduceBill(f)
+			if produce.Reviewed == 1 {
+				if len(produce.SignUsers) > 0 {
+					signs := []*model.Signature{}
+					repo.RepoDocsSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{
+						CollectName: repo.CollectionSignature,
+						Query:       repo.Map{"_id": bson.M{"$in": produce.SignUsers}},
+						Sort:        bson.M{"sort": 1},
+					}, &signs)
+					billExcel.SetSignatures(signs)
+				}
+
+			}
+			productName = produce.ProductName
+			supplierName = produce.Supplier
+			serialNumber = produce.SerialNumber
+			billExcel.SetContent(&produce)
+			billExcel.SetTitle(fmt.Sprintf("%s加工单", companyName))
+
+		}
+		// 成品采购
+		if tidArr[0] == "3" {
+			product := model.ProductBill{}
+			found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
+				CollectName: repo.CollectionBillProduct,
+				Query:       repo.Map{"_id": billId},
+			}, &product)
+			if !found {
+				continue
+			}
+			billExcel = NewProductBill(f)
+			if product.Reviewed == 1 {
+				if len(product.SignUsers) > 0 {
+					signs := []*model.Signature{}
+					repo.RepoDocsSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{
+						CollectName: repo.CollectionSignature,
+						Query:       repo.Map{"_id": bson.M{"$in": product.SignUsers}},
+						Sort:        bson.M{"sort": 1},
+					}, &signs)
+					billExcel.SetSignatures(signs)
+				}
+
+			}
+			productName = product.ProductName
+			supplierName = product.Supplier
+			serialNumber = product.SerialNumber
+			billExcel.SetContent(&product)
+			billExcel.SetTitle(companyName)
+		}
+		billExcel.SetIsPdf("true")
+		billExcel.Draws()
+		buf, _ := f.WriteToBuffer()
+
+		// r := regexp.MustCompile(`/`)
+		_productName := r.ReplaceAllString(productName, `&`)
+		_supplierName := r.ReplaceAllString(supplierName, `&`)
+		targePdfName := fmt.Sprintf("%s-%s-%s.pdf", _supplierName, _productName, serialNumber)
+		// fmt.Println(targePdfName)
+
+		wg.Add(1)
+		go toPdfAndSaveTask(buf, apictx.Svc.Conf.PdfApiAddr, saveTmpDir, targePdfName, c1, &wg)
+
+	}
+	go func() {
+		// 等待所有goroutine
+		wg.Wait()
+		// 关闭channel
+		close(c1)
+	}()
+	// channel关闭后结束后停止遍历接收channel中的值
+	for n := range c1 {
+		if n == -1 {
+			return nil, errors.New("下载失败,请重试")
+		}
+	}
+
+	c.Header("Content-Type", "application/octet-stream")
+	c.Header("Content-Disposition", "attachment; filename="+fmt.Sprintf("%s.zip", compNameId))
+	c.Header("Content-Transfer-Encoding", "binary")
+
+	archive := zip.NewWriter(c.Writer)
+	defer archive.Close()
+	// 遍历路径信息
+	filepath.Walk(saveTmpDir, func(path string, info os.FileInfo, _ error) error {
+
+		// 如果是源路径,提前进行下一个遍历
+		if path == saveTmpDir {
+			return nil
+		}
+
+		// 获取:文件头信息
+		header, _ := zip.FileInfoHeader(info)
+		header.Name = strings.TrimPrefix(path, saveTmpDir+`/`)
+
+		// 判断:文件是不是文件夹
+		if info.IsDir() {
+			header.Name += `/`
+		} else {
+			// 设置:zip的文件压缩算法
+			header.Method = zip.Deflate
+		}
+
+		// 创建:压缩包头部信息
+		writer, _ := archive.CreateHeader(header)
+		if !info.IsDir() {
+			file, _ := os.Open(path)
+			defer file.Close()
+			io.Copy(writer, file)
+		}
+		return nil
+	})
+
+	// 删除缓存目录
+	os.RemoveAll(saveTmpDir)
+
+	return nil, nil
+}
+
 // 创建生产计划
 func CreateProductPlan(c *gin.Context, apictx *ApiSession) (interface{}, error) {
 

+ 36 - 6
boxcost/api/report-excel.go

@@ -167,10 +167,20 @@ func (b *ReportExcel) drawTableContent() error {
 					b.FormatToEmpty(&realCount)
 
 					// 实际金额
-					realPrice := fmt.Sprintf("%.3f", paper.OrderPrice*float64(paper.ConfirmCount))
+					// !10.27 如果是固定价格
+					_realPrice := paper.OrderPrice * float64(paper.ConfirmCount)
+					if paper.IsFix == nil {
+						_fix := false
+						paper.IsFix = &_fix
+					}
+					if *paper.IsFix {
+						_realPrice = paper.OrderPrice * float64(paper.OrderCount)
+					}
+					realPrice := fmt.Sprintf("%.3f", _realPrice)
+
 					b.FormatToEmpty(&realPrice)
 					DrawRow(b.Row, fmt.Sprintf("%d", index), completeTime, paper.Name, paper.Norm, fmt.Sprintf("%s*%s", paper.Height, paper.Width), fmt.Sprintf("%d", paper.ConfirmCount), fmt.Sprintf("%.3f", paper.OrderPrice), realPrice, purchase.ProductName, record)
-					totalPrice += paper.OrderPrice * float64(paper.ConfirmCount)
+					totalPrice += _realPrice
 					totalCount += paper.ConfirmCount
 					b.Row++
 				}
@@ -196,10 +206,20 @@ func (b *ReportExcel) drawTableContent() error {
 					b.FormatToEmpty(&realCount)
 
 					// 实际金额
-					realPrice := fmt.Sprintf("%.3f", pproduce.OrderPrice*float64(pproduce.ConfirmCount))
+					// !10.27 如果是固定价格
+					_realPrice := pproduce.OrderPrice * float64(pproduce.ConfirmCount)
+					if pproduce.IsFix == nil {
+						_fix := false
+						pproduce.IsFix = &_fix
+					}
+					if *pproduce.IsFix {
+						_realPrice = pproduce.OrderPrice * float64(pproduce.OrderCount)
+					}
+					realPrice := fmt.Sprintf("%.3f", _realPrice)
+
 					b.FormatToEmpty(&realPrice)
 					DrawRow(b.Row, fmt.Sprintf("%d", index), completeTime, pproduce.Name, pproduce.Norm, pproduce.PrintSize, fmt.Sprintf("%d", pproduce.ConfirmCount), fmt.Sprintf("%.3f", pproduce.OrderPrice), realPrice, produce.ProductName, record)
-					totalPrice += pproduce.OrderPrice * float64(pproduce.ConfirmCount)
+					totalPrice += _realPrice
 					totalCount += pproduce.ConfirmCount
 					b.Row++
 				}
@@ -225,10 +245,20 @@ func (b *ReportExcel) drawTableContent() error {
 					b.FormatToEmpty(&realCount)
 
 					// 实际金额
-					realPrice := fmt.Sprintf("%.3f", pproduct.OrderPrice*float64(pproduct.ConfirmCount))
+
+					// !10.27 如果是固定价格
+					_realPrice := pproduct.OrderPrice * float64(pproduct.ConfirmCount)
+					if pproduct.IsFix == nil {
+						_fix := false
+						pproduct.IsFix = &_fix
+					}
+					if *pproduct.IsFix {
+						_realPrice = pproduct.OrderPrice * float64(pproduct.OrderCount)
+					}
+					realPrice := fmt.Sprintf("%.3f", _realPrice)
 					b.FormatToEmpty(&realPrice)
 					DrawRow(b.Row, fmt.Sprintf("%d", index), completeTime, pproduct.Name, pproduct.Norm, "-", fmt.Sprintf("%d", pproduct.ConfirmCount), fmt.Sprintf("%.3f", pproduct.OrderPrice), realPrice, product.ProductName, record)
-					totalPrice += pproduct.OrderPrice * float64(pproduct.ConfirmCount)
+					totalPrice += _realPrice
 					totalCount += pproduct.ConfirmCount
 					b.Row++
 				}

+ 76 - 0
boxcost/api/summary.go

@@ -3,12 +3,14 @@ package api
 import (
 	"box-cost/db/model"
 	"box-cost/db/repo"
+	"box-cost/log"
 	"errors"
 	"strings"
 	"time"
 
 	"github.com/gin-gonic/gin"
 	"github.com/xuri/excelize/v2"
+	"go.mongodb.org/mongo-driver/bson"
 	"go.mongodb.org/mongo-driver/bson/primitive"
 )
 
@@ -19,6 +21,11 @@ func Summary(r *GinRouter) {
 	r.GET("/summary/download", SummaryDownload)
 	// 下载简单汇总
 	r.GET("/summary/sample/download", SummarySampleDownload)
+
+	// !10.27 时间和供应商查询
+	// summary/supplier/plan query={"supplierId":"xxx","timeRange":["xxx","xxx"]}
+	// 返回planIds数组 []string
+	r.GET("/summary/supplier/plan", SummarySupplierPlan)
 }
 
 type SupplierPlanSummary struct {
@@ -37,6 +44,75 @@ type PlanSummary struct {
 	SendTo       map[string]string
 }
 
+func SummarySupplierPlan(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	_, _, query := UtilQueryPageSize(c)
+	supplierId := primitive.NilObjectID
+	if _supplierId, ok := query["supplierId"]; ok {
+		supplierId, _ = primitive.ObjectIDFromHex(_supplierId.(string))
+		delete(query, "supplierId")
+	}
+	if _timeRange, ok := query["timeRange"]; ok {
+		timeRange, _ := _timeRange.([]interface{})
+
+		if len(timeRange) == 2 {
+			start, end := getTimeRange(timeRange[0].(string), timeRange[1].(string))
+			query["createTime"] = bson.M{"$gte": start, "$lte": end}
+		}
+		delete(query, "timeRange")
+	}
+	// 遍历判断包含供应商的数据,返回planIds
+	plans := []*model.ProductPlan{}
+	err := repo.RepoDocsSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{
+		CollectName: repo.CollectionProductPlan,
+		Query:       query,
+		Project:     []string{"_id", "pack"},
+		Sort:        bson.M{"createTime": -1},
+	}, &plans)
+	if err != nil {
+		log.Error(err)
+		return nil, errors.New("查询数据错误!")
+	}
+	planIds := []primitive.ObjectID{}
+	if len(plans) < 1 {
+		return planIds, nil
+	}
+	// 遍历
+
+	flag := false
+	for _, plan := range plans {
+		// 选供应商
+		if !supplierId.IsZero() {
+			for _, comp := range plan.Pack.Components {
+				if comp.Id == "" || len(comp.Stages) == 0 {
+					continue
+				}
+				for _, stage := range comp.Stages {
+					// 只要任意一个stage中存在选中的供应商,那么这个计划包含这个供应商,这时应该跳出来
+					if stage.SupplierInfo == nil {
+						continue
+					}
+					if stage.SupplierInfo.Id == supplierId {
+						planIds = append(planIds, plan.Id)
+						flag = true
+						break
+					}
+				}
+				if flag {
+					break
+				}
+			}
+
+		} else {
+			planIds = append(planIds, plan.Id)
+		}
+		if flag {
+			continue
+		}
+	}
+
+	return planIds, nil
+}
+
 // /summary/download?planIds=id1,id2&supplierId=xxx
 func SummaryDownload(c *gin.Context, apictx *ApiSession) (interface{}, error) {
 	// 获取planIds supplierId

+ 8 - 0
boxcost/boxcost.log

@@ -1,3 +1,11 @@
 {"level":"info","timestamp":"2023-06-14 12:50:44","message":"[error decoding key pack.components.1.stages.1.deliveryTime: parsing time \"2023-06-07 05:26:29.341\" as \"2006-01-02T15:04:05.999Z07:00\": cannot parse \" 05:26:29.341\" as \"T\"]","service_name":"boxcost"}
 {"level":"info","timestamp":"2023-06-14 12:53:15","message":"[error decoding key pack.components.1.stages.1.deliveryTime: parsing time \"2023-06-07 05:26:29.341\" as \"2006-01-02T15:04:05.999Z07:00\": cannot parse \" 05:26:29.341\" as \"T\"]","service_name":"boxcost"}
 {"level":"info","timestamp":"2023-06-14 12:53:59","message":"[error decoding key pack.components.1.stages.1.deliveryTime: parsing time \"2023-06-07 05:26:29.341\" as \"2006-01-02T15:04:05.999Z07:00\": cannot parse \" 05:26:29.341\" as \"T\"]","service_name":"boxcost"}
+{"level":"error","timestamp":"2023-10-27 15:27:12","message":"[Post \"http://localhost:3001/convert/office\": dial tcp 127.0.0.1:3001: connect: connection refused]","service_name":"boxcost"}
+{"level":"error","timestamp":"2023-10-27 15:27:12","message":"[Post \"http://localhost:3001/convert/office\": dial tcp 127.0.0.1:3001: connect: connection refused]","service_name":"boxcost"}
+{"level":"error","timestamp":"2023-10-27 15:27:12","message":"[Post \"http://localhost:3001/convert/office\": dial tcp 127.0.0.1:3001: connect: connection refused]","service_name":"boxcost"}
+{"level":"error","timestamp":"2023-10-27 15:27:13","message":"[Post \"http://localhost:3001/convert/office\": dial tcp 127.0.0.1:3001: connect: connection refused]","service_name":"boxcost"}
+{"level":"error","timestamp":"2023-10-27 15:27:24","message":"[Post \"http://localhost:3001/convert/office\": dial tcp 127.0.0.1:3001: connect: connection refused]","service_name":"boxcost"}
+{"level":"error","timestamp":"2023-10-27 15:27:24","message":"[Post \"http://localhost:3001/convert/office\": dial tcp 127.0.0.1:3001: connect: connection refused]","service_name":"boxcost"}
+{"level":"error","timestamp":"2023-10-27 15:27:24","message":"[Post \"http://localhost:3001/convert/office\": dial tcp 127.0.0.1:3001: connect: connection refused]","service_name":"boxcost"}
+{"level":"error","timestamp":"2023-10-27 15:27:24","message":"[Post \"http://localhost:3001/convert/office\": dial tcp 127.0.0.1:3001: connect: connection refused]","service_name":"boxcost"}

+ 9 - 0
boxcost/db/model/bill.go

@@ -31,6 +31,9 @@ type PaperBill struct {
 	// 下单单价
 	OrderPrice float64 `bson:"orderPrice,omitempty" json:"orderPrice"`
 
+	// 是否固定价格 如果是:为预算总价,不是:为实际总价
+	IsFix *bool `bson:"isFix,omitempty" json:"isFix"`
+
 	//备注
 	Remark string `bson:"remark,omitempty" json:"remark"`
 
@@ -116,6 +119,9 @@ type ProduceBillData struct {
 	// 确认收货数量
 	ConfirmCount int `bson:"confirmCount,omitempty" json:"confirmCount"`
 
+	// 是否固定价格 如果是:为预算总价,不是:为实际总价
+	IsFix *bool `bson:"isFix,omitempty" json:"isFix"`
+
 	// 来纸数量
 	PaperCount int `bson:"paperCount,omitempty" json:"paperCount"`
 
@@ -273,6 +279,9 @@ type ProductBillData struct {
 	// 下单单价
 	OrderPrice float64 `bson:"orderPrice,omitempty" json:"orderPrice"`
 
+	// 是否固定价格 如果是:为预算总价,不是:为实际总价
+	IsFix *bool `bson:"isFix,omitempty" json:"isFix"`
+
 	//备注
 	Remark string `bson:"remark,omitempty" json:"remark"`
 

+ 3 - 0
boxcost/db/model/pack.go

@@ -53,6 +53,9 @@ type ComponentStage struct {
 	// 下单数量
 	OrderCount int `bson:"orderCount,omitempty" json:"orderCount"`
 
+	// 是否固定价格 如果是:为预算总价,不是:为实际总价
+	IsFix *bool `bson:"isFix,omitempty" json:"isFix"`
+
 	//实际价格 OrderPrice*ConfirmCount
 	//预算价格 OrderPrice*OrderCount