Update purchase for product category (inventory type)
This commit is contained in:
parent
e7dd9660da
commit
e09feff36d
@ -372,7 +372,7 @@ func (a *App) initProcessors(cfg *config.Config, repos *repositories) *processor
|
|||||||
ingredientProcessor: processor.NewIngredientProcessor(repos.ingredientRepo, repos.unitRepo, repos.ingredientCompositionRepo),
|
ingredientProcessor: processor.NewIngredientProcessor(repos.ingredientRepo, repos.unitRepo, repos.ingredientCompositionRepo),
|
||||||
productRecipeProcessor: processor.NewProductRecipeProcessor(repos.productRecipeRepo, repos.productRepo, repos.ingredientRepo),
|
productRecipeProcessor: processor.NewProductRecipeProcessor(repos.productRecipeRepo, repos.productRepo, repos.ingredientRepo),
|
||||||
vendorProcessor: processor.NewVendorProcessorImpl(repos.vendorRepo),
|
vendorProcessor: processor.NewVendorProcessorImpl(repos.vendorRepo),
|
||||||
purchaseOrderProcessor: processor.NewPurchaseOrderProcessorImpl(repos.purchaseOrderRepo, repos.vendorRepo, repos.ingredientRepo, repos.unitRepo, repos.fileRepo, inventoryMovementService, repos.unitConverterRepo),
|
purchaseOrderProcessor: processor.NewPurchaseOrderProcessorImpl(repos.purchaseOrderRepo, repos.vendorRepo, repos.ingredientRepo, repos.purchaseCategoryRepo, repos.unitRepo, repos.fileRepo, inventoryMovementService, repos.unitConverterRepo),
|
||||||
purchaseCategoryProcessor: processor.NewPurchaseCategoryProcessorImpl(repos.purchaseCategoryRepo),
|
purchaseCategoryProcessor: processor.NewPurchaseCategoryProcessorImpl(repos.purchaseCategoryRepo),
|
||||||
unitConverterProcessor: processor.NewIngredientUnitConverterProcessorImpl(repos.unitConverterRepo, repos.ingredientRepo, repos.unitRepo),
|
unitConverterProcessor: processor.NewIngredientUnitConverterProcessorImpl(repos.unitConverterRepo, repos.ingredientRepo, repos.unitRepo),
|
||||||
chartOfAccountTypeProcessor: processor.NewChartOfAccountTypeProcessorImpl(repos.chartOfAccountTypeRepo),
|
chartOfAccountTypeProcessor: processor.NewChartOfAccountTypeProcessorImpl(repos.chartOfAccountTypeRepo),
|
||||||
|
|||||||
@ -19,11 +19,12 @@ type CreatePurchaseOrderRequest struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type CreatePurchaseOrderItemRequest struct {
|
type CreatePurchaseOrderItemRequest struct {
|
||||||
IngredientID uuid.UUID `json:"ingredient_id" validate:"required"`
|
IngredientID uuid.UUID `json:"ingredient_id" validate:"required"`
|
||||||
Description *string `json:"description,omitempty" validate:"omitempty"`
|
PurchaseCategoryID uuid.UUID `json:"purchase_category_id" validate:"required"`
|
||||||
Quantity float64 `json:"quantity" validate:"required,gt=0"`
|
Description *string `json:"description,omitempty" validate:"omitempty"`
|
||||||
UnitID uuid.UUID `json:"unit_id" validate:"required"`
|
Quantity float64 `json:"quantity" validate:"required,gt=0"`
|
||||||
Amount float64 `json:"amount" validate:"required,gte=0"`
|
UnitID uuid.UUID `json:"unit_id" validate:"required"`
|
||||||
|
Amount float64 `json:"amount" validate:"required,gte=0"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdatePurchaseOrderRequest struct {
|
type UpdatePurchaseOrderRequest struct {
|
||||||
@ -39,12 +40,13 @@ type UpdatePurchaseOrderRequest struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type UpdatePurchaseOrderItemRequest struct {
|
type UpdatePurchaseOrderItemRequest struct {
|
||||||
ID *uuid.UUID `json:"id,omitempty"` // For existing items
|
ID *uuid.UUID `json:"id,omitempty"` // For existing items
|
||||||
IngredientID *uuid.UUID `json:"ingredient_id,omitempty" validate:"omitempty"`
|
IngredientID *uuid.UUID `json:"ingredient_id,omitempty" validate:"omitempty"`
|
||||||
Description *string `json:"description,omitempty" validate:"omitempty"`
|
PurchaseCategoryID *uuid.UUID `json:"purchase_category_id,omitempty" validate:"omitempty"`
|
||||||
Quantity *float64 `json:"quantity,omitempty" validate:"omitempty,gt=0"`
|
Description *string `json:"description,omitempty" validate:"omitempty"`
|
||||||
UnitID *uuid.UUID `json:"unit_id,omitempty" validate:"omitempty"`
|
Quantity *float64 `json:"quantity,omitempty" validate:"omitempty,gt=0"`
|
||||||
Amount *float64 `json:"amount,omitempty" validate:"omitempty,gte=0"`
|
UnitID *uuid.UUID `json:"unit_id,omitempty" validate:"omitempty"`
|
||||||
|
Amount *float64 `json:"amount,omitempty" validate:"omitempty,gte=0"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PurchaseOrderResponse struct {
|
type PurchaseOrderResponse struct {
|
||||||
@ -66,17 +68,19 @@ type PurchaseOrderResponse struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type PurchaseOrderItemResponse struct {
|
type PurchaseOrderItemResponse struct {
|
||||||
ID uuid.UUID `json:"id"`
|
ID uuid.UUID `json:"id"`
|
||||||
PurchaseOrderID uuid.UUID `json:"purchase_order_id"`
|
PurchaseOrderID uuid.UUID `json:"purchase_order_id"`
|
||||||
IngredientID uuid.UUID `json:"ingredient_id"`
|
IngredientID uuid.UUID `json:"ingredient_id"`
|
||||||
Description *string `json:"description"`
|
PurchaseCategoryID uuid.UUID `json:"purchase_category_id"`
|
||||||
Quantity float64 `json:"quantity"`
|
Description *string `json:"description"`
|
||||||
UnitID uuid.UUID `json:"unit_id"`
|
Quantity float64 `json:"quantity"`
|
||||||
Amount float64 `json:"amount"`
|
UnitID uuid.UUID `json:"unit_id"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
Amount float64 `json:"amount"`
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
Ingredient *IngredientResponse `json:"ingredient,omitempty"`
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
Unit *UnitResponse `json:"unit,omitempty"`
|
Ingredient *IngredientResponse `json:"ingredient,omitempty"`
|
||||||
|
PurchaseCategory *PurchaseCategoryResponse `json:"purchase_category,omitempty"`
|
||||||
|
Unit *UnitResponse `json:"unit,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PurchaseOrderAttachmentResponse struct {
|
type PurchaseOrderAttachmentResponse struct {
|
||||||
|
|||||||
@ -36,34 +36,36 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type InventoryMovement struct {
|
type InventoryMovement struct {
|
||||||
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
|
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
|
||||||
OrganizationID uuid.UUID `gorm:"type:uuid;not null;index" json:"organization_id" validate:"required"`
|
OrganizationID uuid.UUID `gorm:"type:uuid;not null;index" json:"organization_id" validate:"required"`
|
||||||
OutletID uuid.UUID `gorm:"type:uuid;not null;index" json:"outlet_id" validate:"required"`
|
OutletID uuid.UUID `gorm:"type:uuid;not null;index" json:"outlet_id" validate:"required"`
|
||||||
ItemID uuid.UUID `gorm:"type:uuid;not null;index" json:"item_id" validate:"required"`
|
ItemID uuid.UUID `gorm:"type:uuid;not null;index" json:"item_id" validate:"required"`
|
||||||
ItemType string `gorm:"not null;size:20" json:"item_type" validate:"required"` // "PRODUCT" or "INGREDIENT"
|
ItemType string `gorm:"not null;size:20" json:"item_type" validate:"required"` // "PRODUCT" or "INGREDIENT"
|
||||||
MovementType InventoryMovementType `gorm:"not null;size:50" json:"movement_type" validate:"required"`
|
MovementType InventoryMovementType `gorm:"not null;size:50" json:"movement_type" validate:"required"`
|
||||||
Quantity float64 `gorm:"type:decimal(12,3);not null" json:"quantity" validate:"required"`
|
Quantity float64 `gorm:"type:decimal(12,3);not null" json:"quantity" validate:"required"`
|
||||||
PreviousQuantity float64 `gorm:"type:decimal(12,3)" json:"previous_quantity"`
|
PreviousQuantity float64 `gorm:"type:decimal(12,3)" json:"previous_quantity"`
|
||||||
NewQuantity float64 `gorm:"type:decimal(12,3)" json:"new_quantity"`
|
NewQuantity float64 `gorm:"type:decimal(12,3)" json:"new_quantity"`
|
||||||
UnitCost float64 `gorm:"type:decimal(12,2);default:0.00" json:"unit_cost"`
|
UnitCost float64 `gorm:"type:decimal(12,2);default:0.00" json:"unit_cost"`
|
||||||
TotalCost float64 `gorm:"type:decimal(12,2);default:0.00" json:"total_cost"`
|
TotalCost float64 `gorm:"type:decimal(12,2);default:0.00" json:"total_cost"`
|
||||||
ReferenceType *InventoryMovementReferenceType `gorm:"size:50" json:"reference_type"`
|
ReferenceType *InventoryMovementReferenceType `gorm:"size:50" json:"reference_type"`
|
||||||
ReferenceID *uuid.UUID `gorm:"type:uuid;index" json:"reference_id"`
|
ReferenceID *uuid.UUID `gorm:"type:uuid;index" json:"reference_id"`
|
||||||
OrderID *uuid.UUID `gorm:"type:uuid;index" json:"order_id"`
|
PurchaseOrderItemID *uuid.UUID `gorm:"type:uuid;index" json:"purchase_order_item_id"`
|
||||||
PaymentID *uuid.UUID `gorm:"type:uuid;index" json:"payment_id"`
|
OrderID *uuid.UUID `gorm:"type:uuid;index" json:"order_id"`
|
||||||
UserID uuid.UUID `gorm:"type:uuid;not null;index" json:"user_id" validate:"required"`
|
PaymentID *uuid.UUID `gorm:"type:uuid;index" json:"payment_id"`
|
||||||
Reason *string `gorm:"size:255" json:"reason"`
|
UserID uuid.UUID `gorm:"type:uuid;not null;index" json:"user_id" validate:"required"`
|
||||||
Notes *string `gorm:"type:text" json:"notes"`
|
Reason *string `gorm:"size:255" json:"reason"`
|
||||||
Metadata Metadata `gorm:"type:jsonb;default:'{}'" json:"metadata"`
|
Notes *string `gorm:"type:text" json:"notes"`
|
||||||
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
Metadata Metadata `gorm:"type:jsonb;default:'{}'" json:"metadata"`
|
||||||
|
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
||||||
|
|
||||||
Organization Organization `gorm:"foreignKey:OrganizationID" json:"organization,omitempty"`
|
Organization Organization `gorm:"foreignKey:OrganizationID" json:"organization,omitempty"`
|
||||||
Outlet Outlet `gorm:"foreignKey:OutletID" json:"outlet,omitempty"`
|
Outlet Outlet `gorm:"foreignKey:OutletID" json:"outlet,omitempty"`
|
||||||
Product *Product `gorm:"foreignKey:ItemID" json:"product,omitempty"`
|
Product *Product `gorm:"foreignKey:ItemID" json:"product,omitempty"`
|
||||||
Ingredient *Ingredient `gorm:"foreignKey:ItemID" json:"ingredient,omitempty"`
|
Ingredient *Ingredient `gorm:"foreignKey:ItemID" json:"ingredient,omitempty"`
|
||||||
Order *Order `gorm:"foreignKey:OrderID" json:"order,omitempty"`
|
PurchaseOrderItem *PurchaseOrderItem `gorm:"foreignKey:PurchaseOrderItemID" json:"purchase_order_item,omitempty"`
|
||||||
Payment *Payment `gorm:"foreignKey:PaymentID" json:"payment,omitempty"`
|
Order *Order `gorm:"foreignKey:OrderID" json:"order,omitempty"`
|
||||||
User User `gorm:"foreignKey:UserID" json:"user,omitempty"`
|
Payment *Payment `gorm:"foreignKey:PaymentID" json:"payment,omitempty"`
|
||||||
|
User User `gorm:"foreignKey:UserID" json:"user,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (im *InventoryMovement) BeforeCreate(tx *gorm.DB) error {
|
func (im *InventoryMovement) BeforeCreate(tx *gorm.DB) error {
|
||||||
|
|||||||
@ -41,19 +41,21 @@ func (PurchaseOrder) TableName() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type PurchaseOrderItem struct {
|
type PurchaseOrderItem struct {
|
||||||
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
|
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
|
||||||
PurchaseOrderID uuid.UUID `gorm:"type:uuid;not null" json:"purchase_order_id" validate:"required"`
|
PurchaseOrderID uuid.UUID `gorm:"type:uuid;not null" json:"purchase_order_id" validate:"required"`
|
||||||
IngredientID uuid.UUID `gorm:"type:uuid;not null" json:"ingredient_id" validate:"required"`
|
IngredientID uuid.UUID `gorm:"type:uuid;not null" json:"ingredient_id" validate:"required"`
|
||||||
Description *string `gorm:"type:text" json:"description" validate:"omitempty"`
|
PurchaseCategoryID uuid.UUID `gorm:"type:uuid;not null;index" json:"purchase_category_id" validate:"required"`
|
||||||
Quantity float64 `gorm:"type:decimal(10,3);not null" json:"quantity" validate:"required,gt=0"`
|
Description *string `gorm:"type:text" json:"description" validate:"omitempty"`
|
||||||
UnitID uuid.UUID `gorm:"type:uuid;not null" json:"unit_id" validate:"required"`
|
Quantity float64 `gorm:"type:decimal(10,3);not null" json:"quantity" validate:"required,gt=0"`
|
||||||
Amount float64 `gorm:"type:decimal(15,2);not null" json:"amount" validate:"required,gte=0"`
|
UnitID uuid.UUID `gorm:"type:uuid;not null" json:"unit_id" validate:"required"`
|
||||||
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
Amount float64 `gorm:"type:decimal(15,2);not null" json:"amount" validate:"required,gte=0"`
|
||||||
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
|
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
||||||
|
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
|
||||||
|
|
||||||
PurchaseOrder *PurchaseOrder `gorm:"foreignKey:PurchaseOrderID" json:"purchase_order,omitempty"`
|
PurchaseOrder *PurchaseOrder `gorm:"foreignKey:PurchaseOrderID" json:"purchase_order,omitempty"`
|
||||||
Ingredient *Ingredient `gorm:"foreignKey:IngredientID" json:"ingredient,omitempty"`
|
Ingredient *Ingredient `gorm:"foreignKey:IngredientID" json:"ingredient,omitempty"`
|
||||||
Unit *Unit `gorm:"foreignKey:UnitID" json:"unit,omitempty"`
|
PurchaseCategory *PurchaseCategory `gorm:"foreignKey:PurchaseCategoryID" json:"purchase_category,omitempty"`
|
||||||
|
Unit *Unit `gorm:"foreignKey:UnitID" json:"unit,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (poi *PurchaseOrderItem) BeforeCreate(tx *gorm.DB) error {
|
func (poi *PurchaseOrderItem) BeforeCreate(tx *gorm.DB) error {
|
||||||
|
|||||||
@ -91,15 +91,16 @@ func PurchaseOrderItemEntityToModel(entity *entities.PurchaseOrderItem) *models.
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &models.PurchaseOrderItem{
|
return &models.PurchaseOrderItem{
|
||||||
ID: entity.ID,
|
ID: entity.ID,
|
||||||
PurchaseOrderID: entity.PurchaseOrderID,
|
PurchaseOrderID: entity.PurchaseOrderID,
|
||||||
IngredientID: entity.IngredientID,
|
IngredientID: entity.IngredientID,
|
||||||
Description: entity.Description,
|
PurchaseCategoryID: entity.PurchaseCategoryID,
|
||||||
Quantity: entity.Quantity,
|
Description: entity.Description,
|
||||||
UnitID: entity.UnitID,
|
Quantity: entity.Quantity,
|
||||||
Amount: entity.Amount,
|
UnitID: entity.UnitID,
|
||||||
CreatedAt: entity.CreatedAt,
|
Amount: entity.Amount,
|
||||||
UpdatedAt: entity.UpdatedAt,
|
CreatedAt: entity.CreatedAt,
|
||||||
|
UpdatedAt: entity.UpdatedAt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,15 +110,16 @@ func PurchaseOrderItemModelToEntity(model *models.PurchaseOrderItem) *entities.P
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &entities.PurchaseOrderItem{
|
return &entities.PurchaseOrderItem{
|
||||||
ID: model.ID,
|
ID: model.ID,
|
||||||
PurchaseOrderID: model.PurchaseOrderID,
|
PurchaseOrderID: model.PurchaseOrderID,
|
||||||
IngredientID: model.IngredientID,
|
IngredientID: model.IngredientID,
|
||||||
Description: model.Description,
|
PurchaseCategoryID: model.PurchaseCategoryID,
|
||||||
Quantity: model.Quantity,
|
Description: model.Description,
|
||||||
UnitID: model.UnitID,
|
Quantity: model.Quantity,
|
||||||
Amount: model.Amount,
|
UnitID: model.UnitID,
|
||||||
CreatedAt: model.CreatedAt,
|
Amount: model.Amount,
|
||||||
UpdatedAt: model.UpdatedAt,
|
CreatedAt: model.CreatedAt,
|
||||||
|
UpdatedAt: model.UpdatedAt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,15 +129,16 @@ func PurchaseOrderItemEntityToResponse(entity *entities.PurchaseOrderItem) *mode
|
|||||||
}
|
}
|
||||||
|
|
||||||
response := &models.PurchaseOrderItemResponse{
|
response := &models.PurchaseOrderItemResponse{
|
||||||
ID: entity.ID,
|
ID: entity.ID,
|
||||||
PurchaseOrderID: entity.PurchaseOrderID,
|
PurchaseOrderID: entity.PurchaseOrderID,
|
||||||
IngredientID: entity.IngredientID,
|
IngredientID: entity.IngredientID,
|
||||||
Description: entity.Description,
|
PurchaseCategoryID: entity.PurchaseCategoryID,
|
||||||
Quantity: entity.Quantity,
|
Description: entity.Description,
|
||||||
UnitID: entity.UnitID,
|
Quantity: entity.Quantity,
|
||||||
Amount: entity.Amount,
|
UnitID: entity.UnitID,
|
||||||
CreatedAt: entity.CreatedAt,
|
Amount: entity.Amount,
|
||||||
UpdatedAt: entity.UpdatedAt,
|
CreatedAt: entity.CreatedAt,
|
||||||
|
UpdatedAt: entity.UpdatedAt,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map ingredient if present
|
// Map ingredient if present
|
||||||
@ -146,6 +149,10 @@ func PurchaseOrderItemEntityToResponse(entity *entities.PurchaseOrderItem) *mode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if entity.PurchaseCategory != nil {
|
||||||
|
response.PurchaseCategory = PurchaseCategoryEntityToResponse(entity.PurchaseCategory)
|
||||||
|
}
|
||||||
|
|
||||||
// Map unit if present
|
// Map unit if present
|
||||||
if entity.Unit != nil {
|
if entity.Unit != nil {
|
||||||
response.Unit = &models.UnitResponse{
|
response.Unit = &models.UnitResponse{
|
||||||
|
|||||||
@ -22,15 +22,16 @@ type PurchaseOrder struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type PurchaseOrderItem struct {
|
type PurchaseOrderItem struct {
|
||||||
ID uuid.UUID `json:"id"`
|
ID uuid.UUID `json:"id"`
|
||||||
PurchaseOrderID uuid.UUID `json:"purchase_order_id"`
|
PurchaseOrderID uuid.UUID `json:"purchase_order_id"`
|
||||||
IngredientID uuid.UUID `json:"ingredient_id"`
|
IngredientID uuid.UUID `json:"ingredient_id"`
|
||||||
Description *string `json:"description"`
|
PurchaseCategoryID uuid.UUID `json:"purchase_category_id"`
|
||||||
Quantity float64 `json:"quantity"`
|
Description *string `json:"description"`
|
||||||
UnitID uuid.UUID `json:"unit_id"`
|
Quantity float64 `json:"quantity"`
|
||||||
Amount float64 `json:"amount"`
|
UnitID uuid.UUID `json:"unit_id"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
Amount float64 `json:"amount"`
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PurchaseOrderAttachment struct {
|
type PurchaseOrderAttachment struct {
|
||||||
@ -59,17 +60,19 @@ type PurchaseOrderResponse struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type PurchaseOrderItemResponse struct {
|
type PurchaseOrderItemResponse struct {
|
||||||
ID uuid.UUID `json:"id"`
|
ID uuid.UUID `json:"id"`
|
||||||
PurchaseOrderID uuid.UUID `json:"purchase_order_id"`
|
PurchaseOrderID uuid.UUID `json:"purchase_order_id"`
|
||||||
IngredientID uuid.UUID `json:"ingredient_id"`
|
IngredientID uuid.UUID `json:"ingredient_id"`
|
||||||
Description *string `json:"description"`
|
PurchaseCategoryID uuid.UUID `json:"purchase_category_id"`
|
||||||
Quantity float64 `json:"quantity"`
|
Description *string `json:"description"`
|
||||||
UnitID uuid.UUID `json:"unit_id"`
|
Quantity float64 `json:"quantity"`
|
||||||
Amount float64 `json:"amount"`
|
UnitID uuid.UUID `json:"unit_id"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
Amount float64 `json:"amount"`
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
Ingredient *IngredientResponse `json:"ingredient,omitempty"`
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
Unit *UnitResponse `json:"unit,omitempty"`
|
Ingredient *IngredientResponse `json:"ingredient,omitempty"`
|
||||||
|
PurchaseCategory *PurchaseCategoryResponse `json:"purchase_category,omitempty"`
|
||||||
|
Unit *UnitResponse `json:"unit,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PurchaseOrderAttachmentResponse struct {
|
type PurchaseOrderAttachmentResponse struct {
|
||||||
@ -93,11 +96,12 @@ type CreatePurchaseOrderRequest struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type CreatePurchaseOrderItemRequest struct {
|
type CreatePurchaseOrderItemRequest struct {
|
||||||
IngredientID uuid.UUID `json:"ingredient_id"`
|
IngredientID uuid.UUID `json:"ingredient_id"`
|
||||||
Description *string `json:"description,omitempty"`
|
PurchaseCategoryID uuid.UUID `json:"purchase_category_id"`
|
||||||
Quantity float64 `json:"quantity"`
|
Description *string `json:"description,omitempty"`
|
||||||
UnitID uuid.UUID `json:"unit_id"`
|
Quantity float64 `json:"quantity"`
|
||||||
Amount float64 `json:"amount"`
|
UnitID uuid.UUID `json:"unit_id"`
|
||||||
|
Amount float64 `json:"amount"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdatePurchaseOrderRequest struct {
|
type UpdatePurchaseOrderRequest struct {
|
||||||
@ -113,12 +117,13 @@ type UpdatePurchaseOrderRequest struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type UpdatePurchaseOrderItemRequest struct {
|
type UpdatePurchaseOrderItemRequest struct {
|
||||||
ID *uuid.UUID `json:"id,omitempty"` // For existing items
|
ID *uuid.UUID `json:"id,omitempty"` // For existing items
|
||||||
IngredientID *uuid.UUID `json:"ingredient_id,omitempty"`
|
IngredientID *uuid.UUID `json:"ingredient_id,omitempty"`
|
||||||
Description *string `json:"description,omitempty"`
|
PurchaseCategoryID *uuid.UUID `json:"purchase_category_id,omitempty"`
|
||||||
Quantity *float64 `json:"quantity,omitempty"`
|
Description *string `json:"description,omitempty"`
|
||||||
UnitID *uuid.UUID `json:"unit_id,omitempty"`
|
Quantity *float64 `json:"quantity,omitempty"`
|
||||||
Amount *float64 `json:"amount,omitempty"`
|
UnitID *uuid.UUID `json:"unit_id,omitempty"`
|
||||||
|
Amount *float64 `json:"amount,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ListPurchaseOrdersRequest struct {
|
type ListPurchaseOrdersRequest struct {
|
||||||
|
|||||||
@ -86,7 +86,7 @@ type CustomerRepository interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type InventoryMovementService interface {
|
type InventoryMovementService interface {
|
||||||
CreateIngredientMovement(ctx context.Context, ingredientID, organizationID, outletID, userID uuid.UUID, movementType entities.InventoryMovementType, quantity float64, unitCost float64, reason string, referenceType *entities.InventoryMovementReferenceType, referenceID *uuid.UUID) error
|
CreateIngredientMovement(ctx context.Context, ingredientID, organizationID, outletID, userID uuid.UUID, movementType entities.InventoryMovementType, quantity float64, unitCost float64, reason string, referenceType *entities.InventoryMovementReferenceType, referenceID *uuid.UUID, purchaseOrderItemID *uuid.UUID) error
|
||||||
CreateProductMovement(ctx context.Context, productID, organizationID, outletID, userID uuid.UUID, movementType entities.InventoryMovementType, quantity float64, unitCost float64, reason string, referenceType *entities.InventoryMovementReferenceType, referenceID *uuid.UUID) error
|
CreateProductMovement(ctx context.Context, productID, organizationID, outletID, userID uuid.UUID, movementType entities.InventoryMovementType, quantity float64, unitCost float64, reason string, referenceType *entities.InventoryMovementReferenceType, referenceID *uuid.UUID) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -25,6 +25,7 @@ type PurchaseOrderProcessorImpl struct {
|
|||||||
purchaseOrderRepo PurchaseOrderRepository
|
purchaseOrderRepo PurchaseOrderRepository
|
||||||
vendorRepo VendorRepository
|
vendorRepo VendorRepository
|
||||||
ingredientRepo IngredientRepository
|
ingredientRepo IngredientRepository
|
||||||
|
purchaseCategoryRepo PurchaseCategoryRepository
|
||||||
unitRepo UnitRepository
|
unitRepo UnitRepository
|
||||||
fileRepo FileRepository
|
fileRepo FileRepository
|
||||||
inventoryMovementService InventoryMovementService
|
inventoryMovementService InventoryMovementService
|
||||||
@ -35,6 +36,7 @@ func NewPurchaseOrderProcessorImpl(
|
|||||||
purchaseOrderRepo PurchaseOrderRepository,
|
purchaseOrderRepo PurchaseOrderRepository,
|
||||||
vendorRepo VendorRepository,
|
vendorRepo VendorRepository,
|
||||||
ingredientRepo IngredientRepository,
|
ingredientRepo IngredientRepository,
|
||||||
|
purchaseCategoryRepo PurchaseCategoryRepository,
|
||||||
unitRepo UnitRepository,
|
unitRepo UnitRepository,
|
||||||
fileRepo FileRepository,
|
fileRepo FileRepository,
|
||||||
inventoryMovementService InventoryMovementService,
|
inventoryMovementService InventoryMovementService,
|
||||||
@ -44,6 +46,7 @@ func NewPurchaseOrderProcessorImpl(
|
|||||||
purchaseOrderRepo: purchaseOrderRepo,
|
purchaseOrderRepo: purchaseOrderRepo,
|
||||||
vendorRepo: vendorRepo,
|
vendorRepo: vendorRepo,
|
||||||
ingredientRepo: ingredientRepo,
|
ingredientRepo: ingredientRepo,
|
||||||
|
purchaseCategoryRepo: purchaseCategoryRepo,
|
||||||
unitRepo: unitRepo,
|
unitRepo: unitRepo,
|
||||||
fileRepo: fileRepo,
|
fileRepo: fileRepo,
|
||||||
inventoryMovementService: inventoryMovementService,
|
inventoryMovementService: inventoryMovementService,
|
||||||
@ -64,13 +67,17 @@ func (p *PurchaseOrderProcessorImpl) CreatePurchaseOrder(ctx context.Context, or
|
|||||||
return nil, fmt.Errorf("purchase order with PO number %s already exists in this organization", req.PONumber)
|
return nil, fmt.Errorf("purchase order with PO number %s already exists in this organization", req.PONumber)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate ingredients and units exist
|
// Validate ingredients, raw-material categories, and units exist
|
||||||
for i, item := range req.Items {
|
for i, item := range req.Items {
|
||||||
_, err := p.ingredientRepo.GetByID(ctx, item.IngredientID, organizationID)
|
_, err := p.ingredientRepo.GetByID(ctx, item.IngredientID, organizationID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("ingredient not found for item %d: %w", i, err)
|
return nil, fmt.Errorf("ingredient not found for item %d: %w", i, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := p.validateRawMaterialPurchaseCategory(ctx, item.PurchaseCategoryID, organizationID, i); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
_, err = p.unitRepo.GetByID(ctx, item.UnitID, organizationID)
|
_, err = p.unitRepo.GetByID(ctx, item.UnitID, organizationID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unit not found for item %d: %w", i, err)
|
return nil, fmt.Errorf("unit not found for item %d: %w", i, err)
|
||||||
@ -109,12 +116,13 @@ func (p *PurchaseOrderProcessorImpl) CreatePurchaseOrder(ctx context.Context, or
|
|||||||
// Create purchase order items
|
// Create purchase order items
|
||||||
for _, itemReq := range req.Items {
|
for _, itemReq := range req.Items {
|
||||||
itemEntity := &entities.PurchaseOrderItem{
|
itemEntity := &entities.PurchaseOrderItem{
|
||||||
PurchaseOrderID: poEntity.ID,
|
PurchaseOrderID: poEntity.ID,
|
||||||
IngredientID: itemReq.IngredientID,
|
IngredientID: itemReq.IngredientID,
|
||||||
Description: itemReq.Description,
|
PurchaseCategoryID: itemReq.PurchaseCategoryID,
|
||||||
Quantity: itemReq.Quantity,
|
Description: itemReq.Description,
|
||||||
UnitID: itemReq.UnitID,
|
Quantity: itemReq.Quantity,
|
||||||
Amount: itemReq.Amount,
|
UnitID: itemReq.UnitID,
|
||||||
|
Amount: itemReq.Amount,
|
||||||
}
|
}
|
||||||
|
|
||||||
err = p.purchaseOrderRepo.CreateItem(ctx, itemEntity)
|
err = p.purchaseOrderRepo.CreateItem(ctx, itemEntity)
|
||||||
@ -197,7 +205,7 @@ func (p *PurchaseOrderProcessorImpl) UpdatePurchaseOrder(ctx context.Context, id
|
|||||||
|
|
||||||
// Create new items
|
// Create new items
|
||||||
totalAmount := 0.0
|
totalAmount := 0.0
|
||||||
for _, itemReq := range req.Items {
|
for i, itemReq := range req.Items {
|
||||||
// Validate ingredients and units exist
|
// Validate ingredients and units exist
|
||||||
if itemReq.IngredientID != nil {
|
if itemReq.IngredientID != nil {
|
||||||
_, err := p.ingredientRepo.GetByID(ctx, *itemReq.IngredientID, organizationID)
|
_, err := p.ingredientRepo.GetByID(ctx, *itemReq.IngredientID, organizationID)
|
||||||
@ -213,8 +221,15 @@ func (p *PurchaseOrderProcessorImpl) UpdatePurchaseOrder(ctx context.Context, id
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if itemReq.PurchaseCategoryID != nil {
|
||||||
|
if err := p.validateRawMaterialPurchaseCategory(ctx, *itemReq.PurchaseCategoryID, organizationID, i); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Use existing values if not provided
|
// Use existing values if not provided
|
||||||
ingredientID := poEntity.Items[0].IngredientID // This is a simplified approach
|
ingredientID := poEntity.Items[0].IngredientID // This is a simplified approach
|
||||||
|
purchaseCategoryID := poEntity.Items[0].PurchaseCategoryID
|
||||||
unitID := poEntity.Items[0].UnitID
|
unitID := poEntity.Items[0].UnitID
|
||||||
quantity := poEntity.Items[0].Quantity
|
quantity := poEntity.Items[0].Quantity
|
||||||
amount := poEntity.Items[0].Amount
|
amount := poEntity.Items[0].Amount
|
||||||
@ -226,6 +241,9 @@ func (p *PurchaseOrderProcessorImpl) UpdatePurchaseOrder(ctx context.Context, id
|
|||||||
if itemReq.UnitID != nil {
|
if itemReq.UnitID != nil {
|
||||||
unitID = *itemReq.UnitID
|
unitID = *itemReq.UnitID
|
||||||
}
|
}
|
||||||
|
if itemReq.PurchaseCategoryID != nil {
|
||||||
|
purchaseCategoryID = *itemReq.PurchaseCategoryID
|
||||||
|
}
|
||||||
if itemReq.Quantity != nil {
|
if itemReq.Quantity != nil {
|
||||||
quantity = *itemReq.Quantity
|
quantity = *itemReq.Quantity
|
||||||
}
|
}
|
||||||
@ -237,12 +255,13 @@ func (p *PurchaseOrderProcessorImpl) UpdatePurchaseOrder(ctx context.Context, id
|
|||||||
}
|
}
|
||||||
|
|
||||||
itemEntity := &entities.PurchaseOrderItem{
|
itemEntity := &entities.PurchaseOrderItem{
|
||||||
PurchaseOrderID: poEntity.ID,
|
PurchaseOrderID: poEntity.ID,
|
||||||
IngredientID: ingredientID,
|
IngredientID: ingredientID,
|
||||||
Description: description,
|
PurchaseCategoryID: purchaseCategoryID,
|
||||||
Quantity: quantity,
|
Description: description,
|
||||||
UnitID: unitID,
|
Quantity: quantity,
|
||||||
Amount: amount,
|
UnitID: unitID,
|
||||||
|
Amount: amount,
|
||||||
}
|
}
|
||||||
|
|
||||||
err = p.purchaseOrderRepo.CreateItem(ctx, itemEntity)
|
err = p.purchaseOrderRepo.CreateItem(ctx, itemEntity)
|
||||||
@ -419,6 +438,7 @@ func (p *PurchaseOrderProcessorImpl) UpdatePurchaseOrderStatus(ctx context.Conte
|
|||||||
reason,
|
reason,
|
||||||
&referenceType,
|
&referenceType,
|
||||||
referenceID,
|
referenceID,
|
||||||
|
&item.ID,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create inventory movement for ingredient %s: %w", item.IngredientID, err)
|
return nil, fmt.Errorf("failed to create inventory movement for ingredient %s: %w", item.IngredientID, err)
|
||||||
@ -440,3 +460,20 @@ func (p *PurchaseOrderProcessorImpl) UpdatePurchaseOrderStatus(ctx context.Conte
|
|||||||
|
|
||||||
return mappers.PurchaseOrderEntityToResponse(updatedPO), nil
|
return mappers.PurchaseOrderEntityToResponse(updatedPO), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *PurchaseOrderProcessorImpl) validateRawMaterialPurchaseCategory(ctx context.Context, categoryID, organizationID uuid.UUID, itemIndex int) error {
|
||||||
|
category, err := p.purchaseCategoryRepo.GetByIDAndOrganizationID(ctx, categoryID, organizationID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("purchase category not found for item %d: %w", itemIndex, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !category.IsActive {
|
||||||
|
return fmt.Errorf("purchase category for item %d is inactive", itemIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
if category.Type != entities.PurchaseCategoryTypeRawMaterial {
|
||||||
|
return fmt.Errorf("purchase category for item %d must be raw_material", itemIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@ -31,6 +31,7 @@ func (r *PurchaseOrderRepositoryImpl) GetByID(ctx context.Context, id uuid.UUID)
|
|||||||
err := r.db.WithContext(ctx).
|
err := r.db.WithContext(ctx).
|
||||||
Preload("Vendor").
|
Preload("Vendor").
|
||||||
Preload("Items.Ingredient").
|
Preload("Items.Ingredient").
|
||||||
|
Preload("Items.PurchaseCategory").
|
||||||
Preload("Items.Unit").
|
Preload("Items.Unit").
|
||||||
Preload("Attachments.File").
|
Preload("Attachments.File").
|
||||||
First(&po, "id = ?", id).Error
|
First(&po, "id = ?", id).Error
|
||||||
@ -45,6 +46,7 @@ func (r *PurchaseOrderRepositoryImpl) GetByIDAndOrganizationID(ctx context.Conte
|
|||||||
err := r.db.WithContext(ctx).
|
err := r.db.WithContext(ctx).
|
||||||
Preload("Vendor").
|
Preload("Vendor").
|
||||||
Preload("Items.Ingredient").
|
Preload("Items.Ingredient").
|
||||||
|
Preload("Items.PurchaseCategory").
|
||||||
Preload("Items.Unit").
|
Preload("Items.Unit").
|
||||||
Preload("Attachments.File").
|
Preload("Attachments.File").
|
||||||
Where("id = ? AND organization_id = ?", id, organizationID).
|
Where("id = ? AND organization_id = ?", id, organizationID).
|
||||||
@ -105,6 +107,7 @@ func (r *PurchaseOrderRepositoryImpl) List(ctx context.Context, organizationID u
|
|||||||
err := query.
|
err := query.
|
||||||
Preload("Vendor").
|
Preload("Vendor").
|
||||||
Preload("Items.Ingredient").
|
Preload("Items.Ingredient").
|
||||||
|
Preload("Items.PurchaseCategory").
|
||||||
Preload("Items.Unit").
|
Preload("Items.Unit").
|
||||||
Preload("Attachments.File").
|
Preload("Attachments.File").
|
||||||
Order("created_at DESC").
|
Order("created_at DESC").
|
||||||
@ -168,6 +171,7 @@ func (r *PurchaseOrderRepositoryImpl) GetByStatus(ctx context.Context, organizat
|
|||||||
Where("organization_id = ? AND status = ?", organizationID, status).
|
Where("organization_id = ? AND status = ?", organizationID, status).
|
||||||
Preload("Vendor").
|
Preload("Vendor").
|
||||||
Preload("Items.Ingredient").
|
Preload("Items.Ingredient").
|
||||||
|
Preload("Items.PurchaseCategory").
|
||||||
Preload("Items.Unit").
|
Preload("Items.Unit").
|
||||||
Find(&pos).Error
|
Find(&pos).Error
|
||||||
return pos, err
|
return pos, err
|
||||||
@ -179,6 +183,7 @@ func (r *PurchaseOrderRepositoryImpl) GetOverdue(ctx context.Context, organizati
|
|||||||
Where("organization_id = ? AND due_date < ? AND status IN (?)", organizationID, time.Now(), []string{"draft", "sent", "approved"}).
|
Where("organization_id = ? AND due_date < ? AND status IN (?)", organizationID, time.Now(), []string{"draft", "sent", "approved"}).
|
||||||
Preload("Vendor").
|
Preload("Vendor").
|
||||||
Preload("Items.Ingredient").
|
Preload("Items.Ingredient").
|
||||||
|
Preload("Items.PurchaseCategory").
|
||||||
Preload("Items.Unit").
|
Preload("Items.Unit").
|
||||||
Find(&pos).Error
|
Find(&pos).Error
|
||||||
return pos, err
|
return pos, err
|
||||||
@ -219,6 +224,7 @@ func (r *PurchaseOrderRepositoryImpl) GetItemsByPurchaseOrderID(ctx context.Cont
|
|||||||
var items []*entities.PurchaseOrderItem
|
var items []*entities.PurchaseOrderItem
|
||||||
err := r.db.WithContext(ctx).
|
err := r.db.WithContext(ctx).
|
||||||
Preload("Ingredient").
|
Preload("Ingredient").
|
||||||
|
Preload("PurchaseCategory").
|
||||||
Preload("Unit").
|
Preload("Unit").
|
||||||
Where("purchase_order_id = ?", purchaseOrderID).
|
Where("purchase_order_id = ?", purchaseOrderID).
|
||||||
Find(&items).Error
|
Find(&items).Error
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type InventoryMovementService interface {
|
type InventoryMovementService interface {
|
||||||
CreateIngredientMovement(ctx context.Context, ingredientID, organizationID, outletID, userID uuid.UUID, movementType entities.InventoryMovementType, quantity float64, unitCost float64, reason string, referenceType *entities.InventoryMovementReferenceType, referenceID *uuid.UUID) error
|
CreateIngredientMovement(ctx context.Context, ingredientID, organizationID, outletID, userID uuid.UUID, movementType entities.InventoryMovementType, quantity float64, unitCost float64, reason string, referenceType *entities.InventoryMovementReferenceType, referenceID *uuid.UUID, purchaseOrderItemID *uuid.UUID) error
|
||||||
CreateProductMovement(ctx context.Context, productID, organizationID, outletID, userID uuid.UUID, movementType entities.InventoryMovementType, quantity float64, unitCost float64, reason string, referenceType *entities.InventoryMovementReferenceType, referenceID *uuid.UUID) error
|
CreateProductMovement(ctx context.Context, productID, organizationID, outletID, userID uuid.UUID, movementType entities.InventoryMovementType, quantity float64, unitCost float64, reason string, referenceType *entities.InventoryMovementReferenceType, referenceID *uuid.UUID) error
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,7 +26,7 @@ func NewInventoryMovementService(inventoryMovementRepo repository.InventoryMovem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InventoryMovementServiceImpl) CreateIngredientMovement(ctx context.Context, ingredientID, organizationID, outletID, userID uuid.UUID, movementType entities.InventoryMovementType, quantity float64, unitCost float64, reason string, referenceType *entities.InventoryMovementReferenceType, referenceID *uuid.UUID) error {
|
func (s *InventoryMovementServiceImpl) CreateIngredientMovement(ctx context.Context, ingredientID, organizationID, outletID, userID uuid.UUID, movementType entities.InventoryMovementType, quantity float64, unitCost float64, reason string, referenceType *entities.InventoryMovementReferenceType, referenceID *uuid.UUID, purchaseOrderItemID *uuid.UUID) error {
|
||||||
ingredient, err := s.ingredientRepo.GetByID(ctx, ingredientID, organizationID)
|
ingredient, err := s.ingredientRepo.GetByID(ctx, ingredientID, organizationID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -36,22 +36,23 @@ func (s *InventoryMovementServiceImpl) CreateIngredientMovement(ctx context.Cont
|
|||||||
newQuantity := previousQuantity + quantity
|
newQuantity := previousQuantity + quantity
|
||||||
|
|
||||||
movement := &entities.InventoryMovement{
|
movement := &entities.InventoryMovement{
|
||||||
ID: uuid.New(),
|
ID: uuid.New(),
|
||||||
OrganizationID: organizationID,
|
OrganizationID: organizationID,
|
||||||
OutletID: outletID,
|
OutletID: outletID,
|
||||||
ItemID: ingredientID,
|
ItemID: ingredientID,
|
||||||
ItemType: "INGREDIENT",
|
ItemType: "INGREDIENT",
|
||||||
MovementType: movementType,
|
MovementType: movementType,
|
||||||
Quantity: quantity,
|
Quantity: quantity,
|
||||||
PreviousQuantity: previousQuantity,
|
PreviousQuantity: previousQuantity,
|
||||||
NewQuantity: newQuantity,
|
NewQuantity: newQuantity,
|
||||||
UnitCost: unitCost,
|
UnitCost: unitCost,
|
||||||
TotalCost: unitCost * quantity,
|
TotalCost: unitCost * quantity,
|
||||||
ReferenceType: referenceType,
|
ReferenceType: referenceType,
|
||||||
ReferenceID: referenceID,
|
ReferenceID: referenceID,
|
||||||
UserID: userID,
|
PurchaseOrderItemID: purchaseOrderItemID,
|
||||||
Reason: &reason,
|
UserID: userID,
|
||||||
CreatedAt: time.Now(),
|
Reason: &reason,
|
||||||
|
CreatedAt: time.Now(),
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.inventoryMovementRepo.Create(ctx, movement)
|
err = s.inventoryMovementRepo.Create(ctx, movement)
|
||||||
|
|||||||
@ -11,11 +11,12 @@ func CreatePurchaseOrderRequestToModel(req *contract.CreatePurchaseOrderRequest)
|
|||||||
items := make([]models.CreatePurchaseOrderItemRequest, len(req.Items))
|
items := make([]models.CreatePurchaseOrderItemRequest, len(req.Items))
|
||||||
for i, item := range req.Items {
|
for i, item := range req.Items {
|
||||||
items[i] = models.CreatePurchaseOrderItemRequest{
|
items[i] = models.CreatePurchaseOrderItemRequest{
|
||||||
IngredientID: item.IngredientID,
|
IngredientID: item.IngredientID,
|
||||||
Description: item.Description,
|
PurchaseCategoryID: item.PurchaseCategoryID,
|
||||||
Quantity: item.Quantity,
|
Description: item.Description,
|
||||||
UnitID: item.UnitID,
|
Quantity: item.Quantity,
|
||||||
Amount: item.Amount,
|
UnitID: item.UnitID,
|
||||||
|
Amount: item.Amount,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,12 +55,13 @@ func UpdatePurchaseOrderRequestToModel(req *contract.UpdatePurchaseOrderRequest)
|
|||||||
items = make([]models.UpdatePurchaseOrderItemRequest, len(req.Items))
|
items = make([]models.UpdatePurchaseOrderItemRequest, len(req.Items))
|
||||||
for i, item := range req.Items {
|
for i, item := range req.Items {
|
||||||
items[i] = models.UpdatePurchaseOrderItemRequest{
|
items[i] = models.UpdatePurchaseOrderItemRequest{
|
||||||
ID: item.ID,
|
ID: item.ID,
|
||||||
IngredientID: item.IngredientID,
|
IngredientID: item.IngredientID,
|
||||||
Description: item.Description,
|
PurchaseCategoryID: item.PurchaseCategoryID,
|
||||||
Quantity: item.Quantity,
|
Description: item.Description,
|
||||||
UnitID: item.UnitID,
|
Quantity: item.Quantity,
|
||||||
Amount: item.Amount,
|
UnitID: item.UnitID,
|
||||||
|
Amount: item.Amount,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -154,15 +156,16 @@ func PurchaseOrderModelResponseToResponse(po *models.PurchaseOrderResponse) *con
|
|||||||
response.Items = make([]contract.PurchaseOrderItemResponse, len(po.Items))
|
response.Items = make([]contract.PurchaseOrderItemResponse, len(po.Items))
|
||||||
for i, item := range po.Items {
|
for i, item := range po.Items {
|
||||||
response.Items[i] = contract.PurchaseOrderItemResponse{
|
response.Items[i] = contract.PurchaseOrderItemResponse{
|
||||||
ID: item.ID,
|
ID: item.ID,
|
||||||
PurchaseOrderID: item.PurchaseOrderID,
|
PurchaseOrderID: item.PurchaseOrderID,
|
||||||
IngredientID: item.IngredientID,
|
IngredientID: item.IngredientID,
|
||||||
Description: item.Description,
|
PurchaseCategoryID: item.PurchaseCategoryID,
|
||||||
Quantity: item.Quantity,
|
Description: item.Description,
|
||||||
UnitID: item.UnitID,
|
Quantity: item.Quantity,
|
||||||
Amount: item.Amount,
|
UnitID: item.UnitID,
|
||||||
CreatedAt: item.CreatedAt,
|
Amount: item.Amount,
|
||||||
UpdatedAt: item.UpdatedAt,
|
CreatedAt: item.CreatedAt,
|
||||||
|
UpdatedAt: item.UpdatedAt,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map ingredient if present
|
// Map ingredient if present
|
||||||
@ -173,6 +176,10 @@ func PurchaseOrderModelResponseToResponse(po *models.PurchaseOrderResponse) *con
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if item.PurchaseCategory != nil {
|
||||||
|
response.Items[i].PurchaseCategory = PurchaseCategoryModelResponseToResponse(item.PurchaseCategory)
|
||||||
|
}
|
||||||
|
|
||||||
// Map unit if present
|
// Map unit if present
|
||||||
if item.Unit != nil {
|
if item.Unit != nil {
|
||||||
response.Items[i].Unit = &contract.UnitResponse{
|
response.Items[i].Unit = &contract.UnitResponse{
|
||||||
|
|||||||
@ -2,11 +2,14 @@ package validator
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"apskel-pos-be/internal/constants"
|
"apskel-pos-be/internal/constants"
|
||||||
"apskel-pos-be/internal/contract"
|
"apskel-pos-be/internal/contract"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PurchaseOrderValidator interface {
|
type PurchaseOrderValidator interface {
|
||||||
@ -26,7 +29,7 @@ func (v *PurchaseOrderValidatorImpl) ValidateCreatePurchaseOrderRequest(req *con
|
|||||||
return errors.New("request body is required"), constants.MissingFieldErrorCode
|
return errors.New("request body is required"), constants.MissingFieldErrorCode
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.VendorID.String() == "" {
|
if req.VendorID == uuid.Nil {
|
||||||
return errors.New("vendor_id is required"), constants.MissingFieldErrorCode
|
return errors.New("vendor_id is required"), constants.MissingFieldErrorCode
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,32 +181,40 @@ func (v *PurchaseOrderValidatorImpl) ValidateListPurchaseOrdersRequest(req *cont
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (v *PurchaseOrderValidatorImpl) validatePurchaseOrderItem(item *contract.CreatePurchaseOrderItemRequest, index int) (error, string) {
|
func (v *PurchaseOrderValidatorImpl) validatePurchaseOrderItem(item *contract.CreatePurchaseOrderItemRequest, index int) (error, string) {
|
||||||
if item.IngredientID.String() == "" {
|
if item.IngredientID == uuid.Nil {
|
||||||
return errors.New("items[" + string(rune(index)) + "].ingredient_id is required"), constants.MissingFieldErrorCode
|
return errors.New("items[" + strconv.Itoa(index) + "].ingredient_id is required"), constants.MissingFieldErrorCode
|
||||||
|
}
|
||||||
|
|
||||||
|
if item.PurchaseCategoryID == uuid.Nil {
|
||||||
|
return errors.New("items[" + strconv.Itoa(index) + "].purchase_category_id is required"), constants.MissingFieldErrorCode
|
||||||
}
|
}
|
||||||
|
|
||||||
if item.Quantity <= 0 {
|
if item.Quantity <= 0 {
|
||||||
return errors.New("items[" + string(rune(index)) + "].quantity must be greater than 0"), constants.MalformedFieldErrorCode
|
return errors.New("items[" + strconv.Itoa(index) + "].quantity must be greater than 0"), constants.MalformedFieldErrorCode
|
||||||
}
|
}
|
||||||
|
|
||||||
if item.UnitID.String() == "" {
|
if item.UnitID == uuid.Nil {
|
||||||
return errors.New("items[" + string(rune(index)) + "].unit_id is required"), constants.MissingFieldErrorCode
|
return errors.New("items[" + strconv.Itoa(index) + "].unit_id is required"), constants.MissingFieldErrorCode
|
||||||
}
|
}
|
||||||
|
|
||||||
if item.Amount < 0 {
|
if item.Amount < 0 {
|
||||||
return errors.New("items[" + string(rune(index)) + "].amount must be greater than or equal to 0"), constants.MalformedFieldErrorCode
|
return errors.New("items[" + strconv.Itoa(index) + "].amount must be greater than or equal to 0"), constants.MalformedFieldErrorCode
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, ""
|
return nil, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *PurchaseOrderValidatorImpl) validateUpdatePurchaseOrderItem(item *contract.UpdatePurchaseOrderItemRequest, index int) (error, string) {
|
func (v *PurchaseOrderValidatorImpl) validateUpdatePurchaseOrderItem(item *contract.UpdatePurchaseOrderItemRequest, index int) (error, string) {
|
||||||
|
if item.PurchaseCategoryID == nil || *item.PurchaseCategoryID == uuid.Nil {
|
||||||
|
return errors.New("items[" + strconv.Itoa(index) + "].purchase_category_id is required"), constants.MissingFieldErrorCode
|
||||||
|
}
|
||||||
|
|
||||||
if item.Quantity != nil && *item.Quantity <= 0 {
|
if item.Quantity != nil && *item.Quantity <= 0 {
|
||||||
return errors.New("items[" + string(rune(index)) + "].quantity must be greater than 0"), constants.MalformedFieldErrorCode
|
return errors.New("items[" + strconv.Itoa(index) + "].quantity must be greater than 0"), constants.MalformedFieldErrorCode
|
||||||
}
|
}
|
||||||
|
|
||||||
if item.Amount != nil && *item.Amount < 0 {
|
if item.Amount != nil && *item.Amount < 0 {
|
||||||
return errors.New("items[" + string(rune(index)) + "].amount must be greater than or equal to 0"), constants.MalformedFieldErrorCode
|
return errors.New("items[" + strconv.Itoa(index) + "].amount must be greater than or equal to 0"), constants.MalformedFieldErrorCode
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, ""
|
return nil, ""
|
||||||
|
|||||||
@ -17,10 +17,11 @@ func validCreatePurchaseOrderRequest() *contract.CreatePurchaseOrderRequest {
|
|||||||
TransactionDate: "2026-05-29",
|
TransactionDate: "2026-05-29",
|
||||||
Items: []contract.CreatePurchaseOrderItemRequest{
|
Items: []contract.CreatePurchaseOrderItemRequest{
|
||||||
{
|
{
|
||||||
IngredientID: uuid.New(),
|
IngredientID: uuid.New(),
|
||||||
Quantity: 1,
|
PurchaseCategoryID: uuid.New(),
|
||||||
UnitID: uuid.New(),
|
Quantity: 1,
|
||||||
Amount: 1000,
|
UnitID: uuid.New(),
|
||||||
|
Amount: 1000,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,5 @@
|
|||||||
|
DROP INDEX IF EXISTS idx_inventory_movements_purchase_order_item_id;
|
||||||
|
ALTER TABLE inventory_movements DROP COLUMN IF EXISTS purchase_order_item_id;
|
||||||
|
|
||||||
|
DROP INDEX IF EXISTS idx_purchase_order_items_purchase_category_id;
|
||||||
|
ALTER TABLE purchase_order_items DROP COLUMN IF EXISTS purchase_category_id;
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
ALTER TABLE purchase_order_items
|
||||||
|
ADD COLUMN IF NOT EXISTS purchase_category_id UUID REFERENCES purchase_categories(id) ON DELETE RESTRICT;
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_purchase_order_items_purchase_category_id
|
||||||
|
ON purchase_order_items(purchase_category_id);
|
||||||
|
|
||||||
|
ALTER TABLE inventory_movements
|
||||||
|
ADD COLUMN IF NOT EXISTS purchase_order_item_id UUID REFERENCES purchase_order_items(id) ON DELETE SET NULL;
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_inventory_movements_purchase_order_item_id
|
||||||
|
ON inventory_movements(purchase_order_item_id);
|
||||||
Loading…
x
Reference in New Issue
Block a user