plan.go 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161
  1. package api
  2. import (
  3. "archive/zip"
  4. "box-cost/db/model"
  5. "box-cost/db/repo"
  6. "box-cost/log"
  7. "bytes"
  8. "errors"
  9. "fmt"
  10. "io"
  11. "os"
  12. "path/filepath"
  13. "regexp"
  14. "strings"
  15. "sync"
  16. "time"
  17. "github.com/gin-gonic/gin"
  18. "github.com/xuri/excelize/v2"
  19. "go.mongodb.org/mongo-driver/bson"
  20. "go.mongodb.org/mongo-driver/bson/primitive"
  21. )
  22. // 生产计划管理
  23. func ProductPlan(r *GinRouter) {
  24. // 创建生产计划
  25. r.POST("/plan/create", CreateProductPlan)
  26. // 获取生产计划详情
  27. r.GET("/plan/detail/:id", GetProductPlan)
  28. // 获取生产计划列表
  29. r.GET("/plan/list", GetProductPlans)
  30. // 更新生产计划
  31. r.POST("/plan/update", UpdateProductPlan)
  32. // 删除生产计划
  33. r.POST("/plan/delete/:id", DelProductPlan)
  34. // 下载部件单据
  35. // r.GET("/bill/plan/download", DownLoadCompBills)
  36. r.GET("/bill/plan/download", DownLoadPlanBills)
  37. r.GET("/bill/plan/downloadPdf", DownLoadPlanBillsPdf)
  38. // 生产成本表
  39. r.GET("/plan/cost/download", DownLoadPlanCost)
  40. // 单据批量分配给供应商
  41. r.GET("/plan/alloc/batch", PlanAllocBatch)
  42. }
  43. // 把某个计划的订单批量分配给供应商
  44. // id 为计划id
  45. // /plan/alloc/batch?id=xxx
  46. func PlanAllocBatch(c *gin.Context, apictx *ApiSession) (interface{}, error) {
  47. _planId := c.Query("id")
  48. planId, err := primitive.ObjectIDFromHex(_planId)
  49. if err != nil {
  50. return nil, errors.New("planId错误")
  51. }
  52. plan := model.ProductPlan{}
  53. found, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
  54. CollectName: repo.CollectionProductPlan,
  55. Query: repo.Map{"_id": planId},
  56. }, &plan)
  57. if !found || err != nil {
  58. return nil, errors.New("数据未找到")
  59. }
  60. // 获取所有stages单据id
  61. billIds := make([]string, 0)
  62. for _, comp := range plan.Pack.Components {
  63. if comp.Id == "" || len(comp.Stages) == 0 {
  64. continue
  65. }
  66. for _, stage := range comp.Stages {
  67. billId, _ := primitive.ObjectIDFromHex(stage.BillId)
  68. if !billId.IsZero() {
  69. billIds = append(billIds, fmt.Sprintf("%d_%s", stage.BillType, stage.BillId))
  70. }
  71. }
  72. }
  73. // 去重单据号
  74. typeBillIds := removeDuplicationSort(billIds)
  75. if len(typeBillIds) < 1 {
  76. return nil, errors.New("未找到单据信息")
  77. }
  78. var wg sync.WaitGroup
  79. for _, tId := range typeBillIds {
  80. var err error
  81. billType := ""
  82. tidArr := strings.Split(tId, "_")
  83. // 采购
  84. billId, _ := primitive.ObjectIDFromHex(tidArr[1])
  85. if tidArr[0] == "1" {
  86. billType = PURCHASE_BILL_TYPE
  87. _, err = repo.RepoUpdateSetDoc(apictx.CreateRepoCtx(), repo.CollectionBillPurchase, billId.Hex(), &model.PurchaseBill{IsSend: true, SendTime: time.Now()})
  88. }
  89. // 工艺
  90. if tidArr[0] == "2" {
  91. billType = PRODUCE_BILL_TYPE
  92. _, err = repo.RepoUpdateSetDoc(apictx.CreateRepoCtx(), repo.CollectionBillProduce, billId.Hex(), &model.ProduceBill{IsSend: true, SendTime: time.Now()})
  93. }
  94. // 成品采购
  95. if tidArr[0] == "3" {
  96. billType = PRODUCT_BILL_TYPE
  97. _, err = repo.RepoUpdateSetDoc(apictx.CreateRepoCtx(), repo.CollectionBillProduct, billId.Hex(), &model.ProductBill{IsSend: true, SendTime: time.Now()})
  98. }
  99. if err == nil {
  100. // 给供应商发送通知短信
  101. smsInfo, err := genSupplierSmsTemp(billId, billType, apictx)
  102. fmt.Println(smsInfo)
  103. if err == nil {
  104. wg.Add(1)
  105. go SendSmsNotify(smsInfo.Phone, &SupplierSmsReq{smsInfo.Product, smsInfo.SerialNumber}, &wg)
  106. // err = SendSmsNotify1(smsInfo.Phone, &SupplierSmsReq{smsInfo.Product, smsInfo.SerialNumber})
  107. // fmt.Println(err)
  108. }
  109. }
  110. }
  111. wg.Wait()
  112. return true, nil
  113. }
  114. // 更新供应商确定数量与plan中stage项的同步
  115. func updateStageCount(billId primitive.ObjectID, planId primitive.ObjectID, idCounts map[string]int, apictx *ApiSession) (interface{}, error) {
  116. if len(idCounts) == 0 {
  117. return true, nil
  118. }
  119. plan := model.ProductPlan{}
  120. found, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
  121. CollectName: repo.CollectionProductPlan,
  122. Query: repo.Map{"_id": planId},
  123. }, &plan)
  124. if !found || err != nil {
  125. return nil, errors.New("数据未找到")
  126. }
  127. for _, comp := range plan.Pack.Components {
  128. if comp.Id == "" || len(comp.Stages) == 0 {
  129. continue
  130. }
  131. for _, stage := range comp.Stages {
  132. if v, ok := idCounts[stage.Id]; ok {
  133. stage.ConfirmCount = v
  134. }
  135. }
  136. }
  137. plan.UpdateTime = time.Now()
  138. result, err := repo.RepoUpdateSetDoc(apictx.CreateRepoCtx(), repo.CollectionProductPlan, planId.Hex(), &plan)
  139. if err != nil {
  140. log.Error(err)
  141. fmt.Println(err)
  142. }
  143. return result, err
  144. }
  145. type SupplierPlanCost struct {
  146. *model.ProductPlan
  147. SupplierId string
  148. }
  149. func DownLoadPlanCost(c *gin.Context, apictx *ApiSession) (interface{}, error) {
  150. _planId := c.Query("id")
  151. _supplierId := c.Query("supplierId")
  152. supplierId, _ := primitive.ObjectIDFromHex(_supplierId)
  153. planId, _ := primitive.ObjectIDFromHex(_planId)
  154. if planId.IsZero() {
  155. return nil, errors.New("planId错误")
  156. }
  157. plan := model.ProductPlan{}
  158. found, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
  159. CollectName: repo.CollectionProductPlan,
  160. Query: repo.Map{"_id": planId},
  161. }, &plan)
  162. if !found || err != nil {
  163. return nil, errors.New("数据未找到")
  164. }
  165. summaryPlans := []*PlanSummary{}
  166. summaryPlan := GetPlanStatus(&plan, apictx)
  167. summaryPlans = append(summaryPlans, summaryPlan)
  168. if len(summaryPlans) < 1 {
  169. return nil, errors.New("数据不存在")
  170. }
  171. f := excelize.NewFile()
  172. index := f.NewSheet("Sheet1")
  173. f.SetActiveSheet(index)
  174. f.SetDefaultFont("宋体")
  175. planSummaryExcel := NewPlanCostExcel(f)
  176. planSummaryExcel.Content = &SupplierPlanSummary{
  177. Plans: summaryPlans,
  178. SupplierId: supplierId,
  179. }
  180. // 绘制表格
  181. planSummaryExcel.Title = fmt.Sprintf("生产成本表(%s)%d盒", plan.Name, plan.Total)
  182. planSummaryExcel.Draws()
  183. c.Header("Content-Type", "application/octet-stream")
  184. c.Header("Content-Disposition", "attachment; filename="+"planCost.xlsx")
  185. c.Header("Content-Transfer-Encoding", "binary")
  186. err = f.Write(c.Writer)
  187. if err != nil {
  188. return nil, err
  189. }
  190. return nil, nil
  191. }
  192. func DownLoadPlanBills(c *gin.Context, apictx *ApiSession) (interface{}, error) {
  193. _planId := c.Query("id")
  194. planId, err := primitive.ObjectIDFromHex(_planId)
  195. if err != nil {
  196. return nil, errors.New("planId错误")
  197. }
  198. plan := model.ProductPlan{}
  199. found, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
  200. CollectName: repo.CollectionProductPlan,
  201. Query: repo.Map{"_id": planId},
  202. }, &plan)
  203. if !found || err != nil {
  204. return nil, errors.New("数据未找到")
  205. }
  206. // 获取所有stages单据id
  207. billIds := make([]string, 0)
  208. for _, comp := range plan.Pack.Components {
  209. if comp.Id == "" || len(comp.Stages) == 0 {
  210. continue
  211. }
  212. for _, stage := range comp.Stages {
  213. billId, _ := primitive.ObjectIDFromHex(stage.BillId)
  214. if !billId.IsZero() {
  215. billIds = append(billIds, fmt.Sprintf("%d_%s", stage.BillType, stage.BillId))
  216. }
  217. }
  218. }
  219. // 去重单据号
  220. typeBillIds := removeDuplicationSort(billIds)
  221. if len(typeBillIds) < 1 {
  222. return nil, errors.New("未找到单据信息")
  223. }
  224. f := excelize.NewFile()
  225. f.SetDefaultFont("宋体")
  226. flagIndex := -1
  227. companyName := getCompanyName(apictx)
  228. // 采购 加工 加工-印刷 加工-覆膜 成品采购
  229. typeRows := []int{0, 0, 0, 0, 0}
  230. for _, tId := range typeBillIds {
  231. tidArr := strings.Split(tId, "_")
  232. var billExcel IExcel
  233. // 采购
  234. billId, _ := primitive.ObjectIDFromHex(tidArr[1])
  235. if tidArr[0] == "1" {
  236. purchase := model.PurchaseBill{}
  237. found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
  238. CollectName: repo.CollectionBillPurchase,
  239. Query: repo.Map{"_id": billId},
  240. }, &purchase)
  241. if !found {
  242. continue
  243. }
  244. sheetName := "采购单"
  245. index := f.NewSheet(sheetName)
  246. if flagIndex < 0 {
  247. flagIndex = index
  248. }
  249. // index := f.NewSheet("采购单")
  250. // f.SetActiveSheet(index)
  251. billExcel = NewPurchaseBill(f)
  252. billExcel.SetSheetName(sheetName)
  253. if purchase.Reviewed == 1 {
  254. if len(purchase.SignUsers) > 0 {
  255. signs := []*model.Signature{}
  256. repo.RepoDocsSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{
  257. CollectName: repo.CollectionSignature,
  258. Query: repo.Map{"_id": bson.M{"$in": purchase.SignUsers}},
  259. Sort: bson.M{"sort": 1},
  260. }, &signs)
  261. billExcel.SetSignatures(signs)
  262. }
  263. }
  264. billExcel.SetContent(&purchase)
  265. billExcel.SetTitle(fmt.Sprintf("%s原材料采购单", companyName))
  266. billExcel.SetRow(typeRows[0])
  267. billExcel.Draws()
  268. typeRows[0] = billExcel.GetRow() + 5
  269. }
  270. // 工艺
  271. if tidArr[0] == "2" {
  272. produce := model.ProduceBill{}
  273. found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
  274. CollectName: repo.CollectionBillProduce,
  275. Query: repo.Map{"_id": billId},
  276. }, &produce)
  277. if !found {
  278. continue
  279. }
  280. sheetName := "加工单"
  281. if produce.IsPrint {
  282. sheetName = "加工单-印刷"
  283. } else if produce.IsLam {
  284. sheetName = "加工单-覆膜"
  285. }
  286. index := f.NewSheet(sheetName)
  287. if flagIndex < 0 {
  288. flagIndex = index
  289. }
  290. billExcel = NewProduceBill(f)
  291. billExcel.SetSheetName(sheetName)
  292. if produce.Reviewed == 1 {
  293. if len(produce.SignUsers) > 0 {
  294. signs := []*model.Signature{}
  295. repo.RepoDocsSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{
  296. CollectName: repo.CollectionSignature,
  297. Query: repo.Map{"_id": bson.M{"$in": produce.SignUsers}},
  298. Sort: bson.M{"sort": 1},
  299. }, &signs)
  300. billExcel.SetSignatures(signs)
  301. }
  302. }
  303. billExcel.SetContent(&produce)
  304. billExcel.SetTitle(fmt.Sprintf("%s加工单", companyName))
  305. if produce.IsPrint {
  306. billExcel.SetRow(typeRows[2])
  307. billExcel.Draws()
  308. typeRows[2] = billExcel.GetRow() + 5
  309. } else if produce.IsLam {
  310. billExcel.SetRow(typeRows[3])
  311. billExcel.Draws()
  312. typeRows[3] = billExcel.GetRow() + 5
  313. } else {
  314. billExcel.SetRow(typeRows[1])
  315. billExcel.Draws()
  316. typeRows[1] = billExcel.GetRow() + 5
  317. }
  318. }
  319. // 成品采购
  320. if tidArr[0] == "3" {
  321. product := model.ProductBill{}
  322. found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
  323. CollectName: repo.CollectionBillProduct,
  324. Query: repo.Map{"_id": billId},
  325. }, &product)
  326. if !found {
  327. continue
  328. }
  329. sheetName := "成品采购单"
  330. index := f.NewSheet(sheetName)
  331. if flagIndex < 0 {
  332. flagIndex = index
  333. }
  334. billExcel = NewProductBill(f)
  335. billExcel.SetSheetName(sheetName)
  336. if product.Reviewed == 1 {
  337. if len(product.SignUsers) > 0 {
  338. signs := []*model.Signature{}
  339. repo.RepoDocsSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{
  340. CollectName: repo.CollectionSignature,
  341. Query: repo.Map{"_id": bson.M{"$in": product.SignUsers}},
  342. Sort: bson.M{"sort": 1},
  343. }, &signs)
  344. billExcel.SetSignatures(signs)
  345. }
  346. }
  347. billExcel.SetContent(&product)
  348. billExcel.SetTitle(companyName)
  349. billExcel.SetRow(typeRows[4])
  350. billExcel.Draws()
  351. typeRows[4] = billExcel.GetRow() + 5
  352. }
  353. }
  354. // 设置活跃sheet
  355. f.SetActiveSheet(flagIndex)
  356. // 删除默认Sheet1
  357. f.DeleteSheet("Sheet1")
  358. c.Header("Content-Type", "application/octet-stream")
  359. c.Header("Content-Disposition", "attachment; filename="+"bill.xlsx")
  360. c.Header("Content-Transfer-Encoding", "binary")
  361. err = f.Write(c.Writer)
  362. if err != nil {
  363. return nil, err
  364. }
  365. return nil, nil
  366. }
  367. // todo old 功能确定后删除
  368. func DownLoadPlanBillsBack(c *gin.Context, apictx *ApiSession) (interface{}, error) {
  369. _planId := c.Query("id")
  370. planId, err := primitive.ObjectIDFromHex(_planId)
  371. if err != nil {
  372. return nil, errors.New("planId错误")
  373. }
  374. plan := model.ProductPlan{}
  375. found, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
  376. CollectName: repo.CollectionProductPlan,
  377. Query: repo.Map{"_id": planId},
  378. }, &plan)
  379. if !found || err != nil {
  380. return nil, errors.New("数据未找到")
  381. }
  382. // 获取所有stages单据id
  383. billIds := make([]string, 0)
  384. for _, comp := range plan.Pack.Components {
  385. if comp.Id == "" || len(comp.Stages) == 0 {
  386. continue
  387. }
  388. for _, stage := range comp.Stages {
  389. billId, _ := primitive.ObjectIDFromHex(stage.BillId)
  390. if !billId.IsZero() {
  391. billIds = append(billIds, fmt.Sprintf("%d_%s", stage.BillType, stage.BillId))
  392. }
  393. }
  394. }
  395. // 去重单据号
  396. typeBillIds := removeDuplicationSort(billIds)
  397. if len(typeBillIds) < 1 {
  398. return nil, errors.New("未找到单据信息")
  399. }
  400. f := excelize.NewFile()
  401. index := f.NewSheet("Sheet1")
  402. f.SetActiveSheet(index)
  403. f.SetDefaultFont("宋体")
  404. companyName := getCompanyName(apictx)
  405. row := 0
  406. for _, tId := range typeBillIds {
  407. tidArr := strings.Split(tId, "_")
  408. var billExcel IExcel
  409. // 采购
  410. billId, _ := primitive.ObjectIDFromHex(tidArr[1])
  411. if tidArr[0] == "1" {
  412. purchase := model.PurchaseBill{}
  413. found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
  414. CollectName: repo.CollectionBillPurchase,
  415. Query: repo.Map{"_id": billId},
  416. }, &purchase)
  417. if found {
  418. billExcel = NewPurchaseBill(f)
  419. if purchase.Reviewed == 1 {
  420. if len(purchase.SignUsers) > 0 {
  421. signs := []*model.Signature{}
  422. repo.RepoDocsSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{
  423. CollectName: repo.CollectionSignature,
  424. Query: repo.Map{"_id": bson.M{"$in": purchase.SignUsers}},
  425. Sort: bson.M{"sort": 1},
  426. }, &signs)
  427. billExcel.SetSignatures(signs)
  428. }
  429. }
  430. billExcel.SetContent(&purchase)
  431. billExcel.SetTitle(fmt.Sprintf("%s原材料采购单", companyName))
  432. }
  433. }
  434. // 工艺
  435. if tidArr[0] == "2" {
  436. produce := model.ProduceBill{}
  437. found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
  438. CollectName: repo.CollectionBillProduce,
  439. Query: repo.Map{"_id": billId},
  440. }, &produce)
  441. if found {
  442. billExcel = NewProduceBill(f)
  443. if produce.Reviewed == 1 {
  444. if len(produce.SignUsers) > 0 {
  445. signs := []*model.Signature{}
  446. repo.RepoDocsSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{
  447. CollectName: repo.CollectionSignature,
  448. Query: repo.Map{"_id": bson.M{"$in": produce.SignUsers}},
  449. Sort: bson.M{"sort": 1},
  450. }, &signs)
  451. billExcel.SetSignatures(signs)
  452. }
  453. }
  454. billExcel.SetContent(&produce)
  455. billExcel.SetTitle(fmt.Sprintf("%s加工单", companyName))
  456. }
  457. }
  458. // 成品采购
  459. if tidArr[0] == "3" {
  460. product := model.ProductBill{}
  461. found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
  462. CollectName: repo.CollectionBillProduct,
  463. Query: repo.Map{"_id": billId},
  464. }, &product)
  465. if found {
  466. billExcel = NewProductBill(f)
  467. if product.Reviewed == 1 {
  468. if len(product.SignUsers) > 0 {
  469. signs := []*model.Signature{}
  470. repo.RepoDocsSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{
  471. CollectName: repo.CollectionSignature,
  472. Query: repo.Map{"_id": bson.M{"$in": product.SignUsers}},
  473. Sort: bson.M{"sort": 1},
  474. }, &signs)
  475. billExcel.SetSignatures(signs)
  476. }
  477. }
  478. billExcel.SetContent(&product)
  479. billExcel.SetTitle(companyName)
  480. }
  481. }
  482. if billExcel == nil {
  483. continue
  484. }
  485. billExcel.SetRow(row)
  486. billExcel.Draws()
  487. row = billExcel.GetRow() + 5
  488. }
  489. c.Header("Content-Type", "application/octet-stream")
  490. c.Header("Content-Disposition", "attachment; filename="+"bill.xlsx")
  491. c.Header("Content-Transfer-Encoding", "binary")
  492. err = f.Write(c.Writer)
  493. if err != nil {
  494. return nil, err
  495. }
  496. return nil, nil
  497. }
  498. func DownLoadPlanBillsPdf(c *gin.Context, apictx *ApiSession) (interface{}, error) {
  499. _planId := c.Query("id")
  500. planId, err := primitive.ObjectIDFromHex(_planId)
  501. if err != nil {
  502. return nil, errors.New("planId错误")
  503. }
  504. plan := model.ProductPlan{}
  505. found, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
  506. CollectName: repo.CollectionProductPlan,
  507. Query: repo.Map{"_id": planId},
  508. }, &plan)
  509. if !found || err != nil {
  510. return nil, errors.New("数据未找到")
  511. }
  512. // 获取所有stages单据id
  513. billIds := make([]string, 0)
  514. for _, comp := range plan.Pack.Components {
  515. if comp.Id == "" || len(comp.Stages) == 0 {
  516. continue
  517. }
  518. for _, stage := range comp.Stages {
  519. billId, _ := primitive.ObjectIDFromHex(stage.BillId)
  520. if !billId.IsZero() {
  521. billIds = append(billIds, fmt.Sprintf("%d_%s", stage.BillType, stage.BillId))
  522. }
  523. }
  524. }
  525. // 去重单据号
  526. typeBillIds := removeDuplicationSort(billIds)
  527. if len(typeBillIds) < 1 {
  528. return nil, errors.New("未找到单据信息")
  529. }
  530. companyName := getCompanyName(apictx)
  531. _planName := plan.Name
  532. r := regexp.MustCompile(`/`)
  533. planName := r.ReplaceAllString(_planName, `&`)
  534. // fmt.Println(planName)
  535. // 打包pdf的缓存目录
  536. saveTmpDir := fmt.Sprintf("tmp1/%s", planName)
  537. if isExistDir(saveTmpDir) {
  538. os.RemoveAll(saveTmpDir)
  539. }
  540. // 记录文件数量
  541. var wg sync.WaitGroup
  542. c1 := make(chan int)
  543. for _, tId := range typeBillIds {
  544. productName := ""
  545. supplierName := ""
  546. serialNumber := ""
  547. f := excelize.NewFile()
  548. index := f.NewSheet("Sheet1")
  549. f.SetActiveSheet(index)
  550. f.SetDefaultFont("宋体")
  551. tidArr := strings.Split(tId, "_")
  552. var billExcel IExcel
  553. // 采购
  554. billId, _ := primitive.ObjectIDFromHex(tidArr[1])
  555. if tidArr[0] == "1" {
  556. purchase := model.PurchaseBill{}
  557. found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
  558. CollectName: repo.CollectionBillPurchase,
  559. Query: repo.Map{"_id": billId},
  560. }, &purchase)
  561. if !found {
  562. continue
  563. }
  564. billExcel = NewPurchaseBill(f)
  565. if purchase.Reviewed == 1 {
  566. if len(purchase.SignUsers) > 0 {
  567. signs := []*model.Signature{}
  568. repo.RepoDocsSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{
  569. CollectName: repo.CollectionSignature,
  570. Query: repo.Map{"_id": bson.M{"$in": purchase.SignUsers}},
  571. Sort: bson.M{"sort": 1},
  572. }, &signs)
  573. billExcel.SetSignatures(signs)
  574. }
  575. }
  576. productName = purchase.ProductName
  577. supplierName = purchase.Supplier
  578. serialNumber = purchase.SerialNumber
  579. billExcel.SetContent(&purchase)
  580. billExcel.SetTitle(fmt.Sprintf("%s原材料采购单", companyName))
  581. }
  582. // 工艺
  583. if tidArr[0] == "2" {
  584. produce := model.ProduceBill{}
  585. found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
  586. CollectName: repo.CollectionBillProduce,
  587. Query: repo.Map{"_id": billId},
  588. }, &produce)
  589. if !found {
  590. continue
  591. }
  592. billExcel = NewProduceBill(f)
  593. if produce.Reviewed == 1 {
  594. if len(produce.SignUsers) > 0 {
  595. signs := []*model.Signature{}
  596. repo.RepoDocsSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{
  597. CollectName: repo.CollectionSignature,
  598. Query: repo.Map{"_id": bson.M{"$in": produce.SignUsers}},
  599. Sort: bson.M{"sort": 1},
  600. }, &signs)
  601. billExcel.SetSignatures(signs)
  602. }
  603. }
  604. productName = produce.ProductName
  605. supplierName = produce.Supplier
  606. serialNumber = produce.SerialNumber
  607. billExcel.SetContent(&produce)
  608. billExcel.SetTitle(fmt.Sprintf("%s加工单", companyName))
  609. }
  610. // 成品采购
  611. if tidArr[0] == "3" {
  612. product := model.ProductBill{}
  613. found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
  614. CollectName: repo.CollectionBillProduct,
  615. Query: repo.Map{"_id": billId},
  616. }, &product)
  617. if !found {
  618. continue
  619. }
  620. billExcel = NewProductBill(f)
  621. if product.Reviewed == 1 {
  622. if len(product.SignUsers) > 0 {
  623. signs := []*model.Signature{}
  624. repo.RepoDocsSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{
  625. CollectName: repo.CollectionSignature,
  626. Query: repo.Map{"_id": bson.M{"$in": product.SignUsers}},
  627. Sort: bson.M{"sort": 1},
  628. }, &signs)
  629. billExcel.SetSignatures(signs)
  630. }
  631. }
  632. productName = product.ProductName
  633. supplierName = product.Supplier
  634. serialNumber = product.SerialNumber
  635. billExcel.SetContent(&product)
  636. billExcel.SetTitle(companyName)
  637. }
  638. billExcel.SetIsPdf("true")
  639. billExcel.Draws()
  640. buf, _ := f.WriteToBuffer()
  641. // r := regexp.MustCompile(`/`)
  642. _productName := r.ReplaceAllString(productName, `&`)
  643. _supplierName := r.ReplaceAllString(supplierName, `&`)
  644. targePdfName := fmt.Sprintf("%s-%s-%s.pdf", _supplierName, _productName, serialNumber)
  645. // fmt.Println(targePdfName)
  646. wg.Add(1)
  647. go toPdfAndSaveTask(buf, apictx.Svc.Conf.PdfApiAddr, saveTmpDir, targePdfName, c1, &wg)
  648. }
  649. go func() {
  650. // 等待所有goroutine
  651. wg.Wait()
  652. // 关闭channel
  653. close(c1)
  654. }()
  655. // channel关闭后结束后停止遍历接收channel中的值
  656. for n := range c1 {
  657. if n == -1 {
  658. return nil, errors.New("下载失败,请重试")
  659. }
  660. }
  661. c.Header("Content-Type", "application/octet-stream")
  662. c.Header("Content-Disposition", "attachment; filename="+planName+".zip")
  663. c.Header("Content-Transfer-Encoding", "binary")
  664. archive := zip.NewWriter(c.Writer)
  665. defer archive.Close()
  666. // 遍历路径信息
  667. filepath.Walk(saveTmpDir, func(path string, info os.FileInfo, _ error) error {
  668. // 如果是源路径,提前进行下一个遍历
  669. if path == saveTmpDir {
  670. return nil
  671. }
  672. // 获取:文件头信息
  673. header, _ := zip.FileInfoHeader(info)
  674. header.Name = strings.TrimPrefix(path, saveTmpDir+`/`)
  675. // 判断:文件是不是文件夹
  676. if info.IsDir() {
  677. header.Name += `/`
  678. } else {
  679. // 设置:zip的文件压缩算法
  680. header.Method = zip.Deflate
  681. }
  682. // 创建:压缩包头部信息
  683. writer, _ := archive.CreateHeader(header)
  684. if !info.IsDir() {
  685. file, _ := os.Open(path)
  686. defer file.Close()
  687. io.Copy(writer, file)
  688. }
  689. return nil
  690. })
  691. // 删除缓存目录
  692. os.RemoveAll(saveTmpDir)
  693. return nil, nil
  694. }
  695. type ToPdfResult struct {
  696. IsSucc bool
  697. Err error
  698. }
  699. func toPdfAndSaveTask(buf *bytes.Buffer, toPdfAddr, saveTmpDir, targetPdfName string, toPdfResult chan<- int, wg *sync.WaitGroup) {
  700. if buf.Len() < 1<<10 {
  701. fmt.Println("execl内容为空")
  702. log.Error("execl内容为空")
  703. toPdfResult <- -1
  704. wg.Done()
  705. return
  706. }
  707. res, err := excelToPdf(buf, toPdfAddr)
  708. if err != nil {
  709. fmt.Println(err)
  710. log.Error(err)
  711. // pdfRes := ToPdfResult{
  712. // IsSucc: false,
  713. // Err: err,
  714. // }
  715. // toPdfResult <- pdfRes
  716. toPdfResult <- -1
  717. wg.Done()
  718. return
  719. }
  720. byteData, err := io.ReadAll(res.Body)
  721. if err != nil {
  722. fmt.Println(err)
  723. // pdfRes := ToPdfResult{
  724. // IsSucc: false,
  725. // Err: err,
  726. // }
  727. // toPdfResult <- pdfRes
  728. toPdfResult <- -1
  729. wg.Done()
  730. return
  731. }
  732. if len(byteData) < 1 {
  733. fmt.Println("pdf内容为空")
  734. log.Error("pdf内容为空")
  735. toPdfResult <- -1
  736. wg.Done()
  737. return
  738. }
  739. defer res.Body.Close()
  740. err = savePdfToTmp(saveTmpDir, targetPdfName, byteData)
  741. if err != nil {
  742. // pdfRes := ToPdfResult{
  743. // IsSucc: false,
  744. // Err: err,
  745. // }
  746. // toPdfResult <- pdfRes
  747. toPdfResult <- -1
  748. wg.Done()
  749. return
  750. }
  751. // pdfRes := ToPdfResult{
  752. // IsSucc: true,
  753. // Err: err,
  754. // }
  755. // toPdfResult <- pdfRes
  756. toPdfResult <- 1
  757. wg.Done()
  758. }
  759. // todo 已弃用 功能稳定后删除
  760. func DownLoadCompBills(c *gin.Context, apictx *ApiSession) (interface{}, error) {
  761. _planId := c.Query("id")
  762. compId := c.Query("compId")
  763. planId, err := primitive.ObjectIDFromHex(_planId)
  764. if err != nil {
  765. return nil, errors.New("planId错误")
  766. }
  767. plan := model.ProductPlan{}
  768. found, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
  769. CollectName: repo.CollectionProductPlan,
  770. Query: repo.Map{"_id": planId},
  771. }, &plan)
  772. if !found || err != nil {
  773. return nil, errors.New("数据未找到")
  774. }
  775. // 获取部件单据
  776. curComp := &model.PackComponent{}
  777. for _, comp := range plan.Pack.Components {
  778. if comp.Id == compId {
  779. curComp = comp
  780. }
  781. }
  782. if curComp.Id == "" {
  783. return nil, errors.New("该组件不存在")
  784. }
  785. // 获取bill
  786. if len(curComp.Stages) == 0 {
  787. return nil, errors.New("该组件数据不存在")
  788. }
  789. // 获取不同类型的单据id
  790. billIds := make([]string, 0)
  791. for _, stage := range curComp.Stages {
  792. billId, _ := primitive.ObjectIDFromHex(stage.BillId)
  793. if !billId.IsZero() {
  794. billIds = append(billIds, fmt.Sprintf("%d_%s", stage.BillType, stage.BillId))
  795. }
  796. }
  797. // 去重单据号
  798. typeBillIds := removeDuplicationSort(billIds)
  799. if len(typeBillIds) < 1 {
  800. return nil, errors.New("未找到单据信息")
  801. }
  802. f := excelize.NewFile()
  803. index := f.NewSheet("Sheet1")
  804. f.SetActiveSheet(index)
  805. f.SetDefaultFont("宋体")
  806. companyName := getCompanyName(apictx)
  807. row := 0
  808. for _, tId := range typeBillIds {
  809. tidArr := strings.Split(tId, "_")
  810. var billExcel IExcel
  811. // 采购
  812. billId, _ := primitive.ObjectIDFromHex(tidArr[1])
  813. if tidArr[0] == "1" {
  814. purchase := model.PurchaseBill{}
  815. found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
  816. CollectName: repo.CollectionBillPurchase,
  817. Query: repo.Map{"_id": billId},
  818. }, &purchase)
  819. if found {
  820. billExcel = NewPurchaseBill(f)
  821. if purchase.Reviewed == 1 {
  822. if len(purchase.SignUsers) > 0 {
  823. signs := []*model.Signature{}
  824. repo.RepoDocsSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{
  825. CollectName: repo.CollectionSignature,
  826. Query: repo.Map{"_id": bson.M{"$in": purchase.SignUsers}},
  827. Sort: bson.M{"sort": 1},
  828. }, &signs)
  829. billExcel.SetSignatures(signs)
  830. }
  831. }
  832. billExcel.SetContent(&purchase)
  833. billExcel.SetTitle(fmt.Sprintf("%s原材料采购单", companyName))
  834. }
  835. }
  836. // 工艺
  837. if tidArr[0] == "2" {
  838. produce := model.ProduceBill{}
  839. found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
  840. CollectName: repo.CollectionBillProduce,
  841. Query: repo.Map{"_id": billId},
  842. }, &produce)
  843. if found {
  844. billExcel = NewProduceBill(f)
  845. if produce.Reviewed == 1 {
  846. if len(produce.SignUsers) > 0 {
  847. signs := []*model.Signature{}
  848. repo.RepoDocsSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{
  849. CollectName: repo.CollectionSignature,
  850. Query: repo.Map{"_id": bson.M{"$in": produce.SignUsers}},
  851. Sort: bson.M{"sort": 1},
  852. }, &signs)
  853. billExcel.SetSignatures(signs)
  854. }
  855. }
  856. billExcel.SetContent(&produce)
  857. billExcel.SetTitle(fmt.Sprintf("%s加工单", companyName))
  858. }
  859. }
  860. // 成品采购
  861. if tidArr[0] == "3" {
  862. product := model.ProductBill{}
  863. found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
  864. CollectName: repo.CollectionBillProduct,
  865. Query: repo.Map{"_id": billId},
  866. }, &product)
  867. if found {
  868. billExcel = NewProductBill(f)
  869. if product.Reviewed == 1 {
  870. if len(product.SignUsers) > 0 {
  871. signs := []*model.Signature{}
  872. repo.RepoDocsSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{
  873. CollectName: repo.CollectionSignature,
  874. Query: repo.Map{"_id": bson.M{"$in": product.SignUsers}},
  875. Sort: bson.M{"sort": 1},
  876. }, &signs)
  877. billExcel.SetSignatures(signs)
  878. }
  879. }
  880. billExcel.SetContent(&product)
  881. billExcel.SetTitle(companyName)
  882. }
  883. }
  884. if billExcel == nil {
  885. continue
  886. }
  887. billExcel.SetRow(row)
  888. billExcel.Draws()
  889. row = billExcel.GetRow() + 5
  890. }
  891. c.Header("Content-Type", "application/octet-stream")
  892. c.Header("Content-Disposition", "attachment; filename="+"bill.xlsx")
  893. c.Header("Content-Transfer-Encoding", "binary")
  894. err = f.Write(c.Writer)
  895. if err != nil {
  896. return nil, err
  897. }
  898. return nil, nil
  899. }
  900. // 创建生产计划
  901. func CreateProductPlan(c *gin.Context, apictx *ApiSession) (interface{}, error) {
  902. var plan model.ProductPlan
  903. err := c.ShouldBindJSON(&plan)
  904. if err != nil {
  905. fmt.Println(err)
  906. return nil, errors.New("参数错误!")
  907. }
  908. if plan.Name == "" {
  909. return nil, errors.New("生产计划名为空")
  910. }
  911. if plan.Total == 0 {
  912. return nil, errors.New("生产计划数应不为0")
  913. }
  914. plan.Status = "process" // 进行中
  915. plan.CreateTime = time.Now()
  916. plan.UpdateTime = time.Now()
  917. result, err := repo.RepoAddDoc(apictx.CreateRepoCtx(), repo.CollectionProductPlan, &plan)
  918. return result, err
  919. }
  920. // 获取生产计划信息
  921. func GetProductPlan(c *gin.Context, apictx *ApiSession) (interface{}, error) {
  922. planId := c.Param("id")
  923. id, err := primitive.ObjectIDFromHex(planId)
  924. if err != nil {
  925. return nil, errors.New("非法id")
  926. }
  927. var plan model.ProductPlan
  928. option := &repo.DocSearchOptions{
  929. CollectName: repo.CollectionProductPlan,
  930. Query: repo.Map{"_id": id},
  931. }
  932. found, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), option, &plan)
  933. if !found || err != nil {
  934. log.Info(err)
  935. return nil, errors.New("数据未找到")
  936. }
  937. billStates := map[string]string{}
  938. billIsSend := map[string]bool{}
  939. billReviewed := map[string]int32{}
  940. billIsAck := map[string]bool{}
  941. if plan.Pack != nil && plan.Pack.Components != nil {
  942. for _, comp := range plan.Pack.Components {
  943. if comp.Stages != nil {
  944. for _, stage := range comp.Stages {
  945. if len(stage.BillId) > 0 {
  946. collectName := ""
  947. // 材料
  948. if stage.BillType == 1 {
  949. collectName = repo.CollectionBillPurchase
  950. }
  951. // 工艺
  952. if stage.BillType == 2 {
  953. collectName = repo.CollectionBillProduce
  954. }
  955. // 成品
  956. if stage.BillType == 3 {
  957. collectName = repo.CollectionBillProduct
  958. }
  959. ok, state := repo.RepoSeachDocMap(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
  960. CollectName: collectName,
  961. Query: repo.Map{"_id": stage.BillId},
  962. Project: []string{"status", "isSend", "reviewed", "isAck", "serialNumber", "remark"}})
  963. if ok {
  964. billStates[stage.BillId] = state["status"].(string)
  965. if v, ok := state["isSend"]; ok {
  966. billIsSend[stage.BillId] = v.(bool)
  967. } else {
  968. billIsSend[stage.BillId] = false
  969. }
  970. if v, ok := state["reviewed"]; ok {
  971. billReviewed[stage.BillId] = v.(int32)
  972. } else {
  973. billReviewed[stage.BillId] = -1
  974. }
  975. if v, ok := state["isAck"]; ok {
  976. billIsAck[stage.BillId] = v.(bool)
  977. } else {
  978. billIsAck[stage.BillId] = false
  979. }
  980. }
  981. }
  982. }
  983. }
  984. }
  985. }
  986. return map[string]interface{}{
  987. "plan": plan,
  988. "billStates": billStates,
  989. "billIsSend": billIsSend,
  990. "billReviewed": billReviewed,
  991. "billIsAck": billIsAck,
  992. }, nil
  993. }
  994. // 获取生产计划列表
  995. func GetProductPlans(c *gin.Context, apictx *ApiSession) (interface{}, error) {
  996. page, size, query := UtilQueryPageSize(c)
  997. if _packId, ok := query["packId"]; ok {
  998. packId, _ := primitive.ObjectIDFromHex(_packId.(string))
  999. query["pack._id"] = packId
  1000. delete(query, "packId")
  1001. }
  1002. if _name, ok := query["name"]; ok {
  1003. delete(query, "name")
  1004. query["name"] = bson.M{"$regex": _name.(string)}
  1005. }
  1006. option := &repo.PageSearchOptions{
  1007. CollectName: repo.CollectionProductPlan,
  1008. Query: query,
  1009. Page: page,
  1010. Size: size,
  1011. Sort: bson.M{"createTime": -1},
  1012. Project: []string{"_id", "thumbnail", "name", "updateTime", "createTime", "createUser", "total", "totalPrice", "status"},
  1013. }
  1014. return repo.RepoPageSearch(apictx.CreateRepoCtx(), option)
  1015. }
  1016. // 更新生产计划
  1017. func UpdateProductPlan(c *gin.Context, apictx *ApiSession) (interface{}, error) {
  1018. var plan model.ProductPlan
  1019. err := c.ShouldBindJSON(&plan)
  1020. if err != nil {
  1021. return nil, errors.New("参数错误")
  1022. }
  1023. if plan.Id.Hex() == "" {
  1024. return nil, errors.New("id的为空")
  1025. }
  1026. plan.UpdateTime = time.Now()
  1027. return repo.RepoUpdateSetDoc(apictx.CreateRepoCtx(), repo.CollectionProductPlan, plan.Id.Hex(), &plan)
  1028. }
  1029. // 删除生产计划
  1030. func DelProductPlan(c *gin.Context, apictx *ApiSession) (interface{}, error) {
  1031. planId := c.Param("id")
  1032. if planId == "" {
  1033. return nil, errors.New("id为空")
  1034. }
  1035. return repo.RepoDeleteDoc(apictx.CreateRepoCtx(), repo.CollectionProductPlan, planId)
  1036. }