mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-25 07:45:44 +00:00
Feat[BE]:: adjust marketing report API
This commit is contained in:
@@ -135,7 +135,19 @@ func (r *MarketingDeliveryProductRepositoryImpl) GetAllWithFilters(ctx context.C
|
|||||||
}
|
}
|
||||||
|
|
||||||
if filters.FilterBy != "" && (filters.StartDate != "" || filters.EndDate != "") {
|
if filters.FilterBy != "" && (filters.StartDate != "" || filters.EndDate != "") {
|
||||||
if filters.FilterBy == "delivery_date" {
|
if filters.FilterBy == "so_date" {
|
||||||
|
if filters.StartDate != "" {
|
||||||
|
if startDate, err := utils.ParseDateString(filters.StartDate); err == nil {
|
||||||
|
db = db.Where("marketings.so_date >= ?", startDate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if filters.EndDate != "" {
|
||||||
|
if endDate, err := utils.ParseDateString(filters.EndDate); err == nil {
|
||||||
|
nextDate := endDate.AddDate(0, 0, 1)
|
||||||
|
db = db.Where("marketings.so_date < ?", nextDate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if filters.FilterBy == "realization_date" {
|
||||||
if filters.StartDate != "" {
|
if filters.StartDate != "" {
|
||||||
if startDate, err := utils.ParseDateString(filters.StartDate); err == nil {
|
if startDate, err := utils.ParseDateString(filters.StartDate); err == nil {
|
||||||
db = db.Where("marketing_delivery_products.delivery_date >= ?", startDate)
|
db = db.Where("marketing_delivery_products.delivery_date >= ?", startDate)
|
||||||
@@ -147,18 +159,6 @@ func (r *MarketingDeliveryProductRepositoryImpl) GetAllWithFilters(ctx context.C
|
|||||||
db = db.Where("marketing_delivery_products.delivery_date < ?", nextDate)
|
db = db.Where("marketing_delivery_products.delivery_date < ?", nextDate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if filters.FilterBy == "realization_date" {
|
|
||||||
if filters.StartDate != "" {
|
|
||||||
if startDate, err := utils.ParseDateString(filters.StartDate); err == nil {
|
|
||||||
db = db.Where("marketings.created_at >= ?", startDate)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if filters.EndDate != "" {
|
|
||||||
if endDate, err := utils.ParseDateString(filters.EndDate); err == nil {
|
|
||||||
nextDate := endDate.AddDate(0, 0, 1)
|
|
||||||
db = db.Where("marketings.created_at < ?", nextDate)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,7 +167,9 @@ func (r *MarketingDeliveryProductRepositoryImpl) GetAllWithFilters(ctx context.C
|
|||||||
|
|
||||||
if filters.SortBy != "" {
|
if filters.SortBy != "" {
|
||||||
switch filters.SortBy {
|
switch filters.SortBy {
|
||||||
case "delivery_date":
|
case "so_date":
|
||||||
|
sortColumn = "marketings.so_date"
|
||||||
|
case "realization_date":
|
||||||
sortColumn = "marketing_delivery_products.delivery_date"
|
sortColumn = "marketing_delivery_products.delivery_date"
|
||||||
case "customer":
|
case "customer":
|
||||||
sortColumn = "customers.name"
|
sortColumn = "customers.name"
|
||||||
|
|||||||
@@ -11,6 +11,17 @@ import (
|
|||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// === Marketing Report Response ===
|
||||||
|
|
||||||
|
type MarketingReportResponse struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Meta response.Meta `json:"meta"`
|
||||||
|
Data []dto.RepportMarketingItemDTO `json:"data"`
|
||||||
|
Total *dto.Summary `json:"total,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type RepportController struct {
|
type RepportController struct {
|
||||||
RepportService service.RepportService
|
RepportService service.RepportService
|
||||||
}
|
}
|
||||||
@@ -85,8 +96,31 @@ func (c *RepportController) GetMarketing(ctx *fiber.Ctx) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate total summary from result items
|
||||||
|
var total *dto.Summary
|
||||||
|
if len(result) > 0 {
|
||||||
|
totalQty := 0
|
||||||
|
totalWeightKg := 0.0
|
||||||
|
totalSalesAmount := int64(0)
|
||||||
|
totalHppAmount := int64(0)
|
||||||
|
|
||||||
|
for _, item := range result {
|
||||||
|
totalQty += int(item.Qty)
|
||||||
|
totalWeightKg += item.TotalWeightKg
|
||||||
|
totalSalesAmount += int64(item.SalesAmount)
|
||||||
|
totalHppAmount += int64(item.HppAmount)
|
||||||
|
}
|
||||||
|
|
||||||
|
total = &dto.Summary{
|
||||||
|
TotalQty: totalQty,
|
||||||
|
TotalWeightKg: totalWeightKg,
|
||||||
|
TotalSalesAmount: totalSalesAmount,
|
||||||
|
TotalHppAmount: totalHppAmount,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ctx.Status(fiber.StatusOK).
|
return ctx.Status(fiber.StatusOK).
|
||||||
JSON(response.SuccessWithPaginate[dto.RepportMarketingItemDTO]{
|
JSON(MarketingReportResponse{
|
||||||
Code: fiber.StatusOK,
|
Code: fiber.StatusOK,
|
||||||
Status: "success",
|
Status: "success",
|
||||||
Message: "Get marketing report successfully",
|
Message: "Get marketing report successfully",
|
||||||
@@ -96,6 +130,7 @@ func (c *RepportController) GetMarketing(ctx *fiber.Ctx) error {
|
|||||||
TotalPages: int64(math.Ceil(float64(totalResults) / float64(query.Limit))),
|
TotalPages: int64(math.Ceil(float64(totalResults) / float64(query.Limit))),
|
||||||
TotalResults: totalResults,
|
TotalResults: totalResults,
|
||||||
},
|
},
|
||||||
Data: result,
|
Data: result,
|
||||||
|
Total: total,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package dto
|
package dto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||||
customerDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/customers/dto"
|
customerDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/customers/dto"
|
||||||
productDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/products/dto"
|
productDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/products/dto"
|
||||||
@@ -8,11 +11,10 @@ import (
|
|||||||
userDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/users/dto"
|
userDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/users/dto"
|
||||||
)
|
)
|
||||||
|
|
||||||
// === Main Report Item DTO ===
|
|
||||||
|
|
||||||
type RepportMarketingItemDTO struct {
|
type RepportMarketingItemDTO struct {
|
||||||
DoDate string `json:"do_date"`
|
ID int `json:"id"`
|
||||||
RealizationDate string `json:"realization_date"`
|
SoDate time.Time `json:"so_date"`
|
||||||
|
RealizationDate time.Time `json:"realization_date"`
|
||||||
AgingDays int `json:"aging_days"`
|
AgingDays int `json:"aging_days"`
|
||||||
Warehouse *warehouseDTO.WarehouseRelationDTO `json:"warehouse,omitempty"`
|
Warehouse *warehouseDTO.WarehouseRelationDTO `json:"warehouse,omitempty"`
|
||||||
Customer *customerDTO.CustomerRelationDTO `json:"customer,omitempty"`
|
Customer *customerDTO.CustomerRelationDTO `json:"customer,omitempty"`
|
||||||
@@ -30,70 +32,70 @@ type RepportMarketingItemDTO struct {
|
|||||||
HppAmount float64 `json:"hpp_amount"`
|
HppAmount float64 `json:"hpp_amount"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// === Report Response DTO ===
|
type Summary struct {
|
||||||
|
TotalQty int `json:"total_qty"`
|
||||||
|
TotalWeightKg float64 `json:"total_weight_kg"`
|
||||||
|
TotalSalesAmount int64 `json:"total_sales_amount"`
|
||||||
|
TotalHppAmount int64 `json:"total_hpp_amount"`
|
||||||
|
}
|
||||||
|
|
||||||
type RepportMarketingResponseDTO struct {
|
type RepportMarketingResponseDTO struct {
|
||||||
Items []RepportMarketingItemDTO `json:"items"`
|
Items []RepportMarketingItemDTO `json:"items"`
|
||||||
|
Total *Summary `json:"total,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// === MAPPERS ===
|
|
||||||
|
|
||||||
// ToRepportMarketingItemDTO maps marketing delivery product to detailed report item
|
|
||||||
func ToRepportMarketingItemDTO(mdp entity.MarketingDeliveryProduct, hppPricePerKg float64) RepportMarketingItemDTO {
|
func ToRepportMarketingItemDTO(mdp entity.MarketingDeliveryProduct, hppPricePerKg float64) RepportMarketingItemDTO {
|
||||||
|
soDate := time.Time{}
|
||||||
agingDays := 0
|
agingDays := 0
|
||||||
|
if mdp.MarketingProduct.Marketing.SoDate.Year() > 1 {
|
||||||
doDate := ""
|
soDate = mdp.MarketingProduct.Marketing.SoDate
|
||||||
if mdp.DeliveryDate != nil {
|
agingDays = int(time.Now().Sub(soDate).Hours() / 24)
|
||||||
doDate = mdp.DeliveryDate.Format("02-Jan-2006")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
realizationDate := ""
|
realizationDate := time.Time{}
|
||||||
if mdp.DeliveryDate != nil {
|
if mdp.DeliveryDate != nil {
|
||||||
realizationDate = mdp.DeliveryDate.Format("02-Jan-2006")
|
realizationDate = *mdp.DeliveryDate
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate sales_amount = total_weight_kg * sales_price_per_kg
|
doNumber := generateDoNumber(mdp.MarketingProduct.Marketing.SoNumber, mdp.DeliveryDate, mdp.MarketingProduct.ProductWarehouse.WarehouseId)
|
||||||
salesAmount := mdp.TotalWeight * mdp.UnitPrice
|
|
||||||
// Calculate hpp_amount = total_weight_kg * hpp_price_per_kg
|
totalWeightKg := mdp.Qty * mdp.AvgWeight
|
||||||
hppAmount := mdp.TotalWeight * hppPricePerKg
|
salesAmount := totalWeightKg * mdp.UnitPrice
|
||||||
|
hppAmount := totalWeightKg * hppPricePerKg
|
||||||
|
|
||||||
item := RepportMarketingItemDTO{
|
item := RepportMarketingItemDTO{
|
||||||
DoDate: doDate,
|
ID: int(mdp.Id),
|
||||||
|
SoDate: soDate,
|
||||||
RealizationDate: realizationDate,
|
RealizationDate: realizationDate,
|
||||||
AgingDays: agingDays,
|
AgingDays: agingDays,
|
||||||
DoNumber: mdp.MarketingProduct.Marketing.SoNumber,
|
DoNumber: doNumber,
|
||||||
MarketingType: "ayam",
|
MarketingType: "ayam",
|
||||||
Qty: mdp.Qty,
|
Qty: mdp.Qty,
|
||||||
AverageWeightKg: mdp.AvgWeight,
|
AverageWeightKg: mdp.AvgWeight,
|
||||||
TotalWeightKg: mdp.TotalWeight,
|
TotalWeightKg: totalWeightKg,
|
||||||
SalesPricePerKg: mdp.UnitPrice,
|
SalesPricePerKg: mdp.UnitPrice,
|
||||||
HppPricePerKg: hppPricePerKg,
|
HppPricePerKg: hppPricePerKg,
|
||||||
SalesAmount: salesAmount,
|
SalesAmount: salesAmount,
|
||||||
HppAmount: hppAmount,
|
HppAmount: hppAmount,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map warehouse with full details
|
|
||||||
if mdp.MarketingProduct.ProductWarehouse.WarehouseId != 0 {
|
if mdp.MarketingProduct.ProductWarehouse.WarehouseId != 0 {
|
||||||
mapped := warehouseDTO.ToWarehouseRelationDTO(mdp.MarketingProduct.ProductWarehouse.Warehouse)
|
mapped := warehouseDTO.ToWarehouseRelationDTO(mdp.MarketingProduct.ProductWarehouse.Warehouse)
|
||||||
item.Warehouse = &mapped
|
item.Warehouse = &mapped
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map customer using CustomerRelationDTO
|
|
||||||
if mdp.MarketingProduct.Marketing.CustomerId != 0 {
|
if mdp.MarketingProduct.Marketing.CustomerId != 0 {
|
||||||
mapped := customerDTO.ToCustomerRelationDTO(mdp.MarketingProduct.Marketing.Customer)
|
mapped := customerDTO.ToCustomerRelationDTO(mdp.MarketingProduct.Marketing.Customer)
|
||||||
item.Customer = &mapped
|
item.Customer = &mapped
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map sales person
|
|
||||||
if mdp.MarketingProduct.Marketing.SalesPersonId != 0 {
|
if mdp.MarketingProduct.Marketing.SalesPersonId != 0 {
|
||||||
mapped := userDTO.ToUserRelationDTO(mdp.MarketingProduct.Marketing.SalesPerson)
|
mapped := userDTO.ToUserRelationDTO(mdp.MarketingProduct.Marketing.SalesPerson)
|
||||||
item.Sales = &mapped
|
item.Sales = &mapped
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map vehicle number
|
|
||||||
item.VehicleNumber = mdp.VehicleNumber
|
item.VehicleNumber = mdp.VehicleNumber
|
||||||
|
|
||||||
// Map product using ProductRelationDTO
|
|
||||||
if mdp.MarketingProduct.ProductWarehouse.ProductId != 0 {
|
if mdp.MarketingProduct.ProductWarehouse.ProductId != 0 {
|
||||||
mapped := productDTO.ToProductRelationDTO(mdp.MarketingProduct.ProductWarehouse.Product)
|
mapped := productDTO.ToProductRelationDTO(mdp.MarketingProduct.ProductWarehouse.Product)
|
||||||
item.Product = &mapped
|
item.Product = &mapped
|
||||||
@@ -102,7 +104,6 @@ func ToRepportMarketingItemDTO(mdp entity.MarketingDeliveryProduct, hppPricePerK
|
|||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToRepportMarketingItemDTOs maps array of delivery products to report items with HPP calculation
|
|
||||||
func ToRepportMarketingItemDTOs(mdps []entity.MarketingDeliveryProduct, hppPricePerKg float64) []RepportMarketingItemDTO {
|
func ToRepportMarketingItemDTOs(mdps []entity.MarketingDeliveryProduct, hppPricePerKg float64) []RepportMarketingItemDTO {
|
||||||
items := make([]RepportMarketingItemDTO, 0, len(mdps))
|
items := make([]RepportMarketingItemDTO, 0, len(mdps))
|
||||||
for _, mdp := range mdps {
|
for _, mdp := range mdps {
|
||||||
@@ -111,11 +112,46 @@ func ToRepportMarketingItemDTOs(mdps []entity.MarketingDeliveryProduct, hppPrice
|
|||||||
return items
|
return items
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToRepportMarketingResponseDTO creates complete marketing report response
|
func ToSummary(mdps []entity.MarketingDeliveryProduct, hppPricePerKg float64) *Summary {
|
||||||
|
if len(mdps) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
totalQty := 0
|
||||||
|
totalWeightKg := 0.0
|
||||||
|
totalSalesAmount := int64(0)
|
||||||
|
totalHppAmount := int64(0)
|
||||||
|
|
||||||
|
for _, mdp := range mdps {
|
||||||
|
calculatedTotalWeight := mdp.Qty * mdp.AvgWeight
|
||||||
|
totalQty += int(mdp.Qty)
|
||||||
|
totalWeightKg += calculatedTotalWeight
|
||||||
|
totalSalesAmount += int64(calculatedTotalWeight * mdp.UnitPrice)
|
||||||
|
totalHppAmount += int64(calculatedTotalWeight * hppPricePerKg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Summary{
|
||||||
|
TotalQty: totalQty,
|
||||||
|
TotalWeightKg: totalWeightKg,
|
||||||
|
TotalSalesAmount: totalSalesAmount,
|
||||||
|
TotalHppAmount: totalHppAmount,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateDoNumber(soNumber string, deliveryDate *time.Time, warehouseId uint) string {
|
||||||
|
dateStr := ""
|
||||||
|
if deliveryDate != nil {
|
||||||
|
dateStr = deliveryDate.Format("20060102")
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s-%s-%d", soNumber, dateStr, warehouseId)
|
||||||
|
}
|
||||||
|
|
||||||
func ToRepportMarketingResponseDTO(mdps []entity.MarketingDeliveryProduct, hppPricePerKg float64) RepportMarketingResponseDTO {
|
func ToRepportMarketingResponseDTO(mdps []entity.MarketingDeliveryProduct, hppPricePerKg float64) RepportMarketingResponseDTO {
|
||||||
items := ToRepportMarketingItemDTOs(mdps, hppPricePerKg)
|
items := ToRepportMarketingItemDTOs(mdps, hppPricePerKg)
|
||||||
|
total := ToSummary(mdps, hppPricePerKg)
|
||||||
|
|
||||||
return RepportMarketingResponseDTO{
|
return RepportMarketingResponseDTO{
|
||||||
Items: items,
|
Items: items,
|
||||||
|
Total: total,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -152,12 +152,22 @@ func (s *repportService) calculateHppPricePerKg(ctx context.Context, projectFloc
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
totalActualCost := float64(0)
|
costBop := float64(0)
|
||||||
|
|
||||||
for _, realization := range realizations {
|
for _, realization := range realizations {
|
||||||
cost := realization.Price * realization.Qty
|
cost := realization.Price * realization.Qty
|
||||||
totalActualCost += cost
|
category := ""
|
||||||
|
if realization.ExpenseNonstock != nil && realization.ExpenseNonstock.Expense != nil {
|
||||||
|
category = realization.ExpenseNonstock.Expense.Category
|
||||||
|
}
|
||||||
|
|
||||||
|
if category == "BOP" {
|
||||||
|
costBop += cost
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
totalActualCost := costBop
|
||||||
|
|
||||||
if totalActualCost == 0 {
|
if totalActualCost == 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ type MarketingQuery struct {
|
|||||||
ProductId int64 `query:"product_id" validate:"omitempty"`
|
ProductId int64 `query:"product_id" validate:"omitempty"`
|
||||||
WarehouseId int64 `query:"warehouse_id" validate:"omitempty"`
|
WarehouseId int64 `query:"warehouse_id" validate:"omitempty"`
|
||||||
SalesPersonId int64 `query:"sales_person_id" validate:"omitempty"`
|
SalesPersonId int64 `query:"sales_person_id" validate:"omitempty"`
|
||||||
FilterBy string `query:"filter_by" validate:"omitempty,oneof=realization_date delivery_date"`
|
FilterBy string `query:"filter_by" validate:"omitempty,oneof=so_date realization_date"`
|
||||||
StartDate string `query:"start_date" validate:"omitempty,datetime=2006-01-02"`
|
StartDate string `query:"start_date" validate:"omitempty,datetime=2006-01-02"`
|
||||||
EndDate string `query:"end_date" validate:"omitempty,datetime=2006-01-02"`
|
EndDate string `query:"end_date" validate:"omitempty,datetime=2006-01-02"`
|
||||||
SortBy string `query:"sort_by" validate:"omitempty,oneof=delivery_date customer warehouse product sales_person vehicle_number sales_amount hpp_amount qty average_weight total_weight sales_price hpp_price aging_days"`
|
SortBy string `query:"sort_by" validate:"omitempty,oneof=so_date realization_date customer warehouse product sales_person vehicle_number sales_amount hpp_amount qty average_weight total_weight sales_price hpp_price aging_days"`
|
||||||
SortOrder string `query:"sort_order" validate:"omitempty,oneof=asc desc"`
|
SortOrder string `query:"sort_order" validate:"omitempty,oneof=asc desc"`
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user