Browse Source

add:验证合同

zoie 1 year ago
parent
commit
43f3fae781

+ 34 - 0
Nats/Nats.go

@@ -3,6 +3,7 @@ package Nats
 import (
 	"ERP_storage/conf"
 	"ERP_storage/models/Account"
+	"ERP_storage/models/Contract"
 	"fmt"
 	menulibs "git.baozhida.cn/ERP_libs/Menu"
 	powerlibs "git.baozhida.cn/ERP_libs/Power"
@@ -231,4 +232,37 @@ func NatsInit() {
 
 	})
 
+	// 更新合同状态
+	_, _ = Nats.Subscribe(conf.Sys_Name+"_Update_Contract_T_out", func(m *nats.Msg) {
+
+		fmt.Printf(conf.Sys_Name+"_Update_Contract_T_out message: %v\n", string(m.Data))
+
+		T_number := string(m.Data)
+		o := orm.NewOrm()
+		o.Begin()
+		ContractDao := Contract.NewContract(o)
+		ContractProductDao := Contract.NewContractProduct(o)
+
+		// 1、添加出库单
+		contract, _ := ContractDao.Read_Contract_ByT_number(T_number)
+
+		T_out := 2
+		// 查询合同产品清单是否全部都为已出库
+		state := ContractProductDao.Read_ContractProduct_T_State_List(T_number)
+		if state == 1 {
+			T_out = 1
+		}
+		if state == 3 {
+			T_out = 3
+		}
+
+		if T_out != contract.T_out {
+			err := ContractDao.Update_Contract(contract, "T_out")
+			if err != nil {
+				o.Rollback()
+			}
+		}
+
+	})
+
 }

+ 1 - 0
conf/app.conf

@@ -37,6 +37,7 @@ FilterOnlyLoginCheckURL =
 
 # 消息通知前端页面跳转url
 ContractApprovalUrl = "/contract"
+VerifyContractUrl = "/contract"
 
 # 静态资源
 Qiniu_AccessKey = -8ezB_d-8-eUFTMvhOGbGzgeQRPeKQnaQ3DBcUxo

+ 1 - 0
conf/config.go

@@ -39,3 +39,4 @@ var OssQiniu, _ = beego.AppConfig.String("OssQiniu")
 
 // 消息通知前端页面跳转url
 var ContractApprovalUrl, _ = beego.AppConfig.String("ContractApprovalUrl")
+var VerifyContractUrl, _ = beego.AppConfig.String("VerifyContractUrl")

+ 21 - 2
controllers/Basic.go

@@ -272,8 +272,9 @@ func (c *BasicController) Product_List() {
 	T_name := c.GetString("T_name")
 	T_model := c.GetString("T_model")
 	T_class, _ := c.GetInt("T_class")
+	T_VerifyContract, _ := c.GetInt("T_VerifyContract")
 
-	R_List, R_cnt := Basic.Read_Product_List(T_name, T_model, T_class, page, page_z)
+	R_List, R_cnt := Basic.Read_Product_List(T_name, T_model, T_class, T_VerifyContract, page, page_z)
 
 	var r_jsons lib.R_JSONS
 	r_jsons.Num = R_cnt
@@ -292,6 +293,7 @@ func (c *BasicController) Product_Add() {
 	T_model := c.GetString("T_model")
 	T_spec := c.GetString("T_spec")
 	T_relation_sn, _ := c.GetInt("T_relation_sn")
+	T_price, _ := c.GetFloat("T_price")
 	T_img := c.GetString("T_img")
 	T_remark := c.GetString("T_remark")
 
@@ -301,11 +303,24 @@ func (c *BasicController) Product_Add() {
 		return
 	}
 
+	product, err := Basic.Read_Product_ByT_name(T_name, T_model, T_spec)
+	if err != nil && err.Error() != orm.ErrNoRows.Error() {
+		c.Data["json"] = lib.JSONS{Code: 203, Msg: "添加失败"}
+		c.ServeJSON()
+		return
+	}
+	if product.Id > 0 {
+		c.Data["json"] = lib.JSONS{Code: 203, Msg: "该产品已存在"}
+		c.ServeJSON()
+		return
+	}
+
 	var_ := Basic.Product{
 		T_name:        T_name,
 		T_class:       T_class,
 		T_model:       T_model,
 		T_spec:        T_spec,
+		T_price:       float32(T_price),
 		T_relation_sn: T_relation_sn,
 		T_img:         T_img,
 		T_remark:      T_remark,
@@ -331,6 +346,7 @@ func (c *BasicController) Product_Edit() {
 	T_class, _ := c.GetInt("T_class")
 	T_model := c.GetString("T_model")
 	T_spec := c.GetString("T_spec")
+	T_price, _ := c.GetFloat("T_price")
 	T_relation_sn, _ := c.GetInt("T_relation_sn")
 	T_img := c.GetString("T_img")
 	T_remark := c.GetString("T_remark")
@@ -363,8 +379,11 @@ func (c *BasicController) Product_Edit() {
 	if len(T_remark) > 0 {
 		Product_r.T_remark = T_remark
 	}
+	if T_price > 0 {
+		Product_r.T_price = float32(T_price)
+	}
 
-	if err = Basic.Update_Product(Product_r, "T_name", "T_class", "T_model", "T_spec", "T_relation_sn", "T_img", "T_remark"); err != nil {
+	if err = Basic.Update_Product(Product_r, "T_name", "T_class", "T_model", "T_spec", "T_price", "T_relation_sn", "T_img", "T_remark"); err != nil {
 		c.Data["json"] = lib.JSONS{Code: 203, Msg: "修改失败"}
 		c.ServeJSON()
 		return

+ 693 - 50
controllers/Contract.go

@@ -3,7 +3,9 @@ package controllers
 import (
 	"ERP_storage/Nats/NatsServer"
 	"ERP_storage/conf"
+	"ERP_storage/logs"
 	"ERP_storage/models/Account"
+	"ERP_storage/models/Basic"
 	"ERP_storage/models/Contract"
 	"ERP_storage/models/Stock"
 	"fmt"
@@ -11,9 +13,11 @@ import (
 	"git.baozhida.cn/ERP_libs/lib"
 	"github.com/beego/beego/v2/adapter/orm"
 	beego "github.com/beego/beego/v2/server/web"
+	"github.com/robfig/cron/v3"
 	"math"
 	"strconv"
 	"strings"
+	"time"
 )
 
 type ContractController struct {
@@ -25,6 +29,27 @@ func (c *ContractController) Prepare() {
 	c.User = *Account.User_r
 }
 
+func (c *ContractController) Contract_GenT_number() {
+
+	o := orm.NewOrm()
+	ContractDao := Contract.NewContract(o)
+	T_number := ""
+	rand_x := 1
+	for true {
+		T_number = fmt.Sprintf("GZBZD-%s%03d", time.Now().Format("20060102"), rand_x)
+		_, err := ContractDao.Read_Contract_ByT_number(T_number)
+		if err != nil && err.Error() == orm.ErrNoRows.Error() {
+			break
+		}
+		rand_x += 1
+	}
+
+	c.Data["json"] = lib.JSONS{Code: 200, Msg: "ok!", Data: T_number}
+	c.ServeJSON()
+	return
+
+}
+
 func (c *ContractController) Contract_List() {
 
 	// 分页参数 初始化
@@ -128,24 +153,31 @@ func (c *ContractController) Contract_Add() {
 
 	T_number := c.GetString("T_number")
 	T_customer := c.GetString("T_customer")
-	T_money, _ := c.GetFloat("T_money")
-	T_type, _ := c.GetInt("T_type")
+	T_money, _ := c.GetFloat("T_money")       // 总价
+	T_discount, _ := c.GetFloat("T_discount") // 优惠价
 	T_date := c.GetString("T_date")
 	T_product := c.GetString("T_product")
 	T_remark := c.GetString("T_remark")
 	T_pdf := c.GetString("T_pdf")
+	T_project := c.GetString("T_project")
+	T_recoveries := c.GetString("T_recoveries")
+	T_invoice := c.GetString("T_invoice")
 
 	var_ := Contract.Contract{
-		T_number:   T_number,
-		T_customer: T_customer,
-		T_money:    float32(T_money),
-		T_type:     T_type,
-		T_date:     T_date,
-		T_out:      1,
-		T_State:    3,
-		T_remark:   T_remark,
-		T_pdf:      T_pdf,
-		T_submit:   c.User.T_uuid,
+		T_number:     T_number,
+		T_customer:   T_customer,
+		T_money:      float32(T_money),
+		T_discount:   float32(T_discount),
+		T_project:    T_project,
+		T_type:       1, //合同类型 1-销售合同 2-验证合同
+		T_date:       T_date,
+		T_out:        1,
+		T_State:      3,
+		T_remark:     T_remark,
+		T_pdf:        T_pdf,
+		T_submit:     c.User.T_uuid,
+		T_recoveries: T_recoveries,
+		T_invoice:    T_invoice,
 	}
 
 	o := orm.NewOrm()
@@ -161,7 +193,7 @@ func (c *ContractController) Contract_Add() {
 		return
 	}
 
-	Id, err := ContractDao.Add_Contract(var_)
+	_, err = ContractDao.Add_Contract(var_)
 	if err != nil {
 		o.Rollback()
 		c.Data["json"] = lib.JSONS{Code: 203, Msg: "添加失败"}
@@ -169,15 +201,6 @@ func (c *ContractController) Contract_Add() {
 		return
 	}
 
-	//合同类型 1-销售合同 2-验证合同
-	if T_type == 2 {
-		o.Commit()
-		NatsServer.AddUserLogs(c.User.T_uuid, "合同", "添加", var_)
-		c.Data["json"] = lib.JSONS{Code: 200, Msg: "ok!", Data: Id}
-		c.ServeJSON()
-		return
-	}
-
 	if len(T_product) == 0 {
 		o.Rollback()
 		c.Data["json"] = lib.JSONS{Code: 202, Msg: "销售合同产品明细不能为空"}
@@ -240,10 +263,10 @@ func (c *ContractController) Contract_Approval() {
 		return
 	}
 	if T_state == 1 {
-		NatsServer.AddNews(contract.T_submit, fmt.Sprintf("您提交的合同(%s)审核已通过", T_number), conf.ContractApprovalUrl)
+		NatsServer.AddNews(contract.T_submit, fmt.Sprintf("【合同审核】您提交的合同(%s)审核已通过", T_number), conf.ContractApprovalUrl)
 	}
 	if T_state == 2 {
-		NatsServer.AddNews(contract.T_submit, fmt.Sprintf("您提交的合同(%s)【审核未通过】", T_number), "")
+		NatsServer.AddNews(contract.T_submit, fmt.Sprintf("【合同审核】您提交的合同(%s)审核未通过", T_number), conf.ContractApprovalUrl)
 	}
 	NatsServer.AddUserLogs(c.User.T_uuid, "合同", "审核", contract)
 
@@ -259,6 +282,9 @@ func (c *ContractController) Contract_Edit() {
 	T_product := c.GetString("T_product")
 	T_remark := c.GetString("T_remark")
 	T_pdf := c.GetString("T_pdf")
+	T_project := c.GetString("T_project")
+	T_recoveries := c.GetString("T_recoveries")
+	T_invoice := c.GetString("T_invoice")
 
 	o := orm.NewOrm()
 	o.Begin()
@@ -288,13 +314,22 @@ func (c *ContractController) Contract_Edit() {
 	if len(T_pdf) > 0 {
 		contract.T_pdf = T_pdf
 	}
+	if len(T_project) > 0 {
+		contract.T_project = T_project
+	}
+	if len(T_recoveries) > 0 {
+		contract.T_recoveries = T_recoveries
+	}
+	if len(T_invoice) > 0 {
+		contract.T_invoice = T_invoice
+	}
 
 	//1-已通过 2-未通过 3-待审核 合同状态为未通过,修改之后将状态更改为待审核
 	if contract.T_State == 2 {
 		contract.T_State = 3
 	}
 
-	err = ContractDao.Update_Contract(contract, "T_customer", "T_money", "T_date", "T_remark", "T_pdf")
+	err = ContractDao.Update_Contract(contract, "T_customer", "T_money", "T_date", "T_remark", "T_pdf", "T_State", "T_project", "T_recoveries", "T_invoice")
 	if err != nil {
 		o.Rollback()
 		c.Data["json"] = lib.JSONS{Code: 203, Msg: "添加失败"}
@@ -304,8 +339,8 @@ func (c *ContractController) Contract_Edit() {
 
 	//合同类型 1-销售合同 2-验证合同
 	if contract.T_type == 2 {
+		o.Commit()
 		NatsServer.AddUserLogs(c.User.T_uuid, "合同", "修改", contract)
-
 		c.Data["json"] = lib.JSONS{Code: 200, Msg: "ok!", Data: contract.Id}
 		c.ServeJSON()
 		return
@@ -318,41 +353,103 @@ func (c *ContractController) Contract_Edit() {
 		return
 	}
 	productList := lib.SplitString(T_product, "|")
+
+	cpl, _ := ContractProductDao.Read_ContractProductList_ByT_number(T_number)
+	needToDeleteList, allList := Check_NeedToDelete_Product_List(productList, cpl)
+	// 检查编辑时需要修改的产品
+	for k, v := range needToDeleteList {
+		id, _ := strconv.Atoi(strings.Split(k, "-")[0])
+		productId, _ := strconv.Atoi(strings.Split(k, "-")[1])
+		if v > 0 {
+			o.Rollback()
+			product, _ := Basic.Read_Product_ById(productId)
+			c.Data["json"] = lib.JSONS{Code: 202, Msg: fmt.Sprintf("%s已(部分)出库,禁止删除!", product.T_name)}
+			c.ServeJSON()
+			return
+		}
+		cp := Contract.ContractProduct{Id: id}
+		err = ContractProductDao.Delete_ContractProduct(cp)
+		if err != nil {
+			o.Rollback()
+			c.Data["json"] = lib.JSONS{Code: 202, Msg: "修改失败!"}
+			c.ServeJSON()
+			return
+		}
+	}
+	//for _, v := range productList {
+	//	product_id, _ := strconv.Atoi(strings.Split(v, ",")[0])
+	//	num, _ := strconv.Atoi(strings.Split(v, ",")[1])
+	//
+	//	var cp Contract.ContractProduct
+	//	cp, err = ContractProductDao.Read_ContractProduct_ByT_number_T_product_id(T_number, product_id)
+	//	if err != nil {
+	//		if err.Error() == orm.ErrNoRows.Error() {
+	//			contractProduct := Contract.ContractProduct{
+	//				T_contract_number: T_number,
+	//				T_product_id:      product_id,
+	//				T_product_total:   num,
+	//				T_product_out:     0, // 已出库数量
+	//				T_State:           1, // 1-未出库 2-已部分出库 3-已全部出库
+	//			}
+	//			_, err = ContractProductDao.Add_ContractProduct(contractProduct)
+	//			if err != nil {
+	//				o.Rollback()
+	//				c.Data["json"] = lib.JSONS{Code: 203, Msg: "修改失败"}
+	//				c.ServeJSON()
+	//				return
+	//			}
+	//
+	//		} else {
+	//			o.Rollback()
+	//			c.Data["json"] = lib.JSONS{Code: 203, Msg: "修改失败"}
+	//			c.ServeJSON()
+	//			return
+	//		}
+	//	}
+	//	// 修改产品总数量
+	//	if cp.T_product_total == num {
+	//		continue
+	//	}
+	//	cp.T_product_total = num
+	//	err = ContractProductDao.Update_ContractProduct(cp, "T_product_total")
+	//	if err != nil {
+	//		o.Rollback()
+	//		c.Data["json"] = lib.JSONS{Code: 203, Msg: "修改失败"}
+	//		c.ServeJSON()
+	//		return
+	//	}
+	//
+	//}
 	for _, v := range productList {
 		product_id, _ := strconv.Atoi(strings.Split(v, ",")[0])
 		num, _ := strconv.Atoi(strings.Split(v, ",")[1])
-
-		var cp Contract.ContractProduct
-		cp, err = ContractProductDao.Read_ContractProduct_ByT_number_T_product_id(T_number, product_id)
-		if err != nil {
-			if err.Error() == orm.ErrNoRows.Error() {
-				contractProduct := Contract.ContractProduct{
-					T_contract_number: T_number,
-					T_product_id:      product_id,
-					T_product_total:   num,
-					T_product_out:     0, // 已出库数量
-					T_State:           1, // 1-未出库 2-已部分出库 3-已全部出库
-				}
-				_, err = ContractProductDao.Add_ContractProduct(contractProduct)
-				if err != nil {
-					o.Rollback()
-					c.Data["json"] = lib.JSONS{Code: 203, Msg: "修改失败"}
-					c.ServeJSON()
-					return
-				}
-
-			} else {
+		oldContractProduct, ok := allList[product_id]
+		if !ok {
+			contractProduct := Contract.ContractProduct{
+				T_contract_number: T_number,
+				T_product_id:      product_id,
+				T_product_total:   num,
+				T_product_out:     0, // 已出库数量
+				T_State:           1, // 1-未出库 2-已部分出库 3-已全部出库
+			}
+			_, err = ContractProductDao.Add_ContractProduct(contractProduct)
+			if err != nil {
 				o.Rollback()
 				c.Data["json"] = lib.JSONS{Code: 203, Msg: "修改失败"}
 				c.ServeJSON()
 				return
+			} else {
+				continue
 			}
 		}
+
 		// 修改产品总数量
-		if cp.T_product_total == num {
+		contractProductId, _ := strconv.Atoi(strings.Split(oldContractProduct, "-")[0])
+		oldNum, _ := strconv.Atoi(strings.Split(oldContractProduct, "-")[1])
+		if oldNum == num {
 			continue
 		}
-		cp.T_product_total = num
+		cp := Contract.ContractProduct{Id: contractProductId, T_product_total: num}
 		err = ContractProductDao.Update_ContractProduct(cp, "T_product_total")
 		if err != nil {
 			o.Rollback()
@@ -473,7 +570,7 @@ func (c *ContractController) Contract_Product_List() {
 		return
 	}
 	if contract.T_type == 2 {
-		c.Data["json"] = lib.JSONS{Code: 202, Msg: "验证合同无产品列表!"}
+		c.Data["json"] = lib.JSONS{Code: 200, Msg: "ok!"}
 		c.ServeJSON()
 		return
 	}
@@ -492,3 +589,549 @@ func (c *ContractController) Contract_Product_List() {
 	c.ServeJSON()
 	return
 }
+
+// 检查需要删除的合同产品列表
+func Check_NeedToDelete_Product_List(list []string, newList []Contract.ContractProduct) (needToDelete map[string]int, all map[int]string) {
+
+	var productMap = map[int]int{}
+	needToDelete = make(map[string]int)
+	all = make(map[int]string)
+	for _, v := range list {
+		product_id, _ := strconv.Atoi(strings.Split(v, ",")[0])
+		num, _ := strconv.Atoi(strings.Split(v, ",")[1])
+		productMap[product_id] = num
+	}
+
+	for _, product := range newList {
+		all[product.T_product_id] = fmt.Sprintf("%d-%d", product.Id, product.T_product_total)
+		_, ok := productMap[product.T_product_id]
+		if !ok {
+			id := fmt.Sprintf("%d-%d", product.Id, product.T_product_id)
+			needToDelete[id] = product.T_product_out
+		}
+
+	}
+	return
+
+}
+
+// 验证合同
+func (c *ContractController) VerifyContract_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_customer_id := c.GetString("T_customer_id")
+
+	// 查询
+	T_name := c.GetString("T_name")
+
+	userList, _ := NatsServer.Read_User_List_All()
+	Account.Read_User_All_Map(userList)
+
+	ContractDao := Contract.NewContract(orm.NewOrm())
+	R_List, R_cnt := ContractDao.Read_VerifyContract_List(T_name, T_customer_id, 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 *ContractController) VerifyContract_Add() {
+
+	T_number := c.GetString("T_number")
+	T_customer_id := c.GetString("T_customer_id")
+	T_money, _ := c.GetFloat("T_money")
+	T_discount, _ := c.GetFloat("T_discount")
+	T_date := c.GetString("T_date")
+	T_product := c.GetString("T_product")
+	T_remark := c.GetString("T_remark")
+	T_pdf := c.GetString("T_pdf")
+	T_project := c.GetString("T_project")
+	T_recoveries := c.GetString("T_recoveries")
+	T_invoice := c.GetString("T_invoice")
+	T_start_date := c.GetString("T_start_date")
+	T_end_date := c.GetString("T_end_date")
+
+	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:      c.User.T_uuid,
+		T_recoveries:  T_recoveries,
+		T_invoice:     T_invoice,
+		T_start_date:  T_start_date,
+		T_end_date:    T_end_date,
+	}
+
+	o := orm.NewOrm()
+	o.Begin()
+	ContractDao := Contract.NewContract(o)
+	ContractProductDao := Contract.NewContractProduct(o)
+
+	_, err := ContractDao.Read_Contract_ByT_date(T_customer_id, T_start_date, T_end_date)
+	if err == nil {
+		o.Rollback()
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: "合同签约时间重复!"}
+		c.ServeJSON()
+		return
+	}
+	_, err = ContractDao.Read_Contract_ByT_number(T_number)
+	if err == nil {
+		o.Rollback()
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: "合同编号重复!"}
+		c.ServeJSON()
+		return
+	}
+
+	_, err = ContractDao.Add_Contract(var_)
+	if err != nil {
+		o.Rollback()
+		c.Data["json"] = lib.JSONS{Code: 203, Msg: "添加失败"}
+		c.ServeJSON()
+		return
+	}
+
+	if len(T_product) == 0 {
+		o.Rollback()
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: "销售合同产品明细不能为空"}
+		c.ServeJSON()
+		return
+	}
+	productList := lib.SplitString(T_product, "|")
+	for _, v := range productList {
+		product_id, _ := strconv.Atoi(strings.Split(v, ",")[0])
+		num, _ := strconv.Atoi(strings.Split(v, ",")[1])
+		contractProduct := Contract.ContractProduct{
+			T_contract_number: T_number,
+			T_product_id:      product_id,
+			T_product_total:   num,
+			T_product_out:     0, // 已出库数量
+			T_State:           1, // 1-未出库 2-已部分出库 3-已全部出库
+		}
+		_, err = ContractProductDao.Add_ContractProduct(contractProduct)
+		if err != nil {
+			o.Rollback()
+			c.Data["json"] = lib.JSONS{Code: 202, Msg: "添加失败"}
+			c.ServeJSON()
+			return
+		}
+
+	}
+
+	o.Commit()
+
+	err = Update_VerifyContract_State(T_customer_id, "ADD")
+	if err != nil {
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: "更新状态失败"}
+		c.ServeJSON()
+		return
+	}
+
+	NatsServer.AddUserLogs(c.User.T_uuid, "验证合同", "添加", var_)
+
+	c.Data["json"] = lib.JSONS{Code: 200, Msg: "ok!", Data: T_number}
+	c.ServeJSON()
+	return
+}
+
+// 验证合同呢修改
+func (c *ContractController) VerifyContract_Edit() {
+	T_number := c.GetString("T_number")
+	T_money, _ := c.GetFloat("T_money")
+	T_date := c.GetString("T_date")
+	T_product := c.GetString("T_product")
+	T_remark := c.GetString("T_remark")
+	T_pdf := c.GetString("T_pdf")
+	T_project := c.GetString("T_project")
+	T_recoveries := c.GetString("T_recoveries")
+	T_invoice := c.GetString("T_invoice")
+	T_start_date := c.GetString("T_start_date")
+	T_end_date := c.GetString("T_end_date")
+
+	o := orm.NewOrm()
+	o.Begin()
+	ContractDao := Contract.NewContract(o)
+	ContractProductDao := Contract.NewContractProduct(o)
+
+	contract, err := ContractDao.Read_Contract_ByT_number(T_number)
+	if err != nil || contract.T_type == 1 {
+		o.Rollback()
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: "T_number Err!"}
+		c.ServeJSON()
+		return
+	}
+
+	if T_money > 0 {
+		contract.T_money = float32(T_money)
+	}
+	if len(T_date) > 0 {
+		contract.T_date = T_date
+	}
+	if len(T_remark) > 0 {
+		contract.T_remark = T_remark
+	}
+	if len(T_pdf) > 0 {
+		contract.T_pdf = T_pdf
+	}
+	if len(T_project) > 0 {
+		contract.T_project = T_project
+	}
+	if len(T_recoveries) > 0 {
+		contract.T_recoveries = T_recoveries
+	}
+	if len(T_invoice) > 0 {
+		contract.T_invoice = T_invoice
+	}
+	if len(T_start_date) > 0 {
+		contract.T_start_date = T_start_date
+	}
+	if len(T_end_date) > 0 {
+		contract.T_end_date = T_end_date
+	}
+
+	err = ContractDao.Update_Contract(contract, "T_money", "T_date", "T_remark", "T_pdf", "T_State", "T_project", "T_recoveries", "T_invoice", "T_start_date", "T_end_date")
+	if err != nil {
+		o.Rollback()
+		c.Data["json"] = lib.JSONS{Code: 203, Msg: "添加失败"}
+		c.ServeJSON()
+		return
+	}
+
+	if len(T_product) == 0 {
+		o.Rollback()
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: "产品明细不能为空"}
+		c.ServeJSON()
+		return
+	}
+	productList := lib.SplitString(T_product, "|")
+
+	cpl, _ := ContractProductDao.Read_ContractProductList_ByT_number(T_number)
+	needToDeleteList, allList := Check_NeedToDelete_Product_List(productList, cpl)
+	// 检查编辑时需要修改的产品
+	for k, v := range needToDeleteList {
+		id, _ := strconv.Atoi(strings.Split(k, "-")[0])
+		productId, _ := strconv.Atoi(strings.Split(k, "-")[1])
+		if v > 0 {
+			o.Rollback()
+			product, _ := Basic.Read_Product_ById(productId)
+			c.Data["json"] = lib.JSONS{Code: 202, Msg: fmt.Sprintf("%s已(部分)出库,禁止删除!", product.T_name)}
+			c.ServeJSON()
+			return
+		}
+		cp := Contract.ContractProduct{Id: id}
+		err = ContractProductDao.Delete_ContractProduct(cp)
+		if err != nil {
+			o.Rollback()
+			c.Data["json"] = lib.JSONS{Code: 202, Msg: "修改失败!"}
+			c.ServeJSON()
+			return
+		}
+	}
+	for _, v := range productList {
+		product_id, _ := strconv.Atoi(strings.Split(v, ",")[0])
+		num, _ := strconv.Atoi(strings.Split(v, ",")[1])
+		oldContractProduct, ok := allList[product_id]
+		if !ok {
+			contractProduct := Contract.ContractProduct{
+				T_contract_number: T_number,
+				T_product_id:      product_id,
+				T_product_total:   num,
+				T_product_out:     0, // 已出库数量
+				T_State:           1, // 1-未出库 2-已部分出库 3-已全部出库
+			}
+			_, err = ContractProductDao.Add_ContractProduct(contractProduct)
+			if err != nil {
+				o.Rollback()
+				c.Data["json"] = lib.JSONS{Code: 203, Msg: "修改失败"}
+				c.ServeJSON()
+				return
+			} else {
+				continue
+			}
+		}
+
+		// 修改产品总数量
+		contractProductId, _ := strconv.Atoi(strings.Split(oldContractProduct, "-")[0])
+		oldNum, _ := strconv.Atoi(strings.Split(oldContractProduct, "-")[1])
+		if oldNum == num {
+			continue
+		}
+		cp := Contract.ContractProduct{Id: contractProductId, T_product_total: num}
+		err = ContractProductDao.Update_ContractProduct(cp, "T_product_total")
+		if err != nil {
+			o.Rollback()
+			c.Data["json"] = lib.JSONS{Code: 203, Msg: "修改失败"}
+			c.ServeJSON()
+			return
+		}
+
+	}
+
+	o.Commit()
+
+	err = Update_VerifyContract_State(contract.T_customer_id, "UPDATE")
+	if err != nil {
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: "更新状态失败"}
+		c.ServeJSON()
+		return
+	}
+
+	NatsServer.AddUserLogs(c.User.T_uuid, "验证合同", "修改", contract)
+
+	c.Data["json"] = lib.JSONS{Code: 200, Msg: "ok!", Data: contract.Id}
+	c.ServeJSON()
+	return
+}
+
+func (c *ContractController) VerifyContract_Del() {
+
+	T_number := c.GetString("T_number")
+
+	o := orm.NewOrm()
+	o.Begin()
+	ContractDao := Contract.NewContract(o)
+	ContractProductDao := Contract.NewContractProduct(o)
+
+	contract, err := ContractDao.Read_Contract_ByT_number(T_number)
+	if err != nil {
+		o.Rollback()
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: "T_number Err!"}
+		c.ServeJSON()
+		return
+	}
+	err = ContractDao.Delete_Contract(contract)
+	if err != nil {
+		o.Rollback()
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: "删除失败!"}
+		c.ServeJSON()
+		return
+	}
+
+	cpList := ContractProductDao.Read_ContractProduct_List(T_number)
+	for _, v := range cpList {
+		err = ContractProductDao.Delete_ContractProduct(v)
+		if err != nil {
+			o.Rollback()
+			c.Data["json"] = lib.JSONS{Code: 202, Msg: "删除失败!"}
+			c.ServeJSON()
+			return
+		}
+	}
+	o.Commit()
+
+	err = Update_VerifyContract_State(contract.T_customer_id, "DELETE")
+	if err != nil {
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: "修改状态失败"}
+		c.ServeJSON()
+		return
+	}
+
+	NatsServer.AddUserLogs(c.User.T_uuid, "验证合同", "删除", T_number)
+
+	c.Data["json"] = lib.JSONS{Code: 200, Msg: "ok!", Data: T_number}
+	c.ServeJSON()
+	return
+}
+
+func Update_VerifyContract_State(T_customer_id, flag string) error {
+	o := orm.NewOrm()
+	ContractDao := Contract.NewContract(o)
+	VerifyContractDao := Contract.NewVerifyContract(o)
+	vcn := ContractDao.Read_VerifyContract_Newest(T_customer_id)
+	vc, err := VerifyContractDao.Read_VerifyContract_ByT_customer_id(T_customer_id)
+	if err != nil {
+		return err
+	}
+	if vcn.Id == 0 {
+		vc.T_sign_times = 0
+		vc.T_start_date = ""
+		vc.T_end_date = ""
+		vc.T_State = 1
+		err = VerifyContractDao.Update_VerifyContract(vc, "T_start_date", "T_end_date", "T_sign_times", "T_State")
+		return err
+	}
+	if flag == "ADD" {
+		vc.T_sign_times += 1
+	} else if flag == "DELETE" {
+		vc.T_sign_times -= 1
+	}
+	if len(vc.T_start_date) == 0 {
+		vc.T_start_date = vcn.T_start_date
+	}
+	if vc.T_end_date != vcn.T_end_date {
+		vc.T_end_date = vcn.T_end_date
+	}
+	nowTime := time.Now()
+	now := time.Date(nowTime.Year(), nowTime.Month(), nowTime.Day(), 0, 0, 0, 0, time.Local)
+	end, _ := lib.DateStrToTime(vcn.T_end_date)
+	now2Month := now.AddDate(0, 2, 0)
+	// 1-未签约 2-已作废 3-已签约 4-即将到期
+	state := 1
+	// 结束时间<今天 已过期
+	if end.Before(now) {
+		state = 2
+	} else {
+		// 结束时间<今天+2月 即将到期 && 结束时间>今天
+		if end.Before(now2Month) || end.Equal(now) {
+			state = 4
+			NatsServer.AddNews(vcn.T_approver, fmt.Sprintf("【合同即将过期提醒】%s的验证合同将于%s过期!", vc.T_customer, vc.T_end_date), conf.VerifyContractUrl)
+		}
+		// 结束时间>今天+2月 已签约
+		if end.After(now2Month) || end.Equal(now2Month) {
+			state = 3
+		}
+	}
+	vc.T_State = state
+	err = VerifyContractDao.Update_VerifyContract(vc, "T_start_date", "T_end_date", "T_sign_times", "T_State")
+	return err
+}
+
+// 验证合同-添加客户
+func (c *ContractController) VerifyContract_Add_Customer() {
+
+	T_customer := c.GetString("T_customer")
+
+	o := orm.NewOrm()
+	VerifyContractDao := Contract.NewVerifyContract(o)
+
+	T_number := ""
+	for true {
+		rand_x := 1
+		T_number = fmt.Sprintf("YZKH-%s", lib.GetRandstring(8, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", int64(rand_x)))
+		_, err := VerifyContractDao.Read_VerifyContract_ByT_customer_id(T_number)
+		if err != nil && err.Error() == orm.ErrNoRows.Error() {
+			break
+		}
+		rand_x += 1
+	}
+
+	var_ := Contract.VerifyContract{
+		T_customer:    T_customer,
+		T_customer_id: T_number,
+		T_State:       1,
+	}
+
+	_, err := VerifyContractDao.Add_VerifyContract(var_)
+	if err != nil {
+		o.Rollback()
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: "添加失败!"}
+		c.ServeJSON()
+		return
+	}
+
+	NatsServer.AddUserLogs(c.User.T_uuid, "验证合同", "添加客户", var_)
+
+	c.Data["json"] = lib.JSONS{Code: 200, Msg: "ok!", Data: T_number}
+	c.ServeJSON()
+	return
+}
+
+// 验证合同-编辑客户
+func (c *ContractController) VerifyContract_Update_Customer() {
+
+	T_customer_id := c.GetString("T_customer_id")
+	T_customer := c.GetString("T_customer")
+
+	o := orm.NewOrm()
+	VerifyContractDao := Contract.NewVerifyContract(o)
+	vc, err := VerifyContractDao.Read_VerifyContract_ByT_customer_id(T_customer_id)
+
+	vc.T_customer = T_customer
+
+	err = VerifyContractDao.Update_VerifyContract(vc)
+	if err != nil {
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: "添加失败!"}
+		c.ServeJSON()
+		return
+	}
+
+	NatsServer.AddUserLogs(c.User.T_uuid, "验证合同", "编辑客户", vc)
+
+	c.Data["json"] = lib.JSONS{Code: 200, Msg: "ok!", Data: T_customer_id}
+	c.ServeJSON()
+	return
+}
+
+// 验证合同-客户列表
+func (c *ContractController) VerifyContract_Customer_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_name := c.GetString("T_name")
+	T_state, _ := c.GetInt("T_state")
+
+	VerifyContractDao := Contract.NewVerifyContract(orm.NewOrm())
+	R_List, R_cnt := VerifyContractDao.Read_VerifyContract_List(T_name, T_state, 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 Cron_VerifyContract() {
+
+	//创建一个定时任务对象
+	c := cron.New(cron.WithSeconds())
+	//给对象增加定时任务
+	//c.AddFunc("0 */1 * * * ?", Cron_VerifyContract_ChangeState)
+	c.AddFunc("@daily", Cron_VerifyContract_ChangeState)
+
+	//启动定时任务
+	c.Start()
+	defer c.Stop()
+
+	//查询语句,阻塞,让main函数不退出,保持程序运行
+	select {}
+
+}
+
+// 修改合同状态
+func Cron_VerifyContract_ChangeState() {
+
+	T_date := time.Now().Format("2006-01-02")
+	logs.Info("开始处理" + T_date + "验证合同状态")
+	VerifyContractDao := Contract.NewVerifyContract(orm.NewOrm())
+	R_List, _ := VerifyContractDao.Read_VerifyContract_List("", 0, 0, 999)
+	for _, v := range R_List {
+		err := Update_VerifyContract_State(v.T_customer_id, "UPDATE")
+		if err != nil {
+			logs.Error("修改合同状态失败")
+		}
+	}
+}

+ 119 - 43
controllers/Stock.go

@@ -63,6 +63,52 @@ func (c *StockController) Device_List() {
 	return
 }
 
+func (c *StockController) Device_Check() {
+
+	T_sn := c.GetString("T_sn")
+	T_type, _ := c.GetInt("T_type") //1-出库 2-入库
+
+	DeviceDao := Stock.NewDevice(orm.NewOrm())
+	device, err := DeviceDao.Read_Device_ByT_sn(T_sn)
+	if err != nil && err.Error() != orm.ErrNoRows.Error() {
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: "请稍后重试!"}
+		c.ServeJSON()
+		return
+	}
+	// T_State 1-已出库 2-未出库/入库
+
+	if len(device.T_in_number) > 0 && device.T_State == 2 && T_type == 2 {
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: fmt.Sprintf("【%s】已入库,请勿重复提交!", T_sn)}
+		c.ServeJSON()
+		return
+	}
+	if T_type == 2 {
+		mqtt := Stock.Read_MqttUser(T_sn)
+		if len(mqtt.Username) == 0 {
+			c.Data["json"] = lib.JSONS{Code: 202, Msg: fmt.Sprintf("【%s】未授权,请先授权!", T_sn)}
+			c.ServeJSON()
+			return
+		}
+	}
+
+	if len(device.T_out_number) > 0 && device.T_State == 1 && T_type == 1 {
+		c.Data["json"] = lib.JSONS{Code: 202, Msg: fmt.Sprintf("【%s】已出库,请勿重复提交!", T_sn)}
+		c.ServeJSON()
+		return
+	}
+	if T_type == 1 {
+		if device.Id == 0 {
+			c.Data["json"] = lib.JSONS{Code: 202, Msg: fmt.Sprintf("【%s】未入库,请先入库!", T_sn)}
+			c.ServeJSON()
+			return
+		}
+	}
+
+	c.Data["json"] = lib.JSONS{Code: 200, Msg: "ok!"}
+	c.ServeJSON()
+	return
+}
+
 func (c *StockController) Stock_List() {
 
 	// 分页参数 初始化
@@ -110,7 +156,7 @@ func (c *StockController) Stock_Detail_List() {
 	T_end_date := c.GetString("T_end_date")
 	now := time.Now()
 	if len(T_start_date) == 0 {
-		T_start_date = time.Date(now.Year(), 1, 0, 0, 0, 0, 0, time.Local).Format("2006-01")
+		T_start_date = time.Date(now.Year(), 1, 1, 0, 0, 0, 0, time.Local).Format("2006-01")
 	}
 	if len(T_end_date) == 0 {
 		T_start_date = now.Format("2006-01")
@@ -128,18 +174,29 @@ func (c *StockController) Stock_Detail_Excel() {
 
 	// 查询
 	T_depot_id, _ := c.GetInt("T_depot_id")
+	T_product_id, _ := c.GetInt("T_product_id")
 	// 查询
 	T_start_date := c.GetString("T_start_date")
 	T_end_date := c.GetString("T_end_date")
 	now := time.Now()
 	if len(T_start_date) == 0 {
-		T_start_date = time.Date(now.Year(), 1, 0, 0, 0, 0, 0, time.Local).Format("2006-01")
+		T_start_date = time.Date(now.Year(), 1, 1, 0, 0, 0, 0, time.Local).Format("2006-01")
 	}
 	if len(T_end_date) == 0 {
 		T_start_date = now.Format("2006-01")
 	}
+	var class_List []Basic.ProductClass_R
+
+	filename := fmt.Sprintf("进销存(%s)", lib.GetRandstring(6, "0123456789", 0))
 
-	class_List, _ := Basic.Read_ProductClass_List("", 0, 9999)
+	if T_product_id > 0 {
+		product, _ := Basic.Read_Product_ById(T_product_id)
+		filename = fmt.Sprintf("进销存-%s明细(%s)", product.T_name, lib.GetRandstring(6, "0123456789", 0))
+		class, _ := Basic.Read_ProductClass_ById(product.T_class)
+		class_List = append(class_List, Basic.ProductClassToProductClass_R(class))
+	} else {
+		class_List, _ = Basic.Read_ProductClass_List("", 0, 9999)
+	}
 	f := excelize.NewFile() // 设置单元格的值
 	Style1, _ := f.NewStyle(
 		&excelize.Style{
@@ -161,6 +218,12 @@ func (c *StockController) Stock_Detail_Excel() {
 
 	StockMonthDao := Stock.NewStockMonth(orm.NewOrm())
 	for i, r := range class_List {
+
+		StockList := StockMonthDao.Read_StockMonth_List(T_depot_id, T_product_id, r.Id, T_start_date, T_end_date)
+		if len(StockList) == 0 {
+			continue
+		}
+
 		if i == 0 {
 			f.SetSheetName("Sheet1", r.T_name)
 		} else {
@@ -198,16 +261,13 @@ func (c *StockController) Stock_Detail_Excel() {
 		f.SetColWidth(r.T_name, "J", "J", 10)
 		f.SetColWidth(r.T_name, "K", "K", 10)
 		line := 2
-		StockList := StockMonthDao.Read_StockMonth_List(T_depot_id, 0, r.Id, T_start_date, T_end_date)
-		if len(StockList) == 0 {
-			continue
-		}
+
 		// 循环写入数据
 		for _, v := range StockList {
 			line++
 			product, _ := Basic.Read_Product_ById(v.T_product_id)
 
-			f.SetCellValue(r.T_name, fmt.Sprintf("A%d", line), line-1)
+			f.SetCellValue(r.T_name, fmt.Sprintf("A%d", line), line-2)
 			f.SetCellValue(r.T_name, fmt.Sprintf("B%d", line), product.T_name)
 			f.SetCellValue(r.T_name, fmt.Sprintf("C%d", line), r.T_name)
 			f.SetCellValue(r.T_name, fmt.Sprintf("D%d", line), product.T_spec)
@@ -234,7 +294,6 @@ func (c *StockController) Stock_Detail_Excel() {
 		f.SetCellStyle(r.T_name, "A2", fmt.Sprintf("K%d", line), Style4)
 
 	}
-	filename := fmt.Sprintf("进销存(%s)", lib.GetRandstring(6, "0123456789", 0))
 
 	// 保存文件
 	if err := f.SaveAs("ofile/" + filename + ".xlsx"); err != nil {
@@ -394,16 +453,19 @@ func (c *StockController) StockIn_Add() {
 			return
 		}
 		if len(T_relation_sn) > 0 {
-			snList := strings.Split(T_relation_sn, ",")
+			snList := strings.Split(strings.Trim(T_relation_sn, ","), ",")
 			for _, sn := range snList {
 				mqtt := Stock.Read_MqttUser(sn)
+				// 添加设备
 				device := Stock.Device{
-					T_product_id: product_id,
-					T_in_number:  T_number,
-					T_sn:         sn,
-					T_iccid:      mqtt.Iccid,
-					T_imei:       mqtt.Imei,
-					T_State:      2,
+					T_contract_number: "",
+					T_out_number:      "",
+					T_product_id:      product_id,
+					T_in_number:       T_number,
+					T_sn:              sn,
+					T_iccid:           mqtt.Iccid,
+					T_imei:            mqtt.Imei,
+					T_State:           2,
 				}
 				_, err = DeviceDao.AddOrUpdate_Device(device, 2)
 				if err != nil {
@@ -413,12 +475,13 @@ func (c *StockController) StockIn_Add() {
 					return
 				}
 
+				// 添加物联网卡
 				iotCard := Property.IotCard{
 					T_iccid: mqtt.Iccid,
 					T_sn:    sn,
 					T_State: 2, //1-未使用 2-已使用 3-已作废
+					T_type:  "4G",
 				}
-
 				_, err = IotCardDao.AddOrUpdate_IotCard(iotCard)
 				if err != nil {
 					o.Rollback()
@@ -673,6 +736,14 @@ func (c *StockController) StockOut_Add() {
 		// 4、更改合同产品列表清单
 		contractProduct, _ := ContractProductDao.Read_ContractProduct_ByT_number_T_product_id(T_contract_number, product_id)
 		contractProduct.T_product_out += num
+
+		if contractProduct.T_product_out > contractProduct.T_product_total && T_type == 2 {
+			o.Rollback()
+			p, _ := Basic.Read_Product_ById(product_id)
+			c.Data["json"] = lib.JSONS{Code: 202, Msg: fmt.Sprintf("【%s】出库数量超过预计出库数量!", p.T_name)}
+			c.ServeJSON()
+			return
+		}
 		contractProduct.T_State = 2
 		if contractProduct.T_product_out == 0 {
 			contractProduct.T_State = 1
@@ -688,32 +759,7 @@ func (c *StockController) StockOut_Add() {
 			return
 		}
 
-		// 5、更新合同出库状态
-		var T_out int
-		if contract.T_State == 1 {
-			T_out = 2
-			// 查询合同产品清单是否全部都为已出库
-			state := ContractProductDao.Read_ContractProduct_T_State_List(contract.T_number)
-			if len(state) == 1 {
-				if state[0] == 1 {
-					T_out = 1
-				}
-				if state[0] == 3 {
-					T_out = 3
-				}
-			}
-		}
-		if T_out != contract.T_out {
-			err = ContractDao.Update_Contract(contract, "T_out")
-			if err != nil {
-				o.Rollback()
-				c.Data["json"] = lib.JSONS{Code: 202, Msg: "更新合同出库状态失败"}
-				c.ServeJSON()
-				return
-			}
-		}
-
-		// 6、更新产品库存列表
+		// 5、更新产品库存列表
 		_, err = StockDao.AddOrUpdate_Stock(T_depot_id, product.Id, product.T_class, product.T_name, product.T_model, num, 1)
 		if err != nil {
 			o.Rollback()
@@ -726,6 +772,36 @@ func (c *StockController) StockOut_Add() {
 
 	o.Commit()
 
+	o2 := orm.NewOrm()
+	o2.Begin()
+	ContractDao2 := Contract.NewContract(o2)
+	ContractProductDao2 := Contract.NewContractProduct(o2)
+	// 5、更新合同出库状态
+	var T_out int
+	if contract.T_State == 1 {
+		T_out = 2
+		// 查询合同产品清单是否全部都为已出库
+		state := ContractProductDao2.Read_ContractProduct_T_State_List(contract.T_number)
+		if state == 1 {
+			T_out = 1
+		}
+		if state == 3 {
+			T_out = 3
+		}
+
+	}
+	if T_out != contract.T_out {
+		contract.T_out = T_out
+		err = ContractDao2.Update_Contract(contract, "T_out")
+		if err != nil {
+			o2.Rollback()
+			c.Data["json"] = lib.JSONS{Code: 202, Msg: "更新合同出库状态失败"}
+			c.ServeJSON()
+			return
+		}
+	}
+	o2.Commit()
+
 	NatsServer.AddUserLogs(c.User.T_uuid, "出库", "出库", var_)
 
 	c.Data["json"] = lib.JSONS{Code: 200, Msg: "ok!", Data: T_number}

+ 2 - 0
go.mod

@@ -16,6 +16,7 @@ require (
 )
 
 require (
+	baliance.com/gooxml v1.0.1 // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/cespare/xxhash/v2 v2.1.2 // indirect
 	github.com/golang/protobuf v1.5.2 // indirect
@@ -40,6 +41,7 @@ require (
 	github.com/richardlehane/mscfb v1.0.4 // indirect
 	github.com/richardlehane/msoleps v1.0.3 // indirect
 	github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 // indirect
+	github.com/shopspring/decimal v1.3.1 // indirect
 	github.com/signintech/gopdf v0.16.1 // indirect
 	github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
 	github.com/xuri/efp v0.0.0-20220603152613-6918739fd470 // indirect

+ 4 - 0
go.sum

@@ -1,3 +1,5 @@
+baliance.com/gooxml v1.0.1 h1:fG5lmxmjEVFfbKQ2NuyCuU3hMuuOb5avh5a38SZNO1o=
+baliance.com/gooxml v1.0.1/go.mod h1:+gpUgmkAF4zCtwOFPNRLDAvpVRWoKs5EeQTSv/HYFnw=
 cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
@@ -283,6 +285,8 @@ github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6po
 github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg=
 github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 h1:DAYUYH5869yV94zvCES9F51oYtN5oGlwjxJJz7ZCnik=
 github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg=
+github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
+github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
 github.com/siddontang/go v0.0.0-20170517070808-cb568a3e5cc0/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw=
 github.com/siddontang/goredis v0.0.0-20150324035039-760763f78400/go.mod h1:DDcKzU3qCuvj/tPnimWSsZZzvk9qvkvrIL5naVBPh5s=
 github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA=

+ 1 - 0
main.go

@@ -71,6 +71,7 @@ func main() {
 
 	go Basic.Read_ProductClass_All_Map()  // 初始化产品类型
 	go controllers.Cron_StockMonth()      // 库存明细详情定时任务
+	go controllers.Cron_VerifyContract()  // 验证合同状态修改定时任务
 	go controllers.CheckPowerUniformity() // 检查角色是否与用户系统一致,不存在则添加
 	beego.Run()
 

+ 32 - 12
models/Basic/Product.go

@@ -21,12 +21,13 @@ type Product struct {
 	Id            int       `orm:"column(ID);size(11);auto;pk"`
 	T_name        string    `orm:"size(256);null"`                                        // 名称
 	T_img         string    `orm:"size(256);null"`                                        // 图片
-	T_class       int       `orm:"size(20);default(0)"`                                   // 产品分类
+	T_class       int       `orm:"size(20);default(0)"`                                   // 产品分类 T_class=0 冷链验证
 	T_model       string    `orm:"size(20);default(0)"`                                   // 产品型号
 	T_spec        string    `orm:"size(20);default(0)"`                                   // 产品规格
 	T_relation_sn int       `orm:"size(2);default(0)"`                                    // 关联sn 0-否 1-是
 	T_State       int       `orm:"size(2);default(1)"`                                    // 0 删除(伪删除)   1 正常
 	T_remark      string    `orm:"type(text);null"`                                       // 备注
+	T_price       float32   `orm:"digits(12);decimals(2)"`                                // 单价
 	CreateTime    time.Time `orm:"column(create_time);type(timestamp);null;auto_now_add"` //auto_now_add 第一次保存时才设置时间
 	UpdateTime    time.Time `orm:"column(update_time);type(timestamp);null;auto_now"`     //auto_now 每次 model 保存时都会对时间自动更新
 }
@@ -96,14 +97,15 @@ func Redis_Product_DelK(Id int) (err error) {
 
 type Product_R struct {
 	Id            int
-	T_name        string // 名称
-	T_class       int    // 产品分类
-	T_class_name  string // 产品分类名称
-	T_model       string // 产品型号
-	T_spec        string // 产品规格
-	T_relation_sn int    // 关联sn 0-否 1-是
-	T_img         string // 图片
-	T_remark      string // 备注
+	T_name        string  // 名称
+	T_class       int     // 产品分类
+	T_class_name  string  // 产品分类名称
+	T_model       string  // 产品型号
+	T_spec        string  // 产品规格
+	T_price       float32 // 单价
+	T_relation_sn int     // 关联sn 0-否 1-是
+	T_img         string  // 图片
+	T_remark      string  // 备注
 }
 
 func ProductToProduct_R(t Product) (r Product_R) {
@@ -114,6 +116,7 @@ func ProductToProduct_R(t Product) (r Product_R) {
 	r.T_class_name = Read_ProductClass_Get(t.T_class)
 	r.T_model = t.T_model
 	r.T_spec = t.T_spec
+	r.T_price = t.T_price
 	r.T_relation_sn = t.T_relation_sn
 	r.T_img = t.T_img
 	r.T_remark = t.T_remark
@@ -150,6 +153,18 @@ func Read_Product_ById(Id int) (r Product, err error) {
 	return
 }
 
+func Read_Product_ByT_name(T_name, T_model, T_spec string) (r Product, err error) {
+
+	o := orm.NewOrm()
+	qs := o.QueryTable(new(Product))
+	err = qs.Filter("T_name", T_name).Filter("T_model", T_model).Filter("T_spec", T_spec).Filter("T_class__gt", 0).One(&r)
+	if err != nil {
+		logs.Error(lib.FuncName(), err)
+		return
+	}
+	return
+}
+
 // 修改
 func Update_Product(m Product, cols ...string) error {
 	o := orm.NewOrm()
@@ -174,7 +189,7 @@ func Delete_Product(v Product) error {
 }
 
 // 获取列表
-func Read_Product_List(T_name, T_model string, T_class, page, page_z int) (r_ []Product_R, cnt int64) {
+func Read_Product_List(T_name, T_model string, T_class, T_VerifyContract, page, page_z int) (r_ []Product_R, cnt int64) {
 	o := orm.NewOrm()
 	// 也可以直接使用 Model 结构体作为表名
 	qs := o.QueryTable(new(Product))
@@ -188,6 +203,11 @@ func Read_Product_List(T_name, T_model string, T_class, page, page_z int) (r_ []
 	// 过滤
 	cond := orm.NewCondition()
 	cond1 := cond.And("T_State", 1)
+	if T_VerifyContract == 1 {
+		cond1 = cond1.And("T_class", 0)
+	} else {
+		cond1 = cond1.And("T_class__gt", 0)
+	}
 
 	if T_class > 0 {
 		cond1 = cond1.And("T_class", T_class)
@@ -227,7 +247,7 @@ func Read_Product_List(T_name, T_model string, T_class, page, page_z int) (r_ []
 func Read_ProductModel_List(T_name string) (lists orm2.ParamsList) {
 	o := orm.NewOrm()
 	var pl_lists orm2.ParamsList
-	_, err := o.Raw("SELECT t_model FROM product WHERE t_name = '" + T_name + "' LIMIT 0,1000").ValuesFlat(&pl_lists)
+	_, err := o.Raw("SELECT t_model FROM product WHERE t_class > 0 AND t_name = '" + T_name + "' LIMIT 0,1000").ValuesFlat(&pl_lists)
 	if err != nil {
 		logs.Error(lib.FuncName(), err)
 		return
@@ -240,7 +260,7 @@ func Read_ProductModel_List(T_name string) (lists orm2.ParamsList) {
 func Read_ProductName_List(T_name string, T_class int) (lists orm2.ParamsList) {
 	o := orm.NewOrm()
 	var pl_lists orm2.ParamsList
-	sql := "SELECT DISTINCT t_name FROM product WHERE t_name like '%" + T_name + "%'"
+	sql := "SELECT DISTINCT t_name FROM product WHERE t_class > 0 AND t_name like '%" + T_name + "%'"
 	if T_class > 0 {
 		sql += " AND T_class = " + strconv.Itoa(T_class)
 	}

+ 1 - 1
models/Basic/ProductClass.go

@@ -102,7 +102,7 @@ func Read_ProductClass_List(T_name string, page, page_z int) (r_ []ProductClass_
 
 	// 过滤
 	cond := orm.NewCondition()
-	cond = cond.And("T_State", 1)
+	cond = cond.And("T_State", 1).And("Id__gt", 0)
 
 	if len(T_name) > 0 {
 		cond = cond.And("T_name__icontains", T_name)

+ 210 - 42
models/Contract/Contract.go

@@ -5,6 +5,7 @@ import (
 	"ERP_storage/models/Account"
 	"git.baozhida.cn/ERP_libs/lib"
 	orm2 "github.com/beego/beego/v2/client/orm"
+	"strings"
 	"time"
 
 	_ "github.com/astaxie/beego/cache/redis"
@@ -14,16 +15,23 @@ import (
 
 // 合同
 type Contract struct {
-	Id         int     `orm:"column(ID);size(11);auto;pk"`
-	T_number   string  `orm:"size(256);null"`         // 合同编号
-	T_customer string  `orm:"size(256);null"`         // 客户名称
-	T_money    float32 `orm:"digits(12);decimals(2)"` // 合同金额
-	T_type     int     `orm:"size(2);default(1)"`     // 合同类型 1-销售合同 2-验证合同
-	T_date     string  `orm:"size(256);null"`         // 业务日期
-	T_out      int     `orm:"size(2);default(1)"`     // 出库状态  1-未出库 2-已部分出库 3-已全部出库
-	T_State    int     `orm:"size(2);default(1)"`     // 0 删除(伪删除)  1-已通过 2-未通过 3-待审核 审核状态
-	T_pdf      string  `orm:"size(256);null"`         // 附件
-	T_remark   string  `orm:"type(text);null"`        // 备注
+	Id            int     `orm:"column(ID);size(11);auto;pk"`
+	T_number      string  `orm:"size(256);null"`         // 合同编号
+	T_customer    string  `orm:"size(256);null"`         // 客户名称
+	T_customer_id string  `orm:"size(256);null"`         // 客户id
+	T_money       float32 `orm:"digits(12);decimals(2)"` // 合同金额
+	T_discount    float32 `orm:"digits(12);decimals(2)"` // 合同优惠价
+	T_project     string  `orm:"size(256);null"`         // 项目
+	T_type        int     `orm:"size(2);default(1)"`     // 合同类型 1-销售合同 2-验证合同
+	T_date        string  `orm:"size(256);null"`         // 签订时间
+	T_out         int     `orm:"size(2);default(1)"`     // 出库状态  1-未出库 2-部分出库 3-已出库
+	T_State       int     `orm:"size(2);default(1)"`     // 0 删除(伪删除)  1-已通过 2-未通过 3-待审核 审核状态
+	T_pdf         string  `orm:"size(256);null"`         // 附件
+	T_remark      string  `orm:"type(text);null"`        // 备注
+	T_recoveries  string  `orm:"type(text);null"`        // 回款信息 回款时间,回款金额|回款时间,回款金额|
+	T_invoice     string  `orm:"type(text);null"`        // 开票信息 开票时间,开票金额|开票时间,开票金额|
+	T_start_date  string  `orm:"size(256);null"`         // 起始时间
+	T_end_date    string  `orm:"size(256);null"`         // 终止时间
 
 	T_submit   string `orm:"size(256);null"` // 合同提交人员
 	T_approver string `orm:"size(256);null"` // 审批人
@@ -54,8 +62,9 @@ type Contract_R struct {
 	T_number        string  // 合同编号
 	T_customer      string  // 客户名称
 	T_money         float32 // 合同金额
+	T_discount      float32 // 合同优惠价
 	T_type          int     // 合同类型 1-销售合同 2-验证合同
-	T_date          string  // 业务日期
+	T_date          string  // 签订时间
 	T_out           int     // 出库状态  1-未出库 2-已部分出库 3-已全部出库
 	T_submit        string  // 合同提交人员uuid
 	T_submit_name   string  // 合同提交人员名称
@@ -66,21 +75,64 @@ type Contract_R struct {
 }
 
 type Contract_Detail struct {
-	Id              int
-	T_number        string
-	T_customer      string
-	T_money         float32
-	T_type          int
-	T_date          string
-	T_submit        string
-	T_submit_name   string
-	T_approver      string // 审批人uuid
-	T_approver_name string // 审批人名称
-	T_out           int    // 出库状态  1-未出库 2-已部分出库 3-已全部出库
-	T_State         int
-	T_remark        string
-	T_pdf           string
-	T_Product       []ContractProduct_R
+	Id                    int
+	T_number              string
+	T_customer            string  // 客户名称
+	T_money               float32 // 合同金额
+	T_discount            float32 // 合同优惠价
+	T_project             string  // 项目
+	T_type                int     // 合同类型 1-销售合同 2-验证合同
+	T_date                string  // 签订时间
+	T_submit              string  // 合同提交人员uuid
+	T_submit_name         string  // 合同提交人员名称
+	T_approver            string  // 审批人uuid
+	T_approver_name       string  // 审批人名称
+	T_out                 int     // 出库状态  1-未出库 2-已部分出库 3-已全部出库
+	T_State               int     // 1-已通过 2-未通过 3-待审核 审核状态
+	T_remark              string  // 备注
+	T_pdf                 string  // 附件
+	T_start_date          string  // 起始时间
+	T_end_date            string  // 终止时间
+	T_Product             []ContractProduct_R
+	T_recoveries          []ContractFinance_R // 回款信息 回款时间,回款金额|回款时间,回款金额|
+	T_no_recoveries_money float32             // 未回款金额
+	T_invoice             []ContractFinance_R // 开票信息 开票时间,开票金额|开票时间,开票金额|
+	T_no_invoice_money    float32             // 未开票金额
+}
+type ContractFinance_R struct {
+	T_date  string  // 时间
+	T_money float32 // 金额)
+}
+
+type Contract_Verify struct {
+	Id             int
+	T_number       string
+	T_money        float32 // 合同金额
+	T_discount     float32 // 合同优惠价
+	T_date         string  // 签订时间
+	T_start_date   string  // 起始时间
+	T_end_date     string  // 终止时间
+	T_verify_state int     // 1-未签约 2-已作废 3-已签约 4-即将到期
+}
+
+func ContractToContractFinance_R(T_detail string) (r []ContractFinance_R, money float32) {
+
+	if len(T_detail) == 0 {
+		return
+	}
+	detailList := strings.Split(strings.Trim(T_detail, "|"), "|")
+	for _, detail := range detailList {
+		T_date := strings.Split(detail, ",")[0]
+		T_money := strings.Split(detail, ",")[1]
+		temp := float32(lib.StringToFloat64TwoDecimal(T_money))
+		money += temp
+		r = append(r, ContractFinance_R{
+			T_date:  T_date,
+			T_money: temp,
+		})
+
+	}
+	return r, money
 }
 
 func ContractToContract_R(t Contract) (r Contract_R) {
@@ -117,24 +169,11 @@ func ContractToContract_R(t Contract) (r Contract_R) {
 func ContractToContract_Detail(t Contract, productList []ContractProduct_R) (r Contract_Detail) {
 	// 1-未出库 2-已部分出库 3-已全部出库
 	r.T_out = t.T_out
-	// 1-已通过 2-未通过 3-待审核
-	//if t.T_State == 1 {
-	//	r.T_out = 2
-	//	state := Read_ContractProduct_T_State_List(t.T_number)
-	//	if len(state) == 1 {
-	//		if state[0] == 1 {
-	//			r.T_out = 1
-	//		}
-	//		if state[0] == 3 {
-	//			r.T_out = 3
-	//		}
-	//	}
-	//}
-
 	r.Id = t.Id
 	r.T_number = t.T_number
 	r.T_customer = t.T_customer
 	r.T_money = t.T_money
+	r.T_project = t.T_project
 	r.T_type = t.T_type
 	r.T_date = t.T_date
 	r.T_State = t.T_State
@@ -145,6 +184,53 @@ func ContractToContract_Detail(t Contract, productList []ContractProduct_R) (r C
 	r.T_approver = t.T_approver
 	r.T_approver_name = Account.Read_User_T_name_Get(t.T_approver)
 	r.T_Product = productList
+	r.T_start_date = t.T_start_date
+	r.T_end_date = t.T_end_date
+
+	recoveriesList, recoveries := ContractToContractFinance_R(t.T_recoveries)
+	r.T_recoveries = recoveriesList
+	r.T_no_recoveries_money = t.T_money - recoveries
+
+	invoiceList, invoice := ContractToContractFinance_R(t.T_invoice)
+	r.T_invoice = invoiceList
+	r.T_no_invoice_money = t.T_money - invoice
+
+	return r
+}
+
+func ContractToContract_Verify(t Contract) (r Contract_Verify) {
+	r.Id = t.Id
+	r.T_number = t.T_number
+	r.T_money = t.T_money
+	r.T_discount = t.T_discount
+	r.T_date = t.T_date
+	r.T_start_date = t.T_start_date
+	r.T_end_date = t.T_end_date
+	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
+		}
+	}
+
+	r.T_verify_state = state
 
 	return r
 }
@@ -211,7 +297,7 @@ func (dao *ContractDaoImpl) Read_Contract_List(T_submit, T_name string, T_state,
 
 	// 过滤
 	cond := orm.NewCondition()
-	cond1 := cond.And("T_State__gt", 0)
+	cond1 := cond.And("T_State__gt", 0).And("T_type", 1)
 	if T_state > 0 {
 		cond1 = cond.And("T_State", T_state)
 	}
@@ -226,7 +312,7 @@ func (dao *ContractDaoImpl) Read_Contract_List(T_submit, T_name string, T_state,
 
 	// T_out == 1 查询未出库或部分出库数据
 	if T_out == 1 {
-		cond1 = cond1.AndNot("T_out", 3)
+		cond1 = cond1.And("T_out__in", []int{1, 2})
 	}
 
 	// 查询
@@ -250,3 +336,85 @@ func (dao *ContractDaoImpl) Read_Contract_List(T_submit, T_name string, T_state,
 
 	return r_, cnt
 }
+
+// 获取列表
+func (dao *ContractDaoImpl) Read_VerifyContract_List(T_name, T_customer_id string, page, page_z int) (r_ []Contract_Verify, cnt int64) {
+	// 也可以直接使用 Model 结构体作为表名
+	qs := dao.orm.QueryTable(new(Contract))
+	var offset int64
+	if page <= 1 {
+		offset = 0
+	} else {
+		offset = int64((page - 1) * page_z)
+	}
+
+	// 过滤
+	cond := orm.NewCondition()
+	cond1 := cond.And("T_State__gt", 0).And("T_type", 2).And("T_customer_id", T_customer_id)
+	if len(T_name) > 0 {
+		cond1 = cond1.AndCond(cond.Or("T_number__icontains", T_name).Or("T_customer__icontains", T_name))
+	}
+
+	// 查询
+	var r []Contract
+	var err error
+	if page_z == 9999 {
+		_, err = qs.SetCond((*orm2.Condition)(cond1)).OrderBy("-T_end_date").All(&r)
+	} else {
+		_, err = qs.Limit(page_z, offset).SetCond((*orm2.Condition)(cond1)).OrderBy("-T_end_date").All(&r)
+	}
+
+	if err != nil {
+		logs.Error(lib.FuncName(), err)
+		return
+	}
+
+	cnt, err = qs.SetCond((*orm2.Condition)(cond1)).Count()
+	if err != nil {
+		logs.Error(lib.FuncName(), err)
+		return
+	}
+
+	for _, v := range r {
+		r_ = append(r_, ContractToContract_Verify(v))
+	}
+
+	return r_, cnt
+}
+
+// 获取最新的验证合同
+func (dao *ContractDaoImpl) Read_VerifyContract_Newest(T_customer_id string) (r_ Contract) {
+	// 也可以直接使用 Model 结构体作为表名
+	qs := dao.orm.QueryTable(new(Contract))
+
+	// 过滤
+	cond := orm.NewCondition()
+	cond1 := cond.And("T_State__gt", 0).And("T_type", 2).And("T_customer_id", T_customer_id)
+
+	// 查询
+	var r []Contract
+	_, err := qs.SetCond((*orm2.Condition)(cond1)).OrderBy("-T_end_date").All(&r)
+
+	if err != nil {
+		logs.Error(lib.FuncName(), err)
+		return
+	}
+
+	if len(r) == 0 {
+		return
+	}
+
+	return r[0]
+}
+
+// 获取验证合同
+func (dao *ContractDaoImpl) Read_Contract_ByT_date(T_customer_id, T_start_date, T_end_date string) (r Contract, err error) {
+	qs := dao.orm.QueryTable(new(Contract))
+
+	err = qs.Filter("T_customer_id", T_customer_id).Filter("T_start_date", T_start_date).Filter("T_end_date", T_end_date).
+		Filter("T_type", 2).Filter("T_State__gt", 0).One(&r)
+	if err != nil {
+		logs.Error(lib.FuncName(), err)
+	}
+	return
+}

+ 30 - 13
models/Contract/ContractProduct.go

@@ -3,8 +3,10 @@ package Contract
 import (
 	"ERP_storage/logs"
 	"ERP_storage/models/Basic"
+	"fmt"
 	"git.baozhida.cn/ERP_libs/lib"
 	orm2 "github.com/beego/beego/v2/client/orm"
+	"strconv"
 	"time"
 
 	_ "github.com/astaxie/beego/cache/redis"
@@ -24,6 +26,14 @@ type ContractProduct struct {
 	UpdateTime        time.Time `orm:"column(update_time);type(timestamp);null;auto_now"`     //auto_now 每次 model 保存时都会对时间自动更新
 }
 
+type ContractProductInfo struct {
+	Id              int
+	T_product_id    int
+	T_product_total int
+	T_product_out   int
+	T_State         int
+}
+
 func (t *ContractProduct) TableName() string {
 	return "contract_product" // 数据库名称   // ************** 替换 FormulaList **************
 }
@@ -56,6 +66,7 @@ type ContractProduct_R struct {
 	T_product_spec        string   // 产品规格
 	T_product_relation_sn int      // 关联sn 0-否 1-是
 	T_product_img         string   // 图片
+	T_price               float32  // 单价
 	T_device_list         []string // sn关联列表
 
 }
@@ -69,13 +80,14 @@ type ContractProduct_Out struct {
 	T_State           int    // 1-未出库 2-已部分出库 3-已全部出库
 
 	// ---------产品信息-----------
-	T_product_id          int    // 产品id
-	T_product_name        string // 产品分类
-	T_product_class_name  string // 产品分类名称
-	T_product_model       string // 产品型号
-	T_product_spec        string // 产品规格
-	T_product_relation_sn int    // 关联sn 0-否 1-是
-	T_product_img         string // 图片
+	T_product_id          int     // 产品id
+	T_product_name        string  // 产品分类
+	T_product_class_name  string  // 产品分类名称
+	T_product_model       string  // 产品型号
+	T_product_spec        string  // 产品规格
+	T_product_relation_sn int     // 关联sn 0-否 1-是
+	T_product_img         string  // 图片
+	T_price               float32 // 单价
 }
 
 func ContractProductToContractProduct_R(t ContractProduct) (r ContractProduct_R) {
@@ -94,6 +106,7 @@ func ContractProductToContractProduct_R(t ContractProduct) (r ContractProduct_R)
 	r.T_product_spec = product.T_spec
 	r.T_product_relation_sn = product.T_relation_sn
 	r.T_product_img = product.T_img
+	r.T_price = product.T_price
 	return r
 }
 func ContractProductToContractProduct_Out(t ContractProduct) (r ContractProduct_Out) {
@@ -111,6 +124,7 @@ func ContractProductToContractProduct_Out(t ContractProduct) (r ContractProduct_
 	r.T_product_spec = product.T_spec
 	r.T_product_relation_sn = product.T_relation_sn
 	r.T_product_img = product.T_img
+	r.T_price = product.T_price
 	return r
 }
 
@@ -127,7 +141,7 @@ func (dao *ContractProductDaoImpl) Add_ContractProduct(r ContractProduct) (id in
 func (dao *ContractProductDaoImpl) Read_ContractProductList_ByT_number(T_number string) (r []ContractProduct, err error) {
 	qs := dao.orm.QueryTable(new(ContractProduct))
 
-	_, err = qs.Filter("T_contract_number", T_number).All(&r)
+	_, err = qs.Filter("T_contract_number", T_number).Filter("T_State__gt", 0).All(&r)
 	if err != nil {
 		logs.Error(lib.FuncName(), err)
 	}
@@ -187,15 +201,18 @@ func (dao *ContractProductDaoImpl) Read_ContractProduct_List(T_number string) (r
 	return r
 }
 
-func (dao *ContractProductDaoImpl) Read_ContractProduct_T_State_List(T_number string) (lists orm2.ParamsList) {
+func (dao *ContractProductDaoImpl) Read_ContractProduct_T_State_List(T_number string) int64 {
 	o := orm.NewOrm()
 	var pl_lists orm2.ParamsList
-	sql := "SELECT DISTINCT t__state FROM contract_product WHERE t_contract_number = " + T_number
+	sql := fmt.Sprintf("SELECT DISTINCT t__state FROM contract_product WHERE t_contract_number = '%s'", T_number)
 	_, err := o.Raw(sql).ValuesFlat(&pl_lists)
 	if err != nil {
 		logs.Error(lib.FuncName(), err)
-		return
+		return 0
 	}
-
-	return pl_lists
+	if pl_lists[0] == nil {
+		return 0
+	}
+	key, _ := strconv.ParseInt(pl_lists[0].(string), 10, 64)
+	return key
 }

+ 145 - 0
models/Contract/VerifyContract.go

@@ -0,0 +1,145 @@
+package Contract
+
+import (
+	"ERP_storage/logs"
+	"git.baozhida.cn/ERP_libs/lib"
+	orm2 "github.com/beego/beego/v2/client/orm"
+	"time"
+
+	_ "github.com/astaxie/beego/cache/redis"
+	"github.com/beego/beego/v2/adapter/orm"
+	_ "github.com/go-sql-driver/mysql"
+)
+
+// 验证合同
+type VerifyContract struct {
+	Id            int    `orm:"column(ID);size(11);auto;pk"`
+	T_customer    string `orm:"size(256);null"`     // 客户名称
+	T_customer_id string `orm:"size(256);null"`     // 客户id
+	T_start_date  string `orm:"size(256);null"`     // 起始时间
+	T_end_date    string `orm:"size(256);null"`     // 终止时间
+	T_remark      string `orm:"type(text);null"`    // 备注
+	T_State       int    `orm:"size(2);default(1)"` // 0 删除(伪删除) 1-未签约 2-已作废 3-已签约 4-即将到期
+	T_sign_times  int    `orm:"size(2);default(1)"` // 签约次数
+
+	CreateTime time.Time `orm:"column(create_time);type(timestamp);null;auto_now_add"` //auto_now_add 第一次保存时才设置时间
+	UpdateTime time.Time `orm:"column(update_time);type(timestamp);null;auto_now"`     //auto_now 每次 model 保存时都会对时间自动更新
+}
+
+func (t *VerifyContract) TableName() string {
+	return "verify_contract" // 数据库名称   // ************** 替换 FormulaList **************
+}
+
+type VerifyContractDaoImpl struct {
+	orm orm.Ormer
+}
+
+func NewVerifyContract(orm orm.Ormer) *VerifyContractDaoImpl {
+	return &VerifyContractDaoImpl{orm: orm}
+}
+
+func init() {
+	//注册模型
+	orm.RegisterModel(new(VerifyContract))
+}
+
+type VerifyContract_R struct {
+	Id            int
+	T_customer    string // 客户名称
+	T_customer_id string // 客户id
+	T_start_date  string // 起始时间
+	T_end_date    string // 终止时间
+	T_remark      string // 备注
+	T_State       int    // 1-未签约 2-已作废 3-已签约 4-即将到期
+	T_sign_times  int    // 签约次数
+}
+
+func VerifyContractToVerifyContract_R(r VerifyContract) (t VerifyContract_R) {
+	t.Id = r.Id
+	t.T_customer = r.T_customer
+	t.T_customer_id = r.T_customer_id
+	t.T_start_date = r.T_start_date
+	t.T_end_date = r.T_end_date
+	t.T_remark = r.T_remark
+	t.T_State = r.T_State
+	t.T_sign_times = r.T_sign_times
+	return t
+}
+
+// 添加
+func (dao *VerifyContractDaoImpl) Add_VerifyContract(r VerifyContract) (id int64, err error) {
+	id, err = dao.orm.Insert(&r)
+	if err != nil {
+		logs.Error(lib.FuncName(), err)
+	}
+	return id, err
+}
+
+// 修改
+func (dao *VerifyContractDaoImpl) Update_VerifyContract(m VerifyContract, cols ...string) error {
+	_, err := dao.orm.Update(&m, cols...)
+	if err != nil {
+		logs.Error(lib.FuncName(), err)
+		return err
+	}
+	return nil
+}
+
+// 获取 ById
+func (dao *VerifyContractDaoImpl) Read_VerifyContract_ByT_customer_id(T_customer_id string) (r VerifyContract, err error) {
+	qs := dao.orm.QueryTable(new(VerifyContract))
+	err = qs.Filter("T_customer_id", T_customer_id).Filter("T_State__gt", 0).One(&r)
+	if err != nil {
+		logs.Error(lib.FuncName(), err)
+	}
+	return
+}
+
+// 获取列表
+func (dao *VerifyContractDaoImpl) Read_VerifyContract_List(T_name string, T_state, page, page_z int) (r_ []VerifyContract_R, cnt int64) {
+	// 也可以直接使用 Model 结构体作为表名
+	qs := dao.orm.QueryTable(new(VerifyContract))
+	var offset int64
+	if page <= 1 {
+		offset = 0
+	} else {
+		offset = int64((page - 1) * page_z)
+	}
+
+	// 过滤
+	cond := orm.NewCondition()
+	cond = cond.And("T_State__gt", 0)
+	if T_state > 0 {
+		cond = cond.And("T_State", T_state)
+	}
+
+	if len(T_name) > 0 {
+		cond = cond.And("T_customer__icontains", T_name)
+	}
+
+	// 查询
+	var r []VerifyContract
+	var err error
+	if page_z == 9999 {
+		_, err = qs.SetCond((*orm2.Condition)(cond)).OrderBy("-T_State").All(&r)
+	}else {
+		_, err = qs.Limit(page_z, offset).SetCond((*orm2.Condition)(cond)).OrderBy("-T_State").All(&r)
+	}
+
+	if err != nil {
+		logs.Error(lib.FuncName(), err)
+		return
+	}
+
+	cnt, err = qs.SetCond((*orm2.Condition)(cond)).Count()
+	if err != nil {
+		logs.Error(lib.FuncName(), err)
+		return
+	}
+
+	for _, v := range r {
+		r_ = append(r_, VerifyContractToVerifyContract_R(v))
+	}
+
+	return r_, cnt
+}

+ 27 - 14
models/Stock/Device.go

@@ -20,7 +20,7 @@ type Device struct {
 	T_sn              string    `orm:"size(256);null"`                                        // 设备sn
 	T_iccid           string    `orm:"size(256);null"`                                        // sim卡号
 	T_imei            string    `orm:"size(256);null"`                                        // 模组imei
-	T_State           int       `orm:"size(2);default(2)"`                                    // 1-已出库 2-未出库
+	T_State           int       `orm:"size(2);default(2)"`                                    // 1-已出库 2-未出库/入库
 	T_remark          string    `orm:"type(text);null"`                                       // 备注
 	CreateTime        time.Time `orm:"column(create_time);type(timestamp);null;auto_now_add"` //auto_now_add 第一次保存时才设置时间
 	UpdateTime        time.Time `orm:"column(update_time);type(timestamp);null;auto_now"`     //auto_now 每次 model 保存时都会对时间自动更新
@@ -93,13 +93,15 @@ func (dao *DeviceDaoImpl) Add_Device(r Device) (id int64, err error) {
 }
 
 // T_type 1-出库 2-入库
-func (dao *DeviceDaoImpl) AddOrUpdate_Device(r Device, T_State int) (id int64, err error) {
+func (dao *DeviceDaoImpl) AddOrUpdate_Device(r Device, T_type int) (id int64, err error) {
 
 	qs := dao.orm.QueryTable(new(Device))
 	var device Device
+
 	err = qs.Filter("T_sn", r.T_sn).One(&device)
 	if err != nil {
 		if err.Error() == orm.ErrNoRows.Error() {
+			r.T_remark = fmt.Sprintf("%s:%s(%s)|", time.Now().Format("2006-01-02"), "入库", r.T_in_number)
 			id, err = dao.orm.Insert(&r)
 			if err != nil {
 				logs.Error(lib.FuncName(), err)
@@ -111,18 +113,25 @@ func (dao *DeviceDaoImpl) AddOrUpdate_Device(r Device, T_State int) (id int64, e
 		}
 	}
 
-	// 1-已出库 2-未出库
-	device.T_State = T_State
 	T_remark := ""
-	if T_State == 1 {
-		T_remark = fmt.Sprintf("%s:%s(%s)|", time.Now().Format("2006-01-02"), "出库", device.T_out_number)
+	if T_type == 1 {
+		T_remark = fmt.Sprintf("%s:%s(%s)|", time.Now().Format("2006-01-02"), "出库", r.T_out_number)
 	}
-	if T_State == 2 {
+	if T_type == 2 {
 		T_remark = fmt.Sprintf("%s:%s(%s)|", time.Now().Format("2006-01-02"), "入库", device.T_in_number)
 	}
-	device.T_remark = device.T_remark + T_remark
 
-	_, err = dao.orm.Update(&device, "T_in_number", "T_out_number", "T_State", "T_remark")
+	r.Id = device.Id
+	// T_State 1-出库 2-入库
+	r.T_State = T_type
+	// 出库时将入库编号服务给update结构体
+	if len(r.T_in_number) == 0 && len(device.T_in_number) > 0 {
+		r.T_in_number = device.T_in_number
+	}
+
+	r.T_remark = device.T_remark + T_remark
+
+	_, err = dao.orm.Update(&r, "T_contract_number", "T_in_number", "T_out_number", "T_State", "T_remark")
 	if err != nil {
 		logs.Error(lib.FuncName(), err)
 		return
@@ -131,8 +140,8 @@ func (dao *DeviceDaoImpl) AddOrUpdate_Device(r Device, T_State int) (id int64, e
 	return
 }
 
-func (dao *DeviceDaoImpl) Read_Device_ByT_sn(T_sn string) (r Stock, err error) {
-	qs := dao.orm.QueryTable(new(Stock))
+func (dao *DeviceDaoImpl) Read_Device_ByT_sn(T_sn string) (r Device, err error) {
+	qs := dao.orm.QueryTable(new(Device))
 	err = qs.Filter("T_sn", T_sn).One(&r)
 	if err != nil {
 		logs.Error(lib.FuncName(), err)
@@ -181,15 +190,19 @@ func (dao *DeviceDaoImpl) Read_Device_List(T_name string, T_State, page, page_z
 	cond := orm.NewCondition()
 	cond1 := cond.And("T_State__gt", 0)
 	if len(T_name) > 0 {
-		cond1 = cond1.AndCond(cond.Or("T_contract_number__icontains", T_name).Or("T_out_number__icontains", T_name).Or("T_sn__icontains", T_name))
+		cond1 = cond1.AndCond(
+			cond.Or("T_contract_number__icontains", T_name).
+				Or("T_out_number__icontains", T_name).
+				Or("T_sn__icontains", T_name).
+				Or("T_iccid__icontains", T_name))
 	}
 
 	if T_State > 0 {
-		cond = cond.And("T_State", T_State)
+		cond1 = cond1.And("T_State", T_State)
 	}
 
 	var maps []Device
-	_, err := qs.Limit(page_z, offset).SetCond((*orm2.Condition)(cond)).All(&maps)
+	_, err := qs.Limit(page_z, offset).SetCond((*orm2.Condition)(cond1)).All(&maps)
 	if err != nil {
 		logs.Error(lib.FuncName(), err)
 		return

+ 1 - 1
models/Stock/Mqtt.go

@@ -14,7 +14,7 @@ type Mqtt struct {
 
 func Read_MqttUser(T_sn string) (r Mqtt) {
 	o := orm2.NewOrmUsingDB("mqtt")
-	err := o.Raw("SELECT username,iccid,imei FROM mqtt_user WHERE username = " + T_sn).QueryRow(&r)
+	err := o.Raw("SELECT username,iccid,imei FROM mqtt_user WHERE username = '" + T_sn + "'").QueryRow(&r)
 	if err != nil {
 		logs.Error(lib.FuncName(), err)
 		return

+ 3 - 0
models/Stock/Stock.go

@@ -226,6 +226,9 @@ func (dao *StockDaoImpl) Read_Stock_Count_ByT_depot_id(T_depot_id int) (cnt int6
 		logs.Error(lib.FuncName(), err)
 		return
 	}
+	if pl_lists[0] == nil {
+		return 0
+	}
 	key, _ := strconv.ParseInt(pl_lists[0].(string), 10, 64)
 	return key
 

+ 11 - 1
routers/Contract.go

@@ -17,6 +17,16 @@ func init() {
 		beego.NSRouter("/Del", &controllers.ContractController{}, "*:Contract_Del"),                   // 删除合同
 		beego.NSRouter("/Product_List", &controllers.ContractController{}, "*:Contract_Product_List"), // 合同产品列表
 		beego.NSRouter("/Out_List", &controllers.ContractController{}, "*:Contract_List_For_Out"),     // 合同产品列表
+		beego.NSRouter("/Gen_Number", &controllers.ContractController{}, "*:Contract_GenT_number"),    // 生成合同编号
 	)
-	beego.AddNamespace(contract)
+	verifyContract := beego.NewNamespace("/VerifyContract",
+		beego.NSRouter("/List", &controllers.ContractController{}, "*:VerifyContract_List"),                       // 验证合同列表
+		beego.NSRouter("/Add", &controllers.ContractController{}, "*:VerifyContract_Add"),                         // 添加验证合同
+		beego.NSRouter("/Edit", &controllers.ContractController{}, "*:VerifyContract_Edit"),                       // 编辑验证合同
+		beego.NSRouter("/Del", &controllers.ContractController{}, "*:VerifyContract_Del"),                         // 删除验证合同
+		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.AddNamespace(contract, verifyContract)
 }

+ 2 - 1
routers/Stock.go

@@ -8,7 +8,8 @@ import (
 func init() {
 
 	device := beego.NewNamespace("/Device",
-		beego.NSRouter("/List", &controllers.StockController{}, "*:Device_List"), // 设备列表
+		beego.NSRouter("/List", &controllers.StockController{}, "*:Device_List"),   // 设备列表
+		beego.NSRouter("/Check", &controllers.StockController{}, "*:Device_Check"), // 检查设备
 	)
 	stock := beego.NewNamespace("/Stock",
 		beego.NSRouter("/List", &controllers.StockController{}, "*:Stock_List"),                 // 库存列表