mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 13:31:56 +00:00
686 lines
20 KiB
Go
686 lines
20 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/go-playground/validator/v10"
|
|
"github.com/gofiber/fiber/v2"
|
|
"github.com/sirupsen/logrus"
|
|
|
|
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
|
"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)
|
|
}
|
|
|
|
type sapronakService struct {
|
|
Log *logrus.Logger
|
|
Validate *validator.Validate
|
|
Repository repository.ClosingRepository
|
|
ProjectFlockKandangRepo projectflockRepository.ProjectFlockKandangRepository
|
|
}
|
|
|
|
func NewSapronakService(
|
|
repo repository.ClosingRepository,
|
|
pfkRepo projectflockRepository.ProjectFlockKandangRepository,
|
|
validate *validator.Validate,
|
|
) SapronakService {
|
|
return &sapronakService{
|
|
Log: utils.Log,
|
|
Validate: validate,
|
|
Repository: repo,
|
|
ProjectFlockKandangRepo: pfkRepo,
|
|
}
|
|
}
|
|
|
|
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")
|
|
}
|
|
reports, err := s.computeSapronakReports(c.Context(), &validation.CountSapronakQuery{
|
|
ProjectFlockID: projectFlockID,
|
|
Status: "all",
|
|
Flag: flag,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(reports) <= 1 {
|
|
return reports, nil
|
|
}
|
|
|
|
combined := s.combineSapronakReports(reports, projectFlockID)
|
|
return []dto.SapronakReportDTO{combined}, nil
|
|
}
|
|
|
|
func (s sapronakService) GetSapronakByKandang(c *fiber.Ctx, projectFlockID uint, pfkID uint, flag string) (*dto.SapronakReportDTO, error) {
|
|
if projectFlockID == 0 || pfkID == 0 {
|
|
return nil, fiber.NewError(fiber.StatusBadRequest, "project_flock_id and project_flock_kandang_id are required")
|
|
}
|
|
|
|
results, err := s.computeSapronakReports(c.Context(), &validation.CountSapronakQuery{
|
|
ProjectFlockID: projectFlockID,
|
|
ProjectFlockKandangID: pfkID,
|
|
Status: "all",
|
|
Flag: flag,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, res := range results {
|
|
if res.ProjectFlockID == projectFlockID && res.ProjectFlockKandangID == pfkID {
|
|
return &res, nil
|
|
}
|
|
}
|
|
|
|
return nil, fiber.NewError(fiber.StatusNotFound, "Sapronak for kandang not found")
|
|
}
|
|
|
|
func (s sapronakService) computeSapronakReports(ctx context.Context, params *validation.CountSapronakQuery) ([]dto.SapronakReportDTO, error) {
|
|
pfks, err := s.loadProjectFlockKandangs(ctx, params)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(pfks) == 0 {
|
|
return []dto.SapronakReportDTO{}, nil
|
|
}
|
|
|
|
filterStatus := strings.ToLower(strings.TrimSpace(params.Status))
|
|
if filterStatus == "" {
|
|
filterStatus = "all"
|
|
}
|
|
|
|
results := make([]dto.SapronakReportDTO, 0, len(pfks))
|
|
for _, pfk := range pfks {
|
|
status := "closing"
|
|
if pfk.ClosedAt == nil {
|
|
status = "active"
|
|
}
|
|
|
|
if (filterStatus == "active" && status != "active") || (filterStatus == "closing" && status != "closing") {
|
|
continue
|
|
}
|
|
|
|
// 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")
|
|
}
|
|
|
|
results = append(results, dto.SapronakReportDTO{
|
|
ProjectFlockKandangID: pfk.Id,
|
|
ProjectFlockID: pfk.ProjectFlockId,
|
|
ProjectName: pfk.ProjectFlock.FlockName,
|
|
KandangID: pfk.KandangId,
|
|
KandangName: pfk.Kandang.Name,
|
|
Period: pfk.Period,
|
|
Status: status,
|
|
StartDate: nil,
|
|
EndDate: nil,
|
|
TotalIncomingValue: totalIncoming,
|
|
TotalUsageValue: totalUsage,
|
|
Items: items,
|
|
Groups: groups,
|
|
})
|
|
}
|
|
|
|
return results, nil
|
|
}
|
|
|
|
func (s sapronakService) loadProjectFlockKandangs(ctx context.Context, params *validation.CountSapronakQuery) ([]entity.ProjectFlockKandang, error) {
|
|
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) combineSapronakReports(reports []dto.SapronakReportDTO, projectID uint) dto.SapronakReportDTO {
|
|
if len(reports) == 0 {
|
|
return dto.SapronakReportDTO{}
|
|
}
|
|
|
|
var (
|
|
totalIncoming float64
|
|
totalUsage float64
|
|
projectName = reports[0].ProjectName
|
|
)
|
|
|
|
itemMap := make(map[uint]dto.SapronakItemDTO)
|
|
groupMap := make(map[string]*dto.SapronakGroupDTO)
|
|
|
|
ensureGroup := func(flag string) *dto.SapronakGroupDTO {
|
|
if g, ok := groupMap[flag]; ok {
|
|
return g
|
|
}
|
|
groupMap[flag] = &dto.SapronakGroupDTO{Flag: flag}
|
|
return groupMap[flag]
|
|
}
|
|
|
|
for _, r := range reports {
|
|
totalIncoming += r.TotalIncomingValue
|
|
totalUsage += r.TotalUsageValue
|
|
|
|
for _, it := range r.Items {
|
|
cur := itemMap[it.ProductID]
|
|
if cur.ProductID == 0 {
|
|
cur.ProductID = it.ProductID
|
|
cur.ProductName = it.ProductName
|
|
cur.Flag = it.Flag
|
|
}
|
|
cur.IncomingQty += it.IncomingQty
|
|
cur.IncomingValue += it.IncomingValue
|
|
cur.UsageQty += it.UsageQty
|
|
cur.UsageValue += it.UsageValue
|
|
if cur.IncomingQty >= cur.UsageQty {
|
|
cur.RemainingQty = cur.IncomingQty - cur.UsageQty
|
|
} else {
|
|
cur.RemainingQty = 0
|
|
}
|
|
if cur.IncomingQty > 0 {
|
|
cur.AveragePrice = cur.IncomingValue / cur.IncomingQty
|
|
} else {
|
|
cur.AveragePrice = it.AveragePrice
|
|
}
|
|
itemMap[it.ProductID] = cur
|
|
}
|
|
|
|
for _, g := range r.Groups {
|
|
agg := ensureGroup(g.Flag)
|
|
agg.TotalMasuk += g.TotalMasuk
|
|
agg.TotalKeluar += g.TotalKeluar
|
|
agg.SaldoAkhir += g.SaldoAkhir
|
|
agg.TotalNilai += g.TotalNilai
|
|
agg.Items = append(agg.Items, g.Items...)
|
|
}
|
|
}
|
|
|
|
items := make([]dto.SapronakItemDTO, 0, len(itemMap))
|
|
for _, it := range itemMap {
|
|
items = append(items, it)
|
|
}
|
|
|
|
groups := make([]dto.SapronakGroupDTO, 0, len(groupMap))
|
|
for _, g := range groupMap {
|
|
groups = append(groups, *g)
|
|
}
|
|
|
|
return dto.SapronakReportDTO{
|
|
ProjectFlockID: projectID,
|
|
ProjectName: projectName,
|
|
Status: "combined",
|
|
StartDate: nil,
|
|
TotalIncomingValue: totalIncoming,
|
|
TotalUsageValue: totalUsage,
|
|
Items: items,
|
|
Groups: groups,
|
|
}
|
|
}
|
|
|
|
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 {
|
|
incoming[row.ProductID] = row
|
|
}
|
|
usage := make(map[uint]repository.SapronakUsageRow, len(usageRows))
|
|
for _, row := range usageRows {
|
|
usage[row.ProductID] = row
|
|
}
|
|
return incoming, usage
|
|
}
|
|
|
|
type sapronakDetailMaps struct {
|
|
Incoming map[uint][]dto.SapronakDetailDTO
|
|
Usage map[uint][]dto.SapronakDetailDTO
|
|
AdjIncoming map[uint][]dto.SapronakDetailDTO
|
|
AdjOutgoing map[uint][]dto.SapronakDetailDTO
|
|
TransferIn map[uint][]dto.SapronakDetailDTO
|
|
TransferOut map[uint][]dto.SapronakDetailDTO
|
|
}
|
|
|
|
func buildSapronakDetails(
|
|
incomingRows map[uint][]repository.SapronakDetailRow,
|
|
usageRows map[uint][]repository.SapronakDetailRow,
|
|
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),
|
|
Usage: make(map[uint][]dto.SapronakDetailDTO),
|
|
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) {
|
|
for pid, rows := range src {
|
|
for _, r := range rows {
|
|
d := dto.SapronakDetailDTO{
|
|
ProductID: r.ProductID,
|
|
ProductName: r.ProductName,
|
|
Flag: r.Flag,
|
|
Tanggal: r.Date,
|
|
NoReferensi: r.Reference,
|
|
JenisTransaksi: jenis,
|
|
Harga: r.Price,
|
|
}
|
|
if masuk {
|
|
d.QtyMasuk = r.QtyIn
|
|
d.Nilai = r.QtyIn * r.Price
|
|
} else {
|
|
d.QtyKeluar = r.QtyOut
|
|
d.Nilai = r.QtyOut * r.Price
|
|
}
|
|
target[pid] = append(target[pid], d)
|
|
}
|
|
}
|
|
}
|
|
|
|
addRows(result.Incoming, incomingRows, "Pembelian", true)
|
|
addRows(result.Usage, usageRows, "Pemakaian", false)
|
|
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) {
|
|
// 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)
|
|
if err != nil {
|
|
return nil, nil, 0, 0, err
|
|
}
|
|
usageRows, err := s.Repository.FetchSapronakUsage(ctx, pfk.Id)
|
|
if err != nil {
|
|
return nil, nil, 0, 0, err
|
|
}
|
|
chickinUsageRows, err := s.Repository.FetchSapronakChickinUsage(ctx, pfk.Id)
|
|
if err != nil {
|
|
return nil, nil, 0, 0, err
|
|
}
|
|
usageDetailsRows, err := s.Repository.FetchSapronakUsageDetails(ctx, pfk.Id)
|
|
if err != nil {
|
|
return nil, nil, 0, 0, err
|
|
}
|
|
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
|
|
}
|
|
|
|
filterFlag := strings.ToUpper(strings.TrimSpace(flagFilter))
|
|
matchesFlag := func(f string) bool {
|
|
if filterFlag == "" {
|
|
return true
|
|
}
|
|
candidate := strings.ToUpper(f)
|
|
if filterFlag == "DOC" || filterFlag == "PULLET" {
|
|
return candidate == "DOC" || candidate == "PULLET"
|
|
}
|
|
return candidate == filterFlag
|
|
}
|
|
|
|
// 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)
|
|
|
|
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 {
|
|
return g
|
|
}
|
|
groupMap[flag] = &dto.SapronakGroupDTO{Flag: flag}
|
|
return groupMap[flag]
|
|
}
|
|
|
|
for _, row := range incoming {
|
|
if !matchesFlag(row.Flag) {
|
|
continue
|
|
}
|
|
avgPrice := row.DefaultPrice
|
|
if row.Qty > 0 && row.Value > 0 {
|
|
avgPrice = row.Value / row.Qty
|
|
}
|
|
|
|
itemMap[row.ProductID] = dto.SapronakItemDTO{
|
|
ProductID: row.ProductID,
|
|
ProductName: row.ProductName,
|
|
Flag: row.Flag,
|
|
IncomingQty: row.Qty,
|
|
IncomingValue: row.Value,
|
|
RemainingQty: row.Qty,
|
|
AveragePrice: avgPrice,
|
|
}
|
|
}
|
|
|
|
for _, row := range usage {
|
|
if !matchesFlag(row.Flag) {
|
|
continue
|
|
}
|
|
existing := itemMap[row.ProductID]
|
|
price := existing.AveragePrice
|
|
if price == 0 {
|
|
price = row.DefaultPrice
|
|
}
|
|
|
|
usageValue := row.Qty * price
|
|
|
|
existing.ProductID = row.ProductID
|
|
if existing.ProductName == "" {
|
|
existing.ProductName = row.ProductName
|
|
}
|
|
if existing.Flag == "" {
|
|
existing.Flag = row.Flag
|
|
}
|
|
existing.AveragePrice = price
|
|
existing.UsageQty += row.Qty
|
|
existing.UsageValue += usageValue
|
|
if existing.IncomingQty >= existing.UsageQty {
|
|
existing.RemainingQty = existing.IncomingQty - existing.UsageQty
|
|
} else {
|
|
existing.RemainingQty = 0
|
|
}
|
|
|
|
itemMap[row.ProductID] = existing
|
|
}
|
|
|
|
for productID, details := range adjIncoming {
|
|
for _, d := range details {
|
|
if !matchesFlag(d.Flag) {
|
|
continue
|
|
}
|
|
existing := itemMap[productID]
|
|
if existing.Flag == "" {
|
|
existing.Flag = d.Flag
|
|
}
|
|
if existing.ProductName == "" {
|
|
existing.ProductName = d.ProductName
|
|
}
|
|
existing.IncomingQty += d.QtyMasuk
|
|
existing.IncomingValue += d.Nilai
|
|
if existing.IncomingQty > 0 {
|
|
existing.AveragePrice = existing.IncomingValue / existing.IncomingQty
|
|
}
|
|
if existing.IncomingQty >= existing.UsageQty {
|
|
existing.RemainingQty = existing.IncomingQty - existing.UsageQty
|
|
} else {
|
|
existing.RemainingQty = 0
|
|
}
|
|
itemMap[productID] = existing
|
|
}
|
|
}
|
|
|
|
for productID, details := range adjOutgoing {
|
|
for _, d := range details {
|
|
if !matchesFlag(d.Flag) {
|
|
continue
|
|
}
|
|
existing := itemMap[productID]
|
|
if existing.Flag == "" {
|
|
existing.Flag = d.Flag
|
|
}
|
|
if existing.ProductName == "" {
|
|
existing.ProductName = d.ProductName
|
|
}
|
|
existing.UsageQty += d.QtyKeluar
|
|
existing.UsageValue += d.Nilai
|
|
if existing.IncomingQty >= existing.UsageQty {
|
|
existing.RemainingQty = existing.IncomingQty - existing.UsageQty
|
|
} else {
|
|
existing.RemainingQty = 0
|
|
}
|
|
itemMap[productID] = existing
|
|
}
|
|
}
|
|
|
|
for productID, details := range transIncoming {
|
|
for _, d := range details {
|
|
if !matchesFlag(d.Flag) {
|
|
continue
|
|
}
|
|
existing := itemMap[productID]
|
|
if existing.Flag == "" {
|
|
existing.Flag = d.Flag
|
|
}
|
|
if existing.ProductName == "" {
|
|
existing.ProductName = d.ProductName
|
|
}
|
|
existing.IncomingQty += d.QtyMasuk
|
|
existing.IncomingValue += d.Nilai
|
|
if existing.IncomingQty > 0 {
|
|
existing.AveragePrice = existing.IncomingValue / existing.IncomingQty
|
|
}
|
|
if existing.IncomingQty >= existing.UsageQty {
|
|
existing.RemainingQty = existing.IncomingQty - existing.UsageQty
|
|
} else {
|
|
existing.RemainingQty = 0
|
|
}
|
|
itemMap[productID] = existing
|
|
}
|
|
}
|
|
|
|
items := make([]dto.SapronakItemDTO, 0, len(itemMap))
|
|
var totalIncoming, totalUsage float64
|
|
for _, item := range itemMap {
|
|
totalIncoming += item.IncomingValue
|
|
totalUsage += item.UsageValue
|
|
items = append(items, item)
|
|
}
|
|
|
|
for productID, details := range incomingDetails {
|
|
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.TotalMasuk += d.QtyMasuk
|
|
group.TotalNilai += d.Nilai
|
|
group.SaldoAkhir += d.QtyMasuk
|
|
}
|
|
}
|
|
|
|
for productID, details := range adjIncoming {
|
|
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.TotalMasuk += d.QtyMasuk
|
|
group.TotalNilai += d.Nilai
|
|
group.SaldoAkhir += d.QtyMasuk
|
|
}
|
|
}
|
|
|
|
for productID, details := range usageDetails {
|
|
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
|
|
}
|
|
}
|
|
|
|
for productID, details := range adjOutgoing {
|
|
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
|
|
}
|
|
}
|
|
|
|
for productID, details := range transIncoming {
|
|
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.TotalMasuk += d.QtyMasuk
|
|
group.TotalNilai += d.Nilai
|
|
group.SaldoAkhir += d.QtyMasuk
|
|
}
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
return items, groups, totalIncoming, totalUsage, nil
|
|
}
|