diff --git a/internal/database/migrations/20260414064542_add_field_weight_per_convertion.down.sql b/internal/database/migrations/20260414064542_add_field_weight_per_convertion.down.sql new file mode 100644 index 00000000..489fce73 --- /dev/null +++ b/internal/database/migrations/20260414064542_add_field_weight_per_convertion.down.sql @@ -0,0 +1,3 @@ +-- Remove convertion fields from marketing_delivery_products table +ALTER TABLE marketing_delivery_products +DROP COLUMN IF EXISTS weight_per_convertion; diff --git a/internal/database/migrations/20260414064542_add_field_weight_per_convertion.up.sql b/internal/database/migrations/20260414064542_add_field_weight_per_convertion.up.sql new file mode 100644 index 00000000..bf7b8bfc --- /dev/null +++ b/internal/database/migrations/20260414064542_add_field_weight_per_convertion.up.sql @@ -0,0 +1,4 @@ +-- Add convertion fields to marketing_delivery_products table +ALTER TABLE marketing_delivery_products +ADD COLUMN IF NOT EXISTS weight_per_convertion NUMERIC(15, 3); + diff --git a/internal/entities/marketing_delivery_product.go b/internal/entities/marketing_delivery_product.go index 78ca61ab..2d20fca4 100644 --- a/internal/entities/marketing_delivery_product.go +++ b/internal/entities/marketing_delivery_product.go @@ -12,6 +12,7 @@ type MarketingDeliveryProduct struct { UnitPrice float64 `gorm:"type:numeric(15,3)"` TotalWeight float64 `gorm:"type:numeric(15,3)"` AvgWeight float64 `gorm:"type:numeric(15,3)"` + WeightPerConvertion *float64 `gorm:"type:numeric(15,3)"` TotalPrice float64 `gorm:"type:numeric(15,3)"` DeliveryDate *time.Time `gorm:"type:timestamptz"` VehicleNumber string `gorm:"type:varchar(50)"` diff --git a/internal/modules/marketing/dto/deliveryorder.dto.go b/internal/modules/marketing/dto/deliveryorder.dto.go index 20b3e42b..e8955d34 100644 --- a/internal/modules/marketing/dto/deliveryorder.dto.go +++ b/internal/modules/marketing/dto/deliveryorder.dto.go @@ -49,26 +49,30 @@ type MarketingDetailDTO struct { } type MarketingDeliveryProductDTO struct { - Id uint `json:"id"` - MarketingProductId uint `json:"marketing_product_id"` - Qty float64 `json:"qty"` - UnitPrice float64 `json:"unit_price"` - TotalWeight float64 `json:"total_weight"` - AvgWeight float64 `json:"avg_weight"` - TotalPrice float64 `json:"total_price"` - DeliveryDate *time.Time `json:"delivery_date"` - VehicleNumber string `json:"vehicle_number"` - ProductWarehouse *productwarehouseDTO.ProductWarehousNestedDTO `json:"product_warehouse,omitempty"` + Id uint `json:"id"` + MarketingProductId uint `json:"marketing_product_id"` + Qty float64 `json:"qty"` + UnitPrice float64 `json:"unit_price"` + TotalWeight float64 `json:"total_weight"` + AvgWeight float64 `json:"avg_weight"` + TotalPrice float64 `json:"total_price"` + DeliveryDate *time.Time `json:"delivery_date"` + VehicleNumber string `json:"vehicle_number"` + ConvertionUnit *string `json:"-"` + WeightPerConvertion *float64 `json:"-"` + ProductWarehouse *productwarehouseDTO.ProductWarehousNestedDTO `json:"product_warehouse,omitempty"` } type DeliveryItemDTO struct { - ProductWarehouse *productwarehouseDTO.ProductWarehousNestedDTO `json:"product_warehouse"` - Qty float64 `json:"qty"` - UnitPrice float64 `json:"unit_price"` - TotalWeight float64 `json:"total_weight"` - AvgWeight float64 `json:"avg_weight"` - TotalPrice float64 `json:"total_price"` - VehicleNumber string `json:"vehicle_number"` + ProductWarehouse *productwarehouseDTO.ProductWarehousNestedDTO `json:"product_warehouse"` + Qty float64 `json:"qty"` + UnitPrice float64 `json:"unit_price"` + TotalWeight float64 `json:"total_weight"` + AvgWeight float64 `json:"avg_weight"` + WeightPerConvertion *float64 `json:"weight_per_convertion"` + TotalPeti *float64 `json:"total_peti"` + TotalPrice float64 `json:"total_price"` + VehicleNumber string `json:"vehicle_number"` } type DeliveryGroupDTO struct { @@ -147,15 +151,16 @@ func ToDeliveryMarketingProductDTO(e entity.MarketingProduct, marketingType stri func ToMarketingDeliveryProductDTO(e entity.MarketingDeliveryProduct) MarketingDeliveryProductDTO { return MarketingDeliveryProductDTO{ - Id: e.Id, - MarketingProductId: e.MarketingProductId, - Qty: e.UsageQty, - UnitPrice: e.UnitPrice, - TotalWeight: e.TotalWeight, - AvgWeight: e.AvgWeight, - TotalPrice: e.TotalPrice, - DeliveryDate: e.DeliveryDate, - VehicleNumber: e.VehicleNumber, + Id: e.Id, + MarketingProductId: e.MarketingProductId, + Qty: e.UsageQty, + UnitPrice: e.UnitPrice, + TotalWeight: e.TotalWeight, + AvgWeight: e.AvgWeight, + TotalPrice: e.TotalPrice, + DeliveryDate: e.DeliveryDate, + VehicleNumber: e.VehicleNumber, + WeightPerConvertion: e.WeightPerConvertion, } } @@ -285,6 +290,7 @@ func enrichDeliveryProductDTOsWithWarehouse(deliveryProductDTOs []MarketingDeliv mapped := productwarehouseDTO.ToProductWarehouseNestedDTO(product.ProductWarehouse) deliveryProductDTOs[i].ProductWarehouse = &mapped } + deliveryProductDTOs[i].ConvertionUnit = product.ConvertionUnit } } @@ -322,13 +328,21 @@ func groupDeliveryProducts(products []MarketingDeliveryProductDTO, soNumber stri } deliveryItem := DeliveryItemDTO{ - ProductWarehouse: product.ProductWarehouse, - Qty: product.Qty, - UnitPrice: product.UnitPrice, - TotalWeight: product.TotalWeight, - AvgWeight: product.AvgWeight, - TotalPrice: product.TotalPrice, - VehicleNumber: product.VehicleNumber, + ProductWarehouse: product.ProductWarehouse, + Qty: product.Qty, + UnitPrice: product.UnitPrice, + TotalWeight: product.TotalWeight, + AvgWeight: product.AvgWeight, + WeightPerConvertion: product.WeightPerConvertion, + TotalPrice: product.TotalPrice, + VehicleNumber: product.VehicleNumber, + } + if product.ConvertionUnit != nil && + strings.EqualFold(*product.ConvertionUnit, "PETI") && + product.WeightPerConvertion != nil && + *product.WeightPerConvertion > 0 { + totalPeti := product.TotalWeight / *product.WeightPerConvertion + deliveryItem.TotalPeti = &totalPeti } group.Deliveries = append(group.Deliveries, deliveryItem) } diff --git a/internal/modules/marketing/services/deliveryorder.service.go b/internal/modules/marketing/services/deliveryorder.service.go index 2ae8dec9..34c415c3 100644 --- a/internal/modules/marketing/services/deliveryorder.service.go +++ b/internal/modules/marketing/services/deliveryorder.service.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "math" "strings" "time" @@ -375,11 +376,12 @@ func (s *deliveryOrdersService) CreateOne(c *fiber.Ctx, req *validation.Delivery itemDeliveryDate = &parsedDate } - totalWeight, totalPrice := s.calculatePriceByMarketingType(marketing.MarketingType, requestedProduct.Qty, requestedProduct.AvgWeight, requestedProduct.UnitPrice, foundMarketingProduct.Week) + totalWeight, totalPrice := s.resolveDeliveryTotals(marketing.MarketingType, requestedProduct, foundMarketingProduct) deliveryProduct.ProductWarehouseId = foundMarketingProduct.ProductWarehouseId deliveryProduct.UnitPrice = requestedProduct.UnitPrice deliveryProduct.AvgWeight = requestedProduct.AvgWeight + deliveryProduct.WeightPerConvertion = requestedProduct.WeightPerConvertion deliveryProduct.TotalWeight = totalWeight deliveryProduct.TotalPrice = totalPrice deliveryProduct.DeliveryDate = itemDeliveryDate @@ -498,11 +500,12 @@ func (s deliveryOrdersService) UpdateOne(c *fiber.Ctx, req *validation.DeliveryO itemDeliveryDate = deliveryProduct.DeliveryDate } - totalWeight, totalPrice := s.calculatePriceByMarketingType(marketing.MarketingType, requestedProduct.Qty, requestedProduct.AvgWeight, requestedProduct.UnitPrice, foundMarketingProduct.Week) + totalWeight, totalPrice := s.resolveDeliveryTotals(marketing.MarketingType, requestedProduct, foundMarketingProduct) deliveryProduct.ProductWarehouseId = foundMarketingProduct.ProductWarehouseId deliveryProduct.UnitPrice = requestedProduct.UnitPrice deliveryProduct.AvgWeight = requestedProduct.AvgWeight + deliveryProduct.WeightPerConvertion = requestedProduct.WeightPerConvertion deliveryProduct.TotalWeight = totalWeight deliveryProduct.TotalPrice = totalPrice deliveryProduct.DeliveryDate = itemDeliveryDate @@ -541,20 +544,53 @@ func (s deliveryOrdersService) UpdateOne(c *fiber.Ctx, req *validation.DeliveryO return s.getMarketingWithDeliveries(c, id) } -func (s *deliveryOrdersService) calculatePriceByMarketingType(marketingType string, qty, avgWeight, unitPrice float64, week *int) (totalWeight, totalPrice float64) { +func (s *deliveryOrdersService) calculatePriceByMarketingType(marketingType string, qty, avgWeight, unitPrice float64, week *int, convertionUnit *string, _ *float64) (totalWeight, totalPrice float64) { if marketingType == string(utils.MarketingTypeTrading) { totalWeight = 0 - totalPrice = qty * unitPrice + totalPrice = math.Round(qty*unitPrice*100) / 100 } else if marketingType == string(utils.MarketingTypeAyamPullet) && week != nil && *week > 0 { - totalWeight = qty * avgWeight - totalPrice = unitPrice * float64(*week) * qty + totalWeight = math.Round(qty*avgWeight*100) / 100 + totalPrice = math.Round(unitPrice*float64(*week)*qty*100) / 100 } else { - totalWeight = qty * avgWeight - totalPrice = totalWeight * unitPrice + totalWeight = math.Round(qty*avgWeight*100) / 100 + + if marketingType == string(utils.MarketingTypeTelur) && convertionUnit != nil { + switch *convertionUnit { + case string(utils.ConvertionUnitQty): + totalPrice = math.Round(qty*unitPrice*100) / 100 + return totalWeight, totalPrice + case string(utils.ConvertionUnitPeti): + totalPrice = math.Round(totalWeight*unitPrice*100) / 100 + return totalWeight, totalPrice + } + } + + totalPrice = math.Round(totalWeight*unitPrice*100) / 100 } return totalWeight, totalPrice } +func (s *deliveryOrdersService) resolveDeliveryTotals(marketingType string, requestedProduct validation.DeliveryProduct, marketingProduct *entity.MarketingProduct) (totalWeight, totalPrice float64) { + totalWeight, totalPrice = s.calculatePriceByMarketingType( + marketingType, + requestedProduct.Qty, + requestedProduct.AvgWeight, + requestedProduct.UnitPrice, + marketingProduct.Week, + marketingProduct.ConvertionUnit, + marketingProduct.WeightPerConvertion, + ) + + if requestedProduct.TotalWeight != nil { + totalWeight = *requestedProduct.TotalWeight + } + if requestedProduct.TotalPrice != nil { + totalPrice = *requestedProduct.TotalPrice + } + + return totalWeight, totalPrice +} + func (s deliveryOrdersService) consumeDeliveryStock(ctx context.Context, tx *gorm.DB, deliveryProduct *entity.MarketingDeliveryProduct, marketingProduct *entity.MarketingProduct, requestedQty float64, actorID uint) error { if marketingProduct == nil || marketingProduct.ProductWarehouseId == 0 { return fiber.NewError(fiber.StatusInternalServerError, "Product warehouse not found") diff --git a/internal/modules/marketing/services/salesorder.service.go b/internal/modules/marketing/services/salesorder.service.go index 858799df..5f646112 100644 --- a/internal/modules/marketing/services/salesorder.service.go +++ b/internal/modules/marketing/services/salesorder.service.go @@ -815,7 +815,7 @@ func (s *salesOrdersService) createMarketingProductWithDelivery(ctx context.Cont return nil } -func (s *salesOrdersService) calculatePriceByMarketingType(marketingType string, qty, avgWeight, unitPrice float64, week *int, convertionUnit *string, weightPerConvertion *float64) (totalWeight, totalPrice float64) { +func (s *salesOrdersService) calculatePriceByMarketingType(marketingType string, qty, avgWeight, unitPrice float64, week *int, convertionUnit *string, _ *float64) (totalWeight, totalPrice float64) { if marketingType == string(utils.MarketingTypeTrading) { totalWeight = 0 totalPrice = math.Round(qty*unitPrice*100) / 100 @@ -831,11 +831,8 @@ func (s *salesOrdersService) calculatePriceByMarketingType(marketingType string, totalPrice = math.Round(qty*unitPrice*100) / 100 return totalWeight, totalPrice case string(utils.ConvertionUnitPeti): - if weightPerConvertion != nil && *weightPerConvertion > 0 { - totalPeti := totalWeight / *weightPerConvertion - totalPrice = math.Round(totalPeti*unitPrice*100) / 100 - return totalWeight, totalPrice - } + totalPrice = math.Round(totalWeight*unitPrice*100) / 100 + return totalWeight, totalPrice } } diff --git a/internal/modules/marketing/validations/deliveryorder.validation.go b/internal/modules/marketing/validations/deliveryorder.validation.go index e4687fad..4b7c1328 100644 --- a/internal/modules/marketing/validations/deliveryorder.validation.go +++ b/internal/modules/marketing/validations/deliveryorder.validation.go @@ -1,12 +1,15 @@ package validation type DeliveryProduct struct { - MarketingProductId uint `json:"marketing_product_id" validate:"required,gt=0"` - Qty float64 `json:"qty" validate:"omitempty,gte=0"` - UnitPrice float64 `json:"unit_price" validate:"omitempty,gte=0"` - AvgWeight float64 `json:"avg_weight" validate:"omitempty,gte=0"` - DeliveryDate string `json:"delivery_date" validate:"omitempty,datetime=2006-01-02"` - VehicleNumber string `json:"vehicle_number" validate:"omitempty,max=50"` + MarketingProductId uint `json:"marketing_product_id" validate:"required,gt=0"` + Qty float64 `json:"qty" validate:"omitempty,gte=0"` + UnitPrice float64 `json:"unit_price" validate:"omitempty,gte=0"` + AvgWeight float64 `json:"avg_weight" validate:"omitempty,gte=0"` + WeightPerConvertion *float64 `json:"weight_per_convertion" validate:"omitempty,gt=0"` + TotalWeight *float64 `json:"total_weight" validate:"omitempty,gte=0"` + TotalPrice *float64 `json:"total_price" validate:"omitempty,gte=0"` + DeliveryDate string `json:"delivery_date" validate:"omitempty,datetime=2006-01-02"` + VehicleNumber string `json:"vehicle_number" validate:"omitempty,max=50"` } type DeliveryOrderCreate struct {