From 76a84d89d63041232c646ca28c59239dd00f7fc5 Mon Sep 17 00:00:00 2001
From: liujiandao <274878379@qq.com>
Date: 星期二, 30 四月 2024 09:58:46 +0800
Subject: [PATCH] 薪资计算

---
 task/salary_statistics.go                     |  227 ++++++++++++++++
 controllers/response/report_forms_response.go |    8 
 models/mini_dict.go                           |   20 +
 controllers/report_forms_controller.go        |   32 ++
 controllers/request/worker_request.go         |    1 
 controllers/salary_plan_controller.go         |   70 ++++
 go.mod                                        |    3 
 utils/salary_calculate.go                     |   63 ++++
 models/raw_silk_price_standard.go             |   18 +
 constvar/const.go                             |   18 +
 models/salary_plan.go                         |    8 
 controllers/attendance_controller.go          |    1 
 controllers/request/report_forms_request.go   |    6 
 models/salary_report_form.go                  |  163 +++++++++++
 go.sum                                        |    2 
 models/work_type_manage.go                    |   11 
 models/salary_details.go                      |  158 +++++++++++
 models/yield_register_item.go                 |   21 +
 models/attendance_manage.go                   |    1 
 19 files changed, 813 insertions(+), 18 deletions(-)

diff --git a/constvar/const.go b/constvar/const.go
index 018497d..b89e679 100644
--- a/constvar/const.go
+++ b/constvar/const.go
@@ -107,3 +107,21 @@
 	FileTemplateCategory_JialianOutput     = 15                            //鍢夎仈鍑哄簱
 	FileTemplateCategory_JialianAttendance = 16                            //鍢夎仈-鍛樺伐鑰冨嫟
 )
+
+const (
+	DailySilkProduction     = "鏃ヤ骇涓濋噺"
+	WasteSilkQuantity       = "閲庣氦鏁伴噺"
+	RawSilkUnitPrice        = "鐢熶笣鍗曚环"
+	WasteSilkUnitPrice      = "閲庣氦鍗曚环"
+	BucketCount             = "妗舵暟(鏃�)"
+	AttendanceDays          = "鍑哄嫟澶╂暟"
+	GroupAverageMonthlyWage = "鍚岀粍鎸¤溅宸ユ湀骞冲潎宸ヨ祫"
+	GroupCarHeadWage        = "鍚岀粍杞﹀ご宸ュ伐璧�"
+	WeekdayOvertimeHours    = "宸ヤ綔鏃ュ姞鐝椂闀�"
+	FullAttendanceAward     = "婊″嫟濂�"
+	WeekendOvertimeHours    = "浼戞伅鏃ュ姞鐝椂闀�"
+	LeaveDays               = "璇峰亣澶╂暟"
+	ApprenticeDays          = "甯﹀緬澶╂暟"
+	TotalAttendanceDays     = "鍑哄嫟澶╂暟"
+	Seniority               = "宸ラ緞"
+)
diff --git a/controllers/attendance_controller.go b/controllers/attendance_controller.go
index 7d0d3f1..984b09a 100644
--- a/controllers/attendance_controller.go
+++ b/controllers/attendance_controller.go
@@ -114,6 +114,7 @@
 		for _, worker := range workers {
 			if attendance.WorkerId == worker.ID {
 				attendance.WorkTypeId = worker.WorkTypeId
+				attendance.PhoneNum = worker.PhoneNum
 				break
 			}
 		}
diff --git a/controllers/report_forms_controller.go b/controllers/report_forms_controller.go
new file mode 100644
index 0000000..b00be87
--- /dev/null
+++ b/controllers/report_forms_controller.go
@@ -0,0 +1,32 @@
+package controllers
+
+import (
+	"github.com/gin-gonic/gin"
+)
+
+type ReportFormsController struct {
+}
+
+// SalaryReportForms
+//
+//	@Tags		鎶ヨ〃绠$悊
+//	@Summary	钖祫鎶ヨ〃
+//	@Produce	application/json
+//	@Param		object	body		request.SalaryReportForms	true	"鍙傛暟"
+//	@Param     	Authorization	header string true "token"
+//	@Success	200		{object}	util.ResponseList{data=[]models.WorkTypeManage}		"鎴愬姛"
+//	@Router		/api-jl/v1/forms/salaryReportForms [post]
+func (slf ReportFormsController) SalaryReportForms(c *gin.Context) {
+	//var params request.SalaryReportForms
+	//err := c.BindJSON(&params)
+	//if err != nil {
+	//	util.ResponseFormat(c, code.RequestParamError, "鍙傛暟瑙f瀽澶辫触锛屾暟鎹被鍨嬮敊璇�")
+	//	return
+	//}
+	//workers, err := models.NewWorkerSearch().SetPage(params.Page, params.PageSize).FindNotTotal()
+	//if err != nil {
+	//	util.ResponseFormat(c, code.RequestParamError, err)
+	//	return
+	//}
+
+}
diff --git a/controllers/request/report_forms_request.go b/controllers/request/report_forms_request.go
new file mode 100644
index 0000000..6ce2f3d
--- /dev/null
+++ b/controllers/request/report_forms_request.go
@@ -0,0 +1,6 @@
+package request
+
+type SalaryReportForms struct {
+	PageInfo
+	Month string `json:"month"`
+}
diff --git a/controllers/request/worker_request.go b/controllers/request/worker_request.go
index 1e15736..3ee1f39 100644
--- a/controllers/request/worker_request.go
+++ b/controllers/request/worker_request.go
@@ -23,6 +23,7 @@
 }
 
 type SalaryTypeValue struct {
+	Id        uint   `json:"id"`
 	Name      string `json:"name"`      //鍚嶇О
 	IsDefault bool   `json:"isDefault"` //鏄惁鍙紪杈�
 }
diff --git a/controllers/response/report_forms_response.go b/controllers/response/report_forms_response.go
new file mode 100644
index 0000000..598df6e
--- /dev/null
+++ b/controllers/response/report_forms_response.go
@@ -0,0 +1,8 @@
+package response
+
+type SalaryReportForms struct {
+	WorkerName string `json:"workerName"`
+	WorkerId   string `json:"workerId"`
+	Phone      string `json:"phone"`
+	WorkType   string `json:"workType"`
+}
diff --git a/controllers/salary_plan_controller.go b/controllers/salary_plan_controller.go
index fb8d9aa..02109c9 100644
--- a/controllers/salary_plan_controller.go
+++ b/controllers/salary_plan_controller.go
@@ -127,23 +127,75 @@
 		util.ResponseFormat(c, code.RequestParamError, "绫诲瀷涓嶈兘涓虹┖")
 		return
 	}
-	types := make([]*models.MiniDict, 0)
-	for _, value := range params.Values {
+	miniDicts, err := models.NewMiniDictSearch().SetType(params.Type).FindNotTotal()
+	if err != nil {
+		util.ResponseFormat(c, code.RequestParamError, err)
+		return
+	}
+	dicts := params.Values
+	add := make([]*models.MiniDict, 0)
+	del := make([]uint, 0)
+	update := make([]*models.MiniDict, 0)
+	for _, mini := range miniDicts {
+		flag := true
+		for i, value := range dicts {
+			var dict models.MiniDict
+			dict.Name = value.Name
+			dict.IsDefault = value.IsDefault
+			dict.Type = params.Type
+			if value.Id == 0 {
+				add = append(add, &dict)
+				flag = false
+				if i < len(dicts)-1 {
+					dicts = append(dicts[:i], dicts[i+1:]...)
+				} else {
+					dicts = dicts[:i]
+				}
+				break
+			} else if value.Id == mini.ID {
+				update = append(update, &dict)
+				flag = false
+				if i < len(dicts)-1 {
+					dicts = append(dicts[:i], dicts[i+1:]...)
+				} else {
+					dicts = dicts[:i]
+				}
+				break
+			}
+		}
+		if flag {
+			del = append(del, mini.ID)
+		}
+	}
+	//鏂板鐨�
+	for _, value := range dicts {
 		var dict models.MiniDict
 		dict.Name = value.Name
 		dict.IsDefault = value.IsDefault
 		dict.Type = params.Type
-		types = append(types, &dict)
+		add = append(add, &dict)
 	}
+
 	err = models.WithTransaction(func(db *gorm.DB) error {
-		err = models.NewMiniDictSearch().SetOrm(db).SetType(params.Type).Delete()
-		if err != nil {
-			return err
+		if len(del) > 0 {
+			err = models.NewMiniDictSearch().SetOrm(db).SetIds(del).Delete()
+			if err != nil {
+				return err
+			}
 		}
-		err = models.NewMiniDictSearch().SetOrm(db).CreateBatch(types)
-		if err != nil {
-			return err
+		if len(update) > 0 {
+			err = models.NewMiniDictSearch().SetOrm(db).SaveBatch(update)
+			if err != nil {
+				return err
+			}
 		}
+		if len(add) > 0 {
+			err = models.NewMiniDictSearch().SetOrm(db).CreateBatch(add)
+			if err != nil {
+				return err
+			}
+		}
+
 		return nil
 	})
 	if err != nil {
diff --git a/go.mod b/go.mod
index 9990c00..0b283cf 100644
--- a/go.mod
+++ b/go.mod
@@ -4,11 +4,13 @@
 
 require (
 	basic.com/aps/nsqclient.git v0.0.0-20230517072415-37491f4a5d25
+	github.com/Knetic/govaluate v3.0.0+incompatible
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 	github.com/gin-gonic/gin v1.9.1
 	github.com/golang-jwt/jwt/v4 v4.5.0
 	github.com/nsqio/go-nsq v1.1.0
 	github.com/shopspring/decimal v1.3.1
+	github.com/spf13/cast v1.6.0
 	github.com/spf13/viper v1.18.2
 	github.com/swaggo/files v1.0.1
 	github.com/swaggo/gin-swagger v1.6.0
@@ -64,7 +66,6 @@
 	github.com/sagikazarmark/slog-shim v0.1.0 // indirect
 	github.com/sourcegraph/conc v0.3.0 // indirect
 	github.com/spf13/afero v1.11.0 // indirect
-	github.com/spf13/cast v1.6.0 // indirect
 	github.com/spf13/pflag v1.0.5 // indirect
 	github.com/subosito/gotenv v1.6.0 // indirect
 	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
diff --git a/go.sum b/go.sum
index 61b0da4..f0f9548 100644
--- a/go.sum
+++ b/go.sum
@@ -1,5 +1,7 @@
 basic.com/aps/nsqclient.git v0.0.0-20230517072415-37491f4a5d25 h1:sZyNfIISgP1eoY94LG48Kav6HYVLem6EzaEbCeXlcXQ=
 basic.com/aps/nsqclient.git v0.0.0-20230517072415-37491f4a5d25/go.mod h1:1RnwEtePLR7ATQorQTxdgvs1o7uuUy1Vw8W7GYtVnoY=
+github.com/Knetic/govaluate v3.0.0+incompatible h1:7o6+MAPhYTCF0+fdvoz1xDedhRb4f6s9Tn1Tt7/WTEg=
+github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
 github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
 github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
 github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
diff --git a/models/attendance_manage.go b/models/attendance_manage.go
index 26996cc..412bf42 100644
--- a/models/attendance_manage.go
+++ b/models/attendance_manage.go
@@ -26,6 +26,7 @@
 		WorkType         WorkTypeManage            `json:"workType" gorm:"foreignKey:WorkTypeId"`
 		Status           constvar.AttendanceStatus `json:"status" gorm:"type:int(11);comment:鐘舵��"`
 		OverTimeDuration decimal.Decimal           `json:"overTimeDuration" gorm:"type:decimal(20,2);comment:鍔犵彮鏃堕暱"`
+		PhoneNum         string                    `gorm:"type:varchar(191);comment:鎵嬫満鍙�" json:"phoneNum"`
 	}
 	AttendanceManageSearch struct {
 		AttendanceManage
diff --git a/models/mini_dict.go b/models/mini_dict.go
index 729307e..8941a78 100644
--- a/models/mini_dict.go
+++ b/models/mini_dict.go
@@ -20,6 +20,7 @@
 	MiniDictSearch struct {
 		MiniDict
 		Order    string
+		Ids      []uint
 		PageNum  int
 		PageSize int
 		Orm      *gorm.DB
@@ -54,6 +55,11 @@
 	return slf
 }
 
+func (slf *MiniDictSearch) SetIds(ids []uint) *MiniDictSearch {
+	slf.Ids = ids
+	return slf
+}
+
 func (slf *MiniDictSearch) SetType(tp constvar.MiniDictType) *MiniDictSearch {
 	slf.Type = tp
 	return slf
@@ -74,6 +80,10 @@
 
 	if slf.ID > 0 {
 		db = db.Where("id = ?", slf.ID)
+	}
+
+	if len(slf.Ids) > 0 {
+		db = db.Where("id in (?)", slf.Ids)
 	}
 
 	if slf.Type > 0 {
@@ -128,6 +138,16 @@
 	return nil
 }
 
+func (slf *MiniDictSearch) SaveBatch(record []*MiniDict) error {
+	var db = slf.build()
+
+	if err := db.Omit("CreatedAt").Save(record).Error; err != nil {
+		return fmt.Errorf("save err: %v, record: %+v", err, record)
+	}
+
+	return nil
+}
+
 func (slf *MiniDictSearch) UpdateByMap(upMap map[string]interface{}) error {
 	var (
 		db = slf.build()
diff --git a/models/raw_silk_price_standard.go b/models/raw_silk_price_standard.go
index 05348ab..fea5114 100644
--- a/models/raw_silk_price_standard.go
+++ b/models/raw_silk_price_standard.go
@@ -20,10 +20,11 @@
 	}
 	RawSilkPriceStandardSearch struct {
 		RawSilkPriceStandard
-		Order    string
-		PageNum  int
-		PageSize int
-		Orm      *gorm.DB
+		Order       string
+		PageNum     int
+		PageSize    int
+		MarketNames []string
+		Orm         *gorm.DB
 	}
 )
 
@@ -55,6 +56,11 @@
 	return slf
 }
 
+func (slf *RawSilkPriceStandardSearch) SetMarketNames(names []string) *RawSilkPriceStandardSearch {
+	slf.MarketNames = names
+	return slf
+}
+
 func (slf *RawSilkPriceStandardSearch) build() *gorm.DB {
 	db := slf.Orm.Table(slf.TableName())
 
@@ -62,6 +68,10 @@
 		db = db.Where("id = ?", slf.ID)
 	}
 
+	if len(slf.MarketNames) > 0 {
+		db = db.Where("market_name in (?)", slf.MarketNames)
+	}
+
 	return db
 }
 
diff --git a/models/salary_details.go b/models/salary_details.go
new file mode 100644
index 0000000..eec1e11
--- /dev/null
+++ b/models/salary_details.go
@@ -0,0 +1,158 @@
+package models
+
+import (
+	"fmt"
+	"github.com/shopspring/decimal"
+	"gorm.io/gorm"
+	"silkserver/pkg/mysqlx"
+)
+
+type (
+	//SalaryDetails 钖祫鏄庣粏琛�
+	SalaryDetails struct {
+		gorm.Model
+		SalaryDetailsId uint            `json:"SalaryDetailsId" gorm:"type:int(11);comment:钖祫鎶ヨ〃id"`
+		SalaryTypeId    uint            `json:"salaryTypeId" gorm:"type:int(11);comment:钖祫绫诲瀷id"`
+		SalaryType      MiniDict        `json:"salaryType" gorm:"foreignKey:SalaryTypeId;references:ID"`
+		Amount          decimal.Decimal `json:"amount" gorm:"type:decimal(20,3);comment:宸ヨ祫鍊�"`
+	}
+	SalaryDetailsSearch struct {
+		SalaryDetails
+		PageNum  int
+		PageSize int
+		Preload  bool
+		Orm      *gorm.DB
+	}
+)
+
+func (slf SalaryDetails) TableName() string {
+	return "salary_details"
+}
+
+func NewSalaryDetailsSearch() *SalaryDetailsSearch {
+	return &SalaryDetailsSearch{Orm: mysqlx.GetDB()}
+}
+
+func (slf *SalaryDetailsSearch) SetOrm(tx *gorm.DB) *SalaryDetailsSearch {
+	slf.Orm = tx
+	return slf
+}
+
+func (slf *SalaryDetailsSearch) SetPage(page, size int) *SalaryDetailsSearch {
+	slf.PageNum, slf.PageSize = page, size
+	return slf
+}
+
+func (slf *SalaryDetailsSearch) SetPreload(preload bool) *SalaryDetailsSearch {
+	slf.Preload = preload
+	return slf
+}
+
+func (slf *SalaryDetailsSearch) build() *gorm.DB {
+	var db = slf.Orm.Table(slf.TableName())
+
+	if slf.Preload {
+		db = db.Model(SalaryDetails{}).Preload("SalaryType")
+	}
+
+	return db
+}
+
+// Create 鍗曟潯鎻掑叆
+func (slf *SalaryDetailsSearch) Create(record *SalaryDetails) error {
+	var db = slf.build()
+
+	if err := db.Create(record).Error; err != nil {
+		return fmt.Errorf("create err: %v, record: %+v", err, record)
+	}
+
+	return nil
+}
+
+// CreateBatch 鎵归噺鎻掑叆
+func (slf *SalaryDetailsSearch) CreateBatch(record []*SalaryDetails) error {
+	var db = slf.build()
+
+	if err := db.Create(record).Error; err != nil {
+		return fmt.Errorf("create err: %v, record: %+v", err, record)
+	}
+
+	return nil
+}
+
+func (slf *SalaryDetailsSearch) Save(record *SalaryDetails) error {
+	var db = slf.build()
+
+	if err := db.Omit("CreatedAt").Save(record).Error; err != nil {
+		return fmt.Errorf("save err: %v, record: %+v", err, record)
+	}
+
+	return nil
+}
+
+func (slf *SalaryDetailsSearch) UpdateByMap(upMap map[string]interface{}) error {
+	var (
+		db = slf.build()
+	)
+
+	if err := db.Updates(upMap).Error; err != nil {
+		return fmt.Errorf("update by map err: %v, upMap: %+v", err, upMap)
+	}
+
+	return nil
+}
+
+func (slf *SalaryDetailsSearch) Delete() error {
+	var db = slf.build()
+
+	if err := db.Unscoped().Delete(&SalaryDetails{}).Error; err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (slf *SalaryDetailsSearch) Find() ([]*SalaryDetails, int64, error) {
+	var (
+		records = make([]*SalaryDetails, 0)
+		total   int64
+		db      = slf.build()
+	)
+
+	if err := db.Count(&total).Error; err != nil {
+		return records, total, fmt.Errorf("find count err: %v", err)
+	}
+	if slf.PageNum*slf.PageSize > 0 {
+		db = db.Offset((slf.PageNum - 1) * slf.PageSize).Limit(slf.PageSize)
+	}
+	if err := db.Find(&records).Error; err != nil {
+		return records, total, fmt.Errorf("find records err: %v", err)
+	}
+
+	return records, total, nil
+}
+
+func (slf *SalaryDetailsSearch) FindNotTotal() ([]*SalaryDetails, error) {
+	var (
+		records = make([]*SalaryDetails, 0)
+		db      = slf.build()
+	)
+
+	if err := db.Find(&records).Error; err != nil {
+		return records, fmt.Errorf("find records err: %v", err)
+	}
+
+	return records, nil
+}
+
+func (slf *SalaryDetailsSearch) Count() (int64, error) {
+	var (
+		total int64
+		db    = slf.build()
+	)
+
+	if err := db.Count(&total).Error; err != nil {
+		return total, fmt.Errorf("find count err: %v", err)
+	}
+	return total, nil
+}
diff --git a/models/salary_plan.go b/models/salary_plan.go
index 1c326ec..55e84d6 100644
--- a/models/salary_plan.go
+++ b/models/salary_plan.go
@@ -10,8 +10,10 @@
 	//SalaryPlan 钖祫鏂规
 	SalaryPlan struct {
 		gorm.Model
-		Name          string            `json:"name" gorm:"type:varchar(255);comment:钖祫鏂规鍚嶇О"`
-		SalaryType    string            `json:"salaryType" gorm:"type:varchar(255);comment:钖祫绫诲瀷"`
+		Name         string   `json:"name" gorm:"type:varchar(255);comment:钖祫鏂规鍚嶇О"`
+		SalaryTypeId uint     `json:"salaryTypeId" gorm:"type:int(11);comment:钖祫绫诲瀷id"`
+		SalaryType   MiniDict `json:"salaryType" gorm:"foreignKey:SalaryTypeId;references:ID"`
+		//SalaryType    string            `json:"salaryType" gorm:"type:varchar(255);comment:钖祫绫诲瀷"`
 		SalaryFormula string            `json:"salaryFormula" gorm:"type:varchar(255);comment:钖祫鍏紡"`
 		Cycle         string            `json:"cycle" gorm:"type:varchar(255);comment:鍛ㄦ湡"`
 		CreateTime    string            `json:"createTime" gorm:"type:varchar(255);comment:娣诲姞鏃堕棿"`
@@ -63,7 +65,7 @@
 	}
 
 	if slf.Preload {
-		db = db.Model(&SalaryPlan{}).Preload("WorkTypes")
+		db = db.Model(&SalaryPlan{}).Preload("WorkTypes").Preload("SalaryType")
 	}
 
 	return db
diff --git a/models/salary_report_form.go b/models/salary_report_form.go
new file mode 100644
index 0000000..67aa59f
--- /dev/null
+++ b/models/salary_report_form.go
@@ -0,0 +1,163 @@
+package models
+
+import (
+	"fmt"
+	"github.com/shopspring/decimal"
+	"gorm.io/gorm"
+	"silkserver/pkg/mysqlx"
+)
+
+type (
+	// SalaryReportForm 钖祫鎶ヨ〃
+	SalaryReportForm struct {
+		gorm.Model
+		WorkerId    string          `json:"workerId" gorm:"type:varchar(255);comment:浜哄憳id"`
+		WorkerName  string          `json:"workerName"  gorm:"type:varchar(255);comment:浜哄憳濮撳悕"`
+		Phone       string          `json:"phone" gorm:"type:varchar(255);comment:鐢佃瘽"`
+		WorkTypeId  uint            `json:"workTypeId" gorm:"type:int(11);comment:宸ョ绫诲瀷id"`
+		WorkType    WorkTypeManage  `json:"workType"  gorm:"foreignKey:WorkTypeId;references:ID"`
+		Month       string          `json:"month" gorm:"type:varchar(255);comment:鏈堜唤"`
+		IssueSalary decimal.Decimal `json:"issueSalary" gorm:"type:decimal(20,3);comment:搴斿彂宸ヨ祫"`
+		Remark      string          `json:"remark" gorm:"type:varchar(255);comment:澶囨敞"`
+		Details     []SalaryDetails `json:"details" gorm:"foreignKey:SalaryReportFormId;references:Id"`
+	}
+	SalaryReportFormSearch struct {
+		SalaryReportForm
+		PageNum  int
+		PageSize int
+		Preload  bool
+		Orm      *gorm.DB
+	}
+)
+
+func (slf SalaryReportForm) TableName() string {
+	return "salary_report_form"
+}
+
+func NewSalaryReportFormSearch() *SalaryReportFormSearch {
+	return &SalaryReportFormSearch{Orm: mysqlx.GetDB()}
+}
+
+func (slf *SalaryReportFormSearch) SetOrm(tx *gorm.DB) *SalaryReportFormSearch {
+	slf.Orm = tx
+	return slf
+}
+
+func (slf *SalaryReportFormSearch) SetPage(page, size int) *SalaryReportFormSearch {
+	slf.PageNum, slf.PageSize = page, size
+	return slf
+}
+
+func (slf *SalaryReportFormSearch) SetPreload(preload bool) *SalaryReportFormSearch {
+	slf.Preload = preload
+	return slf
+}
+
+func (slf *SalaryReportFormSearch) build() *gorm.DB {
+	var db = slf.Orm.Table(slf.TableName())
+
+	if slf.Preload {
+		db = db.Model(SalaryReportForm{}).Preload("Details").Preload("WorkType")
+	}
+
+	return db
+}
+
+// Create 鍗曟潯鎻掑叆
+func (slf *SalaryReportFormSearch) Create(record *SalaryReportForm) error {
+	var db = slf.build()
+
+	if err := db.Create(record).Error; err != nil {
+		return fmt.Errorf("create err: %v, record: %+v", err, record)
+	}
+
+	return nil
+}
+
+// CreateBatch 鎵归噺鎻掑叆
+func (slf *SalaryReportFormSearch) CreateBatch(record []*SalaryReportForm) error {
+	var db = slf.build()
+
+	if err := db.Create(record).Error; err != nil {
+		return fmt.Errorf("create err: %v, record: %+v", err, record)
+	}
+
+	return nil
+}
+
+func (slf *SalaryReportFormSearch) Save(record *SalaryReportForm) error {
+	var db = slf.build()
+
+	if err := db.Omit("CreatedAt").Save(record).Error; err != nil {
+		return fmt.Errorf("save err: %v, record: %+v", err, record)
+	}
+
+	return nil
+}
+
+func (slf *SalaryReportFormSearch) UpdateByMap(upMap map[string]interface{}) error {
+	var (
+		db = slf.build()
+	)
+
+	if err := db.Updates(upMap).Error; err != nil {
+		return fmt.Errorf("update by map err: %v, upMap: %+v", err, upMap)
+	}
+
+	return nil
+}
+
+func (slf *SalaryReportFormSearch) Delete() error {
+	var db = slf.build()
+
+	if err := db.Unscoped().Delete(&SalaryReportForm{}).Error; err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (slf *SalaryReportFormSearch) Find() ([]*SalaryReportForm, int64, error) {
+	var (
+		records = make([]*SalaryReportForm, 0)
+		total   int64
+		db      = slf.build()
+	)
+
+	if err := db.Count(&total).Error; err != nil {
+		return records, total, fmt.Errorf("find count err: %v", err)
+	}
+	if slf.PageNum*slf.PageSize > 0 {
+		db = db.Offset((slf.PageNum - 1) * slf.PageSize).Limit(slf.PageSize)
+	}
+	if err := db.Find(&records).Error; err != nil {
+		return records, total, fmt.Errorf("find records err: %v", err)
+	}
+
+	return records, total, nil
+}
+
+func (slf *SalaryReportFormSearch) FindNotTotal() ([]*SalaryReportForm, error) {
+	var (
+		records = make([]*SalaryReportForm, 0)
+		db      = slf.build()
+	)
+
+	if err := db.Find(&records).Error; err != nil {
+		return records, fmt.Errorf("find records err: %v", err)
+	}
+
+	return records, nil
+}
+
+func (slf *SalaryReportFormSearch) Count() (int64, error) {
+	var (
+		total int64
+		db    = slf.build()
+	)
+
+	if err := db.Count(&total).Error; err != nil {
+		return total, fmt.Errorf("find count err: %v", err)
+	}
+	return total, nil
+}
diff --git a/models/work_type_manage.go b/models/work_type_manage.go
index e19ce79..d937a25 100644
--- a/models/work_type_manage.go
+++ b/models/work_type_manage.go
@@ -8,6 +8,7 @@
 )
 
 type (
+	//WorkTypeManage 宸ョ绠$悊
 	WorkTypeManage struct {
 		gorm.Model
 		WorkName        string          `json:"workName" gorm:"type:varchar(255);comment:宸ョ鍚嶇О"`
@@ -22,6 +23,7 @@
 		PageNum  int
 		PageSize int
 		Preload  bool
+		Ids      []uint
 		Orm      *gorm.DB
 	}
 )
@@ -54,6 +56,11 @@
 	return slf
 }
 
+func (slf *WorkTypeManageSearch) SetIds(ids []uint) *WorkTypeManageSearch {
+	slf.Ids = ids
+	return slf
+}
+
 func (slf *WorkTypeManageSearch) build() *gorm.DB {
 	var db = slf.Orm.Table(slf.TableName())
 
@@ -61,6 +68,10 @@
 		db = db.Where("id = ?", slf.ID)
 	}
 
+	if len(slf.Ids) > 0 {
+		db = db.Where("id in (?)", slf.Ids)
+	}
+
 	if slf.Preload {
 		db = db.Model(WorkTypeManage{}).Preload("SalaryPlans")
 	}
diff --git a/models/yield_register_item.go b/models/yield_register_item.go
index e9a643a..92720c5 100644
--- a/models/yield_register_item.go
+++ b/models/yield_register_item.go
@@ -20,7 +20,9 @@
 	}
 	YieldRegisterItemSearch struct {
 		YieldRegisterItem
-		Orm *gorm.DB
+		YieldRegisterIds []uint
+		CarNumbers       []int
+		Orm              *gorm.DB
 	}
 )
 
@@ -42,6 +44,15 @@
 	return slf
 }
 
+func (slf *YieldRegisterItemSearch) SetYieldRegisterIds(ids []uint) *YieldRegisterItemSearch {
+	slf.YieldRegisterIds = ids
+	return slf
+}
+func (slf *YieldRegisterItemSearch) SetCarNumbers(carNumbers []int) *YieldRegisterItemSearch {
+	slf.CarNumbers = carNumbers
+	return slf
+}
+
 func (slf *YieldRegisterItemSearch) build() *gorm.DB {
 	db := slf.Orm.Table(slf.TableName())
 
@@ -49,6 +60,14 @@
 		db = db.Where("yield_register_id = ?", slf.YieldRegisterId)
 	}
 
+	if len(slf.YieldRegisterIds) > 0 {
+		db = db.Where("yield_register_id in (?)", slf.YieldRegisterIds)
+	}
+
+	if len(slf.CarNumbers) > 0 {
+		db = db.Where("car_number in (?)", slf.CarNumbers)
+	}
+
 	return db
 }
 
diff --git a/task/salary_statistics.go b/task/salary_statistics.go
new file mode 100644
index 0000000..07e1a1e
--- /dev/null
+++ b/task/salary_statistics.go
@@ -0,0 +1,227 @@
+package task
+
+import (
+	"github.com/shopspring/decimal"
+	"silkserver/constvar"
+	"silkserver/models"
+	"silkserver/pkg/logx"
+	"silkserver/utils"
+	"strings"
+	"time"
+)
+
+func SalaryStatistics() {
+	lastMonthStart, lastMonthEnd := GetLastMonthPeriod()
+	month := lastMonthStart.Format("2006-01")
+
+	//鏌ヨ鑰冨嫟缁熻
+	startStr := lastMonthStart.Format("2006-01-02")
+	endStr := lastMonthEnd.Format("2006-01-02")
+	attendances, err := models.NewAttendanceManageSearch().SetMonth(month).FindNotTotal()
+	if err != nil {
+		logx.Error("SalaryStatistics 鏌ヨ鑰冨嫟缁熻 err: " + err.Error())
+		return
+	}
+
+	var reportForms []*models.SalaryReportForm
+	workTypeIdMap := make(map[uint]uint)
+	dataMap := make(map[string]utils.SalaryCalculateData)
+	for _, attendance := range attendances {
+		var rf models.SalaryReportForm
+		var data utils.SalaryCalculateData
+		if _, ok := dataMap[attendance.WorkerId]; ok {
+			data = dataMap[attendance.WorkerId]
+		}
+		rf.WorkerId = attendance.WorkerId
+		rf.WorkerName = attendance.WorkerName
+		rf.WorkTypeId = attendance.WorkTypeId
+		rf.Month = month
+		rf.Phone = attendance.PhoneNum
+		if attendance.WorkTypeId > 0 {
+			workTypeIdMap[attendance.WorkTypeId] = attendance.WorkTypeId
+		}
+		//宸ヤ綔鏃ュ姞鐝椂闀�
+		data.WeekdayOvertimeHours = data.WeekdayOvertimeHours.Add(attendance.OverTimeDuration)
+		//鍑哄嫟澶╂暟
+		if attendance.Status != constvar.Vacation {
+			data.TotalAttendanceDays = data.TotalAttendanceDays + 1
+		}
+		dataMap[attendance.WorkerId] = data
+		reportForms = append(reportForms, &rf)
+	}
+	workTypeIds := make([]uint, 0)
+	for _, v := range workTypeIdMap {
+		workTypeIds = append(workTypeIds, v)
+	}
+
+	//鏌ヨ宸ョ鐨勮柂璧勬柟妗堝拰鏂板绫诲瀷
+	workTypeManages, err := models.NewWorkTypeManageSearch().SetIds(workTypeIds).SetPreload(true).FindNotTotal()
+	if err != nil {
+		logx.Error("SalaryStatistics 鏌ヨ宸ョ鐨勮柂璧勬柟妗堝拰鏂板绫诲瀷 err: " + err.Error())
+		return
+	}
+	for _, form := range reportForms {
+		details := make([]models.SalaryDetails, 0)
+		issueSalary := decimal.NewFromInt(0)
+		for _, manage := range workTypeManages {
+			if form.WorkTypeId == manage.ID {
+				data := dataMap[form.WorkerId]
+				for _, plan := range manage.SalaryPlans {
+					formula := strings.ReplaceAll(plan.SalaryFormula, ",", "")
+					var detail models.SalaryDetails
+					detail.SalaryTypeId = plan.SalaryTypeId
+					amount := decimal.NewFromInt(0)
+					f := constvar.DailySilkProduction + "*" + constvar.RawSilkUnitPrice
+					if strings.Contains(formula, f) {
+						data, err = GetDailySilkProduction(startStr, endStr, form.WorkerId, data)
+						if err != nil {
+							logx.Error("SalaryStatistics 缁熻钖祫鍑洪敊 err: " + err.Error())
+						} else {
+							amount, err = utils.CalculateSalary(data, formula)
+							if err != nil {
+								logx.Error("SalaryStatistics 璁$畻钖祫鍑洪敊 err: " + err.Error())
+							}
+						}
+					}
+					detail.Amount = amount
+					issueSalary = issueSalary.Add(amount)
+					details = append(details, detail)
+				}
+			}
+		}
+		form.Details = details
+		form.IssueSalary = issueSalary
+	}
+
+	//鎻掑叆鏁版嵁搴�
+	err = models.NewSalaryReportFormSearch().CreateBatch(reportForms)
+	if err != nil {
+		logx.Error("SalaryStatistics 鎻掑叆鏁版嵁搴撳嚭閿� err: " + err.Error())
+	}
+}
+
+func GetDailySilkProduction(start, end, workerId string, data utils.SalaryCalculateData) (utils.SalaryCalculateData, error) {
+	//鏌ヨ鏈哄彴绠$悊
+	var cars []models.WorkerPosition
+	err := models.NewWorkerPositionSearch().Orm.Table("silk_worker_position").Where("worker_id = ? and start_date >= ? and "+
+		"end_date <= ?", workerId, start, end).Find(&cars).Error
+	if err != nil {
+		return data, err
+	}
+	endCarMap := make(map[int]int)
+	groupMap := make(map[int]int)
+	workshopMap := make(map[string]string)
+	for _, car := range cars {
+		endCarMap[car.EndWorkerPosition] = car.EndWorkerPosition
+		groupMap[car.WorkshopGroup] = car.WorkshopGroup
+		workshopMap[car.Workshop] = car.Workshop
+	}
+	groups := make([]int, 0)
+	workshops := make([]string, 0)
+	endCars := make([]int, 0)
+	for _, v := range endCarMap {
+		endCars = append(endCars, v)
+	}
+	for _, v := range workshopMap {
+		workshops = append(workshops, v)
+	}
+	for _, v := range groupMap {
+		groups = append(groups, v)
+	}
+
+	//鏌ヨ浜ч噺鐧昏琛�
+	var yield []models.YieldRegister
+	err = models.NewYieldRegisterSearch().Orm.Table("silk_yield_register").Where("workshop_number in (?) and group_number "+
+		"in (?) and create_time >= ? and create_time <= ?", workshops, groups, start, end).Find(&yield).Error
+	if err != nil {
+		return data, err
+	}
+	yieldRegisterIds := make([]uint, 0)
+	for _, register := range yield {
+		yieldRegisterIds = append(yieldRegisterIds, register.ID)
+	}
+	yieldMap := make(map[string]decimal.Decimal)
+	items, err := models.NewYieldRegisterItemSearch().SetYieldRegisterIds(yieldRegisterIds).SetCarNumbers(endCars).FindNotTotal()
+	if err != nil {
+		return data, err
+	}
+	for _, register := range yield {
+		for _, item := range items {
+			if register.ID == item.YieldRegisterId {
+				yieldMap[register.CreateTime] = item.PeopleYield
+			}
+		}
+	}
+	//鏌ヨ绾ゅ害鐧昏琛�
+	var fineness []models.FinenessRegister
+	markets := make([]string, 0)
+	err = models.NewFinenessRegisterSearch().Orm.Table("silk_fineness_register").Where("workshop in (?) and workshop_group "+
+		"in (?) and finish_date >= ? and finish_date <= ?", workshops, groups, start, end).Find(&fineness).Error
+	if err != nil {
+		return data, err
+	}
+	finenessIds := make([]uint, 0)
+	for _, register := range fineness {
+		finenessIds = append(finenessIds, register.ID)
+		markets = append(markets, register.Market)
+	}
+	var checkItems []models.FinenessCheckItem
+	err = models.NewFinenessCheckItemSearch().Orm.Table("silk_fineness_check_item").Where("fineness_register_id in (?) and "+
+		"position in (?)", finenessIds, endCars).Find(&checkItems).Error
+	if err != nil {
+		return data, err
+	}
+	checkMap := make(map[string]string)
+	marketMap := make(map[string]string)
+	for _, item := range checkItems {
+		for _, register := range fineness {
+			if item.FinenessRegisterID == register.ID {
+				checkMap[register.FinishDate] = item.FinenessGrade
+				marketMap[register.FinishDate] = register.Market
+				break
+			}
+		}
+	}
+	//鏌ヨ涓嶅悓搴勫彛涓嬬殑鐢熶笣瀹氫环
+	find, _, err := models.NewRawSilkPriceStandardSearch().SetMarketNames(markets).Find()
+	if err != nil {
+		return data, err
+	}
+	//璁$畻姣忔棩宸ヨ祫
+	total := decimal.NewFromInt(0)
+	for date, amount := range yieldMap {
+		grade := checkMap[date]
+		market := marketMap[date]
+		for _, standard := range find {
+			if standard.MarketName == market && standard.RawSilkGrade == grade {
+				h := amount.Mul(standard.PayStandard)
+				total = total.Add(h)
+				break
+			}
+		}
+	}
+	data.DailySilkProduction = total
+	data.RawSilkUnitPrice = decimal.NewFromInt(1)
+	return data, nil
+}
+
+// GetLastMonthPeriod 杩斿洖涓婁釜鏈堢殑鏈堝垵鏃堕棿鍜屾湀鏈椂闂�
+func GetLastMonthPeriod() (time.Time, time.Time) {
+	// 鑾峰彇褰撳墠鏃堕棿
+	now := time.Now()
+
+	// 璁$畻涓婁釜鏈堢殑骞翠唤鍜屾湀浠�
+	lastMonth := now.AddDate(0, -1, 0)
+	lastYear, lastMonthNum, _ := lastMonth.Date()
+
+	// 鑾峰彇涓婁釜鏈堢殑绗竴澶╃殑鏃ユ湡锛堝嵆涓婁釜鏈堟湀鍒濓級
+	firstDayOfLastMonth := time.Date(lastYear, lastMonthNum, 1, 0, 0, 0, 0, now.Location())
+
+	// 鑾峰彇鏈湀绗竴澶╃殑鏃ユ湡锛堝嵆鏈湀鏈堝垵锛�
+	firstDayOfThisMonth := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())
+
+	// 涓婁釜鏈堟湀鏈椂闂村嵆涓烘湰鏈堟湀鍒濆噺鍘讳竴绉�
+	lastDayOfLastMonth := firstDayOfThisMonth.Add(-time.Second)
+
+	return firstDayOfLastMonth, lastDayOfLastMonth
+}
diff --git a/utils/salary_calculate.go b/utils/salary_calculate.go
new file mode 100644
index 0000000..b5c4618
--- /dev/null
+++ b/utils/salary_calculate.go
@@ -0,0 +1,63 @@
+package utils
+
+import (
+	"github.com/Knetic/govaluate"
+	"github.com/shopspring/decimal"
+	"strconv"
+	"strings"
+)
+
+// 钖祫璁$畻鏁版嵁
+type SalaryCalculateData struct {
+	DailySilkProduction     decimal.Decimal `json:"dailySilkProduction"`     // 鏃ヤ骇涓濋噺
+	WasteSilkQuantity       decimal.Decimal `json:"wasteSilkQuantity"`       // 閲庣氦鏁伴噺
+	RawSilkUnitPrice        decimal.Decimal `json:"rawSilkUnitPrice"`        // 鐢熶笣鍗曚环
+	WasteSilkUnitPrice      decimal.Decimal `json:"wasteSilkUnitPrice"`      // 閲庣氦鍗曚环
+	BucketCount             decimal.Decimal `json:"bucketCount"`             //妗舵暟(鏃�)
+	AttendanceDays          int             `json:"attendanceDays"`          // 鍑哄嫟澶╂暟
+	GroupAverageMonthlyWage decimal.Decimal `json:"groupAverageMonthlyWage"` //鍚岀粍鎸¤溅宸ユ湀骞冲潎宸ヨ祫
+	GroupCarHeadWage        decimal.Decimal `json:"groupCarHeadWage"`        //鍚岀粍杞﹀ご宸ュ伐璧�
+
+	WeekdayOvertimeHours decimal.Decimal `json:"weekdayOvertimeHours"` // 宸ヤ綔鏃ュ姞鐝椂闀�
+	FullAttendanceAward  int             `json:"FullAttendanceAward"`  // 婊″嫟濂�
+	WeekendOvertimeHours decimal.Decimal `json:"weekendOvertimeHours"` // 浼戞伅鏃ュ姞鐝椂闀�
+	LeaveDays            decimal.Decimal `json:"LeaveDays"`            //璇峰亣澶╂暟
+	ApprenticeDays       decimal.Decimal `json:"apprenticeDays"`       // 甯﹀緬澶╂暟
+	TotalAttendanceDays  int             `json:"totalAttendanceDays"`  // 鍑哄嫟澶╂暟
+	Seniority            decimal.Decimal `json:"seniority"`            // 宸ラ緞
+}
+
+// CalculateSalary 璁$畻宸ヨ祫鐨勫嚱鏁�
+func CalculateSalary(date SalaryCalculateData, formula string) (decimal.Decimal, error) {
+	// 鏇挎崲鍏紡涓殑鍙橀噺
+	formula = strings.ReplaceAll(formula, "鏃ヤ骇涓濋噺", date.DailySilkProduction.String())
+	formula = strings.ReplaceAll(formula, "閲庣氦鏁伴噺", date.WasteSilkQuantity.String())
+	formula = strings.ReplaceAll(formula, "鐢熶笣鍗曚环", date.RawSilkUnitPrice.String())
+	formula = strings.ReplaceAll(formula, "閲庣氦鍗曚环", date.WasteSilkUnitPrice.String())
+	formula = strings.ReplaceAll(formula, "妗舵暟(鏃�)", date.BucketCount.String())
+	formula = strings.ReplaceAll(formula, "鍑哄嫟澶╂暟", strconv.Itoa(date.AttendanceDays))
+	formula = strings.ReplaceAll(formula, "鍚岀粍鎸¤溅宸ユ湀骞冲潎宸ヨ祫", date.GroupAverageMonthlyWage.String())
+	formula = strings.ReplaceAll(formula, "鍚岀粍杞﹀ご宸ュ伐璧�", date.GroupCarHeadWage.String())
+
+	formula = strings.ReplaceAll(formula, "宸ヤ綔鏃ュ姞鐝椂闀�", date.WeekdayOvertimeHours.String())
+	formula = strings.ReplaceAll(formula, "婊″嫟濂�", strconv.Itoa(date.FullAttendanceAward))
+	formula = strings.ReplaceAll(formula, "浼戞伅鏃ュ姞鐝椂闀�", date.WeekendOvertimeHours.String())
+	formula = strings.ReplaceAll(formula, "璇峰亣澶╂暟", date.LeaveDays.String())
+	formula = strings.ReplaceAll(formula, "甯﹀緬澶╂暟", date.ApprenticeDays.String())
+	formula = strings.ReplaceAll(formula, "鍑哄嫟澶╂暟", strconv.Itoa(date.TotalAttendanceDays))
+	formula = strings.ReplaceAll(formula, "宸ラ緞", date.Seniority.String())
+
+	// 浣跨敤 govaluate 搴撹绠楄〃杈惧紡
+	wage := decimal.NewFromInt(0)
+	expression, err := govaluate.NewEvaluableExpression(formula)
+	if err != nil {
+		return wage, err
+	}
+	parameters := make(map[string]interface{})
+	result, err := expression.Evaluate(parameters)
+	if err != nil {
+		return wage, err
+	}
+	wage = decimal.NewFromFloat(result.(float64))
+	return wage, nil
+}

--
Gitblit v1.8.0