zhangqian
2023-08-10 12e09639dcbfa63b5a23cde8642b72829d4f3a3b
Merge branch 'zq'
2个文件已添加
14个文件已修改
579 ■■■■ 已修改文件
api/v1/serviceContract.go 44 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/docs.go 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/swagger.json 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/swagger.yaml 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
model/invoice.go 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
model/invoiceProduct.go 131 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
model/product.go 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
model/request/invoice.go 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
model/request/serviceContract.go 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
model/serviceContract.go 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pkg/contextx/contextx.go 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pkg/ecode/code.go 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pkg/ecode/msg.go 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service/invoice.go 76 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service/products.go 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service/serviceContract.go 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/v1/serviceContract.go
@@ -46,11 +46,11 @@
//    @Tags        ServiceContract
//    @Summary    删除服务合同
//    @Produce    application/json
//    @Param        object    body        request.DeleteserviceContract true    "查询参数"
//    @Param        object    body        request.DeleteServiceContract true    "查询参数"
//    @Success    200    {object}    contextx.Response{}
//    @Router        /api/serviceContract/delete [delete]
func (s *ServiceContractApi) Delete(c *gin.Context) {
    var params request.DeleteserviceContract
    var params request.DeleteServiceContract
    ctx, ok := contextx.NewContext(c, &params)
    if !ok {
        return
@@ -99,44 +99,11 @@
// check params
func checkServiceContractParams(serviceContract request.ServiceContract) (errCode int, result model.ServiceContract) {
    //if serviceContract.SignTime == "" {
    //    return ecode.InvalidParams, result
    //}
    //
    //if serviceContract.Number == "" {
    //    return ecode.InvalidParams, result
    //}
    //
    //if serviceContract.MemberId <= 0 {
    //    return ecode.InvalidParams, result
    //}
    t, err := checkTimeFormat(serviceContract.SignTime)
    if err != nil {
        return ecode.InvalidParams, result
    }
    result.SignTime = t
    t, err = checkTimeFormat(serviceContract.StartTime)
    if err != nil {
        return ecode.InvalidParams, result
    }
    result.StartTime = t
    t, err = checkTimeFormat(serviceContract.EndTime)
    if err != nil {
        return ecode.InvalidParams, result
    }
    result.EndTime = t
    result.Number = serviceContract.Number
    result.MemberId = serviceContract.MemberId
    result.Remark = serviceContract.Remark
    result.ClientId = serviceContract.ClientId
    result.ContactId = serviceContract.ContactId
    result.SalesDetailsId = serviceContract.SalesDetailsId
    result.SaleChanceId = serviceContract.SaleChanceId
    result.QuotationId = serviceContract.QuotationId
    result.ServiceContractTypeId = serviceContract.TypeId
@@ -144,6 +111,9 @@
    result.ServiceTimes = serviceContract.ServiceTimes
    result.Terms = serviceContract.Terms
    result.Products = serviceContract.Products
    result.SignTime = serviceContract.SignTime
    result.StartTime = serviceContract.StartTime
    result.EndTime = serviceContract.EndTime
    return ecode.OK, result
}
@@ -151,7 +121,7 @@
// List
//
// @Tags        ServiceContract
// @Summary    生成计划列表
// @Summary    服务合同列表
// @Produce    application/json
// @Param        object    body        request.GetServiceContractList    true    "参数"
// @Success    200        {object}    contextx.Response{data=response.ServiceContractsResponse}
docs/docs.go
@@ -7620,7 +7620,7 @@
                        "in": "body",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/request.DeleteserviceContract"
                            "$ref": "#/definitions/request.DeleteServiceContract"
                        }
                    }
                ],
@@ -7642,7 +7642,7 @@
                "tags": [
                    "ServiceContract"
                ],
                "summary": "生成计划列表",
                "summary": "服务合同列表",
                "parameters": [
                    {
                        "description": "参数",
@@ -11366,6 +11366,9 @@
        "model.ServiceContract": {
            "type": "object",
            "properties": {
                "SaleChance": {
                    "$ref": "#/definitions/model.SaleChance"
                },
                "amountInvoiced": {
                    "description": "已开票金额",
                    "type": "number"
@@ -11382,9 +11385,6 @@
                    "type": "integer"
                },
                "contactId": {
                    "type": "integer"
                },
                "contractId": {
                    "type": "integer"
                },
                "endTime": {
@@ -11405,6 +11405,9 @@
                        "$ref": "#/definitions/model.Product"
                    }
                },
                "quotation": {
                    "$ref": "#/definitions/model.Quotation"
                },
                "quotationId": {
                    "type": "integer"
                },
@@ -11414,8 +11417,20 @@
                "saleChanceId": {
                    "type": "integer"
                },
                "salesDetails": {
                    "$ref": "#/definitions/model.SalesDetails"
                },
                "salesDetailsId": {
                    "type": "integer"
                },
                "serviceContractStatus": {
                    "$ref": "#/definitions/model.ServiceContractStatus"
                },
                "serviceContractStatusId": {
                    "type": "integer"
                },
                "serviceContractType": {
                    "$ref": "#/definitions/model.ServiceContractType"
                },
                "serviceContractTypeId": {
                    "type": "integer"
@@ -12889,9 +12904,6 @@
                "contactId": {
                    "type": "integer"
                },
                "contractId": {
                    "type": "integer"
                },
                "endTime": {
                    "type": "string"
                },
@@ -12914,6 +12926,9 @@
                    "type": "string"
                },
                "saleChanceId": {
                    "type": "integer"
                },
                "salesDetailsId": {
                    "type": "integer"
                },
                "serviceTimes": {
@@ -13536,6 +13551,17 @@
                }
            }
        },
        "request.DeleteServiceContract": {
            "type": "object",
            "properties": {
                "ids": {
                    "type": "array",
                    "items": {
                        "type": "integer"
                    }
                }
            }
        },
        "request.DeleteServiceFeeManage": {
            "type": "object",
            "properties": {
@@ -13564,17 +13590,6 @@
                "userId": {
                    "description": "用户ID",
                    "type": "string"
                }
            }
        },
        "request.DeleteserviceContract": {
            "type": "object",
            "properties": {
                "ids": {
                    "type": "array",
                    "items": {
                        "type": "integer"
                    }
                }
            }
        },
@@ -15888,9 +15903,6 @@
                "contactId": {
                    "type": "integer"
                },
                "contractId": {
                    "type": "integer"
                },
                "endTime": {
                    "type": "string"
                },
@@ -15918,6 +15930,9 @@
                "saleChanceId": {
                    "type": "integer"
                },
                "salesDetailsId": {
                    "type": "integer"
                },
                "serviceTimes": {
                    "type": "integer"
                },
docs/swagger.json
@@ -7608,7 +7608,7 @@
                        "in": "body",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/request.DeleteserviceContract"
                            "$ref": "#/definitions/request.DeleteServiceContract"
                        }
                    }
                ],
@@ -7630,7 +7630,7 @@
                "tags": [
                    "ServiceContract"
                ],
                "summary": "生成计划列表",
                "summary": "服务合同列表",
                "parameters": [
                    {
                        "description": "参数",
@@ -11354,6 +11354,9 @@
        "model.ServiceContract": {
            "type": "object",
            "properties": {
                "SaleChance": {
                    "$ref": "#/definitions/model.SaleChance"
                },
                "amountInvoiced": {
                    "description": "已开票金额",
                    "type": "number"
@@ -11370,9 +11373,6 @@
                    "type": "integer"
                },
                "contactId": {
                    "type": "integer"
                },
                "contractId": {
                    "type": "integer"
                },
                "endTime": {
@@ -11393,6 +11393,9 @@
                        "$ref": "#/definitions/model.Product"
                    }
                },
                "quotation": {
                    "$ref": "#/definitions/model.Quotation"
                },
                "quotationId": {
                    "type": "integer"
                },
@@ -11402,8 +11405,20 @@
                "saleChanceId": {
                    "type": "integer"
                },
                "salesDetails": {
                    "$ref": "#/definitions/model.SalesDetails"
                },
                "salesDetailsId": {
                    "type": "integer"
                },
                "serviceContractStatus": {
                    "$ref": "#/definitions/model.ServiceContractStatus"
                },
                "serviceContractStatusId": {
                    "type": "integer"
                },
                "serviceContractType": {
                    "$ref": "#/definitions/model.ServiceContractType"
                },
                "serviceContractTypeId": {
                    "type": "integer"
@@ -12877,9 +12892,6 @@
                "contactId": {
                    "type": "integer"
                },
                "contractId": {
                    "type": "integer"
                },
                "endTime": {
                    "type": "string"
                },
@@ -12902,6 +12914,9 @@
                    "type": "string"
                },
                "saleChanceId": {
                    "type": "integer"
                },
                "salesDetailsId": {
                    "type": "integer"
                },
                "serviceTimes": {
@@ -13524,6 +13539,17 @@
                }
            }
        },
        "request.DeleteServiceContract": {
            "type": "object",
            "properties": {
                "ids": {
                    "type": "array",
                    "items": {
                        "type": "integer"
                    }
                }
            }
        },
        "request.DeleteServiceFeeManage": {
            "type": "object",
            "properties": {
@@ -13552,17 +13578,6 @@
                "userId": {
                    "description": "用户ID",
                    "type": "string"
                }
            }
        },
        "request.DeleteserviceContract": {
            "type": "object",
            "properties": {
                "ids": {
                    "type": "array",
                    "items": {
                        "type": "integer"
                    }
                }
            }
        },
@@ -15876,9 +15891,6 @@
                "contactId": {
                    "type": "integer"
                },
                "contractId": {
                    "type": "integer"
                },
                "endTime": {
                    "type": "string"
                },
@@ -15906,6 +15918,9 @@
                "saleChanceId": {
                    "type": "integer"
                },
                "salesDetailsId": {
                    "type": "integer"
                },
                "serviceTimes": {
                    "type": "integer"
                },
docs/swagger.yaml
@@ -1270,6 +1270,8 @@
    type: object
  model.ServiceContract:
    properties:
      SaleChance:
        $ref: '#/definitions/model.SaleChance'
      amountInvoiced:
        description: 已开票金额
        type: number
@@ -1283,8 +1285,6 @@
        type: integer
      contactId:
        type: integer
      contractId:
        type: integer
      endTime:
        type: string
      id:
@@ -1297,14 +1297,24 @@
        items:
          $ref: '#/definitions/model.Product'
        type: array
      quotation:
        $ref: '#/definitions/model.Quotation'
      quotationId:
        type: integer
      remark:
        type: string
      saleChanceId:
        type: integer
      salesDetails:
        $ref: '#/definitions/model.SalesDetails'
      salesDetailsId:
        type: integer
      serviceContractStatus:
        $ref: '#/definitions/model.ServiceContractStatus'
      serviceContractStatusId:
        type: integer
      serviceContractType:
        $ref: '#/definitions/model.ServiceContractType'
      serviceContractTypeId:
        type: integer
      serviceTimes:
@@ -2296,8 +2306,6 @@
        type: integer
      contactId:
        type: integer
      contractId:
        type: integer
      endTime:
        type: string
      memberId:
@@ -2313,6 +2321,8 @@
      remark:
        type: string
      saleChanceId:
        type: integer
      salesDetailsId:
        type: integer
      serviceTimes:
        type: integer
@@ -2746,6 +2756,13 @@
          type: integer
        type: array
    type: object
  request.DeleteServiceContract:
    properties:
      ids:
        items:
          type: integer
        type: array
    type: object
  request.DeleteServiceFeeManage:
    properties:
      ids:
@@ -2765,13 +2782,6 @@
      userId:
        description: 用户ID
        type: string
    type: object
  request.DeleteserviceContract:
    properties:
      ids:
        items:
          type: integer
        type: array
    type: object
  request.DownloadFile:
    properties:
@@ -4341,8 +4351,6 @@
        type: integer
      contactId:
        type: integer
      contractId:
        type: integer
      endTime:
        type: string
      id:
@@ -4360,6 +4368,8 @@
      remark:
        type: string
      saleChanceId:
        type: integer
      salesDetailsId:
        type: integer
      serviceTimes:
        type: integer
@@ -9980,7 +9990,7 @@
        name: object
        required: true
        schema:
          $ref: '#/definitions/request.DeleteserviceContract'
          $ref: '#/definitions/request.DeleteServiceContract'
      produces:
      - application/json
      responses:
@@ -10012,7 +10022,7 @@
                data:
                  $ref: '#/definitions/response.ServiceContractsResponse'
              type: object
      summary: 生成计划列表
      summary: 服务合同列表
      tags:
      - ServiceContract
  /api/serviceContract/update:
model/invoice.go
@@ -28,6 +28,7 @@
        CourierNumber    string                     `gorm:"courier_number" json:"courierNumber"`        // 物流单号
        CourierCompanyId int                        `gorm:"courier_company_id" json:"courierCompanyId"` // 物流公司
        CourierCompany   CourierCompany             `gorm:"foreignKey:CourierCompanyId"`
        Products         []*Product                 `json:"products" gorm:"many2many:invoice_product;"`
    }
    // InvoiceSearch 销售发票搜索条件
model/invoiceProduct.go
New file
@@ -0,0 +1,131 @@
package model
import (
    "aps_crm/pkg/mysqlx"
    "gorm.io/gorm"
)
type (
    // InvoiceProduct 合同产品
    InvoiceProduct struct {
        InvoiceId int `gorm:"invoice_id" json:"invoiceId"`
        ProductId int `gorm:"product_id" json:"productId"`
    }
    // InvoiceProductSearch 合同产品搜索条件
    InvoiceProductSearch struct {
        InvoiceProduct
        Orm        *gorm.DB
        Keyword    string
        PageNum    int
        PageSize   int
        ProductIds []uint
    }
)
func (InvoiceProduct) TableName() string {
    return "invoice_product"
}
func NewInvoiceProductSearch() *InvoiceProductSearch {
    return &InvoiceProductSearch{
        Orm: mysqlx.GetDB(),
    }
}
func (slf *InvoiceProductSearch) build() *gorm.DB {
    var db = slf.Orm.Model(&InvoiceProduct{})
    if len(slf.ProductIds) != 0 {
        db = db.Where("product_id in ?", slf.ProductIds)
    }
    if slf.InvoiceId != 0 {
        db = db.Where("invoice_id = ?", slf.InvoiceId)
    }
    return db
}
func (slf *InvoiceProductSearch) Create(record *InvoiceProduct) error {
    var db = slf.build()
    return db.Create(record).Error
}
func (slf *InvoiceProductSearch) CreateBatch(records []*InvoiceProduct) error {
    var db = slf.build()
    return db.Create(records).Error
}
func (slf *InvoiceProductSearch) Delete() error {
    var db = slf.build()
    return db.Delete(&InvoiceProduct{}).Error
}
func (slf *InvoiceProductSearch) Update(record *InvoiceProduct) error {
    var db = slf.build()
    return db.Updates(record).Error
}
func (slf *InvoiceProductSearch) FindAll() ([]*InvoiceProduct, error) {
    var db = slf.build()
    var record = make([]*InvoiceProduct, 0)
    err := db.Find(&record).Error
    return record, err
}
func (slf *InvoiceProductSearch) SetProductIds(ids []uint) *InvoiceProductSearch {
    slf.ProductIds = ids
    return slf
}
func (slf *InvoiceProductSearch) SetInvoiceId(id int) *InvoiceProductSearch {
    slf.InvoiceId = id
    return slf
}
func (slf *InvoiceProductSearch) SetOrm(tx *gorm.DB) *InvoiceProductSearch {
    slf.Orm = tx
    return slf
}
func (slf *InvoiceProductSearch) First() (*InvoiceProduct, error) {
    var db = slf.build()
    var record = new(InvoiceProduct)
    err := db.First(record).Error
    return record, err
}
func (slf *InvoiceProductSearch) Updates(values interface{}) error {
    var db = slf.build()
    return db.Updates(values).Error
}
func (slf *InvoiceProductSearch) Find() ([]*InvoiceProduct, int64, error) {
    var db = slf.build()
    var records = make([]*InvoiceProduct, 0)
    var total int64
    if err := db.Count(&total).Error; err != nil {
        return records, total, err
    }
    if slf.PageNum > 0 && slf.PageSize > 0 {
        db = db.Limit(slf.PageSize).Offset((slf.PageNum - 1) * slf.PageSize)
    }
    err := db.Find(&records).Error
    return records, total, err
}
// InitDefaultData 初始化数据
func (slf *InvoiceProductSearch) InitDefaultData() error {
    var (
        db          = slf.Orm.Table(slf.TableName())
        total int64 = 0
    )
    if err := db.Count(&total).Error; err != nil {
        return err
    }
    if total != 0 {
        return nil
    }
    records := []*InvoiceProduct{}
    return slf.CreateBatch(records)
}
model/product.go
@@ -1,14 +1,17 @@
package model
import "gorm.io/gorm"
import (
    "github.com/shopspring/decimal"
    "gorm.io/gorm"
)
type Product struct {
    Id         int     `json:"id" gorm:"column:id;primary_key;AUTO_INCREMENT"`
    Id         uint            `json:"id" gorm:"column:id;primary_key;AUTO_INCREMENT"`
    Name       string  `json:"name" gorm:"column:name;type:varchar(255);comment:产品名称"`
    Price      float64 `json:"price" gorm:"column:price;type:decimal(10,2);comment:产品价格"`
    Price      decimal.Decimal `json:"price" gorm:"column:price;type:decimal(10,2);comment:产品价格"`
    Number     string  `json:"number" gorm:"column:number;type:varchar(255);comment:产品编号"`
    Amount     int     `json:"amount" gorm:"column:amount;type:int;comment:产品数量"`
    Total      float64 `json:"total" gorm:"column:total;type:decimal(10,2);comment:产品总价"`
    Amount     decimal.Decimal `json:"amount" gorm:"column:amount;type:int;comment:产品数量"`
    Total      decimal.Decimal `json:"total" gorm:"column:total;type:decimal(10,2);comment:产品总价"`
    Desc       string  `json:"desc" gorm:"column:desc;type:varchar(255);comment:产品描述"`
    gorm.Model `json:"-"`
}
model/request/invoice.go
@@ -2,6 +2,7 @@
import (
    "aps_crm/constvar"
    "aps_crm/model"
)
type AddInvoice struct {
@@ -17,10 +18,11 @@
    InvoiceDate      string                     `gorm:"invoice_date" json:"invoiceDate"`            // 开票日期
    CourierNumber    string                     `gorm:"courier_number" json:"courierNumber"`        // 物流单号
    CourierCompanyId int                        `gorm:"courier_company_id" json:"courierCompanyId"` // 物流公司
    Products         []model.Product            `json:"products"`                                   //发票对应产品,从相应源单里获取
}
type UpdateInvoice struct {
    Id               int    `json:"id"`
    Id               int             `json:"id" binding:"required"`
    ClientId         int    `gorm:"client_id" json:"clientId"`                  // 客户id
    InvoiceTypeId    int    `gorm:"invoice_type_id" json:"invoiceTypeId"`       // 发票类型id
    PrincipalId      int    `gorm:"principal_id" json:"principalId"`            // 销售负责人id
@@ -33,6 +35,7 @@
    InvoiceDate      int    `gorm:"invoice_date" json:"invoiceDate"`            // 开票日期
    CourierNumber    string `gorm:"courier_number" json:"courierNumber"`        // 物流单号
    CourierCompanyId int    `gorm:"courier_company_id" json:"courierCompanyId"` // 物流公司
    Products         []model.Product `json:"products"`                                   //发票对应产品,从相应源单里获取
}
type GetInvoiceList struct {
model/request/serviceContract.go
@@ -15,17 +15,17 @@
    MemberId     int             `json:"memberId"`
    ContactId    int             `json:"contactId"`
    SaleChanceId int             `json:"saleChanceId"`
    ContractId   int             `json:"contractId"`
    SalesDetailsId int              `json:"salesDetailsId"`
    QuotationId  int             `json:"quotationId"`
    TypeId       int             `json:"typeId"`
    SignTime     string          `json:"signTime"`
    StartTime    string          `json:"startTime"`
    EndTime      string          `json:"endTime"`
    SignTime       string           `json:"signTime" binding:"datetime=2006-01-02"`
    StartTime      string           `json:"startTime" binding:"datetime=2006-01-02"`
    EndTime        string           `json:"endTime" binding:"datetime=2006-01-02"`
    StatusId     int             `json:"statusId"`
    ServiceTimes int             `json:"serviceTimes"`
    Terms        string          `json:"terms"`
    Remark       string          `json:"remark"`
    Products     []model.Product `json:"products"`
    Products       []*model.Product `json:"products"`
}
type UpdateServiceContract struct {
@@ -40,6 +40,6 @@
    Keyword     string                              `json:"keyword"`
}
type DeleteserviceContract struct {
type DeleteServiceContract struct {
    Ids []int `json:"ids"`
}
model/serviceContract.go
@@ -17,20 +17,26 @@
        MemberId                int             `json:"memberId" gorm:"column:member_id;type:int;comment:负责人id"`
        ContactId               int             `json:"contactId" gorm:"column:contact_id;type:int;comment:联系人id"`
        SaleChanceId            int             `json:"saleChanceId" gorm:"column:sale_chance_id;type:int;comment:销售机会id"`
        ContractId              int             `json:"contractId" gorm:"column:contract_id;type:int;comment:合同id"`
        SaleChance              SaleChance            `json:"SaleChance" gorm:"foreignKey:SaleChanceId"`
        SalesDetailsId          int                   `json:"salesDetailsId" gorm:"column:sales_details_id;type:int;comment:合同订单id"`
        SalesDetails            SalesDetails          `json:"salesDetails" gorm:"foreignKey:SalesDetailsId"`
        QuotationId             int             `json:"quotationId" gorm:"column:quotation_id;type:int;comment:报价单id"`
        Quotation               Quotation             `json:"quotation" gorm:"foreignKey:QuotationId"`
        ServiceContractTypeId   int             `json:"serviceContractTypeId" gorm:"column:service_contract_type_id;type:int;comment:合同类型id"`
        SignTime                time.Time       `json:"signTime" gorm:"column:sign_time;type:datetime;comment:签约时间"`
        StartTime               time.Time       `json:"startTime" gorm:"column:start_time;type:datetime;comment:开始时间"`
        EndTime                 time.Time       `json:"endTime" gorm:"column:end_time;type:datetime;comment:结束时间"`
        ServiceContractType     ServiceContractType   `json:"serviceContractType" gorm:"foreignKey:ServiceContractTypeId"`
        SignTime                string                `json:"signTime" gorm:"column:sign_time;type:datetime;comment:签约时间"`
        StartTime               string                `json:"startTime" gorm:"column:start_time;type:datetime;comment:开始时间"`
        EndTime                 string                `json:"endTime" gorm:"column:end_time;type:datetime;comment:结束时间"`
        ServiceContractStatusId int             `json:"serviceContractStatusId" gorm:"column:service_contract_status_id;type:int;comment:合同状态id"`
        ServiceContractStatus   ServiceContractStatus `json:"serviceContractStatus" gorm:"foreignKey:ServiceContractStatusId"`
        ServiceTimes            int             `json:"serviceTimes" gorm:"column:service_times;type:int;comment:服务次数"`
        Terms                   string          `json:"terms" gorm:"column:terms;type:text;comment:条款"`
        Remark                  string          `json:"remark" gorm:"column:remark;type:text;comment:备注"`
        AmountReceivable        decimal.Decimal `gorm:"amount_receivable" json:"amountReceivable"` // 应收金额
        AmountReceived          decimal.Decimal `gorm:"amount_received" json:"amountReceived"`     // 已收金额
        AmountInvoiced          decimal.Decimal `gorm:"amount_invoiced" json:"amountInvoiced"`     // 已开票金额
        Products                []Product       `json:"products" gorm:"many2many:serviceContract_product;"`
        AmountUnInvoiced        decimal.Decimal       `gorm:"-" json:"amountUnInvoiced"`                 // 未开票金额
        Products                []*Product            `json:"products" gorm:"many2many:service_contract_product;"`
        gorm.Model              `json:"-"`
    }
@@ -44,6 +50,7 @@
        OrderBy     string
        PageNum     int
        PageSize    int
        Preload     bool
    }
)
@@ -64,13 +71,13 @@
    }
    switch slf.QueryClass {
    case constvar.ServiceContractQueryClassExpireAfter30Day:
        db = db.Where("end_time > ?", time.Now(), time.Now().AddDate(0, 0, 30))
        db = db.Where("end_time > ?", time.Now().AddDate(0, 0, 30).Format("2006-01-02"))
    case constvar.ServiceContractQueryClassExpireAfter60Day:
        db = db.Where("end_time > ?", time.Now(), time.Now().AddDate(0, 0, 60))
        db = db.Where("end_time > ?", time.Now().AddDate(0, 0, 60).Format("2006-01-02"))
    case constvar.ServiceContractQueryClassExpiredBefore15Day:
        db = db.Where("end_time < ?", time.Now().AddDate(0, 0, -15))
        db = db.Where("end_time < ?", time.Now().AddDate(0, 0, -15).Format("2006-01-02"))
    case constvar.ServiceContractQueryClassExpiredBefore60Day:
        db = db.Where("end_time < ?", time.Now().AddDate(0, 0, -60))
        db = db.Where("end_time < ?", time.Now().AddDate(0, 0, -60).Format("2006-01-02"))
    }
    switch slf.KeywordType {
@@ -97,6 +104,15 @@
        //todo
    }
    if slf.Preload {
        db = db.
            Preload("SaleChance").
            Preload("SalesDetails").
            Preload("Quotation").
            Preload("ServiceContractType").
            Preload("ServiceContractStatus").
            Preload("Products")
    }
    return db
}
@@ -116,14 +132,7 @@
    return db.Delete(&ServiceContract{}).Error
}
func (slf *ServiceContractSearch) Find() (*ServiceContract, error) {
    var db = slf.build()
    var record = &ServiceContract{}
    err := db.First(record).Error
    return record, err
}
func (slf *ServiceContractSearch) FindAll() ([]*ServiceContract, int64, error) {
func (slf *ServiceContractSearch) Find() ([]*ServiceContract, int64, error) {
    var db = slf.build()
    var records = make([]*ServiceContract, 0)
    var total int64
@@ -134,11 +143,7 @@
        db = db.Limit(slf.PageSize).Offset((slf.PageNum - 1) * slf.PageSize)
    }
    if slf.PageNum > 0 && slf.PageSize > 0 {
        db = db.Limit(slf.PageSize).Offset((slf.PageNum - 1) * slf.PageSize)
    }
    err := db.Preload("Products").Find(&records).Error
    err := db.Find(&records).Error
    return records, total, err
}
@@ -176,6 +181,11 @@
    return slf
}
func (slf *ServiceContractSearch) SetPreload(preload bool) *ServiceContractSearch {
    slf.Preload = preload
    return slf
}
func (slf *ServiceContractSearch) UpdateByMap(upMap map[string]interface{}) error {
    var (
        db = slf.build()
pkg/contextx/contextx.go
@@ -41,6 +41,7 @@
    // validate params
    if params != nil {
        if err := r.ctx.ShouldBind(params); err != nil {
            logx.Errorf("bind param error: %v", err.Error())
            r.Fail(ecode.ParamsErr)
            return
        }
pkg/ecode/code.go
@@ -241,6 +241,9 @@
    SContractSetErr    = 3300004 // 设置服务合同失败
    SContractUpdateErr = 3300005 // 更新服务合同失败
    SContractDeleteErr = 3300006 // 删除服务合同失败
    SContractProductPriceLowerThanInvoiceAmountErr             = 3300007 //产品总价低于已开票金额
    SContractProductPriceLowerThanReceivedAmountErr            = 3300008 //产品总价低于已收金额
    SContractInvoiceProductPriceGreaterThanReceivableAmountErr = 3300009 //开票总额高于应收金额
    OrderManageExist     = 3400001 // 订单管理已存在
    OrderManageNotExist  = 3400002 // 订单管理不存在
pkg/ecode/msg.go
@@ -20,6 +20,9 @@
    ChildrenExistErr:      "存在子菜单",
    MenuNotExist:          "菜单不存在",
    MenuNameExistErr:      "菜单名已存在",
    SContractProductPriceLowerThanInvoiceAmountErr:             "产品总价低于已开票金额",
    SContractProductPriceLowerThanReceivedAmountErr:            "产品总价低于已收金额",
    SContractInvoiceProductPriceGreaterThanReceivableAmountErr: "开票总额高于应收金额",
}
func GetMsg(errCode int) (errMsg string) {
service/invoice.go
@@ -1,9 +1,11 @@
package service
import (
    "aps_crm/constvar"
    "aps_crm/model"
    "aps_crm/model/request"
    "aps_crm/pkg/ecode"
    "github.com/shopspring/decimal"
    "gorm.io/gorm"
)
@@ -15,30 +17,32 @@
func (InvoiceService) AddInvoice(invoice *model.Invoice) int {
    err := model.WithTransaction(func(db *gorm.DB) error {
        err := model.NewInvoiceSearch().Create(invoice)
    if invoice.SourceType == constvar.InvoiceSourceTypeServiceContract {
        serviceContract, err := model.NewServiceContractSearch().SetId(invoice.SourceId).SetPreload(true).First()
        if err != nil {
            return ecode.DBErr
        }
        var amountInvoiced decimal.Decimal
        rightProducts := NewProductsService().PickRightProducts(invoice.Products, serviceContract.Products)
        for _, product := range rightProducts {
            amountInvoiced = serviceContract.AmountInvoiced.Add(product.Amount.Mul(product.Price))
        }
        amountInvoiced = amountInvoiced.Round(2)
        if amountInvoiced.GreaterThan(serviceContract.AmountReceivable) {
            return ecode.SContractInvoiceProductPriceGreaterThanReceivableAmountErr
        }
        err = model.WithTransaction(func(db *gorm.DB) error {
            err = model.NewInvoiceSearch().Create(invoice)
        if err != nil {
            return err
        }
        //if invoice.SourceType == constvar.InvoiceSourceTypeServiceContract {
        //    contract,err := model.NewServiceContractSearch().SetId(invoice.SourceId).First()
        //    if err != nil {
        //        return err
        //    }
        //    AmountInvoiced := contract.AmountReceived.Add()
        //    err := model.NewServiceContractSearch().SetId(invoice.SourceId).UpdateByMap(map[string]interface{}{
        //        "amount_received" :
        //    })
        //    if err != nil {
        //        return err
        //    }
        //}
        return nil
            return model.NewServiceContractSearch().SetId(invoice.SourceId).UpdateByMap(map[string]interface{}{
                "amount_invoiced": amountInvoiced,
    })
        })
    if err != nil {
        return ecode.DBErr
        }
    }
    return ecode.OK
@@ -79,9 +83,43 @@
}
func (InvoiceService) UpdateInvoice(invoice *model.Invoice) int {
    err := model.NewInvoiceSearch().SetId(invoice.Id).Save(invoice)
    if invoice.SourceType == constvar.InvoiceSourceTypeServiceContract {
        serviceContract, err := model.NewServiceContractSearch().SetId(invoice.SourceId).SetPreload(true).First()
    if err != nil {
        return ecode.DBErr
    }
        var amountInvoiced decimal.Decimal
        newProducts, removedProducts := NewProductsService().PickDiffProducts(invoice.Products, serviceContract.Products)
        for _, product := range newProducts {
            amountInvoiced = serviceContract.AmountInvoiced.Add(product.Amount.Mul(product.Price))
        }
        removedProductIds := make([]uint, 0, len(removedProducts))
        for _, product := range removedProducts {
            amountInvoiced = serviceContract.AmountInvoiced.Sub(product.Amount.Mul(product.Price))
            removedProductIds = append(removedProductIds, product.Id)
        }
        amountInvoiced = amountInvoiced.Round(2)
        if amountInvoiced.GreaterThan(serviceContract.AmountReceivable) {
            return ecode.SContractInvoiceProductPriceGreaterThanReceivableAmountErr
        }
        err = model.WithTransaction(func(db *gorm.DB) error {
            err = model.NewInvoiceSearch().SetId(invoice.Id).Save(invoice)
            if err != nil {
                return err
            }
            if len(removedProductIds) > 0 {
                err = model.NewInvoiceProductSearch().SetInvoiceId(invoice.Id).SetProductIds(removedProductIds).Delete()
                if err != nil {
                    return err
                }
            }
            return model.NewServiceContractSearch().SetId(invoice.SourceId).UpdateByMap(map[string]interface{}{
                "amount_invoiced": amountInvoiced,
            })
        })
        if err != nil {
            return ecode.DBErr
        }
    }
    return ecode.OK
}
service/products.go
New file
@@ -0,0 +1,50 @@
package service
import (
    "aps_crm/model"
)
type ProductsService struct{}
func NewProductsService() ProductsService {
    return ProductsService{}
}
func (slf ProductsService) PickRightProducts(products, sourceProducts []*model.Product) (rightProducts []*model.Product) {
    productIdMap, productNumberMap := slf.getMappedProducts(sourceProducts)
    for _, product := range products {
        if p, ok := productIdMap[product.Id]; ok {
            rightProducts = append(rightProducts, p)
        } else if p, ok = productNumberMap[product.Number]; ok {
            rightProducts = append(rightProducts, p)
        }
    }
    return
}
func (slf ProductsService) PickDiffProducts(products, sourceProducts []*model.Product) (newProducts, removedProducts []*model.Product) {
    productIdMap, productNumberMap := slf.getMappedProducts(sourceProducts)
    productNumberMap2 := make(map[string]*model.Product, len(products))
    for _, product := range products {
        if productIdMap[product.Id] == nil && productNumberMap[product.Number] == nil {
            newProducts = append(newProducts, product)
        }
        productNumberMap2[product.Number] = product
    }
    for productNumber, product := range productNumberMap {
        if productNumberMap2[productNumber] == nil {
            removedProducts = append(removedProducts, product)
        }
    }
    return
}
func (slf ProductsService) getMappedProducts(sourceProducts []*model.Product) (map[uint]*model.Product, map[string]*model.Product) {
    productIdMap := make(map[uint]*model.Product, len(sourceProducts))
    productNumberMap := make(map[string]*model.Product, len(sourceProducts))
    for _, product := range sourceProducts {
        productIdMap[product.Id] = product
        productNumberMap[product.Number] = product
    }
    return productIdMap, productNumberMap
}
service/serviceContract.go
@@ -4,11 +4,19 @@
    "aps_crm/constvar"
    "aps_crm/model"
    "aps_crm/pkg/ecode"
    "github.com/shopspring/decimal"
)
type SContractService struct{}
func (SContractService) AddServiceContract(serviceContract *model.ServiceContract) int {
    serviceContract.AmountReceivable = decimal.Zero.Round(2)
    serviceContract.AmountInvoiced = decimal.Zero.Round(2)
    serviceContract.AmountReceived = decimal.Zero.Round(2)
    for _, product := range serviceContract.Products {
        serviceContract.AmountReceivable = serviceContract.AmountReceivable.Add(product.Amount.Mul(product.Price))
    }
    serviceContract.AmountReceivable = serviceContract.AmountReceivable.Round(2)
    err := model.NewServiceContractSearch().Create(serviceContract)
    if err != nil {
        return ecode.SContractExist
@@ -19,11 +27,23 @@
func (SContractService) UpdateServiceContract(serviceContract *model.ServiceContract) int {
    // check serviceContract exist
    _, err := model.NewServiceContractSearch().SetId(serviceContract.Id).Find()
    old, err := model.NewServiceContractSearch().SetId(serviceContract.Id).First()
    if err != nil {
        return ecode.SContractNotExist
    }
    var amountReceivable decimal.Decimal
    for _, product := range serviceContract.Products {
        amountReceivable = serviceContract.AmountReceivable.Add(product.Amount.Mul(product.Price))
    }
    if amountReceivable.LessThan(serviceContract.AmountInvoiced) {
        return ecode.SContractProductPriceLowerThanInvoiceAmountErr
    }
    if amountReceivable.LessThan(serviceContract.AmountReceived) {
        return ecode.SContractProductPriceLowerThanReceivedAmountErr
    }
    serviceContract.AmountInvoiced = old.AmountReceived
    serviceContract.AmountReceived = old.AmountReceived
    serviceContract.AmountReceivable = amountReceivable.Round(2)
    err = model.NewServiceContractSearch().SetId(serviceContract.Id).Update(serviceContract)
    if err != nil {
        return ecode.SContractSetErr
@@ -56,7 +76,9 @@
        SetKeyword(keyword).
        SetKeywordType(keywordType).
        SetQueryClass(queryClass).
        SetPage(page, pageSize).FindAll()
        SetPage(page, pageSize).
        SetPreload(true).
        Find()
    if err != nil {
        return nil, 0, ecode.SContractListErr
    }