From 705939bbf525a2e481ecb1ef92726dd00cda97ad Mon Sep 17 00:00:00 2001 From: ragilap Date: Wed, 28 Jan 2026 13:39:06 +0700 Subject: [PATCH 01/12] [FIX/BE-US] delete *1000 egg mass --- .../production/recordings/services/recording.service.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/modules/production/recordings/services/recording.service.go b/internal/modules/production/recordings/services/recording.service.go index 61f96e81..c3e21770 100644 --- a/internal/modules/production/recordings/services/recording.service.go +++ b/internal/modules/production/recordings/services/recording.service.go @@ -1692,7 +1692,8 @@ func (s *recordingService) computeAndUpdateMetrics(ctx context.Context, tx *gorm var eggMass float64 if remainingChick > 0 && totalEggWeightGrams > 0 { - eggMass = (totalEggWeightGrams / remainingChick) / 1000 + // totalEggWeightGrams is in grams; egg mass is grams per hen. + eggMass = totalEggWeightGrams / remainingChick updates["egg_mass"] = eggMass recording.EggMass = &eggMass } else { From d0dd12776e394a74a3702b449b4f1215e891aa33 Mon Sep 17 00:00:00 2001 From: M1 AIR Date: Wed, 28 Jan 2026 15:48:05 +0700 Subject: [PATCH 02/12] Activated migrate production --- ci/production.yml | 58 +++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/ci/production.yml b/ci/production.yml index 16626929..a1c09825 100644 --- a/ci/production.yml +++ b/ci/production.yml @@ -1,6 +1,6 @@ stages: - build -# - migrate + - migrate - deploy - seed @@ -51,39 +51,39 @@ build_production: # ========================= # MIGRATE (PRODUCTION - MANUAL) # ========================= -#migrate_production: -# stage: migrate -# rules: -# - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "production"' -# when: manual -# allow_failure: false -# needs: -# - job: build_production -# artifacts: false -# script: | -# set -e -# cd /opt/deploy/lti -# test -f .env || (echo "❌ .env not found" && exit 1) +migrate_production: + stage: migrate + rules: + - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "production"' + when: manual + allow_failure: false + needs: + - job: build_production + artifacts: false + script: | + set -e + cd /opt/deploy/lti + test -f .env || (echo "❌ .env not found" && exit 1) -# set -a -# . ./.env -# set +a + set -a + . ./.env + set +a # Validasi env wajib -# : "${DB_HOST:?DB_HOST not set}" -# : "${DB_PORT:?DB_PORT not set}" -# : "${DB_USER:?DB_USER not set}" -# : "${DB_PASSWORD:?DB_PASSWORD not set}" -# : "${DB_NAME:?DB_NAME not set}" + : "${DB_HOST:?DB_HOST not set}" + : "${DB_PORT:?DB_PORT not set}" + : "${DB_USER:?DB_USER not set}" + : "${DB_PASSWORD:?DB_PASSWORD not set}" + : "${DB_NAME:?DB_NAME not set}" -# DB_SSLMODE="${DB_SSLMODE:-require}" -# export DATABASE_URL="postgres://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?sslmode=${DB_SSLMODE}" + DB_SSLMODE="${DB_SSLMODE:-require}" + export DATABASE_URL="postgres://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?sslmode=${DB_SSLMODE}" -# echo "✅ Running migrations (production)..." -# docker run --rm \ -# -v "$CI_PROJECT_DIR/internal/database/migrations:/migrations:ro" \ -# migrate/migrate:v4.15.2 \ -# -path=/migrations -database "$DATABASE_URL" up + echo "✅ Running migrations (production)..." + docker run --rm \ + -v "/opt/deploy/lti/internal/database/migrations:/migrations:ro" \ + migrate/migrate:v4.15.2 \ + -path=/migrations -database "$DATABASE_URL" up # ========================= From 6b58eb0c6546bcc736f30aad47a1ea5e0a42e6a2 Mon Sep 17 00:00:00 2001 From: M1 AIR Date: Wed, 28 Jan 2026 16:59:52 +0700 Subject: [PATCH 03/12] Activated migrate production no manual --- ci/production.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ci/production.yml b/ci/production.yml index a1c09825..930d15f4 100644 --- a/ci/production.yml +++ b/ci/production.yml @@ -54,8 +54,7 @@ build_production: migrate_production: stage: migrate rules: - - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "production"' - when: manual + - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "production"' allow_failure: false needs: - job: build_production From 392b2f51ad3ef08dfce645f1d1684b27d0491ecf Mon Sep 17 00:00:00 2001 From: kris Date: Wed, 28 Jan 2026 10:32:18 +0000 Subject: [PATCH 04/12] Edit production.yml --- ci/production.yml | 74 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 57 insertions(+), 17 deletions(-) diff --git a/ci/production.yml b/ci/production.yml index 930d15f4..48bf64fb 100644 --- a/ci/production.yml +++ b/ci/production.yml @@ -49,40 +49,80 @@ build_production: # ========================= -# MIGRATE (PRODUCTION - MANUAL) +# MIGRATE (PRODUCTION) # ========================= migrate_production: stage: migrate rules: - - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "production"' - allow_failure: false + - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "production"' needs: - job: build_production artifacts: false script: | set -e - cd /opt/deploy/lti - test -f .env || (echo "❌ .env not found" && exit 1) + echo "✅ Running migrations (production) ..." + cd "$DEPLOY_DIR" + test -f "$COMPOSE_FILE" || (echo "❌ $COMPOSE_FILE not found in $DEPLOY_DIR" && exit 1) + test -f .env || (echo "❌ .env not found in $DEPLOY_DIR" && exit 1) + + # ✅ load env dari server set -a . ./.env set +a - # Validasi env wajib - : "${DB_HOST:?DB_HOST not set}" - : "${DB_PORT:?DB_PORT not set}" - : "${DB_USER:?DB_USER not set}" - : "${DB_PASSWORD:?DB_PASSWORD not set}" - : "${DB_NAME:?DB_NAME not set}" + # ✅ validasi + test -n "$DB_HOST" || (echo "❌ DB_HOST empty" && exit 1) + test -n "$DB_PORT" || (echo "❌ DB_PORT empty" && exit 1) + test -n "$DB_USER" || (echo "❌ DB_USER empty" && exit 1) + test -n "$DB_PASSWORD" || (echo "❌ DB_PASSWORD empty" && exit 1) + test -n "$DB_NAME" || (echo "❌ DB_NAME empty" && exit 1) - DB_SSLMODE="${DB_SSLMODE:-require}" - export DATABASE_URL="postgres://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?sslmode=${DB_SSLMODE}" + export DATABASE_URL="postgres://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?sslmode=${DB_SSLMODE:-disable}" + echo "✅ DATABASE_URL=$DATABASE_URL" - echo "✅ Running migrations (production)..." - docker run --rm \ - -v "/opt/deploy/lti/internal/database/migrations:/migrations:ro" \ + # ✅ Pastikan postgres & redis ON (sesuaikan nama service compose kamu!) + echo "✅ Ensuring postgres & redis running ..." + docker compose -f "$COMPOSE_FILE" up -d stg-postgres-lti stg-redis-lti || true + + # ✅ Ambil network key dari compose + COMPOSE_NETWORK_KEY="$(docker compose -f "$COMPOSE_FILE" config | awk '/networks:/ {getline; print $1}' | tr -d ':')" + echo "✅ Compose network key: $COMPOSE_NETWORK_KEY" + + # ✅ Cari network name yang dipakai docker + NETWORK_NAME="$(docker network ls --format '{{.Name}}' | grep "_${COMPOSE_NETWORK_KEY}$" | head -n 1)" + test -n "$NETWORK_NAME" || (echo "❌ Cannot find docker network for compose ($COMPOSE_NETWORK_KEY)" && exit 1) + + echo "✅ Docker network detected: $NETWORK_NAME" + + # ✅ Migrations dari repo (CI workspace) + echo "✅ Checking migrations from repo..." + ls -lah "$CI_PROJECT_DIR/internal/database/migrations" + + echo "✅ Running migrations via migrate/migrate container" + set +e + out=$(docker run --rm \ + --network "$NETWORK_NAME" \ + -v "$CI_PROJECT_DIR/internal/database/migrations:/migrations:ro" \ migrate/migrate:v4.15.2 \ - -path=/migrations -database "$DATABASE_URL" up + -path=/migrations -database "$DATABASE_URL" up 2>&1) + code=$? + set -e + + echo "$out" + + # ✅ Handle no change dengan benar (tidak false-success) + if echo "$out" | grep -qi "no change"; then + echo "✅ No change (already up to date)" + exit 0 + fi + + if [ $code -ne 0 ]; then + echo "❌ Migration failed with exit code $code" + exit $code + fi + + echo "✅ Migration applied successfully" # ========================= From 478487870b67729dd746ee4d15e62f639a1cbcf2 Mon Sep 17 00:00:00 2001 From: Adnan Zahir Date: Wed, 28 Jan 2026 17:34:11 +0700 Subject: [PATCH 05/12] Revert "Edit production.yml" This reverts commit 392b2f51ad3ef08dfce645f1d1684b27d0491ecf --- ci/production.yml | 74 +++++++++++------------------------------------ 1 file changed, 17 insertions(+), 57 deletions(-) diff --git a/ci/production.yml b/ci/production.yml index 48bf64fb..930d15f4 100644 --- a/ci/production.yml +++ b/ci/production.yml @@ -49,80 +49,40 @@ build_production: # ========================= -# MIGRATE (PRODUCTION) +# MIGRATE (PRODUCTION - MANUAL) # ========================= migrate_production: stage: migrate rules: - - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "production"' + - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "production"' + allow_failure: false needs: - job: build_production artifacts: false script: | set -e - echo "✅ Running migrations (production) ..." + cd /opt/deploy/lti + test -f .env || (echo "❌ .env not found" && exit 1) - cd "$DEPLOY_DIR" - test -f "$COMPOSE_FILE" || (echo "❌ $COMPOSE_FILE not found in $DEPLOY_DIR" && exit 1) - test -f .env || (echo "❌ .env not found in $DEPLOY_DIR" && exit 1) - - # ✅ load env dari server set -a . ./.env set +a - # ✅ validasi - test -n "$DB_HOST" || (echo "❌ DB_HOST empty" && exit 1) - test -n "$DB_PORT" || (echo "❌ DB_PORT empty" && exit 1) - test -n "$DB_USER" || (echo "❌ DB_USER empty" && exit 1) - test -n "$DB_PASSWORD" || (echo "❌ DB_PASSWORD empty" && exit 1) - test -n "$DB_NAME" || (echo "❌ DB_NAME empty" && exit 1) + # Validasi env wajib + : "${DB_HOST:?DB_HOST not set}" + : "${DB_PORT:?DB_PORT not set}" + : "${DB_USER:?DB_USER not set}" + : "${DB_PASSWORD:?DB_PASSWORD not set}" + : "${DB_NAME:?DB_NAME not set}" - export DATABASE_URL="postgres://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?sslmode=${DB_SSLMODE:-disable}" - echo "✅ DATABASE_URL=$DATABASE_URL" + DB_SSLMODE="${DB_SSLMODE:-require}" + export DATABASE_URL="postgres://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?sslmode=${DB_SSLMODE}" - # ✅ Pastikan postgres & redis ON (sesuaikan nama service compose kamu!) - echo "✅ Ensuring postgres & redis running ..." - docker compose -f "$COMPOSE_FILE" up -d stg-postgres-lti stg-redis-lti || true - - # ✅ Ambil network key dari compose - COMPOSE_NETWORK_KEY="$(docker compose -f "$COMPOSE_FILE" config | awk '/networks:/ {getline; print $1}' | tr -d ':')" - echo "✅ Compose network key: $COMPOSE_NETWORK_KEY" - - # ✅ Cari network name yang dipakai docker - NETWORK_NAME="$(docker network ls --format '{{.Name}}' | grep "_${COMPOSE_NETWORK_KEY}$" | head -n 1)" - test -n "$NETWORK_NAME" || (echo "❌ Cannot find docker network for compose ($COMPOSE_NETWORK_KEY)" && exit 1) - - echo "✅ Docker network detected: $NETWORK_NAME" - - # ✅ Migrations dari repo (CI workspace) - echo "✅ Checking migrations from repo..." - ls -lah "$CI_PROJECT_DIR/internal/database/migrations" - - echo "✅ Running migrations via migrate/migrate container" - set +e - out=$(docker run --rm \ - --network "$NETWORK_NAME" \ - -v "$CI_PROJECT_DIR/internal/database/migrations:/migrations:ro" \ + echo "✅ Running migrations (production)..." + docker run --rm \ + -v "/opt/deploy/lti/internal/database/migrations:/migrations:ro" \ migrate/migrate:v4.15.2 \ - -path=/migrations -database "$DATABASE_URL" up 2>&1) - code=$? - set -e - - echo "$out" - - # ✅ Handle no change dengan benar (tidak false-success) - if echo "$out" | grep -qi "no change"; then - echo "✅ No change (already up to date)" - exit 0 - fi - - if [ $code -ne 0 ]; then - echo "❌ Migration failed with exit code $code" - exit $code - fi - - echo "✅ Migration applied successfully" + -path=/migrations -database "$DATABASE_URL" up # ========================= From 86caa6c4d4d49d8c722726f208e1536690db2977 Mon Sep 17 00:00:00 2001 From: aguhh18 Date: Thu, 29 Jan 2026 12:24:32 +0700 Subject: [PATCH 06/12] FIX[BE]; fixing penjualan qty tidak berkuranga --- .../services/deliveryorder.service.go | 81 ++++++++++--------- 1 file changed, 45 insertions(+), 36 deletions(-) diff --git a/internal/modules/marketing/services/deliveryorder.service.go b/internal/modules/marketing/services/deliveryorder.service.go index 2022cc78..b145fc98 100644 --- a/internal/modules/marketing/services/deliveryorder.service.go +++ b/internal/modules/marketing/services/deliveryorder.service.go @@ -410,6 +410,7 @@ func (s deliveryOrdersService) UpdateOne(c *fiber.Ctx, req *validation.DeliveryO return fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch delivery product") } + oldRequestedQty := deliveryProduct.UsageQty + deliveryProduct.PendingQty var itemDeliveryDate *time.Time if requestedProduct.DeliveryDate != "" { parsedDate, err := utils.ParseDateString(requestedProduct.DeliveryDate) @@ -421,11 +422,8 @@ func (s deliveryOrdersService) UpdateOne(c *fiber.Ctx, req *validation.DeliveryO itemDeliveryDate = deliveryProduct.DeliveryDate } - oldRequestedQty := deliveryProduct.UsageQty + deliveryProduct.PendingQty - - // Cek apakah product punya flag PAKAN atau OVK isPakanOrOVK := false - if foundMarketingProduct.ProductWarehouse.Product.Id != 0 && len(foundMarketingProduct.ProductWarehouse.Product.Flags) > 0 { + if foundMarketingProduct.ProductWarehouse.Id != 0 && foundMarketingProduct.ProductWarehouse.Product.Id != 0 && len(foundMarketingProduct.ProductWarehouse.Product.Flags) > 0 { for _, flag := range foundMarketingProduct.ProductWarehouse.Product.Flags { if flag.Name == string(utils.FlagPakan) || flag.Name == string(utils.FlagOVK) { isPakanOrOVK = true @@ -506,60 +504,71 @@ func (s deliveryOrdersService) consumeDeliveryStock(ctx context.Context, tx *gor deliveryProductRepo := marketingRepo.NewMarketingDeliveryProductRepository(tx) - if err == nil && result.UsageQuantity > 0 { - if actorID > 0 { - decreaseLog := &entity.StockLog{ - Decrease: result.UsageQuantity, - LoggableType: string(utils.StockLogTypeMarketing), - LoggableId: deliveryProduct.Id, - ProductWarehouseId: marketingProduct.ProductWarehouseId, - CreatedBy: actorID, - Notes: "", - } - s.StockLogRepo.WithTx(tx).CreateOne(ctx, decreaseLog, nil) - } + totalConsumed := 0.0 + var fifoConsumed float64 + var directConsumed float64 + + if result != nil && result.UsageQuantity > 0 { + fifoConsumed = result.UsageQuantity + totalConsumed = result.UsageQuantity } - if err != nil { + if err != nil || (totalConsumed < requestedQty) { + remainder := requestedQty - totalConsumed + pwRepo := productWarehouseRepo.NewProductWarehouseRepository(tx) pw, err2 := pwRepo.GetByID(ctx, marketingProduct.ProductWarehouseId, nil) if err2 != nil { return fiber.NewError(fiber.StatusInternalServerError, "Failed to check product warehouse stock") } - if pw == nil || pw.Quantity < requestedQty { - return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Insufficient stock. Available: %.2f, Requested: %.2f", func() float64 { + if pw == nil || pw.Quantity < remainder { + return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Insufficient stock. FIFO: %.2f, Direct Available: %.2f, Total Needed: %.2f", func() float64 { if pw != nil { return pw.Quantity } else { return 0 } - }(), requestedQty)) + }(), remainder, requestedQty)) } - if err := deliveryProductRepo.UpdateFifoFields(ctx, deliveryProduct.Id, requestedQty, 0); err != nil { - return fiber.NewError(fiber.StatusInternalServerError, "Failed to update delivery product") + if err := pwRepo.AdjustQuantities(ctx, map[uint]float64{ + marketingProduct.ProductWarehouseId: -remainder, + }, func(db *gorm.DB) *gorm.DB { + return tx + }); err != nil { + return fiber.NewError(fiber.StatusInternalServerError, "Failed to adjust product warehouse quantity") } - if actorID > 0 { - decreaseLog := &entity.StockLog{ - Decrease: requestedQty, - LoggableType: string(utils.StockLogTypeMarketing), - LoggableId: deliveryProduct.Id, - ProductWarehouseId: marketingProduct.ProductWarehouseId, - CreatedBy: actorID, - Notes: "", - } - s.StockLogRepo.WithTx(tx).CreateOne(ctx, decreaseLog, nil) - } - - return nil + directConsumed = remainder + totalConsumed += remainder } - if err := deliveryProductRepo.UpdateFifoFields(ctx, deliveryProduct.Id, result.UsageQuantity, result.PendingQuantity); err != nil { + if err := deliveryProductRepo.UpdateFifoFields(ctx, deliveryProduct.Id, totalConsumed, 0); err != nil { return fiber.NewError(fiber.StatusInternalServerError, "Failed to update delivery product") } + if actorID > 0 && totalConsumed > 0 { + notes := "" + if fifoConsumed > 0 && directConsumed > 0 { + notes = fmt.Sprintf("Partial FIFO (%.2f) + Direct (%.2f)", fifoConsumed, directConsumed) + } else if fifoConsumed > 0 { + notes = fmt.Sprintf("FIFO stock only (%.2f)", fifoConsumed) + } else if directConsumed > 0 { + notes = fmt.Sprintf("Direct stock only (%.2f)", directConsumed) + } + + decreaseLog := &entity.StockLog{ + Decrease: totalConsumed, + LoggableType: string(utils.StockLogTypeMarketing), + LoggableId: deliveryProduct.Id, + ProductWarehouseId: marketingProduct.ProductWarehouseId, + CreatedBy: actorID, + Notes: notes, + } + s.StockLogRepo.WithTx(tx).CreateOne(ctx, decreaseLog, nil) + } + return nil } From a21b554fc7b4de8b55caac5982d3750ae7d12e69 Mon Sep 17 00:00:00 2001 From: ragilap Date: Thu, 29 Jan 2026 14:33:26 +0700 Subject: [PATCH 07/12] [FIX/BE-US] changes permission to redis and scope --- internal/config/config.go | 5 +++ .../modules/sso/controllers/sso.controller.go | 21 +++++++-- .../sso/controllers/user_sync.controller.go | 6 +++ internal/modules/sso/verifier/profile.go | 44 ++++++++++++++----- 4 files changed, 60 insertions(+), 16 deletions(-) diff --git a/internal/config/config.go b/internal/config/config.go index 71fb430c..af723b3b 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -61,6 +61,7 @@ var ( SSOCookieDomain string SSOCookieSecure bool SSOCookieSameSite string + SSOAccessTokenMaxBytes int SSOTokenBlacklistPrefix string SSOPKCETTL time.Duration SSOUserSyncDrift time.Duration @@ -144,6 +145,10 @@ func init() { SSOCookieDomain = viper.GetString("SSO_COOKIE_DOMAIN") SSOCookieSecure = viper.GetBool("SSO_COOKIE_SECURE") SSOCookieSameSite = defaultString(viper.GetString("SSO_COOKIE_SAMESITE"), "Lax") + SSOAccessTokenMaxBytes = viper.GetInt("SSO_ACCESS_TOKEN_MAX_BYTES") + if SSOAccessTokenMaxBytes <= 0 { + SSOAccessTokenMaxBytes = 4096 + } SSOTokenBlacklistPrefix = defaultString(viper.GetString("SSO_TOKEN_BLACKLIST_PREFIX"), "sso:blacklist") if ttl := viper.GetInt("SSO_PKCE_TTL_SECONDS"); ttl > 0 { SSOPKCETTL = time.Duration(ttl) * time.Second diff --git a/internal/modules/sso/controllers/sso.controller.go b/internal/modules/sso/controllers/sso.controller.go index b49d73e5..5e75d4a9 100644 --- a/internal/modules/sso/controllers/sso.controller.go +++ b/internal/modules/sso/controllers/sso.controller.go @@ -200,7 +200,7 @@ func (h *Controller) Refresh(c *fiber.Ctx) error { return fiber.NewError(fiber.StatusUnauthorized, "invalid access token") } - issueCookies(c, struct { + if err := issueCookies(c, struct { AccessToken string `json:"access_token"` RefreshToken string `json:"refresh_token"` TokenType string `json:"token_type"` @@ -218,7 +218,9 @@ func (h *Controller) Refresh(c *fiber.Ctx) error { IDToken: tokenResp.IDToken, Error: tokenResp.Error, Description: tokenResp.Description, - }, verification) + }, verification); err != nil { + return err + } utils.Log.WithFields(logrus.Fields{ "user_id": verification.UserID, @@ -307,7 +309,9 @@ func (h *Controller) Callback(c *fiber.Ctx) error { } // prepare cookies - issueCookies(c, tokenResp, verification) + if err := issueCookies(c, tokenResp, verification); err != nil { + return err + } redirectTarget := sessionData.ReturnTo if redirectTarget == "" { @@ -742,13 +746,21 @@ func issueCookies(c *fiber.Ctx, tokenResp struct { IDToken string `json:"id_token"` Error string `json:"error"` Description string `json:"error_description"` -}, verification *sso.VerificationResult) { +}, verification *sso.VerificationResult) error { if revoker := session.GetRevocationStore(); revoker != nil && verification != nil { if err := revoker.ClearUserLogout(c.Context(), verification.UserID); err != nil { utils.Log.WithError(err).Warn("failed to clear logout marker") } } + if max := config.SSOAccessTokenMaxBytes; max > 0 && len(tokenResp.AccessToken) > max { + utils.Log.WithFields(logrus.Fields{ + "token_len": len(tokenResp.AccessToken), + "max_len": max, + }).Warn("sso access token exceeds cookie size limit") + return fiber.NewError(fiber.StatusRequestEntityTooLarge, "access token too large") + } + accessName := resolveSSOCookieName(config.SSOAccessCookieName, "access") refreshName := resolveSSOCookieName(config.SSORefreshCookieName, "refresh") maxAge := tokenResp.ExpiresIn @@ -790,6 +802,7 @@ func issueCookies(c *fiber.Ctx, tokenResp struct { // Optional: expose limited info via headers for FE debugging (avoid tokens) c.Set("X-Auth-User", fmt.Sprintf("%d", verification.UserID)) + return nil } func clearSSOCookie(c *fiber.Ctx, name string) { diff --git a/internal/modules/sso/controllers/user_sync.controller.go b/internal/modules/sso/controllers/user_sync.controller.go index 72c7768a..bdc7900e 100644 --- a/internal/modules/sso/controllers/user_sync.controller.go +++ b/internal/modules/sso/controllers/user_sync.controller.go @@ -291,6 +291,8 @@ func (h *UserSyncController) upsertUser(c *fiber.Ctx, alias string, req *userSyn "user_id": req.User.ID, }).Info("sso user synced") + sso.InvalidateProfileCache(c.Context(), uint(req.User.ID)) + msg := fmt.Sprintf("User %s successfully", req.Action) return c.Status(fiber.StatusOK).JSON(response.Success{ Code: fiber.StatusOK, @@ -318,6 +320,8 @@ func (h *UserSyncController) logoutUser(c *fiber.Ctx, alias string, req *userSyn "user_id": req.User.ID, }).Info("sso user logout enforced") + sso.InvalidateProfileCache(c.Context(), uint(req.User.ID)) + return c.Status(fiber.StatusOK).JSON(response.Common{ Code: fiber.StatusOK, Status: "success", @@ -341,6 +345,8 @@ func (h *UserSyncController) removeUser(c *fiber.Ctx, alias string, req *userSyn "user_id": req.User.ID, }).Info("sso user deleted") + sso.InvalidateProfileCache(c.Context(), uint(req.User.ID)) + return c.Status(fiber.StatusOK).JSON(response.Common{ Code: fiber.StatusOK, Status: "success", diff --git a/internal/modules/sso/verifier/profile.go b/internal/modules/sso/verifier/profile.go index e3cd40ca..4876db1e 100644 --- a/internal/modules/sso/verifier/profile.go +++ b/internal/modules/sso/verifier/profile.go @@ -265,24 +265,44 @@ func profileCacheKey(userID uint) string { return profileCachePrefix + strconv.FormatUint(uint64(userID), 10) } +// InvalidateProfileCache clears cached profile data for the given user in both local and Redis caches. +func InvalidateProfileCache(ctx context.Context, userID uint) { + if userID == 0 { + return + } + key := profileCacheKey(userID) + profileLocalCache.Delete(key) + + client := cache.Redis() + if client == nil { + return + } + if ctx == nil { + ctx = context.Background() + } + if err := client.Del(ctx, key).Err(); err != nil && !errors.Is(err, redis.Nil) { + utils.Log.WithError(err).Warn("sso profile redis delete failed") + } +} + func canonicalPermissionName(name string) string { return strings.ToLower(strings.TrimSpace(name)) } // userInfoEnvelope handles the varying shapes returned by the SSO userinfo endpoint. type userInfoEnvelope struct { - Roles []userInfoRole `json:"roles"` - AreaIDs []uint `json:"area_ids"` - LocationIDs []uint `json:"location_ids"` - AllArea bool `json:"all_area"` - AllLocation bool `json:"all_location"` - Data *struct { - ID int64 `json:"id"` - Roles []userInfoRole `json:"roles"` - AreaIDs []uint `json:"area_ids"` - LocationIDs []uint `json:"location_ids"` - AllArea bool `json:"all_area"` - AllLocation bool `json:"all_location"` + Roles []userInfoRole `json:"roles"` + AreaIDs []uint `json:"area_ids"` + LocationIDs []uint `json:"location_ids"` + AllArea bool `json:"all_area"` + AllLocation bool `json:"all_location"` + Data *struct { + ID int64 `json:"id"` + Roles []userInfoRole `json:"roles"` + AreaIDs []uint `json:"area_ids"` + LocationIDs []uint `json:"location_ids"` + AllArea bool `json:"all_area"` + AllLocation bool `json:"all_location"` } `json:"data"` User *struct { ID int64 `json:"id"` From 3669f20f4a844bf4b28993f03376fd1292a60ead Mon Sep 17 00:00:00 2001 From: aguhh18 Date: Thu, 29 Jan 2026 14:50:46 +0700 Subject: [PATCH 08/12] FEAT[BE] :add validation if supplier id not null in transfer stock --- .../transfers/services/transfer.service.go | 15 ++++++++++++++- .../transfers/validations/transfer.validation.go | 8 ++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/internal/modules/inventory/transfers/services/transfer.service.go b/internal/modules/inventory/transfers/services/transfer.service.go index e74332bc..1d69de6f 100644 --- a/internal/modules/inventory/transfers/services/transfer.service.go +++ b/internal/modules/inventory/transfers/services/transfer.service.go @@ -232,11 +232,24 @@ func (s *transferService) CreateOne(c *fiber.Ctx, req *validation.TransferReques } for _, delivery := range req.Deliveries { - // Skip supplier validation if SupplierID is 0 (optional) + if delivery.SupplierID == 0 { continue } + if delivery.VehiclePlate == "" { + return nil, fiber.NewError(fiber.StatusBadRequest, "Vehicle plate wajib diisi ketika supplier dipilih") + } + if delivery.DriverName == "" { + return nil, fiber.NewError(fiber.StatusBadRequest, "Driver name wajib diisi ketika supplier dipilih") + } + if delivery.DeliveryCost <= 0 { + return nil, fiber.NewError(fiber.StatusBadRequest, "Delivery cost harus lebih dari 0 ketika supplier dipilih") + } + if delivery.DeliveryCostPerItem <= 0 { + return nil, fiber.NewError(fiber.StatusBadRequest, "Delivery cost per item harus lebih dari 0 ketika supplier dipilih") + } + supplier, err := s.SupplierRepo.GetByID(c.Context(), uint(delivery.SupplierID), nil) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { diff --git a/internal/modules/inventory/transfers/validations/transfer.validation.go b/internal/modules/inventory/transfers/validations/transfer.validation.go index e2f357f3..89676a41 100644 --- a/internal/modules/inventory/transfers/validations/transfer.validation.go +++ b/internal/modules/inventory/transfers/validations/transfer.validation.go @@ -21,11 +21,11 @@ type TransferDeliveryProduct struct { } type TransferDelivery struct { - DeliveryCost float64 `json:"delivery_cost" validate:"required"` - DeliveryCostPerItem float64 `json:"delivery_cost_per_item" validate:"required"` + DeliveryCost float64 `json:"delivery_cost"` + DeliveryCostPerItem float64 `json:"delivery_cost_per_item"` DocumentIndex int `json:"document_index" validate:"omitempty,min=-1" default:"-1"` - DriverName string `json:"driver_name" validate:"required"` - VehiclePlate string `json:"vehicle_plate" validate:"required"` + DriverName string `json:"driver_name"` + VehiclePlate string `json:"vehicle_plate"` SupplierID uint `json:"supplier_id" ` Products []TransferDeliveryProduct `json:"products" validate:"required,dive"` } From 3d1d9c418b50025ecc710c1693fb04baca7e07e3 Mon Sep 17 00:00:00 2001 From: aguhh18 Date: Thu, 29 Jan 2026 16:52:19 +0700 Subject: [PATCH 09/12] FEAT[BE] :implement movement number generation and sequence management for transfer layings --- db_lti_erp-202601271102-stg.sql | Bin 0 -> 323287 bytes ...458_create_transfer_laying_sequence.up.sql | 33 ++++++++++++++++++ .../laying_transfer.repository.go | 25 +++++++++++++ .../services/transfer_laying.service.go | 6 +++- 4 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 db_lti_erp-202601271102-stg.sql create mode 100644 internal/database/migrations/20260129083458_create_transfer_laying_sequence.up.sql diff --git a/db_lti_erp-202601271102-stg.sql b/db_lti_erp-202601271102-stg.sql new file mode 100644 index 0000000000000000000000000000000000000000..2a8495d814b6d4cd13ac8c613d9af3150290542a GIT binary patch literal 323287 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%0Dfb&O)id(3mWmK$wWpQLMgxr> zY&R-ViZ5E{|DL+?iB2N$y=`wy9Do-d+^xk%l#jYu%S^>6lM{T%{PcZbG~>y}Kx;G4 zR&UzW1_?Gjn>Vg|GJv$uei-82x#hRcA&+~coZNc6v%=QytDMNfRz#o9@9Fq^`_!IY zk>o26TI|A)y3~%O(W^+9SbP6=R*`fqck^1O>4sE!>EpW{CvYvscH_R8o@&fvX(0lO z7p3|yLX4d@NG6wZUm>_!{pMVRL%yi7AVFCmWDiN${&Xv2YkGMz`ikJW{5R8B;lR0X zJVMzJ`B-A+1IloCYf_?{0MgSJjBbbf1Ko9+3DS>6?%Oo?_s28`^glzb*Ywy=)D!4* z<2#CmlOmzFEv|NpjpXdOH%Op2Y?4IPnhy`yTgA;qhJ$x|oZE&}r|IMsh zGzi_$Cw2SSD1%~E~{0@bMm zS9S$&q|%xsDI<)=|K!Ha%*GpYGIWpPn0EMD1}dW~%F5`V=AtP7V}$94k>_vup9{^R zR}MaCDM8onX<%%8UG?y?42%L+St=KKJ&;9D+>}2(=&O^(>yN(eN7l=v-=9>~w^Qkf z@}hu?3nKcu9D<2qih^U}x`j!o^n0)ys10ra60qb2`N}o4#^o*^)*3B3+8p+VUD86I zh7=9*vnEcyCJpI+aiJoku7(u$;q<43GjEUN!%!Z~dDBWs(Z0b37q{VZ125lqGjb*# zj@dzV2{US}Ssm2e@$h!U(Y)sv@F3DWA;Ty`?wm1q30KVGIFisVG1xP`e!J(qM0m@# zrb=GDgcKw?STXO{9l=qZKhG4mJh|5{Gcsi2w~%k2?rHz=ApOIG)Bkudnrw&tc4&c2 zYorr;>G^+cQE&Q04~t|95ZEqoW2*(Y(bwULfxkiLQ>;M{QkQIo2EZ)!jP7ISVI5ii z<|7dAa(xXr!-Hn2WT^vfG*@wNR83IC#4%q2u{8`2r~O zKymjU2-zPHIR6`-%JCynf($T%Q}zsgjc#W8lww~_Fel4lys=|M6(W68KZu(2)nxXT zNpW^ZKVE5k9~?8$6_Nj~6mqi_@Uj_kx$5!2neZ1s>J7Y41ixe?+z}jsZnFE_Y`+gA z-0?<-KM7f1l0>?pf#1=}9BQwpJ{er-PeBhTT*D&uzJyC(U#}fReFehTYg0RIkh$eN zZX7xu*!{tQo3PyKVL*rZ6Me-vF7qRO@QKeCd3$kO`H1u1FWPY(gM z-RSV`{pIJfO0B>DJSquGh~%I-84dpol?6kJ!gRxe3tbl3`bAXZMh-L;5cdXr!vE;^ zG4d(|BClLL^`$5`5JjJ48tw72ml|FykZY9XH*awu!LLs_m&(g>gg5%C5UjIk$5@?2 z1zKBbOF6HV(pyYe3L&IU7AR7wMFAlztgv%vfe&tchG_nv1+!3~q(forETtHCXz>cp zlJqa|vCTk4@U-=x!uEM;VIEEN3EjA1S73CT0V%mWl`0gq;z_Am#P8U>)X~rQy&?ld zyW-3f1rf@QpiYXHcvu^bqAJ$WB%r8LSIYb#8sX%;qg65WA+57Y&q6--6tMx#idsC? z^l<9JY)QO5kyeG=*0;>|DLV>hN-yq4`zNd9(&pPFS`ruC>t;kr3?rOsm)mjQ!+rKA zonvjaa@UISEoJ?d#lfV@Q8df7M_Amz&yoMQPWa*4`&(QymB+t*)qL=QJFk}%RVmc< z9_=n8&i?R$@cf_%vudDN9$Vi5!UgHOCOmM(ceJq0M;{C!e>^r~yx_&mx?wf_IU-C5 zl=qvgeU)iwPXUZj{%D>ugGJwpyzTAO*=~K{SopV~n2<6B?4!JuE;xv;Car$1gy~1p zF{2R+|A$8E2hHC1UgzJA(G*SDH8JF&oDkWaA(e79hspK31~Z^UM0^SI1qrVk|Gz4< z_(Ln&ogdk?IGlE*%n%eQd(U4P4y<)O9(9pmQ+L!R++b1+J@|*2^*PNCy!P|laNKeT zA)WSKiM4g)zd!j?yetcIfIWDSlE$SK3=8~=%T1Aogv|{q38A#pK!?B?(LXWG)7lC6`942iw|X%<+G!oM4vP)YJe{wts9XB0}M;B3BYGiS=(U;P%)(hQ*4hO(H? z!ve`}rnsF-xOZ~-<5)#yPL*&`z_4?|YaXAMn1J?Rf%C&`L~(ou35 zozpq;5dI_r)oG~MiLL;Qz|vnTJLHW06UH|>_u+8Ew8}iY+w8;h!?CAh!jY7j;xWgh z!<9XgM_Z>Ytgqh}Jx)o&=40jQF4M$|GM)ghzC7;ufxa@=(O8} zxuzsP6^6p*F7T_>FcdSj5ru7&Tj1!QDXj-|$f(GUoRaw8;8J)YgHT!`d3ZySvrj_N z4}yrtNvP&+R_6xKFQJK&{Urc(u2)B1;dSbTOQ0^sUUoX204Cu(m|IM!%K!{1>C_Xp ztn4Ggz-JJt@(an7=sKv)4?thbwRe7UQGnkik{&9-PLd1$BA^Kt&#uFBnZqg(TVrJG za-nqMDm0)E4e6^SCTS*!)f8-%D2TUT@VDyy6)pcjJ!^Do(4}UAMOaw3>h8#fuDGMC zlC-s5YMqXYisEN)F76)y>z60WU(AecR~k+75zh(>ZCUdtsYbInIqUSviV~pwl@G@^ zGyHF!fUeT29gGoW?W&pvtOQF%aMUc>shn=YfU!2AN=_Ha6(m+vm4`Q*|CB+NPGj)iTU3YQ0ZQhIxLAPyy*`5K(1mN4d)nXXaT|;hB_WQ0^-)Hmt(g z+65;_?Z?_%8;e=Gd~G8)~f1z-x!X2~_SYjOv;?#9NT!m7gh7R-s@X01hIC07M-E zlQ=-cG%sLD$}80A%q(O-1V{AaP*HBIowQ|q8u}|;eyNlLF#HCVw7^F9uIWrU0A(g> znD0TEeiatA=y%p4fR!4{AGzmB-}qJZbToH+hxPmG)(<3Y!9%m*aucP}Ms}abotbm~ zTw^RFzJdrN)Hva8XF=~D5$sHx*sVy0PsPQVKW(l__%!88XPR=(x=CP{p+E3pT=X1`GMt21eVsJb)4xWH_N@vMhuIU=&)RJ26jn3r8Ffht z-q{>AxF1a4pbURN{ePv5+x{Fx`rq^5vlG`&w&$0l8r`ai3hkP*z|AOn0UEp7wD_U#o4>UGKmPQ84(ff& zcaM+%FK*|%$Fm8&KyJyFg9#V`-=+{u$;HBtJ8zFE$k_s7)d*$u!G(#e2AIGlqY@y{ zU!%|>JrndRg`5dh)a$y3oz;ixB$Oy5R}-tJ=uQ*|@m81!d4!rte|9na0b~Eu9_au2 zt8(`LMzq}`cc5XZH|;MkZ9IW=1Tt&3M;ba(d3;b}1h6m=h45zv_{8#hXZZSdc!!!g z`DJ{`7CWrvglr5Md7iD(-QDsub)01EEW=cjO1%x0U;417Tr%tMa^?$S!^fjL1{;Pr zJu-0C#d9q@=!AS`;bv0-0;1aX-24J#)zgP0f|eT1W&(@AMS3GK&Y~PVff#z4s4?gQ z+=s@;X|Mz*QEd2KPo~jaFNK1P55Pp6R?yZsKv-zMm>kPUawaHYr##7_9P!Awh5VU} z^mSHrxaSXt0wo`w8luF2dk+}WZqkQEuhkofI4C{D5KpC#t1y6VsjSQ@_c{Nw1;`I6 zIzOajf6I5);e;%-KRx!~&D|rV^RJ<+=2l*JXadOIJV_88W&ukBf&DjGW{6~vsSD^S zQAFw3Tmqm#833fe7|Ym(RWPCk)Ybr>Lv87}qjxuT)e?1geG|2F#~&X9;hU`1h5%w9 z(rDr$CIUyJ_E{8_-i73ovwu831cbj#1&-2>BJq`>^LBGAF;fUZ4OA0Lsu-;Bf~UO_ z(-kGQ-vLM{{M7gqNMfr8ct|wp5h;!LM$?cyf&&fi76|;%Xc=Mg!(6h zv?Yi&pwD1(;nB{tu^xFhWTJydQ=FzL_5A2E}93$%g*Zd8<|N z7z8`A7<|XGjxA63VPjtT?hBK8%i^C6Q+zxp`O+JJhwGHn4qQZ2r*j%oBcn4^%^chF zv`cnp{b8hq4Ct@YNko^@70xlzG8dI6)<%f2Zrr@A#)1=;>SYPKKwi)F9exXbB@BC?gjXOiU~1L-}JGu@$t3< zaCQ57_)y2M^C6j{KJpEyUj_BHA*}l-rIW`WE5mntYg%I=+ZPkS2K-UYaB}4wAryy zH-Fd-0o0&Uj1qJZbkN-4H_O?d)7CdK%T6O1)6#bfSj-+YTv9<@LH_LtV^E?{SNjNy z;_Yosh{GA?tXOt|j_cK7?L<>SRmpZA`;*0}#rwtZHL)rfJ9)Om*>0qmY{;NWM0pml z==1?C3pDccPW=p7(;r5SzQr~3yY>*mxA_HluS1`!_>D`D?q2shvx^5`K6mC^0)oV? zh10um)t^{qchOm0!SJugnhJ9v|FO{op{Mm6>MX8Bv@G&k@rj4ll>MfuN8YQ!ythn+ z&FE0e8{X>~eLX8JJE`+eo8OIE{xo|2E&sFmgnuWUfi9gY8uAJqi`TugkF9@U@5w+U zG@$?uqC=tOfs_?U$l#+ywQpD1CGhlvV}c8me};Pg?IjnOymg&=lQTEuZ;CJ|sgrOJ zB-A-Z&WP;@J1W6%9$yzhum)X3x7(Z_{A=g2Au;k34ORkLfjolZ*ZEw%8gUyuhcce!yRbW->13AP5q1MpH?X>g6`1T;!k60K0EODQNw z*3R7l$qAdBVn-@0glJ|l0 zeZ561EB?;cx2b9f24SL#dPI~X5g)vWSiCRa2n^kAGTMGYj; z=^24}XxslI>duesa~ebI6HZ+>CmnnU1oiu!7pZ&-WrG$5GdjS^vmqGDXjJO0HD*1S zA03qWGANK&7|sBD$lAz-A=Fc_jvyglo}z&`*~kU(lq0Hkw*yA@gr&u`p-JB!$ZMmx zw6H91$?Y^(gjIRxLC$WI*pjEDZ>F>&(H8kTn1@A`(vWINr;J@oa@0Uvl9v7;oEc1$ zHt^j-GgWpLvRQ9yT+lp}rcDqVm?K9``@mK6AM7HNL>7cw#URZe7<$Dk>xHLNYlsG% z^mBXcbSh;rZMqYv3!#>aPOufu>zR#I_b#&g%RkclS+O85mDpGy&Br+uf3l{gs|MLi z?ZYrUD%z~lxepTLh7!+$!wIo;bzVpIh(^z6j_j)Wby#qH-eT6%tqw`Z4%j1|11 z$O&XA;zEk1fc_#nKobbdI}AT<*!Bo38+v@U8|De-0*)s5g-R>HLtv=6xXU2fFKtxz z$V!6b7PIyWsXEdT>Q@a2E! zru>|%_P=jJ(7bkZKR(2Fi3g8xw=F4P2q^Bc%`fZ-;S9@NdOB%dpLgXl6zXNHOaQrS zp0fTR)Va4v-&L&Zs9S$$1YH0VBl+o^n@vCXP)m3!W60T{SD|Z>S5nBA-+XzdLmVhj zMo2;LfO2`MfYODV;g@x;fF(I7{IS0Y;fzuSpnZvWLp%Y3B^gX+e1 zLGjg1|A?eX;Dz_nMxb>YMhQ{gDlsQ&Cw~f?^;-xzQ?-1b1CLZ_FeC>duwrxrlzJaQ zK1uG_Ts2E75kwa{bIzW}XBzMfEahJHGVvRiR>^oPc%Hr6|6TW(E?4qZ`Qm=Dy2(Bi z`q>hKBb1#W=$MEAV9>#@1MOW~T?k7kBJMem7zDE#c_Zu;kyaVW-|>TO6IAJW zCGix0SCWDnQhtpI?J$t53m2Bqxn6}hBcjQ+W{5r&a80>|a=06oP45Ae*1Upc?!8O8 z)^vQZ!7z&VPCjZn&SL~;yvkfpWkYzpBxpGN;cqIUIXO-sDp|z(q-ZnW1nI~gm2R1w z3>~k*1Pfu+Ug4dD^k5{93#^cm4)VBa$bXf}fVF$_U~}&MUgpzc&%w^}jFV_++Aj9b zQFaVoI+h26*Tgc%nwl1(GblEd-S*#PVJ@bRUD^}@PpTW9>5mCFn7j(lgMa#ywyr>S zo5>1BS% zoe8bw#1B^>E*4p&5W$`v$YvBV#(_+`?1a`LNbwurHc4Q7M(A=#B-NouMSDAmKdl5F zf4U-;Gd&Q*h{1&bm9_;HtzwBqt!!aOm0o`6rY!Rgpd5F(Q6e>!FwG5vm0p;1Zr@ke zj&G301-^kPE;n_EBz^dADN5g{5zY{Eum%rw)v+(4^uE;`ebtqxR<>23T#HU#SY~=c zxcXMdm6|oySbx=b?tiRS$e!79dS4ZLmbrq3JCA5p&|-ncvITz95V-nIp(M$pbBuVr(poNK^;u z`Qk9l6V#CI-?fs+ZJMLS^0mY>hhCx5mv8(aj#Anl6GlPZMkb+J^Ti>4Z(!BL8-_gC zx$??i*ShYjPWMm#GHj_yG`vz$=y^F%*Aox4%Gjb}a8}FW(7V7!eW?UNYWC1`A1Zu) z9NEW7b;EIChP;9Lnxy==t5@2ozIYL46MYP@&GMn+uKQpOz!{8|e}h2iOy)uHU4Nts@`$c~?|t@QB`(Nn+1_5Trb$PFvnsbNK>qQOWH6Jt%t$)B)A^GRFEjTh%NPWayA|#HP(hzTRc1sOa6dnpJ@l5e7jElswAVm5AGY6>{ zC0OJb!zQsEKw)zXLwpkNs3_EzR@{ocJ}d@H)Ct8lug2!aqsIDHqsYV^7c#4i1ePU6 zCD!B%C6oNWNKGf0WrOTS9oBELGZLkHM1RX}6(t?{_RaNu92H;T@_OaycysW4J-n`s zHE&&A>c(_^dPxoRIf2E=B8xKnkz+xT#k(8kxMO5d;zg>iJc=3+# zRq$JCr<>)`iy&=mxB=GJ-B6D<38z`uMo$hS98>}W2Aum_YS({82AyECkr|{^v}?GH zGxmIBMl>7{{-(=JZ;s0CO@3Iv>-dE}iEM++dUb+yYU;pR5XGVWkU3_-VxWVsbOhDNkvE;Fv!v85d0Tm$P9w%$cXOk{SVtY8M_>oo3a-zsE|ZJj z`weko<{w`RSnEO^QYcqm9k`{IM!O--VG^^91-}Sw?gO8>_W-D)uCcE7NvfYhz-&52 zys?A?9=(Ev)@r+9Rvc9r43rU7!8Gv8E=HU|O&toNVeVpXQ?L%ufJhjFInivOcAfPS z)d`~Vm>ALnpQYg*R8eme<80zm#6s>uicDn$S!g~G=WjykS)v=luz%SMR||M;e3SW||ls|e-$BUFPH(`TEmK%p7yTY@M7dtN)?A56a zQ44E=CB+|HC8XKlQvJsdDf|D-a~tFNHmDKPXBjKuvDZsFIrdOYiTJ2aX%$r96mu#t z2h$Su2$CE7JVDhLpF~7JN(6|H%qrI!78i|#>KlK^#~>M zsY>jK=odxD!aDvYlM<@z%-Rw@}V<=*vtt819REtfGk}@Z-*=+7t z)P5!dsixZZ?Bi419y}5)lZDa7LxmoE5;=mFrie|Wm!2Qhm1hig#%Cwd7?7F);VR>3 zY$)tQHP}spM){R!(F?uluP`toC{U=F8LGxcJyRD53RUqKV|#mk-QVn--A2@iS++y6 z{gsW(Z^b+$=_nenQp5C6avy9=^G*qfIrcNimo`1le`AUJ3liKj%JO%bOr9>j6O8Q& zDg5gw5j|gdLh2-3ag`)qz_vtYL^U=ANT{v~)ot{+N(ocb(7nmVI0F4>t3u{PR3Sg3 z^ikft--KUX_|)ODtgu$R$mEU3j*z{w{e(==v;r0L?YI-f0eMBf+`(PNpRHjwwy!^S zRG7kMRdVv35f?7R2QZCpLi|6Kr1qnde%3$V>BUq3^BaOpyI)aNrC>98thA@n#xFXa z%tC~lA^?5zqLbjG`?D?dcJl-a{7GI+5ZPkMx<6VYCjfVuPh*C>`8Es&bJ2YHdPURdQ&T#uIN9^{R>K z?{EppfXB`MA2N*}WMuzC#1RdZv!S*l(Ga$ z7i6^>v#jdd(80B7F;xLGLDdr4+DqZ3xbhY-Drf(Hh?IU1`Th@4&;$cC(D!0~oBac2 zqmU$w`JSU4Lc!Yh9PzruUQX-sE8|9KT8i!;I9`vI?2bJ*c!Oj&5XE;)I`5$LmACeL zS+WSYNU|@or9k_T0d1;8JUNnaGVpFrlk$*F#rd+)eJAX~PLBC4-Gf*V`C(YvZS(6& z`f8rTC8#bnQ^kLH#DDM*e%tL`GY3+&ZFYyh_iW(o%A=c_NvnOGb9VSG(@P8@Ag5QS zVP?b80}Q7pI(VM>{&v?ip=v2LDM58X&Xge6Gr!k-T4^3q(5}`sFlU$*YwaU-S?4BE zU7xD;IM}zDs>KsIj3?MDK&$p38hdnpv1VzMQ1}#IIlM8OFKdTzB#wEc7C{2-;FZwUjWEJ7T-KT5 zs{?(P35K-QddM^k3}=8_7knK=6DB8;^!}8^-SZgkDB zee-#X>8Sjfy*~QWC3to>H%aqQqV^h!gYF14)bte3cckyl4M;U#R`a%*a8)_z&Bsiz zw<>9L4(&c=hU_#9EhO_zIHiX#*aUE6GR*w9h9jsaFda!Ulve3h4lTx>n@@}z-Jjyk zAfHUPXmHpO<`5lH-AKx$i!&TW%2>hw{?Wqzs0lhM1IrZ-VcHhoyCc=%4#$ENAoz-t zU?wkwgZ|gOG|Q}UD5b=SjLWn#?)5Jl577jOyquML5A>S#GMTs zt+-X(0g3)om)&PPa+6pcvyS}xKtv`}^sAw3#!luHGV}n!hA-nvTt$AOi6eQzGk~J- z*e#TRp9VL^0wOCAp4`J?5QMgk_K=M_e?D>XlQ>lW7@O`gdu#M1!4wf=ae%A#G8cQY z-ImIF7+WJ1DNmEKlk$?(E_8>4igPyn&`=QkY)42tuQIGsO%J%)do)azt=^51cJpHH zHAO&P%dfv^uYhwq$@65YvcqQPX!mfivQ^b@&)ej(-m^Q#$tHL8lYsn1bHX@w$i?+( zbIwhTu(8n&oDqtIg$f#P!vIF@F0kIczZ(*Y8o*-g`h)@+x!XceW|4r3h?{F(1B^D; zmZU+wJbb{9uO1f;9TV4jtIpNCkPSQCD;0G~ryxfUHmaP*m1A&U3W#?z(FsQM*BNj-vqM+>2Hd+XFpk6i)t5BvSCPvD$7D9x0 zHI1%;+c%>^{Irc^Ttqd@v={DQUhw5pa8TVf#C4eu1w9;NXj;)A*e*Bi)5_0yjFUiH zkkVv!h103UzO4NNtr-;UC4D4yvObS$Fx3B{I#rfiB#rZ!b7~s*|F#uBAEYq(t+RsP zn;MI|RK$KEsdaCbE0F&&*Rp!|oe!j?LkqlR)!7y%-^UFz&a6Y0?AdifkR{m+5m}kQ zZE>U<0c^WQ4F3b14w zD^ZCT&X>ewG^rtatn1^Vsc~rQrj`43H6-7j!p1g43P{c@p+ylF1#qEk$r|d$?yhq# zqX1Evic3rHj{Zk$-S>VUb7nww#g`y8@*2sHYemmOTGF495u`U9*-K*@`4uTGC=@Gi zEVTTN!0nR**eN%KrrC0<`QYZ~PdqBI8{SirXShJrFBn5}L}?)jSL7Bj7lDtgZt0oe zs>C$jEFukFlYjK`Ka8t8r!%mVYw=G*(@SJIFp$*tpgY3Ru#z@#Hx&1-OXJQrM~GcC z`sAsw*7Vw?$_Ti<(fO_t2DNqnF{_)5aoD3}+)=Mk{Nt|0vC?_1aXTsMJ+LpN*G(%H z#`o;%6s1h8ydycvJ)Gwt+}KanKyTe(w4)-pCf4kyz#)cntfjjY_feC`B()|`XWO#= zqg# zODnu_fhgDwOYzoO>SB+h65nj3VY9o@v8HhkspZz4WYse3n94t@LAlv)6>({|F$W&7 zR=d_7IQO`zU!HD!&-v<-M4fWL6!K;&wq3Ctx-;u6NQTv%2V-*CH5c{I2dK8J%Eghk zpKRHM^?2hhBbF&ZTaq#0O3vhc4!CN+r!*{oqecNxXF;_1rrW8A6K-Rfgp}~zv>u-| z?rd~x!(VWY$2&HrIV)@(jgYM`ivzh)sJW$66ECeQ zy64>=T&A6$MNs>11!1N?B{-&#%RVdXXO9l?Wk&MND2e*&5>XmyljGv)F;bur@MwB{ z_+T{1+dX&CSlzbJB0ohuoO&)C;Z8!_n^AnLc5L+F^AhAO024-=QEK!$7dt1N=WV8; zlb~G+upmYJO8e1_D@>>^9!eeqzMCi&woaN?n!5HowfYrLn77aK|7_>0#N3(LYL zilr70hd$%m?OHEJ>pTSwMmcucf^p_ag68zj4%X)n4_1zoIUJt?^9?8??B`e6;gh@7 zMXGmMHP43*7NW|p`Rk+>*|zd08T3}2b=khgD&)5plwBxo?BC8gyw|Gn9nbt;VVb#` zUOJP>CLL*dxGUgsMXf^G3sqTgy}c^|G2ZR*XXp?5dU?lmxo)xliIk&-d9>jT-||=X zTS9=4I@ILA{mYK(W<2%aQIn-PekftehGyH3>@JYa%IRHEm^;_jPP&qqZkI2HPA zG)?NFy^9IvwQh@Ad8A9Nzytl^PwV(3^|mq?+I@uTo0j zRehAmQDZ)xVSM8K7ZDNh1;};BllR|@>G#i!$>RT5P~#_L&gkJqmL?U~xqH)jJqC3M z2~hC^g|4EdTgv2w#pN_Na|-(^x~vs*nn2^y=>NE22|37x2U7V>=6(<2^09hFDUJ%^G{ws8KF|HW zmIKNj5ZO~uMC>3f9)k(eY@Wm0mQr7143RcdTq3@wr93Mhba`s9=4{m*E3>(bh;kvp z(K=kY`1bXBvg}I?eqR>sewd~=oU0QlXBC&{Dy%3&G(g(AV1384h9H4@n{I2WVqGIb z5iJZ+m)mUEJqO~T`tf%w*+lPl7|Jk0RHJ~GC|DL^5VHtlhP$cS1P)UsVZhT6QYo=y zE(el>N1QQ@w5ELxd$BPFG+=k(^q!eWNVf0lc!?%=)BR8{{VhLrj+ysiXlcY~6l|qN zCdT)qK;qz9#S2e_UkX9p({TL`dC>21Q6wSHZxZNetdw z^zU0*S^%Wr&`SQhIq%QSEB@b#f~+tDQ)jZZPlUkogbyaxO^P5^;SHwt<3`~ien=*Y zYz*PvZ}xTf#z%41;Xq)I*Lt5Sh%aJh4y)YlKK0m}7uCsq86F?+UN0Ui+Ml0$2UV9k zU20$UqYvVFWc_db4rUJVW^!bI@f2R8%f4jn<4*i;*EXWOsY5=>Gh&`xL}ff81+6Ia zZ6n#yqT=z$msqbJ_WJCb-wu|JQs{hU3*{gYjHHB!Y6Byq%wz5SsYB>ko5yw{djRvR zv1XYeo_qnz7@pSzK3mv5IsS1Phz$=jqF|D&mQ|X9xZ%J5^VG7oncQ8X|7=6jvall1 z%g6ki9sU0K_;`V)J$iAw^9eIA5Su7oCg*ybi&?@x9dhuwhfxBa%TUZwJI?_$slqJf zg6_gh1aiO#i=mZ|;%8n0SW+%~HN__#5QxtmwCH4G53yPzDkl!$-n#Dn>c9>x4}q9n z;B<-Y&r;?m^YG=^`W^q*xt_^TXJVVKIF~@%=n4`l!nW{9C4Jm?ba07{FyATim*If0 z1LQ5Jj6a+ymHQ%Fx6{@AWp`!9pyfquT=7KP9hMzyfi1GtSf#%3CVIBp*D0B&Fy7;r z1d4JWq{gbx_IGS`X>mG5jf_&1A`Y1iJGP!0G;t$>F;Gdc8JuDhx}D#AaDsb*7@QQQ za-U*??-8>@XcO+7;@USzB?TTu7PAUWv zbrzmTYoi6N2UWwkd0<;Oiow-rpwcyvR(eo4s2WBbrU0hRm0Tn?6KXl zx)U@3p|#Abx>gQ44yp!e0LfcObz$;a;@uDMz^lszEY9 z8bHK{DOu+gqNu^3YLEhu4iH!KyrvK7hETK;Pz}NW831t=&vPDAt;9j&2hk9Mg_wZh z%6&D$sno$KN-j{1;>sQBy}Ejrby#J+Le_`^s!_1s2@1XiadZ~qstqdIFi?%+F9GYN zX6s;W*IeaS9la(ER#Q|jpc)Hr0mpmI*2wyaHW6}9iNYtEGPB9-*=L1RRI z0jkl;W3&u3=T3T?QfsL3T#BmE+~px`=1SYzl&A~Mq`9GmmE2Bx)oAh1DWi%d1B7j|xT;pmiTl+SDrr^N?4KhA#`pd-z$7e7%adfzfE~mJ8FP!Dakwre5?h>``pP8I^BsFvoBQcON2Gdzczqgi=pZUM4 zoTkKF&nKmZ{$VTuR>5dxy{JeN_BG~JW1)W-s{)ICv{EJ=@PcFk%|hCB z@zG>xo|Pm~W`Js}lXpD4)9f!doV@GKHF&Mmo+*c}0jhDtb24z8U#tVh)TuVtLY-)o z>M0Bq=MyrMcEGhdU4Qhdahm4{aGXcf_1409_$l3qM(K`n1$#DJps|j9I_&FhKfr)$ zoZ&eIIL;ZyI{68e(xbcv^~mYTb70p7`T}VWDpMXcrJz<(HI93G++S7TpeJY~oiS4# z$xlgMHBR=N1|0MQqV*eV#V7w;idT(OJcpo22bzI~Qp?OmwX9=}iG6zV-Zs{nlA)(9(TYGdka$o>5GHs2`r4$^ zP4_<;deuPHgRT&S*{z|7C#GD(GXmAJyL%8JC`e##+R!LRT@_UW4G+o)!bYSCte2FW zTk=l_UbU@D9#jzII#Hjq=GAB;>_ng%=wuA9-hO`Mr|-7c$xN$*s`_LtW$QW*YSAHt z_4Zr>mFCP7k@VyL(#81pL&fP=tIfqz*GJ_%%-Y~|7S(~Ob=b#$6La0KlS^AK0-Tu2 W@e$$T^>lG@@egcJ1Z)5R0002_rQA*c literal 0 HcmV?d00001 diff --git a/internal/database/migrations/20260129083458_create_transfer_laying_sequence.up.sql b/internal/database/migrations/20260129083458_create_transfer_laying_sequence.up.sql new file mode 100644 index 00000000..f5f5bdf7 --- /dev/null +++ b/internal/database/migrations/20260129083458_create_transfer_laying_sequence.up.sql @@ -0,0 +1,33 @@ +-- Create sequence for transfer laying movement number +CREATE SEQUENCE transfer_laying_seq START +WITH + 1 INCREMENT BY 1 MINVALUE 1 MAXVALUE 99999 NO CYCLE; + +-- Set sequence starting value based on existing data (if any) +-- This prevents duplicate movement numbers if there's already data +DO $$ DECLARE max_existing INTEGER; + +BEGIN +-- Check if table exists and has data +IF EXISTS ( + SELECT 1 + FROM information_schema.tables + WHERE + table_schema = 'public' + AND table_name = 'transfer_to_layings' +) THEN +-- Get max ID from existing records +SELECT COALESCE(MAX(id), 0) INTO max_existing +FROM transfer_to_layings; + +-- Set sequence to start after the highest existing ID +IF max_existing > 0 THEN PERFORM setval ( + 'transfer_laying_seq', + max_existing +); + +END IF; + +END IF; + +END $$; \ No newline at end of file diff --git a/internal/modules/production/transfer_layings/repositories/laying_transfer.repository.go b/internal/modules/production/transfer_layings/repositories/laying_transfer.repository.go index ebf63252..b3d7e7bc 100644 --- a/internal/modules/production/transfer_layings/repositories/laying_transfer.repository.go +++ b/internal/modules/production/transfer_layings/repositories/laying_transfer.repository.go @@ -2,6 +2,7 @@ package repository import ( "context" + "fmt" "strings" "gitlab.com/mbugroup/lti-api.git/internal/common/repository" @@ -16,6 +17,10 @@ type TransferLayingRepository interface { // Tambah method baru untuk query dengan filter lengkap GetAllWithFilters(ctx context.Context, offset int, limit int, params *GetAllFilterParams) ([]entity.LayingTransfer, int64, error) + + // Get sequence for movement number + GetNextMovementNumber(ctx context.Context) (int64, error) + GenerateMovementNumber(ctx context.Context) (string, error) } type TransferLayingRepositoryImpl struct { @@ -29,6 +34,26 @@ func NewTransferLayingRepository(db *gorm.DB) TransferLayingRepository { db: db, } } + +func (r *TransferLayingRepositoryImpl) GetNextMovementNumber(ctx context.Context) (int64, error) { + var seq int64 + err := r.db.WithContext(ctx).Raw("SELECT nextval('transfer_laying_seq')").Scan(&seq).Error + if err != nil { + return 0, err + } + return seq, nil +} + +func (r *TransferLayingRepositoryImpl) GenerateMovementNumber(ctx context.Context) (string, error) { + seq, err := r.GetNextMovementNumber(ctx) + if err != nil { + return "", err + } + // Format: TL00001, TL00002, dst + movementNumber := fmt.Sprintf("TL%05d", seq) + return movementNumber, nil +} + func (r *TransferLayingRepositoryImpl) IdExists(ctx context.Context, id uint) (bool, error) { return repository.Exists[entity.LayingTransfer](ctx, r.db, id) } diff --git a/internal/modules/production/transfer_layings/services/transfer_laying.service.go b/internal/modules/production/transfer_layings/services/transfer_laying.service.go index 310391c6..2acc2e8f 100644 --- a/internal/modules/production/transfer_layings/services/transfer_laying.service.go +++ b/internal/modules/production/transfer_layings/services/transfer_laying.service.go @@ -271,7 +271,11 @@ func (s *transferLayingService) CreateOne(c *fiber.Ctx, req *validation.Create) return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Jumlah total sumber (%.0f) harus sama dengan jumlah total tujuan (%.0f)", totalSourceQty, totalTargetQty)) } - transferNumber := fmt.Sprintf("TL-%d", time.Now().UnixNano()) + transferNumber, err := s.Repository.GenerateMovementNumber(c.Context()) + if err != nil { + s.Log.Errorf("Failed to generate movement number: %+v", err) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Gagal membuat nomor transfer") + } createBody := &entity.LayingTransfer{ TransferNumber: transferNumber, From 22bd0e589150661bd0c7ba1b4bf5614b4169d24b Mon Sep 17 00:00:00 2001 From: aguhh18 Date: Fri, 30 Jan 2026 08:24:43 +0700 Subject: [PATCH 10/12] FEAT[BE] :enhance UpdateOne method with validation for source and target kandang ownership and quantity checks --- .../services/transfer_laying.service.go | 141 +++++++++++++----- 1 file changed, 105 insertions(+), 36 deletions(-) diff --git a/internal/modules/production/transfer_layings/services/transfer_laying.service.go b/internal/modules/production/transfer_layings/services/transfer_laying.service.go index 2acc2e8f..396944e1 100644 --- a/internal/modules/production/transfer_layings/services/transfer_laying.service.go +++ b/internal/modules/production/transfer_layings/services/transfer_laying.service.go @@ -444,15 +444,105 @@ func (s *transferLayingService) UpdateOne(c *fiber.Ctx, req *validation.Update, return nil, fiber.NewError(fiber.StatusBadRequest, "Target project flock not found") } + sourceKandangIDs := make([]uint, len(req.SourceKandangs)) + for i, detail := range req.SourceKandangs { + sourceKandangIDs[i] = detail.ProjectFlockKandangId + } + + if err := s.validateKandangOwnership( + c.Context(), + req.SourceProjectFlockId, + sourceKandangIDs, + ); err != nil { + return nil, err + } + + targetKandangIDs := make([]uint, len(req.TargetKandangs)) + for i, detail := range req.TargetKandangs { + targetKandangIDs[i] = detail.ProjectFlockKandangId + } + + if err := s.validateKandangOwnership( + c.Context(), + req.TargetProjectFlockId, + targetKandangIDs, + ); err != nil { + return nil, err + } + transferDate, err := time.Parse("2006-01-02", req.TransferDate) if err != nil { return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid transfer date format") } + var totalSourceQty, totalTargetQty float64 + sourceWarehouseMap := make(map[uint]uint) + + for _, sourceDetail := range req.SourceKandangs { + if sourceDetail.Quantity <= 0 { + continue + } + totalSourceQty += sourceDetail.Quantity + + populations, err := s.ProjectFlockPopulationRepo.GetByProjectFlockKandangID(c.Context(), sourceDetail.ProjectFlockKandangId) + if err != nil { + return nil, err + } + + var totalPopulation float64 + var productWarehouseId uint + for _, pop := range populations { + totalPopulation += pop.TotalQty + if productWarehouseId == 0 { + productWarehouseId = pop.ProductWarehouseId + } + } + + if totalPopulation == 0 { + return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Kandang sumber %d tidak memiliki populasi untuk ditransfer", sourceDetail.ProjectFlockKandangId)) + } + + if totalPopulation < sourceDetail.Quantity { + return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Kandang sumber %d jumlah tidak mencukupi. Tersedia: %.0f, Diminta: %.0f", sourceDetail.ProjectFlockKandangId, totalPopulation, sourceDetail.Quantity)) + } + + sourceWarehouseMap[sourceDetail.ProjectFlockKandangId] = productWarehouseId + } + + for _, targetDetail := range req.TargetKandangs { + if targetDetail.Quantity <= 0 { + continue + } + totalTargetQty += targetDetail.Quantity + } + + if totalSourceQty == 0 { + return nil, fiber.NewError(fiber.StatusBadRequest, "Minimal harus ada 1 kandang sumber dengan jumlah lebih dari 0") + } + if totalTargetQty == 0 { + return nil, fiber.NewError(fiber.StatusBadRequest, "Minimal harus ada 1 kandang tujuan dengan jumlah lebih dari 0") + } + + if totalSourceQty != totalTargetQty { + return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Jumlah total sumber (%.0f) harus sama dengan jumlah total tujuan (%.0f)", totalSourceQty, totalTargetQty)) + } + + // Ambil productWarehouseId pertama dari source yang valid (quantity > 0) + var firstProductWarehouseId uint + for _, sourceDetail := range req.SourceKandangs { + if sourceDetail.Quantity > 0 { + if pwId, ok := sourceWarehouseMap[sourceDetail.ProjectFlockKandangId]; ok { + firstProductWarehouseId = pwId + break + } + } + } + err = s.Repository.DB().WithContext(c.Context()).Transaction(func(dbTransaction *gorm.DB) error { repoTx := s.Repository.WithTx(dbTransaction) sourceRepo := s.LayingTransferSourceRepo.WithTx(dbTransaction) targetRepo := s.LayingTransferTargetRepo.WithTx(dbTransaction) + pwRepo := rInventory.NewProductWarehouseRepository(dbTransaction) // Hapus old sources dan targets for _, oldSource := range existingTransfer.Sources { @@ -476,26 +566,11 @@ func (s *transferLayingService) UpdateOne(c *fiber.Ctx, req *validation.Update, // Create new sources dengan pending quantity for _, sourceDetail := range req.SourceKandangs { - populations, err := s.ProjectFlockPopulationRepo.GetByProjectFlockKandangID(c.Context(), sourceDetail.ProjectFlockKandangId) - if err != nil { - return fiber.NewError(fiber.StatusInternalServerError, "Failed to get populations") + if sourceDetail.Quantity == 0 { + continue } - if len(populations) == 0 { - return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Source kandang %d has no population available", sourceDetail.ProjectFlockKandangId)) - } - - var productWarehouseId uint - for _, pop := range populations { - if pop.ProductWarehouseId > 0 { - productWarehouseId = pop.ProductWarehouseId - break - } - } - - if productWarehouseId == 0 { - return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Source kandang %d has no product warehouse", sourceDetail.ProjectFlockKandangId)) - } + productWarehouseId := sourceWarehouseMap[sourceDetail.ProjectFlockKandangId] source := entity.LayingTransferSource{ LayingTransferId: id, @@ -510,7 +585,18 @@ func (s *transferLayingService) UpdateOne(c *fiber.Ctx, req *validation.Update, } } - pwRepo := rInventory.NewProductWarehouseRepository(dbTransaction) + // Ambil product ID dari source warehouse pertama yang valid + var sourceProductID uint + if firstProductWarehouseId > 0 { + sourcePW, err := pwRepo.GetByID(c.Context(), firstProductWarehouseId, nil) + if err == nil { + sourceProductID = sourcePW.ProductId + } + } + + if sourceProductID == 0 { + return fiber.NewError(fiber.StatusInternalServerError, "Failed to get product from source warehouse") + } for _, targetDetail := range req.TargetKandangs { targetprojectFlockKandang, err := s.ProjectFlockKandangRepo.GetByID(c.Context(), targetDetail.ProjectFlockKandangId) @@ -526,23 +612,6 @@ func (s *transferLayingService) UpdateOne(c *fiber.Ctx, req *validation.Update, return fiber.NewError(fiber.StatusInternalServerError, "Failed to get target warehouse") } - // Ambil product ID dari source yang pertama (semua sources seharusnya product-nya sama) - var sourceProductID uint - if len(req.SourceKandangs) > 0 { - firstSourceKandangID := req.SourceKandangs[0].ProjectFlockKandangId - populations, err := s.ProjectFlockPopulationRepo.GetByProjectFlockKandangID(c.Context(), firstSourceKandangID) - if err == nil && len(populations) > 0 && populations[0].ProductWarehouseId > 0 { - sourcePW, err := pwRepo.GetByID(c.Context(), populations[0].ProductWarehouseId, nil) - if err == nil { - sourceProductID = sourcePW.ProductId - } - } - } - - if sourceProductID == 0 { - return fiber.NewError(fiber.StatusInternalServerError, "Failed to get product from source warehouse") - } - targetPW, err := pwRepo.FindByProductWarehouseAndPfk(c.Context(), sourceProductID, targetWarehouse.Id, &targetDetail.ProjectFlockKandangId) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { From d5b3120ea31da910063ffca1f0e8e731651d1fb2 Mon Sep 17 00:00:00 2001 From: aguhh18 Date: Fri, 30 Jan 2026 08:42:03 +0700 Subject: [PATCH 11/12] FEAT[BE] :add GetEggProduksiPiecesAndWeightKgByProjectFlockKandangIdsAll method and update usage in calculateProductionData --- .../repository/common.hpp.repository.go | 19 +++++++++++++++++++ ...8_create_transfer_laying_sequence.down.sql | 2 ++ .../services/closingKeuangan.service.go | 2 +- 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 internal/database/migrations/20260129083458_create_transfer_laying_sequence.down.sql diff --git a/internal/common/repository/common.hpp.repository.go b/internal/common/repository/common.hpp.repository.go index 97ad3800..e0f2bcc5 100644 --- a/internal/common/repository/common.hpp.repository.go +++ b/internal/common/repository/common.hpp.repository.go @@ -20,6 +20,7 @@ type HppCostRepository interface { GetTotalPopulation(ctx context.Context, projectFlockKandangIDs []uint) (float64, error) GetPulletCost(ctx context.Context, projectFlockKandangId uint) (float64, error) GetEggProduksiPiecesAndWeightKgByProjectFlockKandangIds(ctx context.Context, projectFlockKandangIDs []uint, date *time.Time) (float64, float64, error) + GetEggProduksiPiecesAndWeightKgByProjectFlockKandangIdsAll(ctx context.Context, projectFlockKandangIDs []uint) (float64, float64, error) GetEggTerjualPiecesAndWeightKgByProjectFlockKandangIds(ctx context.Context, projectFlockKandangIDs []uint, startDate *time.Time, endDate *time.Time) (float64, float64, error) GetProjectFlockIDByProjectFlockKandangID(ctx context.Context, projectFlockKandangId uint) (uint, error) GetTransferSourceSummary(ctx context.Context, projectFlockKandangId uint) (uint, float64, error) @@ -219,6 +220,24 @@ func (r *HppRepositoryImpl) GetEggProduksiPiecesAndWeightKgByProjectFlockKandang return totals.TotalPieces, totals.TotalWeightKg, nil } +func (r *HppRepositoryImpl) GetEggProduksiPiecesAndWeightKgByProjectFlockKandangIdsAll(ctx context.Context, projectFlockKandangIDs []uint) (float64, float64, error) { + var totals struct { + TotalPieces float64 + TotalWeightKg float64 + } + err := r.db.WithContext(ctx). + Table("recordings AS r"). + Select("COALESCE(SUM(re.qty), 0) AS total_pieces, COALESCE(SUM(re.weight), 0)AS total_weight_kg"). + Joins("JOIN recording_eggs AS re ON re.recording_id = r.id"). + Where("r.project_flock_kandangs_id IN (?)", projectFlockKandangIDs). + Scan(&totals).Error + if err != nil { + return 0, 0, err + } + + return totals.TotalPieces, totals.TotalWeightKg, nil +} + func (r *HppRepositoryImpl) GetEggTerjualPiecesAndWeightKgByProjectFlockKandangIds( ctx context.Context, projectFlockKandangIDs []uint, diff --git a/internal/database/migrations/20260129083458_create_transfer_laying_sequence.down.sql b/internal/database/migrations/20260129083458_create_transfer_laying_sequence.down.sql new file mode 100644 index 00000000..5c44b309 --- /dev/null +++ b/internal/database/migrations/20260129083458_create_transfer_laying_sequence.down.sql @@ -0,0 +1,2 @@ +-- Drop transfer laying sequence +DROP SEQUENCE IF EXISTS transfer_laying_seq; \ No newline at end of file diff --git a/internal/modules/closings/services/closingKeuangan.service.go b/internal/modules/closings/services/closingKeuangan.service.go index ca76c67e..44137fad 100644 --- a/internal/modules/closings/services/closingKeuangan.service.go +++ b/internal/modules/closings/services/closingKeuangan.service.go @@ -262,7 +262,7 @@ func (s closingKeuanganService) calculateProductionData(c *fiber.Ctx, projectFlo } if projectFlock.Category == string(utils.ProjectFlockCategoryLaying) { - _, data.TotalEggWeightKg, err = s.HppRepo.GetEggProduksiPiecesAndWeightKgByProjectFlockKandangIds(c.Context(), projectFlockKandangIDs, nil) + _, data.TotalEggWeightKg, err = s.HppRepo.GetEggProduksiPiecesAndWeightKgByProjectFlockKandangIdsAll(c.Context(), projectFlockKandangIDs) if err != nil { data.TotalEggWeightKg = 0 } From 9b6637066ef3afc4513f435451f9c37781a018da Mon Sep 17 00:00:00 2001 From: giovanni Date: Fri, 30 Jan 2026 10:46:54 +0700 Subject: [PATCH 12/12] fix calculate age at closing data production --- .../closings/dto/closingMarketing.dto.go | 20 +++++++++ .../closings/services/closing.service.go | 42 +++++++------------ .../salesorder_delivery_product.repository.go | 41 ++++++++++++++++++ 3 files changed, 75 insertions(+), 28 deletions(-) diff --git a/internal/modules/closings/dto/closingMarketing.dto.go b/internal/modules/closings/dto/closingMarketing.dto.go index a4bb5cb0..189ef7cb 100644 --- a/internal/modules/closings/dto/closingMarketing.dto.go +++ b/internal/modules/closings/dto/closingMarketing.dto.go @@ -101,6 +101,26 @@ func ToSalesDTO(e entity.MarketingDeliveryProduct) SalesDTO { } } +func ToSalesAgeDTO(e entity.MarketingDeliveryProduct) SalesDTO { + + productFlags := make([]string, len(e.MarketingProduct.ProductWarehouse.Product.Flags)) + for i, f := range e.MarketingProduct.ProductWarehouse.Product.Flags { + productFlags[i] = f.Name + } + + var category string + if e.MarketingProduct.ProductWarehouse.ProjectFlockKandang != nil { + category = e.MarketingProduct.ProductWarehouse.ProjectFlockKandang.ProjectFlock.Category + } + + ageInDay, _ := calculateAgeFromChickin(e.MarketingProduct.ProductWarehouse.ProjectFlockKandang, e.DeliveryDate, productFlags, category) + + return SalesDTO{ + Age: ageInDay, + Qty: e.UsageQty, + } +} + func ToSummaryDto(e []entity.MarketingDeliveryProduct) SummaryDTO { var totalSalesPrice, totalActualPrice, sumSales, sumActual float64 diff --git a/internal/modules/closings/services/closing.service.go b/internal/modules/closings/services/closing.service.go index bd068843..a3cfb2cc 100644 --- a/internal/modules/closings/services/closing.service.go +++ b/internal/modules/closings/services/closing.service.go @@ -841,7 +841,7 @@ func (s closingService) GetClosingDataProduksi(c *fiber.Ctx, projectFlockID uint return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch FCR standard data") } } - age, err := s.calculateAverageSalesAge(c.Context(), projectFlockID) + age, err := s.calculateAverageSalesAge(c.Context(), projectFlockID, kandangID) if err != nil { s.Log.Errorf("Failed to calculate sales age for project flock %d: %+v", projectFlockID, err) return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch sales age data") @@ -1028,38 +1028,24 @@ func (s closingService) GetClosingDataProduksi(c *fiber.Ctx, projectFlockID uint return &result, nil } -func (s closingService) calculateAverageSalesAge(ctx context.Context, projectFlockID uint) (float64, error) { - deliveryProducts, err := s.MarketingDeliveryProductRepo.GetDeliveryProductsByProjectFlockID(ctx, projectFlockID, func(db *gorm.DB) *gorm.DB { - return db. - Preload("MarketingProduct"). - Preload("MarketingProduct.ProductWarehouse"). - Preload("MarketingProduct.ProductWarehouse.ProjectFlockKandang"). - Preload("MarketingProduct.ProductWarehouse.ProjectFlockKandang.Chickins") - }) +func (s closingService) calculateAverageSalesAge(ctx context.Context, projectFlockID uint, projectFlockKandangID *uint) (float64, error) { + penjualan, err := s.MarketingDeliveryProductRepo.GetClosingPenjualanForAgeChickDataProduction(ctx, projectFlockID, projectFlockKandangID) if err != nil { return 0, err } - - var ( - totalQty float64 - totalAgeWeeks float64 - ) - - for _, product := range deliveryProducts { - if product.UsageQty == 0 { - continue - } - projectFlockKandang := product.MarketingProduct.ProductWarehouse.ProjectFlockKandang - ageWeeks := dto.CalculateAgeFromChickinDataProduksi(projectFlockKandang, product.DeliveryDate) - totalAgeWeeks += float64(ageWeeks) * product.UsageQty - totalQty += product.UsageQty + acumulateAgeQty := 0.0 + totalQty := 0.0 + for _, v := range penjualan { + sale := dto.ToSalesAgeDTO(v) + acumulateAgeQty += float64(sale.Age) * sale.Qty + totalQty += sale.Qty + } + if totalQty > 0 { + averageAge := acumulateAgeQty / totalQty + return averageAge, nil } - if totalQty == 0 { - return 0, nil - } - - return totalAgeWeeks / totalQty, nil + return 0, err } func (s closingService) determineProductionWeek(ctx context.Context, projectFlockKandangIDs []uint) (int, error) { diff --git a/internal/modules/marketing/repositories/salesorder_delivery_product.repository.go b/internal/modules/marketing/repositories/salesorder_delivery_product.repository.go index 231c00d4..17394b80 100644 --- a/internal/modules/marketing/repositories/salesorder_delivery_product.repository.go +++ b/internal/modules/marketing/repositories/salesorder_delivery_product.repository.go @@ -22,6 +22,7 @@ type MarketingDeliveryProductRepository interface { UpdateFifoFields(ctx context.Context, id uint, usageQty, pendingQty float64) error GetUsageQty(ctx context.Context, id uint) (float64, error) ResetFifoFields(ctx context.Context, id uint) error + GetClosingPenjualanForAgeChickDataProduction(ctx context.Context, projectFlockID uint, projectFlockKandangID *uint) ([]entity.MarketingDeliveryProduct, error) } type MarketingDeliveryProductRepositoryImpl struct { @@ -93,6 +94,46 @@ func (r *MarketingDeliveryProductRepositoryImpl) GetClosingPenjualan(ctx context return deliveryProducts, nil } +func (r *MarketingDeliveryProductRepositoryImpl) GetClosingPenjualanForAgeChickDataProduction(ctx context.Context, projectFlockID uint, projectFlockKandangID *uint) ([]entity.MarketingDeliveryProduct, error) { + var deliveryProducts []entity.MarketingDeliveryProduct + + db := r.DB().WithContext(ctx). + Joins("JOIN marketing_products ON marketing_products.id = marketing_delivery_products.marketing_product_id"). + Joins("JOIN product_warehouses ON product_warehouses.id = marketing_products.product_warehouse_id"). + Joins("JOIN products ON products.id = product_warehouses.product_id"). + Joins("JOIN flags ON flags.flagable_id = products.id AND flags.flagable_type = 'products'"). + Joins("JOIN project_flock_kandangs ON project_flock_kandangs.id = product_warehouses.project_flock_kandang_id"). + Where("project_flock_kandangs.project_flock_id = ?", projectFlockID). + Where("flags.name IN (?)", []string{ + string(utils.FlagAyamAfkir), + string(utils.FlagAyamCulling), + string(utils.FlagPullet), + string(utils.FlagLayer), + }). + Where("marketing_delivery_products.delivery_date IS NOT NULL"). + Distinct("marketing_delivery_products.*") + + if projectFlockKandangID != nil { + db = db.Where("product_warehouses.project_flock_kandang_id = ?", *projectFlockKandangID) + } + + db = db. + Preload("MarketingProduct"). + Preload("MarketingProduct.ProductWarehouse"). + Preload("MarketingProduct.ProductWarehouse.Product"). + Preload("MarketingProduct.ProductWarehouse.Product.ProductCategory"). + Preload("MarketingProduct.ProductWarehouse.Product.Flags"). + Preload("MarketingProduct.ProductWarehouse.ProjectFlockKandang"). + Preload("MarketingProduct.ProductWarehouse.ProjectFlockKandang.Chickins"). + Order("marketing_delivery_products.delivery_date DESC") + + if err := db.Find(&deliveryProducts).Error; err != nil { + return nil, err + } + + return deliveryProducts, nil +} + func (r *MarketingDeliveryProductRepositoryImpl) GetClosingPenjualanByCategory(ctx context.Context, projectFlockID uint, projectFlockKandangID *uint, category string) ([]entity.MarketingDeliveryProduct, error) { var deliveryProducts []entity.MarketingDeliveryProduct