Fix issue

This commit is contained in:
ryan 2026-06-15 17:44:25 +07:00
parent b2db56f855
commit 6c19876a47
9 changed files with 92 additions and 19 deletions

View File

@ -40,12 +40,12 @@ type UpdatePurchaseOrderRequest struct {
}
type UpdatePurchaseOrderItemRequest struct {
ID *uuid.UUID `json:"id,omitempty"` // For existing items
IngredientID *uuid.UUID `json:"ingredient_id,omitempty" validate:"omitempty"`
PurchaseCategoryID *uuid.UUID `json:"purchase_category_id,omitempty" validate:"omitempty"`
ID *uuid.UUID `json:"id,omitempty"` // Ignored. Supplying items replaces all existing PO items.
IngredientID *uuid.UUID `json:"ingredient_id" validate:"required"`
PurchaseCategoryID *uuid.UUID `json:"purchase_category_id" validate:"required"`
Description *string `json:"description,omitempty" validate:"omitempty"`
Quantity *float64 `json:"quantity,omitempty" validate:"omitempty,gt=0"`
UnitID *uuid.UUID `json:"unit_id,omitempty" validate:"omitempty"`
Quantity *float64 `json:"quantity" validate:"required,gt=0"`
UnitID *uuid.UUID `json:"unit_id" validate:"required"`
Amount *float64 `json:"amount,omitempty" validate:"omitempty,gte=0"`
}

View File

@ -117,7 +117,7 @@ type UpdatePurchaseOrderRequest struct {
}
type UpdatePurchaseOrderItemRequest struct {
ID *uuid.UUID `json:"id,omitempty"` // For existing items
ID *uuid.UUID `json:"id,omitempty"` // Ignored. Supplying items replaces all existing PO items.
IngredientID *uuid.UUID `json:"ingredient_id,omitempty"`
PurchaseCategoryID *uuid.UUID `json:"purchase_category_id,omitempty"`
Description *string `json:"description,omitempty"`

View File

@ -842,10 +842,10 @@ func exclusiveSummarySalaryBreakdown(transactions []entities.ExclusiveSummaryDai
classification := strings.ToLower(transaction.CategoryCode + " " + transaction.CategoryName + " " + transaction.Description)
switch {
case strings.Contains(classification, "staff") || strings.Contains(classification, "kary") || strings.Contains(classification, "karyawan"):
salaryStaff += transaction.Amount
case strings.Contains(classification, "dw"):
salaryDW += transaction.Amount
case strings.Contains(classification, "staff") || strings.Contains(classification, "kary") || strings.Contains(classification, "karyawan"):
salaryStaff += transaction.Amount
default:
salaryOther += transaction.Amount
}

View File

@ -293,7 +293,7 @@ func TestAnalyticsProcessorGetExclusiveSummaryPeriodCalculatesSummaryAndReimburs
},
DailyTransactions: []entities.ExclusiveSummaryDailyTransaction{
{Date: now, CategoryCode: "biaya_gaji", CategoryName: "Gaji", Description: "gaji kary", Amount: 48203333, Source: "expense"},
{Date: now, CategoryCode: "biaya_gaji", CategoryName: "Gaji", Description: "DW", Amount: 3555000, Source: "expense"},
{Date: now, CategoryCode: "biaya_gaji_dw", CategoryName: "Gaji DW", Description: "gaji karyawan", Amount: 3555000, Source: "expense"},
},
},
}, expenseRepositoryStub{})

View File

@ -173,7 +173,7 @@ func (r *AnalyticsRepositoryImpl) getPurchaseOrderPurchasingAnalytics(ctx contex
Joins("JOIN units u ON poi.unit_id = u.id").
Where("po.organization_id = ?", organizationID).
Where("pc.type = ?", entities.PurchaseCategoryTypeRawMaterial).
Where("po.status != ?", "cancelled").
Where("po.status = ?", "received").
Where("po.transaction_date >= ? AND po.transaction_date <= ?", dateFrom, dateTo)
summaryQuery = r.applyPurchaseOrderItemOutletFilter(summaryQuery, outletID)
@ -214,7 +214,7 @@ func (r *AnalyticsRepositoryImpl) getPurchaseOrderPurchasingAnalytics(ctx contex
Joins("JOIN units u ON poi.unit_id = u.id").
Where("po.organization_id = ?", organizationID).
Where("pc.type = ?", entities.PurchaseCategoryTypeRawMaterial).
Where("po.status != ?", "cancelled").
Where("po.status = ?", "received").
Where("po.transaction_date >= ? AND po.transaction_date <= ?", dateFrom, dateTo).
Group(dateFormat).
Order(dateFormat)
@ -245,7 +245,7 @@ func (r *AnalyticsRepositoryImpl) getPurchaseOrderPurchasingAnalytics(ctx contex
Joins("LEFT JOIN units u ON poi.unit_id = u.id").
Where("po.organization_id = ?", organizationID).
Where("pc.type = ?", entities.PurchaseCategoryTypeRawMaterial).
Where("po.status != ?", "cancelled").
Where("po.status = ?", "received").
Where("po.transaction_date >= ? AND po.transaction_date <= ?", dateFrom, dateTo).
Group("i.id, i.name").
Order("total_cost DESC")
@ -273,7 +273,7 @@ func (r *AnalyticsRepositoryImpl) getPurchaseOrderPurchasingAnalytics(ctx contex
Joins("JOIN units u ON poi.unit_id = u.id").
Where("po.organization_id = ?", organizationID).
Where("pc.type = ?", entities.PurchaseCategoryTypeRawMaterial).
Where("po.status != ?", "cancelled").
Where("po.status = ?", "received").
Where("po.transaction_date >= ? AND po.transaction_date <= ?", dateFrom, dateTo).
Group("v.id, v.name").
Order("total_cost DESC")
@ -296,7 +296,15 @@ func (r *AnalyticsRepositoryImpl) applyPurchaseOrderItemOutletFilter(query *gorm
if outletID == nil {
return query
}
return query.Where("(i.outlet_id = ? OR u.outlet_id = ?)", *outletID, *outletID)
return query.Where(`
EXISTS (
SELECT 1
FROM inventory_movements im
WHERE im.purchase_order_item_id = poi.id
AND im.movement_type = ?
AND im.outlet_id = ?
)
`, entities.InventoryMovementTypePurchase, *outletID)
}
func (r *AnalyticsRepositoryImpl) GetProductAnalytics(ctx context.Context, organizationID uuid.UUID, outletID *uuid.UUID, dateFrom, dateTo time.Time, limit int) ([]*entities.ProductAnalytics, error) {
@ -307,6 +315,7 @@ func (r *AnalyticsRepositoryImpl) GetProductAnalytics(ctx context.Context, organ
Select(`
p.id as product_id,
p.name as product_name,
p.price as product_price,
c.id as category_id,
c.name as category_name,
c.order as category_order,
@ -365,7 +374,7 @@ func (r *AnalyticsRepositoryImpl) GetProductAnalytics(ctx context.Context, organ
query = r.resolveOutletID(query, outletID, "o.outlet_id")
err := query.
Group("p.id, p.name, p.cost, c.id, c.name, c.order, mahpp.hpp_per_unit").
Group("p.id, p.name, p.price, p.cost, c.id, c.name, c.order, mahpp.hpp_per_unit").
Order("revenue DESC").
Limit(limit).
Scan(&results).Error
@ -854,8 +863,14 @@ func (r *AnalyticsRepositoryImpl) exclusiveSummaryTransactionUnionQuery(organiza
}
if outletID != nil {
poOutletFilter = "AND (i.outlet_id = ? OR u.outlet_id = ?)"
args = append(args, *outletID, *outletID)
poOutletFilter = `AND EXISTS (
SELECT 1
FROM inventory_movements im
WHERE im.purchase_order_item_id = poi.id
AND im.movement_type = 'purchase'
AND im.outlet_id = ?
)`
args = append(args, *outletID)
}
args = append(args,

View File

@ -73,3 +73,31 @@ func TestPurchaseOrderValidatorCreateRejectsDueDateBeforeTransactionDate(t *test
require.Equal(t, constants.MalformedFieldErrorCode, code)
require.Contains(t, err.Error(), "due_date must be after transaction_date")
}
func TestPurchaseOrderValidatorUpdateItemsRequireFullReplacementFields(t *testing.T) {
validator := NewPurchaseOrderValidator()
req := &contract.UpdatePurchaseOrderRequest{
Items: []contract.UpdatePurchaseOrderItemRequest{
{
PurchaseCategoryID: ptrUUID(uuid.New()),
Quantity: ptrFloat64(1),
UnitID: ptrUUID(uuid.New()),
Amount: ptrFloat64(1000),
},
},
}
err, code := validator.ValidateUpdatePurchaseOrderRequest(req)
require.Error(t, err)
require.Equal(t, constants.MissingFieldErrorCode, code)
require.Contains(t, err.Error(), "ingredient_id is required")
}
func ptrUUID(id uuid.UUID) *uuid.UUID {
return &id
}
func ptrFloat64(value float64) *float64 {
return &value
}

View File

@ -2,6 +2,7 @@ DROP TRIGGER IF EXISTS trigger_validate_purchase_order_item_raw_material ON purc
DROP FUNCTION IF EXISTS validate_purchase_order_item_raw_material();
ALTER TABLE purchase_order_items
ALTER COLUMN purchase_category_id DROP NOT NULL,
ALTER COLUMN ingredient_id DROP NOT NULL,
ALTER COLUMN quantity DROP NOT NULL,
ALTER COLUMN unit_id DROP NOT NULL;

View File

@ -1,10 +1,21 @@
UPDATE purchase_order_items poi
SET purchase_category_id = pc.id
FROM purchase_orders po
JOIN purchase_categories pc ON pc.organization_id = po.organization_id
AND pc.code = 'bahan_baku'
AND pc.type = 'raw_material'
WHERE poi.purchase_order_id = po.id
AND poi.purchase_category_id IS NULL;
DO $$
BEGIN
IF EXISTS (
SELECT 1
FROM purchase_order_items poi
JOIN purchase_categories pc ON pc.id = poi.purchase_category_id
WHERE pc.type <> 'raw_material'
LEFT JOIN purchase_categories pc ON pc.id = poi.purchase_category_id
WHERE poi.purchase_category_id IS NULL
OR pc.id IS NULL
OR pc.type <> 'raw_material'
OR poi.ingredient_id IS NULL
OR poi.quantity IS NULL
OR poi.unit_id IS NULL
@ -14,6 +25,7 @@ BEGIN
END $$;
ALTER TABLE purchase_order_items
ALTER COLUMN purchase_category_id SET NOT NULL,
ALTER COLUMN ingredient_id SET NOT NULL,
ALTER COLUMN quantity SET NOT NULL,
ALTER COLUMN unit_id SET NOT NULL;

View File

@ -1,3 +1,20 @@
UPDATE expense_items ei
SET purchase_category_id = pc.id
FROM expenses e
JOIN purchase_categories pc ON pc.organization_id = e.organization_id
AND pc.code = 'biaya_lain_lain'
AND pc.type = 'expense'
WHERE ei.expense_id = e.id
AND (
ei.purchase_category_id IS NULL
OR NOT EXISTS (
SELECT 1
FROM purchase_categories current_pc
WHERE current_pc.id = ei.purchase_category_id
AND current_pc.type = 'expense'
)
);
DO $$
BEGIN
IF EXISTS (