package services

import (
	"database/sql"
	"errors"
	"fmt"
	"gin-vue-admin/common"
	"gin-vue-admin/global"
	"gin-vue-admin/models"
	"gin-vue-admin/repository"
	"gin-vue-admin/utils"
	"strconv"
	"time"
)

func StockService(phone, t string) (interface{}, error) {

	chainCode, employeeId, err := repository.QueryChainCodeAndLocalIDByPhone(phone)
	if err != nil {
		return nil, err
	}

	if chainCode == "" {
		return nil, errors.New("请设置默认医院")
	}

	// 2. 拿到db
	safeMap := common.NewSafeMap()
	db, err := safeMap.GetOrStore(chainCode)
	if err != nil {
		return nil, err
	}

	isNew, err := repository.ISNewStock(chainCode)
	if err != nil {
		return nil, err
	}

	hospitalLocalId, err := repository.QueryHospitalLocalIDByEmployeeLocalID(db, employeeId)
	if err != nil {
		return nil, err
	}

	return repository.StockRes(db, t, isNew, hospitalLocalId)
}

func StockEarlyWarningService(phone string, page, pageSize int64) (interface{}, error) {

	chainCode, employeeId, err := repository.QueryChainCodeAndLocalIDByPhone(phone)
	if err != nil {
		return nil, err
	}

	if chainCode == "" {
		return nil, errors.New("请设置默认医院")
	}

	// 2. 拿到db
	safeMap := common.NewSafeMap()
	db, err := safeMap.GetOrStore(chainCode)
	if err != nil {
		return nil, err
	}

	isNew, err := repository.ISNewStock(chainCode)
	if err != nil {
		return nil, err
	}

	hospitalLocalId, err := repository.QueryHospitalLocalIDByEmployeeLocalID(db, employeeId)
	if err != nil {
		return nil, err
	}

	total, err := repository.StockEarlyWarningTotalRes(db, isNew, hospitalLocalId)
	if err != nil {
		return nil, err
	}

	pageSize, page, pageData := utils.GetPageData(page, pageSize, total)

	response, err := repository.StockEarlyWarningRes(db, page, pageSize, isNew, hospitalLocalId)

	pageData.Data = response

	return pageData, err
}

func CheckStockList(phone, t string) (interface{}, error) {

	chainCode, employeeId, err := repository.QueryChainCodeAndLocalIDByPhone(phone)
	if err != nil {
		return nil, err
	}

	if chainCode == "" {
		return nil, errors.New("请设置默认医院")
	}

	// 2. 拿到db
	safeMap := common.NewSafeMap()
	db, err := safeMap.GetOrStore(chainCode)
	if err != nil {
		return nil, err
	}

	isNew, err := repository.ISNewStock(chainCode)
	if err != nil {
		return nil, err
	}

	return repository.CheckStockList(db, t, employeeId, isNew)
}

func IsNewStock(phone string) (interface{}, error) {

	chainCode, _, err := repository.QueryChainCodeAndLocalIDByPhone(phone)
	if err != nil {
		return nil, err
	}

	if chainCode == "" {
		return nil, errors.New("请设置默认医院")
	}

	isNew, err := repository.ISNewStock(chainCode)
	if err != nil {
		return nil, err
	}

	return isNew, nil

}

func SearchCommodity(phone, keywords string) (interface{}, error) {

	chainCode, employeeId, err := repository.QueryChainCodeAndLocalIDByPhone(phone)
	if err != nil {
		return nil, err
	}

	if chainCode == "" {
		return nil, errors.New("请设置默认医院")
	}

	// 2. 拿到db
	safeMap := common.NewSafeMap()
	db, err := safeMap.GetOrStore(chainCode)
	if err != nil {
		return nil, err
	}

	d, err := repository.SearchCommodity(db, chainCode, keywords, employeeId)
	if err != nil {
		return nil, err
	}

	//marshal, _ := json.Marshal(d)
	//global.GVA_LOG.Info(fmt.Sprintf(`库存批次结果： %v `, string(marshal)))
	return d, nil

}

func AddCheckStockList(phone string, p *models.StockChecks) (interface{}, error) {

	chainCode, employeeId, err := repository.QueryChainCodeAndLocalIDByPhone(phone)
	if err != nil {
		return nil, err
	}

	if chainCode == "" {
		return nil, errors.New("请设置默认医院")
	}

	// 2. 拿到db
	safeMap := common.NewSafeMap()
	db, err := safeMap.GetOrStore(chainCode)
	if err != nil {
		return nil, err
	}

	isNew, err := repository.ISNewStock(chainCode)
	if err != nil {
		return nil, err
	}

	if isNew == 1 {
		err = repository.AddNewStockCheckStockList(db, p, int64(employeeId))
		if err != nil {
			return nil, err
		}
	} else {
		err = repository.AddStockCheckStockList(db, p, int64(employeeId))
		if err != nil {
			return nil, err
		}
	}

	return nil, err
}

func CheckDetail(phone, id string) (interface{}, error) {

	var (
		data interface{}
		err  error
	)

	chainCode, employeeId, err := repository.QueryChainCodeAndLocalIDByPhone(phone)
	if err != nil {
		return nil, err
	}

	if chainCode == "" {
		return nil, errors.New("请设置默认医院")
	}

	// 2. 拿到db
	safeMap := common.NewSafeMap()
	db, err := safeMap.GetOrStore(chainCode)
	if err != nil {
		return nil, err
	}

	isNew, err := repository.ISNewStock(chainCode)
	if err != nil {
		return nil, err
	}

	if isNew == 1 {
		data, err = repository.NewStockCheckDetail(db, id, int64(employeeId))
		if err != nil {
			return nil, err
		}
	} else {
		data, err = repository.StockCheckDetail(db, id, int64(employeeId))
		if err != nil {
			return nil, err
		}

	}

	return data, err
}

func GetNewStockByStatisticsListService(phone, categoryIds, keyword string) (interface{}, error) {

	chainCode, userId, err := repository.QueryChainCodeAndLocalIDByPhone(phone)
	if err != nil {
		return nil, err
	}

	if chainCode == "" {
		return nil, errors.New("请设置默认医院")
	}

	// 2. 拿到db
	safeMap := common.NewSafeMap()
	db, err := safeMap.GetOrStore(chainCode)
	if err != nil {
		return nil, err
	}

	hospitalLocalId, err := repository.QueryHospitalLocalIDByEmployeeLocalID(db, userId)
	if err != nil {
		return nil, err
	}

	//isNew, err := repository.ISNewStock(chainCode)
	//if err != nil {
	//	return nil, err
	//}
	//if isNew == 1 {
	//	data, err = repository.NewStockCheckDetail(db, id, int64(employeeId))
	//	if err != nil {
	//		return nil, err
	//	}
	//} else {
	//	data, err = repository.StockCheckDetail(db, id, int64(employeeId))
	//	if err != nil {
	//		return nil, err
	//	}
	//}

	data, err := repository.GetNewStockByStatisticsListDao(db, categoryIds, keyword, hospitalLocalId, chainCode)
	if err != nil {
		return nil, err
	}

	return data, err
}

func GetNewStockByStatisticsListDetailService(phone, id string) (interface{}, error) {

	chainCode, userId, err := repository.QueryChainCodeAndLocalIDByPhone(phone)
	if err != nil {
		return nil, err
	}

	if chainCode == "" {
		return nil, errors.New("请设置默认医院")
	}

	// 2. 拿到db
	safeMap := common.NewSafeMap()
	db, err := safeMap.GetOrStore(chainCode)
	if err != nil {
		return nil, err
	}

	hospitalLocalId, err := repository.QueryHospitalLocalIDByEmployeeLocalID(db, userId)
	if err != nil {
		return nil, err
	}

	//isNew, err := repository.ISNewStock(chainCode)
	//if err != nil {
	//	return nil, err
	//}
	//if isNew == 1 {
	//	data, err = repository.NewStockCheckDetail(db, id, int64(employeeId))
	//	if err != nil {
	//		return nil, err
	//	}
	//} else {
	//	data, err = repository.StockCheckDetail(db, id, int64(employeeId))
	//	if err != nil {
	//		return nil, err
	//	}
	//}

	intId, _ := strconv.Atoi(id)

	data, err := repository.GetNewStockByStatisticsListDetailDao(db, intId, hospitalLocalId)
	if err != nil {
		return nil, err
	}

	return data, err
}

func GetCategoryService(phone string) (interface{}, error) {

	chainCode, userId, err := repository.QueryChainCodeAndLocalIDByPhone(phone)
	if err != nil {
		return nil, err
	}

	if chainCode == "" {
		return nil, errors.New("请设置默认医院")
	}

	// 2. 拿到db
	safeMap := common.NewSafeMap()
	db, err := safeMap.GetOrStore(chainCode)
	if err != nil {
		return nil, err
	}

	hospitalLocalId, err := repository.QueryHospitalLocalIDByEmployeeLocalID(db, userId)
	if err != nil {
		return nil, err
	}

	data, err := repository.GetCategoryDao(db, hospitalLocalId)
	if err != nil {
		return nil, err
	}

	return data, err
}

func GetOutOfStockListService(phone, timer string) (interface{}, error) {

	chainCode, userId, err := repository.QueryChainCodeAndLocalIDByPhone(phone)
	if err != nil {
		return nil, err
	}

	if chainCode == "" {
		return nil, errors.New("请设置默认医院")
	}

	// 2. 拿到db
	safeMap := common.NewSafeMap()
	db, err := safeMap.GetOrStore(chainCode)
	if err != nil {
		return nil, err
	}

	sysHospitalId, err := repository.QueryHospitalLocalIDByEmployeeLocalID(db, userId)
	if err != nil {
		return nil, err
	}

	timeProcessor, err := TimeProcessor(timer)
	if err != nil {
		return nil, err
	}

	// g_new_stockout_io
	// g_new_stockoutio_detail

	stockOutSQL := fmt.Sprintf(`select id, con_company_id, con_company_name, con_employee_id, con_employee_name,
_code as 'code', type, is_payover, eventtime, add_note from g_new_stockout_io 
where delflag = 0 and sys_hospital_id = ?   and DATE_FORMAT(eventtime,%v)  BETWEEN '%s' and '%s' `, timeProcessor, timer, timer)

	check, err := utils.CheckColumn(db, "g_new_stockout_io", "is_commodity_deposit", chainCode)
	if err != nil {
		return nil, err
	}

	if check > 0 {
		stockOutSQL += `  and is_commodity_deposit = 0  `
	}

	stockOutSQL += " order by id desc"

	stockOutDetailSQL := `select
stock.id, stock.new_stockout_io_id, stock.con_commodity_id, stock.con_category_id, stock.con_category_name, stock.top_category_id,
stock.commodity_code, stock.commodity_encode, commodity._name, stock.commodity_namepy, stock.commodity_brand, ifnull(commodity.isuse_alias,0),
ifnull(commodity.aliasname,""), stock.commodity_standardname, stock.commodity_standardcode, stock.commodity_vender, stock.commodity_format, stock.commodity_unit,
convert(stock.quantity / 1000, decimal(11,3)) as quantity, convert(stock.sellprice / 1000, decimal(11,3)) as sellprice, convert(stock.payprice / 1000, decimal(11,3)) as payprice,stock.description,stock.commodity_batch ,convert(stock.sellprice / 1000, decimal(11,3)) as sellprice,convert(stock.wholesale_price / 1000, decimal(11,3)) as wholesale_price
from g_new_stockoutio_detail stock
join con_commodity  commodity on commodity.id  = stock.con_commodity_id
where stock.delflag = 0 and stock.sys_hospital_id = ? and stock.new_stockout_io_id = ?`

	return repository.NewQueryStockOutIoExWarehousing(db, sysHospitalId, stockOutSQL, stockOutDetailSQL, "2")
}

func TimeProcessor(t string) (string, error) {
	if len(t) == 10 {
		formatSQL := "'%Y-%m-%d'"
		return formatSQL, nil
	} else if len(t) == 7 {
		formatSQL := "'%Y-%m'"
		return formatSQL, nil
	} else if len(t) == 4 {
		formatSQL := "'%Y'"
		return formatSQL, nil
	}
	return "", errors.New("time error")

}

func GetCompanyListService(phone string) (interface{}, error) {

	chainCode, userId, err := repository.QueryChainCodeAndLocalIDByPhone(phone)
	if err != nil {
		return nil, err
	}

	if chainCode == "" {
		return nil, errors.New("请设置默认医院")
	}

	// 2. 拿到db
	safeMap := common.NewSafeMap()
	db, err := safeMap.GetOrStore(chainCode)
	if err != nil {
		return nil, err
	}

	hospitalId, err := repository.QueryHospitalLocalIDByEmployeeLocalID(db, userId)
	if err != nil {
		return nil, err
	}

	return repository.GetCompanyListDao(hospitalId, db)

}

func GetEmployeeListService(phone string) (interface{}, error) {

	chainCode, userId, err := repository.QueryChainCodeAndLocalIDByPhone(phone)
	if err != nil {
		return nil, err
	}

	if chainCode == "" {
		return nil, errors.New("请设置默认医院")
	}

	// 2. 拿到db
	safeMap := common.NewSafeMap()
	db, err := safeMap.GetOrStore(chainCode)
	if err != nil {
		return nil, err
	}

	hospitalId, err := repository.QueryHospitalLocalIDByEmployeeLocalID(db, userId)
	if err != nil {
		return nil, err
	}

	return repository.GetEmployeeList(hospitalId, db)
}

func GetNumberService(phone, numberType string) (interface{}, error) {

	chainCode, userId, err := repository.QueryChainCodeAndLocalIDByPhone(phone)
	if err != nil {
		return nil, err
	}

	if chainCode == "" {
		return nil, errors.New("请设置默认医院")
	}

	// 2. 拿到db
	safeMap := common.NewSafeMap()
	db, err := safeMap.GetOrStore(chainCode)
	if err != nil {
		return nil, err
	}

	hospitalId, err := repository.QueryHospitalLocalIDByEmployeeLocalID(db, userId)
	if err != nil {
		return nil, err
	}

	return repository.GetNumber(hospitalId, numberType, db)
}

func AddOutOfStockService(phone string, warehousing *models.NewWarehousingParamModel) (interface{}, error) {

	chainCode, userId, err := repository.QueryChainCodeAndLocalIDByPhone(phone)
	if err != nil {
		return nil, err
	}

	if chainCode == "" {
		return nil, errors.New("请设置默认医院")
	}

	// 2. 拿到db
	safeMap := common.NewSafeMap()
	db, err := safeMap.GetOrStore(chainCode)
	if err != nil {
		return nil, err
	}

	sysHospitalId, err := repository.QueryHospitalLocalIDByEmployeeLocalID(db, userId)
	if err != nil {
		return nil, err
	}

	id, err := AddOutOfStockDao(int64(sysHospitalId), int64(userId), db, warehousing)
	if err != nil {
		return nil, err
	}

	return id, nil

}

func checkTime(time int) string {
	if time < 10 {
		return "0" + strconv.Itoa(time)
	}
	return strconv.Itoa(time)
}

func AddOutOfStockDao(hospitalId, userId int64, db *sql.DB, p *models.NewWarehousingParamModel) (interface{}, error) {

	// time
	eventTimeStr := p.Eventtime
	if eventTimeStr == "" {
		return 0, nil
	}

	hour := time.Now().Hour()
	minute := time.Now().Minute()
	second := time.Now().Second()

	eventTimeStr = eventTimeStr + " " + checkTime(hour) + ":" + checkTime(minute) + ":" + checkTime(second)

	parseTime, err := time.Parse("2006-01-02 15:04:05", eventTimeStr)
	if err != nil {
		fmt.Println("err: ", err)
		return 0, err
	}

	global.GVA_LOG.Info(fmt.Sprintf(`拼接时间为： %s `, eventTimeStr))
	p.Eventtime = eventTimeStr

	// tx begin
	tx, err := db.Begin()
	if err != nil {
		return 0, err
	}

	// 事务处理
	//defer func() {
	//	switch err {
	//	case nil:
	//		err = tx.Commit()
	//	default:
	//		tx.Rollback()
	//	}
	//}()

	if p.ID != 0 {
		stringID := strconv.Itoa(int(p.ID))
		err = delOriginalOutList(tx, db, stringID, hospitalId)
		if err != nil {
			tx.Rollback()
			return nil, err
		}
	}

	// 创建出库单
	stockOutId, err := createStockOutList(tx, hospitalId, p)
	if err != nil {
		tx.Rollback()
		return 0, err
	}

	// 新增商品单日库存流记录
	insertStockCountSQL := `insert g_new_stock_count set creater = ?, sys_hospital_id = ?, con_commodity_id = ?,
_year = ?, _month = ?,_day = ? ,eventtime = ? , change_quantity = ? * 1000, after_change_quantity = ? * 1000, before_change_quantity = ? * 1000, _type = 2 ,lot_number = ? `

	//fmt.Println("出库单id: ", stockOutId)

	// 新增出库单详情
	stockOutIoDetailSQL := `insert g_new_stockoutio_detail set 
creater = ?, sys_hospital_id = ?, new_stockout_io_id = ?, con_commodity_id = ?, con_category_id = ?, con_category_name = ?, top_category_id = ?, 
commodity_code = ?, commodity_encode = ?, commodity_name = ?, commodity_namepy = ?, commodity_brand = ?, commodity_isuse_alias = ?, 
commodity_aliasname = ?, commodity_standardname = ?, commodity_standardcode = ?, commodity_vender = ?, commodity_format = ?, commodity_unit = ?, 
quantity = ? * 1000, sellprice = ? * 1000, payprice = ? * 1000,description = ?,commodity_batch =?,wholesale_price = ? * 1000`

	stockOutIoLotNumberDetailSQL := `insert g_new_stockoutio_lot_number_detail set
creater = ?, sys_hospital_id = ?, new_stockout_io_detail_id = ?, con_commodity_id = ?, con_category_id = ?, con_category_name = ?, top_category_id = ?,
commodity_code = ?, commodity_encode = ?, commodity_name = ?, commodity_namepy = ?, commodity_brand = ?, commodity_isuse_alias = ?,
commodity_aliasname = ?, commodity_standardname = ?, commodity_standardcode = ?, commodity_vender = ?, commodity_format = ?, commodity_unit = ?,
quantity = ? * 1000, sellprice = ? * 1000, payprice = ? * 1000,lot_number = ?,commodity_batch =?, count_out_id = ?`

	// 更新库存数量
	updateStockSQL := "update g_new_stock set quantity = quantity - ( ? * 1000)  where id = ?"

	insertStockCountStmt, err := tx.Prepare(insertStockCountSQL)
	if err != nil {
		tx.Rollback()
		return 0, err
	}

	stockOutIoDetailStmt, err := tx.Prepare(stockOutIoDetailSQL)
	if err != nil {
		tx.Rollback()
		return nil, err
	}

	stockOutIoLotNumberDetailStmt, err := tx.Prepare(stockOutIoLotNumberDetailSQL)
	if err != nil {
		tx.Rollback()
		return nil, err
	}

	updateStockStmt, err := tx.Prepare(updateStockSQL)
	if err != nil {
		tx.Rollback()
		return 0, err
	}

	//批号设置获取
	//lotNumberSetting, err := dedicatedStockDao.QueryLotNumberSetting(tx, hospitalId, userId)

	now_Year := parseTime.Year()
	now_Month := int(parseTime.Month())
	now_Day := int(parseTime.Day())

	for _, v := range p.DetailData {

		if v.CommodityBatch == "无批次" {
			v.CommodityBatch = ""
		}

		if v.CommodityBatch == "" {
			// 自动出库
			err = OutStockRules(tx, hospitalId, userId, stockOutId, v, insertStockCountStmt, updateStockStmt, stockOutIoDetailStmt, stockOutIoLotNumberDetailStmt, now_Year, now_Month, now_Day)
			if err != nil {
				tx.Rollback()
				return nil, err
			}
		} else {
			//用户选择批次进行出库
			//进行记录
			//更新库存
			//记录count
			//按照批次出库的话
			//首先批次有多种 相同中又有价格不同
			//多种批次中也同样是批号出库

			/*
				1.判断是否能够出库
			*/

			// 1.判断是否能够出库
			isHaveStock, counts, err := isAbleStockOut(hospitalId, tx, v)
			if err != nil {
				tx.Rollback()
				return nil, err
			}

			if !isHaveStock || counts < v.Quantity {
				tx.Rollback()
				return nil, errors.New("商品" + v.CommodityName + "库存不足")
			}

			//开始执行出库操作
			//查询此此出库的都应该出哪些批号的商品
			// 按照批号进行出库
			err = stockOutByCommodityBatch(tx, hospitalId, v, insertStockCountStmt, userId, now_Year, now_Month, now_Day, updateStockStmt, stockOutIoDetailStmt, stockOutIoLotNumberDetailStmt, stockOutId)
			if err != nil {
				tx.Rollback()
				return nil, err
			}
		}

	}

	err = newStockOutCash(p, tx, userId, hospitalId, eventTimeStr, stockOutId)
	if err != nil {
		return nil, err
	}

	if p.Type == 1 {
		_, err_ := tx.Exec("update con_company set   arrears  = arrears + (? * 1000) where id = ?   ", p.Total, p.ConCompanyID)
		if err_ != nil {
			tx.Rollback()
			return nil, err_
		}
	}

	tx.Commit()

	return stockOutId, nil
}

// 创建出库单
func createStockOutList(tx *sql.Tx, hospitalId int64, p *models.NewWarehousingParamModel) (int64, error) {
	// 出库单 id
	var stockOutId int64

	// 创建出库单
	stockInIoSQL := "insert g_new_stockout_io set sys_hospital_id = ?, con_company_id = ?, con_company_name = ?, con_employee_id = ?, con_employee_name = ?, _code = ?, type = ?, is_payover = ?, eventtime = ?,add_note = ?,is_commodity_deposit = ?"

	stockInStmt, err := tx.Prepare(stockInIoSQL)
	if err != nil {
		return 0, err
	}

	result, err := stockInStmt.Exec(hospitalId, p.ConCompanyID, p.ConCompanyName, p.ConEmployeeID, p.ConEmployeeName, p.Code, p.Type, p.IsPayover, p.Eventtime, p.AddNote, p.IsCommodityDeposit)
	if err != nil {
		return 0, err
	}

	stockOutId, err = result.LastInsertId()
	if err != nil {
		return 0, err
	}
	return stockOutId, err
}

func QueryNumber(db *sql.DB, q string) (int64, error) {

	var orderValue int64
	err := db.QueryRow(q).Scan(&orderValue)

	if err != nil {
		return 0, err
	}

	return orderValue, err
}

//  获取单子上的商品列表
func getCommodityList(db *sql.DB, hospitalId int64, id string) ([]models.CommodityList, error) {

	rows, err := db.Query("select id,  convert(quantity / 1000, decimal(11,3)),con_commodity_id  from g_new_stockoutio_detail where delflag = 0 and sys_hospital_id = ? and new_stockout_io_id = ?", hospitalId, id)
	if err != nil {
		return nil, err
	}

	tempGoods := make([]models.CommodityList, 0)

	for rows.Next() {

		tempGood := new(models.CommodityList)
		err = rows.Scan(&tempGood.ID, &tempGood.Quantity, &tempGood.ConCommodityID)
		if err != nil {
			return nil, err
		}
		// 或者每一项商品背后的信息
		commodityDetail, err := getCommodityDetail(db, hospitalId, tempGood.ID)
		if err != nil {
			return nil, err
		}
		tempGood.CommodityDetail = commodityDetail
		tempGoods = append(tempGoods, *tempGood)
	}
	return tempGoods, nil
}

// 获取一项商品其实扣除的各项信息
func getCommodityDetail(db *sql.DB, hospitalId int64, id int) ([]models.CommodityDetail, error) {
	rows, err := db.Query("select id,  convert(quantity / 1000, decimal(11,3)),lot_number,con_commodity_id,count_out_id from g_new_stockoutio_lot_number_detail where delflag = 0 and sys_hospital_id = ? and new_stockout_io_detail_id = ?", hospitalId, id)
	if err != nil {
		return nil, err
	}

	tempGoods := make([]models.CommodityDetail, 0)

	for rows.Next() {

		tempGood := new(models.CommodityDetail)
		err = rows.Scan(&tempGood.ID, &tempGood.Quantity, &tempGood.LotNumber, &tempGood.ConCommodityID, &tempGood.CountID)
		if err != nil {
			return nil, err
		}
		tempGoods = append(tempGoods, *tempGood)
	}
	return tempGoods, nil
}

func delOriginalOutList(tx *sql.Tx, db *sql.DB, id string, hospitalId int64) error {

	stockCount, err := QueryNumber(db, fmt.Sprintf("select count(*) as count from g_new_stockout_io where id = %v", id))
	if stockCount <= 0 {
		return errors.New("出库清单不存在")
	}

	isPayOver, err := QueryNumber(db, fmt.Sprintf("select is_payover as count from g_new_stockout_io where id = %v", id))
	if err != nil {
		return err
	}

	if isPayOver == 1 {
		return errors.New("不能删除已结算的采购单")
	}

	// 获取单子上商品列表
	commodityLists, err := getCommodityList(db, hospitalId, id)
	if err != nil {
		return err
	}

	err = delStockOutListAndDetailAndRollbackStock(commodityLists, tx, hospitalId, id)
	if err != nil {
		tx.Rollback()
		return err
	}

	return err
}

// 删除相关信息(库存记录表和库存详细信息表,库存记录批号记录表)以及回滚库存
func delStockOutListAndDetailAndRollbackStock(commodityLists []models.CommodityList, tx *sql.Tx, hospitalId int64, id string) error {
	for _, list := range commodityLists {

		for _, v := range list.CommodityDetail {

			// 删除Count
			// 删除库存记录
			_, err := tx.Exec(fmt.Sprintf("update g_new_stock_count set delflag = 1 where id = ? "), v.CountID)
			if err != nil {
				tx.Rollback()
				return err
			}

			// 删除出库单详情
			delResult, err := tx.Exec(fmt.Sprintf("update g_new_stockoutio_lot_number_detail set delflag = 1 where id = %v", v.ID))
			if err != nil {
				tx.Rollback()
				return err
			}
			_, err = delResult.RowsAffected()
			if err != nil {
				tx.Rollback()
				return err
			}

			// 更新库存表
			err = UpdateStockAddTo(tx, v.Quantity, v.LotNumber, int64(v.ConCommodityID))
			if err != nil {
				tx.Rollback()
				return err
			}

		}

		// 删除出库单
		_, err := tx.Exec(fmt.Sprintf("update g_new_stockoutio_detail set delflag = 1 where id = %v", list.ID))
		if err != nil {
			tx.Rollback()
			return err
		}
	}

	// 删除出库单
	_, err := tx.Exec(fmt.Sprintf("update g_new_stockout_io set delflag = 1 where id = %v", id))
	if err != nil {
		tx.Rollback()
		return err
	}
	return nil
}

func newStockOutCash(p *models.NewWarehousingParamModel, tx *sql.Tx, userId int64, hospitalId int64, eventTimeStr string, stockOutId int64) error {
	if p.Type == 2 {

		// 1. 查询出 往来单位的预结算余额
		queryPreEttlementPriceSQL := `select convert(previous_receivebalance / 1000, decimal(11,3)) as previous_receivebalance from con_company where delflag = 0 and id = ?`
		var previousReceiveBalance float64
		err := tx.QueryRow(queryPreEttlementPriceSQL, p.ConCompanyID).Scan(&previousReceiveBalance)
		if err != nil {
			tx.Rollback()
			return err
		}
		fmt.Println("往来单位 预付款余额: ", previousReceiveBalance)

		// 2. 判断 使用预收款的金额 不能大于 预收款的余额

		PreEttlement := 0.00
		for _, v := range p.BillDetailData {
			if v.Paytype == 6 {
				PreEttlement = v.Money
				if v.Money > previousReceiveBalance {
					tx.Rollback()
					return errors.New("预结算金额 不能大于预结算余额")
				}
			}
		}

		// 3. 计算出本次合计 (现金 银行卡 微信 支付宝 预结算)
		var preEttlementParam float64
		var settlementPrice float64
		for _, v := range p.BillDetailData {
			settlementPrice += v.Money
			if v.Paytype == 6 {
				preEttlementParam += v.Money
			}
		}

		// 当前余额
		// previousPayBalance - param.PreEttlement
		currentBalance := previousReceiveBalance - preEttlementParam

		// 4. 计算出 这个采购单所有的 商品价格之和
		// 5. 计算出来的金额  和  合计是否一致
		//    计算出来的金额  和  待结算的金额
		//if compute != p.Total || compute != settlementPrice {
		//	tx.Rollback()
		//	return nil, errors.New("金额不一致")
		//}

		// 7. 操作账单
		billSQL := `insert g_new_stockout_bill set creater = %v, sys_hospital_id = %v, con_company_id = %v,
con_company_name = '%v', con_employee_id = %v, con_employee_name = '%v', _code = '%v',
totalmoney = %v * 1000, paymoney = %v * 1000, eventtime = '%v'`

		billSQL = fmt.Sprintf(billSQL, userId, hospitalId, p.ConCompanyID, p.ConCompanyName,
			p.ConEmployeeID, p.ConEmployeeName, p.Code, p.Total, p.Total, eventTimeStr)

		billResult, err := tx.Exec(billSQL)
		if err != nil {
			tx.Rollback()
			return err
		}
		billId, err := billResult.LastInsertId()
		fmt.Println("账单 id: ", billId)
		if err != nil {
			tx.Rollback()
			return err
		}

		// 8. 操作账单详情
		billDetailSQL := `insert g_new_stockoutbill_detail set creater = %v, sys_hospital_id = %v, g_new_stockoutbill_id = %v, g_new_stockoutio_id = %v, money = %v * 1000, discription = '%v'`
		billDetailSQL = fmt.Sprintf(billDetailSQL, userId, hospitalId, billId, stockOutId, p.Total, p.Discription)
		billDetailRestult, err := tx.Exec(billDetailSQL)
		if err != nil {
			tx.Rollback()
			return err
		}
		billDetailId, err := billDetailRestult.LastInsertId()
		if err != nil {
			tx.Rollback()
			return err
		}

		// 9. 操作账单结算方式
		for _, v := range p.BillDetailData {

			if v.Money > 0 {
				// 添加具体结算详情
				payDetail := "insert g_new_stockoutbill_pay set creater = %v,  sys_hospital_id = %v, g_new_stockoutbill_id = %v, paytype = %v, money = %v * 1000, eventtime = '%v'"
				payDetail = fmt.Sprintf(payDetail, userId, hospitalId, billId, v.Paytype, v.Money, eventTimeStr)
				payDetailResult, err := tx.Exec(payDetail)
				if err != nil {
					tx.Rollback()
					return err
				}
				_, err = payDetailResult.LastInsertId()
				if err != nil {
					tx.Rollback()
					return err
				}

				// 资金流
				cashFlowSQL := `insert c_cashflow set creater = %v, sys_hospital_id = %v, bussiness_id = %v, bussiness_type = %v, pay_type = %v, money = %v * 1000, eventime = '%v'`
				cashFlowSQL = fmt.Sprintf(cashFlowSQL, userId, hospitalId, billDetailId, 6, v.Paytype, v.Money, eventTimeStr)
				cashFlowResult, err := tx.Exec(cashFlowSQL)
				if err != nil {
					tx.Rollback()
					return err
				}
				_, err = cashFlowResult.LastInsertId()
				if err != nil {
					tx.Rollback()
					return err
				}

			}

			if v.Paytype == 6 && v.Money > 0 {
				fmt.Println("添加预收款")
				// 添加一条预收款
				insertChargeSQL := "insert c_prein_fund set creater = %v, sys_hospital_id = %v, con_company_id = %v, _code = '%v', type = %v, money = %v * 1000, currentmoney = %v * 1000, discription = '%v', con_employee_id = %v, con_employee_name = '%v', eventtime = '%v'"

				insertChargeSQL = fmt.Sprintf(insertChargeSQL, userId, hospitalId, p.ConCompanyID, p.Code, 2, preEttlementParam, currentBalance, p.Discription, p.ConEmployeeID, p.ConCompanyName, eventTimeStr)

				// 查询 c_preout_fund code + 1   替换 param.Code

				result, err := tx.Exec(insertChargeSQL)
				if err != nil {
					tx.Rollback()
					return err
				}
				preinFundId, err := result.LastInsertId()
				if err != nil {
					tx.Rollback()
					return err
				}

				// 添加一条预收款明细
				insertChargeDetailSQL := "insert c_preinfund_pay set creater = %v, sys_hospital_id = %v, c_prein_fund_id = %v, paytype = %v, money = %v * 1000, eventtime = '%v'"

				insertChargeDetailSQL = fmt.Sprintf(insertChargeDetailSQL, userId, hospitalId, preinFundId, 6, PreEttlement, eventTimeStr)

				detailResult, err := tx.Exec(insertChargeDetailSQL)
				if err != nil {
					tx.Rollback()
					return err
				}
				_, err = detailResult.LastInsertId()
				if err != nil {
					tx.Rollback()
					return err
				}

				updateCompanySQL := fmt.Sprintf("update con_company set previous_receivebalance = %v * 1000 where id = %v", currentBalance, p.ConCompanyID)
				res, err := tx.Exec(updateCompanySQL)
				if err != nil {
					tx.Rollback()
					return err
				}

				_, err = res.RowsAffected()
				if err != nil {
					tx.Rollback()
					return err
				}
			}
		}

		// 10 置为已结清
		changeToSettledSQL := "update g_new_stockout_io set is_payover = 1 where id = %v"

		changeToSettledResult, err := tx.Exec(fmt.Sprintf(changeToSettledSQL, stockOutId))
		if err != nil {
			tx.Rollback()
			return err
		}
		_, err = changeToSettledResult.RowsAffected()
		if err != nil {
			tx.Rollback()
			return err
		}

	}
	return nil
}

func isAbleStockOut(hospitalId int64, tx *sql.Tx, v models.NewDetailData) (bool, float64, error) {

	isHaveStock, counts, err := QueryGoodRecordIsHaveStockInTxByPriceAndCommodityBatch(tx, hospitalId, v.ConCommodityID, v.CommodityBatch, v.BatchPrice)
	if err != nil {
		return false, 0, err
	}

	// fmt.Println("库存中是否有这个商品: ", isHaveStock)
	if !isHaveStock {
		return false, 0, errors.New("(" + v.CommodityName + ")商品库存为0, 不能出库")
	}
	// 判断库存数量是否 < 出库商品的数量   如果小于 则直接返回报错
	if counts < v.Quantity {
		return false, 0, errors.New(`(` + v.CommodityName + ")出库数量不能大于库存数量")
	}
	return isHaveStock, counts, err
}

// QueryGoodRecordIsHaveStockInTx 查询库存表(用户按照批次出库时使用)
func QueryGoodRecordIsHaveStockInTxByPriceAndCommodityBatch(tx *sql.Tx, hospitalId, goodId int64, commodityBatch string, batchPrice float64) (bool, float64, error) {

	/*
		1.首先找到这个批次的这个价格的商品有无
		2.有之后判断数量
		3.进行出库扣减,记录
	*/

	//tx.QueryRow(`select  convert(buyprice / 1000, decimal(11,3)) as buyprice where  lot_number = ? `,l)
	// 进行 1 判断此批次,此价格的商品有无
	sql := "select count(0) from g_new_stock where sys_hospital_id = ? and delflag=0 and con_commodity_id = ? and commodity_batch = ? and convert(costmoney/1000,decimal(11,3)) = ?"
	sqlCount := "select ifnull(sum(convert(quantity / 1000, decimal(11,3)) ),0) from g_new_stock where sys_hospital_id = ? and delflag=0 and commodity_batch = ?  and con_commodity_id = ? and convert(costmoney/1000,decimal(11,3)) = ?  "

	var (
		isHave float64
		counts float64
	)
	err := tx.QueryRow(sql, hospitalId, goodId, commodityBatch, batchPrice).Scan(&isHave)
	if err != nil {
		return false, 0, err
	}
	err = tx.QueryRow(sqlCount, hospitalId, commodityBatch, goodId, batchPrice).Scan(&counts)
	if err != nil {
		return false, 0, err
	}

	if isHave <= 0 {
		return false, 0, nil
	}

	return true, counts, nil
}

// OutStockRules 出库规则
func OutStockRules(tx *sql.Tx, hospitalId, userId, stockOutId int64, v models.NewDetailData,
	insertStockCountStmt, updateStockStmt, stockOutIoDetailStmt, stockOutIoLotNumberDetailStmt *sql.Stmt, now_Year, now_Month, now_Day int) error {

	// 操作库存表  确认此商品是否有库存
	isHaveStock, stockGood, err := QueryIsHaveStockInTxByPriceNotCommodityBatch(tx, hospitalId, v.ConCommodityID, v.BatchPrice)
	if err != nil {
		return err
	}

	if !isHaveStock {
		return errors.New("(" + v.CommodityName + ")商品库存为0, 不能出库")
	}

	// 查询库存中 这个商品的id、 和 数量
	//sql := "select  SUM(convert(quantity / 1000, decimal(11,3))) as quantity from g_new_stock where sys_hospital_id = ? and con_commodity_id = ? and delflag = 0"
	//
	//var (
	//	stockGood float64
	//)
	//
	//err = tx.QueryRow(sql, hospitalId, v.ConCommodityID).Scan(&stockGood)
	//if err != nil {
	//	return err
	//}

	// fmt.Println("查询库存中这个商品id: ", id, "数量: ", stockGood)

	// 判断库存数量是否 < 出库商品的数量   如果小于 则直接返回报错
	if stockGood < v.Quantity {
		return errors.New(`(` + v.CommodityName + ")出库数量 不能大于库存数量")
	}

	// 出库逻辑
	// 查询此商品的自动批号列表 正序  从前到后
	lotNumberLists, err := QueryLotNumberListDifferentiateBatchByPrice(tx, hospitalId, v.ConCommodityID, v.BatchPrice)
	if err != nil {
		return err
	}

	res, err := stockOutIoDetailStmt.Exec(userId, hospitalId, stockOutId, v.ConCommodityID, v.ConCategoryID, v.ConCategoryName,
		v.TopCategoryID, v.CommodityCode, v.CommodityEncode, v.CommodityName, v.CommodityNamepy, v.CommodityBrand,
		v.CommodityIsuseAlias, v.CommodityAliasname, v.CommodityStandardname, v.CommodityStandardcode, v.CommodityVender,
		v.CommdityFormat, v.CommodityUnit, v.Quantity, v.BatchPrice, v.Payprice, v.Description, v.CommodityBatch, v.WholesalePrice)
	if err != nil {
		return err
	}

	stockOutDetailId, _ := res.LastInsertId()

	// 按规则判断记录出库
	var tempQuantity float64

	for _, list := range lotNumberLists {

		// 消除是0的
		if list.Quantity == 0 {
			continue
		}

		if v.Quantity > 0 {

			if v.Quantity > list.Quantity {

				tempQuantity = list.Quantity

				v.Quantity = v.Quantity - tempQuantity

			} else {

				tempQuantity = v.Quantity

				v.Quantity = v.Quantity - tempQuantity

			}

			// 新增Count记录
			countID, err := InsertStockCount(tx, userId, hospitalId, v.ConCommodityID, now_Year, now_Month, now_Day, time.Now().Format("2006-01-02 15:04:05"), tempQuantity, list.Quantity-tempQuantity, list.Quantity, list.LotNumber, v.CommodityBatch)
			if err != nil {
				return err
			}

			// 更新库存数量
			_, err = updateStockStmt.Exec(tempQuantity, list.ID)
			if err != nil {
				return err
			}

			_, err = stockOutIoLotNumberDetailStmt.Exec(userId, hospitalId, stockOutDetailId, v.ConCommodityID, v.ConCategoryID, v.ConCategoryName,
				v.TopCategoryID, v.CommodityCode, v.CommodityEncode, v.CommodityName, v.CommodityNamepy, v.CommodityBrand,
				v.CommodityIsuseAlias, v.CommodityAliasname, v.CommodityStandardname, v.CommodityStandardcode, v.CommodityVender,
				v.CommdityFormat, v.CommodityUnit, tempQuantity, v.Payprice, list.CostMoney, list.LotNumber, v.CommodityBatch, countID)

			if err != nil {
				return err
			}

		}
	}

	return nil
}

// QueryGoodRecordIsHaveStockInTx 查询库存表(用户按照不按照批次出库但是按照价格使用)
func QueryIsHaveStockInTxByPriceNotCommodityBatch(tx *sql.Tx, hospitalId, goodId int64, price float64) (bool, float64, error) {

	/*
		1.首先找到这个批次的这个价格的商品有无
		2.有之后判断数量
		3.进行出库扣减,记录
	*/

	// 进行 1 判断此批次,此价格的商品有无
	sql := "select count(0) from g_new_stock where sys_hospital_id = ? and delflag=0 and con_commodity_id = ? and  commodity_batch = ''  and convert(costmoney/1000,decimal(11,3)) = ? "
	sqlCount := "select ifnull ((convert(sum(quantity) / 1000, decimal(11,3)) ),0) from g_new_stock where sys_hospital_id = ? and delflag=0 and con_commodity_id = ?   and  commodity_batch = '' and convert(costmoney/1000,decimal(11,3)) = ? "

	var (
		isHave float64
		counts float64
	)
	err := tx.QueryRow(sql, hospitalId, goodId, price).Scan(&isHave)
	if err != nil {
		return false, 0, err
	}
	err = tx.QueryRow(sqlCount, hospitalId, goodId, price).Scan(&counts)
	if err != nil {
		return false, 0, err
	}

	if isHave <= 0 {
		return false, 0, nil
	}

	return true, counts, nil
}

func QueryLotNumberListDifferentiateBatchByPrice(tx *sql.Tx, hospitalId, commodityId int64, price float64) ([]models.LotNumberList, error) {

	// 正序
	//QuerySQL := "select  id , lot_number,convert(quantity / 1000, decimal(11,3)) ,convert(costmoney / 1000, decimal(11,3))  from g_new_stock  where  delflag = 0 and sys_hospital_id = ? and con_commodity_id =? order by lot_number asc "
	querySQL := "select id,lot_number, convert(quantity / 1000, decimal(11,3)) as quantity ,convert(costmoney / 1000, decimal(11,3))  from g_new_stock where sys_hospital_id = ? and con_commodity_id = ?  and commodity_batch = '' and convert(costmoney/1000,decimal(11,3)) = ? order by lot_number asc  "

	rows, err := tx.Query(querySQL, hospitalId, commodityId, price)
	if err != nil {
		return nil, errors.New("查询出错")
	}

	lotNumberLists := make([]models.LotNumberList, 0)

	for rows.Next() {
		var ln models.LotNumberList
		err := rows.Scan(&ln.ID, &ln.LotNumber, &ln.Quantity, &ln.CostMoney)
		if err != nil {
			return nil, errors.New("赋值失败")
		}
		lotNumberLists = append(lotNumberLists, ln)

	}

	return lotNumberLists, err
}

func InsertStockCount(tx *sql.Tx, userId, hospitalId, conCommodityID int64, now_Year, now_Month, now_Day int,
	now string, tempQuantity, tQuantity, aquantity float64, lotNumber, commodityBatch string) (int64, error) {

	// 新增商品单日库存流记录
	insertStockCountSQL := `insert g_new_stock_count set creater = ?, sys_hospital_id = ?, con_commodity_id = ?,
		_year = ?, _month = ?,_day = ? , eventtime = ?, change_quantity = ? * 1000, after_change_quantity = ? * 1000,
		before_change_quantity = ? * 1000, _type = 2 ,lot_number = ? ,commodity_batch  = ?`

	insertStockCountStmt, err := tx.Prepare(insertStockCountSQL)
	if err != nil {
		return 0, err
	}

	exec, err := insertStockCountStmt.Exec(userId, hospitalId, conCommodityID, now_Year, now_Month, now_Day,
		now, tempQuantity, tQuantity, aquantity, lotNumber, commodityBatch)
	if err != nil {
		return 0, err
	}

	insertId, _ := exec.LastInsertId()

	return insertId, err

}

func stockOutByCommodityBatch(tx *sql.Tx, hospitalId int64, v models.NewDetailData, insertStockCountStmt *sql.Stmt,
	userId int64, now_Year int, now_Month int, now_Day int, updateStockStmt *sql.Stmt, stockOutIoDetailStmt, stockOutIoLotNumberDetailStmt *sql.Stmt, stockOutId int64) error {

	// 查询库存中选择批次符合条件响应的批号商品 这个商品的id、 和 数量
	queryLotNumberSql := "select id, convert(quantity / 1000, decimal(11,3)) as quantity,lot_number  from g_new_stock where sys_hospital_id = ? and con_commodity_id = ? and  commodity_batch = ? and convert(costmoney/1000,decimal(11,3)) = ?  order by lot_number asc "

	rows, err := tx.Query(queryLotNumberSql, hospitalId, v.ConCommodityID, v.CommodityBatch, v.BatchPrice)
	if err != nil {
		return err
	}

	lotNumberLists := make([]models.LotNumberListByCommodityBatch, 0)

	for rows.Next() {
		var ln models.LotNumberListByCommodityBatch
		err = rows.Scan(&ln.ID, &ln.Quantity, &ln.LotNumber)
		if err != nil {
			return err
		}
		lotNumberLists = append(lotNumberLists, ln)
	}

	res, err := stockOutIoDetailStmt.Exec(userId, hospitalId, stockOutId, v.ConCommodityID, v.ConCategoryID, v.ConCategoryName,
		v.TopCategoryID, v.CommodityCode, v.CommodityEncode, v.CommodityName, v.CommodityNamepy, v.CommodityBrand,
		v.CommodityIsuseAlias, v.CommodityAliasname, v.CommodityStandardname, v.CommodityStandardcode, v.CommodityVender,
		v.CommdityFormat, v.CommodityUnit, v.Quantity, v.BatchPrice, v.Payprice, v.Description, v.CommodityBatch, v.WholesalePrice)
	if err != nil {
		return err
	}

	stockOutIoDetailId, _ := res.LastInsertId()

	// 按规则判断记录出库
	var tempQuantity float64

	for _, list := range lotNumberLists {

		// 消除是0的
		if list.Quantity == 0 {
			continue
		}

		if v.Quantity > 0 {

			if v.Quantity > list.Quantity {

				tempQuantity = list.Quantity

				v.Quantity = v.Quantity - tempQuantity

			} else {

				tempQuantity = v.Quantity

				v.Quantity = v.Quantity - tempQuantity

			}

			// 新增Count记录
			countResult, err := insertStockCountStmt.Exec(userId, hospitalId, v.ConCommodityID, now_Year, now_Month, now_Day, time.Now().Format("2006-01-02 15:04:05"), tempQuantity, list.Quantity-tempQuantity, list.Quantity, list.LotNumber)
			if err != nil {
				return err
			}

			countID, err := countResult.LastInsertId()
			if err != nil {
				return err
			}

			// 更新库存数量
			_, err = updateStockStmt.Exec(tempQuantity, list.ID)
			if err != nil {
				return err
			}

			_, err = stockOutIoLotNumberDetailStmt.Exec(userId, hospitalId, stockOutIoDetailId, v.ConCommodityID, v.ConCategoryID, v.ConCategoryName,
				v.TopCategoryID, v.CommodityCode, v.CommodityEncode, v.CommodityName, v.CommodityNamepy, v.CommodityBrand,
				v.CommodityIsuseAlias, v.CommodityAliasname, v.CommodityStandardname, v.CommodityStandardcode, v.CommodityVender,
				v.CommdityFormat, v.CommodityUnit, tempQuantity, v.BatchPrice, v.Payprice, list.LotNumber, v.CommodityBatch, countID)

			if err != nil {
				return err
			}

		}
	}
	return err
}

func UpdateStockAddTo(tx *sql.Tx, quantity float64, lotNumber string, conCommodityID int64) error {
	// 更新库存数量
	updateStockAddToSQL := "update g_new_stock set quantity = quantity +  ? * 1000  where lot_number = ? and con_commodity_id = ?"

	updateStockAddToStmt, err := tx.Prepare(updateStockAddToSQL)
	if err != nil {
		return nil
	}

	_, err = updateStockAddToStmt.Exec(quantity, lotNumber, conCommodityID)
	if err != nil {
		return err
	}
	return nil
}

func AddExWarehouseSettlementService(phone string, p *models.SettlementSheet) (interface{}, error) {

	chainCode, userId, err := repository.QueryChainCodeAndLocalIDByPhone(phone)
	if err != nil {
		return nil, err
	}

	if chainCode == "" {
		return nil, errors.New("请设置默认医院")
	}

	// 2. 拿到db
	safeMap := common.NewSafeMap()
	db, err := safeMap.GetOrStore(chainCode)
	if err != nil {
		return nil, err
	}

	hospitalId, err := repository.QueryHospitalLocalIDByEmployeeLocalID(db, userId)
	if err != nil {
		return nil, err
	}

	if p.Totalmoney == 0 {
		return nil, errors.New("金额不能等于0")
	}

	// 判断本次结算金额 与 实付金额 是否一致
	var preEttlementParam float64 // 预付款结算金额
	var settlementPrice float64   // 本次结算金额
	for _, v := range p.BillDetailData {
		settlementPrice += v.Money
		if v.Paytype == 6 {
			preEttlementParam += v.Money
		}
	}
	if p.Paymoney != settlementPrice {
		return nil, errors.New("金额不符")
	}

	// 判断实付金额 与 本次结算总额 是否一致
	var thisTimeSettlementPrice float64
	for _, v := range p.SettlementDetail {
		thisTimeSettlementPrice += v.ThisTimeSettlementPrice
	}

	thisTimeSettlementPrice, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", thisTimeSettlementPrice), 64)

	if p.Paymoney != thisTimeSettlementPrice {
		return nil, errors.New("金额不符")
	}

	// 置为已结清
	changeToSettledSQL := "update g_new_stockout_io set is_payover = 1 where id = %v"

	// 1. 查询出 往来单位的预结算余额
	queryPreEttlementPriceSQL := `select convert(previous_receivebalance / 1000, decimal(11,3)) as previous_receivebalance from con_company where delflag = 0 and id = ?`
	var previousPayBalance float64
	err = db.QueryRow(queryPreEttlementPriceSQL, p.ConCompanyID).Scan(&previousPayBalance)
	if err != nil {
		return nil, err
	}

	// 当前余额
	currentBalance := previousPayBalance - preEttlementParam

	tx, err := db.Begin()
	if err != nil {
		return nil, err
	}

	// 创建账单
	// 7. 操作账单
	billSQL := `insert g_new_stockout_bill set creater = %v, sys_hospital_id = %v, con_company_id = %v,
con_company_name = '%v', con_employee_id = %v, con_employee_name = '%v', _code = '%v',
totalmoney = %v * 1000, paymoney = %v * 1000, eventtime = '%v', description = '%v'`
	billSQL = fmt.Sprintf(billSQL, userId, hospitalId, p.ConCompanyID, p.ConCompanyName,
		p.ConEmployeeID, p.ConEmployeeName, p.Code, p.Totalmoney, p.Paymoney, p.Eventtime, p.Discription)
	billResult, err := tx.Exec(billSQL)
	if err != nil {
		tx.Rollback()
		return nil, err
	}
	billId, err := billResult.LastInsertId()
	if err != nil {
		tx.Rollback()
		return nil, err
	}

	// 循环处理 采购单
	for _, v := range p.SettlementDetail {

		// 8. 操作账单详情
		billDetailSQL := `insert g_new_stockoutbill_detail set creater = %v, sys_hospital_id = %v, g_new_stockoutbill_id = %v, g_new_stockoutio_id = %v, money = %v * 1000, discription = '%v'`
		billDetailSQL = fmt.Sprintf(billDetailSQL, userId, hospitalId, billId, v.ID, v.ThisTimeSettlementPrice, v.Discription)
		billDetailRestult, err := tx.Exec(billDetailSQL)
		if err != nil {
			tx.Rollback()
			return nil, err
		}
		_, err = billDetailRestult.LastInsertId()
		if err != nil {
			tx.Rollback()
			return nil, err
		}

		// 欠款还款
		_, err_ := tx.Exec("update con_company set   arrears  = arrears  - (? * 1000) where id = ?   ", v.ThisTimeSettlementPrice, p.ConCompanyID)
		if err_ != nil {
			tx.Rollback()
			return nil, err_
		}

		// 剩余结算 和 待结算金额 一致 就可以置为已结清
		if (v.SumMoney - v.HistoricalSettlementPrice) == v.ThisTimeSettlementPrice {

			// 置为已结清
			changeToSettledResult, err := tx.Exec(fmt.Sprintf(changeToSettledSQL, v.ID))
			if err != nil {
				tx.Rollback()
				return nil, err
			}
			_, err = changeToSettledResult.RowsAffected()
			if err != nil {
				tx.Rollback()
				return nil, err
			}
		}

	}

	// 9. 操作账单结算方式
	for _, BillDetail := range p.BillDetailData {

		if BillDetail.Money > 0 {
			// 添加具体结算详情
			payDetail := "insert g_new_stockoutbill_pay set creater = %v,  sys_hospital_id = %v, g_new_stockoutbill_id = %v, paytype = %v, money = %v * 1000, eventtime = '%v'"
			payDetail = fmt.Sprintf(payDetail, userId, hospitalId, billId, BillDetail.Paytype, BillDetail.Money)
			payDetailResult, err := tx.Exec(payDetail)
			if err != nil {
				tx.Rollback()
				return nil, err
			}
			_, err = payDetailResult.LastInsertId()
			if err != nil {
				tx.Rollback()
				return nil, err
			}
			if BillDetail.Paytype != 5 {
				// 资金流
				cashFlowSQL := `insert c_cashflow set creater = %v, sys_hospital_id = %v, bussiness_id = %v, bussiness_type = %v, pay_type = %v, money = %v * 1000, eventime = '%v'`
				cashFlowSQL = fmt.Sprintf(cashFlowSQL, userId, hospitalId, billId, 6, BillDetail.Paytype, BillDetail.Money, p.Eventtime)
				cashFlowResult, err := tx.Exec(cashFlowSQL)
				if err != nil {
					tx.Rollback()
					return nil, err
				}
				_, err = cashFlowResult.LastInsertId()
				if err != nil {
					tx.Rollback()
					return nil, err
				}
			}

		}

		if BillDetail.Paytype == 6 && BillDetail.Money > 0 {
			// 添加一条预付款
			insertChargeSQL := "insert c_prein_fund set creater = %v, sys_hospital_id = %v, con_company_id = %v, _code = '%v', type = %v, money = %v * 1000, currentmoney = %v * 1000, discription = '%v', con_employee_id = %v, con_employee_name = '%v', eventtime = '%v'"

			insertChargeSQL = fmt.Sprintf(insertChargeSQL, userId, hospitalId, p.ConCompanyID, p.Code, 2, preEttlementParam, currentBalance, "", p.ConEmployeeID, p.ConEmployeeName, p.Eventtime)

			// 查询 c_preout_fund code + 1   替换 param.Code

			_, err = tx.Exec(insertChargeSQL)
			if err != nil {
				tx.Rollback()
				return nil, err
			}

			updateCompanySQL := fmt.Sprintf("update con_company set previous_receivebalance = %v * 1000 where id = %v", currentBalance, p.ConCompanyID)
			res, err := tx.Exec(updateCompanySQL)
			if err != nil {
				tx.Rollback()
				return nil, err
			}

			_, err = res.RowsAffected()
			if err != nil {
				tx.Rollback()
				return nil, err
			}
		}
	}

	tx.Commit()

	return nil, err
}
