215 lines
7.0 KiB
Go
215 lines
7.0 KiB
Go
package validator
|
|
|
|
import (
|
|
"errors"
|
|
"strings"
|
|
"time"
|
|
|
|
"apskel-pos-be/internal/constants"
|
|
"apskel-pos-be/internal/contract"
|
|
)
|
|
|
|
type PurchaseOrderValidator interface {
|
|
ValidateCreatePurchaseOrderRequest(req *contract.CreatePurchaseOrderRequest) (error, string)
|
|
ValidateUpdatePurchaseOrderRequest(req *contract.UpdatePurchaseOrderRequest) (error, string)
|
|
ValidateListPurchaseOrdersRequest(req *contract.ListPurchaseOrdersRequest) (error, string)
|
|
}
|
|
|
|
type PurchaseOrderValidatorImpl struct{}
|
|
|
|
func NewPurchaseOrderValidator() *PurchaseOrderValidatorImpl {
|
|
return &PurchaseOrderValidatorImpl{}
|
|
}
|
|
|
|
func (v *PurchaseOrderValidatorImpl) ValidateCreatePurchaseOrderRequest(req *contract.CreatePurchaseOrderRequest) (error, string) {
|
|
if req == nil {
|
|
return errors.New("request body is required"), constants.MissingFieldErrorCode
|
|
}
|
|
|
|
if req.VendorID.String() == "" {
|
|
return errors.New("vendor_id is required"), constants.MissingFieldErrorCode
|
|
}
|
|
|
|
if strings.TrimSpace(req.PONumber) == "" {
|
|
return errors.New("po_number is required"), constants.MissingFieldErrorCode
|
|
}
|
|
|
|
if len(req.PONumber) < 1 || len(req.PONumber) > 50 {
|
|
return errors.New("po_number must be between 1 and 50 characters"), constants.MalformedFieldErrorCode
|
|
}
|
|
|
|
// Validate transaction date
|
|
if strings.TrimSpace(req.TransactionDate) == "" {
|
|
return errors.New("transaction_date is required"), constants.MissingFieldErrorCode
|
|
}
|
|
transactionDate, err := time.Parse("2006-01-02", req.TransactionDate)
|
|
if err != nil {
|
|
return errors.New("transaction_date must be in YYYY-MM-DD format"), constants.MalformedFieldErrorCode
|
|
}
|
|
|
|
// Validate due date
|
|
if strings.TrimSpace(req.DueDate) == "" {
|
|
return errors.New("due_date is required"), constants.MissingFieldErrorCode
|
|
}
|
|
dueDate, err := time.Parse("2006-01-02", req.DueDate)
|
|
if err != nil {
|
|
return errors.New("due_date must be in YYYY-MM-DD format"), constants.MalformedFieldErrorCode
|
|
}
|
|
|
|
// Check if due date is after transaction date
|
|
if dueDate.Before(transactionDate) {
|
|
return errors.New("due_date must be after transaction_date"), constants.MalformedFieldErrorCode
|
|
}
|
|
|
|
if req.Reference != nil && len(*req.Reference) > 100 {
|
|
return errors.New("reference must be at most 100 characters"), constants.MalformedFieldErrorCode
|
|
}
|
|
|
|
if req.Status != nil {
|
|
validStatuses := []string{"draft", "sent", "approved", "received", "cancelled"}
|
|
if !contains(validStatuses, *req.Status) {
|
|
return errors.New("status must be one of: draft, sent, approved, received, cancelled"), constants.MalformedFieldErrorCode
|
|
}
|
|
}
|
|
|
|
if len(req.Items) == 0 {
|
|
return errors.New("at least one item is required"), constants.MissingFieldErrorCode
|
|
}
|
|
|
|
// Validate items
|
|
for i, item := range req.Items {
|
|
if err, code := v.validatePurchaseOrderItem(&item, i); err != nil {
|
|
return err, code
|
|
}
|
|
}
|
|
|
|
return nil, ""
|
|
}
|
|
|
|
func (v *PurchaseOrderValidatorImpl) ValidateUpdatePurchaseOrderRequest(req *contract.UpdatePurchaseOrderRequest) (error, string) {
|
|
if req == nil {
|
|
return errors.New("request body is required"), constants.MissingFieldErrorCode
|
|
}
|
|
|
|
if req.PONumber != nil {
|
|
if strings.TrimSpace(*req.PONumber) == "" {
|
|
return errors.New("po_number cannot be empty"), constants.MalformedFieldErrorCode
|
|
}
|
|
if len(*req.PONumber) < 1 || len(*req.PONumber) > 50 {
|
|
return errors.New("po_number must be between 1 and 50 characters"), constants.MalformedFieldErrorCode
|
|
}
|
|
}
|
|
|
|
// Validate dates if both are provided
|
|
if req.TransactionDate != nil && req.DueDate != nil {
|
|
if *req.TransactionDate != "" && *req.DueDate != "" {
|
|
transactionDate, err := time.Parse("2006-01-02", *req.TransactionDate)
|
|
if err != nil {
|
|
return errors.New("transaction_date must be in YYYY-MM-DD format"), constants.MalformedFieldErrorCode
|
|
}
|
|
|
|
dueDate, err := time.Parse("2006-01-02", *req.DueDate)
|
|
if err != nil {
|
|
return errors.New("due_date must be in YYYY-MM-DD format"), constants.MalformedFieldErrorCode
|
|
}
|
|
|
|
if dueDate.Before(transactionDate) {
|
|
return errors.New("due_date must be after transaction_date"), constants.MalformedFieldErrorCode
|
|
}
|
|
}
|
|
}
|
|
|
|
if req.Reference != nil && len(*req.Reference) > 100 {
|
|
return errors.New("reference must be at most 100 characters"), constants.MalformedFieldErrorCode
|
|
}
|
|
|
|
if req.Status != nil {
|
|
validStatuses := []string{"draft", "sent", "approved", "received", "cancelled"}
|
|
if !contains(validStatuses, *req.Status) {
|
|
return errors.New("status must be one of: draft, sent, approved, received, cancelled"), constants.MalformedFieldErrorCode
|
|
}
|
|
}
|
|
|
|
// Validate items if provided
|
|
if req.Items != nil {
|
|
for i, item := range req.Items {
|
|
if err, code := v.validateUpdatePurchaseOrderItem(&item, i); err != nil {
|
|
return err, code
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil, ""
|
|
}
|
|
|
|
func (v *PurchaseOrderValidatorImpl) ValidateListPurchaseOrdersRequest(req *contract.ListPurchaseOrdersRequest) (error, string) {
|
|
if req == nil {
|
|
return errors.New("request body is required"), constants.MissingFieldErrorCode
|
|
}
|
|
|
|
if req.Page < 1 {
|
|
return errors.New("page must be at least 1"), constants.MalformedFieldErrorCode
|
|
}
|
|
|
|
if req.Limit < 1 || req.Limit > 100 {
|
|
return errors.New("limit must be between 1 and 100"), constants.MalformedFieldErrorCode
|
|
}
|
|
|
|
if req.Status != "" {
|
|
validStatuses := []string{"draft", "sent", "approved", "received", "cancelled"}
|
|
if !contains(validStatuses, req.Status) {
|
|
return errors.New("status must be one of: draft, sent, approved, received, cancelled"), constants.MalformedFieldErrorCode
|
|
}
|
|
}
|
|
|
|
if req.StartDate != nil && req.EndDate != nil {
|
|
if req.EndDate.Before(*req.StartDate) {
|
|
return errors.New("end_date must be after start_date"), constants.MalformedFieldErrorCode
|
|
}
|
|
}
|
|
|
|
return nil, ""
|
|
}
|
|
|
|
func (v *PurchaseOrderValidatorImpl) validatePurchaseOrderItem(item *contract.CreatePurchaseOrderItemRequest, index int) (error, string) {
|
|
if item.IngredientID.String() == "" {
|
|
return errors.New("items[" + string(rune(index)) + "].ingredient_id is required"), constants.MissingFieldErrorCode
|
|
}
|
|
|
|
if item.Quantity <= 0 {
|
|
return errors.New("items[" + string(rune(index)) + "].quantity must be greater than 0"), constants.MalformedFieldErrorCode
|
|
}
|
|
|
|
if item.UnitID.String() == "" {
|
|
return errors.New("items[" + string(rune(index)) + "].unit_id is required"), constants.MissingFieldErrorCode
|
|
}
|
|
|
|
if item.Amount < 0 {
|
|
return errors.New("items[" + string(rune(index)) + "].amount must be greater than or equal to 0"), constants.MalformedFieldErrorCode
|
|
}
|
|
|
|
return nil, ""
|
|
}
|
|
|
|
func (v *PurchaseOrderValidatorImpl) validateUpdatePurchaseOrderItem(item *contract.UpdatePurchaseOrderItemRequest, index int) (error, string) {
|
|
if item.Quantity != nil && *item.Quantity <= 0 {
|
|
return errors.New("items[" + string(rune(index)) + "].quantity must be greater than 0"), constants.MalformedFieldErrorCode
|
|
}
|
|
|
|
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 nil, ""
|
|
}
|
|
|
|
// Helper function to check if a string is in a slice
|
|
func contains(slice []string, item string) bool {
|
|
for _, s := range slice {
|
|
if s == item {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|