diff --git a/database/contract/contract.go b/database/contract/contract.go index 6f5c56c..962256f 100644 --- a/database/contract/contract.go +++ b/database/contract/contract.go @@ -27,8 +27,8 @@ func GetContracts(status []string, companyName string, companyAddress string, db := shared.GetDb() countDb := db - // Define custom fields to be selected, varies based on joined tables - customFields := "distinct contracts.*, array_length(contracts.device_ids, 1) as number_of_devices" + customFields := "distinct contracts.*, array_length(contracts.device_ids, 1) as number_of_devices, " + + "(SELECT array_agg(devices.imei) FROM devices WHERE devices.id = ANY(contracts.device_ids)) as devices_imeis_scanner" // Search by Statuses if len(status) > 0 { @@ -87,8 +87,8 @@ func GetContracts(status []string, companyName string, companyAddress string, // Search by Device IDs if len(deviceIDs) > 0 { - db = db.Where("device_ids && ?", pq.Array(deviceIDs)) - countDb = countDb.Where("device_ids && ?", pq.Array(deviceIDs)) + db = db.Where("contracts.device_ids && ?", pq.Array(deviceIDs)) + countDb = countDb.Where("contracts.device_ids && ?", pq.Array(deviceIDs)) } // Fetch total count of filtered records @@ -112,6 +112,10 @@ func GetContracts(status []string, companyName string, companyAddress string, return contracts, total, http.StatusInternalServerError, err } } + for i := range contracts { + contracts[i].DevicesImeis = []string(contracts[i].DevicesImeisScanner) + // Now DevicesImeis contains the IMEIs, and you can use them as needed + } return contracts, total, http.StatusOK, nil } @@ -262,6 +266,32 @@ func GetContractByID(contractID uint) (models.Contract, int, error) { } contract.BuyerName = buyer.Name + // Fetch the devices + // Convert DeviceIDs from pq.Int64Array to []int64 for the query + deviceIDs := make([]int64, 0, len(contract.DeviceIDs)) + for _, id := range contract.DeviceIDs { + deviceIDs = append(deviceIDs, int64(id)) + } + + // Fetch the devices using the slice of device IDs + var devicesInfo []models.ContractDeviceInfo + if err := shared.GetDb(). + Table("devices"). // Specify the actual table name for devices + Where("id IN (?)", deviceIDs). + Select("id, imei"). + Find(&devicesInfo).Error; err != nil { + log.Printf("GetContractByID Error: Could not fetch devices: %v", err) + return contract, http.StatusInternalServerError, err + } + + // Prepare a slice of IMEIs to add to the contract + deviceImeis := make([]string, len(devicesInfo)) + for i, device := range devicesInfo { + deviceImeis[i] = device.IMEI + } + + contract.DevicesImeis = deviceImeis + return contract, http.StatusOK, nil } diff --git a/models/contract.go b/models/contract.go index 210d2ca..1a18929 100644 --- a/models/contract.go +++ b/models/contract.go @@ -1,6 +1,8 @@ package models import ( + "errors" + "strings" "time" "github.com/lib/pq" @@ -8,35 +10,66 @@ import ( type Contract struct { BaseModel - Name string `json:"name"` - DeviceIDs pq.Int64Array `json:"deviceIds" gorm:"type:integer[]"` - BuyerID uint `json:"buyerId"` - SellerID uint `json:"sellerId"` - Description string `json:"description"` - StartPlaceName string `json:"startPlaceName"` - StartLat float64 `json:"startLat"` - StartLon float64 `json:"startLon"` - EndPlaceName string `json:"endPlaceName"` - EndLat float64 `json:"endLat"` - EndLon float64 `json:"endLon"` - StartTime time.Time `json:"startTime"` - EndTime time.Time `json:"endTime"` - Status string `json:"status"` - BlockchainSecret string `json:"blockchainSecret"` - ContractInfos []ContractInfo `json:"contractInfos"` - ProductID uint `json:"productId"` - TemplateID uint `json:"templateId"` - MaxTemp float64 `json:"maxTemp"` - MinTemp float64 `json:"minTemp"` - ArrivalDate time.Time `json:"arrivalDate"` - PenaltyType string `json:"penaltyType"` - PenaltyValue int `json:"penaltyValue"` - PenaltyRec string `json:"penaltyRec"` - BuyerName string `json:"buyerName" gorm:"-"` - SellerName string `json:"sellerName" gorm:"-"` - ProductName string `json:"productName" gorm:"-"` - NumberOfDevices int `json:"numberOfDevices" gorm:"-"` - DevicesImeis []string `json:"devicesImeis" gorm:"-"` + Name string `json:"name"` + DeviceIDs pq.Int64Array `json:"deviceIds" gorm:"type:integer[]"` + BuyerID uint `json:"buyerId"` + SellerID uint `json:"sellerId"` + Description string `json:"description"` + StartPlaceName string `json:"startPlaceName"` + StartLat float64 `json:"startLat"` + StartLon float64 `json:"startLon"` + EndPlaceName string `json:"endPlaceName"` + EndLat float64 `json:"endLat"` + EndLon float64 `json:"endLon"` + StartTime time.Time `json:"startTime"` + EndTime time.Time `json:"endTime"` + Status string `json:"status"` + BlockchainSecret string `json:"blockchainSecret"` + ContractInfos []ContractInfo `json:"contractInfos"` + ProductID uint `json:"productId"` + TemplateID uint `json:"templateId"` + MaxTemp float64 `json:"maxTemp"` + MinTemp float64 `json:"minTemp"` + ArrivalDate time.Time `json:"arrivalDate"` + PenaltyType string `json:"penaltyType"` + PenaltyValue int `json:"penaltyValue"` + PenaltyRec string `json:"penaltyRec"` + BuyerName string `json:"buyerName" gorm:"-"` + SellerName string `json:"sellerName" gorm:"-"` + ProductName string `json:"productName" gorm:"-"` + NumberOfDevices int `json:"numberOfDevices" gorm:"-"` + DevicesImeis []string `json:"devicesImeis" gorm:"-"` + DevicesImeisScanner PQStringArray `json:"contractDevicesImeis" gorm:"-"` +} + +type PQStringArray []string + +func (a *PQStringArray) Scan(src interface{}) error { + if src == nil { + *a = nil + return nil + } + + // The database driver will give us a string in the format "{item1,item2,item3}" + // We need to convert this to a slice + var str string + switch src := src.(type) { + case []byte: + str = string(src) + case string: + str = src + default: + return errors.New("incompatible type for PQStringArray") + } + + // Remove the curly braces and split the string + str = strings.Trim(str, "{}") + if str != "" { + *a = strings.Split(str, ",") + } else { + *a = []string{} + } + return nil } type DashboardContractResponse struct { @@ -104,6 +137,7 @@ type ContractResponse struct { PenaltyType string `json:"penaltyType"` PenaltyValue int `json:"penaltyValue"` PenaltyRec string `json:"penaltyRec"` + DeviceImeis []string `json:"devicesImeis"` } func ConvertContractsToContractResponse(contracts []Contract) []ContractResponse { @@ -173,6 +207,7 @@ func ConvertContractToContractResponse(contract Contract) ContractResponse { PenaltyType: contract.PenaltyType, PenaltyValue: contract.PenaltyValue, PenaltyRec: contract.PenaltyRec, + DeviceImeis: contract.DevicesImeis, } return contractResponse @@ -240,6 +275,7 @@ func ConvertContractToListResponse(contracts []Contract) []ListContractResponse ContractName: contract.Name, DateCreated: contract.CreatedAt, NumberOfDevices: contract.NumberOfDevices, + DeviceImeis: contract.DevicesImeis, } listInvoiceResponses = append(listInvoiceResponses, listInvoiceResponse) } @@ -291,6 +327,7 @@ type ListContractResponse struct { ContractName string `json:"contractName"` NumberOfDevices int `json:"numberOfDevices"` DateCreated time.Time `json:"dateCreated"` + DeviceImeis []string `json:"deviceImeis"` } type CreateContractRequestPayload struct { diff --git a/models/device.go b/models/device.go index 24ea6be..112b298 100644 --- a/models/device.go +++ b/models/device.go @@ -27,6 +27,11 @@ type DeviceResponse struct { DeviceInfos *[]DeviceInfo `json:"deviceInfos"` } +type ContractDeviceInfo struct { + ID uint `json:"id"` + IMEI string `json:"imei"` +} + func ConvertDeviceToResponse(devices []Device) []DeviceResponse { var deviceResponses []DeviceResponse for _, device := range devices { diff --git a/postman/NOVA.postman_collection.json b/postman/NOVA.postman_collection.json index e219b2a..2ed9086 100644 --- a/postman/NOVA.postman_collection.json +++ b/postman/NOVA.postman_collection.json @@ -250,15 +250,15 @@ }, { "key": "Date", - "value": "Fri, 06 Oct 2023 08:27:52 GMT" + "value": "Thu, 09 Nov 2023 09:14:11 GMT" }, { "key": "Content-Length", - "value": "1056" + "value": "1819" } ], "cookie": [], - "body": "{\n \"data\": [\n {\n \"status\": {\n \"key\": \"Unknown\",\n \"value\": \"unknown\"\n },\n \"buyer\": {\n \"id\": 2,\n \"name\": \"Nova kompanija\"\n },\n \"contractID\": 7,\n \"numberOfDevices\": 0,\n \"dateCreated\": \"2023-10-06T08:36:23.752245+02:00\"\n },\n {\n \"status\": {\n \"key\": \"Unknown\",\n \"value\": \"unknown\"\n },\n \"buyer\": {\n \"id\": 2,\n \"name\": \"Nova kompanija\"\n },\n \"contractID\": 6,\n \"numberOfDevices\": 0,\n \"dateCreated\": \"2023-10-06T08:25:40.488392+02:00\"\n },\n {\n \"status\": {\n \"key\": \"Active\",\n \"value\": \"active\"\n },\n \"buyer\": {\n \"id\": 1,\n \"name\": \"Nova kompanija\"\n },\n \"contractID\": 1,\n \"numberOfDevices\": 3,\n \"dateCreated\": \"2023-09-13T08:36:41.742294+02:00\"\n },\n {\n \"status\": {\n \"key\": \"Pending signature\",\n \"value\": \"pending\"\n },\n \"buyer\": {\n \"id\": 1,\n \"name\": \"Nova kompanija\"\n },\n \"contractID\": 2,\n \"numberOfDevices\": 1,\n \"dateCreated\": \"2023-09-13T08:36:41.742294+02:00\"\n },\n {\n \"status\": {\n \"key\": \"Active\",\n \"value\": \"active\"\n },\n \"buyer\": {\n \"id\": 1,\n \"name\": \"Nova kompanija\"\n },\n \"contractID\": 3,\n \"numberOfDevices\": 4,\n \"dateCreated\": \"2023-09-13T08:36:41.742294+02:00\"\n },\n {\n \"status\": {\n \"key\": \"Active\",\n \"value\": \"active\"\n },\n \"buyer\": {\n \"id\": 1,\n \"name\": \"Nova kompanija\"\n },\n \"contractID\": 4,\n \"numberOfDevices\": 4,\n \"dateCreated\": \"2023-09-13T08:36:41.742294+02:00\"\n }\n ],\n \"total\": 6\n}" + "body": "{\n \"data\": [\n {\n \"status\": {\n \"key\": \"\",\n \"value\": \"\"\n },\n \"buyer\": {\n \"id\": 2,\n \"name\": \"Nova kompanija 2\"\n },\n \"contractID\": 9,\n \"contractName\": \"\",\n \"numberOfDevices\": 0,\n \"dateCreated\": \"2023-10-06T10:40:15.342985+02:00\",\n \"deviceImeis\": null\n },\n {\n \"status\": {\n \"key\": \"\",\n \"value\": \"\"\n },\n \"buyer\": {\n \"id\": 2,\n \"name\": \"Nova kompanija 2\"\n },\n \"contractID\": 8,\n \"contractName\": \"\",\n \"numberOfDevices\": 0,\n \"dateCreated\": \"2023-10-06T10:40:13.150385+02:00\",\n \"deviceImeis\": null\n },\n {\n \"status\": {\n \"key\": \"\",\n \"value\": \"\"\n },\n \"buyer\": {\n \"id\": 2,\n \"name\": \"Nova kompanija 2\"\n },\n \"contractID\": 7,\n \"contractName\": \"\",\n \"numberOfDevices\": 0,\n \"dateCreated\": \"2023-10-06T08:36:23.752245+02:00\",\n \"deviceImeis\": null\n },\n {\n \"status\": {\n \"key\": \"\",\n \"value\": \"\"\n },\n \"buyer\": {\n \"id\": 2,\n \"name\": \"Nova kompanija 2\"\n },\n \"contractID\": 6,\n \"contractName\": \"\",\n \"numberOfDevices\": 0,\n \"dateCreated\": \"2023-10-06T08:25:40.488392+02:00\",\n \"deviceImeis\": null\n },\n {\n \"status\": {\n \"key\": \"active\",\n \"value\": \"Active\"\n },\n \"buyer\": {\n \"id\": 1,\n \"name\": \"Nova kompanija\"\n },\n \"contractID\": 1,\n \"contractName\": \"Contract1\",\n \"numberOfDevices\": 3,\n \"dateCreated\": \"2023-09-13T08:36:41.742294+02:00\",\n \"deviceImeis\": [\n \"352656107000002\",\n \"352656107000000\",\n \"352656107000001\"\n ]\n },\n {\n \"status\": {\n \"key\": \"executed\",\n \"value\": \"Executed\"\n },\n \"buyer\": {\n \"id\": 2,\n \"name\": \"Nova kompanija 2\"\n },\n \"contractID\": 2,\n \"contractName\": \"Cont 1\",\n \"numberOfDevices\": 1,\n \"dateCreated\": \"2023-09-13T08:36:41.742294+02:00\",\n \"deviceImeis\": [\n \"352656107000002\"\n ]\n },\n {\n \"status\": {\n \"key\": \"active\",\n \"value\": \"Active\"\n },\n \"buyer\": {\n \"id\": 2,\n \"name\": \"Nova kompanija 2\"\n },\n \"contractID\": 3,\n \"contractName\": \"Contract2\",\n \"numberOfDevices\": 4,\n \"dateCreated\": \"2023-09-13T08:36:41.742294+02:00\",\n \"deviceImeis\": [\n \"SOME_VALID_IMEI\",\n \"UNKNOWN\",\n \"123456789\"\n ]\n },\n {\n \"status\": {\n \"key\": \"active\",\n \"value\": \"Active\"\n },\n \"buyer\": {\n \"id\": 2,\n \"name\": \"Nova kompanija 2\"\n },\n \"contractID\": 4,\n \"contractName\": \"Contract2\",\n \"numberOfDevices\": 4,\n \"dateCreated\": \"2023-09-13T08:36:41.742294+02:00\",\n \"deviceImeis\": [\n \"SOME_VALID_IMEI\",\n \"UNKNOWN\",\n \"123456789\"\n ]\n }\n ],\n \"total\": 8\n}" } ] }, @@ -1467,7 +1467,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"email\": \"someemail@gmail.com\"\n}", + "raw": "{\n \"email\": \"email@mail.com\"\n}", "options": { "raw": { "language": "json" @@ -1557,7 +1557,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"password\": \"someemail@gmail.com\",\n \"token\": \"sb6qxahXINNsg52dH0Q7u7iR6yaPLRRQ4OnbUWlxEo0=\"\n}", + "raw": "{\n \"password\": \"address@gmail.com\",\n \"token\": \"oR4wIlYRsUgkzpK07KTIhwIZhB-HYCGKNYFDeNqpY0M=\"\n}", "options": { "raw": { "language": "json" @@ -1576,69 +1576,7 @@ ] } }, - "response": [ - { - "name": "Create Contracts", - "originalRequest": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"sellerId\": 1,\n \"buyerId\": 2,\n \"description\": \"This is a sample contract.\",\n \"productId\": 3,\n \"minTemp\": -20.0,\n \"maxTemp\": 40.0,\n \"arrivalDate\": 1674019200,\n \"penaltyType\": \"AMOUNT\",\n \"penaltyValue\": 100,\n \"penaltyRec\": \"DAILY\"\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "{{URL}}/contracts/create", - "host": [ - "{{URL}}" - ], - "path": [ - "contracts", - "create" - ] - } - }, - "status": "OK", - "code": 200, - "_postman_previewlanguage": "json", - "header": [ - { - "key": "Access-Control-Allow-Credentials", - "value": "true" - }, - { - "key": "Access-Control-Allow-Headers", - "value": "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With" - }, - { - "key": "Access-Control-Allow-Methods", - "value": "POST, OPTIONS, GET, PUT" - }, - { - "key": "Access-Control-Allow-Origin", - "value": "*" - }, - { - "key": "Content-Type", - "value": "application/json; charset=utf-8" - }, - { - "key": "Date", - "value": "Fri, 06 Oct 2023 08:40:15 GMT" - }, - { - "key": "Content-Length", - "value": "61" - } - ], - "cookie": [], - "body": "{\n \"id\": 9,\n \"message\": \"Successfully received and saved contract\"\n}" - } - ] + "response": [] }, { "name": "Login", @@ -1655,6 +1593,43 @@ }, "method": "POST", "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"email\": \"someemail@gmail.com\",\n \"password\": \"pass\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/user/login", + "host": [ + "{{URL}}" + ], + "path": [ + "user", + "login" + ] + } + }, + "response": [] + }, + { + "name": "Logout", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{AuthToken}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], "body": { "mode": "raw", "raw": "", @@ -1665,129 +1640,47 @@ } }, "url": { - "raw": "{{URL}}/user/logout", + "raw": "{{URL}}/logout", "host": [ "{{URL}}" ], "path": [ - "user", "logout" ] } }, - "response": [ - { - "name": "Create Contracts", - "originalRequest": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"sellerId\": 1,\n \"buyerId\": 2,\n \"description\": \"This is a sample contract.\",\n \"productId\": 3,\n \"minTemp\": -20.0,\n \"maxTemp\": 40.0,\n \"arrivalDate\": 1674019200,\n \"penaltyType\": \"AMOUNT\",\n \"penaltyValue\": 100,\n \"penaltyRec\": \"DAILY\"\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "{{URL}}/contracts/create", - "host": [ - "{{URL}}" - ], - "path": [ - "contracts", - "create" - ] - } - }, - "status": "OK", - "code": 200, - "_postman_previewlanguage": "json", - "header": [ - { - "key": "Access-Control-Allow-Credentials", - "value": "true" - }, - { - "key": "Access-Control-Allow-Headers", - "value": "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With" - }, - { - "key": "Access-Control-Allow-Methods", - "value": "POST, OPTIONS, GET, PUT" - }, - { - "key": "Access-Control-Allow-Origin", - "value": "*" - }, - { - "key": "Content-Type", - "value": "application/json; charset=utf-8" - }, - { - "key": "Date", - "value": "Fri, 06 Oct 2023 08:40:15 GMT" - }, - { - "key": "Content-Length", - "value": "61" - } - ], - "cookie": [], - "body": "{\n \"id\": 9,\n \"message\": \"Successfully received and saved contract\"\n}" - } - ] + "response": [] }, { - "name": "Logout", + "name": "Contract by ID", "request": { - "method": "POST", + "method": "GET", "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"password\": \"someemail@gmail.com\",\n \"token\": \"sb6qxahXINNsg52dH0Q7u7iR6yaPLRRQ4OnbUWlxEo0=\"\n}", - "options": { - "raw": { - "language": "json" - } - } - }, "url": { - "raw": "{{URL}}/user/set/password", + "raw": "{{URL}}/contracts/1", "host": [ "{{URL}}" ], "path": [ - "user", - "set", - "password" + "contracts", + "1" ] } }, "response": [ { - "name": "Create Contracts", + "name": "Contract by ID", "originalRequest": { - "method": "POST", + "method": "GET", "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"sellerId\": 1,\n \"buyerId\": 2,\n \"description\": \"This is a sample contract.\",\n \"productId\": 3,\n \"minTemp\": -20.0,\n \"maxTemp\": 40.0,\n \"arrivalDate\": 1674019200,\n \"penaltyType\": \"AMOUNT\",\n \"penaltyValue\": 100,\n \"penaltyRec\": \"DAILY\"\n}", - "options": { - "raw": { - "language": "json" - } - } - }, "url": { - "raw": "{{URL}}/contracts/create", + "raw": "{{URL}}/contracts/1", "host": [ "{{URL}}" ], "path": [ "contracts", - "create" + "1" ] } }, @@ -1817,17 +1710,23 @@ }, { "key": "Date", - "value": "Fri, 06 Oct 2023 08:40:15 GMT" + "value": "Thu, 09 Nov 2023 09:18:14 GMT" }, { "key": "Content-Length", - "value": "61" + "value": "703" } ], "cookie": [], - "body": "{\n \"id\": 9,\n \"message\": \"Successfully received and saved contract\"\n}" + "body": "{\n \"data\": {\n \"id\": 1,\n \"createdAt\": \"2023-09-13T08:36:41.742294+02:00\",\n \"updatedAt\": \"2023-09-13T08:36:41.742294+02:00\",\n \"deletedAt\": {\n \"Time\": \"0001-01-01T00:00:00Z\",\n \"Valid\": false\n },\n \"name\": \"Contract1\",\n \"deviceIds\": [\n 5,\n 6,\n 7\n ],\n \"seller\": {\n \"id\": 2,\n \"name\": \"\"\n },\n \"buyer\": {\n \"id\": 1,\n \"name\": \"\"\n },\n \"start\": {\n \"name\": \"\",\n \"lat\": 1,\n \"lon\": 1,\n \"time\": \"2023-09-13T08:36:41.742294+02:00\"\n },\n \"end\": {\n \"name\": \"\",\n \"lat\": 1,\n \"lon\": 1,\n \"time\": \"2023-09-20T08:36:41.742294+02:00\"\n },\n \"product\": {\n \"id\": 0,\n \"name\": \"\"\n },\n \"description\": \"\",\n \"status\": \"active\",\n \"blockchainId\": \"\",\n \"contractInfos\": null,\n \"maxTemp\": 10,\n \"minTemp\": 1,\n \"arrivalDate\": \"0001-01-01T00:00:00Z\",\n \"penaltyType\": \"\",\n \"penaltyValue\": 0,\n \"penaltyRec\": \"\",\n \"devicesImeis\": [\n \"352656107000002\",\n \"352656107000000\",\n \"352656107000001\"\n ]\n }\n}" } ] } + ], + "variable": [ + { + "key": "AuthToken", + "value": "JBYYy0cb5FOzc_VxWg1tDOF8tKu2Dl5NhR" + } ] } \ No newline at end of file