Browse Source

add:出库申请

zoie 3 months ago
parent
commit
a6296fb543

+ 184 - 16
controllers/Contract.go

@@ -18,6 +18,7 @@ import (
 	userlibs "gogs.baozhida.cn/zoie/ERP_libs/User"
 	"gogs.baozhida.cn/zoie/ERP_libs/lib"
 	"math"
+	"net/url"
 	"os"
 	"strconv"
 	"strings"
@@ -630,23 +631,27 @@ func (c *ContractController) VerifyContract_Add() {
 	T_invoice := c.GetString("T_invoice")
 	T_start_date := c.GetString("T_start_date")
 	T_end_date := c.GetString("T_end_date")
+	T_stamping_date := c.GetString("T_stamping_date")
+	T_payment_method := c.GetString("T_payment_method")
 
 	var_ := Contract.Contract{
-		T_number:      T_number,
-		T_customer_id: T_customer_id,
-		T_money:       float32(T_money),
-		T_discount:    float32(T_discount),
-		T_project:     T_project,
-		T_type:        2, //合同类型 1-销售合同 2-验证合同
-		T_date:        T_date,
-		T_State:       1,
-		T_remark:      T_remark,
-		T_pdf:         T_pdf,
-		T_submit:      T_submit,
-		T_recoveries:  T_recoveries,
-		T_invoice:     T_invoice,
-		T_start_date:  T_start_date,
-		T_end_date:    T_end_date,
+		T_number:         T_number,
+		T_customer_id:    T_customer_id,
+		T_money:          float32(T_money),
+		T_discount:       float32(T_discount),
+		T_project:        T_project,
+		T_type:           2, //合同类型 1-销售合同 2-验证合同
+		T_date:           T_date,
+		T_State:          1,
+		T_remark:         T_remark,
+		T_pdf:            T_pdf,
+		T_submit:         T_submit,
+		T_recoveries:     T_recoveries,
+		T_invoice:        T_invoice,
+		T_start_date:     T_start_date,
+		T_end_date:       T_end_date,
+		T_stamping_date:  T_stamping_date,
+		T_payment_method: T_payment_method,
 	}
 	_, var_.T_recoveries_money = Contract.ContractToContractFinance_R(T_recoveries)
 	_, var_.T_invoice_money = Contract.ContractToContractFinance_R(T_invoice)
@@ -757,6 +762,8 @@ func (c *ContractController) VerifyContract_Edit() {
 	T_start_date := c.GetString("T_start_date")
 	T_end_date := c.GetString("T_end_date")
 	T_submit := c.GetString("T_submit")
+	T_stamping_date := c.GetString("T_stamping_date")
+	T_payment_method := c.GetString("T_payment_method")
 
 	o := orm.NewOrm()
 	o.Begin()
@@ -817,9 +824,15 @@ func (c *ContractController) VerifyContract_Edit() {
 	if len(T_submit) > 0 {
 		contract.T_submit = T_submit
 	}
+	if len(T_stamping_date) > 0 {
+		contract.T_stamping_date = T_stamping_date
+	}
+	if len(T_payment_method) > 0 {
+		contract.T_payment_method = T_payment_method
+	}
 
 	err = ContractDao.Update_Contract(contract, "T_number", "T_money", "T_discount", "T_date", "T_remark", "T_pdf", "T_State", "T_project",
-		"T_recoveries", "T_recoveries_money", "T_invoice", "T_invoice_money", "T_start_date", "T_end_date", "T_submit")
+		"T_recoveries", "T_recoveries_money", "T_invoice", "T_invoice_money", "T_start_date", "T_end_date", "T_submit", "T_stamping_date", "T_payment_method")
 	if err != nil {
 		o.Rollback()
 		c.Data["json"] = lib.JSONS{Code: 203, Msg: "添加失败"}
@@ -969,6 +982,152 @@ func (c *ContractController) VerifyContract_Del() {
 	return
 }
 
+// 导出验证合同
+func (c *ContractController) VerifyContract_Excel() {
+
+	// 分页参数 初始化
+	page, _ := c.GetInt("page")
+	if page < 1 {
+		page = 1
+	}
+	page_z, _ := c.GetInt("page_z")
+	if page_z < 1 {
+		page_z = conf.Page_size
+	}
+
+	// 查询
+	T_name := c.GetString("T_name")             // 客户名称
+	T_state, _ := c.GetInt("T_state")           // 签约状态
+	T_start_date := c.GetString("T_start_date") // 盖章开始时间
+	T_end_date := c.GetString("T_end_date")     // 盖章结束时间
+
+	data := ""
+	if len(T_start_date) > 0 || len(T_end_date) > 0 {
+		data = fmt.Sprintf("%s - %s", T_start_date, T_end_date)
+	}
+
+	userList, _ := NatsServer.Read_User_List_All()
+	Account.Read_User_All_Map(userList)
+
+	ContractDao := Contract.NewContract(orm.NewOrm())
+	R_List := ContractDao.Read_VerifyContract_Excel(T_state, T_name, T_start_date, T_end_date)
+	f := excelize.NewFile() //设置单元格值
+	// 这里设置表头ÒÒ
+	Style1, _ := f.NewStyle(
+		&excelize.Style{
+			Font:      &excelize.Font{Size: 20, Family: "宋体"},
+			Alignment: &excelize.Alignment{Horizontal: "center", Vertical: "center"},
+		})
+	Style2, _ := f.NewStyle(
+		&excelize.Style{
+			Font:      &excelize.Font{Size: 15, Family: "宋体"},
+			Alignment: &excelize.Alignment{Horizontal: "center", Vertical: "center", WrapText: true},
+			Border: []excelize.Border{
+				{Type: "left", Color: "000000", Style: 1},
+				{Type: "top", Color: "000000", Style: 1},
+				{Type: "bottom", Color: "000000", Style: 1},
+				{Type: "right", Color: "000000", Style: 1},
+			},
+			Fill: excelize.Fill{Type: "pattern", Color: []string{"D9D9D9"}, Pattern: 1},
+		})
+	Style3, _ := f.NewStyle(
+		&excelize.Style{
+			Font:      &excelize.Font{Size: 15, Family: "宋体"},
+			Alignment: &excelize.Alignment{Horizontal: "center", Vertical: "center", WrapText: true},
+			Border: []excelize.Border{
+				{Type: "left", Color: "000000", Style: 1},
+				{Type: "top", Color: "000000", Style: 1},
+				{Type: "bottom", Color: "000000", Style: 1},
+				{Type: "right", Color: "000000", Style: 1},
+			},
+		})
+
+	f.MergeCell("Sheet1", "A1", "T1")
+	f.SetCellValue("Sheet1", "A1", data+"合同明细")
+	f.SetCellStyle("Sheet1", "A1", "T1", Style1)
+
+	// 写入表头
+	headers := []string{"合同编号", "客户名称", "销售人员", "签订时间", "起始时间", "终止时间", "盖章时间", "合同名称", "验证明细",
+		"合同金额", "优惠金额", "付款方式/预付款", "回款时间", "以往回款", "回款金额", "未回款金额", "开票时间", "已开票金额", "未开票金额",
+		"状态"}
+	for col, header := range headers {
+		cell := fmt.Sprintf("%c%d", rune('A'+col), 2)
+		f.SetCellValue("Sheet1", cell, header)
+	}
+	// 这里设置表头
+	f.SetCellStyle("Sheet1", "A2", "T2", Style2)
+	f.SetRowHeight("Sheet1", 1, 50)
+	f.SetRowHeight("Sheet1", 2, 50)
+
+	colsWidth := []float64{15, 15, 12, 12, 12, 12, 12, 15, 50, 12, 12, 12, 12, 25, 12, 10, 25, 10, 10, 10}
+
+	// 冻结1-2行
+	f.SetPanes("Sheet1", &excelize.Panes{
+		Freeze:      true,
+		Split:       false,
+		XSplit:      0,
+		YSplit:      2,
+		TopLeftCell: "A1",
+		ActivePane:  "topRight",
+	})
+
+	for col, width := range colsWidth {
+		cell := fmt.Sprintf("%c", rune('A'+col))
+		f.SetColWidth("Sheet1", cell, cell, width)
+	}
+	line := 2
+	for _, v := range R_List {
+		valueList := []interface{}{
+			v.T_number, v.T_customer, v.T_submit_name, v.T_date, v.T_start_date, v.T_end_date, v.T_stamping_date, "冷链验证合同",
+			v.T_Product, v.T_money, v.T_discount, Contract.Get_Contract_payment_method(v.T_payment_method), v.T_recoveries_time, v.T_recoveries, v.T_discount - v.T_no_recoveries_money,
+			v.T_no_recoveries_money, v.T_invoice, v.T_discount - v.T_no_invoice_money, v.T_no_invoice_money, v.T_verify_state,
+		}
+
+		line++
+		for col, value := range valueList {
+			cell := fmt.Sprintf("%c", rune('A'+col))
+			if cell == "N" || cell == "Q" {
+				ll := value.([]Contract.ContractFinance_R)
+				var sv []string
+				for _, info := range ll {
+					sv = append(sv, fmt.Sprintf("%s  %s", info.T_date, FormatFloat(float64(info.T_money))))
+				}
+				f.SetCellValue("Sheet1", fmt.Sprintf("%s%d", cell, line), strings.Join(sv, "\n"))
+				continue
+			}
+			if cell == "I" {
+				ll := value.([]Contract.ContractProduct_R)
+				var sv []string
+				for _, info := range ll {
+					sv = append(sv, fmt.Sprintf("%s %s*%d=%s", info.T_product_name, FormatFloat(float64(info.T_price)), info.T_product_total, FormatFloat(float64(info.T_price)*float64(info.T_product_total))))
+				}
+				f.SetCellValue("Sheet1", fmt.Sprintf("%s%d", cell, line), strings.Join(sv, "\n"))
+				continue
+			}
+			f.SetCellValue("Sheet1", fmt.Sprintf("%s%d", cell, line), value)
+		}
+
+	}
+
+	f.SetCellStyle("Sheet1", "A3", fmt.Sprintf("T%d", line), Style3)
+
+	timeStr := time.Now().Format("20060102150405")
+
+	fileName := fmt.Sprintf("%s合同明细%v.xlsx", data, timeStr)
+	filePath := "ofile/" + fileName
+	// 保存文件
+	if err := f.SaveAs(filePath); err != nil {
+		logs.Error("保存文件失败:", err)
+	}
+	defer func() {
+		os.Remove(filePath)
+	}()
+	c.Ctx.Output.Header("Content-Type", "application/vnd.ms-excel;charset=utf8")
+	c.Ctx.Output.Header("Content-Disposition", "attachment; filename="+url.PathEscape(fileName))
+	c.Ctx.Output.Header("Content-Transfer-Encoding", "binary")
+	c.Ctx.Output.Download(filePath, fileName)
+}
+
 func Update_VerifyContract_State(T_customer_id, flag string) error {
 	o := orm.NewOrm()
 	ContractDao := Contract.NewContract(o)
@@ -1500,3 +1659,12 @@ func (c *ContractController) User_List() {
 	c.ServeJSON()
 	return
 }
+
+func FormatFloat(f float64) string {
+	// 先判断是否为整数
+	if f == math.Trunc(f) {
+		return strconv.FormatFloat(f, 'f', 0, 64)
+	}
+	// 否则保留2位小数
+	return strconv.FormatFloat(f, 'f', 2, 64)
+}

+ 202 - 96
controllers/Stock.go

@@ -11,6 +11,7 @@ import (
 	"ERP_storage/models/Contract"
 	"ERP_storage/models/Property"
 	"ERP_storage/models/Stock"
+	"errors"
 	"fmt"
 	"github.com/beego/beego/v2/adapter/orm"
 	beego "github.com/beego/beego/v2/server/web"
@@ -797,6 +798,9 @@ func (c *StockController) StockIn_Add() {
 }
 
 func StockIn_Edit_StockMonth(T_date string, T_depot_id int, allProductList []int) error {
+	if len(T_date) == 0 {
+		return errors.New("日期不能为空")
+	}
 	date, _ := lib.DateStrToTime(T_date)
 
 	//if date.Year() == time.Now().Year() && date.Month() == time.Now().Month() {
@@ -891,13 +895,14 @@ func (c *StockController) StockIn_Edit() {
 
 	// 查询入库信息
 	stockIn, err := StockInDao.Read_StockIn_ByT_number(T_number)
-	T_old_date := stockIn.T_date
 	if err != nil {
 		o.Rollback()
 		c.Data["json"] = lib.JSONS{Code: 202, Msg: "查询失败!"}
 		c.ServeJSON()
 		return
 	}
+	T_old_date := stockIn.T_date
+
 	// 查询入库产品信息
 	productOldList := StockInProductDao.Read_StockInProduct_List_ByT_number(stockIn.T_number)
 
@@ -1541,9 +1546,6 @@ func (c *StockController) StockIn_Excel_Batch() {
 		}
 
 		productList := StockInProductDao.Read_StockInProduct_List_ByT_number(stockIn.T_number)
-		productList = append(productList, productList...)
-		productList = append(productList, productList...)
-		productList = append(productList, productList...)
 		var pList []Stock.StockInProduct_R
 		for _, v := range productList {
 			pList = append(pList, Stock.StockInProductToStockInProduct_R(v))
@@ -2568,7 +2570,7 @@ func (c *StockController) StockOut_Edit() {
 	return
 }
 
-// 删除
+// 删除
 func (c *StockController) StockOut_Del() {
 
 	T_number := c.GetString("T_number") // 入库单号
@@ -2580,7 +2582,7 @@ func (c *StockController) StockOut_Del() {
 	StockDao := Stock.NewStock(o)
 	DeviceDao := Stock.NewDevice(o)
 
-	// 查询库信息
+	// 查询库信息
 	stockOut, err := StockOutDao.Read_StockOut_ByT_number(T_number)
 	if err != nil {
 		o.Rollback()
@@ -2885,10 +2887,6 @@ func (c *StockController) StockOut_Excel_Batch() {
 		}
 
 		productList := StockOutProductDao.Read_StockOutProduct_List(stockOut.T_number)
-		productList = append(productList, productList...)
-		productList = append(productList, productList...)
-		productList = append(productList, productList...)
-		productList = append(productList, productList...)
 
 		var pList []Stock.StockOutProduct_R
 		for _, v := range productList {
@@ -3171,6 +3169,44 @@ func generateMonthList(startMonth time.Time, t_type string) []string {
 	return months
 }
 
+// 我的申请列表
+func (c *StockController) StockOut_Apply_List() {
+	// 分页参数 初始化
+	page, _ := c.GetInt("page")
+	if page < 1 {
+		page = 1
+	}
+	page_z, _ := c.GetInt("page_z")
+	if page_z < 1 {
+		page_z = conf.Page_size
+	}
+
+	// 查询
+	T_depot_id, _ := c.GetInt("T_depot_id")
+	T_state, _ := c.GetInt("T_state")
+	T_contract_number := c.GetString("T_contract_number")
+	T_start_date := c.GetString("T_start_date")
+	T_end_date := c.GetString("T_end_date")
+	T_name := c.GetString("T_name")
+
+	userList, _ := NatsServer.Read_User_List_All()
+	Account.Read_User_All_Map(userList)
+	Basic.Read_Depot_All_Map()
+
+	StockOutDao := Stock.NewStockOut(orm.NewOrm())
+	R_List, R_cnt := StockOutDao.Read_StockOut_Audit_List(c.User.T_uuid, T_name, Stock.StockOutTypeApply, T_state, T_depot_id, T_contract_number, T_start_date, T_end_date, page, page_z)
+
+	var r_jsons lib.R_JSONS
+	r_jsons.Num = R_cnt
+	r_jsons.Data = R_List
+	r_jsons.Page = page
+	r_jsons.Page_size = int(math.Ceil(float64(R_cnt) / float64(page_z)))
+
+	c.Data["json"] = lib.JSONS{Code: 200, Msg: "ok!", Data: r_jsons}
+	c.ServeJSON()
+	return
+}
+
 // 出库申请
 func (c *StockController) StockOut_Apply() {
 
@@ -3192,7 +3228,6 @@ func (c *StockController) StockOut_Apply() {
 	T_depot_id, _ := c.GetInt("T_depot_id")
 	T_product := c.GetString("T_product")
 	T_remark := c.GetString("T_remark")
-	//T_date := c.GetString("T_date")
 	T_project := c.GetString("T_project")
 	T_company_name := c.GetString("T_company_name")
 	T_payment_method := c.GetString("T_payment_method")
@@ -3208,18 +3243,18 @@ func (c *StockController) StockOut_Apply() {
 	}
 
 	var_ := Stock.StockOut{
-		T_number:          T_number,
-		T_contract_number: T_contract_number,
-		T_depot_id:        T_depot_id,
-		T_type:            1,
-		//T_date:            T_date,
-		T_receive:        c.User.T_uuid,
-		T_remark:         T_remark,
-		T_project:        T_project,
-		T_submit:         c.User.T_uuid,
-		T_state:          Stock.StockOutWaitAudit,
-		T_company_name:   T_company_name,
-		T_payment_method: T_payment_method,
+		T_number:           T_number,
+		T_contract_number:  T_contract_number,
+		T_depot_id:         T_depot_id,
+		T_type:             1,
+		T_application_date: time.Now().Format("2006-01-02"),
+		T_receive:          c.User.T_uuid,
+		T_remark:           T_remark,
+		T_project:          T_project,
+		T_submit:           c.User.T_uuid,
+		T_state:            Stock.StockOutWaitAudit,
+		T_company_name:     T_company_name,
+		T_payment_method:   T_payment_method,
 	}
 
 	o.Begin()
@@ -3251,6 +3286,12 @@ func (c *StockController) StockOut_Apply() {
 		product, _ := Basic.Read_Product_ById(product_id)
 
 		num, _ := strconv.Atoi(strings.Split(v, "-")[1])
+		if num == 0 {
+			o.Rollback()
+			c.Data["json"] = lib.JSONS{Code: 202, Msg: fmt.Sprintf("%s出库数量不能为0!", product.T_name)}
+			c.ServeJSON()
+			return
+		}
 		// 2、添加出库产品清单
 		stockOutProduct := Stock.StockOutProduct{
 			T_number:     T_number,
@@ -3291,17 +3332,11 @@ func (c *StockController) StockOut_Apply_Edit() {
 
 	T_number := c.GetString("T_number") // 出库单号
 	//T_depot_id, _ := c.GetInt("T_depot_id")
-	T_date := c.GetString("T_date")
-	T_receive := c.GetString("T_receive")
 	T_project := c.GetString("T_project")
 	T_product := c.GetString("T_product")
 	T_remark := c.GetString("T_remark")
-	_, is := lib.DateStrToTime(T_date)
-	if !is {
-		c.Data["json"] = lib.JSONS{Code: 202, Msg: "日期格式错误!"}
-		c.ServeJSON()
-		return
-	}
+	T_company_name := c.GetString("T_company_name")
+	T_payment_method := c.GetString("T_payment_method")
 
 	NatsServer.AddUserLogs(c.User.T_uuid, "仓库管理", "修改出库", T_product)
 	o := orm.NewOrm()
@@ -3318,7 +3353,6 @@ func (c *StockController) StockOut_Apply_Edit() {
 		c.ServeJSON()
 		return
 	}
-	T_old_date := StockOut.T_date
 	// 查询入库产品信息
 	productOldList := StockOutProductDao.Read_StockOutProduct_List(StockOut.T_number)
 
@@ -3338,13 +3372,31 @@ func (c *StockController) StockOut_Apply_Edit() {
 		if _, ok := allProductListMap[product.T_product_id]; !ok {
 			allProductListMap[product.T_product_id] = struct{}{}
 		}
+		if StockOut.T_state != Stock.StockOutAuditManagerUnPass && StockOut.T_state != Stock.StockOutAuditFinanceUnPass {
+			// 减少占用库存
+			product2, _ := Basic.Read_Product_ById(product.T_product_id)
+			_, err = StockDao.AddOrUpdate_Occupy_Stock(StockOut.T_depot_id, product2.Id, product2.T_class, product2.T_name, product2.T_model, product.T_num, 1)
+			if err != nil {
+				o.Rollback()
+				c.Data["json"] = lib.JSONS{Code: 203, Msg: "更新占用库存失败"}
+				c.ServeJSON()
+				return
+			}
+		}
 
 	}
 	productNewList := lib.SplitString(T_product, "|")
 	productNewMap := map[int]dto.StockProduct{}
 	for _, v := range productNewList {
 		product_id, _ := strconv.Atoi(strings.Split(v, "-")[0])
+		product, _ := Basic.Read_Product_ById(product_id)
 		num, _ := strconv.Atoi(strings.Split(v, "-")[1])
+		if num == 0 {
+			o.Rollback()
+			c.Data["json"] = lib.JSONS{Code: 202, Msg: fmt.Sprintf("%s出库数量不能为0!", product.T_name)}
+			c.ServeJSON()
+			return
+		}
 		T_relation_sn := strings.Split(v, "-")[2]
 		stockProduct := dto.StockProduct{
 			T_product_id:  product_id,
@@ -3356,6 +3408,15 @@ func (c *StockController) StockOut_Apply_Edit() {
 		if _, ok := allProductListMap[product_id]; !ok {
 			allProductListMap[product_id] = struct{}{}
 		}
+
+		// 增加占用库存
+		_, err = StockDao.AddOrUpdate_Occupy_Stock(StockOut.T_depot_id, product.Id, product.T_class, product.T_name, product.T_model, num, 2)
+		if err != nil {
+			o.Rollback()
+			c.Data["json"] = lib.JSONS{Code: 203, Msg: "更新占用库存失败"}
+			c.ServeJSON()
+			return
+		}
 	}
 
 	// 判断产品列表信息是否相同
@@ -3365,7 +3426,6 @@ func (c *StockController) StockOut_Apply_Edit() {
 		if len(needDelete) > 0 {
 			// 删除入库产品列表
 			for _, v := range needDelete {
-				product, _ := Basic.Read_Product_ById(v.T_product_id)
 				// 删除入库产品列表
 				err = StockOutProductDao.Delete_StockOutProduct(StockOut.T_number, StockOut.T_depot_id, v.T_product_id)
 				if err != nil {
@@ -3374,23 +3434,12 @@ func (c *StockController) StockOut_Apply_Edit() {
 					c.ServeJSON()
 					return
 				}
-
-				// 增加库存
-				_, err = StockDao.AddOrUpdate_Occupy_Stock(StockOut.T_depot_id, product.Id, product.T_class, product.T_name, product.T_model, v.T_num, 1)
-				if err != nil {
-					o.Rollback()
-					c.Data["json"] = lib.JSONS{Code: 203, Msg: "更新库存信息失败"}
-					c.ServeJSON()
-					return
-				}
 			}
 		}
 
 		if len(needAdd) > 0 {
 			// 新增入库产品列表
 			for _, v := range needAdd {
-				product, _ := Basic.Read_Product_ById(v.T_product_id)
-
 				StockOutProduct := Stock.StockOutProduct{
 					T_number:     T_number,
 					T_product_id: v.T_product_id,
@@ -3405,21 +3454,11 @@ func (c *StockController) StockOut_Apply_Edit() {
 					c.ServeJSON()
 					return
 				}
-
-				// 减少库存
-				_, err = StockDao.AddOrUpdate_Occupy_Stock(StockOut.T_depot_id, product.Id, product.T_class, product.T_name, product.T_model, v.T_num, 2)
-				if err != nil {
-					o.Rollback()
-					c.Data["json"] = lib.JSONS{Code: 203, Msg: "增加库存失败"}
-					c.ServeJSON()
-					return
-				}
 			}
 		}
 
 		if len(needEdit) > 0 {
 			for _, v := range needEdit {
-				product, _ := Basic.Read_Product_ById(v.T_product_id)
 				StockOutProduct := Stock.StockOutProduct{
 					T_number:     StockOut.T_number,
 					T_product_id: v.T_product_id,
@@ -3427,27 +3466,6 @@ func (c *StockController) StockOut_Apply_Edit() {
 					T_num:        productNewMap[v.T_product_id].T_num, // 入库数量
 					T_state:      1,
 				}
-				// 出库数量比之前多,减少库存
-				var T_type int
-				var T_num int
-
-				if productNewMap[v.T_product_id].T_num > productOldMap[v.T_product_id].T_num {
-					// 减少库存
-					T_type = 2
-					T_num = productNewMap[v.T_product_id].T_num - productOldMap[v.T_product_id].T_num
-				} else {
-					// 增加库存
-					T_type = 1
-					T_num = productOldMap[v.T_product_id].T_num - productNewMap[v.T_product_id].T_num
-				}
-
-				_, err = StockDao.AddOrUpdate_Occupy_Stock(StockOut.T_depot_id, product.Id, product.T_class, product.T_name, product.T_model, T_num, T_type)
-				if err != nil {
-					o.Rollback()
-					c.Data["json"] = lib.JSONS{Code: 203, Msg: err.Error()}
-					c.ServeJSON()
-					return
-				}
 				// 更新产品表
 				err = StockOutProductDao.Update_StockOutProduct(StockOutProduct)
 				if err != nil {
@@ -3463,18 +3481,18 @@ func (c *StockController) StockOut_Apply_Edit() {
 	if len(T_remark) > 0 {
 		StockOut.T_remark = T_remark
 	}
-	if len(T_receive) > 0 {
-		StockOut.T_receive = T_receive
-	}
 	if len(T_project) > 0 {
 		StockOut.T_project = T_project
 	}
-
-	if len(T_date) > 0 {
-		StockOut.T_date = T_date
+	if len(T_company_name) > 0 {
+		StockOut.T_company_name = T_company_name
+	}
+	if len(T_payment_method) > 0 {
+		StockOut.T_payment_method = T_payment_method
 	}
+	StockOut.T_state = Stock.StockOutWaitAudit
 
-	err = StockOutDao.Update_StockOut(StockOut, "T_remark", "T_receive", "T_project", "T_date")
+	err = StockOutDao.Update_StockOut(StockOut, "T_remark", "T_receive", "T_project", "T_company_name", "T_payment_method", "T_state")
 	if err != nil {
 		o.Rollback()
 		c.Data["json"] = lib.JSONS{Code: 203, Msg: "修改入库失败"}
@@ -3483,12 +3501,79 @@ func (c *StockController) StockOut_Apply_Edit() {
 	}
 
 	o.Commit()
-	if len(T_date) > 0 && T_old_date != T_date {
-		// 修改出库产品日期
-		StockOutProductDao.Update_StockOutProduct_T_date(StockOut.T_number, T_date)
+	NatsServer.AddUserLogs(c.User.T_uuid, "出库申请", "修改", StockOut)
+
+	c.Data["json"] = lib.JSONS{Code: 200, Msg: "ok!", Data: T_number}
+	c.ServeJSON()
+	return
+}
+
+// 删除出库申请
+func (c *StockController) StockOut_Apply_Del() {
+
+	T_number := c.GetString("T_number") // 入库单号
+
+	o := orm.NewOrm()
+	o.Begin()
+	StockOutDao := Stock.NewStockOut(o)
+	StockOutProductDao := Stock.NewStockOutProduct(o)
+	StockDao := Stock.NewStock(o)
+
+	// 查询出库信息
+	stockOut, err := StockOutDao.Read_StockOut_ByT_number(T_number)
+	if err != nil {
+		o.Rollback()
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: "查询失败!"}
+		c.ServeJSON()
+		return
 	}
+	// 查询出库产品信息
+	productOldList := StockOutProductDao.Read_StockOutProduct_List(stockOut.T_number)
 
-	NatsServer.AddUserLogs(c.User.T_uuid, "出库申请", "修改", StockOut)
+	var oldProductList []dto.StockProduct
+	allProductList := []int{}
+	for _, product := range productOldList {
+		oldProductList = append(oldProductList, dto.StockProduct{
+			T_product_id:  product.T_product_id,
+			T_num:         product.T_num,
+			T_relation_sn: lib.SplitString(product.T_relation_sn, ","),
+		})
+		allProductList = append(allProductList, product.T_product_id)
+	}
+
+	// 删除入库产品列表
+	for _, v := range oldProductList {
+		product, _ := Basic.Read_Product_ById(v.T_product_id)
+		// 删除入库产品列表
+		err = StockOutProductDao.Delete_StockOutProduct(stockOut.T_number, stockOut.T_depot_id, v.T_product_id)
+		if err != nil {
+			o.Rollback()
+			c.Data["json"] = lib.JSONS{Code: 202, Msg: "删除产品出库信息失败!"}
+			c.ServeJSON()
+			return
+		}
+
+		// 减少占用库存
+		_, err = StockDao.AddOrUpdate_Occupy_Stock(stockOut.T_depot_id, product.Id, product.T_class, product.T_name, product.T_model, v.T_num, 1)
+		if err != nil {
+			o.Rollback()
+			c.Data["json"] = lib.JSONS{Code: 203, Msg: "更新占用库存失败"}
+			c.ServeJSON()
+			return
+		}
+	}
+
+	err = StockOutDao.Delete_StockOut(stockOut)
+	if err != nil {
+		o.Rollback()
+		c.Data["json"] = lib.JSONS{Code: 203, Msg: "删除出库失败"}
+		c.ServeJSON()
+		return
+	}
+
+	o.Commit()
+
+	NatsServer.AddUserLogs(c.User.T_uuid, "出库申请", "删除", stockOut)
 
 	c.Data["json"] = lib.JSONS{Code: 200, Msg: "ok!", Data: T_number}
 	c.ServeJSON()
@@ -3509,6 +3594,7 @@ func (c *StockController) StockOut_Finance_List() {
 
 	// 查询
 	T_depot_id, _ := c.GetInt("T_depot_id")
+	T_state, _ := c.GetInt("T_state")
 	T_contract_number := c.GetString("T_contract_number")
 	T_start_date := c.GetString("T_start_date")
 	T_end_date := c.GetString("T_end_date")
@@ -3519,7 +3605,7 @@ func (c *StockController) StockOut_Finance_List() {
 	Basic.Read_Depot_All_Map()
 
 	StockOutDao := Stock.NewStockOut(orm.NewOrm())
-	R_List, R_cnt := StockOutDao.Read_StockOut_Audit_List(T_name, Stock.StockOutTypeFinance, T_depot_id, T_contract_number, T_start_date, T_end_date, page, page_z)
+	R_List, R_cnt := StockOutDao.Read_StockOut_Audit_List("", T_name, Stock.StockOutTypeFinance, T_state, T_depot_id, T_contract_number, T_start_date, T_end_date, page, page_z)
 
 	var r_jsons lib.R_JSONS
 	r_jsons.Num = R_cnt
@@ -3546,6 +3632,7 @@ func (c *StockController) StockOut_Manager_List() {
 
 	// 查询
 	T_depot_id, _ := c.GetInt("T_depot_id")
+	T_state, _ := c.GetInt("T_state")
 	T_contract_number := c.GetString("T_contract_number")
 	T_start_date := c.GetString("T_start_date")
 	T_end_date := c.GetString("T_end_date")
@@ -3556,7 +3643,7 @@ func (c *StockController) StockOut_Manager_List() {
 	Basic.Read_Depot_All_Map()
 
 	StockOutDao := Stock.NewStockOut(orm.NewOrm())
-	R_List, R_cnt := StockOutDao.Read_StockOut_Audit_List(T_name, Stock.StockOutTypeManager, T_depot_id, T_contract_number, T_start_date, T_end_date, page, page_z)
+	R_List, R_cnt := StockOutDao.Read_StockOut_Audit_List("", T_name, Stock.StockOutTypeManager, T_state, T_depot_id, T_contract_number, T_start_date, T_end_date, page, page_z)
 
 	var r_jsons lib.R_JSONS
 	r_jsons.Num = R_cnt
@@ -3583,6 +3670,7 @@ func (c *StockController) StockOut_Warehouse_List() {
 
 	// 查询
 	T_depot_id, _ := c.GetInt("T_depot_id")
+	T_state, _ := c.GetInt("T_state")
 	T_contract_number := c.GetString("T_contract_number")
 	T_start_date := c.GetString("T_start_date")
 	T_end_date := c.GetString("T_end_date")
@@ -3593,7 +3681,7 @@ func (c *StockController) StockOut_Warehouse_List() {
 	Basic.Read_Depot_All_Map()
 
 	StockOutDao := Stock.NewStockOut(orm.NewOrm())
-	R_List, R_cnt := StockOutDao.Read_StockOut_Audit_List(T_name, Stock.StockOutTypeWarehouse, T_depot_id, T_contract_number, T_start_date, T_end_date, page, page_z)
+	R_List, R_cnt := StockOutDao.Read_StockOut_Audit_List(c.User.T_uuid, T_name, Stock.StockOutTypeWarehouse, T_state, T_depot_id, T_contract_number, T_start_date, T_end_date, page, page_z)
 
 	var r_jsons lib.R_JSONS
 	r_jsons.Num = R_cnt
@@ -3606,7 +3694,7 @@ func (c *StockController) StockOut_Warehouse_List() {
 	return
 }
 
-// 财务审核
+// 财务/总经理审核
 func (c *StockController) StockOut_Audit() {
 
 	T_number := c.GetString("T_number") // 出库单号
@@ -3701,6 +3789,13 @@ func (c *StockController) StockOut_Warehouse() {
 		product, _ := Basic.Read_Product_ById(product_id)
 
 		num, _ := strconv.Atoi(strings.Split(v, "-")[1])
+		if num == 0 {
+			o.Rollback()
+			c.Data["json"] = lib.JSONS{Code: 202, Msg: fmt.Sprintf("%s出库数量不能为0!", product.T_name)}
+			c.ServeJSON()
+			return
+		}
+
 		T_relation_sn := strings.Split(v, "-")[2]
 		if T_relation_sn == "" && product.T_relation_sn == 1 {
 			o.Rollback()
@@ -3708,9 +3803,18 @@ func (c *StockController) StockOut_Warehouse() {
 			c.ServeJSON()
 			return
 		}
+
+		snList := strings.Split(T_relation_sn, ",")
+		if num != len(snList) {
+			o.Rollback()
+			c.Data["json"] = lib.JSONS{Code: 202, Msg: fmt.Sprintf("%sSN数量与出库数量不一致,请核查!", product.T_name)}
+			c.ServeJSON()
+			return
+		}
+
 		// 2、更新设备状态为已出库
-		if len(T_relation_sn) > 0 {
-			snList := strings.Split(T_relation_sn, ",")
+		if len(snList) > 0 {
+
 			for _, sn := range snList {
 				mqtt := Stock.Read_MqttUser(sn)
 				device := Stock.Device{
@@ -3742,7 +3846,7 @@ func (c *StockController) StockOut_Warehouse() {
 			T_num:         num,    // 出库数量
 			T_date:        T_date, // 出库数量
 			T_relation_sn: T_relation_sn,
-			T_state:       2,
+			T_state:       1,
 		}
 
 		// 更新产品表
@@ -3754,7 +3858,7 @@ func (c *StockController) StockOut_Warehouse() {
 			return
 		}
 
-		// 5、更新产品库存列表
+		// 5、更新产品库存列表、占用库存
 		_, err = StockDao.Stock_Warehouse(StockOut.T_depot_id, product.Id, product.T_class, product.T_name, product.T_model, num)
 		if err != nil {
 			o.Rollback()
@@ -3764,8 +3868,10 @@ func (c *StockController) StockOut_Warehouse() {
 		}
 	}
 
+	StockOut.T_warehouse = c.User.T_uuid
 	StockOut.T_state = Stock.StockOutAlreadyOut
-	err = StockOutDao.Update_StockOut(StockOut, "T_state")
+	StockOut.T_date = T_date
+	err = StockOutDao.Update_StockOut(StockOut, "T_state", "T_date", "T_warehouse")
 	if err != nil {
 		o.Rollback()
 		c.Data["json"] = lib.JSONS{Code: 203, Msg: "出库审批失败"}
@@ -3776,7 +3882,7 @@ func (c *StockController) StockOut_Warehouse() {
 	o.Commit()
 	StockOut_Edit_StockMonth(StockOut.T_date, StockOut.T_depot_id, allProductList)
 
-	NatsServer.AddUserLogs(c.User.T_uuid, "出库申请", "修改", StockOut)
+	NatsServer.AddUserLogs(c.User.T_uuid, "出库申请", "出库", StockOut)
 
 	c.Data["json"] = lib.JSONS{Code: 200, Msg: "ok!", Data: T_number}
 	c.ServeJSON()

+ 370 - 0
controllers/Validation.go

@@ -942,3 +942,373 @@ func (c *ValidationController) Operation_Excel() {
 	c.Ctx.Output.Header("Content-Transfer-Encoding", "binary")
 	c.Ctx.Output.Download(filePath, fileName)
 }
+
+// 设备转移请求结构体
+type TransferValidationToolRequest struct {
+	T_sn          []string `json:"T_sn"`
+	CurrentUser   string `json:"CurrentUser"`
+	TargetUser    string `json:"TargetUser"`
+	TransferRemark string `json:"TransferRemark"` // 转移备注
+}
+
+// 设备转移结果结构体
+type TransferResult struct {
+	T_sn         string `json:"T_sn"`
+	Success      bool   `json:"success"`
+	Message      string `json:"message"`
+	Status       string `json:"status"`
+}
+
+// 检查所有转移结果是否都失败
+func allFailed(results []TransferResult) bool {
+	for _, r := range results {
+		if r.Success {
+			return false
+		}
+	}
+	return true
+}
+
+// 检查是否有任何转移结果成功
+func anySuccess(results []TransferResult) bool {
+	for _, r := range results {
+		if r.Success {
+			return true
+		}
+	}
+	return false
+}
+
+// 设备转移请求
+func (c *ValidationController) TransferValidationTool() {
+	var request TransferValidationToolRequest
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &request)
+	if err != nil {
+		c.Data["json"] = lib.JSONS{Code: 1201, Msg: "json 序列化失败!", Data: nil}
+		c.ServeJSON()
+		return
+	}
+
+	// 验证参数
+	if len(request.T_sn) <= 0 {
+		c.Data["json"] = lib.JSONS{Code: 1201, Msg: "T_sn不能为空!", Data: nil}
+		c.ServeJSON()
+		return
+	}
+
+	if len(request.TargetUser) <= 0 {
+		c.Data["json"] = lib.JSONS{Code: 1201, Msg: "目标借出用户不能为空!", Data: nil}
+		c.ServeJSON()
+		return
+	}
+
+	if request.CurrentUser == request.TargetUser {
+		c.Data["json"] = lib.JSONS{Code: 1201, Msg: "目标用户不能与当前用户相同!", Data: nil}
+		c.ServeJSON()
+		return
+	}
+
+	o := orm.NewOrm()
+	o.Begin()
+	vali := validationtool.NewValidationTool(o)
+	valiRecord := validationtool.NewValidationToolRecord(o)
+	BatchNumber := time.Now().Format("2006-01-02 15:04:05")
+
+	var results []TransferResult
+
+	// 遍历所有SN进行检查和更新
+	for _, sn := range request.T_sn {
+		result := TransferResult{T_sn: sn, Success: false, Status: "failed"}
+
+		validation, err2 := vali.ReadValidationBytSn(sn)
+		if err2 != nil {
+			if err2.Error() == orm.ErrNoRows.Error() {
+				result.Message = fmt.Sprintf("当前sn:%v 未入库", sn)
+			} else {
+				result.Message = fmt.Sprintf("查询sn:%v 失败", sn)
+			}
+			results = append(results, result)
+			continue
+		}
+
+		// 检查设备当前状态是否为已出库
+		if validation.T_state != validationtool.ValidationToolStateStockOut {
+			result.Message = fmt.Sprintf("当前sn:%v 未出库,无法进行转移", sn)
+			results = append(results, result)
+			continue
+		}
+
+		// 检查当前借出用户是否匹配
+		if validation.LendUser != request.CurrentUser {
+			result.Message = fmt.Sprintf("当前sn:%v 的借出用户与请求不符", sn)
+			results = append(results, result)
+			continue
+		}
+
+		// 检查设备是否已经在转移中
+		if validation.T_state == validationtool.ValidationToolStateTransferring {
+			result.Message = fmt.Sprintf("当前sn:%v 正在转移中,请等待对方确认或取消当前转移", sn)
+			results = append(results, result)
+			continue
+		}
+
+		// 修改设备状态和备注,记录转移信息
+		originalRemark := validation.T_remark
+		transferInfo := fmt.Sprintf("[转移请求] 从 %s 转移至 %s", request.CurrentUser, request.TargetUser)
+		if len(request.TransferRemark) > 0 {
+			transferInfo += fmt.Sprintf(",备注:%s", request.TransferRemark)
+		}
+		if len(originalRemark) > 0 {
+			validation.T_remark = originalRemark + "\n" + transferInfo
+		} else {
+			validation.T_remark = transferInfo
+		}
+
+		// 设置转移中的状态
+		validation.T_state = validationtool.ValidationToolStateTransferring
+		
+		cols := []string{"T_remark", "T_state"}
+		err = vali.UpdateValidationTool(validation, cols...)
+		if err != nil {
+			result.Message = fmt.Sprintf("更新设备sn:%v 状态失败", sn)
+			results = append(results, result)
+			continue
+		}
+		
+		_, err = valiRecord.ADD(validation, BatchNumber)
+		if err != nil {
+			result.Message = fmt.Sprintf("保存sn:%v 历史记录失败", sn)
+			results = append(results, result)
+			continue
+		}
+		result.Success = true
+		result.Status = "pending"
+		result.Message = fmt.Sprintf("sn:%v 转移请求已发送", sn)
+		results = append(results, result)
+
+	}
+
+	if anySuccess(results) {
+		// 创建转移记录到新表
+		snListJSON, err := json.Marshal(request.T_sn)
+		if err != nil {
+			o.Rollback()
+			c.Data["json"] = lib.JSONS{Code: 1201, Msg: "T_sn列表序列化失败!", Data: nil}
+			c.ServeJSON()
+			return
+		}
+		transferRecord := validationtool.ValidationToolTransfer{
+			T_sn:     string(snListJSON),
+			FromUser: request.CurrentUser,
+			ToUser:   request.TargetUser,
+			Status:   "pending",
+			Remark:   request.TransferRemark,
+		}
+
+		transferTool := validationtool.NewValidationToolTransfer(o)
+		_, err = transferTool.Add(&transferRecord)
+		if err != nil {
+			o.Rollback()
+			c.Data["json"] = lib.JSONS{Code: 1201, Msg: "创建转移记录失败!", Data: nil}
+			c.ServeJSON()
+			return
+		}
+
+		err = o.Commit() // 提交事务
+		if err != nil {
+			o.Rollback() // 回滚事务
+			c.Data["json"] = lib.JSONS{Code: 1201, Msg: "提交事务失败!", Data: nil}
+			c.ServeJSON()
+			return
+		}
+
+		NatsServer.AddUserLogs(c.User.T_uuid, "验证工具", "转移请求", request.T_sn)
+		c.Data["json"] = lib.JSONS{Code: 200, Msg: "批量转移请求处理完成", Data: results}
+		c.ServeJSON()
+		return
+	}
+
+	o.Rollback() // 如果没有一个成功,则回滚所有操作
+	c.Data["json"] = lib.JSONS{Code: 1201, Msg: "所有SN转移失败", Data: results}
+	c.ServeJSON()
+	return
+}
+
+// 确认接收设备请求结构体
+type ConfirmTransferValidationTool struct {
+	T_sn         []string `json:"T_sn"`
+	ConfirmUser  string `json:"ConfirmUser"`  // 确认接收的用户
+	// 添加设备信息字段用于比对
+	DeviceInfo   map[string]interface{} `json:"DeviceInfo"` // 设备信息用于比对
+}
+
+// 设备一致性比对结果结构体
+type DeviceComparisonResult struct {
+	Field       string `json:"field"`
+	Expected    string `json:"expected"`
+	Actual      string `json:"actual"`
+}
+
+// 确认接收设备
+func (c *ValidationController) ConfirmTransferValidationTool() {
+	var request ConfirmTransferValidationTool
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &request)
+	if err != nil {
+		c.Data["json"] = lib.JSONS{Code: 1201, Msg: "json 序列化失败!", Data: nil}
+		c.ServeJSON()
+		return
+	}
+
+	// 验证参数
+	if len(request.T_sn) <= 0 {
+		c.Data["json"] = lib.JSONS{Code: 1201, Msg: "T_sn不能为空!", Data: nil}
+		c.ServeJSON()
+		return
+	}
+
+	if len(request.ConfirmUser) <= 0 {
+		c.Data["json"] = lib.JSONS{Code: 1201, Msg: "确认用户不能为空!", Data: nil}
+		c.ServeJSON()
+		return
+	}
+
+	o := orm.NewOrm()
+	o.Begin()
+	vali := validationtool.NewValidationTool(o)
+	valiRecord := validationtool.NewValidationToolRecord(o)
+	BatchNumber := time.Now().Format("2006-01-02 15:04:05")
+
+	// 查询设备信息
+	validation, err2 := vali.ReadValidationBytSn(request.T_sn)
+	if err2 != nil {
+		o.Rollback()
+		if err2.Error() == orm.ErrNoRows.Error() {
+			c.Data["json"] = lib.JSONS{Code: 1201, Msg: fmt.Sprintf("当前sn:%v 未入库", request.T_sn), Data: nil}
+			c.ServeJSON()
+			return
+		} else {
+			c.Data["json"] = lib.JSONS{Code: 1201, Msg: fmt.Sprintf("查询sn:%v 失败", request.T_sn), Data: nil}
+			c.ServeJSON()
+			return
+		}
+	}
+
+	// 检查设备是否在转移中
+	if validation.T_state != validationtool.ValidationToolStateTransferring {
+		o.Rollback()
+		c.Data["json"] = lib.JSONS{Code: 1201, Msg: fmt.Sprintf("当前sn:%v 不在转移状态", request.T_sn), Data: nil}
+		c.ServeJSON()
+		return
+	}
+
+	// 查询待处理的转移记录
+	transfer := validationtool.NewValidationToolTransfer(o)
+	transferRecord, err := transfer.GetPendingTransferBySn(request.T_sn)
+	if err != nil {
+		o.Rollback()
+		c.Data["json"] = lib.JSONS{Code: 1201, Msg: fmt.Sprintf("当前sn:%v 没有待确认的转移请求", request.T_sn), Data: nil}
+		c.ServeJSON()
+		return
+	}
+
+	// 检查确认用户是否为目标用户
+	if transferRecord.ToUser != request.ConfirmUser {
+		o.Rollback()
+		c.Data["json"] = lib.JSONS{Code: 1201, Msg: fmt.Sprintf("您不是当前sn:%v 的目标接收用户", request.T_sn), Data: nil}
+		c.ServeJSON()
+		return
+	}
+
+	// 设备一致性比对
+	var comparisonResults []DeviceComparisonResult
+
+	// 如果提供了设备信息进行比对
+	if request.DeviceInfo != nil {
+		// 比对T_iccid
+		if iccid, ok := request.DeviceInfo["T_iccid"].(string); ok && iccid != validation.T_iccid {
+			comparisonResults = append(comparisonResults, DeviceComparisonResult{
+				Field:    "T_iccid",
+				Expected: validation.T_iccid,
+				Actual:   iccid,
+			})
+		}
+
+		// 比对T_imei
+		if imei, ok := request.DeviceInfo["T_imei"].(string); ok && imei != validation.T_imei {
+			comparisonResults = append(comparisonResults, DeviceComparisonResult{
+				Field:    "T_imei",
+				Expected: validation.T_imei,
+				Actual:   imei,
+			})
+		}
+
+		// 比对T_class
+		if class, ok := request.DeviceInfo["T_class"]; ok {
+			classStr := fmt.Sprintf("%v", class)
+			if classStr != fmt.Sprintf("%v", validation.T_class) {
+				comparisonResults = append(comparisonResults, DeviceComparisonResult{
+					Field:    "T_class",
+					Expected: fmt.Sprintf("%v", validation.T_class),
+					Actual:   classStr,
+				})
+			}
+		}
+
+		// 如果比对结果不一致,返回不一致信息
+		if len(comparisonResults) > 0 {
+			o.Rollback()
+			c.Data["json"] = lib.JSONS{Code: 1201, Msg: "设备信息比对不一致", Data: comparisonResults}
+			c.ServeJSON()
+			return
+		}
+	}
+
+	// 更新借出用户
+	validation.LendUser = request.ConfirmUser
+	// 将状态改回已出库
+	validation.T_state = validationtool.ValidationToolStateStockOut
+	// 在备注中记录转移确认信息
+	confirmInfo := fmt.Sprintf("[转移完成] %s 确认接收", request.ConfirmUser)
+	validation.T_remark += "\n" + confirmInfo
+
+	cols := []string{"LendUser", "T_remark", "T_state"}
+	err = vali.UpdateValidationTool(validation, cols...)
+	if err != nil {
+		o.Rollback()
+		c.Data["json"] = lib.JSONS{Code: 1201, Msg: "修改失败!", Data: nil}
+		c.ServeJSON()
+		return
+	}
+
+	_, err = valiRecord.ADD(validation, BatchNumber)
+	if err != nil {
+		o.Rollback() // 回滚事务
+		c.Data["json"] = lib.JSONS{Code: 1201, Msg: "保存历史记录失败!", Data: nil}
+		c.ServeJSON()
+		return
+	}
+
+	err = o.Commit() // 提交事务
+	if err != nil {
+		o.Rollback() // 回滚事务
+		c.Data["json"] = lib.JSONS{Code: 1201, Msg: "提交事务失败!", Data: nil}
+		c.ServeJSON()
+		return
+	}
+
+	NatsServer.AddUserLogs(c.User.T_uuid, "验证工具", "确认转移", []string{request.T_sn})
+
+	// 返回转移结果
+	result := TransferResult{
+		T_sn:    request.T_sn,
+		Success: true,
+		Message: "设备转移确认成功!",
+		Status:  "completed",
+	}
+
+	c.Data["json"] = lib.JSONS{Code: 200, Msg: "设备转移确认成功!", Data: result}
+	c.ServeJSON()
+	return
+}
+
+// ... existing code ...

+ 110 - 2
models/Contract/Contract.go

@@ -16,6 +16,20 @@ import (
 	_ "github.com/go-sql-driver/mysql"
 )
 
+func Get_Contract_payment_method(payment_method string) string {
+	switch payment_method {
+	case "A":
+		return "先款后货"
+	case "B":
+		return "收部分定金50%"
+	case "C":
+		return "先货后款"
+	default:
+		return ""
+	}
+
+}
+
 // 合同
 type Contract struct {
 	Id                int     `orm:"column(ID);size(11);auto;pk"`
@@ -39,6 +53,8 @@ type Contract struct {
 	T_invoice_money    float32 `orm:"digits(12);decimals(2)"` // 开票总金额
 	T_start_date       string  `orm:"size(256);null"`         // 起始时间
 	T_end_date         string  `orm:"size(256);null"`         // 终止时间
+	T_stamping_date    string  `orm:"size(256);null"`         // 盖章时间
+	T_payment_method   string  `orm:"size(256);null"`         // 付款方式 A先款后货 B收部分定金50% C先货后款
 
 	T_submit   string `orm:"size(256);null"` // 合同提交人员
 	T_approver string `orm:"size(256);null"` // 审批人
@@ -90,6 +106,8 @@ type Contract_R struct {
 	T_recoveries_state string  // 回款状态
 	T_invoice_state    string  // 开票状态
 	T_percentage_state int     // 提成状态 1-未打款 2-已打款
+	T_stamping_date    string  // 盖章时间
+	T_payment_method   string  // 付款方式 A先款后货 B收部分定金50% C先货后款
 
 }
 
@@ -112,11 +130,17 @@ type Contract_Detail struct {
 	T_pdf                 string  // 附件
 	T_start_date          string  // 起始时间
 	T_end_date            string  // 终止时间
+	T_stamping_date       string  // 盖章时间
 	T_Product             []ContractProduct_R
 	T_recoveries          []ContractFinance_R // 回款信息 回款时间,回款金额|回款时间,回款金额|
+	T_recoveries_time     string              // 回款时间
 	T_no_recoveries_money float32             // 未回款金额
 	T_invoice             []ContractFinance_R // 开票信息 开票时间,开票金额|开票时间,开票金额|
 	T_no_invoice_money    float32             // 未开票金额
+
+	T_verify_state   string // 1-未签约 2-已作废 3-已签约 4-即将到期
+	T_payment_method string // 付款方式 A先款后货 B收部分定金50% C先货后款
+
 }
 type ContractFinance_R struct {
 	T_date  string  // 时间
@@ -135,6 +159,9 @@ type Contract_Verify struct {
 	T_recoveries       string  // 回款信息
 	T_recoveries_money float32 // 回款总金额
 	T_invoice          string  // 开票信息
+	T_stamping_date    string  // 盖章时间
+	T_payment_method   string  // 付款方式 A先款后货 B收部分定金50% C先货后款
+
 }
 type ContractMoney_Stat struct {
 	T_date             int
@@ -190,6 +217,7 @@ func ContractToContract_R(t Contract) (r Contract_R) {
 	r.T_recoveries_time = t.T_recoveries_time
 	r.T_percentage_state = t.T_percentage_state
 	r.T_invoice_money = t.T_invoice_money
+
 	r.T_recoveries_state = "部分回款"
 	if t.T_recoveries_money == 0 {
 		r.T_recoveries_state = "未回款"
@@ -204,6 +232,8 @@ func ContractToContract_R(t Contract) (r Contract_R) {
 	if t.T_invoice_money == t.T_discount {
 		r.T_invoice_state = "全部开票"
 	}
+	r.T_stamping_date = t.T_stamping_date
+	r.T_payment_method = t.T_payment_method
 	return r
 }
 
@@ -228,14 +258,53 @@ func ContractToContract_Detail(t Contract, productList []ContractProduct_R) (r C
 	r.T_Product = productList
 	r.T_start_date = t.T_start_date
 	r.T_end_date = t.T_end_date
+	r.T_stamping_date = t.T_stamping_date
+	r.T_payment_method = t.T_payment_method
 
 	recoveriesList, recoveries := ContractToContractFinance_R(t.T_recoveries)
 	r.T_recoveries = recoveriesList
-	r.T_no_recoveries_money = t.T_money - recoveries
+	r.T_recoveries_time = t.T_recoveries_time
+	r.T_no_recoveries_money = t.T_discount - recoveries
 
 	invoiceList, invoice := ContractToContractFinance_R(t.T_invoice)
 	r.T_invoice = invoiceList
-	r.T_no_invoice_money = t.T_money - invoice
+	r.T_no_invoice_money = t.T_discount - invoice
+
+	if t.T_type == 2 {
+		nowTime := time.Now()
+		now := time.Date(nowTime.Year(), nowTime.Month(), nowTime.Day(), 0, 0, 0, 0, time.Local)
+		end, _ := lib.DateStrToTime(t.T_end_date)
+		now2Month := now.AddDate(0, 2, 0)
+		// 1-未签约 2-已作废 3-已签约 4-即将到期
+		state := 0
+		if len(t.T_end_date) == 0 {
+			state = 1
+		}
+		// 结束时间<今天 已过期
+		if end.Before(now) {
+			state = 2
+		} else {
+			// 结束时间<今天+2月 即将到期 && 结束时间>今天
+			if end.Before(now2Month) || end.Equal(now) {
+				state = 4
+			}
+			// 结束时间>今天+2月 已签约
+			if end.After(now2Month) || end.Equal(now2Month) {
+				state = 3
+			}
+		}
+
+		switch state {
+		case 1:
+			r.T_verify_state = "未签约"
+		case 2:
+			r.T_verify_state = "已作废"
+		case 3:
+			r.T_verify_state = "已签约"
+		case 4:
+			r.T_verify_state = "即将到期"
+		}
+	}
 
 	return r
 }
@@ -248,6 +317,8 @@ func ContractToContract_Verify(t Contract) (r Contract_Verify) {
 	r.T_date = t.T_date
 	r.T_start_date = t.T_start_date
 	r.T_end_date = t.T_end_date
+	r.T_stamping_date = t.T_stamping_date
+	r.T_payment_method = t.T_payment_method
 	nowTime := time.Now()
 	now := time.Date(nowTime.Year(), nowTime.Month(), nowTime.Day(), 0, 0, 0, 0, time.Local)
 	end, _ := lib.DateStrToTime(t.T_end_date)
@@ -429,6 +500,43 @@ func (dao *ContractDaoImpl) Read_VerifyContract_List(T_name, T_customer_id strin
 
 	return r_, cnt
 }
+func (dao *ContractDaoImpl) Read_VerifyContract_Excel(T_state int, T_name, T_start_date, T_end_date string) (r_ []Contract_Detail) {
+
+	var maps []Contract
+	sql := "SELECT vc.t_customer as t_customer,vc.t__state, contract.*" +
+		" FROM contract LEFT JOIN verify_contract vc ON vc.t_customer_id=contract.t_customer_id" +
+		" WHERE vc.t__state>0 AND contract.t__state>0"
+	if T_state > 0 {
+		sql += " AND vc.t__state=" + strconv.Itoa(T_state)
+	}
+	if len(T_name) > 0 {
+		sql += " AND vc.t_customer LIKE '%" + T_name + "%'"
+	}
+	if len(T_start_date) > 0 {
+		sql += " AND contract.T_stamping_date>='" + T_start_date + "'"
+	}
+	if len(T_end_date) > 0 {
+		sql += " AND contract.T_stamping_date<='" + T_end_date + "'"
+	}
+	fmt.Println(sql)
+	_, err := dao.orm.Raw(sql).QueryRows(&maps)
+	if err != nil {
+		logs.Error(lib.FuncName(), err)
+	}
+
+	ContractProductDao := NewContractProduct(dao.orm)
+	for _, v := range maps {
+		productList := ContractProductDao.Read_ContractProduct_List(v.T_number)
+		var pList []ContractProduct_R
+		for _, product := range productList {
+			p := ContractProductToContractProduct_R(product)
+			pList = append(pList, p)
+		}
+		r_ = append(r_, ContractToContract_Detail(v, pList))
+	}
+
+	return r_
+}
 
 func (dao *ContractDaoImpl) Read_Contract_List_For_RecoveriesAndInvoice(T_uuid, T_submit string, T_mine int, T_name, T_start_date, T_end_date string, T_recoveries_state, T_invoice_state, T_percentage_state, page, page_z int) (r_ []Contract_R, cnt int64) {
 	// 也可以直接使用 Model 结构体作为表名

+ 5 - 21
models/Stock/Stock.go

@@ -57,6 +57,7 @@ type Stock_R struct {
 	T_depot_id int // 仓库id
 	T_total    int // 库存数量
 	T_reality  int // 实际库存数量
+	T_occupy   int // 占用库存
 	T_sort     int // 排序
 
 	// ---------产品信息-----------
@@ -76,6 +77,7 @@ func StockToStock_R(t Stock) (r Stock_R) {
 	r.T_depot_id = t.T_depot_id
 	r.T_total = t.T_total
 	r.T_reality = t.T_reality
+	r.T_occupy = t.T_occupy
 	r.T_sort = t.T_sort
 
 	r.T_product_id = t.T_product_id
@@ -163,27 +165,9 @@ func (dao *StockDaoImpl) AddOrUpdate_Occupy_Stock(T_depot_id, T_product_id, T_pr
 	var stock Stock
 	err = qs.Filter("T_depot_id", T_depot_id).Filter("T_product_id", T_product_id).One(&stock)
 	if err != nil {
-		if err.Error() == orm.ErrNoRows.Error() {
-			r := Stock{
-				T_depot_id:      T_depot_id,
-				T_product_id:    T_product_id,
-				T_product_name:  T_product_name,
-				T_product_class: T_product_class,
-				T_product_model: T_product_model,
-				T_occupy:        T_num,
-			}
-			id, err = dao.orm.Insert(&r)
-			if err != nil {
-				err = errors.New("添加库存失败")
-				logs.Error(lib.FuncName(), err)
-			}
-			return
-		} else {
-			err = errors.New("查询库存失败")
-
-			logs.Error(lib.FuncName(), err)
-			return
-		}
+		err = errors.New("查询库存失败")
+		logs.Error(lib.FuncName(), err)
+		return
 	}
 	// T_type 1-审核不通过 2-其他状态
 	if T_type == 1 {

+ 53 - 29
models/Stock/StockOut.go

@@ -19,7 +19,7 @@ import (
 func Get_StockOut_audit_name(T_audt int) string {
 	switch T_audt {
 	case 1:
-		return "待审"
+		return "待审"
 	case 2:
 		return "财务通过"
 	case 3:
@@ -45,6 +45,7 @@ const (
 	StockOutAuditManagerUnPass     // 总经理驳回
 	StockOutAlreadyOut             // 已出库
 
+	StockOutTypeApply     = "Apply"
 	StockOutTypeFinance   = "Finance"
 	StockOutTypeManager   = "Manager"
 	StockOutTypeWarehouse = "Warehouse"
@@ -56,18 +57,20 @@ const (
 	StockOutPaymentMethodD = "D" // 项目支持,无同意(必须冯总同意)
 )
 
-// 
+// 
 type StockOut struct {
-	Id                int    `orm:"column(ID);size(11);auto;pk"`
-	T_number          string `orm:"size(256);null"`  // 入库单号
-	T_contract_number string `orm:"size(256);null"`  // 合同编号
-	T_depot_id        int    `orm:"size(20);null"`   // 仓库id
-	T_type            int    `orm:"size(2);null"`    // 出库类型 1-领料出库 2-销售出库
-	T_date            string `orm:"size(256);null"`  // 业务日期
-	T_receive         string `orm:"size(256);null"`  // 领取人
-	T_submit          string `orm:"size(256);null"`  // 经办人
-	T_remark          string `orm:"type(text);null"` // 备注
-	T_project         string `orm:"type(text);null"` // 出库项目
+	Id                 int    `orm:"column(ID);size(11);auto;pk"`
+	T_number           string `orm:"size(256);null"`  // 出库单号
+	T_contract_number  string `orm:"size(256);null"`  // 合同编号
+	T_depot_id         int    `orm:"size(20);null"`   // 仓库id
+	T_type             int    `orm:"size(2);null"`    // 出库类型 1-领料出库 2-销售出库
+	T_date             string `orm:"size(256);null"`  // 出库日期
+	T_application_date string `orm:"size(256);null"`  // 申请日期
+	T_receive          string `orm:"size(256);null"`  // 领取人
+	T_submit           string `orm:"size(256);null"`  // 经办人
+	T_remark           string `orm:"type(text);null"` // 备注
+	T_project          string `orm:"type(text);null"` // 出库项目
+	T_warehouse        string `orm:"type(text);null"` // 出库人
 	// ----销售出库------
 	T_delivery_type  int    `orm:"size(2);null"`   // 1-自送 2-自提 3-快递
 	T_signer_unit    string `orm:"size(256);null"` // 签收单位
@@ -111,8 +114,11 @@ type StockOut_R struct {
 	T_depot_name               string // 仓库名称
 	T_type                     int    // 出库类型 1-领料出库 2-销售出库
 	T_date                     string // 业务日期
+	T_application_date         string // 申请日期
 	T_receive                  string // 领取人
 	T_receive_name             string // 领取人名称
+	T_warehouse                string // 出库人
+	T_warehouse_name           string // 出库人名称
 	T_project                  string // 关联项目
 	T_company_name             string // 公司名称
 	T_payment_method           string // 付款方式 A先款后货 B收部分定金50% C先货后款 D项目支持,无同意
@@ -123,19 +129,22 @@ type StockOut_R struct {
 }
 
 type StockOut_Detail struct {
-	Id                int
-	T_number          string // 入库单号
-	T_contract_number string // 合同编号
-	T_depot_id        int    // 仓库id
-	T_depot_name      string // 仓库名称
-	T_type            int    // 出库类型 1-领料出库 2-销售出库
-	T_date            string // 业务日期
-	T_receive         string // 领取人
-	T_receive_name    string // 领取人名称
-	T_submit          string // 经办人
-	T_submit_name     string // 经办人名称
-	T_project         string // 关联项目
-	T_remark          string // 备注
+	Id                 int
+	T_number           string // 出库单号
+	T_contract_number  string // 合同编号
+	T_depot_id         int    // 仓库id
+	T_depot_name       string // 仓库名称
+	T_type             int    // 出库类型 1-领料出库 2-销售出库
+	T_date             string // 业务日期
+	T_application_date string // 申请日期=
+	T_receive          string // 领取人
+	T_receive_name     string // 领取人名称
+	T_warehouse        string // 出库人
+	T_warehouse_name   string // 出库人名称
+	T_submit           string // 经办人
+	T_submit_name      string // 经办人名称
+	T_project          string // 关联项目
+	T_remark           string // 备注
 	// ----销售出库------
 	T_delivery_type            int    // 1-自送 2-自提 3-快递
 	T_signer_unit              string // 签收单位
@@ -160,8 +169,11 @@ func StockOutToStockOut_R(t StockOut) (r StockOut_R) {
 	r.T_depot_name = Basic.Read_Depot_Get(t.T_depot_id)
 	r.T_type = t.T_type
 	r.T_date = t.T_date
+	r.T_application_date = t.T_application_date
 	r.T_receive = t.T_receive
 	r.T_receive_name = Account.Read_User_T_name_Get(t.T_receive)
+	r.T_warehouse = t.T_warehouse
+	r.T_warehouse_name = Account.Read_User_T_name_Get(t.T_warehouse)
 	r.T_project = t.T_project
 	r.T_company_name = t.T_company_name
 	r.T_payment_method = t.T_payment_method
@@ -180,8 +192,11 @@ func StockOutToStockOut_Detail(t StockOut, productList []StockOutProduct_R) (r S
 	r.T_depot_name = Basic.Read_Depot_Get(t.T_depot_id)
 	r.T_type = t.T_type
 	r.T_date = t.T_date
+	r.T_application_date = t.T_application_date
 	r.T_receive = t.T_receive
 	r.T_receive_name = Account.Read_User_T_name_Get(t.T_receive)
+	r.T_warehouse = t.T_warehouse
+	r.T_warehouse_name = Account.Read_User_T_name_Get(t.T_warehouse)
 	r.T_project = t.T_project
 	r.T_remark = t.T_remark
 	r.T_delivery_type = t.T_delivery_type
@@ -293,7 +308,7 @@ func (dao *StockOutDaoImpl) Read_StockOut_List(T_name string, T_depot_id int, T_
 }
 
 // 获取出库审核列表
-func (dao *StockOutDaoImpl) Read_StockOut_Audit_List(T_name, T_type string, T_depot_id int, T_contract_number, T_start_date, T_end_date string, page, page_z int) (r_ []StockOut_R, cnt int64) {
+func (dao *StockOutDaoImpl) Read_StockOut_Audit_List(T_uuid, T_name, T_type string, T_state, T_depot_id int, T_contract_number, T_start_date, T_end_date string, page, page_z int) (r_ []StockOut_R, cnt int64) {
 	qs := dao.orm.QueryTable(new(StockOut))
 	var offset int64
 	if page <= 1 {
@@ -305,6 +320,11 @@ func (dao *StockOutDaoImpl) Read_StockOut_Audit_List(T_name, T_type string, T_de
 	// 过滤
 	cond1 := orm.NewCondition()
 	cond := orm.NewCondition()
+
+	if T_type == StockOutTypeApply {
+		cond = cond.And("T_submit", T_uuid)
+	}
+
 	if T_type == StockOutTypeFinance {
 		cond = cond.And("T_state__in", []int{StockOutWaitAudit, StockOutAuditFinancePass, StockOutAuditFinanceUnPass})
 		cond = cond.And("T_payment_method__in", []string{StockOutPaymentMethodA, StockOutPaymentMethodB})
@@ -315,13 +335,16 @@ func (dao *StockOutDaoImpl) Read_StockOut_Audit_List(T_name, T_type string, T_de
 	}
 
 	if T_type == StockOutTypeWarehouse {
-		cond = cond.And("T_state", StockOutAuditManagerPass)
-
+		cond = cond.AndCond(cond.And("T_state", StockOutAuditManagerPass).
+			OrCond(cond1.And("T_state", StockOutAlreadyOut).And("T_warehouse", T_uuid)))
 	}
 
 	if T_depot_id > 0 {
 		cond = cond.And("T_depot_id", T_depot_id)
 	}
+	if T_state > 0 {
+		cond = cond.And("T_state", T_state)
+	}
 	if len(T_name) > 0 {
 		cond = cond.And("T_number__icontains", T_name)
 	}
@@ -339,6 +362,7 @@ func (dao *StockOutDaoImpl) Read_StockOut_Audit_List(T_name, T_type string, T_de
 
 	// 查询
 	var r []StockOut
+	//_, err := qs.Limit(page_z, offset).SetCond((*orm2.Condition)(cond)).OrderBy("T_application_date").All(&r)
 	_, err := qs.Limit(page_z, offset).SetCond((*orm2.Condition)(cond)).OrderBy("-Id").All(&r)
 
 	if err != nil {
@@ -402,7 +426,7 @@ func (dao *StockOutDaoImpl) Read_StockOut_T_project(T_depot_id, T_product_id int
 
 }
 
-// 通过入库编号查询入库产品列表
+// 通过出库编号查询出库产品列表
 func (dao *StockOutDaoImpl) Read_StockOutProduct_List(T_name, T_start_date, T_end_date string, T_depot_id int,
 	T_product_name, T_product_model string, page int, page_z int) (
 	r_ []StockOutProductList, cnt int64) {

+ 1 - 0
routers/Contract.go

@@ -29,6 +29,7 @@ func init() {
 		beego.NSRouter("/Customer_List", &controllers.ContractController{}, "*:VerifyContract_Customer_List"),     // 验证合同客户列表
 		beego.NSRouter("/Add_Customer", &controllers.ContractController{}, "*:VerifyContract_Add_Customer"),       // 添加客户
 		beego.NSRouter("/Update_Customer", &controllers.ContractController{}, "*:VerifyContract_Update_Customer"), // 编辑客户
+		beego.NSRouter("/Excel", &controllers.ContractController{}, "*:VerifyContract_Excel"),                     // 导出验证额合同
 	)
 	recoveriesContract := beego.NewNamespace("/RecoveriesContract",
 		beego.NSRouter("/List", &controllers.ContractController{}, "*:Contract_List_RecoveriesMoney"),          // 回款明细列表

+ 3 - 0
routers/Stock.go

@@ -41,6 +41,9 @@ func init() {
 		beego.NSRouter("/Excel", &controllers.StockController{}, "*:StockOut_Excel"),
 		beego.NSRouter("/Excel_Batch", &controllers.StockController{}, "*:StockOut_Excel_Batch"),       // 批量导出入库
 		beego.NSRouter("/Apply", &controllers.StockController{}, "*:StockOut_Apply"),                   // 出库申请
+		beego.NSRouter("/Apply_Edit", &controllers.StockController{}, "*:StockOut_Apply_Edit"),         // 修改出库申请
+		beego.NSRouter("/Apply_Del", &controllers.StockController{}, "*:StockOut_Apply_Del"),           // 修改出库申请
+		beego.NSRouter("/Apply_List", &controllers.StockController{}, "*:StockOut_Apply_List"),         // 出库申请列表
 		beego.NSRouter("/Finance_List", &controllers.StockController{}, "*:StockOut_Finance_List"),     // 财务审核列表
 		beego.NSRouter("/Manager_List", &controllers.StockController{}, "*:StockOut_Manager_List"),     // 总经理审核列表
 		beego.NSRouter("/Warehouse_List", &controllers.StockController{}, "*:StockOut_Warehouse_List"), // 总经理审核列表

+ 2 - 0
routers/vaildationTool.go

@@ -24,6 +24,8 @@ func init() {
 		beego.NSRouter("/recordList", &controllers.ValidationController{}, "*:Record_List"),                    // 读取文件
 		beego.NSRouter("/operationList", &controllers.ValidationController{}, "*:Operation_List"),              // 操作记录列表
 		beego.NSRouter("/exportOperationExcel", &controllers.ValidationController{}, "*:Operation_Excel"),      // 操作记录列表
+		beego.NSRouter("/transfer", &controllers.ValidationController{}, "*:TransferValidationTool"),          // 设备转移请求
+		beego.NSRouter("/confirmTransfer", &controllers.ValidationController{}, "*:ConfirmTransferValidationTool"), // 确认接收设备
 	)
 	beego.AddNamespace(validationTool)
 }