feat: update inventory API for status, category and harvest relations

This commit is contained in:
Natthapol SERMSARAN 2025-04-01 15:17:39 +07:00
parent b79f3f69f3
commit 022a6e4caa

View File

@ -48,32 +48,66 @@ func (a *api) registerInventoryRoutes(_ chi.Router, api huma.API) {
Path: prefix + "/{id}", Path: prefix + "/{id}",
Tags: tags, Tags: tags,
}, a.deleteInventoryItemHandler) }, a.deleteInventoryItemHandler)
huma.Register(api, huma.Operation{
OperationID: "getInventoryStatus",
Method: http.MethodGet,
Path: prefix + "/status",
Tags: tags,
}, a.getInventoryStatusHandler)
huma.Register(api, huma.Operation{
OperationID: "getInventoryCategory",
Method: http.MethodGet,
Path: prefix + "/category",
Tags: tags,
}, a.getInventoryCategoryHandler)
huma.Register(api, huma.Operation{
OperationID: "getHarvestUnits",
Method: http.MethodGet,
Path: "/harvest/units",
Tags: []string{"harvest"},
}, a.getHarvestUnitsHandler)
} }
type InventoryItemResponse struct { type InventoryItemResponse struct {
ID string `json:"id"` ID string `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Category string `json:"category"` Category InventoryCategory `json:"category"`
Type string `json:"type"` Quantity float64 `json:"quantity"`
Quantity float64 `json:"quantity"` Unit HarvestUnit `json:"unit"`
Unit string `json:"unit"` DateAdded time.Time `json:"date_added"`
DateAdded time.Time `json:"date_added"` Status InventoryStatus `json:"status"`
Status string `json:"status"` CreatedAt time.Time `json:"created_at,omitempty"`
CreatedAt time.Time `json:"created_at,omitempty"` UpdatedAt time.Time `json:"updated_at,omitempty"`
UpdatedAt time.Time `json:"updated_at,omitempty"` }
type InventoryStatus struct {
ID int `json:"id"`
Name string `json:"name"`
}
type InventoryCategory struct {
ID int `json:"id"`
Name string `json:"name"`
}
type HarvestUnit struct {
ID int `json:"id"`
Name string `json:"name"`
} }
type CreateInventoryItemInput struct { type CreateInventoryItemInput struct {
Header string `header:"Authorization" required:"true" example:"Bearer token"` Header string `header:"Authorization" required:"true" example:"Bearer token"`
UserID string `header:"user_id" required:"true" example:"user-uuid"` UserID string `header:"user_id" required:"true" example:"user-uuid"`
Body struct { Body struct {
Name string `json:"name" required:"true"` Name string `json:"name" required:"true"`
Category string `json:"category" required:"true"` CategoryID int `json:"category_id" required:"true"`
Type string `json:"type" required:"true"` Quantity float64 `json:"quantity" required:"true"`
Quantity float64 `json:"quantity" required:"true"` UnitID int `json:"unit_id" required:"true"`
Unit string `json:"unit" required:"true"` DateAdded time.Time `json:"date_added" required:"true"`
DateAdded time.Time `json:"date_added" required:"true"` StatusID int `json:"status_id" required:"true"`
Status string `json:"status" required:"true" enum:"In Stock,Low Stock,Out of Stock"`
} }
} }
@ -83,16 +117,83 @@ type CreateInventoryItemOutput struct {
} }
} }
type UpdateInventoryItemInput struct {
Header string `header:"Authorization" required:"true" example:"Bearer token"`
UserID string `header:"user_id" required:"true" example:"user-uuid"`
ID string `path:"id"`
Body struct {
Name string `json:"name"`
CategoryID int `json:"category_id"`
Quantity float64 `json:"quantity"`
UnitID int `json:"unit_id"`
DateAdded time.Time `json:"date_added"`
StatusID int `json:"status_id"`
}
}
type UpdateInventoryItemOutput struct {
Body InventoryItemResponse
}
type GetInventoryItemsInput struct {
Header string `header:"Authorization" required:"true" example:"Bearer token"`
UserID string `header:"user_id" required:"true" example:"user-uuid"`
CategoryID int `query:"category_id"`
StatusID int `query:"status_id"`
StartDate time.Time `query:"start_date" format:"date-time"`
EndDate time.Time `query:"end_date" format:"date-time"`
SearchQuery string `query:"search"`
SortBy string `query:"sort_by" enum:"name,quantity,date_added,created_at"`
SortOrder string `query:"sort_order" enum:"asc,desc" default:"desc"`
}
type GetInventoryItemsOutput struct {
Body []InventoryItemResponse
}
type GetInventoryItemInput struct {
Header string `header:"Authorization" required:"true" example:"Bearer token"`
UserID string `header:"user_id" required:"true" example:"user-uuid"`
ID string `path:"id"`
}
type GetInventoryItemOutput struct {
Body InventoryItemResponse
}
type DeleteInventoryItemInput struct {
Header string `header:"Authorization" required:"true" example:"Bearer token"`
UserID string `header:"user_id" required:"true" example:"user-uuid"`
ID string `path:"id"`
}
type DeleteInventoryItemOutput struct {
Body struct {
Message string `json:"message"`
}
}
type GetInventoryStatusOutput struct {
Body []InventoryStatus
}
type GetInventoryCategoryOutput struct {
Body []InventoryCategory
}
type GetHarvestUnitsOutput struct {
Body []HarvestUnit
}
func (a *api) createInventoryItemHandler(ctx context.Context, input *CreateInventoryItemInput) (*CreateInventoryItemOutput, error) { func (a *api) createInventoryItemHandler(ctx context.Context, input *CreateInventoryItemInput) (*CreateInventoryItemOutput, error) {
item := &domain.InventoryItem{ item := &domain.InventoryItem{
UserID: input.UserID, UserID: input.UserID,
Name: input.Body.Name, Name: input.Body.Name,
Category: input.Body.Category, CategoryID: input.Body.CategoryID,
Type: input.Body.Type, Quantity: input.Body.Quantity,
Quantity: input.Body.Quantity, UnitID: input.Body.UnitID,
Unit: input.Body.Unit, DateAdded: input.Body.DateAdded,
DateAdded: input.Body.DateAdded, StatusID: input.Body.StatusID,
Status: domain.InventoryStatus(input.Body.Status),
} }
if err := item.Validate(); err != nil { if err := item.Validate(); err != nil {
@ -109,29 +210,11 @@ func (a *api) createInventoryItemHandler(ctx context.Context, input *CreateInven
}{ID: item.ID}}, nil }{ID: item.ID}}, nil
} }
type GetInventoryItemsInput struct {
Header string `header:"Authorization" required:"true" example:"Bearer token"`
UserID string `header:"user_id" required:"true" example:"user-uuid"`
Category string `query:"category"`
Type string `query:"type"`
Status string `query:"status" enum:"In Stock,Low Stock,Out of Stock"`
StartDate time.Time `query:"start_date" format:"date-time"`
EndDate time.Time `query:"end_date" format:"date-time"`
SearchQuery string `query:"search"`
SortBy string `query:"sort_by" enum:"name,category,type,quantity,date_added,status,created_at"`
SortOrder string `query:"sort_order" enum:"asc,desc" default:"desc"`
}
type GetInventoryItemsOutput struct {
Body []InventoryItemResponse
}
func (a *api) getInventoryItemsByUserHandler(ctx context.Context, input *GetInventoryItemsInput) (*GetInventoryItemsOutput, error) { func (a *api) getInventoryItemsByUserHandler(ctx context.Context, input *GetInventoryItemsInput) (*GetInventoryItemsOutput, error) {
filter := domain.InventoryFilter{ filter := domain.InventoryFilter{
UserID: input.UserID, UserID: input.UserID,
Category: input.Category, CategoryID: input.CategoryID,
Type: input.Type, StatusID: input.StatusID,
Status: domain.InventoryStatus(input.Status),
StartDate: input.StartDate, StartDate: input.StartDate,
EndDate: input.EndDate, EndDate: input.EndDate,
SearchQuery: input.SearchQuery, SearchQuery: input.SearchQuery,
@ -150,14 +233,22 @@ func (a *api) getInventoryItemsByUserHandler(ctx context.Context, input *GetInve
response := make([]InventoryItemResponse, len(items)) response := make([]InventoryItemResponse, len(items))
for i, item := range items { for i, item := range items {
response[i] = InventoryItemResponse{ response[i] = InventoryItemResponse{
ID: item.ID, ID: item.ID,
Name: item.Name, Name: item.Name,
Category: item.Category, Category: InventoryCategory{
Type: item.Type, ID: item.Category.ID,
Quantity: item.Quantity, Name: item.Category.Name,
Unit: item.Unit, },
Quantity: item.Quantity,
Unit: HarvestUnit{
ID: item.Unit.ID,
Name: item.Unit.Name,
},
DateAdded: item.DateAdded, DateAdded: item.DateAdded,
Status: string(item.Status), Status: InventoryStatus{
ID: item.Status.ID,
Name: item.Status.Name,
},
CreatedAt: item.CreatedAt, CreatedAt: item.CreatedAt,
UpdatedAt: item.UpdatedAt, UpdatedAt: item.UpdatedAt,
} }
@ -166,16 +257,6 @@ func (a *api) getInventoryItemsByUserHandler(ctx context.Context, input *GetInve
return &GetInventoryItemsOutput{Body: response}, nil return &GetInventoryItemsOutput{Body: response}, nil
} }
type GetInventoryItemInput struct {
Header string `header:"Authorization" required:"true" example:"Bearer token"`
UserID string `header:"user_id" required:"true" example:"user-uuid"`
ID string `path:"id"`
}
type GetInventoryItemOutput struct {
Body InventoryItemResponse
}
func (a *api) getInventoryItemHandler(ctx context.Context, input *GetInventoryItemInput) (*GetInventoryItemOutput, error) { func (a *api) getInventoryItemHandler(ctx context.Context, input *GetInventoryItemInput) (*GetInventoryItemOutput, error) {
item, err := a.inventoryRepo.GetByID(ctx, input.ID, input.UserID) item, err := a.inventoryRepo.GetByID(ctx, input.ID, input.UserID)
if err != nil { if err != nil {
@ -183,38 +264,27 @@ func (a *api) getInventoryItemHandler(ctx context.Context, input *GetInventoryIt
} }
return &GetInventoryItemOutput{Body: InventoryItemResponse{ return &GetInventoryItemOutput{Body: InventoryItemResponse{
ID: item.ID, ID: item.ID,
Name: item.Name, Name: item.Name,
Category: item.Category, Category: InventoryCategory{
Type: item.Type, ID: item.Category.ID,
Quantity: item.Quantity, Name: item.Category.Name,
Unit: item.Unit, },
Quantity: item.Quantity,
Unit: HarvestUnit{
ID: item.Unit.ID,
Name: item.Unit.Name,
},
DateAdded: item.DateAdded, DateAdded: item.DateAdded,
Status: string(item.Status), Status: InventoryStatus{
ID: item.Status.ID,
Name: item.Status.Name,
},
CreatedAt: item.CreatedAt, CreatedAt: item.CreatedAt,
UpdatedAt: item.UpdatedAt, UpdatedAt: item.UpdatedAt,
}}, nil }}, nil
} }
type UpdateInventoryItemInput struct {
Header string `header:"Authorization" required:"true" example:"Bearer token"`
UserID string `header:"user_id" required:"true" example:"user-uuid"`
ID string `path:"id"`
Body struct {
Name string `json:"name"`
Category string `json:"category"`
Type string `json:"type"`
Quantity float64 `json:"quantity"`
Unit string `json:"unit"`
DateAdded time.Time `json:"date_added"`
Status string `json:"status" enum:"In Stock,Low Stock,Out of Stock"`
}
}
type UpdateInventoryItemOutput struct {
Body InventoryItemResponse
}
func (a *api) updateInventoryItemHandler(ctx context.Context, input *UpdateInventoryItemInput) (*UpdateInventoryItemOutput, error) { func (a *api) updateInventoryItemHandler(ctx context.Context, input *UpdateInventoryItemInput) (*UpdateInventoryItemOutput, error) {
item, err := a.inventoryRepo.GetByID(ctx, input.ID, input.UserID) item, err := a.inventoryRepo.GetByID(ctx, input.ID, input.UserID)
if err != nil { if err != nil {
@ -224,23 +294,20 @@ func (a *api) updateInventoryItemHandler(ctx context.Context, input *UpdateInven
if input.Body.Name != "" { if input.Body.Name != "" {
item.Name = input.Body.Name item.Name = input.Body.Name
} }
if input.Body.Category != "" { if input.Body.CategoryID != 0 {
item.Category = input.Body.Category item.CategoryID = input.Body.CategoryID
}
if input.Body.Type != "" {
item.Type = input.Body.Type
} }
if input.Body.Quantity != 0 { if input.Body.Quantity != 0 {
item.Quantity = input.Body.Quantity item.Quantity = input.Body.Quantity
} }
if input.Body.Unit != "" { if input.Body.UnitID != 0 {
item.Unit = input.Body.Unit item.UnitID = input.Body.UnitID
} }
if !input.Body.DateAdded.IsZero() { if !input.Body.DateAdded.IsZero() {
item.DateAdded = input.Body.DateAdded item.DateAdded = input.Body.DateAdded
} }
if input.Body.Status != "" { if input.Body.StatusID != 0 {
item.Status = domain.InventoryStatus(input.Body.Status) item.StatusID = input.Body.StatusID
} }
if err := item.Validate(); err != nil { if err := item.Validate(); err != nil {
@ -258,31 +325,27 @@ func (a *api) updateInventoryItemHandler(ctx context.Context, input *UpdateInven
} }
return &UpdateInventoryItemOutput{Body: InventoryItemResponse{ return &UpdateInventoryItemOutput{Body: InventoryItemResponse{
ID: updatedItem.ID, ID: updatedItem.ID,
Name: updatedItem.Name, Name: updatedItem.Name,
Category: updatedItem.Category, Category: InventoryCategory{
Type: updatedItem.Type, ID: updatedItem.Category.ID,
Quantity: updatedItem.Quantity, Name: updatedItem.Category.Name,
Unit: updatedItem.Unit, },
Quantity: updatedItem.Quantity,
Unit: HarvestUnit{
ID: updatedItem.Unit.ID,
Name: updatedItem.Unit.Name,
},
DateAdded: updatedItem.DateAdded, DateAdded: updatedItem.DateAdded,
Status: string(updatedItem.Status), Status: InventoryStatus{
ID: updatedItem.Status.ID,
Name: updatedItem.Status.Name,
},
CreatedAt: updatedItem.CreatedAt, CreatedAt: updatedItem.CreatedAt,
UpdatedAt: updatedItem.UpdatedAt, UpdatedAt: updatedItem.UpdatedAt,
}}, nil }}, nil
} }
type DeleteInventoryItemInput struct {
Header string `header:"Authorization" required:"true" example:"Bearer token"`
UserID string `header:"user_id" required:"true" example:"user-uuid"`
ID string `path:"id"`
}
type DeleteInventoryItemOutput struct {
Body struct {
Message string `json:"message"`
}
}
func (a *api) deleteInventoryItemHandler(ctx context.Context, input *DeleteInventoryItemInput) (*DeleteInventoryItemOutput, error) { func (a *api) deleteInventoryItemHandler(ctx context.Context, input *DeleteInventoryItemInput) (*DeleteInventoryItemOutput, error) {
err := a.inventoryRepo.Delete(ctx, input.ID, input.UserID) err := a.inventoryRepo.Delete(ctx, input.ID, input.UserID)
if err != nil { if err != nil {
@ -293,3 +356,54 @@ func (a *api) deleteInventoryItemHandler(ctx context.Context, input *DeleteInven
Message string `json:"message"` Message string `json:"message"`
}{Message: "Inventory item deleted successfully"}}, nil }{Message: "Inventory item deleted successfully"}}, nil
} }
func (a *api) getInventoryStatusHandler(ctx context.Context, input *struct{}) (*GetInventoryStatusOutput, error) {
statuses, err := a.inventoryRepo.GetStatuses(ctx)
if err != nil {
return nil, err
}
response := make([]InventoryStatus, len(statuses))
for i, status := range statuses {
response[i] = InventoryStatus{
ID: status.ID,
Name: status.Name,
}
}
return &GetInventoryStatusOutput{Body: response}, nil
}
func (a *api) getInventoryCategoryHandler(ctx context.Context, input *struct{}) (*GetInventoryCategoryOutput, error) {
categories, err := a.inventoryRepo.GetCategories(ctx)
if err != nil {
return nil, err
}
response := make([]InventoryCategory, len(categories))
for i, category := range categories {
response[i] = InventoryCategory{
ID: category.ID,
Name: category.Name,
}
}
return &GetInventoryCategoryOutput{Body: response}, nil
}
func (a *api) getHarvestUnitsHandler(ctx context.Context, input *struct{}) (*GetHarvestUnitsOutput, error) {
units, err := a.harvestRepo.GetUnits(ctx)
if err != nil {
return nil, err
}
response := make([]HarvestUnit, len(units))
for i, unit := range units {
response[i] = HarvestUnit{
ID: unit.ID,
Name: unit.Name,
}
}
return &GetHarvestUnitsOutput{Body: response}, nil
}