Compare commits
No commits in common. "67a5c076e70b347a32c204abe306fa29c36808c2" and "7a2060efdcea047033dbaefb30d956a0dca92cb0" have entirely different histories.
67a5c076e7
...
7a2060efdc
@ -140,7 +140,7 @@ type PurchasingIngredientData struct {
|
||||
}
|
||||
|
||||
type PurchasingVendorData struct {
|
||||
VendorID *uuid.UUID `json:"vendor_id"`
|
||||
VendorID uuid.UUID `json:"vendor_id"`
|
||||
VendorName string `json:"vendor_name"`
|
||||
TotalCost float64 `json:"total_cost"`
|
||||
PurchaseOrderCount int64 `json:"purchase_order_count"`
|
||||
|
||||
@ -7,7 +7,7 @@ import (
|
||||
)
|
||||
|
||||
type CreatePurchaseOrderRequest struct {
|
||||
VendorID *uuid.UUID `json:"vendor_id,omitempty" validate:"omitempty"`
|
||||
VendorID uuid.UUID `json:"vendor_id" validate:"required"`
|
||||
PONumber string `json:"po_number" validate:"required,min=1,max=50"`
|
||||
TransactionDate string `json:"transaction_date" validate:"required"` // Format: YYYY-MM-DD
|
||||
DueDate *string `json:"due_date,omitempty" validate:"omitempty"` // Format: YYYY-MM-DD
|
||||
@ -52,7 +52,7 @@ type UpdatePurchaseOrderItemRequest struct {
|
||||
type PurchaseOrderResponse struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
OrganizationID uuid.UUID `json:"organization_id"`
|
||||
VendorID *uuid.UUID `json:"vendor_id"`
|
||||
VendorID uuid.UUID `json:"vendor_id"`
|
||||
PONumber string `json:"po_number"`
|
||||
TransactionDate time.Time `json:"transaction_date"`
|
||||
DueDate *time.Time `json:"due_date"`
|
||||
|
||||
@ -72,7 +72,7 @@ type PurchasingIngredientData struct {
|
||||
}
|
||||
|
||||
type PurchasingVendorData struct {
|
||||
VendorID *uuid.UUID `json:"vendor_id"`
|
||||
VendorID uuid.UUID `json:"vendor_id"`
|
||||
VendorName string `json:"vendor_name"`
|
||||
TotalCost float64 `json:"total_cost"`
|
||||
PurchaseOrderCount int64 `json:"purchase_order_count"`
|
||||
|
||||
@ -11,7 +11,7 @@ import (
|
||||
type PurchaseOrder struct {
|
||||
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
|
||||
OrganizationID uuid.UUID `gorm:"type:uuid;not null" json:"organization_id" validate:"required"`
|
||||
VendorID *uuid.UUID `gorm:"type:uuid" json:"vendor_id" validate:"omitempty"`
|
||||
VendorID uuid.UUID `gorm:"type:uuid;not null" json:"vendor_id" validate:"required"`
|
||||
PONumber string `gorm:"not null;size:50" json:"po_number" validate:"required,min=1,max=50"`
|
||||
TransactionDate time.Time `gorm:"type:date;not null" json:"transaction_date" validate:"required"`
|
||||
DueDate *time.Time `gorm:"type:date" json:"due_date" validate:"omitempty"`
|
||||
|
||||
@ -150,7 +150,7 @@ type PurchasingIngredientData struct {
|
||||
|
||||
// PurchasingVendorData represents purchasing analytics for a vendor
|
||||
type PurchasingVendorData struct {
|
||||
VendorID *uuid.UUID `json:"vendor_id"`
|
||||
VendorID uuid.UUID `json:"vendor_id"`
|
||||
VendorName string `json:"vendor_name"`
|
||||
TotalCost float64 `json:"total_cost"`
|
||||
PurchaseOrderCount int64 `json:"purchase_order_count"`
|
||||
|
||||
@ -9,7 +9,7 @@ import (
|
||||
type PurchaseOrder struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
OrganizationID uuid.UUID `json:"organization_id"`
|
||||
VendorID *uuid.UUID `json:"vendor_id"`
|
||||
VendorID uuid.UUID `json:"vendor_id"`
|
||||
PONumber string `json:"po_number"`
|
||||
TransactionDate time.Time `json:"transaction_date"`
|
||||
DueDate *time.Time `json:"due_date"`
|
||||
@ -44,7 +44,7 @@ type PurchaseOrderAttachment struct {
|
||||
type PurchaseOrderResponse struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
OrganizationID uuid.UUID `json:"organization_id"`
|
||||
VendorID *uuid.UUID `json:"vendor_id"`
|
||||
VendorID uuid.UUID `json:"vendor_id"`
|
||||
PONumber string `json:"po_number"`
|
||||
TransactionDate time.Time `json:"transaction_date"`
|
||||
DueDate *time.Time `json:"due_date"`
|
||||
@ -84,7 +84,7 @@ type PurchaseOrderAttachmentResponse struct {
|
||||
}
|
||||
|
||||
type CreatePurchaseOrderRequest struct {
|
||||
VendorID *uuid.UUID `json:"vendor_id,omitempty"`
|
||||
VendorID uuid.UUID `json:"vendor_id"`
|
||||
PONumber string `json:"po_number"`
|
||||
TransactionDate time.Time `json:"transaction_date"`
|
||||
DueDate *time.Time `json:"due_date,omitempty"`
|
||||
|
||||
@ -55,13 +55,11 @@ func NewPurchaseOrderProcessorImpl(
|
||||
}
|
||||
|
||||
func (p *PurchaseOrderProcessorImpl) CreatePurchaseOrder(ctx context.Context, organizationID uuid.UUID, req *models.CreatePurchaseOrderRequest) (*models.PurchaseOrderResponse, error) {
|
||||
// Check if vendor exists and belongs to organization when provided.
|
||||
if req.VendorID != nil {
|
||||
_, err := p.vendorRepo.GetByIDAndOrganizationID(ctx, *req.VendorID, organizationID)
|
||||
// Check if vendor exists and belongs to organization
|
||||
_, err := p.vendorRepo.GetByIDAndOrganizationID(ctx, req.VendorID, organizationID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("vendor not found: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Check if PO number already exists in organization
|
||||
existingPO, err := p.purchaseOrderRepo.GetByPONumber(ctx, req.PONumber, organizationID)
|
||||
@ -188,7 +186,7 @@ func (p *PurchaseOrderProcessorImpl) UpdatePurchaseOrder(ctx context.Context, id
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("vendor not found: %w", err)
|
||||
}
|
||||
poEntity.VendorID = req.VendorID
|
||||
poEntity.VendorID = *req.VendorID
|
||||
}
|
||||
|
||||
// Check if PO number already exists (if PO number is being updated)
|
||||
|
||||
@ -162,7 +162,7 @@ func (r *AnalyticsRepositoryImpl) getPurchaseOrderPurchasingAnalytics(ctx contex
|
||||
ELSE 0
|
||||
END as average_purchase_order_value,
|
||||
COUNT(DISTINCT i.id) as total_ingredients,
|
||||
COUNT(DISTINCT COALESCE(po.vendor_id::text, 'no-vendor')) as total_vendors
|
||||
COUNT(DISTINCT po.vendor_id) as total_vendors
|
||||
`).
|
||||
Joins("LEFT JOIN purchase_order_items poi ON poi.purchase_order_id = po.id").
|
||||
Joins("LEFT JOIN ingredients i ON poi.ingredient_id = i.id").
|
||||
@ -197,7 +197,7 @@ func (r *AnalyticsRepositoryImpl) getPurchaseOrderPurchasingAnalytics(ctx contex
|
||||
COUNT(DISTINCT po.id) as purchase_orders,
|
||||
COALESCE(SUM(poi.quantity), 0) as quantity,
|
||||
COUNT(DISTINCT i.id) as ingredients,
|
||||
COUNT(DISTINCT COALESCE(po.vendor_id::text, 'no-vendor')) as vendors
|
||||
COUNT(DISTINCT po.vendor_id) as vendors
|
||||
`).
|
||||
Joins("LEFT JOIN purchase_order_items poi ON poi.purchase_order_id = po.id").
|
||||
Joins("LEFT JOIN ingredients i ON poi.ingredient_id = i.id").
|
||||
@ -247,20 +247,20 @@ func (r *AnalyticsRepositoryImpl) getPurchaseOrderPurchasingAnalytics(ctx contex
|
||||
Table("purchase_orders po").
|
||||
Select(`
|
||||
v.id as vendor_id,
|
||||
COALESCE(v.name, 'No Vendor') as vendor_name,
|
||||
v.name as vendor_name,
|
||||
COALESCE(SUM(poi.amount), 0) as total_cost,
|
||||
COUNT(DISTINCT po.id) as purchase_order_count,
|
||||
COUNT(DISTINCT i.id) as ingredient_count,
|
||||
COALESCE(SUM(poi.quantity), 0) as quantity
|
||||
`).
|
||||
Joins("LEFT JOIN vendors v ON po.vendor_id = v.id").
|
||||
Joins("JOIN vendors v ON po.vendor_id = v.id").
|
||||
Joins("LEFT JOIN purchase_order_items poi ON poi.purchase_order_id = po.id").
|
||||
Joins("LEFT JOIN ingredients i ON poi.ingredient_id = i.id").
|
||||
Joins("LEFT JOIN units u ON poi.unit_id = u.id").
|
||||
Where("po.organization_id = ?", organizationID).
|
||||
Where("po.status != ?", "cancelled").
|
||||
Where("po.transaction_date >= ? AND po.transaction_date <= ?", dateFrom, dateTo).
|
||||
Group("v.id, COALESCE(v.name, 'No Vendor')").
|
||||
Group("v.id, v.name").
|
||||
Order("total_cost DESC")
|
||||
vendorQuery = r.applyPurchaseOrderItemOutletFilter(vendorQuery, outletID)
|
||||
|
||||
|
||||
@ -12,13 +12,12 @@ import (
|
||||
)
|
||||
|
||||
func TestCreatePurchaseOrderRequestToModelAllowsMissingDueDate(t *testing.T) {
|
||||
vendorID := uuid.New()
|
||||
ingredientID := uuid.New()
|
||||
quantity := 1.0
|
||||
unitID := uuid.New()
|
||||
|
||||
result, err := CreatePurchaseOrderRequestToModel(&contract.CreatePurchaseOrderRequest{
|
||||
VendorID: &vendorID,
|
||||
VendorID: uuid.New(),
|
||||
PONumber: "PO-001",
|
||||
TransactionDate: "2026-05-29",
|
||||
Items: []contract.CreatePurchaseOrderItemRequest{
|
||||
@ -36,10 +35,9 @@ func TestCreatePurchaseOrderRequestToModelAllowsMissingDueDate(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPurchaseOrderModelResponseToResponseIncludesNullDueDate(t *testing.T) {
|
||||
vendorID := uuid.New()
|
||||
result := PurchaseOrderModelResponseToResponse(&models.PurchaseOrderResponse{
|
||||
ID: uuid.New(),
|
||||
VendorID: &vendorID,
|
||||
VendorID: uuid.New(),
|
||||
PONumber: "PO-001",
|
||||
})
|
||||
|
||||
@ -47,19 +45,3 @@ func TestPurchaseOrderModelResponseToResponseIncludesNullDueDate(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, string(payload), `"due_date":null`)
|
||||
}
|
||||
|
||||
func TestCreatePurchaseOrderRequestToModelAllowsMissingVendor(t *testing.T) {
|
||||
result, err := CreatePurchaseOrderRequestToModel(&contract.CreatePurchaseOrderRequest{
|
||||
PONumber: "PO-001",
|
||||
TransactionDate: "2026-05-29",
|
||||
Items: []contract.CreatePurchaseOrderItemRequest{
|
||||
{
|
||||
PurchaseCategoryID: uuid.New(),
|
||||
Amount: 1000,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, result.VendorID)
|
||||
}
|
||||
|
||||
@ -29,8 +29,8 @@ func (v *PurchaseOrderValidatorImpl) ValidateCreatePurchaseOrderRequest(req *con
|
||||
return errors.New("request body is required"), constants.MissingFieldErrorCode
|
||||
}
|
||||
|
||||
if req.VendorID != nil && *req.VendorID == uuid.Nil {
|
||||
return errors.New("vendor_id cannot be empty"), constants.MalformedFieldErrorCode
|
||||
if req.VendorID == uuid.Nil {
|
||||
return errors.New("vendor_id is required"), constants.MissingFieldErrorCode
|
||||
}
|
||||
|
||||
if strings.TrimSpace(req.PONumber) == "" {
|
||||
|
||||
@ -11,13 +11,12 @@ import (
|
||||
)
|
||||
|
||||
func validCreatePurchaseOrderRequest() *contract.CreatePurchaseOrderRequest {
|
||||
vendorID := uuid.New()
|
||||
ingredientID := uuid.New()
|
||||
quantity := 1.0
|
||||
unitID := uuid.New()
|
||||
|
||||
return &contract.CreatePurchaseOrderRequest{
|
||||
VendorID: &vendorID,
|
||||
VendorID: uuid.New(),
|
||||
PONumber: "PO-001",
|
||||
TransactionDate: "2026-05-29",
|
||||
Items: []contract.CreatePurchaseOrderItemRequest{
|
||||
@ -32,30 +31,6 @@ func validCreatePurchaseOrderRequest() *contract.CreatePurchaseOrderRequest {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPurchaseOrderValidatorCreateAllowsMissingVendor(t *testing.T) {
|
||||
validator := NewPurchaseOrderValidator()
|
||||
req := validCreatePurchaseOrderRequest()
|
||||
req.VendorID = nil
|
||||
|
||||
err, code := validator.ValidateCreatePurchaseOrderRequest(req)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, code)
|
||||
}
|
||||
|
||||
func TestPurchaseOrderValidatorCreateRejectsEmptyVendor(t *testing.T) {
|
||||
validator := NewPurchaseOrderValidator()
|
||||
req := validCreatePurchaseOrderRequest()
|
||||
vendorID := uuid.Nil
|
||||
req.VendorID = &vendorID
|
||||
|
||||
err, code := validator.ValidateCreatePurchaseOrderRequest(req)
|
||||
|
||||
require.Error(t, err)
|
||||
require.Equal(t, constants.MalformedFieldErrorCode, code)
|
||||
require.Contains(t, err.Error(), "vendor_id cannot be empty")
|
||||
}
|
||||
|
||||
func TestPurchaseOrderValidatorCreateAllowsMissingDueDate(t *testing.T) {
|
||||
validator := NewPurchaseOrderValidator()
|
||||
|
||||
|
||||
@ -1,2 +0,0 @@
|
||||
ALTER TABLE purchase_orders
|
||||
ALTER COLUMN vendor_id SET NOT NULL;
|
||||
@ -1,2 +0,0 @@
|
||||
ALTER TABLE purchase_orders
|
||||
ALTER COLUMN vendor_id DROP NOT NULL;
|
||||
Loading…
x
Reference in New Issue
Block a user