mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 13:31:56 +00:00
feat/BE/US-284/TASK-,299-Create API (GET ONE in tab Perhitungan Sapronak),add filtering by flag
This commit is contained in:
@@ -130,18 +130,19 @@ func (u *ClosingController) GetPenjualan(c *fiber.Ctx) error {
|
||||
|
||||
func (u *ClosingController) GetSapronakByProject(c *fiber.Ctx) error {
|
||||
param := c.Params("project_flock_id")
|
||||
flag := c.Query("flag", "")
|
||||
|
||||
projectID, err := strconv.Atoi(param)
|
||||
if err != nil || projectID <= 0 {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid project_flock_id")
|
||||
}
|
||||
|
||||
result, err := u.SapronakService.GetSapronakByProject(c, uint(projectID))
|
||||
result, err := u.SapronakService.GetSapronakByProject(c, uint(projectID), flag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
payload := u.SapronakFormatter.ProjectPayload(result)
|
||||
payload := u.SapronakFormatter.ProjectPayload(result, flag)
|
||||
|
||||
return c.Status(fiber.StatusOK).
|
||||
JSON(response.Success{
|
||||
@@ -155,6 +156,7 @@ func (u *ClosingController) GetSapronakByProject(c *fiber.Ctx) error {
|
||||
func (u *ClosingController) GetSapronakByKandang(c *fiber.Ctx) error {
|
||||
projectParam := c.Params("project_flock_id")
|
||||
kandangParam := c.Params("project_flock_kandang_id")
|
||||
flag := c.Query("flag", "")
|
||||
|
||||
projectID, err := strconv.Atoi(projectParam)
|
||||
if err != nil || projectID <= 0 {
|
||||
@@ -165,12 +167,12 @@ func (u *ClosingController) GetSapronakByKandang(c *fiber.Ctx) error {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid project_flock_kandang_id")
|
||||
}
|
||||
|
||||
result, err := u.SapronakService.GetSapronakByKandang(c, uint(projectID), uint(pfkID))
|
||||
result, err := u.SapronakService.GetSapronakByKandang(c, uint(projectID), uint(pfkID), flag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
payload := u.SapronakFormatter.KandangPayload(result)
|
||||
payload := u.SapronakFormatter.KandangPayload(result, flag)
|
||||
|
||||
return c.Status(fiber.StatusOK).
|
||||
JSON(response.Success{
|
||||
|
||||
@@ -82,7 +82,7 @@ type SapronakCategoryDTO struct {
|
||||
}
|
||||
|
||||
type SapronakProjectAggregatedDTO struct {
|
||||
Doc SapronakCategoryDTO `json:"doc"`
|
||||
Ovk SapronakCategoryDTO `json:"ovk"`
|
||||
Pakan SapronakCategoryDTO `json:"pakan"`
|
||||
Doc *SapronakCategoryDTO `json:"doc,omitempty"`
|
||||
Ovk *SapronakCategoryDTO `json:"ovk,omitempty"`
|
||||
Pakan *SapronakCategoryDTO `json:"pakan,omitempty"`
|
||||
}
|
||||
|
||||
@@ -17,8 +17,8 @@ import (
|
||||
)
|
||||
|
||||
type SapronakService interface {
|
||||
GetSapronakByProject(ctx *fiber.Ctx, projectFlockID uint) ([]dto.SapronakReportDTO, error)
|
||||
GetSapronakByKandang(ctx *fiber.Ctx, projectFlockID uint, pfkID uint) (*dto.SapronakReportDTO, error)
|
||||
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.SapronakQuery) ([]dto.SapronakReportDTO, error)
|
||||
}
|
||||
|
||||
@@ -43,13 +43,14 @@ func (s sapronakService) GetSapronakReport(c *fiber.Ctx, params *validation.Sapr
|
||||
return s.computeSapronakReports(c.Context(), params)
|
||||
}
|
||||
|
||||
func (s sapronakService) GetSapronakByProject(c *fiber.Ctx, projectFlockID uint) ([]dto.SapronakReportDTO, error) {
|
||||
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.SapronakQuery{
|
||||
ProjectFlockID: projectFlockID,
|
||||
Status: "all",
|
||||
Flag: flag,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -62,7 +63,7 @@ func (s sapronakService) GetSapronakByProject(c *fiber.Ctx, projectFlockID uint)
|
||||
return []dto.SapronakReportDTO{combined}, nil
|
||||
}
|
||||
|
||||
func (s sapronakService) GetSapronakByKandang(c *fiber.Ctx, projectFlockID uint, pfkID uint) (*dto.SapronakReportDTO, error) {
|
||||
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")
|
||||
}
|
||||
@@ -71,6 +72,7 @@ func (s sapronakService) GetSapronakByKandang(c *fiber.Ctx, projectFlockID uint,
|
||||
ProjectFlockID: projectFlockID,
|
||||
ProjectFlockKandangID: pfkID,
|
||||
Status: "all",
|
||||
Flag: flag,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -130,7 +132,7 @@ func (s sapronakService) computeSapronakReports(ctx context.Context, params *val
|
||||
endPtr = &endCopy
|
||||
}
|
||||
|
||||
items, groups, totalIncoming, totalUsage, err := s.buildSapronakItems(ctx, pfk, startPtr, endPtr)
|
||||
items, groups, totalIncoming, totalUsage, err := s.buildSapronakItems(ctx, pfk, startPtr, endPtr, 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")
|
||||
@@ -310,7 +312,75 @@ func (s sapronakService) computeStatusAndNextStart(pfks []entity.ProjectFlockKan
|
||||
return statusMap, nextStartMap
|
||||
}
|
||||
|
||||
func (s sapronakService) buildSapronakItems(ctx context.Context, pfk entity.ProjectFlockKandang, start, end *time.Time) ([]dto.SapronakItemDTO, []dto.SapronakGroupDTO, float64, float64, error) {
|
||||
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
|
||||
}
|
||||
|
||||
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,
|
||||
) 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),
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, nil, 0, 0, err
|
||||
@@ -336,10 +406,24 @@ func (s sapronakService) buildSapronakItems(ctx context.Context, pfk entity.Proj
|
||||
return nil, nil, 0, 0, err
|
||||
}
|
||||
|
||||
filterFlag := strings.ToUpper(strings.TrimSpace(flagFilter))
|
||||
matchesFlag := func(f string) bool {
|
||||
if filterFlag == "" {
|
||||
return true
|
||||
}
|
||||
return strings.ToUpper(f) == filterFlag
|
||||
}
|
||||
|
||||
incoming, usage := mapIncomingUsage(incomingRows, usageRows)
|
||||
itemMap := make(map[uint]dto.SapronakItemDTO, len(incoming)+len(usage))
|
||||
groupMap := make(map[string]*dto.SapronakGroupDTO)
|
||||
details := buildSapronakDetails(incomingDetailsRows, usageDetailsRows, adjIncomingRows, adjOutgoingRows, transIncomingRows)
|
||||
|
||||
detailMaps := buildSapronakDetails(incomingDetailsRows, usageDetailsRows, adjIncomingRows, adjOutgoingRows, transIncomingRows)
|
||||
incomingDetails := detailMaps.Incoming
|
||||
usageDetails := detailMaps.Usage
|
||||
adjIncoming := detailMaps.AdjIncoming
|
||||
adjOutgoing := detailMaps.AdjOutgoing
|
||||
transIncoming := detailMaps.TransferIn
|
||||
|
||||
ensureGroup := func(flag string) *dto.SapronakGroupDTO {
|
||||
if g, ok := groupMap[flag]; ok {
|
||||
@@ -350,6 +434,9 @@ func (s sapronakService) buildSapronakItems(ctx context.Context, pfk entity.Proj
|
||||
}
|
||||
|
||||
for _, row := range incoming {
|
||||
if !matchesFlag(row.Flag) {
|
||||
continue
|
||||
}
|
||||
avgPrice := row.DefaultPrice
|
||||
if row.Qty > 0 && row.Value > 0 {
|
||||
avgPrice = row.Value / row.Qty
|
||||
@@ -367,6 +454,9 @@ func (s sapronakService) buildSapronakItems(ctx context.Context, pfk entity.Proj
|
||||
}
|
||||
|
||||
for _, row := range usage {
|
||||
if !matchesFlag(row.Flag) {
|
||||
continue
|
||||
}
|
||||
existing := itemMap[row.ProductID]
|
||||
price := existing.AveragePrice
|
||||
if price == 0 {
|
||||
@@ -396,6 +486,9 @@ func (s sapronakService) buildSapronakItems(ctx context.Context, pfk entity.Proj
|
||||
|
||||
for productID, details := range adjIncoming {
|
||||
for _, d := range details {
|
||||
if !matchesFlag(d.Flag) {
|
||||
continue
|
||||
}
|
||||
existing := itemMap[productID]
|
||||
if existing.Flag == "" {
|
||||
existing.Flag = d.Flag
|
||||
@@ -419,6 +512,9 @@ func (s sapronakService) buildSapronakItems(ctx context.Context, pfk entity.Proj
|
||||
|
||||
for productID, details := range adjOutgoing {
|
||||
for _, d := range details {
|
||||
if !matchesFlag(d.Flag) {
|
||||
continue
|
||||
}
|
||||
existing := itemMap[productID]
|
||||
if existing.Flag == "" {
|
||||
existing.Flag = d.Flag
|
||||
@@ -439,6 +535,9 @@ func (s sapronakService) buildSapronakItems(ctx context.Context, pfk entity.Proj
|
||||
|
||||
for productID, details := range transIncoming {
|
||||
for _, d := range details {
|
||||
if !matchesFlag(d.Flag) {
|
||||
continue
|
||||
}
|
||||
existing := itemMap[productID]
|
||||
if existing.Flag == "" {
|
||||
existing.Flag = d.Flag
|
||||
@@ -475,6 +574,9 @@ func (s sapronakService) buildSapronakItems(ctx context.Context, pfk entity.Proj
|
||||
flag = item.Flag
|
||||
name = item.ProductName
|
||||
}
|
||||
if !matchesFlag(flag) {
|
||||
continue
|
||||
}
|
||||
group := ensureGroup(flag)
|
||||
for _, d := range details {
|
||||
d.Flag = flag
|
||||
@@ -493,6 +595,9 @@ func (s sapronakService) buildSapronakItems(ctx context.Context, pfk entity.Proj
|
||||
flag = item.Flag
|
||||
name = item.ProductName
|
||||
}
|
||||
if !matchesFlag(flag) {
|
||||
continue
|
||||
}
|
||||
group := ensureGroup(flag)
|
||||
for _, d := range details {
|
||||
d.Flag = flag
|
||||
@@ -511,6 +616,9 @@ func (s sapronakService) buildSapronakItems(ctx context.Context, pfk entity.Proj
|
||||
flag = item.Flag
|
||||
name = item.ProductName
|
||||
}
|
||||
if !matchesFlag(flag) {
|
||||
continue
|
||||
}
|
||||
group := ensureGroup(flag)
|
||||
for _, d := range details {
|
||||
d.Flag = flag
|
||||
@@ -528,6 +636,9 @@ func (s sapronakService) buildSapronakItems(ctx context.Context, pfk entity.Proj
|
||||
flag = item.Flag
|
||||
name = item.ProductName
|
||||
}
|
||||
if !matchesFlag(flag) {
|
||||
continue
|
||||
}
|
||||
group := ensureGroup(flag)
|
||||
for _, d := range details {
|
||||
d.Flag = flag
|
||||
@@ -545,6 +656,9 @@ func (s sapronakService) buildSapronakItems(ctx context.Context, pfk entity.Proj
|
||||
flag = item.Flag
|
||||
name = item.ProductName
|
||||
}
|
||||
if !matchesFlag(flag) {
|
||||
continue
|
||||
}
|
||||
group := ensureGroup(flag)
|
||||
for _, d := range details {
|
||||
d.Flag = flag
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
)
|
||||
|
||||
type SapronakFormatter interface {
|
||||
ProjectPayload(reports []dto.SapronakReportDTO) dto.SapronakProjectAggregatedDTO
|
||||
KandangPayload(report *dto.SapronakReportDTO) dto.SapronakProjectAggregatedDTO
|
||||
ProjectPayload(reports []dto.SapronakReportDTO, flag string) dto.SapronakProjectAggregatedDTO
|
||||
KandangPayload(report *dto.SapronakReportDTO, flag string) dto.SapronakProjectAggregatedDTO
|
||||
}
|
||||
|
||||
type sapronakFormatter struct{}
|
||||
@@ -18,40 +18,42 @@ func NewSapronakFormatter() SapronakFormatter {
|
||||
return &sapronakFormatter{}
|
||||
}
|
||||
|
||||
func (f *sapronakFormatter) ProjectPayload(reports []dto.SapronakReportDTO) dto.SapronakProjectAggregatedDTO {
|
||||
result := dto.SapronakProjectAggregatedDTO{
|
||||
Doc: dto.SapronakCategoryDTO{},
|
||||
Ovk: dto.SapronakCategoryDTO{},
|
||||
Pakan: dto.SapronakCategoryDTO{},
|
||||
}
|
||||
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)
|
||||
return f.mapFromReport(&rep, flag)
|
||||
}
|
||||
|
||||
func (f *sapronakFormatter) KandangPayload(report *dto.SapronakReportDTO) dto.SapronakProjectAggregatedDTO {
|
||||
return f.mapFromReport(report)
|
||||
func (f *sapronakFormatter) KandangPayload(report *dto.SapronakReportDTO, flag string) dto.SapronakProjectAggregatedDTO {
|
||||
return f.mapFromReport(report, flag)
|
||||
}
|
||||
|
||||
func (f *sapronakFormatter) mapFromReport(report *dto.SapronakReportDTO) dto.SapronakProjectAggregatedDTO {
|
||||
result := dto.SapronakProjectAggregatedDTO{
|
||||
Doc: dto.SapronakCategoryDTO{},
|
||||
Ovk: dto.SapronakCategoryDTO{},
|
||||
Pakan: dto.SapronakCategoryDTO{},
|
||||
}
|
||||
func (f *sapronakFormatter) mapFromReport(report *dto.SapronakReportDTO, flag string) dto.SapronakProjectAggregatedDTO {
|
||||
result := dto.SapronakProjectAggregatedDTO{}
|
||||
|
||||
if report == nil {
|
||||
return result
|
||||
}
|
||||
|
||||
byFlag := map[string]*dto.SapronakCategoryDTO{
|
||||
"DOC": &result.Doc,
|
||||
"OVK": &result.Ovk,
|
||||
"PAKAN": &result.Pakan,
|
||||
filter := strings.ToUpper(strings.TrimSpace(flag))
|
||||
|
||||
byFlag := map[string]**dto.SapronakCategoryDTO{}
|
||||
if filter == "" || filter == "DOC" {
|
||||
result.Doc = &dto.SapronakCategoryDTO{}
|
||||
byFlag["DOC"] = &result.Doc
|
||||
}
|
||||
if filter == "" || filter == "OVK" {
|
||||
result.Ovk = &dto.SapronakCategoryDTO{}
|
||||
byFlag["OVK"] = &result.Ovk
|
||||
}
|
||||
if filter == "" || filter == "PAKAN" {
|
||||
result.Pakan = &dto.SapronakCategoryDTO{}
|
||||
byFlag["PAKAN"] = &result.Pakan
|
||||
}
|
||||
|
||||
formatDate := func(t *time.Time) string {
|
||||
@@ -63,10 +65,11 @@ func (f *sapronakFormatter) mapFromReport(report *dto.SapronakReportDTO) dto.Sap
|
||||
|
||||
for _, group := range report.Groups {
|
||||
flag := strings.ToUpper(group.Flag)
|
||||
target := byFlag[flag]
|
||||
if target == nil {
|
||||
ptr := byFlag[flag]
|
||||
if ptr == nil || *ptr == nil {
|
||||
continue
|
||||
}
|
||||
target := *ptr
|
||||
for idx, item := range group.Items {
|
||||
qtyUsed := item.QtyKeluar
|
||||
if qtyUsed == 0 {
|
||||
@@ -90,6 +93,9 @@ func (f *sapronakFormatter) mapFromReport(report *dto.SapronakReportDTO) dto.Sap
|
||||
}
|
||||
|
||||
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
|
||||
@@ -111,9 +117,9 @@ func (f *sapronakFormatter) mapFromReport(report *dto.SapronakReportDTO) dto.Sap
|
||||
}
|
||||
}
|
||||
|
||||
buildTotals(&result.Doc, "TOTAL DOC")
|
||||
buildTotals(&result.Ovk, "TOTAL OVK")
|
||||
buildTotals(&result.Pakan, "TOTAL PAKAN")
|
||||
buildTotals(result.Doc, "TOTAL DOC")
|
||||
buildTotals(result.Ovk, "TOTAL OVK")
|
||||
buildTotals(result.Pakan, "TOTAL PAKAN")
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -5,5 +5,5 @@ type SapronakQuery struct {
|
||||
KandangID uint `query:"kandang_id" validate:"omitempty,gt=0"`
|
||||
ProjectFlockKandangID uint `query:"project_flock_kandang_id" validate:"omitempty,gt=0"`
|
||||
Status string `query:"status" validate:"omitempty,oneof=active closing all"`
|
||||
Debug bool `query:"debug"`
|
||||
Flag string `query:"flag" validate:"omitempty,oneof=DOC OVK PAKAN doc ovk pakan"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user