mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 13:31:56 +00:00
fixing report-counting-sapronak
This commit is contained in:
@@ -13,36 +13,35 @@ import (
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/modules/closings/dto"
|
||||
repository "gitlab.com/mbugroup/lti-api.git/internal/modules/closings/repositories"
|
||||
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/closings/validations"
|
||||
projectflockRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/repositories"
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||
)
|
||||
|
||||
type SapronakService interface {
|
||||
GetSapronakByProject(ctx *fiber.Ctx, projectFlockID uint, flag string) ([]dto.SapronakReportDTO, error)
|
||||
GetSapronakByKandang(ctx *fiber.Ctx, projectFlockID uint, pfkID uint, flag string) (*dto.SapronakReportDTO, error)
|
||||
GetSapronakReport(ctx *fiber.Ctx, params *validation.CountSapronakQuery) ([]dto.SapronakReportDTO, error)
|
||||
}
|
||||
|
||||
type sapronakService struct {
|
||||
Log *logrus.Logger
|
||||
Validate *validator.Validate
|
||||
Repository repository.ClosingRepository
|
||||
Log *logrus.Logger
|
||||
Validate *validator.Validate
|
||||
Repository repository.ClosingRepository
|
||||
ProjectFlockKandangRepo projectflockRepository.ProjectFlockKandangRepository
|
||||
}
|
||||
|
||||
func NewSapronakService(repo repository.ClosingRepository, validate *validator.Validate) SapronakService {
|
||||
func NewSapronakService(
|
||||
repo repository.ClosingRepository,
|
||||
pfkRepo projectflockRepository.ProjectFlockKandangRepository,
|
||||
validate *validator.Validate,
|
||||
) SapronakService {
|
||||
return &sapronakService{
|
||||
Log: utils.Log,
|
||||
Validate: validate,
|
||||
Repository: repo,
|
||||
Log: utils.Log,
|
||||
Validate: validate,
|
||||
Repository: repo,
|
||||
ProjectFlockKandangRepo: pfkRepo,
|
||||
}
|
||||
}
|
||||
|
||||
func (s sapronakService) GetSapronakReport(c *fiber.Ctx, params *validation.CountSapronakQuery) ([]dto.SapronakReportDTO, error) {
|
||||
if err := s.Validate.Struct(params); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.computeSapronakReports(c.Context(), params)
|
||||
}
|
||||
|
||||
func (s sapronakService) GetSapronakByProject(c *fiber.Ctx, projectFlockID uint, flag string) ([]dto.SapronakReportDTO, error) {
|
||||
if projectFlockID == 0 {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "project_flock_id is required")
|
||||
@@ -96,13 +95,6 @@ func (s sapronakService) computeSapronakReports(ctx context.Context, params *val
|
||||
return []dto.SapronakReportDTO{}, nil
|
||||
}
|
||||
|
||||
startMap, err := s.mapStartDates(ctx, pfks)
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to prepare start dates for sapronak report: %+v", err)
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to prepare sapronak report")
|
||||
}
|
||||
statusMap, nextStartMap := s.computeStatusAndNextStart(pfks, startMap)
|
||||
|
||||
filterStatus := strings.ToLower(strings.TrimSpace(params.Status))
|
||||
if filterStatus == "" {
|
||||
filterStatus = "all"
|
||||
@@ -110,29 +102,17 @@ func (s sapronakService) computeSapronakReports(ctx context.Context, params *val
|
||||
|
||||
results := make([]dto.SapronakReportDTO, 0, len(pfks))
|
||||
for _, pfk := range pfks {
|
||||
status := statusMap[pfk.Id]
|
||||
if status == "" {
|
||||
status = "closing"
|
||||
status := "closing"
|
||||
if pfk.ClosedAt == nil {
|
||||
status = "active"
|
||||
}
|
||||
|
||||
if (filterStatus == "active" && status != "active") || (filterStatus == "closing" && status != "closing") {
|
||||
continue
|
||||
}
|
||||
|
||||
start := startMap[pfk.Id]
|
||||
var startPtr *time.Time
|
||||
if !start.IsZero() {
|
||||
startCopy := start
|
||||
startPtr = &startCopy
|
||||
}
|
||||
|
||||
var endPtr *time.Time
|
||||
if end, ok := nextStartMap[pfk.Id]; ok {
|
||||
endCopy := end
|
||||
endPtr = &endCopy
|
||||
}
|
||||
|
||||
items, groups, totalIncoming, totalUsage, err := s.buildSapronakItems(ctx, pfk, startPtr, endPtr, params.Flag)
|
||||
// We no longer filter by date for closing sapronak report; pass nil pointers.
|
||||
items, groups, totalIncoming, totalUsage, err := s.buildSapronakItems(ctx, pfk, nil, nil, params.Flag)
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to build sapronak items for pfk %d: %+v", pfk.Id, err)
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to calculate sapronak report")
|
||||
@@ -146,8 +126,8 @@ func (s sapronakService) computeSapronakReports(ctx context.Context, params *val
|
||||
KandangName: pfk.Kandang.Name,
|
||||
Period: pfk.Period,
|
||||
Status: status,
|
||||
StartDate: startPtr,
|
||||
EndDate: endPtr,
|
||||
StartDate: nil,
|
||||
EndDate: nil,
|
||||
TotalIncomingValue: totalIncoming,
|
||||
TotalUsageValue: totalUsage,
|
||||
Items: items,
|
||||
@@ -159,41 +139,31 @@ func (s sapronakService) computeSapronakReports(ctx context.Context, params *val
|
||||
}
|
||||
|
||||
func (s sapronakService) loadProjectFlockKandangs(ctx context.Context, params *validation.CountSapronakQuery) ([]entity.ProjectFlockKandang, error) {
|
||||
pfks, err := s.Repository.ListProjectFlockKandangsForSapronak(ctx, params)
|
||||
if err != nil {
|
||||
db := s.ProjectFlockKandangRepo.DB().WithContext(ctx).
|
||||
Preload("ProjectFlock").
|
||||
Preload("Kandang").
|
||||
Preload("Chickins")
|
||||
|
||||
if params != nil {
|
||||
if params.ProjectFlockID > 0 {
|
||||
db = db.Where("project_flock_kandangs.project_flock_id = ?", params.ProjectFlockID)
|
||||
}
|
||||
if params.KandangID > 0 {
|
||||
db = db.Where("project_flock_kandangs.kandang_id = ?", params.KandangID)
|
||||
}
|
||||
if params.ProjectFlockKandangID > 0 {
|
||||
db = db.Where("project_flock_kandangs.id = ?", params.ProjectFlockKandangID)
|
||||
}
|
||||
}
|
||||
|
||||
var pfks []entity.ProjectFlockKandang
|
||||
if err := db.Find(&pfks).Error; err != nil {
|
||||
s.Log.Errorf("Failed to load project flock kandangs for sapronak report: %+v", err)
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to load project flock kandangs")
|
||||
}
|
||||
return pfks, nil
|
||||
}
|
||||
|
||||
func (s sapronakService) mapStartDates(ctx context.Context, pfks []entity.ProjectFlockKandang) (map[uint]time.Time, error) {
|
||||
result := make(map[uint]time.Time, len(pfks))
|
||||
if len(pfks) == 0 {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
ids := make([]uint, len(pfks))
|
||||
for i, pfk := range pfks {
|
||||
ids[i] = pfk.Id
|
||||
}
|
||||
|
||||
startDates, err := s.Repository.MapSapronakStartDates(ctx, ids)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, pfk := range pfks {
|
||||
if start, ok := startDates[pfk.Id]; ok {
|
||||
result[pfk.Id] = start
|
||||
continue
|
||||
}
|
||||
result[pfk.Id] = pfk.CreatedAt.UTC()
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s sapronakService) combineSapronakReports(reports []dto.SapronakReportDTO, projectID uint) dto.SapronakReportDTO {
|
||||
if len(reports) == 0 {
|
||||
return dto.SapronakReportDTO{}
|
||||
@@ -202,7 +172,6 @@ func (s sapronakService) combineSapronakReports(reports []dto.SapronakReportDTO,
|
||||
var (
|
||||
totalIncoming float64
|
||||
totalUsage float64
|
||||
earliestStart *time.Time
|
||||
projectName = reports[0].ProjectName
|
||||
)
|
||||
|
||||
@@ -220,11 +189,6 @@ func (s sapronakService) combineSapronakReports(reports []dto.SapronakReportDTO,
|
||||
for _, r := range reports {
|
||||
totalIncoming += r.TotalIncomingValue
|
||||
totalUsage += r.TotalUsageValue
|
||||
if r.StartDate != nil {
|
||||
if earliestStart == nil || r.StartDate.Before(*earliestStart) {
|
||||
earliestStart = r.StartDate
|
||||
}
|
||||
}
|
||||
|
||||
for _, it := range r.Items {
|
||||
cur := itemMap[it.ProductID]
|
||||
@@ -274,7 +238,7 @@ func (s sapronakService) combineSapronakReports(reports []dto.SapronakReportDTO,
|
||||
ProjectFlockID: projectID,
|
||||
ProjectName: projectName,
|
||||
Status: "combined",
|
||||
StartDate: earliestStart,
|
||||
StartDate: nil,
|
||||
TotalIncomingValue: totalIncoming,
|
||||
TotalUsageValue: totalUsage,
|
||||
Items: items,
|
||||
@@ -282,36 +246,6 @@ func (s sapronakService) combineSapronakReports(reports []dto.SapronakReportDTO,
|
||||
}
|
||||
}
|
||||
|
||||
func (s sapronakService) computeStatusAndNextStart(pfks []entity.ProjectFlockKandang, startMap map[uint]time.Time) (map[uint]string, map[uint]time.Time) {
|
||||
statusMap := make(map[uint]string, len(pfks))
|
||||
nextStartMap := make(map[uint]time.Time, len(pfks))
|
||||
|
||||
if len(pfks) == 0 {
|
||||
return statusMap, nextStartMap
|
||||
}
|
||||
|
||||
grouped := make(map[uint][]entity.ProjectFlockKandang)
|
||||
for _, pfk := range pfks {
|
||||
grouped[pfk.KandangId] = append(grouped[pfk.KandangId], pfk)
|
||||
}
|
||||
|
||||
for _, list := range grouped {
|
||||
for idx, item := range list {
|
||||
if idx < len(list)-1 {
|
||||
next := list[idx+1]
|
||||
if start, ok := startMap[next.Id]; ok {
|
||||
nextStartMap[item.Id] = start
|
||||
}
|
||||
statusMap[item.Id] = "closing"
|
||||
continue
|
||||
}
|
||||
statusMap[item.Id] = "active"
|
||||
}
|
||||
}
|
||||
|
||||
return statusMap, nextStartMap
|
||||
}
|
||||
|
||||
func mapIncomingUsage(incomingRows []repository.SapronakIncomingRow, usageRows []repository.SapronakUsageRow) (map[uint]repository.SapronakIncomingRow, map[uint]repository.SapronakUsageRow) {
|
||||
incoming := make(map[uint]repository.SapronakIncomingRow, len(incomingRows))
|
||||
for _, row := range incomingRows {
|
||||
@@ -330,6 +264,7 @@ type sapronakDetailMaps struct {
|
||||
AdjIncoming map[uint][]dto.SapronakDetailDTO
|
||||
AdjOutgoing map[uint][]dto.SapronakDetailDTO
|
||||
TransferIn map[uint][]dto.SapronakDetailDTO
|
||||
TransferOut map[uint][]dto.SapronakDetailDTO
|
||||
}
|
||||
|
||||
func buildSapronakDetails(
|
||||
@@ -338,6 +273,7 @@ func buildSapronakDetails(
|
||||
adjIncomingRows map[uint][]repository.SapronakDetailRow,
|
||||
adjOutgoingRows map[uint][]repository.SapronakDetailRow,
|
||||
transferInRows map[uint][]repository.SapronakDetailRow,
|
||||
transferOutRows map[uint][]repository.SapronakDetailRow,
|
||||
) sapronakDetailMaps {
|
||||
result := sapronakDetailMaps{
|
||||
Incoming: make(map[uint][]dto.SapronakDetailDTO),
|
||||
@@ -345,6 +281,7 @@ func buildSapronakDetails(
|
||||
AdjIncoming: make(map[uint][]dto.SapronakDetailDTO),
|
||||
AdjOutgoing: make(map[uint][]dto.SapronakDetailDTO),
|
||||
TransferIn: make(map[uint][]dto.SapronakDetailDTO),
|
||||
TransferOut: make(map[uint][]dto.SapronakDetailDTO),
|
||||
}
|
||||
|
||||
addRows := func(target map[uint][]dto.SapronakDetailDTO, src map[uint][]repository.SapronakDetailRow, jenis string, masuk bool) {
|
||||
@@ -376,32 +313,43 @@ func buildSapronakDetails(
|
||||
addRows(result.AdjIncoming, adjIncomingRows, "Adjustment Masuk", true)
|
||||
addRows(result.AdjOutgoing, adjOutgoingRows, "Adjustment Keluar", false)
|
||||
addRows(result.TransferIn, transferInRows, "Mutasi Masuk", true)
|
||||
addRows(result.TransferOut, transferOutRows, "Mutasi Keluar", false)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (s sapronakService) buildSapronakItems(ctx context.Context, pfk entity.ProjectFlockKandang, start, end *time.Time, flagFilter string) ([]dto.SapronakItemDTO, []dto.SapronakGroupDTO, float64, float64, error) {
|
||||
incomingRows, err := s.Repository.FetchSapronakIncoming(ctx, pfk.KandangId, start, end)
|
||||
// For sapronak closing report we intentionally ignore date range
|
||||
// and aggregate all historical transactions for the kandang/project.
|
||||
incomingRows, err := s.Repository.FetchSapronakIncoming(ctx, pfk.KandangId)
|
||||
if err != nil {
|
||||
return nil, nil, 0, 0, err
|
||||
}
|
||||
incomingDetailsRows, err := s.Repository.FetchSapronakIncomingDetails(ctx, pfk.KandangId, start, end)
|
||||
incomingDetailsRows, err := s.Repository.FetchSapronakIncomingDetails(ctx, pfk.KandangId)
|
||||
if err != nil {
|
||||
return nil, nil, 0, 0, err
|
||||
}
|
||||
usageRows, err := s.Repository.FetchSapronakUsage(ctx, pfk.Id, start, end)
|
||||
usageRows, err := s.Repository.FetchSapronakUsage(ctx, pfk.Id)
|
||||
if err != nil {
|
||||
return nil, nil, 0, 0, err
|
||||
}
|
||||
usageDetailsRows, err := s.Repository.FetchSapronakUsageDetails(ctx, pfk.Id, start, end)
|
||||
chickinUsageRows, err := s.Repository.FetchSapronakChickinUsage(ctx, pfk.Id)
|
||||
if err != nil {
|
||||
return nil, nil, 0, 0, err
|
||||
}
|
||||
adjIncomingRows, adjOutgoingRows, err := s.Repository.FetchSapronakAdjustments(ctx, pfk.KandangId, start, end)
|
||||
usageDetailsRows, err := s.Repository.FetchSapronakUsageDetails(ctx, pfk.Id)
|
||||
if err != nil {
|
||||
return nil, nil, 0, 0, err
|
||||
}
|
||||
transIncomingRows, _, err := s.Repository.FetchSapronakTransfers(ctx, pfk.KandangId, start, end)
|
||||
chickinUsageDetailsRows, err := s.Repository.FetchSapronakChickinUsageDetails(ctx, pfk.Id)
|
||||
if err != nil {
|
||||
return nil, nil, 0, 0, err
|
||||
}
|
||||
adjIncomingRows, adjOutgoingRows, err := s.Repository.FetchSapronakAdjustments(ctx, pfk.KandangId)
|
||||
if err != nil {
|
||||
return nil, nil, 0, 0, err
|
||||
}
|
||||
transIncomingRows, transOutgoingRows, err := s.Repository.FetchSapronakTransfers(ctx, pfk.KandangId)
|
||||
if err != nil {
|
||||
return nil, nil, 0, 0, err
|
||||
}
|
||||
@@ -414,16 +362,50 @@ func (s sapronakService) buildSapronakItems(ctx context.Context, pfk entity.Proj
|
||||
return strings.ToUpper(f) == filterFlag
|
||||
}
|
||||
|
||||
incoming, usage := mapIncomingUsage(incomingRows, usageRows)
|
||||
// For project flocks with category GROWING, pullet usage from chickin
|
||||
// should not be counted yet. Only when category is LAYING we allow
|
||||
// pullet usage to contribute to qty_used.
|
||||
isLaying := strings.EqualFold(string(pfk.ProjectFlock.Category), string(utils.ProjectFlockCategoryLaying))
|
||||
|
||||
if !isLaying {
|
||||
filteredUsage := make([]repository.SapronakUsageRow, 0, len(chickinUsageRows))
|
||||
for _, row := range chickinUsageRows {
|
||||
if strings.ToUpper(row.Flag) == "DOC" {
|
||||
filteredUsage = append(filteredUsage, row)
|
||||
}
|
||||
}
|
||||
chickinUsageRows = filteredUsage
|
||||
|
||||
filteredDetail := make(map[uint][]repository.SapronakDetailRow, len(chickinUsageDetailsRows))
|
||||
for pid, rows := range chickinUsageDetailsRows {
|
||||
for _, d := range rows {
|
||||
if strings.ToUpper(d.Flag) == "DOC" {
|
||||
filteredDetail[pid] = append(filteredDetail[pid], d)
|
||||
}
|
||||
}
|
||||
}
|
||||
chickinUsageDetailsRows = filteredDetail
|
||||
}
|
||||
|
||||
allUsageRows := append(usageRows, chickinUsageRows...)
|
||||
incoming, usage := mapIncomingUsage(incomingRows, allUsageRows)
|
||||
itemMap := make(map[uint]dto.SapronakItemDTO, len(incoming)+len(usage))
|
||||
groupMap := make(map[string]*dto.SapronakGroupDTO)
|
||||
|
||||
detailMaps := buildSapronakDetails(incomingDetailsRows, usageDetailsRows, adjIncomingRows, adjOutgoingRows, transIncomingRows)
|
||||
for pid, rows := range chickinUsageDetailsRows {
|
||||
if len(rows) == 0 {
|
||||
continue
|
||||
}
|
||||
usageDetailsRows[pid] = append(usageDetailsRows[pid], rows...)
|
||||
}
|
||||
|
||||
detailMaps := buildSapronakDetails(incomingDetailsRows, usageDetailsRows, adjIncomingRows, adjOutgoingRows, transIncomingRows, transOutgoingRows)
|
||||
incomingDetails := detailMaps.Incoming
|
||||
usageDetails := detailMaps.Usage
|
||||
adjIncoming := detailMaps.AdjIncoming
|
||||
adjOutgoing := detailMaps.AdjOutgoing
|
||||
transIncoming := detailMaps.TransferIn
|
||||
transOutgoing := detailMaps.TransferOut
|
||||
|
||||
ensureGroup := func(flag string) *dto.SapronakGroupDTO {
|
||||
if g, ok := groupMap[flag]; ok {
|
||||
@@ -670,6 +652,26 @@ func (s sapronakService) buildSapronakItems(ctx context.Context, pfk entity.Proj
|
||||
}
|
||||
}
|
||||
|
||||
for productID, details := range transOutgoing {
|
||||
flag := ""
|
||||
name := ""
|
||||
if item, ok := itemMap[productID]; ok {
|
||||
flag = item.Flag
|
||||
name = item.ProductName
|
||||
}
|
||||
if !matchesFlag(flag) {
|
||||
continue
|
||||
}
|
||||
group := ensureGroup(flag)
|
||||
for _, d := range details {
|
||||
d.Flag = flag
|
||||
d.ProductName = name
|
||||
group.Items = append(group.Items, d)
|
||||
group.TotalKeluar += d.QtyKeluar
|
||||
group.SaldoAkhir -= d.QtyKeluar
|
||||
}
|
||||
}
|
||||
|
||||
groups := make([]dto.SapronakGroupDTO, 0, len(groupMap))
|
||||
for _, g := range groupMap {
|
||||
groups = append(groups, *g)
|
||||
|
||||
@@ -1,125 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/modules/closings/dto"
|
||||
)
|
||||
|
||||
type SapronakFormatter interface {
|
||||
ProjectPayload(reports []dto.SapronakReportDTO, flag string) dto.SapronakProjectAggregatedDTO
|
||||
KandangPayload(report *dto.SapronakReportDTO, flag string) dto.SapronakProjectAggregatedDTO
|
||||
}
|
||||
|
||||
type sapronakFormatter struct{}
|
||||
|
||||
func NewSapronakFormatter() SapronakFormatter {
|
||||
return &sapronakFormatter{}
|
||||
}
|
||||
|
||||
func (f *sapronakFormatter) ProjectPayload(reports []dto.SapronakReportDTO, flag string) dto.SapronakProjectAggregatedDTO {
|
||||
result := dto.SapronakProjectAggregatedDTO{}
|
||||
|
||||
if len(reports) == 0 {
|
||||
return result
|
||||
}
|
||||
|
||||
rep := reports[0]
|
||||
return f.mapFromReport(&rep, flag)
|
||||
}
|
||||
|
||||
func (f *sapronakFormatter) KandangPayload(report *dto.SapronakReportDTO, flag string) dto.SapronakProjectAggregatedDTO {
|
||||
return f.mapFromReport(report, flag)
|
||||
}
|
||||
|
||||
func (f *sapronakFormatter) mapFromReport(report *dto.SapronakReportDTO, flag string) dto.SapronakProjectAggregatedDTO {
|
||||
result := dto.SapronakProjectAggregatedDTO{}
|
||||
|
||||
if report == nil {
|
||||
report = &dto.SapronakReportDTO{}
|
||||
}
|
||||
|
||||
filter := strings.ToUpper(strings.TrimSpace(flag))
|
||||
|
||||
byFlag := map[string]**dto.SapronakCategoryDTO{}
|
||||
if filter == "" || filter == "DOC" {
|
||||
result.Doc = &dto.SapronakCategoryDTO{Rows: make([]dto.SapronakCategoryRowDTO, 0)}
|
||||
byFlag["DOC"] = &result.Doc
|
||||
}
|
||||
if filter == "" || filter == "OVK" {
|
||||
result.Ovk = &dto.SapronakCategoryDTO{Rows: make([]dto.SapronakCategoryRowDTO, 0),}
|
||||
byFlag["OVK"] = &result.Ovk
|
||||
}
|
||||
if filter == "" || filter == "PAKAN" {
|
||||
result.Pakan = &dto.SapronakCategoryDTO{Rows: make([]dto.SapronakCategoryRowDTO, 0),}
|
||||
byFlag["PAKAN"] = &result.Pakan
|
||||
}
|
||||
|
||||
formatDate := func(t *time.Time) string {
|
||||
if t == nil {
|
||||
return ""
|
||||
}
|
||||
return t.Format("02-Jan-2006")
|
||||
}
|
||||
|
||||
for _, group := range report.Groups {
|
||||
flag := strings.ToUpper(group.Flag)
|
||||
ptr := byFlag[flag]
|
||||
if ptr == nil || *ptr == nil {
|
||||
continue
|
||||
}
|
||||
target := *ptr
|
||||
for idx, item := range group.Items {
|
||||
qtyUsed := item.QtyKeluar
|
||||
if qtyUsed == 0 {
|
||||
qtyUsed = item.QtyMasuk
|
||||
}
|
||||
|
||||
target.Rows = append(target.Rows, dto.SapronakCategoryRowDTO{
|
||||
ID: idx + 1,
|
||||
Date: formatDate(item.Tanggal),
|
||||
ReferenceNumber: item.NoReferensi,
|
||||
QtyIn: item.QtyMasuk,
|
||||
QtyOut: item.QtyKeluar,
|
||||
QtyUsed: qtyUsed,
|
||||
Description: item.ProductName,
|
||||
ProductCategory: item.ProductName,
|
||||
UnitPrice: item.Harga,
|
||||
TotalAmount: item.Nilai,
|
||||
Notes: "-",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
buildTotals := func(cat *dto.SapronakCategoryDTO, label string) {
|
||||
if cat == nil {
|
||||
return
|
||||
}
|
||||
var qtyIn, qtyOut, qtyUsed, total float64
|
||||
for _, r := range cat.Rows {
|
||||
qtyIn += r.QtyIn
|
||||
qtyOut += r.QtyOut
|
||||
qtyUsed += r.QtyUsed
|
||||
total += r.TotalAmount
|
||||
}
|
||||
avg := 0.0
|
||||
if qtyIn > 0 {
|
||||
avg = total / qtyIn
|
||||
}
|
||||
cat.Total = dto.SapronakCategoryTotalDTO{
|
||||
Label: label,
|
||||
QtyIn: qtyIn,
|
||||
QtyOut: qtyOut,
|
||||
QtyUsed: qtyUsed,
|
||||
AvgUnitPrice: avg,
|
||||
TotalAmount: total,
|
||||
}
|
||||
}
|
||||
|
||||
buildTotals(result.Doc, "TOTAL DOC")
|
||||
buildTotals(result.Ovk, "TOTAL OVK")
|
||||
buildTotals(result.Pakan, "TOTAL PAKAN")
|
||||
|
||||
return result
|
||||
}
|
||||
Reference in New Issue
Block a user