From 7cc2f77503135e266264eb897e8f688e8ad216d5 Mon Sep 17 00:00:00 2001
From: zhangqian <zhangqian@123.com>
Date: 星期三, 09 八月 2023 19:27:38 +0800
Subject: [PATCH] 增加发票和合同产品的关联,发票新增和修改时更改对应合同已开票金额
---
model/product.go | 2
model/serviceContract.go | 2
model/invoiceProduct.go | 131 ++++++++++++++++++++++++++
model/request/serviceContract.go | 32 +++---
service/products.go | 50 ++++++++++
model/invoice.go | 2
service/invoice.go | 51 ++++++++--
model/request/invoice.go | 27 ++--
8 files changed, 255 insertions(+), 42 deletions(-)
diff --git a/model/invoice.go b/model/invoice.go
index 8db5f13..9145a6a 100644
--- a/model/invoice.go
+++ b/model/invoice.go
@@ -28,7 +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;"`
+ Products []*Product `json:"products" gorm:"many2many:invoice_product;"`
}
// InvoiceSearch 閿�鍞彂绁ㄦ悳绱㈡潯浠�
diff --git a/model/invoiceProduct.go b/model/invoiceProduct.go
new file mode 100644
index 0000000..6a8bb50
--- /dev/null
+++ b/model/invoiceProduct.go
@@ -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)
+}
diff --git a/model/product.go b/model/product.go
index 20f0fc2..a61ee23 100644
--- a/model/product.go
+++ b/model/product.go
@@ -6,7 +6,7 @@
)
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 decimal.Decimal `json:"price" gorm:"column:price;type:decimal(10,2);comment:浜у搧浠锋牸"`
Number string `json:"number" gorm:"column:number;type:varchar(255);comment:浜у搧缂栧彿"`
diff --git a/model/request/invoice.go b/model/request/invoice.go
index 5f9128f..526af5f 100644
--- a/model/request/invoice.go
+++ b/model/request/invoice.go
@@ -22,19 +22,20 @@
}
type UpdateInvoice struct {
- Id int `json:"id"`
- ClientId int `gorm:"client_id" json:"clientId"` // 瀹㈡埛id
- InvoiceTypeId int `gorm:"invoice_type_id" json:"invoiceTypeId"` // 鍙戠エ绫诲瀷id
- PrincipalId int `gorm:"principal_id" json:"principalId"` // 閿�鍞礋璐d汉id
- Subject string `gorm:"subject" json:"subject"` // 涓婚
- InvoiceStatusId int `gorm:"invoice_status_id" json:"invoiceStatusId"` // 鍙戠エ鐘舵�乮d
- SourceType int `gorm:"source_type" json:"sourceType"` // 婧愬崟绫诲瀷(1閿�鍞槑缁嗗崟2鏈嶅姟鍚堝悓)
- SourceId int `gorm:"source_id" json:"sourceId"` // 婧愬崟id
- TaxpayerIdNumber string `gorm:"taxpayer_id_number" json:"taxpayerIdNumber"` // 绾崇◣璇嗗埆鍙�
- InvoiceNumber string `gorm:"invoice_number" json:"invoiceNumber"` // 鍙戠エ鍙风爜
- InvoiceDate int `gorm:"invoice_date" json:"invoiceDate"` // 寮�绁ㄦ棩鏈�
- CourierNumber string `gorm:"courier_number" json:"courierNumber"` // 鐗╂祦鍗曞彿
- CourierCompanyId int `gorm:"courier_company_id" json:"courierCompanyId"` // 鐗╂祦鍏徃
+ 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"` // 閿�鍞礋璐d汉id
+ Subject string `gorm:"subject" json:"subject"` // 涓婚
+ InvoiceStatusId int `gorm:"invoice_status_id" json:"invoiceStatusId"` // 鍙戠エ鐘舵�乮d
+ SourceType int `gorm:"source_type" json:"sourceType"` // 婧愬崟绫诲瀷(1閿�鍞槑缁嗗崟2鏈嶅姟鍚堝悓)
+ SourceId int `gorm:"source_id" json:"sourceId"` // 婧愬崟id
+ TaxpayerIdNumber string `gorm:"taxpayer_id_number" json:"taxpayerIdNumber"` // 绾崇◣璇嗗埆鍙�
+ InvoiceNumber string `gorm:"invoice_number" json:"invoiceNumber"` // 鍙戠エ鍙风爜
+ 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 {
diff --git a/model/request/serviceContract.go b/model/request/serviceContract.go
index 1ab30c4..65068e4 100644
--- a/model/request/serviceContract.go
+++ b/model/request/serviceContract.go
@@ -10,22 +10,22 @@
}
type ServiceContract struct {
- ClientId int `json:"clientId"`
- Number string `json:"number"`
- MemberId int `json:"memberId"`
- ContactId int `json:"contactId"`
- SaleChanceId int `json:"saleChanceId"`
- SalesDetailsId int `json:"salesDetailsId"`
- QuotationId int `json:"quotationId"`
- TypeId int `json:"typeId"`
- 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"`
+ ClientId int `json:"clientId"`
+ Number string `json:"number"`
+ MemberId int `json:"memberId"`
+ ContactId int `json:"contactId"`
+ SaleChanceId int `json:"saleChanceId"`
+ SalesDetailsId int `json:"salesDetailsId"`
+ QuotationId int `json:"quotationId"`
+ TypeId int `json:"typeId"`
+ 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"`
}
type UpdateServiceContract struct {
diff --git a/model/serviceContract.go b/model/serviceContract.go
index 4345d2c..651ad73 100644
--- a/model/serviceContract.go
+++ b/model/serviceContract.go
@@ -36,7 +36,7 @@
AmountReceived decimal.Decimal `gorm:"amount_received" json:"amountReceived"` // 宸叉敹閲戦
AmountInvoiced decimal.Decimal `gorm:"amount_invoiced" json:"amountInvoiced"` // 宸插紑绁ㄩ噾棰�
AmountUnInvoiced decimal.Decimal `gorm:"-" json:"amountUnInvoiced"` // 鏈紑绁ㄩ噾棰�
- Products []Product `json:"products" gorm:"many2many:service_contract_product;"`
+ Products []*Product `json:"products" gorm:"many2many:service_contract_product;"`
gorm.Model `json:"-"`
}
diff --git a/service/invoice.go b/service/invoice.go
index c70450d..59301a9 100644
--- a/service/invoice.go
+++ b/service/invoice.go
@@ -18,12 +18,13 @@
func (InvoiceService) AddInvoice(invoice *model.Invoice) int {
if invoice.SourceType == constvar.InvoiceSourceTypeServiceContract {
- serviceContract, err := model.NewServiceContractSearch().SetId(invoice.SourceId).First()
+ serviceContract, err := model.NewServiceContractSearch().SetId(invoice.SourceId).SetPreload(true).First()
if err != nil {
return ecode.DBErr
}
var amountInvoiced decimal.Decimal
- for _, product := range invoice.Products {
+ rightProducts := NewProductsService().PickRightProducts(invoice.Products, serviceContract.Products)
+ for _, product := range rightProducts {
amountInvoiced = serviceContract.AmountInvoiced.Add(product.Amount.Mul(product.Price))
}
amountInvoiced = amountInvoiced.Round(2)
@@ -35,13 +36,9 @@
if err != nil {
return err
}
- err = model.NewServiceContractSearch().SetId(invoice.SourceId).UpdateByMap(map[string]interface{}{
+ return model.NewServiceContractSearch().SetId(invoice.SourceId).UpdateByMap(map[string]interface{}{
"amount_invoiced": amountInvoiced,
})
- if err != nil {
- return err
- }
- return nil
})
if err != nil {
return ecode.DBErr
@@ -86,9 +83,43 @@
}
func (InvoiceService) UpdateInvoice(invoice *model.Invoice) int {
- err := model.NewInvoiceSearch().SetId(invoice.Id).Save(invoice)
- if err != nil {
- return ecode.DBErr
+ 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
}
diff --git a/service/products.go b/service/products.go
new file mode 100644
index 0000000..e8d46e5
--- /dev/null
+++ b/service/products.go
@@ -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
+}
--
Gitblit v1.8.0