fuelly api complete
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
@@ -116,6 +117,18 @@ func getAttachmentFile(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getFileBytes(c *gin.Context, fileVariable string) ([]byte, error) {
|
||||||
|
if fileVariable == "" {
|
||||||
|
fileVariable = "file"
|
||||||
|
}
|
||||||
|
formFile, err := c.FormFile(fileVariable)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
openedFile, _ := formFile.Open()
|
||||||
|
return ioutil.ReadAll(openedFile)
|
||||||
|
}
|
||||||
|
|
||||||
func saveUploadedFile(c *gin.Context, fileVariable string) (*db.Attachment, error) {
|
func saveUploadedFile(c *gin.Context, fileVariable string) (*db.Attachment, error) {
|
||||||
if fileVariable == "" {
|
if fileVariable == "" {
|
||||||
fileVariable = "file"
|
fileVariable = "file"
|
||||||
|
|||||||
26
server/controllers/import.go
Normal file
26
server/controllers/import.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/akhilrex/hammond/service"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RegisteImportController(router *gin.RouterGroup) {
|
||||||
|
router.POST("/import/fuelly", fuellyImport)
|
||||||
|
}
|
||||||
|
|
||||||
|
func fuellyImport(c *gin.Context) {
|
||||||
|
bytes, err := getFileBytes(c, "file")
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusUnprocessableEntity, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = service.FuellyImport(bytes, c.MustGet("userId").(string))
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, gin.H{})
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ require (
|
|||||||
github.com/go-playground/validator/v10 v10.4.1
|
github.com/go-playground/validator/v10 v10.4.1
|
||||||
github.com/jasonlvhit/gocron v0.0.1
|
github.com/jasonlvhit/gocron v0.0.1
|
||||||
github.com/joho/godotenv v1.3.0
|
github.com/joho/godotenv v1.3.0
|
||||||
|
github.com/leekchan/accounting v1.0.0 // indirect
|
||||||
github.com/satori/go.uuid v1.2.0
|
github.com/satori/go.uuid v1.2.0
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
|
||||||
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1
|
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
|
||||||
|
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@@ -45,8 +47,11 @@ github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn
|
|||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/leekchan/accounting v1.0.0 h1:+Wd7dJ//dFPa28rc1hjyy+qzCbXPMR91Fb6F1VGTQHg=
|
||||||
|
github.com/leekchan/accounting v1.0.0/go.mod h1:3timm6YPhY3YDaGxl0q3eaflX0eoSx3FXn7ckHe4tO0=
|
||||||
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
||||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||||
|
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/mattn/go-sqlite3 v1.14.5 h1:1IdxlwTNazvbKJQSxoJ5/9ECbEeaTTyeU7sEAZ5KKTQ=
|
github.com/mattn/go-sqlite3 v1.14.5 h1:1IdxlwTNazvbKJQSxoJ5/9ECbEeaTTyeU7sEAZ5KKTQ=
|
||||||
@@ -58,10 +63,14 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
|
|||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
|
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||||
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||||
|
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 h1:pntxY8Ary0t43dCZ5dqY4YTJCObLY1kIXl0uzMv+7DE=
|
||||||
|
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ func main() {
|
|||||||
controllers.RegisterAuthController(router)
|
controllers.RegisterAuthController(router)
|
||||||
controllers.RegisterVehicleController(router)
|
controllers.RegisterVehicleController(router)
|
||||||
controllers.RegisterFilesController(router)
|
controllers.RegisterFilesController(router)
|
||||||
|
controllers.RegisteImportController(router)
|
||||||
|
|
||||||
go assetEnv()
|
go assetEnv()
|
||||||
go intiCron()
|
go intiCron()
|
||||||
|
|||||||
157
server/service/importService.go
Normal file
157
server/service/importService.go
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/csv"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/akhilrex/hammond/db"
|
||||||
|
"github.com/leekchan/accounting"
|
||||||
|
)
|
||||||
|
|
||||||
|
func FuellyImport(content []byte, userId string) error {
|
||||||
|
stream := bytes.NewReader(content)
|
||||||
|
reader := csv.NewReader(stream)
|
||||||
|
records, err := reader.ReadAll()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
vehicles, err := GetUserVehicles(userId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user, err := GetUserById(userId)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var vehicleMap map[string]db.Vehicle = make(map[string]db.Vehicle)
|
||||||
|
for _, vehicle := range *vehicles {
|
||||||
|
vehicleMap[vehicle.Nickname] = vehicle
|
||||||
|
}
|
||||||
|
|
||||||
|
var fillups []db.Fillup
|
||||||
|
var expenses []db.Expense
|
||||||
|
layout := "2006-01-02 15:04"
|
||||||
|
altLayout := "2006-01-02 3:04 PM"
|
||||||
|
|
||||||
|
var errors []string
|
||||||
|
|
||||||
|
for index, record := range records {
|
||||||
|
if index == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var vehicle db.Vehicle
|
||||||
|
var ok bool
|
||||||
|
if vehicle, ok = vehicleMap[record[4]]; !ok {
|
||||||
|
errors = append(errors, "Found an unmapped vehicle entry at row "+strconv.Itoa(index+1))
|
||||||
|
}
|
||||||
|
dateStr := record[2] + " " + record[3]
|
||||||
|
date, err := time.Parse(layout, dateStr)
|
||||||
|
if err != nil {
|
||||||
|
date, err = time.Parse(altLayout, dateStr)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
errors = append(errors, "Found an invalid date/time at row "+strconv.Itoa(index+1))
|
||||||
|
}
|
||||||
|
|
||||||
|
totalCostStr := accounting.UnformatNumber(record[9], 3, user.Currency)
|
||||||
|
totalCost64, err := strconv.ParseFloat(totalCostStr, 32)
|
||||||
|
if err != nil {
|
||||||
|
errors = append(errors, "Found an invalid total cost at row "+strconv.Itoa(index+1))
|
||||||
|
}
|
||||||
|
|
||||||
|
totalCost := float32(totalCost64)
|
||||||
|
odoStr := accounting.UnformatNumber(record[5], 0, user.Currency)
|
||||||
|
odoreading, err := strconv.Atoi(odoStr)
|
||||||
|
if err != nil {
|
||||||
|
errors = append(errors, "Found an invalid odo reading at row "+strconv.Itoa(index+1))
|
||||||
|
}
|
||||||
|
location := record[12]
|
||||||
|
|
||||||
|
//Create Fillup
|
||||||
|
if record[0] == "Gas" {
|
||||||
|
rateStr := accounting.UnformatNumber(record[7], 3, user.Currency)
|
||||||
|
ratet64, err := strconv.ParseFloat(rateStr, 32)
|
||||||
|
if err != nil {
|
||||||
|
errors = append(errors, "Found an invalid cost per gallon at row "+strconv.Itoa(index+1))
|
||||||
|
}
|
||||||
|
rate := float32(ratet64)
|
||||||
|
|
||||||
|
quantity64, err := strconv.ParseFloat(record[8], 32)
|
||||||
|
if err != nil {
|
||||||
|
errors = append(errors, "Found an invalid quantity at row "+strconv.Itoa(index+1))
|
||||||
|
}
|
||||||
|
quantity := float32(quantity64)
|
||||||
|
|
||||||
|
notes := fmt.Sprintf("Octane:%s\nGas Brand:%s\nLocation%s\nTags:%s\nPayment Type:%s\nTire Pressure:%s\nNotes:%s\nMPG:%s",
|
||||||
|
record[10], record[11], record[12], record[13], record[14], record[15], record[16], record[1],
|
||||||
|
)
|
||||||
|
|
||||||
|
isTankFull := record[6] == "Full"
|
||||||
|
|
||||||
|
fillups = append(fillups, db.Fillup{
|
||||||
|
VehicleID: vehicle.ID,
|
||||||
|
FuelUnit: vehicle.FuelUnit,
|
||||||
|
FuelQuantity: quantity,
|
||||||
|
PerUnitPrice: rate,
|
||||||
|
TotalAmount: totalCost,
|
||||||
|
OdoReading: odoreading,
|
||||||
|
IsTankFull: &isTankFull,
|
||||||
|
Comments: notes,
|
||||||
|
FillingStation: location,
|
||||||
|
UserID: userId,
|
||||||
|
Date: date,
|
||||||
|
Currency: user.Currency,
|
||||||
|
DistanceUnit: user.DistanceUnit,
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
if record[0] == "Service" {
|
||||||
|
notes := fmt.Sprintf("Tags:%s\nPayment Type:%s\nNotes:%s",
|
||||||
|
record[13], record[14], record[16],
|
||||||
|
)
|
||||||
|
expenses = append(expenses, db.Expense{
|
||||||
|
VehicleID: vehicle.ID,
|
||||||
|
Amount: totalCost,
|
||||||
|
OdoReading: odoreading,
|
||||||
|
Comments: notes,
|
||||||
|
ExpenseType: record[17],
|
||||||
|
UserID: userId,
|
||||||
|
Currency: user.Currency,
|
||||||
|
Date: date,
|
||||||
|
DistanceUnit: user.DistanceUnit,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if len(errors) != 0 {
|
||||||
|
return fmt.Errorf(strings.Join(errors, "\n"))
|
||||||
|
}
|
||||||
|
|
||||||
|
tx := db.DB.Begin()
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if err := tx.Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := tx.Create(&fillups).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := tx.Create(&expenses).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return tx.Commit().Error
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user