From 2cfac4a023335c5deae8a5fc189d459adbf4e7bc Mon Sep 17 00:00:00 2001 From: Nedim Date: Tue, 3 Oct 2023 18:26:57 +0200 Subject: [PATCH] Contracts for buyer --- controllers/contracts_controller.go | 98 ++++++++++++++++++- controllers/devices_controller.go | 4 +- controllers/invoices_controller.go | 41 +++++++- database/contract/contract.go | 142 ++++++++++++++++------------ database/invoice/invoice.go | 1 - models/company.go | 5 + models/contract.go | 34 +++++++ models/invoice.go | 24 +++++ routes/public_routes.go | 4 + 9 files changed, 283 insertions(+), 70 deletions(-) diff --git a/controllers/contracts_controller.go b/controllers/contracts_controller.go index 39a5b49..340b35d 100644 --- a/controllers/contracts_controller.go +++ b/controllers/contracts_controller.go @@ -73,7 +73,7 @@ func GetLatestContracts(c *gin.Context) { } // Fetch contracts - contracts, total, st, err := contract.GetContracts(status, companyName, companyAddress, companyEmail, companyPhone, startTime, endTime, contractName, deviceIDs, limit, offset) + contracts, total, st, err := contract.GetContracts(status, companyName, companyAddress, companyEmail, companyPhone, &startTime, &endTime, contractName, deviceIDs, 0, nil, limit, offset) if err != nil { c.JSON(st, gin.H{"error": err.Error()}) @@ -81,6 +81,100 @@ func GetLatestContracts(c *gin.Context) { } // Respond with the contracts and the total count - c.JSON(http.StatusOK, gin.H{"total": total, "contracts": contracts}) + c.JSON(http.StatusOK, gin.H{"total": total, "data": contracts}) } + +func GetBuyerContracts(c *gin.Context) { + // Existing parameters + limitStr := c.DefaultQuery("limit", "50") + offsetStr := c.DefaultQuery("offset", "0") + status := c.Query("status") + + contractIDStr := c.DefaultQuery("contract_id","0") + dateCreatedStr := c.Query("date_created") + + + // Convert limit and offset to int + limit, err := strconv.Atoi(limitStr) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid limit value"}) + return + } + + offset, err := strconv.Atoi(offsetStr) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid offset value"}) + return + } + + // Convert limit and offset to int + contractID, err := strconv.Atoi(contractIDStr) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid contractID value"}) + return + } + + // Convert startTime to time.Time + var dateCreated time.Time + if dateCreatedStr != "" { + startTimeUnix, err := strconv.ParseInt(dateCreatedStr, 10, 64) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid start_time value"}) + return + } + dateCreated = time.Unix(startTimeUnix, 0) + } + + + + // Fetch contracts + contracts, total, st, err := contract.GetContracts(status, "", "", "", "", nil, nil, "", nil, contractID, &dateCreated, limit, offset) + + if err != nil { + c.JSON(st, gin.H{"error": err.Error()}) + return + } + + // Respond with the contracts and the total count + c.JSON(http.StatusOK, gin.H{"total": total, "data": convertContractToResponseModel(contracts)}) +} + +func GetContractStatuses(c *gin.Context) { + + // Respond with the contract statuses + c.JSON(http.StatusOK, gin.H{"data": models.GetContractStatuses()}) +} + + +func convertContractToResponseModel(contracts []models.Contract) []models.ListContractResponse { + var listInvoiceResponses []models.ListContractResponse + + // Get all statuses + statuses := models.GetContractStatuses() + statusMap := make(map[string]models.Status) + for _, s := range statuses { + statusMap[s.Value] = s + } + + for _, contract := range contracts { + // Get the status based on Value in the DB + status, ok := statusMap[contract.Status] + if !ok { + status = models.Status{Key: "Unknown", Value: "unknown"} + } + + listInvoiceResponse := models.ListContractResponse{ + Status: models.KeyValue{Key: status.Key, Value: status.Value}, + Buyer: models.CompanyShortResponse{ID: int(contract.BuyerID), Name: contract.BuyerName}, + ContractID: int(contract.ID), + DateCreated: contract.CreatedAt, + NumberOfDevices: contract.NumberOfDevices, + } + listInvoiceResponses = append(listInvoiceResponses, listInvoiceResponse) + } + return listInvoiceResponses +} + + + diff --git a/controllers/devices_controller.go b/controllers/devices_controller.go index b96f7e6..6205738 100644 --- a/controllers/devices_controller.go +++ b/controllers/devices_controller.go @@ -133,7 +133,7 @@ func GetDeviceData(c *gin.Context) { } // Respond with the GeoJSON feature collection - c.JSON(http.StatusOK, featureCollection) + c.JSON(http.StatusOK, gin.H{"data": featureCollection}) } func GetDevicesByContract(c *gin.Context) { @@ -161,5 +161,5 @@ func GetDevicesByContract(c *gin.Context) { return } // Respond with the devices - c.JSON(http.StatusOK, devices) + c.JSON(http.StatusOK,gin.H{"data" : devices}) } diff --git a/controllers/invoices_controller.go b/controllers/invoices_controller.go index ca3431c..766330a 100644 --- a/controllers/invoices_controller.go +++ b/controllers/invoices_controller.go @@ -6,6 +6,7 @@ import ( "github.com/gin-gonic/gin" "gitlab.com/pactual1/backend/database/invoice" + "gitlab.com/pactual1/backend/models" ) func GetInvoices(c *gin.Context) { @@ -32,7 +33,10 @@ func GetInvoices(c *gin.Context) { return } - c.JSON(http.StatusOK, gin.H{"total": total, "invoices": invoices}) + // Convert to ListInvoiceResponse type + listInvoiceResponses := convertToResponseModel(invoices) + + c.JSON(http.StatusOK, gin.H{"total": total, "data": listInvoiceResponses}) } func GetInvoiceByID(c *gin.Context) { @@ -50,9 +54,42 @@ func GetInvoiceByID(c *gin.Context) { } if len(invoices) > 0 { - c.JSON(http.StatusOK, gin.H{"invoice": invoices[0]}) + c.JSON(http.StatusOK, gin.H{"data": invoices[0]}) } else { c.JSON(http.StatusNotFound, gin.H{"error": "Invoice not found"}) } } + + +func convertToResponseModel(invoices []models.Invoice) []models.ListInvoiceResponse { + var listInvoiceResponses []models.ListInvoiceResponse + + // Get all statuses + statuses := models.GetInvoiceStatuses() + statusMap := make(map[string]models.Status) + for _, s := range statuses { + statusMap[s.Value] = s + } + + for _, invoice := range invoices { + // Get the status based on Value in the DB + status, ok := statusMap[invoice.Status] + if !ok { + status = models.Status{Key: "Unknown", Value: "unknown"} + } + + listInvoiceResponse := models.ListInvoiceResponse{ + Status: models.KeyValue{Key: status.Key, Value: status.Value}, + Buyer: models.CompanyShortResponse{ID: int(invoice.BuyerID), Name: invoice.BuyerName}, + ContractID: int(invoice.ContractID), + DateCreated: invoice.InvoiceDate, + DueDate: invoice.InvoiceDueDate, + Amount: strconv.FormatInt(invoice.PriceCents, 10), + } + listInvoiceResponses = append(listInvoiceResponses, listInvoiceResponse) + } + return listInvoiceResponses +} + + diff --git a/database/contract/contract.go b/database/contract/contract.go index 03d8075..90bc826 100644 --- a/database/contract/contract.go +++ b/database/contract/contract.go @@ -12,87 +12,103 @@ import ( "gitlab.com/pactual1/backend/shared" ) -func GetContracts(status string, companyName string, companyAddress string, - companyEmail string, companyPhone string, startTime time.Time, endTime time.Time, - contractName string, deviceIDs []int64, limit, offset int) ([]models.Contract, int64, int, error) { - var contracts []models.Contract - db := shared.GetDb() - countDb := db +func GetContracts(status string, companyName string, companyAddress string, + companyEmail string, companyPhone string, startTime *time.Time, endTime *time.Time, + contractName string, deviceIDs []int64, contractID int, dateCreated *time.Time, limit, offset int) ([]models.Contract, int64, int, error) { - // Search by Status - if status != "" { - db = shared.GetDb().Where("contracts.status = ?", status) - countDb = countDb.Where("contracts.status = ?", status) - } + var contracts []models.Contract + db := shared.GetDb() + countDb := db - // Search by Company Fields - if companyName != "" || companyAddress != "" || companyEmail != "" || companyPhone != "" { - db = db.Joins("left join companies on companies.id = contracts.buyer_id") - countDb = countDb.Joins("left join companies on companies.id = contracts.buyer_id") + // Define custom fields to be selected, varies based on joined tables + customFields := "contracts.*, array_length(contracts.device_ids, 1) as number_of_devices" - if companyName != "" { - db = db.Where("companies.name LIKE ?", "%"+companyName+"%") - countDb = countDb.Where("companies.name LIKE ?", "%"+companyName+"%") - } + // Search by Status + if status != "" { + db = db.Where("contracts.status = ?", status) + countDb = countDb.Where("contracts.status = ?", status) + } - if companyAddress != "" { - db = db.Where("companies.address LIKE ?", "%"+companyAddress+"%") - countDb = countDb.Where("companies.address LIKE ?", "%"+companyAddress+"%") - } + // Search by Company Fields + db = db.Joins("left join companies on companies.id = contracts.buyer_id") + countDb = countDb.Joins("left join companies on companies.id = contracts.buyer_id") + customFields += ", companies.name as buyer_name" + if companyName != "" { + db = db.Where("companies.name LIKE ?", "%"+companyName+"%") + countDb = countDb.Where("companies.name LIKE ?", "%"+companyName+"%") + } + if companyAddress != "" { + db = db.Where("companies.address LIKE ?", "%"+companyAddress+"%") + countDb = countDb.Where("companies.address LIKE ?", "%"+companyAddress+"%") + } + if companyEmail != "" { + db = db.Where("companies.email LIKE ?", "%"+companyEmail+"%") + countDb = countDb.Where("companies.email LIKE ?", "%"+companyEmail+"%") + } + if companyPhone != "" { + db = db.Where("companies.phone LIKE ?", "%"+companyPhone+"%") + countDb = countDb.Where("companies.phone LIKE ?", "%"+companyPhone+"%") + } - if companyEmail != "" { - db = db.Where("companies.email LIKE ?", "%"+companyEmail+"%") - countDb = countDb.Where("companies.email LIKE ?", "%"+companyEmail+"%") - } - - if companyPhone != "" { - db = db.Where("companies.phone LIKE ?", "%"+companyPhone+"%") - countDb = countDb.Where("companies.phone LIKE ?", "%"+companyPhone+"%") - } - } - - // Search by Contract Name + // Search by Contract Name if contractName != "" { db = db.Where("contracts.name LIKE ?", "%"+contractName+"%") countDb = countDb.Where("contracts.name LIKE ?", "%"+contractName+"%") } - // Search by Start Time - if !startTime.IsZero() { - db = db.Where("start_time >= ?", startTime) - countDb = countDb.Where("start_time >= ?", startTime) - } + // Search by Contract Name + if contractID != 0{ + db = db.Where("contracts.id = ?", contractID) + countDb = countDb.Where("contracts.id = ?", contractID) + } - // Search by End Time - if !endTime.IsZero() { - db = db.Where("end_time <= ?", endTime) - countDb = countDb.Where("end_time <= ?", endTime) - } + // Search by Start Time and End Time + if startTime != nil &&!startTime.IsZero() { + db = db.Where("start_time >= ?", startTime) + countDb = countDb.Where("start_time >= ?", startTime) + } - // Search by Device IDs - if len(deviceIDs) > 0 { + if endTime != nil && !startTime.IsZero() { + db = db.Where("end_time <= ?", endTime) + countDb = countDb.Where("end_time <= ?", endTime) + } + + if dateCreated != nil && !dateCreated.IsZero() { + db = db.Where("contracts.created_at = ?", dateCreated) + countDb = countDb.Where("created_at = ?", dateCreated) + } + + + // Search by Device IDs + if len(deviceIDs) > 0 { db = db.Where("device_ids && ?", pq.Array(deviceIDs)) countDb = countDb.Where("device_ids && ?", pq.Array(deviceIDs)) - } + } - var total int64 - if err := countDb.Model(&models.Contract{}).Count(&total).Error; err != nil { - log.Printf("GetContracts Error: Database error: %v", err) - return contracts, total, http.StatusInternalServerError, err - } + // Fetch total count of filtered records + var total int64 + if err := countDb.Model(&models.Contract{}).Count(&total).Error; err != nil { + log.Printf("GetContracts Error: Database error: %v", err) + return contracts, total, http.StatusInternalServerError, err + } - if err := db.Order("created_at desc").Limit(limit).Offset(offset).Find(&contracts).Error; err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) { - log.Printf("GetContracts Error: No contracts found: %v", err) - return contracts, total, http.StatusNotFound, err - } else { - log.Printf("GetContracts Error: Database error: %v", err) - return contracts, total, http.StatusInternalServerError, err - } - } + // Fetch contracts with custom fields + if err := db.Select(customFields). + Order("created_at desc"). + Limit(limit). + Offset(offset). + Find(&contracts).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + log.Printf("GetContracts Error: No contracts found: %v", err) + return contracts, total, http.StatusNotFound, err + } else { + log.Printf("GetContracts Error: Database error: %v", err) + return contracts, total, http.StatusInternalServerError, err + } + } - return contracts, total, http.StatusOK, nil + return contracts, total, http.StatusOK, nil } diff --git a/database/invoice/invoice.go b/database/invoice/invoice.go index c9a625c..1add62f 100644 --- a/database/invoice/invoice.go +++ b/database/invoice/invoice.go @@ -58,4 +58,3 @@ func GetInvoices(buyerName string, sortBy string, limit int, offset int, id uint return invoices, total, nil } - diff --git a/models/company.go b/models/company.go index b30fa2e..9f9c8ab 100644 --- a/models/company.go +++ b/models/company.go @@ -12,6 +12,11 @@ type Company struct { Devices []Device } +type CompanyShortResponse struct { + ID int + Name string +} + func (Company) Update() (bool, error) { return false, nil diff --git a/models/contract.go b/models/contract.go index 1402d2d..da8cdab 100644 --- a/models/contract.go +++ b/models/contract.go @@ -21,10 +21,44 @@ type Contract struct { Status string BlockchainID string ContractInfos []ContractInfo + BuyerName string `gorm:"-"` + NumberOfDevices int `gorm:"-"` } const ContractStatusActive = "active" const ContractStatusPending = "pending" +const ContractStatusDraft = "draft" +const ContractStatusSigned = "signed" +const ContractStatusReadyForActivation = "ready_for_activation" +const ContractStatusExecuted = "executed" +const ContractStatusRevoked = "revoked" + + +type Status struct { + Key string `json:"key"` + Value string `json:"value"` +} + +func GetContractStatuses() []Status { + return []Status{ + {Key: "Active", Value: "active"}, + {Key: "Pending signature", Value: "pending"}, + {Key: "Draft", Value: "draft"}, + {Key: "Signed", Value: "signed"}, + {Key: "Ready for Activation", Value: "ready_for_activation"}, + {Key: "Executed", Value: "executed"}, + {Key: "Revoked", Value: "revoked"}, + } +} + +type ListContractResponse struct { + Status KeyValue `json:"Status"` + Buyer CompanyShortResponse `json:"Buyer"` + ContractID int + NumberOfDevices int + DateCreated time.Time + +} func (Contract) Update() (bool, error) { return false, nil diff --git a/models/invoice.go b/models/invoice.go index ff68619..4c082f8 100644 --- a/models/invoice.go +++ b/models/invoice.go @@ -27,9 +27,33 @@ type Invoice struct { InvoiceDueDate time.Time ContractID uint InvoiceItem [] InvoiceItem + Status string } +type ListInvoiceResponse struct { + Status KeyValue `json:"Status"` + Buyer CompanyShortResponse `json:"Buyer"` + ContractID int + DateCreated time.Time + DueDate time.Time + Amount string + +} + +type KeyValue struct { + Key string + Value string +} + + + +func GetInvoiceStatuses() []Status { + return []Status{ + {Key: "Insurance claimed", Value: "insurance_claimed"}, + {Key: "Invoice issued", Value: "invoice_issued"}, + } +} func (Invoice) Update() (bool, error) { return false, nil diff --git a/routes/public_routes.go b/routes/public_routes.go index 5eb86cb..780d96a 100644 --- a/routes/public_routes.go +++ b/routes/public_routes.go @@ -23,5 +23,9 @@ func RegisterPublicRoutes(r *gin.Engine) { r.GET("/text_templates/", controllers.ListTextTemplates) r.POST("/text_templates/save", controllers.CreateTextTemplate) r.GET("/product_templates/:template_id", controllers.GetProductTemplate) + + // Contracts + r.GET("/contracts/statuses", controllers.GetContractStatuses) + r.GET("/contracts/list", controllers.GetBuyerContracts) }