diff options
Diffstat (limited to 'item')
-rw-r--r-- | item/controller.go | 156 | ||||
-rw-r--r-- | item/hooks.go | 57 | ||||
-rw-r--r-- | item/item.go | 52 | ||||
-rw-r--r-- | item/router.go | 40 | ||||
-rw-r--r-- | item/service.go | 126 | ||||
-rw-r--r-- | item/validators.go | 111 |
6 files changed, 542 insertions, 0 deletions
diff --git a/item/controller.go b/item/controller.go new file mode 100644 index 0000000..b4e27c1 --- /dev/null +++ b/item/controller.go @@ -0,0 +1,156 @@ +/* openbills - Server for web based Libre Billing Software + * Copyright (C) 2023 Vidhu Kant Sharma <vidhukant@vidhukant.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +package item + +import ( + e "vidhukant.com/openbills/errors" + "github.com/gin-gonic/gin" + "net/http" + "strconv" +) + +func handleGetBrandItems (ctx *gin.Context) { + id, err := strconv.ParseUint(ctx.Param("id"), 10, 64) + if err != nil { + ctx.Error(e.ErrInvalidID) + return + } + + var items []SavedItem + err = getBrandItems(&items, uint(id)) + if err != nil { + ctx.Error(err) + ctx.Abort() + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "message": "success", + "data": items, + }) +} + +func handleGetBrands (ctx *gin.Context) { + var brands []Brand + + err := getBrands(&brands) + if err != nil { + ctx.Error(err) + ctx.Abort() + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "message": "success", + "data": brands, + }) +} + +func handleSaveBrand (ctx *gin.Context) { + var brand Brand + ctx.Bind(&brand) + + err := brand.upsert() + if err != nil { + ctx.Error(err) + ctx.Abort() + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "message": "success", + "data": brand, + }) +} + +func handleDelBrand (ctx *gin.Context) { + id, err := strconv.ParseUint(ctx.Param("id"), 10, 64) + if err != nil { + ctx.Error(e.ErrInvalidID) + return + } + + var brand Brand + brand.ID = uint(id) + + err = brand.del() + if err != nil { + ctx.Error(err) + ctx.Abort() + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "message": "success", + }) +} + +func handleGetItems (ctx *gin.Context) { + var items []SavedItem + + err := getItems(&items) + if err != nil { + ctx.Error(err) + ctx.Abort() + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "message": "success", + "data": items, + }) +} + +func handleSaveItem (ctx *gin.Context) { + var item SavedItem + ctx.Bind(&item) + + err := item.upsert() + if err != nil { + ctx.Error(err) + ctx.Abort() + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "message": "success", + "data": item, + }) +} + +func handleDelItem (ctx *gin.Context) { + id, err := strconv.ParseUint(ctx.Param("id"), 10, 64) + if err != nil { + ctx.Error(e.ErrInvalidID) + return + } + + var item SavedItem + item.ID = uint(id) + + err = item.del() + if err != nil { + ctx.Error(err) + ctx.Abort() + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "message": "success", + }) +} diff --git a/item/hooks.go b/item/hooks.go new file mode 100644 index 0000000..ddb9a44 --- /dev/null +++ b/item/hooks.go @@ -0,0 +1,57 @@ +/* openbills - Server for web based Libre Billing Software + * Copyright (C) 2023 Vidhu Kant Sharma <vidhukant@vidhukant.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +package item + +import ( + "gorm.io/gorm" + "vidhukant.com/openbills/errors" +) + +func (i *SavedItem) BeforeSave(tx *gorm.DB) error { + var err error + + err = checkIfBrandExists(i.BrandID) + if err != nil { + return err + } + + err = i.validate() + if err != nil { + return err + } + + return nil +} + +func (b *Brand) BeforeSave(tx *gorm.DB) error { + err := b.validate() + if err != nil { + return err + } + + return nil +} + +func (b *Brand) BeforeDelete(tx *gorm.DB) error { + // if ID is 0, brand won't be deleted + if b.ID == 0 { + return errors.ErrNoWhereCondition + } + + return nil +} diff --git a/item/item.go b/item/item.go new file mode 100644 index 0000000..4da9c78 --- /dev/null +++ b/item/item.go @@ -0,0 +1,52 @@ +/* openbills - Server for web based Libre Billing Software + * Copyright (C) 2023 Vidhu Kant Sharma <vidhukant@vidhukant.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +package item + +import ( + "gorm.io/gorm" + d "vidhukant.com/openbills/db" +) + +var db *gorm.DB +func init() { + db = d.DB + + db.AutoMigrate(&SavedItem{}, &Brand{}) +} + +type Brand struct { + gorm.Model + Name string +} + +type Item struct { + BrandID uint + Brand Brand + UnitOfMeasure string // TODO: probably has to be a custom type + HasDecimalQuantity bool + Name string + Description string + HSN string + UnitPrice string // float + GSTPercentage string // float +} + +type SavedItem struct { + gorm.Model + Item +} diff --git a/item/router.go b/item/router.go new file mode 100644 index 0000000..fab973f --- /dev/null +++ b/item/router.go @@ -0,0 +1,40 @@ +/* openbills - Server for web based Libre Billing Software + * Copyright (C) 2023 Vidhu Kant Sharma <vidhukant@vidhukant.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +package item + +import ( + "github.com/gin-gonic/gin" +) + +func Routes(route *gin.RouterGroup) { + b := route.Group("/brand") + { + b.GET("/", handleGetBrands) + b.GET("/:id/items", handleGetBrandItems) + b.POST("/", handleSaveBrand) + b.DELETE("/:id", handleDelBrand) + } + + i := route.Group("/item") + { + i.GET("/", handleGetItems) + //i.GET("/:id", handleGetBrandItems) + i.POST("/", handleSaveItem) + i.DELETE("/:id", handleDelItem) + } +} diff --git a/item/service.go b/item/service.go new file mode 100644 index 0000000..c8a72f6 --- /dev/null +++ b/item/service.go @@ -0,0 +1,126 @@ +/* openbills - Server for web based Libre Billing Software + * Copyright (C) 2023 Vidhu Kant Sharma <vidhukant@vidhukant.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +package item + +import ( + "vidhukant.com/openbills/errors" + e "vidhukant.com/openbills/errors" +) + +func getBrandItems(items *[]SavedItem, id uint) error { + // check if id is valid + var count int64 + err := db.Model(&Brand{}). + Select("id"). + Where("id = ?", id). + Count(&count). + Error + + if err != nil { + return err + } + + if count == 0 { + return errors.ErrBrandNotFound + } + + res := db.Model(&SavedItem{}).Where("brand_id = ?", id).Find(&items) + + // TODO: handle potential errors + if res.Error != nil { + return res.Error + } + + if res.RowsAffected == 0 { + return e.ErrEmptyResponse + } + + return nil +} + +func getBrands(brands *[]Brand) error { + res := db.Find(&brands) + + // TODO: handle potential errors + if res.Error != nil { + return res.Error + } + + if res.RowsAffected == 0 { + return e.ErrEmptyResponse + } + + return nil +} + +func (b *Brand) upsert() error { + res := db.Save(b) + // TODO: handle potential errors + return res.Error +} + +func (b *Brand) del() error { + res := db.Delete(b) + + // TODO: handle potential errors + if res.Error != nil { + return res.Error + } + + if res.RowsAffected == 0 { + return e.ErrNotFound + } + + return nil +} + +func getItems(items *[]SavedItem) error { + res := db.Preload("Brand").Find(&items) + + // TODO: handle potential errors + if res.Error != nil { + return res.Error + } + + if res.RowsAffected == 0 { + return e.ErrEmptyResponse + } + + return nil +} + +func (i *SavedItem) upsert() error { + res := db.Save(i) + // TODO: handle potential errors + return res.Error +} + +func (i *SavedItem) del() error { + res := db.Delete(i) + + // TODO: handle potential errors + if res.Error != nil { + return res.Error + } + + if res.RowsAffected == 0 { + return e.ErrNotFound + } + + return nil +} diff --git a/item/validators.go b/item/validators.go new file mode 100644 index 0000000..09162ab --- /dev/null +++ b/item/validators.go @@ -0,0 +1,111 @@ +/* openbills - Server for web based Libre Billing Software + * Copyright (C) 2023 Vidhu Kant Sharma <vidhukant@vidhukant.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +package item + +import ( + "strconv" + "strings" + + "vidhukant.com/openbills/errors" +) + +func (b *Brand) validate() error { + // trim whitespaces + b.Name = strings.TrimSpace(b.Name) + + if b.Name == "" { + return errors.ErrEmptyBrandName + } + + // make sure GSTIN is unique + var count int64 + err := db.Model(&Brand{}). + Select("name"). + Where("name = ?", b.Name). + Count(&count). + Error + + if err != nil { + return err + } + + if count > 0 { + return errors.ErrNonUniqueBrandName + } + + return nil +} + +func checkIfBrandExists(id uint) error { + // check if brand id is valid + var count int64 + err := db.Model(&Brand{}). + Select("id"). + Where("id = ?", id). + Count(&count). + Error + + if err != nil { + return err + } + + if count == 0 { + return errors.ErrBrandNotFound + } + + return nil +} + +func (i *SavedItem) validate() error { + // trim whitespaces + i.Name = strings.TrimSpace(i.Name) + i.Description = strings.TrimSpace(i.Description) + i.HSN = strings.TrimSpace(i.HSN) + + var err error + + // check if UnitPrice is float + _, err = strconv.ParseFloat(i.UnitPrice, 64) + if err != nil && strings.TrimSpace(i.UnitPrice) != "" { + return errors.ErrInvalidUnitPrice + } + + // check if GSTPercentage is float + _, err = strconv.ParseFloat(i.GSTPercentage, 64) + if err != nil && strings.TrimSpace(i.GSTPercentage) != "" { + return errors.ErrInvalidGSTPercentage + } + + // check if item with same name and brand already exists + var count int64 + err = db.Model(&SavedItem{}). + Select("name, brand_id"). + Where("brand_id = ? and name = ?", i.BrandID, i.Name). + Count(&count). + Error + + if err != nil { + return err + } + + if count != 0 { + return errors.ErrNonUniqueBrandItem + } + + return nil +} |