final project structure
This commit is contained in:
28
wagfarm-api/asset/asset.go
Normal file
28
wagfarm-api/asset/asset.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package asset
|
||||||
|
|
||||||
|
import (
|
||||||
|
"wagfarm-api/asset/fertilizer"
|
||||||
|
"wagfarm-api/asset/land"
|
||||||
|
"wagfarm-api/asset/machine"
|
||||||
|
"wagfarm-api/asset/pesticide"
|
||||||
|
"wagfarm-api/asset/plant"
|
||||||
|
"wagfarm-api/asset/product"
|
||||||
|
"wagfarm-api/asset/seed"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
const Models []interface{} {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterRoutes(rg *gin.RouterGroup) {
|
||||||
|
group := rg.Group("/asset")
|
||||||
|
fertilizer.RegisterRoutes(group)
|
||||||
|
land.RegisterRoutes(group)
|
||||||
|
machine.RegisterRoutes(group)
|
||||||
|
pesticide.RegisterRoutes(group)
|
||||||
|
plant.RegisterRoutes(group)
|
||||||
|
product.RegisterRoutes(group)
|
||||||
|
seed.RegisterRoutes(group)
|
||||||
|
}
|
||||||
30
wagfarm-api/asset/base/model.go
Normal file
30
wagfarm-api/asset/base/model.go
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package base
|
||||||
|
|
||||||
|
type Asset struct {
|
||||||
|
ID uint `gorm:"primaryKey`
|
||||||
|
Name string `gorm:"not null"`
|
||||||
|
Notes string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Asset) GetID() uint { return a.ID }
|
||||||
|
|
||||||
|
type Resource struct {
|
||||||
|
Asset
|
||||||
|
UnitID uint `gorm:"not null"`
|
||||||
|
Unit unit.Unit `gorm:"not null; constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
|
||||||
|
Cost decimal.Decimal `gorm:"not null; type:decimal(10,2);"` // in euro/unit
|
||||||
|
}
|
||||||
|
|
||||||
|
type BoughtResource struct {
|
||||||
|
Resource
|
||||||
|
BuyDate Datetime.Time `gorm:"type:date;default:CURRENT_DATE"`
|
||||||
|
BoughtFromID uint `gorm:"not null"`
|
||||||
|
BoughtFrom Seller `gorm:"not null; constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SoldResource struct {
|
||||||
|
Resource
|
||||||
|
SaleDate Datetime.Time `gorm:"type:date;default:CURRENT_DATE"`
|
||||||
|
SoldToID uint `gorm:"not null"`
|
||||||
|
SoldTo Customer `gorm:"not null; constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
|
||||||
|
}
|
||||||
20
wagfarm-api/asset/fertilizer/model.go
Normal file
20
wagfarm-api/asset/fertilizer/model.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package fertilizer
|
||||||
|
|
||||||
|
import "wagfarm-api/asset/base"
|
||||||
|
|
||||||
|
type Fertilizer struct {
|
||||||
|
base.BoughtResource
|
||||||
|
FertilizerTypeID uint `gorm:"not null"`
|
||||||
|
FertilizerType FertilizerType `gorm:"not null; constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
|
||||||
|
// Association to the ingredients. This represents the composition.
|
||||||
|
Ingredients []FertilizerIngredient `gorm:"not null; many2many:fertilizer_ingredient"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FertilizerIngredient struct {
|
||||||
|
FertilizerAssetID uint `gorm:"primaryKey"` // foreign key to the fertilizer asset
|
||||||
|
IngredientID uint `gorm:"primaryKey"` // foreign key to the ingredient
|
||||||
|
Amount float64 `gorm:"not null"` // amount of the ingredient
|
||||||
|
|
||||||
|
// Preload the ingredient details if needed.
|
||||||
|
Ingredient Ingredient `gorm:"not null"`
|
||||||
|
}
|
||||||
11
wagfarm-api/asset/fertilizer/route.go
Normal file
11
wagfarm-api/asset/fertilizer/route.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package fertilizer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"wagfarm-api/generic"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RegisterRoutes(rg *gin.RouterGroup) {
|
||||||
|
generic.RegisterCRUDRoutes(rg.Group("/fertilizer"), generic.DefaultCRUDController[Fertilizer]())
|
||||||
|
}
|
||||||
11
wagfarm-api/asset/land/model.go
Normal file
11
wagfarm-api/asset/land/model.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package land
|
||||||
|
|
||||||
|
import "wagfarm-api/asset/base"
|
||||||
|
|
||||||
|
type Land struct {
|
||||||
|
base.Asset
|
||||||
|
FID string `gorm:"not null"`
|
||||||
|
IsWaterProtectionArea bool `gorm:"not null"`
|
||||||
|
IsRedArea bool `gorm:"not null"`
|
||||||
|
Area float64 `gorm:"not null"`
|
||||||
|
}
|
||||||
11
wagfarm-api/asset/land/route.go
Normal file
11
wagfarm-api/asset/land/route.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package land
|
||||||
|
|
||||||
|
import (
|
||||||
|
"wagfarm-api/generic"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RegisterRoutes(rg *gin.RouterGroup) {
|
||||||
|
generic.RegisterCRUDRoutes(rg.Group("/land"), generic.DefaultCRUDController[Land]())
|
||||||
|
}
|
||||||
9
wagfarm-api/asset/machine/model.go
Normal file
9
wagfarm-api/asset/machine/model.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package machine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"wagfarm-api/asset/base"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Machine struct {
|
||||||
|
base.Resource
|
||||||
|
}
|
||||||
11
wagfarm-api/asset/machine/route.go
Normal file
11
wagfarm-api/asset/machine/route.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package machine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"wagfarm-api/generic"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RegisterRoutes(rg *gin.RouterGroup) {
|
||||||
|
generic.RegisterCRUDRoutes(rg.Group("/machine"), generic.DefaultCRUDController[Machine]())
|
||||||
|
}
|
||||||
9
wagfarm-api/asset/pesticide/model.go
Normal file
9
wagfarm-api/asset/pesticide/model.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package pesticide
|
||||||
|
|
||||||
|
import "wagfarm-api/asset/base"
|
||||||
|
|
||||||
|
type Pesticide struct {
|
||||||
|
base.BoughtResource
|
||||||
|
PesticideTypeID uint `gorm:"not null"`
|
||||||
|
PesticideType PesticideType `gorm:"not null; constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
|
||||||
|
}
|
||||||
11
wagfarm-api/asset/pesticide/route.go
Normal file
11
wagfarm-api/asset/pesticide/route.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package pesticide
|
||||||
|
|
||||||
|
import (
|
||||||
|
"wagfarm-api/generic"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RegisterRoutes(rg *gin.RouterGroup) {
|
||||||
|
generic.RegisterCRUDRoutes(rg.Group("/pesticide"), generic.DefaultCRUDController[Pesticide]())
|
||||||
|
}
|
||||||
18
wagfarm-api/asset/plant/model.go
Normal file
18
wagfarm-api/asset/plant/model.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package plant
|
||||||
|
|
||||||
|
import (
|
||||||
|
"wagfarm-api/asset/base"
|
||||||
|
"wagfarm-api/asset/land"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Plant struct {
|
||||||
|
base.Asset
|
||||||
|
LandId uint `gorm:"not null"`
|
||||||
|
Land land.Land `gorm:"not null; constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
|
||||||
|
CropID uint `gorm:"not null"`
|
||||||
|
Crop crop.Crop `gorm:"not null; constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
|
||||||
|
CoverCropID uint `gorm:"not null"`
|
||||||
|
CoverCrop crop.Crop `gorm:"not null; constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
|
||||||
|
Year uint `gorm:"not null"`
|
||||||
|
AreaPercentage float64 `gorm:"not null"`
|
||||||
|
}
|
||||||
11
wagfarm-api/asset/plant/route.go
Normal file
11
wagfarm-api/asset/plant/route.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package plant
|
||||||
|
|
||||||
|
import (
|
||||||
|
"wagfarm-api/generic"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RegisterRoutes(rg *gin.RouterGroup) {
|
||||||
|
generic.RegisterCRUDRoutes(rg.Group("/plant"), generic.DefaultCRUDController[Plant]())
|
||||||
|
}
|
||||||
9
wagfarm-api/asset/product/model.go
Normal file
9
wagfarm-api/asset/product/model.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package product
|
||||||
|
|
||||||
|
import "wagfarm-api/asset/base"
|
||||||
|
|
||||||
|
type Product struct {
|
||||||
|
base.SoldResource
|
||||||
|
ProductTypeID uint `gorm:"not null"`
|
||||||
|
ProductType ProductType `gorm:"not null; constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
|
||||||
|
}
|
||||||
11
wagfarm-api/asset/product/route.go
Normal file
11
wagfarm-api/asset/product/route.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package product
|
||||||
|
|
||||||
|
import (
|
||||||
|
"wagfarm-api/generic"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RegisterRoutes(rg *gin.RouterGroup) {
|
||||||
|
generic.RegisterCRUDRoutes(rg.Group("/product"), generic.DefaultCRUDController[Product]())
|
||||||
|
}
|
||||||
11
wagfarm-api/asset/seed/model.go
Normal file
11
wagfarm-api/asset/seed/model.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package seed
|
||||||
|
|
||||||
|
import (
|
||||||
|
"wagfarm-api/asset/base"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Seed struct {
|
||||||
|
base.BoughtResource
|
||||||
|
SeedTypeID uint `gorm:"not null"`
|
||||||
|
SeedType SeedType `gorm:"not null; constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
|
||||||
|
}
|
||||||
11
wagfarm-api/asset/seed/route.go
Normal file
11
wagfarm-api/asset/seed/route.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package seed
|
||||||
|
|
||||||
|
import (
|
||||||
|
"wagfarm-api/generic"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RegisterRoutes(rg *gin.RouterGroup) {
|
||||||
|
generic.RegisterCRUDRoutes(rg.Group("/seed"), generic.DefaultCRUDController[Seed]())
|
||||||
|
}
|
||||||
@@ -1,142 +0,0 @@
|
|||||||
package logs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
"wagfarm-api/database"
|
|
||||||
"wagfarm-api/models"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
type LogController interface {
|
|
||||||
GetAll(c *gin.Context)
|
|
||||||
GetOne(c *gin.Context)
|
|
||||||
Create(c *gin.Context)
|
|
||||||
Update(c *gin.Context)
|
|
||||||
Delete(c *gin.Context)
|
|
||||||
}
|
|
||||||
|
|
||||||
type GenericLogController struct{}
|
|
||||||
|
|
||||||
// GetLogs handles GET /logs and returns all log records.
|
|
||||||
func (l *GenericLogController) GetAll(c *gin.Context) {
|
|
||||||
var logs []models.Log
|
|
||||||
// Preload the Visibility association
|
|
||||||
if err := database.DB.Preload("Visibility").Find(&logs).Error; err != nil {
|
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Error fetching logs: " + err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.JSON(http.StatusOK, logs)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetLog handles GET /logs/:id and returns a single log record.
|
|
||||||
func (l *GenericLogController) GetOne(c *gin.Context) {
|
|
||||||
idParam := c.Param("id")
|
|
||||||
id, err := strconv.Atoi(idParam)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid log ID"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var logEntry models.Log
|
|
||||||
if err := database.DB.Preload("Visibility").First(&logEntry, id).Error; err != nil {
|
|
||||||
c.JSON(http.StatusNotFound, gin.H{"error": "Log not found"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.JSON(http.StatusOK, logEntry)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateLog handles POST /logs and creates a new log record.
|
|
||||||
func (l *GenericLogController) Create(c *gin.Context) {
|
|
||||||
var input models.Log
|
|
||||||
if err := c.ShouldBindJSON(&input); err != nil {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// For safety, you might want to assign defaults (for example, date) if not provided.
|
|
||||||
if input.Date.IsZero() {
|
|
||||||
input.Date = time.Now()
|
|
||||||
}
|
|
||||||
|
|
||||||
newLog := models.Log{
|
|
||||||
Name: input.Name,
|
|
||||||
Notes: input.Notes,
|
|
||||||
LogType: input.LogType,
|
|
||||||
Date: input.Date,
|
|
||||||
Duration: input.Duration,
|
|
||||||
VisibiltyID: input.VisibiltyID,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := database.DB.Create(&newLog).Error; err != nil {
|
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create log: " + err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optionally preload visibility before returning.
|
|
||||||
database.DB.Preload("Visibility").First(&newLog, newLog.ID)
|
|
||||||
c.JSON(http.StatusCreated, newLog)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateLog handles PUT /logs/:id and updates an existing log record.
|
|
||||||
func (l *GenericLogController) Update(c *gin.Context) {
|
|
||||||
idParam := c.Param("id")
|
|
||||||
id, err := strconv.Atoi(idParam)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid log ID"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var existing models.Log
|
|
||||||
if err := database.DB.First(&existing, id).Error; err != nil {
|
|
||||||
c.JSON(http.StatusNotFound, gin.H{"error": "Log not found"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var input models.Log
|
|
||||||
if err := c.ShouldBindJSON(&input); err != nil {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
updated := models.Log{
|
|
||||||
Name: input.Name,
|
|
||||||
Notes: input.Notes,
|
|
||||||
LogType: input.LogType,
|
|
||||||
Date: input.Date,
|
|
||||||
Duration: input.Duration,
|
|
||||||
VisibiltyID: input.VisibiltyID,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := database.DB.Model(&existing).Updates(updated).Error; err != nil {
|
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update log: " + err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
database.DB.Preload("Visibility").First(&existing, id)
|
|
||||||
c.JSON(http.StatusOK, existing)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteLog handles DELETE /logs/:id and deletes a log record.
|
|
||||||
func (l *GenericLogController) Delete(c *gin.Context) {
|
|
||||||
idParam := c.Param("id")
|
|
||||||
id, err := strconv.Atoi(idParam)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid log ID"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var logEntry models.Log
|
|
||||||
if err := database.DB.First(&logEntry, id).Error; err != nil {
|
|
||||||
c.JSON(http.StatusNotFound, gin.H{"error": "Log not found"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := database.DB.Delete(&logEntry).Error; err != nil {
|
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete log: " + err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.JSON(http.StatusOK, gin.H{"message": "Log deleted successfully"})
|
|
||||||
}
|
|
||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"gorm.io/driver/mysql"
|
"gorm.io/driver/mysql"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
"gorm.io/gorm/clause"
|
||||||
)
|
)
|
||||||
|
|
||||||
var DB *gorm.DB
|
var DB *gorm.DB
|
||||||
@@ -37,7 +38,7 @@ func Connect() error {
|
|||||||
func Migrate() error {
|
func Migrate() error {
|
||||||
var err error
|
var err error
|
||||||
err = DB.AutoMigrate(slices.Concat(
|
err = DB.AutoMigrate(slices.Concat(
|
||||||
models.LogModels, models.AssetModels, models.TaxonomyModels))
|
log.Models, asset.Models, taxonomy.Models))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("auto migration failed: %w", err)
|
return fmt.Errorf("auto migration failed: %w", err)
|
||||||
}
|
}
|
||||||
@@ -49,3 +50,7 @@ func Migrate() error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Preload() *gorm.DB {
|
||||||
|
return DB.Preload(clause.Associations)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,24 +1,46 @@
|
|||||||
package logs
|
package generic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
|
||||||
"wagfarm-api/database"
|
"wagfarm-api/database"
|
||||||
"wagfarm-api/models"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SeedingLogController struct {
|
type RController struct {
|
||||||
GenericLogController
|
GetAll func(*gin.Context)
|
||||||
|
GetOne func(*gin.Context)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DefaultRController[T Model]() RController {
|
||||||
|
return RController{
|
||||||
|
GetAll: GetAll[T],
|
||||||
|
GetOne: GetOne[T],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type CRUDController struct {
|
||||||
|
RController
|
||||||
|
Create func(*gin.Context)
|
||||||
|
Update func(*gin.Context)
|
||||||
|
Delete func(*gin.Context)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DefaultCRUDController[T Model]() CRUDController {
|
||||||
|
return CRUDController{
|
||||||
|
RController: DefaultRController[T](),
|
||||||
|
Create: Create[T],
|
||||||
|
Update: Update[T],
|
||||||
|
Delete: Delete[T],
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLogs handles GET /logs and returns all log records.
|
// GetLogs handles GET /logs and returns all log records.
|
||||||
func (l *SeedingLogController) GetAll(c *gin.Context) {
|
func GetAll[T Model](c *gin.Context) {
|
||||||
var logs []models.SeedingLog
|
var logs []T
|
||||||
// Preload the Visibility association
|
|
||||||
if err := database.DB.Preload("Log").Find(&logs).Error; err != nil {
|
if err := database.Preload().Find(&logs).Error; err != nil {
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Error fetching logs: " + err.Error()})
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Error fetching logs: " + err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -26,7 +48,7 @@ func (l *SeedingLogController) GetAll(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetLog handles GET /logs/:id and returns a single log record.
|
// GetLog handles GET /logs/:id and returns a single log record.
|
||||||
func (l *SeedingLogController) GetOne(c *gin.Context) {
|
func GetOne[T Model](c *gin.Context) {
|
||||||
idParam := c.Param("id")
|
idParam := c.Param("id")
|
||||||
id, err := strconv.Atoi(idParam)
|
id, err := strconv.Atoi(idParam)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -34,48 +56,33 @@ func (l *SeedingLogController) GetOne(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var logEntry models.Log
|
var log T
|
||||||
if err := database.DB.Preload("Visibility").First(&logEntry, id).Error; err != nil {
|
if err := database.Preload().First(&log, id).Error; err != nil {
|
||||||
c.JSON(http.StatusNotFound, gin.H{"error": "Log not found"})
|
c.JSON(http.StatusNotFound, gin.H{"error": "Log not found"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.JSON(http.StatusOK, logEntry)
|
c.JSON(http.StatusOK, log)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateLog handles POST /logs and creates a new log record.
|
// CreateLog handles POST /logs and creates a new log record.
|
||||||
func (l *SeedingLogController) Create(c *gin.Context) {
|
func Create[T Model](c *gin.Context) {
|
||||||
var input models.Log
|
var log T
|
||||||
if err := c.ShouldBindJSON(&input); err != nil {
|
if err := c.ShouldBindJSON(&log); err != nil {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if err := database.DB.Create(&log).Error; err != nil {
|
||||||
// For safety, you might want to assign defaults (for example, date) if not provided.
|
|
||||||
if input.Date.IsZero() {
|
|
||||||
input.Date = time.Now()
|
|
||||||
}
|
|
||||||
|
|
||||||
newLog := models.Log{
|
|
||||||
Name: input.Name,
|
|
||||||
Notes: input.Notes,
|
|
||||||
LogType: input.LogType,
|
|
||||||
Date: input.Date,
|
|
||||||
Duration: input.Duration,
|
|
||||||
VisibiltyID: input.VisibiltyID,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := database.DB.Create(&newLog).Error; err != nil {
|
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create log: " + err.Error()})
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create log: " + err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optionally preload visibility before returning.
|
// Optionally preload visibility before returning.
|
||||||
database.DB.Preload("Visibility").First(&newLog, newLog.ID)
|
database.Preload().First(&log, log.GetID())
|
||||||
c.JSON(http.StatusCreated, newLog)
|
c.JSON(http.StatusCreated, log)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateLog handles PUT /logs/:id and updates an existing log record.
|
// UpdateLog handles PUT /logs/:id and updates an existing log record.
|
||||||
func (l *SeedingLogController) Update(c *gin.Context) {
|
func Update[T Model](c *gin.Context) {
|
||||||
idParam := c.Param("id")
|
idParam := c.Param("id")
|
||||||
id, err := strconv.Atoi(idParam)
|
id, err := strconv.Atoi(idParam)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -83,38 +90,29 @@ func (l *SeedingLogController) Update(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var existing models.Log
|
var existing T
|
||||||
if err := database.DB.First(&existing, id).Error; err != nil {
|
if err := database.DB.First(&existing, id).Error; err != nil {
|
||||||
c.JSON(http.StatusNotFound, gin.H{"error": "Log not found"})
|
c.JSON(http.StatusNotFound, gin.H{"error": "Log not found"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var input models.Log
|
var log T
|
||||||
if err := c.ShouldBindJSON(&input); err != nil {
|
if err := c.ShouldBindJSON(&log); err != nil {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
updated := models.Log{
|
if err := database.DB.Model(&existing).Updates(log).Error; err != nil {
|
||||||
Name: input.Name,
|
|
||||||
Notes: input.Notes,
|
|
||||||
LogType: input.LogType,
|
|
||||||
Date: input.Date,
|
|
||||||
Duration: input.Duration,
|
|
||||||
VisibiltyID: input.VisibiltyID,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := database.DB.Model(&existing).Updates(updated).Error; err != nil {
|
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update log: " + err.Error()})
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update log: " + err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
database.DB.Preload("Visibility").First(&existing, id)
|
database.Preload().First(&existing, log.GetID())
|
||||||
c.JSON(http.StatusOK, existing)
|
c.JSON(http.StatusOK, existing)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteLog handles DELETE /logs/:id and deletes a log record.
|
// DeleteLog handles DELETE /logs/:id and deletes a log record.
|
||||||
func (l *SeedingLogController) Delete(c *gin.Context) {
|
func Delete[T Model](c *gin.Context) {
|
||||||
idParam := c.Param("id")
|
idParam := c.Param("id")
|
||||||
id, err := strconv.Atoi(idParam)
|
id, err := strconv.Atoi(idParam)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -122,13 +120,13 @@ func (l *SeedingLogController) Delete(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var logEntry models.Log
|
var log T
|
||||||
if err := database.DB.First(&logEntry, id).Error; err != nil {
|
if err := database.DB.First(&log, id).Error; err != nil {
|
||||||
c.JSON(http.StatusNotFound, gin.H{"error": "Log not found"})
|
c.JSON(http.StatusNotFound, gin.H{"error": "Log not found"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := database.DB.Delete(&logEntry).Error; err != nil {
|
if err := database.DB.Delete(&log).Error; err != nil {
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete log: " + err.Error()})
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete log: " + err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
5
wagfarm-api/generic/model.go
Normal file
5
wagfarm-api/generic/model.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package generic
|
||||||
|
|
||||||
|
type Model interface {
|
||||||
|
GetID() uint
|
||||||
|
}
|
||||||
16
wagfarm-api/generic/route.go
Normal file
16
wagfarm-api/generic/route.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package generic
|
||||||
|
|
||||||
|
import "github.com/gin-gonic/gin"
|
||||||
|
|
||||||
|
func RegisterCRUDRoutes(rg *gin.RouterGroup, l CRUDController) {
|
||||||
|
rg.GET("/", l.GetAll)
|
||||||
|
rg.GET("/:id", l.GetOne)
|
||||||
|
rg.POST("/", l.Create)
|
||||||
|
rg.PUT("/:id", l.Update)
|
||||||
|
rg.DELETE("/:id", l.Delete)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterRRoutes(rg *gin.RouterGroup, l RController) {
|
||||||
|
rg.GET("/", l.GetAll)
|
||||||
|
rg.GET("/:id", l.GetOne)
|
||||||
|
}
|
||||||
31
wagfarm-api/log/base/model.go
Normal file
31
wagfarm-api/log/base/model.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package base
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type BaseLog struct {
|
||||||
|
ID uint `gorm:"primaryKey"`
|
||||||
|
Name string `gorm:"not null"`
|
||||||
|
Notes string `gorm:"not null"`
|
||||||
|
LogType string `gorm:"not null"`
|
||||||
|
Date time.Time `gorm:"default:CURRENT_TIME"`
|
||||||
|
Duration float64 `gorm:"not null"` // in hours
|
||||||
|
VisibiltyID uint `gorm:"not null"`
|
||||||
|
Visibility taxonomy.Visibility `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;not null"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l BaseLog) GetID() uint { return l.ID }
|
||||||
|
|
||||||
|
type ExtendedLog struct {
|
||||||
|
ID uint `gorm:"primaryKey"`
|
||||||
|
LogID uint `gorm:"not null"`
|
||||||
|
BaseLog BaseLog `gorm:"foreignKey:LogID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;not null"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l ExtendedLog) GetID() uint { return l.ID }
|
||||||
|
|
||||||
|
type FieldWorkLog struct {
|
||||||
|
ExtendedLog
|
||||||
|
WorkedAreaPercentage float64 `gorm:"not null"`
|
||||||
|
MachineID uint `gorm:"not null"`
|
||||||
|
Machine asset.Machine `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;not null"`
|
||||||
|
}
|
||||||
11
wagfarm-api/log/base/route.go
Normal file
11
wagfarm-api/log/base/route.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package base
|
||||||
|
|
||||||
|
import (
|
||||||
|
"wagfarm-api/generic"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RegisterRoutes(rg *gin.RouterGroup) {
|
||||||
|
generic.RegisterCRUDRoutes(rg, generic.DefaultCRUDController[BaseLog]())
|
||||||
|
}
|
||||||
12
wagfarm-api/log/cropprotection/model.go
Normal file
12
wagfarm-api/log/cropprotection/model.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package cropprotection
|
||||||
|
|
||||||
|
import "wagfarm-api/log/base"
|
||||||
|
|
||||||
|
type CropProtectionLog struct {
|
||||||
|
base.FieldWorkLog
|
||||||
|
PesticideID uint `gorm:"not null"`
|
||||||
|
Pesticide taxonomy.Pesticide `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;not null"`
|
||||||
|
Amount float64 `gorm:"not null"` // in units of the pesticide
|
||||||
|
PerformedByID uint `gorm:"not null"`
|
||||||
|
PerformedBy taxonomy.Worker `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;not null"`
|
||||||
|
}
|
||||||
11
wagfarm-api/log/cropprotection/route.go
Normal file
11
wagfarm-api/log/cropprotection/route.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package cropprotection
|
||||||
|
|
||||||
|
import (
|
||||||
|
"wagfarm-api/generic"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RegisterRoutes(rg *gin.RouterGroup) {
|
||||||
|
generic.RegisterCRUDRoutes(rg.Group("/cropprotection"), generic.DefaultCRUDController[CropProtectionLog]())
|
||||||
|
}
|
||||||
12
wagfarm-api/log/fertilizing/model.go
Normal file
12
wagfarm-api/log/fertilizing/model.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package fertilizing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"wagfarm-api/log/base"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FertilizingLog struct {
|
||||||
|
base.ExtendedLog
|
||||||
|
FertilizerID uint `gorm:"not null"`
|
||||||
|
Fertilizer asset.Fertilizer `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;not null"`
|
||||||
|
Amount float64 `gorm:"not null"` // in units of the fertilizer
|
||||||
|
}
|
||||||
11
wagfarm-api/log/fertilizing/route.go
Normal file
11
wagfarm-api/log/fertilizing/route.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package fertilizing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"wagfarm-api/generic"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RegisterRoutes(rg *gin.RouterGroup) {
|
||||||
|
generic.RegisterCRUDRoutes(rg.Group("/fertilizer"), generic.DefaultCRUDController[FertilizingLog]())
|
||||||
|
}
|
||||||
9
wagfarm-api/log/harvest/model.go
Normal file
9
wagfarm-api/log/harvest/model.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package harvest
|
||||||
|
|
||||||
|
import base "wagfarm-api/log/seeding"
|
||||||
|
|
||||||
|
type HarvestLog struct {
|
||||||
|
base.FieldWorkLog
|
||||||
|
Product asset.Product `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;not null"`
|
||||||
|
Amount float64 `gorm:"not null"` // in units of the product
|
||||||
|
}
|
||||||
11
wagfarm-api/log/harvest/route.go
Normal file
11
wagfarm-api/log/harvest/route.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package harvest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"wagfarm-api/generic"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RegisterRoutes(rg *gin.RouterGroup) {
|
||||||
|
generic.RegisterCRUDRoutes(rg.Group("/harvest"), generic.DefaultCRUDController[HarvestLog]())
|
||||||
|
}
|
||||||
14
wagfarm-api/log/seeding/model.go
Normal file
14
wagfarm-api/log/seeding/model.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package seeding
|
||||||
|
|
||||||
|
import "wagfarm-api/log/base"
|
||||||
|
|
||||||
|
type SeedingLog struct {
|
||||||
|
base.FieldWorkLog
|
||||||
|
SeedID uint `gorm:"not null"`
|
||||||
|
Seed asset.Seed `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;not null"`
|
||||||
|
TechniqueID uint `gorm:"not null"`
|
||||||
|
Technique taxonomy.SeedingTechnique `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;not null"`
|
||||||
|
Amount float64 `gorm:"not null"` // in units/ha
|
||||||
|
ConditionsID uint `gorm:"not null"`
|
||||||
|
Conditions taxonomy.SeedingCondition `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;not null"`
|
||||||
|
}
|
||||||
11
wagfarm-api/log/seeding/route.go
Normal file
11
wagfarm-api/log/seeding/route.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package seeding
|
||||||
|
|
||||||
|
import (
|
||||||
|
"wagfarm-api/generic"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RegisterRoutes(rg *gin.RouterGroup) {
|
||||||
|
generic.RegisterCRUDRoutes(rg.Group("/seeding"), generic.DefaultCRUDController[SeedingLog]())
|
||||||
|
}
|
||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"wagfarm-api/database"
|
"wagfarm-api/database"
|
||||||
"wagfarm-api/routes"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
@@ -26,6 +25,10 @@ func initLogging() {
|
|||||||
gin.DefaultWriter = f // capture Gin logs too
|
gin.DefaultWriter = f // capture Gin logs too
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RegisterRoutes(r *gin.Engine) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
initLogging()
|
initLogging()
|
||||||
|
|
||||||
@@ -44,7 +47,7 @@ func main() {
|
|||||||
log.Println("Database migrated successfully!")
|
log.Println("Database migrated successfully!")
|
||||||
|
|
||||||
r := gin.Default()
|
r := gin.Default()
|
||||||
routes.RegisterRoutes(r)
|
RegisterRoutes(r)
|
||||||
|
|
||||||
if err := r.Run(":8080"); err != nil {
|
if err := r.Run(":8080"); err != nil {
|
||||||
log.Fatalf("Failed to run server: %v", err)
|
log.Fatalf("Failed to run server: %v", err)
|
||||||
|
|||||||
@@ -1,98 +0,0 @@
|
|||||||
package models
|
|
||||||
|
|
||||||
var AssetModels = []interface{}{
|
|
||||||
&Land{},
|
|
||||||
&Plant{},
|
|
||||||
&Machine{},
|
|
||||||
&Seed{},
|
|
||||||
&Fertilizer{},
|
|
||||||
&FertilizerIngredient{},
|
|
||||||
&Pesticide{},
|
|
||||||
&Product{},
|
|
||||||
}
|
|
||||||
|
|
||||||
type _Asset struct {
|
|
||||||
ID uint `gorm:"primaryKey`
|
|
||||||
Name string `gorm:"not null"`
|
|
||||||
Notes string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Land struct {
|
|
||||||
_Asset
|
|
||||||
FID string `gorm:"not null"`
|
|
||||||
IsWaterProtectionArea bool `gorm:"not null"`
|
|
||||||
IsRedArea bool `gorm:"not null"`
|
|
||||||
Area float64 `gorm:"not null"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Plant struct {
|
|
||||||
_Asset
|
|
||||||
LandId uint `gorm:"not null"`
|
|
||||||
Land Land `gorm:"not null; constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
|
|
||||||
CropID uint `gorm:"not null"`
|
|
||||||
Crop Crop `gorm:"not null; constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
|
|
||||||
CoverCropID uint `gorm:"not null"`
|
|
||||||
CoverCrop Crop `gorm:"not null; constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
|
|
||||||
Year uint `gorm:"not null"`
|
|
||||||
AreaPercentage float64 `gorm:"not null"`
|
|
||||||
}
|
|
||||||
type _Resource struct {
|
|
||||||
_Asset
|
|
||||||
UnitID uint `gorm:"not null"`
|
|
||||||
Unit Unit `gorm:"not null; constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
|
|
||||||
Cost decimal.Decimal `gorm:"not null; type:decimal(10,2);"` // in euro/unit
|
|
||||||
}
|
|
||||||
|
|
||||||
type Machine struct {
|
|
||||||
_Resource
|
|
||||||
}
|
|
||||||
|
|
||||||
type _BoughtResource struct {
|
|
||||||
_Resource
|
|
||||||
BuyDate Datetime.Time `gorm:"type:date;default:CURRENT_DATE"`
|
|
||||||
BoughtFromID uint `gorm:"not null"`
|
|
||||||
BoughtFrom Seller `gorm:"not null; constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type _SoldResource struct {
|
|
||||||
_Resource
|
|
||||||
SaleDate Datetime.Time `gorm:"type:date;default:CURRENT_DATE"`
|
|
||||||
SoldToID uint `gorm:"not null"`
|
|
||||||
SoldTo Customer `gorm:"not null; constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Seed struct {
|
|
||||||
_BoughtResource
|
|
||||||
SeedTypeID uint `gorm:"not null"`
|
|
||||||
SeedType SeedType `gorm:"not null; constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Fertilizer struct {
|
|
||||||
_Asset
|
|
||||||
_BoughtResource
|
|
||||||
FertilizerTypeID uint `gorm:"not null"`
|
|
||||||
FertilizerType FertilizerType `gorm:"not null; constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
|
|
||||||
// Association to the ingredients. This represents the composition.
|
|
||||||
Ingredients []FertilizerIngredient `gorm:"not null; many2many:fertilizer_ingredient"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type FertilizerIngredient struct {
|
|
||||||
FertilizerAssetID uint `gorm:"primaryKey"` // foreign key to the fertilizer asset
|
|
||||||
IngredientID uint `gorm:"primaryKey"` // foreign key to the ingredient
|
|
||||||
Amount float64 `gorm:"not null"` // amount of the ingredient
|
|
||||||
|
|
||||||
// Preload the ingredient details if needed.
|
|
||||||
Ingredient Ingredient `gorm:"not null"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Pesticide struct {
|
|
||||||
_BoughtResource
|
|
||||||
PesticideTypeID uint `gorm:"not null"`
|
|
||||||
PesticideType PesticideType `gorm:"not null; constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Product struct {
|
|
||||||
_SoldResource
|
|
||||||
ProductTypeID uint `gorm:"not null"`
|
|
||||||
ProductType ProductType `gorm:"not null; constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
|
|
||||||
}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
package models
|
|
||||||
|
|
||||||
var LogModels = []interface{}{
|
|
||||||
&Log{},
|
|
||||||
&SeedingLog{},
|
|
||||||
&FertilizingLog{},
|
|
||||||
&CropProtectionLog{},
|
|
||||||
&HarvestLog{},
|
|
||||||
}
|
|
||||||
|
|
||||||
type Log struct {
|
|
||||||
ID uint `gorm:"primaryKey"`
|
|
||||||
Name string `gorm:"not null"`
|
|
||||||
Notes string `gorm:"not null"`
|
|
||||||
LogType string `gorm:"not null"`
|
|
||||||
Date Datetime.Time `gorm:"type:date;default:CURRENT_DATE"not null`
|
|
||||||
Duration float64 `gorm:"not null"` // in hours
|
|
||||||
VisibiltyID uint `gorm:"not null"`
|
|
||||||
Visibility Visibility `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;not null"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type _LogMetadata struct {
|
|
||||||
ID uint `gorm:"primaryKey"`
|
|
||||||
LogID uint `gorm:"not null"`
|
|
||||||
Log Log `gorm:"foreignKey:LogID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;not null"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type _FieldWorkLog struct {
|
|
||||||
_LogMetadata
|
|
||||||
WorkedAreaPercentage float64 `gorm:"not null"`
|
|
||||||
MachineID uint `gorm:"not null"`
|
|
||||||
Machine Machine `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;not null"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type SeedingLog struct {
|
|
||||||
_FieldWorkLog
|
|
||||||
SeedID uint `gorm:"not null"`
|
|
||||||
Seed Seed `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;not null"`
|
|
||||||
TechniqueID uint `gorm:"not null"`
|
|
||||||
Technique SeedingTechnique `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;not null"`
|
|
||||||
Amount float64 `gorm:"not null"` // in units/ha
|
|
||||||
ConditionsID uint `gorm:"not null"`
|
|
||||||
Conditions SeedingCondition `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;not null"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type FertilizingLog struct {
|
|
||||||
_FieldWorkLog
|
|
||||||
FertilizerID uint `gorm:"not null"`
|
|
||||||
Fertilizer Fertilizer `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;not null"`
|
|
||||||
Amount float64 `gorm:"not null"` // in units of the fertilizer
|
|
||||||
}
|
|
||||||
|
|
||||||
type CropProtectionLog struct {
|
|
||||||
_FieldWorkLog
|
|
||||||
PesticideID uint `gorm:"not null"`
|
|
||||||
Pesticide Pesticide `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;not null"`
|
|
||||||
Amount float64 `gorm:"not null"` // in units of the pesticide
|
|
||||||
PerformedByID uint `gorm:"not null"`
|
|
||||||
PerformedBy Worker `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;not null"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type HarvestLog struct {
|
|
||||||
_FieldWorkLog
|
|
||||||
ProductID uint `gorm:"not null"`
|
|
||||||
Product Product `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;not null"`
|
|
||||||
Amount float64 `gorm:"not null"` // in units of the product
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
package models
|
|
||||||
|
|
||||||
var TaxonomyModels = []interface{}{
|
|
||||||
&Unit{},
|
|
||||||
&Visibility{},
|
|
||||||
&SeedingTechnique{},
|
|
||||||
&SeedingCondition{},
|
|
||||||
&Worker{},
|
|
||||||
&Crop{},
|
|
||||||
&Seller{},
|
|
||||||
&Customer{},
|
|
||||||
&SeedType{},
|
|
||||||
&Ingredient{},
|
|
||||||
&FertilizerType{},
|
|
||||||
&PesticideType{},
|
|
||||||
&ProductType{},
|
|
||||||
}
|
|
||||||
|
|
||||||
type _Taxonomy struct {
|
|
||||||
ID uint `gorm:"primaryKey" json:"id"`
|
|
||||||
Name string `gorm:"not null;index" json:"name"` // e.g., "kg", "lb"
|
|
||||||
}
|
|
||||||
|
|
||||||
type Unit struct{ _Taxonomy }
|
|
||||||
type Visibility struct{ _Taxonomy }
|
|
||||||
type SeedingTechnique struct{ _Taxonomy }
|
|
||||||
type SeedingCondition struct{ _Taxonomy }
|
|
||||||
type Worker struct{ _Taxonomy }
|
|
||||||
type Crop struct{ _Taxonomy }
|
|
||||||
type Seller struct{ _Taxonomy }
|
|
||||||
type Customer struct{ _Taxonomy }
|
|
||||||
type SeedType struct {
|
|
||||||
_Taxonomy
|
|
||||||
CropID uint `gorm:"not null"`
|
|
||||||
Crop Crop `gorm:"not null; constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
|
|
||||||
}
|
|
||||||
type Ingredient struct {
|
|
||||||
_Taxonomy
|
|
||||||
UnitID uint `gorm:"not null"`
|
|
||||||
Unit Unit `gorm:"not null"`
|
|
||||||
}
|
|
||||||
type FertilizerType struct{ _Taxonomy }
|
|
||||||
type PesticideType struct{ _Taxonomy }
|
|
||||||
type ProductType struct{ _Taxonomy }
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
package routes
|
|
||||||
|
|
||||||
import (
|
|
||||||
"wagfarm-api/controllers/logs"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
func RegisterLogRoutes(rg *gin.RouterGroup) {
|
|
||||||
group := rg.Group("/log")
|
|
||||||
{
|
|
||||||
RegisterLog(group, &logs.GenericLogController{})
|
|
||||||
RegisterLog(group.Group("/seeding"), &logs.SeedingLogController{})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func RegisterLog(rg *gin.RouterGroup controller logs.LogController) {
|
|
||||||
group := rg.Group(routePrefix)
|
|
||||||
{
|
|
||||||
group.GET("/", controller.GetAll)
|
|
||||||
group.GET("/:id", controller.GetOne)
|
|
||||||
group.POST("/", controller.Create)
|
|
||||||
group.PUT("/:id", controller.Update)
|
|
||||||
group.DELETE("/:id", controller.Delete)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package routes
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
func RegisterRoutes(r *gin.Engine) {
|
|
||||||
// Use API versioning if desired, e.g., v1
|
|
||||||
v1 := r.Group("/v1")
|
|
||||||
{
|
|
||||||
RegisterLogRoutes(v1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
8
wagfarm-api/taxonomy/base/model.go
Normal file
8
wagfarm-api/taxonomy/base/model.go
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package base
|
||||||
|
|
||||||
|
type Taxonomy struct {
|
||||||
|
ID uint `gorm:"primaryKey" json:"id"`
|
||||||
|
Name string `gorm:"not null;index" json:"name"` // e.g., "kg", "lb"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Taxonomy) GetID() uint { return t.ID }
|
||||||
5
wagfarm-api/taxonomy/crop/model.go
Normal file
5
wagfarm-api/taxonomy/crop/model.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package crop
|
||||||
|
|
||||||
|
import "wagfarm-api/taxonomy/base"
|
||||||
|
|
||||||
|
type Crop struct{ base.Taxonomy }
|
||||||
11
wagfarm-api/taxonomy/crop/route.go
Normal file
11
wagfarm-api/taxonomy/crop/route.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package crop
|
||||||
|
|
||||||
|
import (
|
||||||
|
"wagfarm-api/generic"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RegisterRoutes(rg *gin.RouterGroup) {
|
||||||
|
generic.RegisterCRUDRoutes(rg.Group("/crop"), generic.DefaultCRUDController[Crop]())
|
||||||
|
}
|
||||||
5
wagfarm-api/taxonomy/customer/model.go
Normal file
5
wagfarm-api/taxonomy/customer/model.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package customer
|
||||||
|
|
||||||
|
import "wagfarm-api/taxonomy/base"
|
||||||
|
|
||||||
|
type Customer struct{ base.Taxonomy }
|
||||||
11
wagfarm-api/taxonomy/customer/route.go
Normal file
11
wagfarm-api/taxonomy/customer/route.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package customer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"wagfarm-api/generic"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RegisterRoutes(rg *gin.RouterGroup) {
|
||||||
|
generic.RegisterCRUDRoutes(rg.Group("/customer"), generic.DefaultCRUDController[Customer]())
|
||||||
|
}
|
||||||
5
wagfarm-api/taxonomy/fertilizertype/model.go
Normal file
5
wagfarm-api/taxonomy/fertilizertype/model.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package fertilizertype
|
||||||
|
|
||||||
|
import "wagfarm-api/taxonomy/base"
|
||||||
|
|
||||||
|
type FertilizerType struct{ base.Taxonomy }
|
||||||
11
wagfarm-api/taxonomy/fertilizertype/route.go
Normal file
11
wagfarm-api/taxonomy/fertilizertype/route.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package fertilizertype
|
||||||
|
|
||||||
|
import (
|
||||||
|
"wagfarm-api/generic"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RegisterRoutes(rg *gin.RouterGroup) {
|
||||||
|
generic.RegisterCRUDRoutes(rg.Group("/fertilizertype"), generic.DefaultCRUDController[FertilizerType]())
|
||||||
|
}
|
||||||
12
wagfarm-api/taxonomy/ingredient/model.go
Normal file
12
wagfarm-api/taxonomy/ingredient/model.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package ingredient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"wagfarm-api/taxonomy/base"
|
||||||
|
"wagfarm-api/taxonomy/unit"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Ingredient struct {
|
||||||
|
base.Taxonomy
|
||||||
|
UnitID uint `gorm:"not null"`
|
||||||
|
Unit unit.Unit `gorm:"not null"`
|
||||||
|
}
|
||||||
11
wagfarm-api/taxonomy/ingredient/route.go
Normal file
11
wagfarm-api/taxonomy/ingredient/route.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package ingredient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"wagfarm-api/generic"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RegisterRoutes(rg *gin.RouterGroup) {
|
||||||
|
generic.RegisterCRUDRoutes(rg.Group("/ingredient"), generic.DefaultCRUDController[Ingredient]())
|
||||||
|
}
|
||||||
5
wagfarm-api/taxonomy/pesticidetype/model.go
Normal file
5
wagfarm-api/taxonomy/pesticidetype/model.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package pesticidetype
|
||||||
|
|
||||||
|
import "wagfarm-api/taxonomy/base"
|
||||||
|
|
||||||
|
type PesticideType struct{ base.Taxonomy }
|
||||||
11
wagfarm-api/taxonomy/pesticidetype/route.go
Normal file
11
wagfarm-api/taxonomy/pesticidetype/route.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package pesticidetype
|
||||||
|
|
||||||
|
import (
|
||||||
|
"wagfarm-api/generic"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RegisterRoutes(rg *gin.RouterGroup) {
|
||||||
|
generic.RegisterCRUDRoutes(rg.Group("/pesticidetype"), generic.DefaultCRUDController[PesticideType]())
|
||||||
|
}
|
||||||
5
wagfarm-api/taxonomy/producttype/model.go
Normal file
5
wagfarm-api/taxonomy/producttype/model.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package producttype
|
||||||
|
|
||||||
|
import "wagfarm-api/taxonomy/base"
|
||||||
|
|
||||||
|
type ProductType struct{ base.Taxonomy }
|
||||||
11
wagfarm-api/taxonomy/producttype/route.go
Normal file
11
wagfarm-api/taxonomy/producttype/route.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package producttype
|
||||||
|
|
||||||
|
import (
|
||||||
|
"wagfarm-api/generic"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RegisterRoutes(rg *gin.RouterGroup) {
|
||||||
|
generic.RegisterCRUDRoutes(rg.Group("/producttype"), generic.DefaultCRUDController[ProductType]())
|
||||||
|
}
|
||||||
5
wagfarm-api/taxonomy/seedingcondition/model.go
Normal file
5
wagfarm-api/taxonomy/seedingcondition/model.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package seedingcondition
|
||||||
|
|
||||||
|
import "wagfarm-api/taxonomy/base"
|
||||||
|
|
||||||
|
type SeedingCondition struct{ base.Taxonomy }
|
||||||
11
wagfarm-api/taxonomy/seedingcondition/route.go
Normal file
11
wagfarm-api/taxonomy/seedingcondition/route.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package seedingcondition
|
||||||
|
|
||||||
|
import (
|
||||||
|
"wagfarm-api/generic"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RegisterRoutes(rg *gin.RouterGroup) {
|
||||||
|
generic.RegisterCRUDRoutes(rg.Group("/seedingcondition"), generic.DefaultCRUDController[SeedingCondition]())
|
||||||
|
}
|
||||||
5
wagfarm-api/taxonomy/seedingtechnique/model.go
Normal file
5
wagfarm-api/taxonomy/seedingtechnique/model.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package seedingtechnique
|
||||||
|
|
||||||
|
import "wagfarm-api/taxonomy/base"
|
||||||
|
|
||||||
|
type SeedingTechnique struct{ base.Taxonomy }
|
||||||
11
wagfarm-api/taxonomy/seedingtechnique/route.go
Normal file
11
wagfarm-api/taxonomy/seedingtechnique/route.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package seedingtechnique
|
||||||
|
|
||||||
|
import (
|
||||||
|
"wagfarm-api/generic"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RegisterRoutes(rg *gin.RouterGroup) {
|
||||||
|
generic.RegisterCRUDRoutes(rg.Group("/seedingtechnique"), generic.DefaultCRUDController[SeedingTechnique]())
|
||||||
|
}
|
||||||
12
wagfarm-api/taxonomy/seedtype/model.go
Normal file
12
wagfarm-api/taxonomy/seedtype/model.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package seedtype
|
||||||
|
|
||||||
|
import (
|
||||||
|
"wagfarm-api/taxonomy/base"
|
||||||
|
"wagfarm-api/taxonomy/crop"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SeedType struct {
|
||||||
|
base.Taxonomy
|
||||||
|
CropID uint `gorm:"not null"`
|
||||||
|
Crop crop.Crop `gorm:"not null; constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
|
||||||
|
}
|
||||||
11
wagfarm-api/taxonomy/seedtype/route.go
Normal file
11
wagfarm-api/taxonomy/seedtype/route.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package seedtype
|
||||||
|
|
||||||
|
import (
|
||||||
|
"wagfarm-api/generic"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RegisterRoutes(rg *gin.RouterGroup) {
|
||||||
|
generic.RegisterCRUDRoutes(rg.Group("/seedtype"), generic.DefaultCRUDController[SeedType]())
|
||||||
|
}
|
||||||
5
wagfarm-api/taxonomy/seller/model.go
Normal file
5
wagfarm-api/taxonomy/seller/model.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package seller
|
||||||
|
|
||||||
|
import "wagfarm-api/taxonomy/base"
|
||||||
|
|
||||||
|
type Seller struct{ base.Taxonomy }
|
||||||
11
wagfarm-api/taxonomy/seller/route.go
Normal file
11
wagfarm-api/taxonomy/seller/route.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package seller
|
||||||
|
|
||||||
|
import (
|
||||||
|
"wagfarm-api/generic"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RegisterRoutes(rg *gin.RouterGroup) {
|
||||||
|
generic.RegisterCRUDRoutes(rg.Group("/seller"), generic.DefaultCRUDController[Seller]())
|
||||||
|
}
|
||||||
5
wagfarm-api/taxonomy/unit/model.go
Normal file
5
wagfarm-api/taxonomy/unit/model.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package unit
|
||||||
|
|
||||||
|
import "wagfarm-api/taxonomy/base"
|
||||||
|
|
||||||
|
type Unit struct{ base.Taxonomy }
|
||||||
11
wagfarm-api/taxonomy/unit/route.go
Normal file
11
wagfarm-api/taxonomy/unit/route.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package unit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"wagfarm-api/generic"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RegisterRoutes(rg *gin.RouterGroup) {
|
||||||
|
generic.RegisterCRUDRoutes(rg.Group("/unit"), generic.DefaultCRUDController[Unit]())
|
||||||
|
}
|
||||||
5
wagfarm-api/taxonomy/visibility/model.go
Normal file
5
wagfarm-api/taxonomy/visibility/model.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package visibility
|
||||||
|
|
||||||
|
import "wagfarm-api/taxonomy/base"
|
||||||
|
|
||||||
|
type Visibility struct{ base.Taxonomy }
|
||||||
11
wagfarm-api/taxonomy/visibility/route.go
Normal file
11
wagfarm-api/taxonomy/visibility/route.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package visibility
|
||||||
|
|
||||||
|
import (
|
||||||
|
"wagfarm-api/generic"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RegisterRoutes(rg *gin.RouterGroup) {
|
||||||
|
generic.RegisterCRUDRoutes(rg.Group("/visibility"), generic.DefaultCRUDController[Visibility]())
|
||||||
|
}
|
||||||
5
wagfarm-api/taxonomy/worker/model.go
Normal file
5
wagfarm-api/taxonomy/worker/model.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package worker
|
||||||
|
|
||||||
|
import "wagfarm-api/taxonomy/base"
|
||||||
|
|
||||||
|
type Worker struct{ base.Taxonomy }
|
||||||
11
wagfarm-api/taxonomy/worker/route.go
Normal file
11
wagfarm-api/taxonomy/worker/route.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package worker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"wagfarm-api/generic"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RegisterRoutes(rg *gin.RouterGroup) {
|
||||||
|
generic.RegisterCRUDRoutes(rg.Group("/worker"), generic.DefaultCRUDController[Worker]())
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user