mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-24 07:15:43 +00:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a38491fef1 | |||
| b234778634 | |||
| 59e71856ac | |||
| 1ee97b91a5 | |||
| 3a5c49c511 | |||
| 48730e1b74 | |||
| 8220e34302 | |||
| c72db5bd18 | |||
| 86f37a89c1 | |||
| 20f1be2ef8 | |||
| 672c76d26d | |||
| 219a6a39ed | |||
| c91d84b652 | |||
| bf14ab7865 | |||
| 31bb28f7da | |||
| a390d1d23a |
@@ -3,13 +3,9 @@ root = "."
|
|||||||
tmp_dir = "tmp"
|
tmp_dir = "tmp"
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
# Build binary utama
|
cmd = "go build -o ./tmp/main ./cmd/api"
|
||||||
cmd = "go build -o /lti-api/tmp/main ./cmd/api"
|
bin = "tmp/main"
|
||||||
# Lokasi binary hasil build
|
full_bin = "APP_ENV=dev ./tmp/main"
|
||||||
bin = "/lti-api/tmp/main"
|
|
||||||
# Jalankan binary langsung dengan environment dev
|
|
||||||
full_bin = "APP_ENV=dev /lti-api/tmp/main"
|
|
||||||
# File yang dipantau oleh Air
|
|
||||||
include_ext = ["go", "tpl", "tmpl", "html"]
|
include_ext = ["go", "tpl", "tmpl", "html"]
|
||||||
exclude_dir = ["vendor", "tmp"]
|
exclude_dir = ["vendor", "tmp"]
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# server configuration
|
||||||
|
# Env value : prod || dev
|
||||||
|
VERSION=0.0.1
|
||||||
|
APP_ENV=dev
|
||||||
|
APP_HOST=0.0.0.0
|
||||||
|
APP_PORT=8081
|
||||||
|
APP_URL=http://localhost:8081
|
||||||
|
|
||||||
|
# database configuration
|
||||||
|
DB_HOST=postgresdb
|
||||||
|
DB_USER=postgres
|
||||||
|
DB_PASSWORD=changeme
|
||||||
|
DB_NAME=db_lti_erp
|
||||||
|
DB_PORT=5432
|
||||||
|
DB_PORT_HOST=5542
|
||||||
|
|
||||||
|
# JWT
|
||||||
|
JWT_SECRET=changeme
|
||||||
|
JWT_ACCESS_EXP_MINUTES=30
|
||||||
|
JWT_REFRESH_EXP_DAYS=30
|
||||||
|
JWT_RESET_PASSWORD_EXP_MINUTES=10
|
||||||
|
JWT_VERIFY_EMAIL_EXP_MINUTES=10
|
||||||
|
|
||||||
|
# CORS
|
||||||
|
CORS_ALLOW_ORIGINS=changeme
|
||||||
|
CORS_ALLOW_METHODS=GET,POST,PUT,PATCH,DELETE,OPTIONS
|
||||||
|
CORS_ALLOW_HEADERS=Authorization,Content-Type,X-Requested-With
|
||||||
|
CORS_EXPOSE_HEADERS=Link,Location
|
||||||
|
CORS_ALLOW_CREDENTIALS=true
|
||||||
|
CORS_MAX_AGE=600
|
||||||
|
|
||||||
|
# Redis
|
||||||
|
REDIS_URL=redis://redis:6379/0
|
||||||
|
REDIS_PORT_HOST=6381
|
||||||
@@ -10,13 +10,8 @@ bin/
|
|||||||
*.exe
|
*.exe
|
||||||
*.out
|
*.out
|
||||||
|
|
||||||
Makefile
|
|
||||||
docker-compose.local.yml
|
|
||||||
docker-compose.yaml
|
|
||||||
Dockerfile.local
|
|
||||||
# Go build cache
|
# Go build cache
|
||||||
.gocache/
|
.gocache/
|
||||||
vendor
|
|
||||||
|
|
||||||
# Logs & reports
|
# Logs & reports
|
||||||
*.log
|
*.log
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
stages:
|
|
||||||
- deploy
|
|
||||||
|
|
||||||
deploy-dev:
|
|
||||||
stage: deploy
|
|
||||||
image: alpine:3.20
|
|
||||||
variables:
|
|
||||||
DEPLOY_APP: "LTI-MBUGROUP"
|
|
||||||
|
|
||||||
before_script:
|
|
||||||
- echo "🧰 Installing dependencies..."
|
|
||||||
- apk update && apk add --no-cache openssh git curl
|
|
||||||
- mkdir -p ~/.ssh
|
|
||||||
- echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
|
|
||||||
- chmod 600 ~/.ssh/id_rsa
|
|
||||||
- eval $(ssh-agent -s)
|
|
||||||
- ssh-add ~/.ssh/id_rsa
|
|
||||||
- ssh-keyscan -H "$SERVER_IP" >> ~/.ssh/known_hosts
|
|
||||||
|
|
||||||
script:
|
|
||||||
- echo "🚀 Deploying latest code to $SERVER_USER@$SERVER_IP"
|
|
||||||
- >
|
|
||||||
if ssh -o StrictHostKeyChecking=no "$SERVER_USER@$SERVER_IP" "
|
|
||||||
cd /home/devops/docker/deployment/development/lti-api &&
|
|
||||||
git fetch origin development &&
|
|
||||||
git reset --hard origin/development &&
|
|
||||||
docker compose restart dev-api-lti || docker compose up -d dev-api-lti
|
|
||||||
"; then
|
|
||||||
STATUS='success';
|
|
||||||
else
|
|
||||||
STATUS='failed';
|
|
||||||
fi;
|
|
||||||
|
|
||||||
RUN_URL="${CI_PROJECT_URL}/-/pipelines/${CI_PIPELINE_ID}";
|
|
||||||
|
|
||||||
if [ "$STATUS" = "success" ]; then
|
|
||||||
COLOR=3066993;
|
|
||||||
TITLE="✅ Deployment API Succeeded";
|
|
||||||
DESC="Deployment job on branch \`${CI_COMMIT_REF_NAME}\` completed successfully.";
|
|
||||||
else
|
|
||||||
COLOR=15158332;
|
|
||||||
TITLE="❌ Deployment API Failed Gaes";
|
|
||||||
DESC="Deployment job on branch \`${CI_COMMIT_REF_NAME}\` failed.";
|
|
||||||
fi;
|
|
||||||
|
|
||||||
echo "{
|
|
||||||
\"username\": \"CI Bot\",
|
|
||||||
\"embeds\": [{
|
|
||||||
\"title\": \"$TITLE\",
|
|
||||||
\"description\": \"$DESC\",
|
|
||||||
\"color\": $COLOR,
|
|
||||||
\"fields\": [
|
|
||||||
{\"name\": \"Repository\", \"value\": \"${CI_PROJECT_PATH}\", \"inline\": true},
|
|
||||||
{\"name\": \"Actor\", \"value\": \"${GITLAB_USER_LOGIN}\", \"inline\": true},
|
|
||||||
{\"name\": \"Commit\", \"value\": \"${CI_COMMIT_SHA}\", \"inline\": false},
|
|
||||||
{\"name\": \"Pipeline\", \"value\": \"[Open run](${RUN_URL})\", \"inline\": false}
|
|
||||||
]
|
|
||||||
}]
|
|
||||||
}" > payload.json;
|
|
||||||
|
|
||||||
echo "📡 Sending notification to Discord...";
|
|
||||||
curl -sS -H "Content-Type: application/json" \
|
|
||||||
-d @payload.json "$DISCORD_WEBHOOK_URL";
|
|
||||||
|
|
||||||
only:
|
|
||||||
- development
|
|
||||||
|
|
||||||
environment:
|
|
||||||
name: development
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
stages:
|
|
||||||
- build
|
|
||||||
- deploy
|
|
||||||
- cleanup
|
|
||||||
|
|
||||||
# ==============================
|
|
||||||
# 🏗️ BUILD IMAGE (Overwrite :dev)
|
|
||||||
# ==============================
|
|
||||||
build_image:
|
|
||||||
stage: build
|
|
||||||
image: docker:latest
|
|
||||||
services:
|
|
||||||
- docker:dind
|
|
||||||
variables:
|
|
||||||
DOCKER_TLS_CERTDIR: "/certs"
|
|
||||||
script:
|
|
||||||
- echo "🔧 Building Docker image for :dev..."
|
|
||||||
- docker login -u gitlab-ci-token -p "$CI_JOB_TOKEN" "$CI_REGISTRY"
|
|
||||||
- docker build -f Dockerfile.local -t registry.gitlab.com/mbugroup/sso-mbugroup/lti-api:dev .
|
|
||||||
- docker push registry.gitlab.com/mbugroup/sso-mbugroup/lti-api:dev
|
|
||||||
only:
|
|
||||||
- development
|
|
||||||
|
|
||||||
# ==============================
|
|
||||||
# 🚀 DEPLOY TO DEV SERVER
|
|
||||||
# ==============================
|
|
||||||
deploy_lti:
|
|
||||||
stage: deploy
|
|
||||||
image: alpine:latest
|
|
||||||
before_script:
|
|
||||||
- apk add --no-cache openssh-client bash curl
|
|
||||||
- mkdir -p ~/.ssh
|
|
||||||
- echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
|
|
||||||
- chmod 600 ~/.ssh/id_rsa
|
|
||||||
- ssh-keyscan -H "$SERVER_IP" >> ~/.ssh/known_hosts
|
|
||||||
|
|
||||||
script:
|
|
||||||
- echo "🚀 Deploy ke ${SERVER_USER}@${SERVER_IP} menggunakan image :dev"
|
|
||||||
- |
|
|
||||||
ssh -o StrictHostKeyChecking=no ${SERVER_USER}@${SERVER_IP} bash -s <<REMOTE
|
|
||||||
set -e
|
|
||||||
|
|
||||||
APP_NAME="lti-api"
|
|
||||||
DOCKER_IMAGE="registry.gitlab.com/mbugroup/sso-mbugroup/lti-api:dev"
|
|
||||||
NETWORK_NAME="lti-network"
|
|
||||||
ENV_PATH="/home/devops/code/api/lti-api/.env.lti-api"
|
|
||||||
PORT=8081
|
|
||||||
|
|
||||||
echo "🔑 Login ke GitLab Registry..."
|
|
||||||
echo "${GITLAB_TOKEN}" | docker login -u "${GITLAB_USER}" --password-stdin registry.gitlab.com
|
|
||||||
|
|
||||||
echo "🛑 Stop & remove old container..."
|
|
||||||
docker stop "\${APP_NAME}" >/dev/null 2>&1 || true
|
|
||||||
docker rm -f "\${APP_NAME}" >/dev/null 2>&1 || true
|
|
||||||
|
|
||||||
echo "🧹 Membersihkan container zombie di port \${PORT}..."
|
|
||||||
OLD_ID=\$(docker ps -aq --filter "publish=\${PORT}")
|
|
||||||
if [ -n "\${OLD_ID}" ]; then
|
|
||||||
echo "⚠️ Container lain masih pakai port \${PORT}, hapus..."
|
|
||||||
docker stop \${OLD_ID} >/dev/null 2>&1 || true
|
|
||||||
docker rm -f \${OLD_ID} >/dev/null 2>&1 || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "🐳 Pull image baru..."
|
|
||||||
docker pull "\${DOCKER_IMAGE}"
|
|
||||||
|
|
||||||
echo "🚀 Run container baru..."
|
|
||||||
docker run -d --name "\${APP_NAME}" --restart always \
|
|
||||||
--env-file "\${ENV_PATH}" \
|
|
||||||
-p \${PORT}:8081 \
|
|
||||||
--network "\${NETWORK_NAME}" \
|
|
||||||
"\${DOCKER_IMAGE}"
|
|
||||||
|
|
||||||
echo "✅ Deployment selesai di port \${PORT}"
|
|
||||||
REMOTE
|
|
||||||
|
|
||||||
only:
|
|
||||||
- development
|
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
# --- Load .env kalau ada, dan export ke shell child ---
|
||||||
|
ifneq (,$(wildcard .env))
|
||||||
|
include .env
|
||||||
|
export
|
||||||
|
endif
|
||||||
|
|
||||||
|
# --- Konfigurasi umum ---
|
||||||
|
COMPOSE ?= docker compose -f docker-compose.local.yml
|
||||||
|
NETWORK ?= lti-api_go-network
|
||||||
|
MIGRATE_IMAGE ?= migrate/migrate
|
||||||
|
MIGRATIONS_DIR := $(PWD)/internal/database/migrations
|
||||||
|
|
||||||
|
# Fallback agar tetap jalan meski .env kosong
|
||||||
|
DB_HOST ?= postgresdb
|
||||||
|
DB_PORT ?= 5432
|
||||||
|
DB_USER ?= postgres
|
||||||
|
DB_PASSWORD ?= postgres
|
||||||
|
DB_NAME ?= db_lti_erp
|
||||||
|
|
||||||
|
DB_URL := postgres://$(DB_USER):$(DB_PASSWORD)@$(DB_HOST):$(DB_PORT)/$(DB_NAME)?sslmode=disable
|
||||||
|
|
||||||
|
# Tunggu DB ready memakai pg_isready dari image postgres
|
||||||
|
WAIT_DB := docker run --rm --network $(NETWORK) postgres:alpine \
|
||||||
|
sh -c 'until pg_isready -h $(DB_HOST) -p $(DB_PORT) -U $(DB_USER) -d $(DB_NAME); do echo "waiting for postgres..."; sleep 1; done'
|
||||||
|
|
||||||
|
# Default target
|
||||||
|
.DEFAULT_GOAL := start
|
||||||
|
|
||||||
|
# --- Daftar phony targets ---
|
||||||
|
.PHONY: start build test lint gen \
|
||||||
|
db-up wait-db \
|
||||||
|
migration-% migrate-up migrate-down migrate-fresh \
|
||||||
|
seed \
|
||||||
|
docker-local docker-down docker-nuke docker-cache psql
|
||||||
|
|
||||||
|
# --- Go workflow ---
|
||||||
|
start:
|
||||||
|
@go run cmd/api/main.go
|
||||||
|
|
||||||
|
build:
|
||||||
|
@go build -o tmp/app ./cmd/api
|
||||||
|
|
||||||
|
test:
|
||||||
|
@go test ./test/...
|
||||||
|
|
||||||
|
lint:
|
||||||
|
@golangci-lint run
|
||||||
|
|
||||||
|
# --- Compose / DB helpers ---
|
||||||
|
db-up:
|
||||||
|
@$(COMPOSE) up -d postgresdb
|
||||||
|
|
||||||
|
wait-db:
|
||||||
|
@$(WAIT_DB)
|
||||||
|
|
||||||
|
# --- Migration (pembuatan file) ---
|
||||||
|
# Contoh: make migration-create_users_table
|
||||||
|
# ":" akan diubah ke "_" (biar aman untuk nama file)
|
||||||
|
migration-%:
|
||||||
|
@migrate create -ext sql -dir $(MIGRATIONS_DIR) $(subst :,_,$*)
|
||||||
|
|
||||||
|
# --- Migration (apply via docker image 'migrate') ---
|
||||||
|
migrate-up: db-up wait-db
|
||||||
|
@docker run --rm -v $(MIGRATIONS_DIR):/migrations --network $(NETWORK) \
|
||||||
|
$(MIGRATE_IMAGE) -path=/migrations/ -database "$(DB_URL)" up
|
||||||
|
|
||||||
|
# Contoh:
|
||||||
|
# make migrate-down step=2 → rollback 2 step
|
||||||
|
# make migrate-down → rollback semua
|
||||||
|
|
||||||
|
migrate-down: db-up wait-db
|
||||||
|
@if [ -n "$(step)" ]; then \
|
||||||
|
echo "⬇️ Migrating down $(step) step(s)..."; \
|
||||||
|
docker run --rm -v $(MIGRATIONS_DIR):/migrations --network $(NETWORK) \
|
||||||
|
$(MIGRATE_IMAGE) -path=/migrations/ -database "$(DB_URL)" down $(step); \
|
||||||
|
else \
|
||||||
|
echo "⬇️ Migrating down ALL steps..."; \
|
||||||
|
docker run --rm -v $(MIGRATIONS_DIR):/migrations --network $(NETWORK) \
|
||||||
|
$(MIGRATE_IMAGE) -path=/migrations/ -database "$(DB_URL)" down -all; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
migrate-fresh: migrate-down migrate-up
|
||||||
|
@true
|
||||||
|
|
||||||
|
# Pakai: make migrate-force v=20250917120000
|
||||||
|
migrate-force:
|
||||||
|
@docker run --rm -v $(MIGRATIONS_DIR):/migrations --network $(NETWORK) \
|
||||||
|
$(MIGRATE_IMAGE) -path=/migrations/ -database "$(DB_URL)" force $(v)
|
||||||
|
|
||||||
|
|
||||||
|
# --- Seeder ---
|
||||||
|
seed: db-up wait-db
|
||||||
|
@$(COMPOSE) run --rm app go run cmd/seed/main.go
|
||||||
|
|
||||||
|
# --- Docker orchestration convenience ---
|
||||||
|
docker-local:
|
||||||
|
@$(COMPOSE) up --build -d
|
||||||
|
|
||||||
|
docker-down:
|
||||||
|
@$(COMPOSE) down --remove-orphans
|
||||||
|
|
||||||
|
# ⚠️ Akan menghapus container, images dan volumes.
|
||||||
|
docker-nuke:
|
||||||
|
@$(COMPOSE) down --rmi all --volumes --remove-orphans
|
||||||
|
|
||||||
|
docker-cache:
|
||||||
|
@docker builder prune -f
|
||||||
|
|
||||||
|
# --- PSQL shell ke DB di container ---
|
||||||
|
psql: db-up
|
||||||
|
@$(COMPOSE) exec -it postgresdb psql -U $(DB_USER) -d $(DB_NAME)
|
||||||
|
|
||||||
|
# Single feature
|
||||||
|
# example: make gen feat=product-category
|
||||||
|
|
||||||
|
# Sub feature
|
||||||
|
# make gen feat=master/area
|
||||||
|
gen:
|
||||||
|
@go run tools/gen.go $(feat)
|
||||||
|
# @goimports -w internal
|
||||||
@@ -9,13 +9,10 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gitlab.com/mbugroup/lti-api.git/internal/cache"
|
|
||||||
"gitlab.com/mbugroup/lti-api.git/internal/config"
|
"gitlab.com/mbugroup/lti-api.git/internal/config"
|
||||||
"gitlab.com/mbugroup/lti-api.git/internal/database"
|
"gitlab.com/mbugroup/lti-api.git/internal/database"
|
||||||
"gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
"gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
||||||
"gitlab.com/mbugroup/lti-api.git/internal/modules/sso/session"
|
|
||||||
"gitlab.com/mbugroup/lti-api.git/internal/route"
|
"gitlab.com/mbugroup/lti-api.git/internal/route"
|
||||||
"gitlab.com/mbugroup/lti-api.git/internal/sso"
|
|
||||||
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
@@ -36,7 +33,6 @@ func main() {
|
|||||||
defer closeDatabase(db)
|
defer closeDatabase(db)
|
||||||
rdb := setupRedis()
|
rdb := setupRedis()
|
||||||
defer rdb.Close()
|
defer rdb.Close()
|
||||||
setupSSO(ctx, rdb)
|
|
||||||
setupRoutes(app, db, rdb)
|
setupRoutes(app, db, rdb)
|
||||||
|
|
||||||
address := fmt.Sprintf("%s:%d", config.AppHost, config.AppPort)
|
address := fmt.Sprintf("%s:%d", config.AppHost, config.AppPort)
|
||||||
@@ -56,47 +52,10 @@ func setupRedis() *redis.Client {
|
|||||||
if err := rdb.Ping(context.Background()).Err(); err != nil {
|
if err := rdb.Ping(context.Background()).Err(); err != nil {
|
||||||
utils.Log.Fatalf("Redis ping failed: %v", err)
|
utils.Log.Fatalf("Redis ping failed: %v", err)
|
||||||
}
|
}
|
||||||
cache.SetRedis(rdb)
|
|
||||||
utils.Log.Infof("Redis connected: %s", config.RedisURL)
|
utils.Log.Infof("Redis connected: %s", config.RedisURL)
|
||||||
return rdb
|
return rdb
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupSSO(ctx context.Context, rdb *redis.Client) {
|
|
||||||
const (
|
|
||||||
maxAttempts = 12
|
|
||||||
retryDelay = 5 * time.Second
|
|
||||||
)
|
|
||||||
|
|
||||||
var lastErr error
|
|
||||||
for attempt := 1; attempt <= maxAttempts; attempt++ {
|
|
||||||
if err := sso.Init(ctx, config.SSOJWKSURL, config.SSOIssuer, config.SSOAllowedAudiences); err != nil {
|
|
||||||
lastErr = err
|
|
||||||
utils.Log.WithError(err).Warnf("SSO initialization attempt %d/%d failed", attempt, maxAttempts)
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
utils.Log.Fatalf("SSO initialization aborted: %v", ctx.Err())
|
|
||||||
case <-time.After(retryDelay):
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
lastErr = nil
|
|
||||||
if attempt > 1 {
|
|
||||||
utils.Log.Infof("SSO initialization succeeded after %d attempts", attempt)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if lastErr != nil {
|
|
||||||
utils.Log.Fatalf("SSO initialization failed: %v", lastErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if rdb != nil {
|
|
||||||
session.SetRevocationStore(session.NewRevocationStore(rdb, config.SSOTokenBlacklistPrefix))
|
|
||||||
} else {
|
|
||||||
session.SetRevocationStore(nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func setupFiberApp() *fiber.App {
|
func setupFiberApp() *fiber.App {
|
||||||
app := fiber.New(config.FiberConfig())
|
app := fiber.New(config.FiberConfig())
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
POSTGRES_USER=postgres
|
|
||||||
POSTGRES_PASSWORD=Postgres@Secure2025!
|
|
||||||
POSTGRES_DB=db_lti_erp
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
-- ============================================================
|
|
||||||
-- 🧩 INIT SCRIPT: CREATE LIMITED APP USER FOR LTI API
|
|
||||||
-- ============================================================
|
|
||||||
|
|
||||||
-- Buat user aplikasi jika belum ada
|
|
||||||
DO
|
|
||||||
$$
|
|
||||||
BEGIN
|
|
||||||
IF NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = 'app_lti_user') THEN
|
|
||||||
CREATE ROLE app_lti_user WITH LOGIN PASSWORD 'AppLti@Secure2025!' NOINHERIT NOCREATEROLE NOCREATEDB NOSUPERUSER;
|
|
||||||
RAISE NOTICE '✅ Role app_lti_user created successfully.';
|
|
||||||
ELSE
|
|
||||||
RAISE NOTICE 'ℹ️ Role app_lti_user already exists.';
|
|
||||||
END IF;
|
|
||||||
END
|
|
||||||
$$;
|
|
||||||
|
|
||||||
-- Buat database jika belum ada
|
|
||||||
DO
|
|
||||||
$$
|
|
||||||
BEGIN
|
|
||||||
IF NOT EXISTS (SELECT FROM pg_database WHERE datname = 'db_lti_erp') THEN
|
|
||||||
CREATE DATABASE db_lti_erp OWNER app_lti_user;
|
|
||||||
RAISE NOTICE '✅ Database db_lti_erp created and owned by app_lti_user.';
|
|
||||||
ELSE
|
|
||||||
RAISE NOTICE 'ℹ️ Database db_lti_erp already exists.';
|
|
||||||
END IF;
|
|
||||||
END
|
|
||||||
$$;
|
|
||||||
|
|
||||||
\connect db_lti_erp
|
|
||||||
|
|
||||||
-- Beri hak CRUD untuk app_lti_user
|
|
||||||
GRANT CONNECT ON DATABASE db_lti_erp TO app_lti_user;
|
|
||||||
GRANT USAGE ON SCHEMA public TO app_lti_user;
|
|
||||||
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_lti_user;
|
|
||||||
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO app_lti_user;
|
|
||||||
|
|
||||||
-- Set default privileges agar tabel baru juga bisa diakses
|
|
||||||
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
|
||||||
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_lti_user;
|
|
||||||
|
|
||||||
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
|
||||||
GRANT USAGE, SELECT ON SEQUENCES TO app_lti_user;
|
|
||||||
|
|
||||||
-- Tampilkan hasil
|
|
||||||
\du app_lti_user
|
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
services:
|
||||||
|
postgresdb:
|
||||||
|
image: postgres:alpine
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "${DB_PORT_HOST:-5542}:5432"
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: ${DB_USER:-postgres}
|
||||||
|
POSTGRES_PASSWORD: ${DB_PASSWORD:-postgres}
|
||||||
|
POSTGRES_DB: ${DB_NAME:-db_lti_erp}
|
||||||
|
volumes:
|
||||||
|
- dbdata:/var/lib/postgresql/data
|
||||||
|
- ./internal/database/init:/docker-entrypoint-initdb.d
|
||||||
|
networks: [go-network]
|
||||||
|
healthcheck:
|
||||||
|
test:
|
||||||
|
[
|
||||||
|
"CMD-SHELL",
|
||||||
|
"pg_isready -U ${DB_USER:-postgres} -d ${DB_NAME:-db_lti_erp}",
|
||||||
|
]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
redis:
|
||||||
|
image: redis:7-alpine
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "${REDIS_PORT_HOST:-6381}:6379"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 3s
|
||||||
|
retries: 10
|
||||||
|
networks: [go-network]
|
||||||
|
|
||||||
|
app:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile.local
|
||||||
|
image: cosmtrek/air:v1.52.3
|
||||||
|
working_dir: /lti-api
|
||||||
|
volumes:
|
||||||
|
- .:/lti-api
|
||||||
|
- ./internal/config/jwtRS256.key:/run/keys/jwtRS256.key
|
||||||
|
- ./internal/config/jwtRS256.key.pub:/run/keys/jwtRS256.key.pub
|
||||||
|
command: air -c .air.toml
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
environment:
|
||||||
|
DB_HOST: postgresdb
|
||||||
|
DB_PORT: 5432
|
||||||
|
DB_USER: ${DB_USER:-postgres}
|
||||||
|
DB_PASSWORD: ${DB_PASSWORD:-postgres}
|
||||||
|
DB_NAME: ${DB_NAME:-db_lti_erp}
|
||||||
|
REDIS_URL: ${REDIS_URL:-redis://redis:6379/0}
|
||||||
|
ports:
|
||||||
|
- "${APP_PORT:-8081}:8081"
|
||||||
|
depends_on:
|
||||||
|
postgresdb:
|
||||||
|
condition: service_healthy
|
||||||
|
networks: [go-network]
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "wget -qO- http://localhost:8081/healthz || exit 1"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 3s
|
||||||
|
retries: 10
|
||||||
|
start_period: 10s
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
dbdata:
|
||||||
|
go-mod-cache:
|
||||||
|
go-build-cache:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
go-network:
|
||||||
|
name: lti-api_go-network
|
||||||
|
driver: bridge
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
services:
|
|
||||||
dev-api-lti:
|
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
container_name: dev-api-lti
|
|
||||||
working_dir: /lti-api
|
|
||||||
command: ["/bin/sh", "scripts/entrypoint.sh"]
|
|
||||||
ports:
|
|
||||||
- "8081:8081"
|
|
||||||
env_file:
|
|
||||||
- .env
|
|
||||||
environment:
|
|
||||||
# override agar koneksi ke container internal
|
|
||||||
DB_HOST: dev-postgres-lti
|
|
||||||
DB_PORT: 5432
|
|
||||||
REDIS_URL: redis://dev-redis-lti:6379/0
|
|
||||||
volumes:
|
|
||||||
- .:/lti-api
|
|
||||||
- ./.air.toml:/lti-api/.air.toml:ro
|
|
||||||
- ./internal/config/jwtRS256.key:/run/keys/jwtRS256.key
|
|
||||||
- ./internal/config/jwtRS256.key.pub:/run/keys/jwtRS256.key.pub
|
|
||||||
depends_on:
|
|
||||||
- dev-postgres-lti
|
|
||||||
- dev-redis-lti
|
|
||||||
networks:
|
|
||||||
- lti-network
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD-SHELL", "wget -qO- http://localhost:8081/healthz || exit 1"]
|
|
||||||
interval: 10s
|
|
||||||
timeout: 3s
|
|
||||||
retries: 10
|
|
||||||
start_period: 10s
|
|
||||||
deploy:
|
|
||||||
resources:
|
|
||||||
limits:
|
|
||||||
cpus: "2.0"
|
|
||||||
memory: 2G
|
|
||||||
reservations:
|
|
||||||
cpus: "1.0"
|
|
||||||
memory: 512M
|
|
||||||
|
|
||||||
dev-postgres-lti:
|
|
||||||
image: postgres:15-alpine
|
|
||||||
container_name: dev-postgres-lti
|
|
||||||
restart: always
|
|
||||||
env_file:
|
|
||||||
- credential/.env.db
|
|
||||||
ports:
|
|
||||||
- "5433:5432"
|
|
||||||
volumes:
|
|
||||||
- dev-postgres-lti-data:/var/lib/postgresql/data
|
|
||||||
- ./credential:/docker-entrypoint-initdb.d:ro
|
|
||||||
networks:
|
|
||||||
- lti-network
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-postgres} -d ${DB_NAME:-db_lti_erp}"]
|
|
||||||
interval: 10s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 5
|
|
||||||
start_period: 5s
|
|
||||||
deploy:
|
|
||||||
resources:
|
|
||||||
limits:
|
|
||||||
cpus: "1.0"
|
|
||||||
memory: 2G
|
|
||||||
reservations:
|
|
||||||
cpus: "0.5"
|
|
||||||
memory: 512M
|
|
||||||
|
|
||||||
dev-redis-lti:
|
|
||||||
image: redis:7-alpine
|
|
||||||
container_name: dev-redis-lti
|
|
||||||
restart: always
|
|
||||||
ports:
|
|
||||||
- "6380:6379"
|
|
||||||
networks:
|
|
||||||
- lti-network
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD", "redis-cli", "ping"]
|
|
||||||
interval: 10s
|
|
||||||
timeout: 3s
|
|
||||||
retries: 10
|
|
||||||
deploy:
|
|
||||||
resources:
|
|
||||||
limits:
|
|
||||||
cpus: "0.5"
|
|
||||||
memory: 512M
|
|
||||||
reservations:
|
|
||||||
cpus: "0.2"
|
|
||||||
memory: 256M
|
|
||||||
|
|
||||||
networks:
|
|
||||||
lti-network:
|
|
||||||
driver: bridge
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
dev-postgres-lti-data:
|
|
||||||
@@ -3,14 +3,12 @@ module gitlab.com/mbugroup/lti-api.git
|
|||||||
go 1.23
|
go 1.23
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/MicahParks/keyfunc/v2 v2.1.0
|
|
||||||
github.com/bytedance/sonic v1.12.1
|
github.com/bytedance/sonic v1.12.1
|
||||||
github.com/glebarez/sqlite v1.11.0
|
github.com/glebarez/sqlite v1.11.0
|
||||||
github.com/go-playground/validator/v10 v10.27.0
|
github.com/go-playground/validator/v10 v10.27.0
|
||||||
github.com/gofiber/contrib/jwt v1.0.10
|
github.com/gofiber/contrib/jwt v1.0.10
|
||||||
github.com/gofiber/fiber/v2 v2.52.5
|
github.com/gofiber/fiber/v2 v2.52.5
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.1
|
github.com/golang-jwt/jwt/v5 v5.2.1
|
||||||
github.com/jackc/pgconn v1.14.1
|
|
||||||
github.com/redis/go-redis/v9 v9.14.0
|
github.com/redis/go-redis/v9 v9.14.0
|
||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/sirupsen/logrus v1.9.3
|
||||||
github.com/spf13/viper v1.19.0
|
github.com/spf13/viper v1.19.0
|
||||||
@@ -20,6 +18,7 @@ require (
|
|||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/MicahParks/keyfunc/v2 v2.1.0 // indirect
|
||||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
github.com/andybalholm/brotli v1.1.0 // indirect
|
||||||
github.com/bytedance/sonic/loader v0.2.0 // indirect
|
github.com/bytedance/sonic/loader v0.2.0 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
@@ -35,10 +34,7 @@ require (
|
|||||||
github.com/google/go-cmp v0.6.0 // indirect
|
github.com/google/go-cmp v0.6.0 // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
|
|
||||||
github.com/jackc/pgio v1.0.0 // indirect
|
|
||||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
github.com/jackc/pgproto3/v2 v2.3.2 // indirect
|
|
||||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||||
github.com/jackc/pgx/v5 v5.5.5 // indirect
|
github.com/jackc/pgx/v5 v5.5.5 // indirect
|
||||||
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
||||||
|
|||||||
@@ -17,10 +17,6 @@ github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/
|
|||||||
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||||
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
||||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
|
||||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
|
||||||
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
|
||||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||||
@@ -47,7 +43,6 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
|
|||||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4=
|
github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4=
|
||||||
github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
|
github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
|
||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
|
||||||
github.com/gofiber/contrib/jwt v1.0.10 h1:/ilGepl6i0Bntl0Zcd+lAzagY8BiS1+fEiAj32HMApk=
|
github.com/gofiber/contrib/jwt v1.0.10 h1:/ilGepl6i0Bntl0Zcd+lAzagY8BiS1+fEiAj32HMApk=
|
||||||
github.com/gofiber/contrib/jwt v1.0.10/go.mod h1:1qBENE6sZ6PPT4xIpBzx1VxeyROQO7sj48OlM1I9qdU=
|
github.com/gofiber/contrib/jwt v1.0.10/go.mod h1:1qBENE6sZ6PPT4xIpBzx1VxeyROQO7sj48OlM1I9qdU=
|
||||||
github.com/gofiber/fiber/v2 v2.52.5 h1:tWoP1MJQjGEe4GB5TUGOi7P2E0ZMMRx5ZTG4rT+yGMo=
|
github.com/gofiber/fiber/v2 v2.52.5 h1:tWoP1MJQjGEe4GB5TUGOi7P2E0ZMMRx5ZTG4rT+yGMo=
|
||||||
@@ -62,47 +57,12 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
|||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
|
|
||||||
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
|
||||||
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
|
|
||||||
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
|
||||||
github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
|
|
||||||
github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
|
|
||||||
github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
|
|
||||||
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
|
|
||||||
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
|
|
||||||
github.com/jackc/pgconn v1.14.1 h1:smbxIaZA08n6YuxEX1sDyjV/qkbtUtkH20qLkR9MUR4=
|
|
||||||
github.com/jackc/pgconn v1.14.1/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E=
|
|
||||||
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
|
|
||||||
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
|
|
||||||
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
|
|
||||||
github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=
|
|
||||||
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc=
|
|
||||||
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
|
|
||||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
|
|
||||||
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
|
|
||||||
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
|
|
||||||
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
|
||||||
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
|
||||||
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
|
||||||
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
|
||||||
github.com/jackc/pgproto3/v2 v2.3.2 h1:7eY55bdBeCz1F2fTzSz69QC+pG46jYq9/jtSPiJ5nn0=
|
|
||||||
github.com/jackc/pgproto3/v2 v2.3.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
|
||||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
|
||||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
||||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||||
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
|
|
||||||
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
|
|
||||||
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
|
|
||||||
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
|
|
||||||
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
|
|
||||||
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
|
|
||||||
github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw=
|
github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw=
|
||||||
github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
|
github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
|
||||||
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
|
||||||
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
|
||||||
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
|
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
|
||||||
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||||
@@ -115,28 +75,16 @@ github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02
|
|||||||
github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU=
|
github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|
||||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
|
||||||
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
|
||||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
|
||||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
|
||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
|
||||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
|
||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
@@ -148,7 +96,6 @@ github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6
|
|||||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||||
github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw=
|
github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw=
|
||||||
github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0=
|
github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
@@ -162,17 +109,10 @@ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
|||||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
|
||||||
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
|
|
||||||
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
|
|
||||||
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
|
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
|
||||||
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
|
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
|
||||||
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
||||||
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
||||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
|
||||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
|
||||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
|
||||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||||
@@ -186,15 +126,10 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
|
|||||||
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
|
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
|
||||||
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
|
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
|
||||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
@@ -215,41 +150,24 @@ github.com/valyala/fasthttp v1.55.0/go.mod h1:NkY9JtkrpPKmgwV3HTaS2HWaJss9RSIsRV
|
|||||||
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
|
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
|
||||||
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
|
||||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
|
||||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
|
||||||
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
|
||||||
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
|
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
|
||||||
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
|
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
|
||||||
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
|
||||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
|
||||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU=
|
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU=
|
||||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
|
||||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
|
||||||
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
|
||||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
|
||||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
|
||||||
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
||||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
||||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
|
||||||
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||||
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@@ -257,14 +175,7 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ
|
|||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
@@ -273,43 +184,28 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
|
||||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
|
||||||
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
|
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
|
||||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
|
||||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|||||||
Vendored
-38
@@ -1,38 +0,0 @@
|
|||||||
package cache
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/redis/go-redis/v9"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
redisClient *redis.Client
|
|
||||||
mu sync.RWMutex
|
|
||||||
)
|
|
||||||
|
|
||||||
// SetRedis assigns the global redis client used across the application.
|
|
||||||
func SetRedis(client *redis.Client) {
|
|
||||||
mu.Lock()
|
|
||||||
defer mu.Unlock()
|
|
||||||
redisClient = client
|
|
||||||
}
|
|
||||||
|
|
||||||
// Redis returns the configured redis client. It may be nil if not yet initialised.
|
|
||||||
func Redis() *redis.Client {
|
|
||||||
mu.RLock()
|
|
||||||
defer mu.RUnlock()
|
|
||||||
return redisClient
|
|
||||||
}
|
|
||||||
|
|
||||||
// MustRedis returns the redis client or panics if it has not been set.
|
|
||||||
func MustRedis() *redis.Client {
|
|
||||||
mu.RLock()
|
|
||||||
client := redisClient
|
|
||||||
mu.RUnlock()
|
|
||||||
if client == nil {
|
|
||||||
panic(errors.New("redis client not initialised"))
|
|
||||||
}
|
|
||||||
return client
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
package capabilities
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
recordings "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// FromPermissions returns a filtered map of capabilities that the frontend can use
|
|
||||||
// to toggle features. Only permissions recognized by the application are exposed.
|
|
||||||
func FromPermissions(perms []string) map[string]bool {
|
|
||||||
if len(perms) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
out := make(map[string]bool)
|
|
||||||
for _, perm := range perms {
|
|
||||||
if key, ok := normalizeAndAllow(perm); ok {
|
|
||||||
out[key] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(out) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
func normalizeAndAllow(perm string) (string, bool) {
|
|
||||||
perm = strings.ToLower(strings.TrimSpace(perm))
|
|
||||||
if perm == "" {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
if _, ok := allowed[perm]; !ok {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
return perm, true
|
|
||||||
}
|
|
||||||
|
|
||||||
var allowed = map[string]struct{}{
|
|
||||||
recordings.PermissionRecordingRead: {},
|
|
||||||
recordings.PermissionRecordingCreate: {},
|
|
||||||
recordings.PermissionRecordingUpdate: {},
|
|
||||||
recordings.PermissionRecordingDelete: {},
|
|
||||||
}
|
|
||||||
@@ -11,7 +11,6 @@ type BaseRepository[T any] interface {
|
|||||||
GetAll(ctx context.Context, offset, limit int, modifier func(*gorm.DB) *gorm.DB) ([]T, int64, error)
|
GetAll(ctx context.Context, offset, limit int, modifier func(*gorm.DB) *gorm.DB) ([]T, int64, error)
|
||||||
GetByID(ctx context.Context, id uint, modifier func(*gorm.DB) *gorm.DB) (*T, error)
|
GetByID(ctx context.Context, id uint, modifier func(*gorm.DB) *gorm.DB) (*T, error)
|
||||||
GetByIDs(ctx context.Context, ids []uint, modifier func(*gorm.DB) *gorm.DB) ([]T, error)
|
GetByIDs(ctx context.Context, ids []uint, modifier func(*gorm.DB) *gorm.DB) ([]T, error)
|
||||||
First(ctx context.Context, modifier func(*gorm.DB) *gorm.DB) (*T, error)
|
|
||||||
|
|
||||||
CreateOne(ctx context.Context, entity *T, modifier func(*gorm.DB) *gorm.DB) error
|
CreateOne(ctx context.Context, entity *T, modifier func(*gorm.DB) *gorm.DB) error
|
||||||
CreateMany(ctx context.Context, entities []*T, modifier func(*gorm.DB) *gorm.DB) error
|
CreateMany(ctx context.Context, entities []*T, modifier func(*gorm.DB) *gorm.DB) error
|
||||||
@@ -97,21 +96,6 @@ func (r *BaseRepositoryImpl[T]) GetByIDs(
|
|||||||
return entities, nil
|
return entities, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *BaseRepositoryImpl[T]) First(
|
|
||||||
ctx context.Context,
|
|
||||||
modifier func(*gorm.DB) *gorm.DB,
|
|
||||||
) (*T, error) {
|
|
||||||
entity := new(T)
|
|
||||||
q := r.db.WithContext(ctx)
|
|
||||||
if modifier != nil {
|
|
||||||
q = modifier(q)
|
|
||||||
}
|
|
||||||
if err := q.First(entity).Error; err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return entity, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- CREATE ----
|
// ---- CREATE ----
|
||||||
func (r *BaseRepositoryImpl[T]) CreateOne(
|
func (r *BaseRepositoryImpl[T]) CreateOne(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
|
|||||||
+22
-164
@@ -2,69 +2,36 @@ package config
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||||
|
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SSOClientConfig struct {
|
|
||||||
PublicID string `json:"public_id"`
|
|
||||||
RedirectURI string `json:"redirect_uri"`
|
|
||||||
Scope string `json:"scope"`
|
|
||||||
// Prompt string `json:"prompt"`
|
|
||||||
DefaultReturnURI string `json:"default_return_uri"`
|
|
||||||
AllowedReturnOrigins []string `json:"allowed_return_origins"`
|
|
||||||
SyncSecret string `json:"sync_secret"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
IsProd bool
|
IsProd bool
|
||||||
AppHost string
|
AppHost string
|
||||||
Version string
|
Version string
|
||||||
LogLevel string
|
LogLevel string
|
||||||
AppPort int
|
AppPort int
|
||||||
DBHost string
|
DBHost string
|
||||||
DBUser string
|
DBUser string
|
||||||
DBPassword string
|
DBPassword string
|
||||||
DBName string
|
DBName string
|
||||||
DBPort int
|
DBPort int
|
||||||
DBSSLMode string
|
JWTSecret string
|
||||||
DBSSLRootCert string
|
JWTAccessExp int
|
||||||
DBSSLCert string
|
JWTRefreshExp int
|
||||||
DBSSLKey string
|
JWTResetPasswordExp int
|
||||||
JWTSecret string
|
JWTVerifyEmailExp int
|
||||||
JWTAccessExp int
|
RedisURL string
|
||||||
JWTRefreshExp int
|
CORSAllowOrigins []string
|
||||||
JWTResetPasswordExp int
|
CORSAllowMethods []string
|
||||||
JWTVerifyEmailExp int
|
CORSAllowHeaders []string
|
||||||
RedisURL string
|
CORSExposeHeaders []string
|
||||||
CORSAllowOrigins []string
|
CORSAllowCredentials bool
|
||||||
CORSAllowMethods []string
|
CORSMaxAge int
|
||||||
CORSAllowHeaders []string
|
|
||||||
CORSExposeHeaders []string
|
|
||||||
CORSAllowCredentials bool
|
|
||||||
CORSMaxAge int
|
|
||||||
SSOIssuer string
|
|
||||||
SSOJWKSURL string
|
|
||||||
SSOAllowedAudiences []string
|
|
||||||
SSOAuthorizeURL string
|
|
||||||
SSOTokenURL string
|
|
||||||
SSOGetMeURL string
|
|
||||||
SSOClients map[string]SSOClientConfig
|
|
||||||
SSOAccessCookieName string
|
|
||||||
SSORefreshCookieName string
|
|
||||||
SSOCookieDomain string
|
|
||||||
SSOCookieSecure bool
|
|
||||||
SSOCookieSameSite string
|
|
||||||
SSOTokenBlacklistPrefix string
|
|
||||||
SSOPKCETTL time.Duration
|
|
||||||
SSOUserSyncDrift time.Duration
|
|
||||||
SSOUserSyncNonceTTL time.Duration
|
|
||||||
SSOUserSyncMaxBodyBytes int
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -83,10 +50,6 @@ func init() {
|
|||||||
DBPassword = viper.GetString("DB_PASSWORD")
|
DBPassword = viper.GetString("DB_PASSWORD")
|
||||||
DBName = viper.GetString("DB_NAME")
|
DBName = viper.GetString("DB_NAME")
|
||||||
DBPort = viper.GetInt("DB_PORT")
|
DBPort = viper.GetInt("DB_PORT")
|
||||||
DBSSLMode = defaultString(viper.GetString("DB_SSLMODE"), "disable")
|
|
||||||
DBSSLRootCert = strings.TrimSpace(viper.GetString("DB_SSLROOTCERT"))
|
|
||||||
DBSSLCert = strings.TrimSpace(viper.GetString("DB_SSLCERT"))
|
|
||||||
DBSSLKey = strings.TrimSpace(viper.GetString("DB_SSLKEY"))
|
|
||||||
|
|
||||||
// jwt configuration
|
// jwt configuration
|
||||||
JWTSecret = viper.GetString("JWT_SECRET")
|
JWTSecret = viper.GetString("JWT_SECRET")
|
||||||
@@ -105,44 +68,6 @@ func init() {
|
|||||||
|
|
||||||
// Redis
|
// Redis
|
||||||
RedisURL = viper.GetString("REDIS_URL")
|
RedisURL = viper.GetString("REDIS_URL")
|
||||||
|
|
||||||
// SSO integration
|
|
||||||
SSOIssuer = viper.GetString("SSO_ISSUER")
|
|
||||||
SSOJWKSURL = viper.GetString("SSO_JWKS_URL")
|
|
||||||
SSOAllowedAudiences = parseList("SSO_ALLOWED_AUDIENCES")
|
|
||||||
SSOAuthorizeURL = viper.GetString("SSO_AUTHORIZE_URL")
|
|
||||||
SSOTokenURL = viper.GetString("SSO_TOKEN_URL")
|
|
||||||
SSOGetMeURL = viper.GetString("SSO_GETME_URL")
|
|
||||||
SSOAccessCookieName = defaultString(viper.GetString("SSO_ACCESS_COOKIE_NAME"), "sso_access")
|
|
||||||
SSORefreshCookieName = defaultString(viper.GetString("SSO_REFRESH_COOKIE_NAME"), "sso_refresh")
|
|
||||||
SSOCookieDomain = viper.GetString("SSO_COOKIE_DOMAIN")
|
|
||||||
SSOCookieSecure = viper.GetBool("SSO_COOKIE_SECURE")
|
|
||||||
SSOCookieSameSite = defaultString(viper.GetString("SSO_COOKIE_SAMESITE"), "Lax")
|
|
||||||
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
|
|
||||||
} else {
|
|
||||||
SSOPKCETTL = 5 * time.Minute
|
|
||||||
}
|
|
||||||
SSOClients = loadSSOClients("SSO_CLIENTS")
|
|
||||||
if drift := viper.GetInt("SSO_USER_SYNC_SIGNATURE_DRIFT_SECONDS"); drift > 0 {
|
|
||||||
SSOUserSyncDrift = time.Duration(drift) * time.Second
|
|
||||||
} else {
|
|
||||||
SSOUserSyncDrift = 2 * time.Minute
|
|
||||||
}
|
|
||||||
if ttl := viper.GetInt("SSO_USER_SYNC_NONCE_TTL_SECONDS"); ttl > 0 {
|
|
||||||
SSOUserSyncNonceTTL = time.Duration(ttl) * time.Second
|
|
||||||
} else {
|
|
||||||
SSOUserSyncNonceTTL = 10 * time.Minute
|
|
||||||
}
|
|
||||||
SSOUserSyncMaxBodyBytes = viper.GetInt("SSO_USER_SYNC_MAX_BODY_BYTES")
|
|
||||||
if SSOUserSyncMaxBodyBytes <= 0 {
|
|
||||||
SSOUserSyncMaxBodyBytes = 32 * 1024
|
|
||||||
}
|
|
||||||
|
|
||||||
if IsProd {
|
|
||||||
ensureProdConfig()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadConfig() {
|
func loadConfig() {
|
||||||
@@ -192,70 +117,3 @@ func parseListWithDefault(key, def string) []string {
|
|||||||
}
|
}
|
||||||
return parts
|
return parts
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadSSOClients(key string) map[string]SSOClientConfig {
|
|
||||||
clients := make(map[string]SSOClientConfig)
|
|
||||||
raw := strings.TrimSpace(viper.GetString(key))
|
|
||||||
if raw == "" {
|
|
||||||
return clients
|
|
||||||
}
|
|
||||||
if err := json.Unmarshal([]byte(raw), &clients); err != nil {
|
|
||||||
utils.Log.Errorf("Failed to parse %s: %v", key, err)
|
|
||||||
return make(map[string]SSOClientConfig)
|
|
||||||
}
|
|
||||||
result := make(map[string]SSOClientConfig, len(clients))
|
|
||||||
for alias, cfg := range clients {
|
|
||||||
alias = strings.ToLower(strings.TrimSpace(alias))
|
|
||||||
for i, origin := range cfg.AllowedReturnOrigins {
|
|
||||||
cfg.AllowedReturnOrigins[i] = strings.TrimSpace(origin)
|
|
||||||
}
|
|
||||||
cfg.SyncSecret = strings.TrimSpace(cfg.SyncSecret)
|
|
||||||
result[alias] = cfg
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func defaultString(v, def string) string {
|
|
||||||
if strings.TrimSpace(v) == "" {
|
|
||||||
return def
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func ensureProdConfig() {
|
|
||||||
if SSOAuthorizeURL == "" || !strings.HasPrefix(SSOAuthorizeURL, "https://") {
|
|
||||||
panic("SSO_AUTHORIZE_URL must be https in production")
|
|
||||||
}
|
|
||||||
if SSOTokenURL == "" || !strings.HasPrefix(SSOTokenURL, "https://") {
|
|
||||||
panic("SSO_TOKEN_URL must be https in production")
|
|
||||||
}
|
|
||||||
if SSOGetMeURL == "" || !strings.HasPrefix(SSOGetMeURL, "https://") {
|
|
||||||
panic("SSO_GETME_URL must be https in production")
|
|
||||||
}
|
|
||||||
if !SSOCookieSecure {
|
|
||||||
panic("SSO_COOKIE_SECURE must be true in production")
|
|
||||||
}
|
|
||||||
if SSOCookieDomain == "" {
|
|
||||||
panic("SSO_COOKIE_DOMAIN must be configured in production")
|
|
||||||
}
|
|
||||||
if len(SSOAllowedAudiences) == 0 {
|
|
||||||
panic("SSO_ALLOWED_AUDIENCES must contain at least one audience in production")
|
|
||||||
}
|
|
||||||
for alias, cfg := range SSOClients {
|
|
||||||
if strings.TrimSpace(cfg.SyncSecret) == "" {
|
|
||||||
panic(fmt.Sprintf("SSO_CLIENTS[%s].sync_secret must be configured in production", alias))
|
|
||||||
}
|
|
||||||
if len(cfg.SyncSecret) < 16 {
|
|
||||||
panic(fmt.Sprintf("SSO_CLIENTS[%s].sync_secret must be at least 16 characters", alias))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if SSOUserSyncDrift <= 0 {
|
|
||||||
panic("SSO_USER_SYNC_SIGNATURE_DRIFT_SECONDS must be greater than zero in production")
|
|
||||||
}
|
|
||||||
if SSOUserSyncNonceTTL <= 0 {
|
|
||||||
panic("SSO_USER_SYNC_NONCE_TTL_SECONDS must be greater than zero in production")
|
|
||||||
}
|
|
||||||
if SSOUserSyncMaxBodyBytes <= 0 {
|
|
||||||
panic("SSO_USER_SYNC_MAX_BODY_BYTES must be greater than zero in production")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package database
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gitlab.com/mbugroup/lti-api.git/internal/config"
|
"gitlab.com/mbugroup/lti-api.git/internal/config"
|
||||||
@@ -14,25 +13,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Connect(dbHost, dbName string) *gorm.DB {
|
func Connect(dbHost, dbName string) *gorm.DB {
|
||||||
parts := []string{
|
dsn := fmt.Sprintf(
|
||||||
fmt.Sprintf("host=%s", dbHost),
|
"host=%s user=%s password=%s dbname=%s port=%d sslmode=disable TimeZone=Asia/Shanghai",
|
||||||
fmt.Sprintf("user=%s", config.DBUser),
|
dbHost, config.DBUser, config.DBPassword, dbName, config.DBPort,
|
||||||
fmt.Sprintf("password=%s", config.DBPassword),
|
)
|
||||||
fmt.Sprintf("dbname=%s", dbName),
|
|
||||||
fmt.Sprintf("port=%d", config.DBPort),
|
|
||||||
fmt.Sprintf("sslmode=%s", config.DBSSLMode),
|
|
||||||
"TimeZone=Asia/Shanghai",
|
|
||||||
}
|
|
||||||
if config.DBSSLRootCert != "" {
|
|
||||||
parts = append(parts, fmt.Sprintf("sslrootcert=%s", config.DBSSLRootCert))
|
|
||||||
}
|
|
||||||
if config.DBSSLCert != "" {
|
|
||||||
parts = append(parts, fmt.Sprintf("sslcert=%s", config.DBSSLCert))
|
|
||||||
}
|
|
||||||
if config.DBSSLKey != "" {
|
|
||||||
parts = append(parts, fmt.Sprintf("sslkey=%s", config.DBSSLKey))
|
|
||||||
}
|
|
||||||
dsn := strings.Join(parts, " ")
|
|
||||||
|
|
||||||
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
|
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
|
||||||
Logger: logger.Default.LogMode(logger.Info),
|
Logger: logger.Default.LogMode(logger.Info),
|
||||||
|
|||||||
@@ -9,9 +9,13 @@ CREATE TABLE users (
|
|||||||
deleted_at TIMESTAMPTZ
|
deleted_at TIMESTAMPTZ
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE UNIQUE INDEX users_id_user_unique ON users (id_user) WHERE deleted_at IS NULL;
|
CREATE UNIQUE INDEX users_id_user_unique ON users (id_user)
|
||||||
|
WHERE
|
||||||
|
deleted_at IS NULL;
|
||||||
|
|
||||||
CREATE UNIQUE INDEX users_email_unique ON users (email) WHERE deleted_at IS NULL;
|
CREATE UNIQUE INDEX users_email_unique ON users (email)
|
||||||
|
WHERE
|
||||||
|
deleted_at IS NULL;
|
||||||
|
|
||||||
-- FLAGS
|
-- FLAGS
|
||||||
CREATE TABLE flags (
|
CREATE TABLE flags (
|
||||||
@@ -330,4 +334,4 @@ CREATE INDEX stock_logs_created_by_idx ON stock_logs (created_by);
|
|||||||
|
|
||||||
CREATE INDEX stock_logs_created_at_idx ON stock_logs (created_at);
|
CREATE INDEX stock_logs_created_at_idx ON stock_logs (created_at);
|
||||||
|
|
||||||
CREATE INDEX stock_logs_deleted_at_idx ON stock_logs (deleted_at);
|
CREATE INDEX stock_logs_deleted_at_idx ON stock_logs (deleted_at);
|
||||||
-2
@@ -1,2 +0,0 @@
|
|||||||
ALTER TABLE users
|
|
||||||
DROP CONSTRAINT IF EXISTS users_id_user_key;
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
ALTER TABLE users
|
|
||||||
ADD CONSTRAINT users_id_user_key UNIQUE (id_user);
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS project_chickins (
|
|
||||||
id BIGSERIAL PRIMARY KEY,
|
|
||||||
project_flock_kandang_id BIGINT NOT NULL,
|
|
||||||
chick_in_date DATE NOT NULL,
|
|
||||||
quantity NUMERIC(15, 3) NOT NULL,
|
|
||||||
note TEXT,
|
|
||||||
created_by BIGINT NOT NULL,
|
|
||||||
created_at TIMESTAMPTZ DEFAULT now(),
|
|
||||||
updated_at TIMESTAMPTZ DEFAULT now(),
|
|
||||||
deleted_at TIMESTAMPTZ
|
|
||||||
);
|
|
||||||
|
|
||||||
-- FOREIGN KEYS (dijalankan setelah semua tabel parent ada)
|
|
||||||
DO $$
|
|
||||||
BEGIN
|
|
||||||
IF EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'project_flock_kandangs') THEN
|
|
||||||
ALTER TABLE project_chickins
|
|
||||||
ADD CONSTRAINT fk_project_flock_kandang_id
|
|
||||||
FOREIGN KEY (project_flock_kandang_id)
|
|
||||||
REFERENCES project_flock_kandangs(id)
|
|
||||||
ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
IF EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'users') THEN
|
|
||||||
ALTER TABLE project_chickins
|
|
||||||
ADD CONSTRAINT fk_created_by
|
|
||||||
FOREIGN KEY (created_by)
|
|
||||||
REFERENCES users(id)
|
|
||||||
ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
||||||
END IF;
|
|
||||||
END $$;
|
|
||||||
|
|
||||||
-- INDEXES
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_project_chickins_project_flock_kandang_id ON project_chickins (project_flock_kandang_id);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_project_chickins_created_by ON project_chickins (created_by);
|
|
||||||
-36
@@ -1,36 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS project_flock_populations (
|
|
||||||
id BIGSERIAL PRIMARY KEY,
|
|
||||||
project_flock_kandang_id BIGINT NOT NULL,
|
|
||||||
initial_quantity NUMERIC(15, 3) NOT NULL,
|
|
||||||
current_quantity NUMERIC(15, 3) NOT NULL,
|
|
||||||
reserved_quantity NUMERIC(15, 3),
|
|
||||||
created_by BIGINT NOT NULL,
|
|
||||||
created_at TIMESTAMPTZ DEFAULT now(),
|
|
||||||
updated_at TIMESTAMPTZ DEFAULT now(),
|
|
||||||
deleted_at TIMESTAMPTZ
|
|
||||||
);
|
|
||||||
|
|
||||||
-- FOREIGN KEYS (dijalankan setelah semua tabel parent ada)
|
|
||||||
DO $$
|
|
||||||
BEGIN
|
|
||||||
IF EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'project_flock_kandangs') THEN
|
|
||||||
ALTER TABLE project_flock_populations
|
|
||||||
ADD CONSTRAINT fk_project_flock_kandang_id
|
|
||||||
FOREIGN KEY (project_flock_kandang_id)
|
|
||||||
REFERENCES project_flock_kandangs(id)
|
|
||||||
ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
IF EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'users') THEN
|
|
||||||
ALTER TABLE project_flock_populations
|
|
||||||
ADD CONSTRAINT fk_created_by
|
|
||||||
FOREIGN KEY (created_by)
|
|
||||||
REFERENCES users(id)
|
|
||||||
ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
||||||
END IF;
|
|
||||||
END $$;
|
|
||||||
|
|
||||||
-- INDEXES
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_project_flock_populations_project_flock_kandang_id ON project_flock_populations (project_flock_kandang_id);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_project_flock_populations_created_by ON project_flock_populations (created_by);
|
|
||||||
@@ -9,7 +9,7 @@ CREATE TABLE IF NOT EXISTS project_chickin_details (
|
|||||||
deleted_at TIMESTAMPTZ
|
deleted_at TIMESTAMPTZ
|
||||||
);
|
);
|
||||||
|
|
||||||
-- FOREIGN KEYS (dijalankan setelah semua tabel parent ada)
|
|
||||||
DO $$
|
DO $$
|
||||||
BEGIN
|
BEGIN
|
||||||
IF EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'project_chickins') THEN
|
IF EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'project_chickins') THEN
|
||||||
|
|||||||
+26
-18
@@ -1,22 +1,30 @@
|
|||||||
|
|
||||||
ALTER TABLE kandangs
|
ALTER TABLE kandangs
|
||||||
DROP CONSTRAINT IF EXISTS kandangs_project_flock_id_fkey;
|
DROP CONSTRAINT IF EXISTS kandangs_project_flock_id_fkey;
|
||||||
|
|
||||||
ALTER TABLE kandangs
|
ALTER TABLE kandangs DROP COLUMN IF EXISTS project_flock_id;
|
||||||
DROP COLUMN IF EXISTS project_flock_id;
|
|
||||||
|
|
||||||
ALTER TABLE project_chickins
|
-- Only alter if tables exist
|
||||||
DROP CONSTRAINT fk_project_flock_kandang_id,
|
DO $$
|
||||||
ADD CONSTRAINT fk_project_flock_kandang_id
|
BEGIN
|
||||||
FOREIGN KEY (project_flock_kandang_id)
|
IF EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'project_chickins') THEN
|
||||||
REFERENCES project_flock_kandangs(id)
|
ALTER TABLE project_chickins
|
||||||
ON UPDATE CASCADE
|
DROP CONSTRAINT IF EXISTS fk_project_flock_kandang_id;
|
||||||
ON DELETE CASCADE;
|
ALTER TABLE project_chickins
|
||||||
|
ADD CONSTRAINT fk_project_flock_kandang_id
|
||||||
|
FOREIGN KEY (project_flock_kandang_id)
|
||||||
|
REFERENCES project_flock_kandangs(id)
|
||||||
|
ON UPDATE CASCADE
|
||||||
|
ON DELETE CASCADE;
|
||||||
|
END IF;
|
||||||
|
|
||||||
ALTER TABLE project_flock_populations
|
IF EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'project_flock_populations') THEN
|
||||||
DROP CONSTRAINT fk_project_flock_kandang_id,
|
ALTER TABLE project_flock_populations
|
||||||
ADD CONSTRAINT fk_project_flock_kandang_id
|
DROP CONSTRAINT IF EXISTS fk_project_flock_kandang_id;
|
||||||
FOREIGN KEY (project_flock_kandang_id)
|
ALTER TABLE project_flock_populations
|
||||||
REFERENCES project_flock_kandangs(id)
|
ADD CONSTRAINT fk_project_flock_kandang_id
|
||||||
ON UPDATE CASCADE
|
FOREIGN KEY (project_flock_kandang_id)
|
||||||
ON DELETE CASCADE;
|
REFERENCES project_flock_kandangs(id)
|
||||||
|
ON UPDATE CASCADE
|
||||||
|
ON DELETE CASCADE;
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
DROP TABLE IF EXISTS laying_transfers CASCADE;
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS laying_transfers (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
transfer_number VARCHAR(50) UNIQUE NOT NULL,
|
||||||
|
from_project_flock_id BIGINT NOT NULL,
|
||||||
|
to_project_flock_id BIGINT NOT NULL,
|
||||||
|
transfer_date DATE NOT NULL,
|
||||||
|
pending_usage_qty NUMERIC(15, 3),
|
||||||
|
usage_qty NUMERIC(15, 3),
|
||||||
|
notes TEXT,
|
||||||
|
created_at TIMESTAMPTZ DEFAULT now(),
|
||||||
|
updated_at TIMESTAMPTZ DEFAULT now(),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
created_by BIGINT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- FOREIGN KEYS (dijalankan setelah semua tabel parent ada)
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'project_flocks') THEN
|
||||||
|
ALTER TABLE laying_transfers
|
||||||
|
ADD CONSTRAINT fk_laying_from_project_flock
|
||||||
|
FOREIGN KEY (from_project_flock_id)
|
||||||
|
REFERENCES project_flocks(id)
|
||||||
|
ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
ALTER TABLE laying_transfers
|
||||||
|
ADD CONSTRAINT fk_laying_to_project_flock
|
||||||
|
FOREIGN KEY (to_project_flock_id)
|
||||||
|
REFERENCES project_flocks(id)
|
||||||
|
ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'users') THEN
|
||||||
|
ALTER TABLE laying_transfers
|
||||||
|
ADD CONSTRAINT fk_laying_created_by
|
||||||
|
FOREIGN KEY (created_by)
|
||||||
|
REFERENCES users(id)
|
||||||
|
ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
-- INDEXES
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_laying_transfers_transfer_number ON laying_transfers (transfer_number)
|
||||||
|
WHERE
|
||||||
|
deleted_at IS NULL;
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_laying_transfers_from_project_flock_id ON laying_transfers (from_project_flock_id);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_laying_transfers_to_project_flock_id ON laying_transfers (to_project_flock_id);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_laying_transfers_created_by ON laying_transfers (created_by);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_laying_transfers_deleted_at ON laying_transfers (deleted_at);
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
-- ============================================
|
||||||
|
-- MIGRATION: project_chickins
|
||||||
|
-- ============================================
|
||||||
|
|
||||||
|
-- STEP 1: Hapus tabel jika sudah ada
|
||||||
|
DROP TABLE IF EXISTS project_chickins;
|
||||||
|
|
||||||
|
-- STEP 2: Buat tabel project_chickins
|
||||||
|
CREATE TABLE IF NOT EXISTS project_chickins (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
project_flock_kandang_id BIGINT NOT NULL,
|
||||||
|
product_warehouse_id BIGINT NOT NULL,
|
||||||
|
chick_in_date DATE NOT NULL,
|
||||||
|
usage_qty NUMERIC(15, 3) NOT NULL,
|
||||||
|
pending_usage_qty NUMERIC(15, 3) DEFAULT 0,
|
||||||
|
notes TEXT,
|
||||||
|
created_by BIGINT NOT NULL,
|
||||||
|
created_at TIMESTAMPTZ DEFAULT now(),
|
||||||
|
updated_at TIMESTAMPTZ DEFAULT now(),
|
||||||
|
deleted_at TIMESTAMPTZ
|
||||||
|
);
|
||||||
|
|
||||||
|
-- STEP 3: FOREIGN KEYS
|
||||||
|
BEGIN;
|
||||||
|
|
||||||
|
-- Relasi ke project_flock_kandangs
|
||||||
|
ALTER TABLE project_chickins
|
||||||
|
ADD CONSTRAINT fk_project_chickins_kandang FOREIGN KEY (project_flock_kandang_id) REFERENCES project_flock_kandangs (id) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- Relasi ke product_warehouses
|
||||||
|
ALTER TABLE project_chickins
|
||||||
|
ADD CONSTRAINT fk_project_chickins_warehouse FOREIGN KEY (product_warehouse_id) REFERENCES product_warehouses (id) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- Relasi ke users
|
||||||
|
ALTER TABLE project_chickins
|
||||||
|
ADD CONSTRAINT fk_project_chickins_created_by FOREIGN KEY (created_by) REFERENCES users (id) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
-- STEP 4: INDEXES
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_chickins_kandang_id ON project_chickins (project_flock_kandang_id)
|
||||||
|
WHERE
|
||||||
|
deleted_at IS NULL;
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_chickins_warehouse_id ON project_chickins (product_warehouse_id)
|
||||||
|
WHERE
|
||||||
|
deleted_at IS NULL;
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_chickins_created_by ON project_chickins (created_by);
|
||||||
|
|
||||||
|
-- Composite index for common queries
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_chickins_kandang_deleted ON project_chickins (
|
||||||
|
project_flock_kandang_id,
|
||||||
|
deleted_at
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Index for soft delete queries
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_chickins_deleted_at ON project_chickins (deleted_at);
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
-- ============================================
|
||||||
|
-- MIGRATION: project_flock_populations
|
||||||
|
-- ============================================
|
||||||
|
|
||||||
|
-- STEP 1: Hapus tabel jika sudah ada
|
||||||
|
DROP TABLE IF EXISTS project_flock_populations;
|
||||||
|
|
||||||
|
-- STEP 2: Buat tabel project_flock_populations
|
||||||
|
CREATE TABLE IF NOT EXISTS project_flock_populations (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
project_chickin_id BIGINT NOT NULL,
|
||||||
|
product_warehouse_id BIGINT NOT NULL,
|
||||||
|
total_qty NUMERIC(15, 3) NOT NULL,
|
||||||
|
total_used_qty NUMERIC(15, 3) DEFAULT 0,
|
||||||
|
notes TEXT,
|
||||||
|
created_by BIGINT NOT NULL,
|
||||||
|
created_at TIMESTAMPTZ DEFAULT now(),
|
||||||
|
updated_at TIMESTAMPTZ DEFAULT now(),
|
||||||
|
deleted_at TIMESTAMPTZ
|
||||||
|
);
|
||||||
|
|
||||||
|
-- STEP 3: FOREIGN KEYS
|
||||||
|
BEGIN;
|
||||||
|
|
||||||
|
-- Relasi ke project_chickins
|
||||||
|
ALTER TABLE project_flock_populations
|
||||||
|
ADD CONSTRAINT fk_project_flock_populations_chickin FOREIGN KEY (project_chickin_id) REFERENCES project_chickins (id) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- Relasi ke product_warehouses
|
||||||
|
ALTER TABLE project_flock_populations
|
||||||
|
ADD CONSTRAINT fk_project_flock_populations_warehouse FOREIGN KEY (product_warehouse_id) REFERENCES product_warehouses (id) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- Relasi ke users
|
||||||
|
ALTER TABLE project_flock_populations
|
||||||
|
ADD CONSTRAINT fk_project_flock_populations_created_by FOREIGN KEY (created_by) REFERENCES users (id) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
-- STEP 4: INDEXES
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_populations_chickin_id ON project_flock_populations (project_chickin_id)
|
||||||
|
WHERE
|
||||||
|
deleted_at IS NULL;
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_populations_warehouse_id ON project_flock_populations (product_warehouse_id)
|
||||||
|
WHERE
|
||||||
|
deleted_at IS NULL;
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_populations_created_by ON project_flock_populations (created_by);
|
||||||
|
|
||||||
|
-- Composite index for common queries
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_populations_chickin_deleted ON project_flock_populations (
|
||||||
|
project_chickin_id,
|
||||||
|
deleted_at
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Index for soft delete queries
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_populations_deleted_at ON project_flock_populations (deleted_at);
|
||||||
|
|
||||||
|
-- Unique constraint: one population per chickin
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_populations_chickin_unique ON project_flock_populations (project_chickin_id)
|
||||||
|
WHERE
|
||||||
|
deleted_at IS NULL;
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
-- Rollback laying_transfer_sources dan laying_transfer_targets tables
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS laying_transfer_targets CASCADE;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS laying_transfer_sources CASCADE;
|
||||||
+93
@@ -0,0 +1,93 @@
|
|||||||
|
-- Create laying_transfer_sources dan laying_transfer_targets tables
|
||||||
|
|
||||||
|
-- 1. Create laying_transfer_sources table (detail sumber - kandang asal growing)
|
||||||
|
CREATE TABLE laying_transfer_sources (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
laying_transfer_id BIGINT NOT NULL,
|
||||||
|
source_project_flock_kandang_id BIGINT NOT NULL,
|
||||||
|
product_warehouse_id BIGINT,
|
||||||
|
qty NUMERIC(15, 3) NOT NULL,
|
||||||
|
note TEXT,
|
||||||
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
|
deleted_at TIMESTAMPTZ
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Add foreign keys untuk laying_transfer_sources
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'laying_transfers') THEN
|
||||||
|
ALTER TABLE laying_transfer_sources
|
||||||
|
ADD CONSTRAINT fk_laying_transfer_sources_laying_transfer_id
|
||||||
|
FOREIGN KEY (laying_transfer_id) REFERENCES laying_transfers(id) ON DELETE CASCADE;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'project_flock_kandangs') THEN
|
||||||
|
ALTER TABLE laying_transfer_sources
|
||||||
|
ADD CONSTRAINT fk_laying_transfer_sources_project_flock_kandang_id
|
||||||
|
FOREIGN KEY (source_project_flock_kandang_id) REFERENCES project_flock_kandangs(id) ON DELETE RESTRICT;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'product_warehouses') THEN
|
||||||
|
ALTER TABLE laying_transfer_sources
|
||||||
|
ADD CONSTRAINT fk_laying_transfer_sources_product_warehouse_id
|
||||||
|
FOREIGN KEY (product_warehouse_id) REFERENCES product_warehouses(id) ON DELETE SET NULL;
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
-- 2. Create laying_transfer_targets table (detail tujuan - kandang laying)
|
||||||
|
CREATE TABLE laying_transfer_targets (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
laying_transfer_id BIGINT NOT NULL,
|
||||||
|
target_project_flock_kandang_id BIGINT NOT NULL,
|
||||||
|
qty NUMERIC(15, 3) NOT NULL,
|
||||||
|
product_warehouse_id BIGINT,
|
||||||
|
note TEXT,
|
||||||
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
|
deleted_at TIMESTAMPTZ
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Add foreign keys untuk laying_transfer_targets
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'laying_transfers') THEN
|
||||||
|
ALTER TABLE laying_transfer_targets
|
||||||
|
ADD CONSTRAINT fk_laying_transfer_targets_laying_transfer_id
|
||||||
|
FOREIGN KEY (laying_transfer_id) REFERENCES laying_transfers(id) ON DELETE CASCADE;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'project_flock_kandangs') THEN
|
||||||
|
ALTER TABLE laying_transfer_targets
|
||||||
|
ADD CONSTRAINT fk_laying_transfer_targets_project_flock_kandang_id
|
||||||
|
FOREIGN KEY (target_project_flock_kandang_id) REFERENCES project_flock_kandangs(id) ON DELETE RESTRICT;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'product_warehouses') THEN
|
||||||
|
ALTER TABLE laying_transfer_targets
|
||||||
|
ADD CONSTRAINT fk_laying_transfer_targets_product_warehouse_id
|
||||||
|
FOREIGN KEY (product_warehouse_id) REFERENCES product_warehouses(id) ON DELETE SET NULL;
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
-- 3. Create indexes untuk laying_transfer_sources
|
||||||
|
CREATE INDEX idx_laying_transfer_sources_laying_transfer_id ON laying_transfer_sources (laying_transfer_id);
|
||||||
|
|
||||||
|
CREATE INDEX idx_laying_transfer_sources_source_kandang_id ON laying_transfer_sources (
|
||||||
|
source_project_flock_kandang_id
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_laying_transfer_sources_product_warehouse_id ON laying_transfer_sources (product_warehouse_id);
|
||||||
|
|
||||||
|
CREATE INDEX idx_laying_transfer_sources_deleted_at ON laying_transfer_sources (deleted_at);
|
||||||
|
|
||||||
|
-- 4. Create indexes untuk laying_transfer_targets
|
||||||
|
CREATE INDEX idx_laying_transfer_targets_laying_transfer_id ON laying_transfer_targets (laying_transfer_id);
|
||||||
|
|
||||||
|
CREATE INDEX idx_laying_transfer_targets_target_kandang_id ON laying_transfer_targets (
|
||||||
|
target_project_flock_kandang_id
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_laying_transfer_targets_product_warehouse_id ON laying_transfer_targets (product_warehouse_id);
|
||||||
|
|
||||||
|
CREATE INDEX idx_laying_transfer_targets_deleted_at ON laying_transfer_targets (deleted_at);
|
||||||
@@ -571,52 +571,54 @@ func seedProducts(tx *gorm.DB, createdBy uint, uoms map[string]uint, categories
|
|||||||
Flags: []utils.FlagType{utils.FlagDOC},
|
Flags: []utils.FlagType{utils.FlagDOC},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "Ayam Afkir",
|
Name: "Ayam Pullet",
|
||||||
Brand: "-",
|
Brand: "MBU Pullet",
|
||||||
Sku: "1",
|
Sku: "PLT0001",
|
||||||
Uom: "Ekor",
|
Uom: "Ekor",
|
||||||
Category: "Day Old Chick",
|
Category: "Pullet",
|
||||||
Price: 1,
|
Price: 15000,
|
||||||
|
Suppliers: []string{"PT CHAROEN POKPHAND INDONESIA Tbk"},
|
||||||
|
Flags: []utils.FlagType{utils.FlagPullet},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "Ayam Mati",
|
Name: "Ayam Afkir",
|
||||||
Brand: "-",
|
Brand: "-",
|
||||||
Sku: "2",
|
Sku: "1",
|
||||||
Uom: "Ekor",
|
Uom: "Ekor",
|
||||||
Category: "Day Old Chick",
|
Category: "Day Old Chick",
|
||||||
Price: 1,
|
Price: 1,
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "Ayam Culling",
|
Name: "Ayam Mati",
|
||||||
Brand: "-",
|
Brand: "-",
|
||||||
Sku: "3",
|
Sku: "2",
|
||||||
Uom: "Ekor",
|
Uom: "Ekor",
|
||||||
Category: "Day Old Chick",
|
Category: "Day Old Chick",
|
||||||
Price: 1,
|
Price: 1,
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "Telur Konsumsi Baik",
|
Name: "Ayam Culling",
|
||||||
Brand: "-",
|
Brand: "-",
|
||||||
Sku: "4",
|
Sku: "3",
|
||||||
Uom: "Unit",
|
Uom: "Ekor",
|
||||||
Category: "Telur",
|
Category: "Day Old Chick",
|
||||||
Price: 1,
|
Price: 1,
|
||||||
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "Telur Pecah",
|
Name: "Telur Konsumsi Baik",
|
||||||
Brand: "-",
|
Brand: "-",
|
||||||
Sku: "5",
|
Sku: "4",
|
||||||
Uom: "Unit",
|
Uom: "Unit",
|
||||||
Category: "Telur",
|
Category: "Telur",
|
||||||
Price: 1,
|
Price: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Telur Pecah",
|
||||||
|
Brand: "-",
|
||||||
|
Sku: "5",
|
||||||
|
Uom: "Unit",
|
||||||
|
Category: "Telur",
|
||||||
|
Price: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "281 SPECIAL STARTER",
|
Name: "281 SPECIAL STARTER",
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package entities
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LayingKandangTransfer struct {
|
||||||
|
Id uint `gorm:"primaryKey"`
|
||||||
|
KandangId uint
|
||||||
|
ProductWarehouseId uint
|
||||||
|
Qty float64 `gorm:"type:numeric(15,3)"`
|
||||||
|
LayingTransferId uint `gorm:"not null"`
|
||||||
|
CreatedAt time.Time `gorm:"autoCreateTime"`
|
||||||
|
UpdatedAt time.Time `gorm:"autoUpdateTime"`
|
||||||
|
DeletedAt gorm.DeletedAt `gorm:"index"`
|
||||||
|
|
||||||
|
Kandang *Kandang `gorm:"foreignKey:KandangId;references:Id"`
|
||||||
|
ProductWarehouse *ProductWarehouse `gorm:"foreignKey:ProductWarehouseId;references:Id"`
|
||||||
|
LayingTransfer *LayingTransfer `gorm:"foreignKey:LayingTransferId;references:Id"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package entities
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LayingTransfer struct {
|
||||||
|
Id uint `gorm:"primaryKey"`
|
||||||
|
TransferNumber string `gorm:"uniqueIndex;not null"`
|
||||||
|
FromProjectFlockId uint `gorm:"not null"`
|
||||||
|
ToProjectFlockId uint `gorm:"not null"`
|
||||||
|
TransferDate time.Time `gorm:"type:date;not null"`
|
||||||
|
PendingUsageQty *float64 `gorm:"type:numeric(15,3)"`
|
||||||
|
UsageQty *float64 `gorm:"type:numeric(15,3)"`
|
||||||
|
Notes string `gorm:"type:text"`
|
||||||
|
CreatedBy uint `gorm:"not null"`
|
||||||
|
CreatedAt time.Time `gorm:"autoCreateTime"`
|
||||||
|
UpdatedAt time.Time `gorm:"autoUpdateTime"`
|
||||||
|
DeletedAt gorm.DeletedAt `gorm:"index"`
|
||||||
|
|
||||||
|
FromProjectFlock *ProjectFlock `gorm:"foreignKey:FromProjectFlockId;references:Id"`
|
||||||
|
ToProjectFlock *ProjectFlock `gorm:"foreignKey:ToProjectFlockId;references:Id"`
|
||||||
|
CreatedUser *User `gorm:"foreignKey:CreatedBy;references:Id"`
|
||||||
|
Sources []LayingTransferSource `gorm:"foreignKey:LayingTransferId;constraint:OnDelete:CASCADE"`
|
||||||
|
Targets []LayingTransferTarget `gorm:"foreignKey:LayingTransferId;constraint:OnDelete:CASCADE"`
|
||||||
|
LatestApproval *Approval `gorm:"-" json:"-"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package entities
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LayingTransferSource struct {
|
||||||
|
Id uint `gorm:"primaryKey"`
|
||||||
|
LayingTransferId uint `gorm:"index;not null"`
|
||||||
|
SourceProjectFlockKandangId uint `gorm:"not null"`
|
||||||
|
ProductWarehouseId *uint `gorm:""`
|
||||||
|
Qty float64 `gorm:"type:numeric(15,3);not null"`
|
||||||
|
Note string `gorm:"type:text"`
|
||||||
|
CreatedAt time.Time `gorm:"autoCreateTime"`
|
||||||
|
UpdatedAt time.Time `gorm:"autoUpdateTime"`
|
||||||
|
DeletedAt gorm.DeletedAt `gorm:"index"`
|
||||||
|
|
||||||
|
LayingTransfer *LayingTransfer `gorm:"foreignKey:LayingTransferId;references:Id"`
|
||||||
|
SourceProjectFlockKandang *ProjectFlockKandang `gorm:"foreignKey:SourceProjectFlockKandangId;references:Id"`
|
||||||
|
ProductWarehouse *ProductWarehouse `gorm:"foreignKey:ProductWarehouseId;references:Id"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package entities
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LayingTransferTarget struct {
|
||||||
|
Id uint `gorm:"primaryKey"`
|
||||||
|
LayingTransferId uint `gorm:"index;not null"`
|
||||||
|
TargetProjectFlockKandangId uint `gorm:"not null"`
|
||||||
|
Qty float64 `gorm:"type:numeric(15,3);not null"`
|
||||||
|
ProductWarehouseId *uint `gorm:""`
|
||||||
|
Note string `gorm:"type:text"`
|
||||||
|
CreatedAt time.Time `gorm:"autoCreateTime"`
|
||||||
|
UpdatedAt time.Time `gorm:"autoUpdateTime"`
|
||||||
|
DeletedAt gorm.DeletedAt `gorm:"index"`
|
||||||
|
|
||||||
|
LayingTransfer *LayingTransfer `gorm:"foreignKey:LayingTransferId;references:Id"`
|
||||||
|
TargetProjectFlockKandang *ProjectFlockKandang `gorm:"foreignKey:TargetProjectFlockKandangId;references:Id"`
|
||||||
|
ProductWarehouse *ProductWarehouse `gorm:"foreignKey:ProductWarehouseId;references:Id"`
|
||||||
|
}
|
||||||
|
|
||||||
@@ -12,13 +12,16 @@ type ProjectChickin struct {
|
|||||||
Id uint `gorm:"primaryKey"`
|
Id uint `gorm:"primaryKey"`
|
||||||
ProjectFlockKandangId uint `gorm:"not null;index;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`
|
ProjectFlockKandangId uint `gorm:"not null;index;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`
|
||||||
ChickInDate time.Time `gorm:"not null"`
|
ChickInDate time.Time `gorm:"not null"`
|
||||||
Quantity float64 `gorm:"not null"`
|
ProductWarehouseId uint `gorm:"not null"`
|
||||||
Note string `gorm:"type:text"`
|
UsageQty float64 `gorm:"type:numeric(15,3);not null"`
|
||||||
|
PendingUsageQty float64 `gorm:"type:numeric(15,3);default:0"`
|
||||||
|
Notes string `gorm:"type:text"`
|
||||||
CreatedBy uint `gorm:"not null"`
|
CreatedBy uint `gorm:"not null"`
|
||||||
CreatedAt time.Time `gorm:"autoCreateTime"`
|
CreatedAt time.Time `gorm:"autoCreateTime"`
|
||||||
UpdatedAt time.Time `gorm:"autoUpdateTime"`
|
UpdatedAt time.Time `gorm:"autoUpdateTime"`
|
||||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||||||
|
|
||||||
ProjectFlockKandang ProjectFlockKandang `gorm:"foreignKey:ProjectFlockKandangId;references:Id"`
|
ProjectFlockKandang *ProjectFlockKandang `gorm:"foreignKey:ProjectFlockKandangId;references:Id"`
|
||||||
CreatedUser User `gorm:"foreignKey:CreatedBy;references:Id"`
|
ProductWarehouse *ProductWarehouse `gorm:"foreignKey:ProductWarehouseId;references:Id"`
|
||||||
|
CreatedUser *User `gorm:"foreignKey:CreatedBy;references:Id"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,17 +7,18 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type ProjectFlockPopulation struct {
|
type ProjectFlockPopulation struct {
|
||||||
Id uint `gorm:"primaryKey"`
|
Id uint `gorm:"primaryKey"`
|
||||||
ProjectFlockKandangId uint `gorm:"not null;index;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`
|
ProjectChickinId uint `gorm:"not null"`
|
||||||
InitialQuantity float64 `gorm:"type:numeric(15,3);not null"`
|
ProductWarehouseId uint `gorm:"not null"`
|
||||||
CurrentQuantity float64 `gorm:"type:numeric(15,3);not null"`
|
TotalQty float64 `gorm:"type:numeric(15,3);not null"`
|
||||||
ReservedQuantity float64 `gorm:"type:numeric(15,3)"`
|
TotalUsedQty float64 `gorm:"type:numeric(15,3);not null"`
|
||||||
CreatedBy uint `gorm:"not null"`
|
Notes string `gorm:"type:text"`
|
||||||
CreatedAt time.Time `gorm:"autoCreateTime"`
|
CreatedBy uint `gorm:"not null"`
|
||||||
UpdatedAt time.Time `gorm:"autoUpdateTime"`
|
CreatedAt time.Time `gorm:"autoCreateTime"`
|
||||||
DeletedAt gorm.DeletedAt `gorm:"index"`
|
UpdatedAt time.Time `gorm:"autoUpdateTime"`
|
||||||
|
DeletedAt gorm.DeletedAt `gorm:"index"`
|
||||||
|
|
||||||
ProjectFlockKandang *ProjectFlockKandang `gorm:"foreignKey:ProjectFlockKandangId;references:Id"`
|
ProjectChickin *ProjectChickin `gorm:"foreignKey:ProjectChickinId;references:Id"`
|
||||||
|
ProductWarehouse *ProductWarehouse `gorm:"foreignKey:ProductWarehouseId;references:Id"`
|
||||||
CreatedUser *User `gorm:"foreignKey:CreatedBy;references:Id"`
|
CreatedUser *User `gorm:"foreignKey:CreatedBy;references:Id"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,3 +28,4 @@ type ProjectFlock struct {
|
|||||||
|
|
||||||
LatestApproval *Approval `gorm:"-" json:"-"`
|
LatestApproval *Approval `gorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ package entities
|
|||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
type ProjectFlockKandang struct {
|
type ProjectFlockKandang struct {
|
||||||
Id uint `gorm:"primaryKey"`
|
Id uint `gorm:"primaryKey"`
|
||||||
ProjectFlockId uint `gorm:"not null;index:idx_project_flock_kandangs_project;uniqueIndex:idx_project_flock_kandangs_unique"`
|
ProjectFlockId uint `gorm:"not null;index:idx_project_flock_kandangs_project;uniqueIndex:idx_project_flock_kandangs_unique"`
|
||||||
KandangId uint `gorm:"not null;index:idx_project_flock_kandangs_kandang;uniqueIndex:idx_project_flock_kandangs_unique"`
|
KandangId uint `gorm:"not null;index:idx_project_flock_kandangs_kandang;uniqueIndex:idx_project_flock_kandangs_unique"`
|
||||||
CreatedAt time.Time `gorm:"autoCreateTime"`
|
CreatedAt time.Time `gorm:"autoCreateTime"`
|
||||||
|
|
||||||
|
|
||||||
ProjectFlock ProjectFlock `gorm:"foreignKey:ProjectFlockId;references:Id"`
|
|
||||||
Kandang Kandang `gorm:"foreignKey:KandangId;references:Id"`
|
|
||||||
|
|
||||||
|
ProjectFlock ProjectFlock `gorm:"foreignKey:ProjectFlockId;references:Id"`
|
||||||
|
Kandang Kandang `gorm:"foreignKey:KandangId;references:Id"`
|
||||||
|
Chickins []ProjectChickin `gorm:"foreignKey:ProjectFlockKandangId;references:Id"`
|
||||||
|
LatestApproval *Approval `gorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|||||||
+85
-177
@@ -1,193 +1,101 @@
|
|||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
// import (
|
||||||
"strings"
|
// "strings"
|
||||||
|
|
||||||
"gitlab.com/mbugroup/lti-api.git/internal/config"
|
// "gitlab.com/mbugroup/lti-api.git/internal/config"
|
||||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
// service "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
||||||
"gitlab.com/mbugroup/lti-api.git/internal/modules/sso/session"
|
// "gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||||
service "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
|
||||||
"gitlab.com/mbugroup/lti-api.git/internal/sso"
|
|
||||||
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
// "github.com/gofiber/fiber/v2"
|
||||||
)
|
// )
|
||||||
|
|
||||||
const (
|
// func Auth(userService service.UserService, requiredRights ...string) fiber.Handler {
|
||||||
authContextLocalsKey = "auth.context"
|
// return func(c *fiber.Ctx) error {
|
||||||
authUserLocalsKey = "auth.user"
|
// authHeader := c.Get("Authorization")
|
||||||
)
|
// token := strings.TrimSpace(strings.TrimPrefix(authHeader, "Bearer "))
|
||||||
|
|
||||||
// AuthContext keeps authentication details captured by the middleware.
|
// if token == "" {
|
||||||
type AuthContext struct {
|
// return fiber.NewError(fiber.StatusUnauthorized, "Please authenticate")
|
||||||
Token string
|
// }
|
||||||
Verification *sso.VerificationResult
|
|
||||||
User *entity.User
|
|
||||||
Roles []sso.Role
|
|
||||||
Permissions map[string]struct{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Auth validates the incoming request against the central SSO access token and
|
// userID, err := utils.VerifyToken(token, config.JWTSecret, config.TokenTypeAccess)
|
||||||
// loads the corresponding local user. Optional scopes can be provided to enforce
|
// if err != nil {
|
||||||
// fine-grained authorization using the SSO access token scopes.
|
// return fiber.NewError(fiber.StatusUnauthorized, "Please authenticate")
|
||||||
func Auth(userService service.UserService, requiredScopes ...string) fiber.Handler {
|
// }
|
||||||
return func(c *fiber.Ctx) error {
|
|
||||||
token := bearerToken(c)
|
|
||||||
if token == "" {
|
|
||||||
token = strings.TrimSpace(c.Cookies(config.SSOAccessCookieName))
|
|
||||||
}
|
|
||||||
if token == "" {
|
|
||||||
return fiber.NewError(fiber.StatusUnauthorized, "Please authenticate")
|
|
||||||
}
|
|
||||||
|
|
||||||
verification, err := sso.VerifyAccessToken(token)
|
// // Only end-user subjects are allowed by this middleware. Service tokens
|
||||||
if err != nil {
|
// if verification.UserID == 0 {
|
||||||
utils.Log.WithError(err).Warn("auth: token verification failed")
|
// return fiber.NewError(fiber.StatusUnauthorized, "Please authenticate")
|
||||||
return fiber.NewError(fiber.StatusUnauthorized, "Please authenticate")
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
if verification.UserID == 0 {
|
// // Fail-closed on revocation check errors for stricter security posture.
|
||||||
return fiber.NewError(fiber.StatusForbidden, "Service authentication is not permitted for this endpoint")
|
// if revoker := session.GetRevocationStore(); revoker != nil {
|
||||||
}
|
// if fingerprint := session.TokenFingerprint(token); fingerprint != "" {
|
||||||
|
// revoked, err := revoker.IsRevoked(c.Context(), fingerprint)
|
||||||
|
// if err != nil {
|
||||||
|
// utils.Log.WithError(err).Warn("failed to check token revocation")
|
||||||
|
// return fiber.NewError(fiber.StatusUnauthorized, "Please authenticate")
|
||||||
|
// }
|
||||||
|
// if revoked {
|
||||||
|
// return fiber.NewError(fiber.StatusUnauthorized, "Please authenticate")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
if err := ensureNotRevoked(c, token, verification); err != nil {
|
// user, err := userService.GetBySSOUserID(c, verification.UserID)
|
||||||
return err
|
// if err != nil || user == nil {
|
||||||
}
|
// return fiber.NewError(fiber.StatusUnauthorized, "Please authenticate")
|
||||||
|
// }
|
||||||
|
|
||||||
user, err := userService.GetBySSOUserID(c, verification.UserID)
|
// if len(requiredRights) > 0 && verification.Claims != nil {
|
||||||
if err != nil || user == nil {
|
// if !hasAllScopes(verification.Claims.Scopes(), requiredRights) {
|
||||||
utils.Log.WithError(err).Warn("auth: failed to resolve user from repository")
|
// return fiber.NewError(fiber.StatusForbidden, "Insufficient scope")
|
||||||
return fiber.NewError(fiber.StatusUnauthorized, "Please authenticate")
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
if len(requiredScopes) > 0 {
|
// c.Locals("user", user)
|
||||||
if verification.Claims == nil || !hasAllScopes(verification.Claims.Scopes(), requiredScopes) {
|
|
||||||
return fiber.NewError(fiber.StatusForbidden, "Insufficient scope")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var roles []sso.Role
|
// // if len(requiredRights) > 0 {
|
||||||
permissions := make(map[string]struct{})
|
// // userRights, hasRights := config.RoleRights[user.Role]
|
||||||
if verification.UserID != 0 {
|
// // if (!hasRights || !hasAllRights(userRights, requiredRights)) && c.Params("userId") != userID {
|
||||||
if profile, err := sso.FetchProfile(c.Context(), token, verification); err != nil {
|
// // return fiber.NewError(fiber.StatusForbidden, "You don't have permission to access this resource")
|
||||||
utils.Log.WithError(err).Warn("auth: failed to fetch sso profile")
|
// // }
|
||||||
} else if profile != nil {
|
// // }
|
||||||
roles = profile.Roles
|
|
||||||
for _, perm := range profile.PermissionNames() {
|
|
||||||
if perm != "" {
|
|
||||||
permissions[perm] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := &AuthContext{
|
// return c.Next()
|
||||||
Token: token,
|
// }
|
||||||
Verification: verification,
|
// }
|
||||||
User: user,
|
|
||||||
Roles: roles,
|
|
||||||
Permissions: permissions,
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Locals(authContextLocalsKey, ctx)
|
// // bearerToken extracts a Bearer token from the Authorization header using
|
||||||
c.Locals(authUserLocalsKey, user)
|
// // case-insensitive scheme matching and tolerant whitespace handling.
|
||||||
|
// func bearerToken(c *fiber.Ctx) string {
|
||||||
|
// parts := strings.Fields(c.Get("Authorization"))
|
||||||
|
// if len(parts) == 2 && strings.EqualFold(parts[0], "Bearer") {
|
||||||
|
// return strings.TrimSpace(parts[1])
|
||||||
|
// }
|
||||||
|
// return ""
|
||||||
|
// }
|
||||||
|
|
||||||
return c.Next()
|
// func hasAllScopes(have, required []string) bool {
|
||||||
}
|
// if len(required) == 0 {
|
||||||
}
|
// return true
|
||||||
|
// }
|
||||||
// AuthenticatedUser returns the authenticated user populated by Auth.
|
// set := make(map[string]struct{}, len(have))
|
||||||
func AuthenticatedUser(c *fiber.Ctx) (*entity.User, bool) {
|
// for _, s := range have {
|
||||||
value := c.Locals(authUserLocalsKey)
|
// s = strings.ToLower(strings.TrimSpace(s))
|
||||||
if user, ok := value.(*entity.User); ok && user != nil {
|
// if s != "" {
|
||||||
return user, true
|
// set[s] = struct{}{}
|
||||||
}
|
// }
|
||||||
return nil, false
|
// }
|
||||||
}
|
// for _, r := range required {
|
||||||
|
// r = strings.ToLower(strings.TrimSpace(r))
|
||||||
// AuthDetails returns the full authentication context (token, claims, user).
|
// if r == "" {
|
||||||
func AuthDetails(c *fiber.Ctx) (*AuthContext, bool) {
|
// continue
|
||||||
value := c.Locals(authContextLocalsKey)
|
// }
|
||||||
if ctx, ok := value.(*AuthContext); ok && ctx != nil {
|
// if _, ok := set[r]; !ok {
|
||||||
return ctx, true
|
// return false
|
||||||
}
|
// }
|
||||||
return nil, false
|
// }
|
||||||
}
|
// return true
|
||||||
|
// }
|
||||||
// ensureNotRevoked ensures the token is not revoked or superseded by a forced logout.
|
|
||||||
func ensureNotRevoked(c *fiber.Ctx, token string, verification *sso.VerificationResult) error {
|
|
||||||
revoker := session.GetRevocationStore()
|
|
||||||
if revoker == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if fingerprint := session.TokenFingerprint(token); fingerprint != "" {
|
|
||||||
revoked, err := revoker.IsRevoked(c.Context(), fingerprint)
|
|
||||||
if err != nil {
|
|
||||||
utils.Log.WithError(err).Warn("auth: token revocation check failed")
|
|
||||||
return fiber.NewError(fiber.StatusUnauthorized, "Please authenticate")
|
|
||||||
}
|
|
||||||
if revoked {
|
|
||||||
return fiber.NewError(fiber.StatusUnauthorized, "Please authenticate")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if verification.UserID == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
logoutAt, err := revoker.UserLogoutTime(c.Context(), verification.UserID)
|
|
||||||
if err != nil {
|
|
||||||
utils.Log.WithError(err).Warn("auth: failed to load user logout marker")
|
|
||||||
return fiber.NewError(fiber.StatusUnauthorized, "Please authenticate")
|
|
||||||
}
|
|
||||||
if logoutAt.IsZero() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
claims := verification.Claims
|
|
||||||
if claims == nil || claims.IssuedAt == nil {
|
|
||||||
return fiber.NewError(fiber.StatusUnauthorized, "Please authenticate")
|
|
||||||
}
|
|
||||||
|
|
||||||
issuedAt := claims.IssuedAt.Time
|
|
||||||
// Treat tokens issued at or before the forced logout timestamp as invalid.
|
|
||||||
if !issuedAt.After(logoutAt) {
|
|
||||||
return fiber.NewError(fiber.StatusUnauthorized, "Please authenticate")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// bearerToken extracts a Bearer token from the Authorization header using
|
|
||||||
// case-insensitive scheme matching and tolerant whitespace handling.
|
|
||||||
func bearerToken(c *fiber.Ctx) string {
|
|
||||||
parts := strings.Fields(c.Get("Authorization"))
|
|
||||||
if len(parts) == 2 && strings.EqualFold(parts[0], "Bearer") {
|
|
||||||
return strings.TrimSpace(parts[1])
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func hasAllScopes(have, required []string) bool {
|
|
||||||
if len(required) == 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
set := make(map[string]struct{}, len(have))
|
|
||||||
for _, s := range have {
|
|
||||||
s = strings.ToLower(strings.TrimSpace(s))
|
|
||||||
if s != "" {
|
|
||||||
set[s] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, r := range required {
|
|
||||||
r = strings.ToLower(strings.TrimSpace(r))
|
|
||||||
if r == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if _, ok := set[r]; !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -24,24 +24,3 @@ func LimiterConfig() fiber.Handler {
|
|||||||
SkipSuccessfulRequests: true,
|
SkipSuccessfulRequests: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLimiter(max int, expiration time.Duration) fiber.Handler {
|
|
||||||
if max <= 0 {
|
|
||||||
max = 10
|
|
||||||
}
|
|
||||||
if expiration <= 0 {
|
|
||||||
expiration = time.Minute
|
|
||||||
}
|
|
||||||
return limiter.New(limiter.Config{
|
|
||||||
Max: max,
|
|
||||||
Expiration: expiration,
|
|
||||||
LimitReached: func(c *fiber.Ctx) error {
|
|
||||||
return c.Status(fiber.StatusTooManyRequests).
|
|
||||||
JSON(response.Common{
|
|
||||||
Code: fiber.StatusTooManyRequests,
|
|
||||||
Status: "error",
|
|
||||||
Message: "Too many requests, please try again later",
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,75 +0,0 @@
|
|||||||
package middleware
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
// RequirePermissions ensures the authenticated user possesses all specified permissions.
|
|
||||||
func RequirePermissions(perms ...string) fiber.Handler {
|
|
||||||
required := canonicalPermissions(perms)
|
|
||||||
return func(c *fiber.Ctx) error {
|
|
||||||
if len(required) == 0 {
|
|
||||||
return c.Next()
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, ok := AuthDetails(c)
|
|
||||||
if !ok || ctx == nil {
|
|
||||||
return fiber.NewError(fiber.StatusUnauthorized, "Please authenticate")
|
|
||||||
}
|
|
||||||
|
|
||||||
userPerms := ctx.permissionSet()
|
|
||||||
if len(userPerms) == 0 {
|
|
||||||
return fiber.NewError(fiber.StatusForbidden, "Insufficient permission")
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, perm := range required {
|
|
||||||
if _, has := userPerms[perm]; !has {
|
|
||||||
return fiber.NewError(fiber.StatusForbidden, "Insufficient permission")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.Next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// HasPermission reports whether the current request context includes the given permission.
|
|
||||||
func HasPermission(c *fiber.Ctx, perm string) bool {
|
|
||||||
ctx, ok := AuthDetails(c)
|
|
||||||
if !ok || ctx == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
perm = canonicalPermission(perm)
|
|
||||||
if perm == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
_, has := ctx.permissionSet()[perm]
|
|
||||||
return has
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *AuthContext) permissionSet() map[string]struct{} {
|
|
||||||
if a == nil || a.Permissions == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return a.Permissions
|
|
||||||
}
|
|
||||||
|
|
||||||
func canonicalPermissions(perms []string) []string {
|
|
||||||
out := make([]string, 0, len(perms))
|
|
||||||
seen := make(map[string]struct{}, len(perms))
|
|
||||||
for _, perm := range perms {
|
|
||||||
if canonical := canonicalPermission(perm); canonical != "" {
|
|
||||||
if _, ok := seen[canonical]; ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
seen[canonical] = struct{}{}
|
|
||||||
out = append(out, canonical)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
func canonicalPermission(perm string) string {
|
|
||||||
return strings.ToLower(strings.TrimSpace(perm))
|
|
||||||
}
|
|
||||||
@@ -16,10 +16,6 @@ func JSONBody() fiber.Handler {
|
|||||||
return c.Next()
|
return c.Next()
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.EqualFold(c.Path(), "/api/sso/users/sync") {
|
|
||||||
return c.Next()
|
|
||||||
}
|
|
||||||
|
|
||||||
body := c.Body()
|
body := c.Body()
|
||||||
if len(body) == 0 {
|
if len(body) == 0 {
|
||||||
return c.Next()
|
return c.Next()
|
||||||
|
|||||||
@@ -82,6 +82,10 @@ func (r *ConstantRepositoryImpl) GetConstants() map[string]interface{} {
|
|||||||
"LOKASI",
|
"LOKASI",
|
||||||
"KANDANG",
|
"KANDANG",
|
||||||
},
|
},
|
||||||
|
"stock_log": map[string][]string{
|
||||||
|
"log_types": []string{"TRANSFER", "ADJUSTMENT"},
|
||||||
|
"transaction_types": []string{"INCREASE", "DECREASE"},
|
||||||
|
},
|
||||||
"supplier_categories": []string{
|
"supplier_categories": []string{
|
||||||
"BOP",
|
"BOP",
|
||||||
"SAPRONAK",
|
"SAPRONAK",
|
||||||
|
|||||||
@@ -23,3 +23,4 @@ func (ProductWarehouseModule) RegisterRoutes(router fiber.Router, db *gorm.DB, v
|
|||||||
|
|
||||||
ProductWarehouseRoutes(router, userService, productWarehouseService)
|
ProductWarehouseRoutes(router, userService, productWarehouseService)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+48
-7
@@ -19,6 +19,8 @@ type ProductWarehouseRepository interface {
|
|||||||
GetProductWarehouseByProductAndWarehouseID(ctx context.Context, productId, warehouseId uint) (*entity.ProductWarehouse, error)
|
GetProductWarehouseByProductAndWarehouseID(ctx context.Context, productId, warehouseId uint) (*entity.ProductWarehouse, error)
|
||||||
GetByCategoryCodeAndWarehouseID(ctx context.Context, categoryCode string, warehouseId uint) ([]entity.ProductWarehouse, error)
|
GetByCategoryCodeAndWarehouseID(ctx context.Context, categoryCode string, warehouseId uint) ([]entity.ProductWarehouse, error)
|
||||||
GetLatestByCategoryCodeAndWarehouseID(ctx context.Context, categoryCode string, warehouseId uint, db *gorm.DB) (*entity.ProductWarehouse, error)
|
GetLatestByCategoryCodeAndWarehouseID(ctx context.Context, categoryCode string, warehouseId uint, db *gorm.DB) (*entity.ProductWarehouse, error)
|
||||||
|
GetByFlagAndWarehouseID(ctx context.Context, flagName string, warehouseId uint) ([]entity.ProductWarehouse, error)
|
||||||
|
GetFirstProductByFlag(ctx context.Context, flagName string) (*entity.Product, error)
|
||||||
ApplyFlagsFilter(db *gorm.DB, flags []string) *gorm.DB
|
ApplyFlagsFilter(db *gorm.DB, flags []string) *gorm.DB
|
||||||
AdjustQuantities(ctx context.Context, deltas map[uint]float64, modifier func(*gorm.DB) *gorm.DB) error
|
AdjustQuantities(ctx context.Context, deltas map[uint]float64, modifier func(*gorm.DB) *gorm.DB) error
|
||||||
}
|
}
|
||||||
@@ -78,14 +80,14 @@ func (r *ProductWarehouseRepositoryImpl) GetProductWarehouseByProductAndWarehous
|
|||||||
|
|
||||||
func (r *ProductWarehouseRepositoryImpl) GetByCategoryCodeAndWarehouseID(ctx context.Context, categoryCode string, warehouseId uint) ([]entity.ProductWarehouse, error) {
|
func (r *ProductWarehouseRepositoryImpl) GetByCategoryCodeAndWarehouseID(ctx context.Context, categoryCode string, warehouseId uint) ([]entity.ProductWarehouse, error) {
|
||||||
var productWarehouses []entity.ProductWarehouse
|
var productWarehouses []entity.ProductWarehouse
|
||||||
err := r.DB().WithContext(ctx).
|
q := r.DB().WithContext(ctx).Model(&entity.ProductWarehouse{}).
|
||||||
Table("product_warehouses").
|
|
||||||
Select("product_warehouses.*").
|
|
||||||
Joins("JOIN products ON products.id = product_warehouses.product_id").
|
Joins("JOIN products ON products.id = product_warehouses.product_id").
|
||||||
Joins("JOIN product_categories ON product_categories.id = products.product_category_id").
|
Joins("JOIN product_categories ON product_categories.id = products.product_category_id").
|
||||||
Where("product_categories.code = ? AND product_warehouses.warehouse_id = ?", categoryCode, warehouseId).
|
Where("product_categories.code = ? AND product_warehouses.warehouse_id = ?", categoryCode, warehouseId).
|
||||||
Order("product_warehouses.created_at DESC").
|
Order("product_warehouses.created_at DESC")
|
||||||
Find(&productWarehouses).Error
|
|
||||||
|
// preload relations so nested Product and Warehouse are populated
|
||||||
|
err := q.Preload("Product").Preload("Warehouse").Find(&productWarehouses).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -100,12 +102,12 @@ func (r *ProductWarehouseRepositoryImpl) GetLatestByCategoryCodeAndWarehouseID(c
|
|||||||
}
|
}
|
||||||
fmt.Println(warehouseId)
|
fmt.Println(warehouseId)
|
||||||
err := query.WithContext(ctx).
|
err := query.WithContext(ctx).
|
||||||
Table("product_warehouses").
|
Model(&entity.ProductWarehouse{}).
|
||||||
Select("product_warehouses.*").
|
|
||||||
Joins("JOIN products ON products.id = product_warehouses.product_id").
|
Joins("JOIN products ON products.id = product_warehouses.product_id").
|
||||||
Joins("JOIN product_categories ON product_categories.id = products.product_category_id").
|
Joins("JOIN product_categories ON product_categories.id = products.product_category_id").
|
||||||
Where("product_categories.code = ? AND product_warehouses.warehouse_id = ?", categoryCode, warehouseId).
|
Where("product_categories.code = ? AND product_warehouses.warehouse_id = ?", categoryCode, warehouseId).
|
||||||
Order("product_warehouses.created_at DESC").
|
Order("product_warehouses.created_at DESC").
|
||||||
|
Preload("Product").Preload("Warehouse").
|
||||||
First(&productWarehouse).Error
|
First(&productWarehouse).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -146,3 +148,42 @@ func (r *ProductWarehouseRepositoryImpl) AdjustQuantities(ctx context.Context, d
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *ProductWarehouseRepositoryImpl) GetFirstProductByCategoryCode(ctx context.Context, categoryCode string) (*entity.Product, error) {
|
||||||
|
var product entity.Product
|
||||||
|
err := r.DB().WithContext(ctx).
|
||||||
|
Joins("JOIN product_categories ON product_categories.id = products.product_category_id").
|
||||||
|
Where("product_categories.code = ?", categoryCode).
|
||||||
|
First(&product).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &product, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ProductWarehouseRepositoryImpl) GetByFlagAndWarehouseID(ctx context.Context, flagName string, warehouseId uint) ([]entity.ProductWarehouse, error) {
|
||||||
|
var productWarehouses []entity.ProductWarehouse
|
||||||
|
err := r.DB().WithContext(ctx).Model(&entity.ProductWarehouse{}).
|
||||||
|
Joins("JOIN products ON products.id = product_warehouses.product_id").
|
||||||
|
Joins("JOIN flags ON flags.flagable_id = products.id AND flags.flagable_type = ?", "products").
|
||||||
|
Where("flags.name = ? AND product_warehouses.warehouse_id = ?", flagName, warehouseId).
|
||||||
|
Order("product_warehouses.created_at DESC").
|
||||||
|
Preload("Product").Preload("Warehouse").
|
||||||
|
Find(&productWarehouses).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return productWarehouses, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ProductWarehouseRepositoryImpl) GetFirstProductByFlag(ctx context.Context, flagName string) (*entity.Product, error) {
|
||||||
|
var product entity.Product
|
||||||
|
err := r.DB().WithContext(ctx).
|
||||||
|
Joins("JOIN flags ON flags.flagable_id = products.id AND flags.flagable_type = ?", "products").
|
||||||
|
Where("flags.name = ?", flagName).
|
||||||
|
First(&product).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &product, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package productWarehouses
|
package productWarehouses
|
||||||
|
|
||||||
import (
|
import (
|
||||||
m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
// m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
||||||
controller "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/controllers"
|
controller "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/controllers"
|
||||||
productWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/services"
|
productWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/services"
|
||||||
user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
||||||
@@ -13,7 +13,12 @@ func ProductWarehouseRoutes(v1 fiber.Router, u user.UserService, s productWareho
|
|||||||
ctrl := controller.NewProductWarehouseController(s)
|
ctrl := controller.NewProductWarehouseController(s)
|
||||||
|
|
||||||
route := v1.Group("/product-warehouses")
|
route := v1.Group("/product-warehouses")
|
||||||
route.Use(m.Auth(u))
|
|
||||||
|
// route.Get("/", m.Auth(u), ctrl.GetAll)
|
||||||
|
// route.Post("/", m.Auth(u), ctrl.CreateOne)
|
||||||
|
// route.Get("/:id", m.Auth(u), ctrl.GetOne)
|
||||||
|
// route.Patch("/:id", m.Auth(u), ctrl.UpdateOne)
|
||||||
|
// route.Delete("/:id", m.Auth(u), ctrl.DeleteOne)
|
||||||
|
|
||||||
route.Get("/", ctrl.GetAll)
|
route.Get("/", ctrl.GetAll)
|
||||||
route.Get("/:id", ctrl.GetOne)
|
route.Get("/:id", ctrl.GetOne)
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import (
|
|||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
adjustments "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/adjustments"
|
|
||||||
productWarehouses "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses"
|
productWarehouses "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses"
|
||||||
|
adjustments "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/adjustments"
|
||||||
transfers "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/transfers"
|
transfers "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/transfers"
|
||||||
// MODULE IMPORTS
|
// MODULE IMPORTS
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package transfers
|
package transfers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
// m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
||||||
controller "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/transfers/controllers"
|
controller "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/transfers/controllers"
|
||||||
transfer "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/transfers/services"
|
transfer "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/transfers/services"
|
||||||
user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
||||||
@@ -13,7 +13,12 @@ func TransferRoutes(v1 fiber.Router, u user.UserService, s transfer.TransferServ
|
|||||||
ctrl := controller.NewTransferController(s)
|
ctrl := controller.NewTransferController(s)
|
||||||
|
|
||||||
route := v1.Group("/transfers")
|
route := v1.Group("/transfers")
|
||||||
route.Use(m.Auth(u))
|
|
||||||
|
// route.Get("/", m.Auth(u), ctrl.GetAll)
|
||||||
|
// route.Post("/", m.Auth(u), ctrl.CreateOne)
|
||||||
|
// route.Get("/:id", m.Auth(u), ctrl.GetOne)
|
||||||
|
// route.Patch("/:id", m.Auth(u), ctrl.UpdateOne)
|
||||||
|
// route.Delete("/:id", m.Auth(u), ctrl.DeleteOne)
|
||||||
|
|
||||||
route.Get("/", ctrl.GetAll)
|
route.Get("/", ctrl.GetAll)
|
||||||
route.Post("/", ctrl.CreateOne)
|
route.Post("/", ctrl.CreateOne)
|
||||||
|
|||||||
@@ -23,3 +23,4 @@ func (AreaModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate *val
|
|||||||
|
|
||||||
AreaRoutes(router, userService, areaService)
|
AreaRoutes(router, userService, areaService)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package areas
|
package areas
|
||||||
|
|
||||||
import (
|
import (
|
||||||
m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
// m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
||||||
controller "gitlab.com/mbugroup/lti-api.git/internal/modules/master/areas/controllers"
|
controller "gitlab.com/mbugroup/lti-api.git/internal/modules/master/areas/controllers"
|
||||||
area "gitlab.com/mbugroup/lti-api.git/internal/modules/master/areas/services"
|
area "gitlab.com/mbugroup/lti-api.git/internal/modules/master/areas/services"
|
||||||
user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
||||||
@@ -13,7 +13,12 @@ func AreaRoutes(v1 fiber.Router, u user.UserService, s area.AreaService) {
|
|||||||
ctrl := controller.NewAreaController(s)
|
ctrl := controller.NewAreaController(s)
|
||||||
|
|
||||||
route := v1.Group("/areas")
|
route := v1.Group("/areas")
|
||||||
route.Use(m.Auth(u))
|
|
||||||
|
// route.Get("/", m.Auth(u), ctrl.GetAll)
|
||||||
|
// route.Post("/", m.Auth(u), ctrl.CreateOne)
|
||||||
|
// route.Get("/:id", m.Auth(u), ctrl.GetOne)
|
||||||
|
// route.Patch("/:id", m.Auth(u), ctrl.UpdateOne)
|
||||||
|
// route.Delete("/:id", m.Auth(u), ctrl.DeleteOne)
|
||||||
|
|
||||||
route.Get("/", ctrl.GetAll)
|
route.Get("/", ctrl.GetAll)
|
||||||
route.Post("/", ctrl.CreateOne)
|
route.Post("/", ctrl.CreateOne)
|
||||||
|
|||||||
@@ -23,3 +23,4 @@ func (BankModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate *val
|
|||||||
|
|
||||||
BankRoutes(router, userService, bankService)
|
BankRoutes(router, userService, bankService)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package banks
|
package banks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
// m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
||||||
controller "gitlab.com/mbugroup/lti-api.git/internal/modules/master/banks/controllers"
|
controller "gitlab.com/mbugroup/lti-api.git/internal/modules/master/banks/controllers"
|
||||||
bank "gitlab.com/mbugroup/lti-api.git/internal/modules/master/banks/services"
|
bank "gitlab.com/mbugroup/lti-api.git/internal/modules/master/banks/services"
|
||||||
user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
||||||
@@ -13,7 +13,12 @@ func BankRoutes(v1 fiber.Router, u user.UserService, s bank.BankService) {
|
|||||||
ctrl := controller.NewBankController(s)
|
ctrl := controller.NewBankController(s)
|
||||||
|
|
||||||
route := v1.Group("/banks")
|
route := v1.Group("/banks")
|
||||||
route.Use(m.Auth(u))
|
|
||||||
|
// route.Get("/", m.Auth(u), ctrl.GetAll)
|
||||||
|
// route.Post("/", m.Auth(u), ctrl.CreateOne)
|
||||||
|
// route.Get("/:id", m.Auth(u), ctrl.GetOne)
|
||||||
|
// route.Patch("/:id", m.Auth(u), ctrl.UpdateOne)
|
||||||
|
// route.Delete("/:id", m.Auth(u), ctrl.DeleteOne)
|
||||||
|
|
||||||
route.Get("/", ctrl.GetAll)
|
route.Get("/", ctrl.GetAll)
|
||||||
route.Post("/", ctrl.CreateOne)
|
route.Post("/", ctrl.CreateOne)
|
||||||
|
|||||||
@@ -23,3 +23,4 @@ func (CustomerModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate
|
|||||||
|
|
||||||
CustomerRoutes(router, userService, customerService)
|
CustomerRoutes(router, userService, customerService)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package customers
|
package customers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
// m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
||||||
controller "gitlab.com/mbugroup/lti-api.git/internal/modules/master/customers/controllers"
|
controller "gitlab.com/mbugroup/lti-api.git/internal/modules/master/customers/controllers"
|
||||||
customer "gitlab.com/mbugroup/lti-api.git/internal/modules/master/customers/services"
|
customer "gitlab.com/mbugroup/lti-api.git/internal/modules/master/customers/services"
|
||||||
user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
||||||
@@ -13,7 +13,12 @@ func CustomerRoutes(v1 fiber.Router, u user.UserService, s customer.CustomerServ
|
|||||||
ctrl := controller.NewCustomerController(s)
|
ctrl := controller.NewCustomerController(s)
|
||||||
|
|
||||||
route := v1.Group("/customers")
|
route := v1.Group("/customers")
|
||||||
route.Use(m.Auth(u))
|
|
||||||
|
// route.Get("/", m.Auth(u), ctrl.GetAll)
|
||||||
|
// route.Post("/", m.Auth(u), ctrl.CreateOne)
|
||||||
|
// route.Get("/:id", m.Auth(u), ctrl.GetOne)
|
||||||
|
// route.Patch("/:id", m.Auth(u), ctrl.UpdateOne)
|
||||||
|
// route.Delete("/:id", m.Auth(u), ctrl.DeleteOne)
|
||||||
|
|
||||||
route.Get("/", ctrl.GetAll)
|
route.Get("/", ctrl.GetAll)
|
||||||
route.Post("/", ctrl.CreateOne)
|
route.Post("/", ctrl.CreateOne)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package fcrs
|
package fcrs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
// m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
||||||
controller "gitlab.com/mbugroup/lti-api.git/internal/modules/master/fcrs/controllers"
|
controller "gitlab.com/mbugroup/lti-api.git/internal/modules/master/fcrs/controllers"
|
||||||
fcr "gitlab.com/mbugroup/lti-api.git/internal/modules/master/fcrs/services"
|
fcr "gitlab.com/mbugroup/lti-api.git/internal/modules/master/fcrs/services"
|
||||||
user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
||||||
@@ -13,7 +13,12 @@ func FcrRoutes(v1 fiber.Router, u user.UserService, s fcr.FcrService) {
|
|||||||
ctrl := controller.NewFcrController(s)
|
ctrl := controller.NewFcrController(s)
|
||||||
|
|
||||||
route := v1.Group("/fcrs")
|
route := v1.Group("/fcrs")
|
||||||
route.Use(m.Auth(u))
|
|
||||||
|
// route.Get("/", m.Auth(u), ctrl.GetAll)
|
||||||
|
// route.Post("/", m.Auth(u), ctrl.CreateOne)
|
||||||
|
// route.Get("/:id", m.Auth(u), ctrl.GetOne)
|
||||||
|
// route.Patch("/:id", m.Auth(u), ctrl.UpdateOne)
|
||||||
|
// route.Delete("/:id", m.Auth(u), ctrl.DeleteOne)
|
||||||
|
|
||||||
route.Get("/", ctrl.GetAll)
|
route.Get("/", ctrl.GetAll)
|
||||||
route.Post("/", ctrl.CreateOne)
|
route.Post("/", ctrl.CreateOne)
|
||||||
|
|||||||
@@ -43,9 +43,9 @@ func ToFlockListDTO(e entity.Flock) FlockListDTO {
|
|||||||
|
|
||||||
return FlockListDTO{
|
return FlockListDTO{
|
||||||
FlockBaseDTO: ToFlockBaseDTO(e),
|
FlockBaseDTO: ToFlockBaseDTO(e),
|
||||||
CreatedAt: e.CreatedAt,
|
CreatedAt: e.CreatedAt,
|
||||||
UpdatedAt: e.UpdatedAt,
|
UpdatedAt: e.UpdatedAt,
|
||||||
CreatedUser: createdUser,
|
CreatedUser: createdUser,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package flocks
|
package flocks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
// m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
||||||
controller "gitlab.com/mbugroup/lti-api.git/internal/modules/master/flocks/controllers"
|
controller "gitlab.com/mbugroup/lti-api.git/internal/modules/master/flocks/controllers"
|
||||||
flock "gitlab.com/mbugroup/lti-api.git/internal/modules/master/flocks/services"
|
flock "gitlab.com/mbugroup/lti-api.git/internal/modules/master/flocks/services"
|
||||||
user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
||||||
@@ -13,7 +13,12 @@ func FlockRoutes(v1 fiber.Router, u user.UserService, s flock.FlockService) {
|
|||||||
ctrl := controller.NewFlockController(s)
|
ctrl := controller.NewFlockController(s)
|
||||||
|
|
||||||
route := v1.Group("/flocks")
|
route := v1.Group("/flocks")
|
||||||
route.Use(m.Auth(u))
|
|
||||||
|
// route.Get("/", m.Auth(u), ctrl.GetAll)
|
||||||
|
// route.Post("/", m.Auth(u), ctrl.CreateOne)
|
||||||
|
// route.Get("/:id", m.Auth(u), ctrl.GetOne)
|
||||||
|
// route.Patch("/:id", m.Auth(u), ctrl.UpdateOne)
|
||||||
|
// route.Delete("/:id", m.Auth(u), ctrl.DeleteOne)
|
||||||
|
|
||||||
route.Get("/", ctrl.GetAll)
|
route.Get("/", ctrl.GetAll)
|
||||||
route.Post("/", ctrl.CreateOne)
|
route.Post("/", ctrl.CreateOne)
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
package validation
|
package validation
|
||||||
|
|
||||||
type Create struct {
|
type Create struct {
|
||||||
Name string `json:"name" validate:"required_strict,min=3"`
|
Name string `json:"name" validate:"required_strict,min=3"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Update struct {
|
type Update struct {
|
||||||
Name *string `json:"name,omitempty" validate:"omitempty"`
|
Name *string `json:"name,omitempty" validate:"omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Query struct {
|
type Query struct {
|
||||||
|
|||||||
@@ -23,3 +23,4 @@ func (KandangModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate *
|
|||||||
|
|
||||||
KandangRoutes(router, userService, kandangService)
|
KandangRoutes(router, userService, kandangService)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package kandangs
|
package kandangs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
// m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
||||||
controller "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/controllers"
|
controller "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/controllers"
|
||||||
kandang "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/services"
|
kandang "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/services"
|
||||||
user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
||||||
@@ -13,7 +13,12 @@ func KandangRoutes(v1 fiber.Router, u user.UserService, s kandang.KandangService
|
|||||||
ctrl := controller.NewKandangController(s)
|
ctrl := controller.NewKandangController(s)
|
||||||
|
|
||||||
route := v1.Group("/kandangs")
|
route := v1.Group("/kandangs")
|
||||||
route.Use(m.Auth(u))
|
|
||||||
|
// route.Get("/", m.Auth(u), ctrl.GetAll)
|
||||||
|
// route.Post("/", m.Auth(u), ctrl.CreateOne)
|
||||||
|
// route.Get("/:id", m.Auth(u), ctrl.GetOne)
|
||||||
|
// route.Patch("/:id", m.Auth(u), ctrl.UpdateOne)
|
||||||
|
// route.Delete("/:id", m.Auth(u), ctrl.DeleteOne)
|
||||||
|
|
||||||
route.Get("/", ctrl.GetAll)
|
route.Get("/", ctrl.GetAll)
|
||||||
route.Post("/", ctrl.CreateOne)
|
route.Post("/", ctrl.CreateOne)
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ func NewKandangService(repo repository.KandangRepository, validate *validator.Va
|
|||||||
|
|
||||||
func (s kandangService) withRelations(db *gorm.DB) *gorm.DB {
|
func (s kandangService) withRelations(db *gorm.DB) *gorm.DB {
|
||||||
return db.Preload("CreatedUser").Preload("Location").Preload("Pic").Preload("ProjectFlockKandangs.ProjectFlock")
|
return db.Preload("CreatedUser").Preload("Location").Preload("Pic").Preload("ProjectFlockKandangs.ProjectFlock")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s kandangService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity.Kandang, int64, error) {
|
func (s kandangService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity.Kandang, int64, error) {
|
||||||
@@ -132,11 +132,11 @@ func (s *kandangService) CreateOne(c *fiber.Ctx, req *validation.Create) (*entit
|
|||||||
|
|
||||||
//TODO: created by dummy
|
//TODO: created by dummy
|
||||||
createBody := &entity.Kandang{
|
createBody := &entity.Kandang{
|
||||||
Name: req.Name,
|
Name: req.Name,
|
||||||
LocationId: req.LocationId,
|
LocationId: req.LocationId,
|
||||||
Status: status,
|
Status: status,
|
||||||
PicId: req.PicId,
|
PicId: req.PicId,
|
||||||
CreatedBy: 1,
|
CreatedBy: 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.Repository.CreateOne(c.Context(), createBody, nil); err != nil {
|
if err := s.Repository.CreateOne(c.Context(), createBody, nil); err != nil {
|
||||||
|
|||||||
@@ -23,3 +23,4 @@ func (LocationModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate
|
|||||||
|
|
||||||
LocationRoutes(router, userService, locationService)
|
LocationRoutes(router, userService, locationService)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package locations
|
package locations
|
||||||
|
|
||||||
import (
|
import (
|
||||||
m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
// m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
||||||
controller "gitlab.com/mbugroup/lti-api.git/internal/modules/master/locations/controllers"
|
controller "gitlab.com/mbugroup/lti-api.git/internal/modules/master/locations/controllers"
|
||||||
location "gitlab.com/mbugroup/lti-api.git/internal/modules/master/locations/services"
|
location "gitlab.com/mbugroup/lti-api.git/internal/modules/master/locations/services"
|
||||||
user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
||||||
@@ -13,7 +13,12 @@ func LocationRoutes(v1 fiber.Router, u user.UserService, s location.LocationServ
|
|||||||
ctrl := controller.NewLocationController(s)
|
ctrl := controller.NewLocationController(s)
|
||||||
|
|
||||||
route := v1.Group("/locations")
|
route := v1.Group("/locations")
|
||||||
route.Use(m.Auth(u))
|
|
||||||
|
// route.Get("/", m.Auth(u), ctrl.GetAll)
|
||||||
|
// route.Post("/", m.Auth(u), ctrl.CreateOne)
|
||||||
|
// route.Get("/:id", m.Auth(u), ctrl.GetOne)
|
||||||
|
// route.Patch("/:id", m.Auth(u), ctrl.UpdateOne)
|
||||||
|
// route.Delete("/:id", m.Auth(u), ctrl.DeleteOne)
|
||||||
|
|
||||||
route.Get("/", ctrl.GetAll)
|
route.Get("/", ctrl.GetAll)
|
||||||
route.Post("/", ctrl.CreateOne)
|
route.Post("/", ctrl.CreateOne)
|
||||||
|
|||||||
@@ -23,3 +23,4 @@ func (NonstockModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate
|
|||||||
|
|
||||||
NonstockRoutes(router, userService, nonstockService)
|
NonstockRoutes(router, userService, nonstockService)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package nonstocks
|
package nonstocks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
// m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
||||||
controller "gitlab.com/mbugroup/lti-api.git/internal/modules/master/nonstocks/controllers"
|
controller "gitlab.com/mbugroup/lti-api.git/internal/modules/master/nonstocks/controllers"
|
||||||
nonstock "gitlab.com/mbugroup/lti-api.git/internal/modules/master/nonstocks/services"
|
nonstock "gitlab.com/mbugroup/lti-api.git/internal/modules/master/nonstocks/services"
|
||||||
user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
||||||
@@ -13,7 +13,12 @@ func NonstockRoutes(v1 fiber.Router, u user.UserService, s nonstock.NonstockServ
|
|||||||
ctrl := controller.NewNonstockController(s)
|
ctrl := controller.NewNonstockController(s)
|
||||||
|
|
||||||
route := v1.Group("/nonstocks")
|
route := v1.Group("/nonstocks")
|
||||||
route.Use(m.Auth(u))
|
|
||||||
|
// route.Get("/", m.Auth(u), ctrl.GetAll)
|
||||||
|
// route.Post("/", m.Auth(u), ctrl.CreateOne)
|
||||||
|
// route.Get("/:id", m.Auth(u), ctrl.GetOne)
|
||||||
|
// route.Patch("/:id", m.Auth(u), ctrl.UpdateOne)
|
||||||
|
// route.Delete("/:id", m.Auth(u), ctrl.DeleteOne)
|
||||||
|
|
||||||
route.Get("/", ctrl.GetAll)
|
route.Get("/", ctrl.GetAll)
|
||||||
route.Post("/", ctrl.CreateOne)
|
route.Post("/", ctrl.CreateOne)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package productcategories
|
package productcategories
|
||||||
|
|
||||||
import (
|
import (
|
||||||
m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
// m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
||||||
controller "gitlab.com/mbugroup/lti-api.git/internal/modules/master/product-categories/controllers"
|
controller "gitlab.com/mbugroup/lti-api.git/internal/modules/master/product-categories/controllers"
|
||||||
productCategory "gitlab.com/mbugroup/lti-api.git/internal/modules/master/product-categories/services"
|
productCategory "gitlab.com/mbugroup/lti-api.git/internal/modules/master/product-categories/services"
|
||||||
user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
||||||
@@ -13,7 +13,12 @@ func ProductCategoryRoutes(v1 fiber.Router, u user.UserService, s productCategor
|
|||||||
ctrl := controller.NewProductCategoryController(s)
|
ctrl := controller.NewProductCategoryController(s)
|
||||||
|
|
||||||
route := v1.Group("/product-categories")
|
route := v1.Group("/product-categories")
|
||||||
route.Use(m.Auth(u))
|
|
||||||
|
// route.Get("/", m.Auth(u), ctrl.GetAll)
|
||||||
|
// route.Post("/", m.Auth(u), ctrl.CreateOne)
|
||||||
|
// route.Get("/:id", m.Auth(u), ctrl.GetOne)
|
||||||
|
// route.Patch("/:id", m.Auth(u), ctrl.UpdateOne)
|
||||||
|
// route.Delete("/:id", m.Auth(u), ctrl.DeleteOne)
|
||||||
|
|
||||||
route.Get("/", ctrl.GetAll)
|
route.Get("/", ctrl.GetAll)
|
||||||
route.Post("/", ctrl.CreateOne)
|
route.Post("/", ctrl.CreateOne)
|
||||||
|
|||||||
@@ -23,3 +23,4 @@ func (ProductModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate *
|
|||||||
|
|
||||||
ProductRoutes(router, userService, productService)
|
ProductRoutes(router, userService, productService)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package products
|
package products
|
||||||
|
|
||||||
import (
|
import (
|
||||||
m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
// m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
||||||
controller "gitlab.com/mbugroup/lti-api.git/internal/modules/master/products/controllers"
|
controller "gitlab.com/mbugroup/lti-api.git/internal/modules/master/products/controllers"
|
||||||
product "gitlab.com/mbugroup/lti-api.git/internal/modules/master/products/services"
|
product "gitlab.com/mbugroup/lti-api.git/internal/modules/master/products/services"
|
||||||
user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
||||||
@@ -13,7 +13,12 @@ func ProductRoutes(v1 fiber.Router, u user.UserService, s product.ProductService
|
|||||||
ctrl := controller.NewProductController(s)
|
ctrl := controller.NewProductController(s)
|
||||||
|
|
||||||
route := v1.Group("/products")
|
route := v1.Group("/products")
|
||||||
route.Use(m.Auth(u))
|
|
||||||
|
// route.Get("/", m.Auth(u), ctrl.GetAll)
|
||||||
|
// route.Post("/", m.Auth(u), ctrl.CreateOne)
|
||||||
|
// route.Get("/:id", m.Auth(u), ctrl.GetOne)
|
||||||
|
// route.Patch("/:id", m.Auth(u), ctrl.UpdateOne)
|
||||||
|
// route.Delete("/:id", m.Auth(u), ctrl.DeleteOne)
|
||||||
|
|
||||||
route.Get("/", ctrl.GetAll)
|
route.Get("/", ctrl.GetAll)
|
||||||
route.Post("/", ctrl.CreateOne)
|
route.Post("/", ctrl.CreateOne)
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import (
|
|||||||
banks "gitlab.com/mbugroup/lti-api.git/internal/modules/master/banks"
|
banks "gitlab.com/mbugroup/lti-api.git/internal/modules/master/banks"
|
||||||
customers "gitlab.com/mbugroup/lti-api.git/internal/modules/master/customers"
|
customers "gitlab.com/mbugroup/lti-api.git/internal/modules/master/customers"
|
||||||
fcrs "gitlab.com/mbugroup/lti-api.git/internal/modules/master/fcrs"
|
fcrs "gitlab.com/mbugroup/lti-api.git/internal/modules/master/fcrs"
|
||||||
flocks "gitlab.com/mbugroup/lti-api.git/internal/modules/master/flocks"
|
|
||||||
kandangs "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs"
|
kandangs "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs"
|
||||||
locations "gitlab.com/mbugroup/lti-api.git/internal/modules/master/locations"
|
locations "gitlab.com/mbugroup/lti-api.git/internal/modules/master/locations"
|
||||||
nonstocks "gitlab.com/mbugroup/lti-api.git/internal/modules/master/nonstocks"
|
nonstocks "gitlab.com/mbugroup/lti-api.git/internal/modules/master/nonstocks"
|
||||||
@@ -20,6 +19,7 @@ import (
|
|||||||
suppliers "gitlab.com/mbugroup/lti-api.git/internal/modules/master/suppliers"
|
suppliers "gitlab.com/mbugroup/lti-api.git/internal/modules/master/suppliers"
|
||||||
uoms "gitlab.com/mbugroup/lti-api.git/internal/modules/master/uoms"
|
uoms "gitlab.com/mbugroup/lti-api.git/internal/modules/master/uoms"
|
||||||
warehouses "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses"
|
warehouses "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses"
|
||||||
|
flocks "gitlab.com/mbugroup/lti-api.git/internal/modules/master/flocks"
|
||||||
// MODULE IMPORTS
|
// MODULE IMPORTS
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -23,3 +23,4 @@ func (SupplierModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate
|
|||||||
|
|
||||||
SupplierRoutes(router, userService, supplierService)
|
SupplierRoutes(router, userService, supplierService)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package suppliers
|
package suppliers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
// m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
||||||
controller "gitlab.com/mbugroup/lti-api.git/internal/modules/master/suppliers/controllers"
|
controller "gitlab.com/mbugroup/lti-api.git/internal/modules/master/suppliers/controllers"
|
||||||
supplier "gitlab.com/mbugroup/lti-api.git/internal/modules/master/suppliers/services"
|
supplier "gitlab.com/mbugroup/lti-api.git/internal/modules/master/suppliers/services"
|
||||||
user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
||||||
@@ -13,7 +13,12 @@ func SupplierRoutes(v1 fiber.Router, u user.UserService, s supplier.SupplierServ
|
|||||||
ctrl := controller.NewSupplierController(s)
|
ctrl := controller.NewSupplierController(s)
|
||||||
|
|
||||||
route := v1.Group("/suppliers")
|
route := v1.Group("/suppliers")
|
||||||
route.Use(m.Auth(u))
|
|
||||||
|
// route.Get("/", m.Auth(u), ctrl.GetAll)
|
||||||
|
// route.Post("/", m.Auth(u), ctrl.CreateOne)
|
||||||
|
// route.Get("/:id", m.Auth(u), ctrl.GetOne)
|
||||||
|
// route.Patch("/:id", m.Auth(u), ctrl.UpdateOne)
|
||||||
|
// route.Delete("/:id", m.Auth(u), ctrl.DeleteOne)
|
||||||
|
|
||||||
route.Get("/", ctrl.GetAll)
|
route.Get("/", ctrl.GetAll)
|
||||||
route.Post("/", ctrl.CreateOne)
|
route.Post("/", ctrl.CreateOne)
|
||||||
|
|||||||
@@ -23,3 +23,4 @@ func (UomModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate *vali
|
|||||||
|
|
||||||
UomRoutes(router, userService, uomService)
|
UomRoutes(router, userService, uomService)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package uoms
|
package uoms
|
||||||
|
|
||||||
import (
|
import (
|
||||||
m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
// m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
||||||
controller "gitlab.com/mbugroup/lti-api.git/internal/modules/master/uoms/controllers"
|
controller "gitlab.com/mbugroup/lti-api.git/internal/modules/master/uoms/controllers"
|
||||||
uom "gitlab.com/mbugroup/lti-api.git/internal/modules/master/uoms/services"
|
uom "gitlab.com/mbugroup/lti-api.git/internal/modules/master/uoms/services"
|
||||||
user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
||||||
@@ -13,7 +13,12 @@ func UomRoutes(v1 fiber.Router, u user.UserService, s uom.UomService) {
|
|||||||
ctrl := controller.NewUomController(s)
|
ctrl := controller.NewUomController(s)
|
||||||
|
|
||||||
route := v1.Group("/uoms")
|
route := v1.Group("/uoms")
|
||||||
route.Use(m.Auth(u))
|
|
||||||
|
// route.Get("/", m.Auth(u), ctrl.GetAll)
|
||||||
|
// route.Post("/", m.Auth(u), ctrl.CreateOne)
|
||||||
|
// route.Get("/:id", m.Auth(u), ctrl.GetOne)
|
||||||
|
// route.Patch("/:id", m.Auth(u), ctrl.UpdateOne)
|
||||||
|
// route.Delete("/:id", m.Auth(u), ctrl.DeleteOne)
|
||||||
|
|
||||||
route.Get("/", ctrl.GetAll)
|
route.Get("/", ctrl.GetAll)
|
||||||
route.Post("/", ctrl.CreateOne)
|
route.Post("/", ctrl.CreateOne)
|
||||||
|
|||||||
@@ -23,3 +23,4 @@ func (WarehouseModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate
|
|||||||
|
|
||||||
WarehouseRoutes(router, userService, warehouseService)
|
WarehouseRoutes(router, userService, warehouseService)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ type WarehouseRepository interface {
|
|||||||
NameExists(ctx context.Context, name string, excludeID *uint) (bool, error)
|
NameExists(ctx context.Context, name string, excludeID *uint) (bool, error)
|
||||||
IdExists(ctx context.Context, id uint) (bool, error)
|
IdExists(ctx context.Context, id uint) (bool, error)
|
||||||
GetByKandangID(ctx context.Context, kandangId uint) (*entity.Warehouse, error)
|
GetByKandangID(ctx context.Context, kandangId uint) (*entity.Warehouse, error)
|
||||||
|
GetLatestByKandangID(ctx context.Context, kandangId uint) (*entity.Warehouse, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type WarehouseRepositoryImpl struct {
|
type WarehouseRepositoryImpl struct {
|
||||||
@@ -60,3 +61,16 @@ func (r *WarehouseRepositoryImpl) GetByKandangID(ctx context.Context, kandangId
|
|||||||
}
|
}
|
||||||
return &warehouse, nil
|
return &warehouse, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *WarehouseRepositoryImpl) GetLatestByKandangID(ctx context.Context, kandangId uint) (*entity.Warehouse, error) {
|
||||||
|
var warehouse entity.Warehouse
|
||||||
|
err := r.db.WithContext(ctx).
|
||||||
|
Where("kandang_id = ?", kandangId).
|
||||||
|
Where("deleted_at IS NULL").
|
||||||
|
Order("id DESC").
|
||||||
|
First(&warehouse).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &warehouse, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package warehouses
|
package warehouses
|
||||||
|
|
||||||
import (
|
import (
|
||||||
m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
// m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
||||||
controller "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/controllers"
|
controller "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/controllers"
|
||||||
warehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/services"
|
warehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/services"
|
||||||
user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
||||||
@@ -13,7 +13,12 @@ func WarehouseRoutes(v1 fiber.Router, u user.UserService, s warehouse.WarehouseS
|
|||||||
ctrl := controller.NewWarehouseController(s)
|
ctrl := controller.NewWarehouseController(s)
|
||||||
|
|
||||||
route := v1.Group("/warehouses")
|
route := v1.Group("/warehouses")
|
||||||
route.Use(m.Auth(u))
|
|
||||||
|
// route.Get("/", m.Auth(u), ctrl.GetAll)
|
||||||
|
// route.Post("/", m.Auth(u), ctrl.CreateOne)
|
||||||
|
// route.Get("/:id", m.Auth(u), ctrl.GetOne)
|
||||||
|
// route.Patch("/:id", m.Auth(u), ctrl.UpdateOne)
|
||||||
|
// route.Delete("/:id", m.Auth(u), ctrl.DeleteOne)
|
||||||
|
|
||||||
route.Get("/", ctrl.GetAll)
|
route.Get("/", ctrl.GetAll)
|
||||||
route.Post("/", ctrl.CreateOne)
|
route.Post("/", ctrl.CreateOne)
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package controller
|
package controller
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"gitlab.com/mbugroup/lti-api.git/internal/modules/production/chickins/dto"
|
"gitlab.com/mbugroup/lti-api.git/internal/modules/production/chickins/dto"
|
||||||
@@ -22,30 +21,84 @@ func NewChickinController(chickinService service.ChickinService) *ChickinControl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *ChickinController) GetAll(c *fiber.Ctx) error {
|
// func (u *ChickinController) GetAll(c *fiber.Ctx) error {
|
||||||
query := &validation.Query{
|
// query := &validation.Query{
|
||||||
Page: c.QueryInt("page", 1),
|
// Page: c.QueryInt("page", 1),
|
||||||
Limit: c.QueryInt("limit", 10),
|
// Limit: c.QueryInt("limit", 10),
|
||||||
ProjectFlockKandangId: uint(c.QueryInt("project_flock_kandang_id", 0)),
|
// ProjectFlockKandangId: uint(c.QueryInt("project_flock_kandang_id", 0)),
|
||||||
|
// }
|
||||||
|
|
||||||
|
// result, totalResults, err := u.ChickinService.GetAll(c, query)
|
||||||
|
// if err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return c.Status(fiber.StatusOK).
|
||||||
|
// JSON(response.SuccessWithPaginate[dto.ChickinListDTO]{
|
||||||
|
// Code: fiber.StatusOK,
|
||||||
|
// Status: "success",
|
||||||
|
// Message: "Get all chickins successfully",
|
||||||
|
// Meta: response.Meta{
|
||||||
|
// Page: query.Page,
|
||||||
|
// Limit: query.Limit,
|
||||||
|
// TotalPages: int64(math.Ceil(float64(totalResults) / float64(query.Limit))),
|
||||||
|
// TotalResults: totalResults,
|
||||||
|
// },
|
||||||
|
// Data: dto.ToChickinListDTOs(result),
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func (u *ChickinController) GetOne(c *fiber.Ctx) error {
|
||||||
|
// param := c.Params("id")
|
||||||
|
|
||||||
|
// id, err := strconv.Atoi(param)
|
||||||
|
// if err != nil {
|
||||||
|
// return fiber.NewError(fiber.StatusBadRequest, "Invalid Id")
|
||||||
|
// }
|
||||||
|
|
||||||
|
// result, err := u.ChickinService.GetOne(c, uint(id))
|
||||||
|
// if err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return c.Status(fiber.StatusOK).
|
||||||
|
// JSON(response.Success{
|
||||||
|
// Code: fiber.StatusOK,
|
||||||
|
// Status: "success",
|
||||||
|
// Message: "Get chickin successfully",
|
||||||
|
// Data: dto.ToChickinListDTO(*result),
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
func (u *ChickinController) CreateOne(c *fiber.Ctx) error {
|
||||||
|
req := new(validation.Create)
|
||||||
|
|
||||||
|
if err := c.BodyParser(req); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
|
||||||
}
|
}
|
||||||
|
|
||||||
result, totalResults, err := u.ChickinService.GetAll(c, query)
|
results, err := u.ChickinService.CreateOne(c, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Status(fiber.StatusOK).
|
var (
|
||||||
JSON(response.SuccessWithPaginate[dto.ChickinListDTO]{
|
data interface{}
|
||||||
Code: fiber.StatusOK,
|
message = "Create chickin successfully"
|
||||||
|
)
|
||||||
|
if len(results) == 1 {
|
||||||
|
data = dto.ToChickinListDTO(results[0])
|
||||||
|
} else {
|
||||||
|
message = "Create chickins successfully"
|
||||||
|
data = dto.ToChickinListDTOs(results)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Status(fiber.StatusCreated).
|
||||||
|
JSON(response.Success{
|
||||||
|
Code: fiber.StatusCreated,
|
||||||
Status: "success",
|
Status: "success",
|
||||||
Message: "Get all chickins successfully",
|
Message: message,
|
||||||
Meta: response.Meta{
|
Data: data,
|
||||||
Page: query.Page,
|
|
||||||
Limit: query.Limit,
|
|
||||||
TotalPages: int64(math.Ceil(float64(totalResults) / float64(query.Limit))),
|
|
||||||
TotalResults: totalResults,
|
|
||||||
},
|
|
||||||
Data: dto.ToChickinListDTOs(result),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,95 +120,85 @@ func (u *ChickinController) GetOne(c *fiber.Ctx) error {
|
|||||||
Code: fiber.StatusOK,
|
Code: fiber.StatusOK,
|
||||||
Status: "success",
|
Status: "success",
|
||||||
Message: "Get chickin successfully",
|
Message: "Get chickin successfully",
|
||||||
Data: dto.ToChickinListDTO(*result),
|
Data: dto.ToChickinDetailDTO(*result),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *ChickinController) CreateOne(c *fiber.Ctx) error {
|
// func (u *ChickinController) UpdateOne(c *fiber.Ctx) error {
|
||||||
req := new(validation.Create)
|
// req := new(validation.Update)
|
||||||
|
// param := c.Params("id")
|
||||||
|
|
||||||
|
// id, err := strconv.Atoi(param)
|
||||||
|
// if err != nil {
|
||||||
|
// return fiber.NewError(fiber.StatusBadRequest, "Invalid Id")
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if err := c.BodyParser(req); err != nil {
|
||||||
|
// return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
|
||||||
|
// }
|
||||||
|
|
||||||
|
// result, err := u.ChickinService.UpdateOne(c, req, uint(id))
|
||||||
|
// if err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return c.Status(fiber.StatusOK).
|
||||||
|
// JSON(response.Success{
|
||||||
|
// Code: fiber.StatusOK,
|
||||||
|
// Status: "success",
|
||||||
|
// Message: "Update chickin successfully",
|
||||||
|
// Data: dto.ToChickinListDTO(*result),
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func (u *ChickinController) DeleteOne(c *fiber.Ctx) error {
|
||||||
|
// param := c.Params("id")
|
||||||
|
|
||||||
|
// id, err := strconv.Atoi(param)
|
||||||
|
// if err != nil {
|
||||||
|
// return fiber.NewError(fiber.StatusBadRequest, "Invalid Id")
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if err := u.ChickinService.DeleteOne(c, uint(id)); err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return c.Status(fiber.StatusOK).
|
||||||
|
// JSON(response.Common{
|
||||||
|
// Code: fiber.StatusOK,
|
||||||
|
// Status: "success",
|
||||||
|
// Message: "Delete chickin successfully",
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
func (u *ChickinController) Approval(c *fiber.Ctx) error {
|
||||||
|
req := new(validation.Approve)
|
||||||
|
|
||||||
if err := c.BodyParser(req); err != nil {
|
if err := c.BodyParser(req); err != nil {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
|
return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := u.ChickinService.CreateOne(c, req)
|
results, err := u.ChickinService.Approval(c, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Status(fiber.StatusCreated).
|
var (
|
||||||
JSON(response.Success{
|
data interface{}
|
||||||
Code: fiber.StatusCreated,
|
message = "Submit chickin approval successfully"
|
||||||
Status: "success",
|
)
|
||||||
Message: "Create chickin successfully",
|
if len(results) == 1 {
|
||||||
Data: dto.ToChickinListDTO(*result),
|
data = dto.ToChickinListDTO(results[0])
|
||||||
})
|
} else {
|
||||||
}
|
message = "Submit chickin approvals successfully"
|
||||||
|
data = dto.ToChickinListDTOs(results)
|
||||||
func (u *ChickinController) UpdateOne(c *fiber.Ctx) error {
|
|
||||||
req := new(validation.Update)
|
|
||||||
param := c.Params("id")
|
|
||||||
|
|
||||||
id, err := strconv.Atoi(param)
|
|
||||||
if err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid Id")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := c.BodyParser(req); err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
|
|
||||||
}
|
|
||||||
|
|
||||||
result, err := u.ChickinService.UpdateOne(c, req, uint(id))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Status(fiber.StatusOK).
|
return c.Status(fiber.StatusOK).
|
||||||
JSON(response.Success{
|
JSON(response.Success{
|
||||||
Code: fiber.StatusOK,
|
Code: fiber.StatusOK,
|
||||||
Status: "success",
|
Status: "success",
|
||||||
Message: "Update chickin successfully",
|
Message: message,
|
||||||
Data: dto.ToChickinListDTO(*result),
|
Data: data,
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *ChickinController) DeleteOne(c *fiber.Ctx) error {
|
|
||||||
param := c.Params("id")
|
|
||||||
|
|
||||||
id, err := strconv.Atoi(param)
|
|
||||||
if err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid Id")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := u.ChickinService.DeleteOne(c, uint(id)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.Status(fiber.StatusOK).
|
|
||||||
JSON(response.Common{
|
|
||||||
Code: fiber.StatusOK,
|
|
||||||
Status: "success",
|
|
||||||
Message: "Delete chickin successfully",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *ChickinController) Approve(c *fiber.Ctx) error {
|
|
||||||
param := c.Params("id")
|
|
||||||
|
|
||||||
id, err := strconv.Atoi(param)
|
|
||||||
if err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid Id")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := u.ChickinService.Approve(c, uint(id)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.Status(fiber.StatusOK).
|
|
||||||
JSON(response.Success{
|
|
||||||
Code: fiber.StatusOK,
|
|
||||||
Status: "success",
|
|
||||||
Message: "Approve chickin successfully",
|
|
||||||
Data: nil,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,11 +16,13 @@ import (
|
|||||||
// === DTO Structs (ordered) ===
|
// === DTO Structs (ordered) ===
|
||||||
|
|
||||||
type ChickinBaseDTO struct {
|
type ChickinBaseDTO struct {
|
||||||
Id uint `json:"id"`
|
Id uint `json:"id"`
|
||||||
ProjectFlockKandang *ProjectFlockKandangDTO `json:"project_flock_kandang"`
|
ProjectFlockKandangId uint `json:"project_flock_kandang_id"`
|
||||||
ChickInDate time.Time `json:"chick_in_date"`
|
ChickInDate time.Time `json:"chick_in_date"`
|
||||||
Quantity float64 `json:"quantity"`
|
ProductWarehouseId uint `json:"product_warehouse_id"`
|
||||||
Note string `json:"note"`
|
UsageQty float64 `json:"usage_qty"`
|
||||||
|
PendingUsageQty float64 `json:"pending_usage_qty"`
|
||||||
|
Notes string `json:"notes"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProjectFlockDTO struct {
|
type ProjectFlockDTO struct {
|
||||||
@@ -45,21 +47,32 @@ type ChickinSimpleDTO struct {
|
|||||||
Id uint `json:"id"`
|
Id uint `json:"id"`
|
||||||
ProjectFlockKandangId uint `json:"project_flock_kandang_id"`
|
ProjectFlockKandangId uint `json:"project_flock_kandang_id"`
|
||||||
ChickInDate time.Time `json:"chick_in_date"`
|
ChickInDate time.Time `json:"chick_in_date"`
|
||||||
Quantity float64 `json:"quantity"`
|
ProductWarehouseId uint `json:"product_warehouse_id"`
|
||||||
Note string `json:"note"`
|
UsageQty float64 `json:"usage_qty"`
|
||||||
|
PendingUsageQty float64 `json:"pending_usage_qty"`
|
||||||
|
Notes string `json:"notes"`
|
||||||
CreatedBy uint `json:"created_by"`
|
CreatedBy uint `json:"created_by"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChickinListDTO struct {
|
type ChickinListDTO struct {
|
||||||
ChickinBaseDTO
|
ChickinBaseDTO
|
||||||
ProjectFlockKandang *ProjectFlockKandangDTO `json:"project_flock_kandang"`
|
CreatedUser *userBaseDTO.UserBaseDTO `json:"created_user"`
|
||||||
CreatedUser *userBaseDTO.UserBaseDTO `json:"created_user"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChickinDetailDTO struct {
|
type ChickinDetailDTO struct {
|
||||||
ChickinListDTO
|
Id uint `json:"id"`
|
||||||
|
ProjectFlockKandangId uint `json:"project_flock_kandang_id"`
|
||||||
|
ChickInDate time.Time `json:"chick_in_date"`
|
||||||
|
ProductWarehouseId uint `json:"product_warehouse_id"`
|
||||||
|
UsageQty float64 `json:"usage_qty"`
|
||||||
|
PendingUsageQty float64 `json:"pending_usage_qty"`
|
||||||
|
Notes string `json:"notes"`
|
||||||
|
CreatedBy uint `json:"created_by"`
|
||||||
|
CreatedUser *userBaseDTO.UserBaseDTO `json:"created_user"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// === Mapper Functions (ordered) ===
|
// === Mapper Functions (ordered) ===
|
||||||
@@ -138,17 +151,22 @@ func ToProjectFlockKandangDTO(e entity.ProjectFlockKandang) ProjectFlockKandangD
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ToChickinBaseDTO(e entity.ProjectChickin) ChickinBaseDTO {
|
func ToChickinBaseDTO(e entity.ProjectChickin) ChickinBaseDTO {
|
||||||
var pfk *ProjectFlockKandangDTO
|
var projectFlockKandangId uint
|
||||||
if e.ProjectFlockKandang.Id != 0 {
|
// Check if ProjectFlockKandang relation is loaded
|
||||||
mapped := ToProjectFlockKandangDTO(e.ProjectFlockKandang)
|
if e.ProjectFlockKandang != nil && e.ProjectFlockKandang.Id != 0 {
|
||||||
pfk = &mapped
|
projectFlockKandangId = e.ProjectFlockKandang.Id
|
||||||
|
} else if e.ProjectFlockKandangId != 0 {
|
||||||
|
// If relation is not loaded but ID is available, use the ID
|
||||||
|
projectFlockKandangId = e.ProjectFlockKandangId
|
||||||
}
|
}
|
||||||
return ChickinBaseDTO{
|
return ChickinBaseDTO{
|
||||||
Id: e.Id,
|
Id: e.Id,
|
||||||
ProjectFlockKandang: pfk,
|
ProjectFlockKandangId: projectFlockKandangId,
|
||||||
ChickInDate: e.ChickInDate,
|
ChickInDate: e.ChickInDate,
|
||||||
Quantity: e.Quantity,
|
ProductWarehouseId: e.ProductWarehouseId,
|
||||||
Note: e.Note,
|
UsageQty: e.UsageQty,
|
||||||
|
PendingUsageQty: e.PendingUsageQty,
|
||||||
|
Notes: e.Notes,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,29 +175,25 @@ func ToChickinSimpleDTO(e entity.ProjectChickin) ChickinSimpleDTO {
|
|||||||
Id: e.Id,
|
Id: e.Id,
|
||||||
ProjectFlockKandangId: e.ProjectFlockKandangId,
|
ProjectFlockKandangId: e.ProjectFlockKandangId,
|
||||||
ChickInDate: e.ChickInDate,
|
ChickInDate: e.ChickInDate,
|
||||||
Quantity: e.Quantity,
|
ProductWarehouseId: e.ProductWarehouseId,
|
||||||
Note: e.Note,
|
UsageQty: e.UsageQty,
|
||||||
|
PendingUsageQty: e.PendingUsageQty,
|
||||||
|
Notes: e.Notes,
|
||||||
CreatedBy: e.CreatedBy,
|
CreatedBy: e.CreatedBy,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ToChickinListDTO(e entity.ProjectChickin) ChickinListDTO {
|
func ToChickinListDTO(e entity.ProjectChickin) ChickinListDTO {
|
||||||
var createdUser *userBaseDTO.UserBaseDTO
|
var createdUser *userBaseDTO.UserBaseDTO
|
||||||
if e.CreatedUser.Id != 0 {
|
if e.CreatedUser != nil && e.CreatedUser.Id != 0 {
|
||||||
mapped := userBaseDTO.ToUserBaseDTO(e.CreatedUser)
|
mapped := userBaseDTO.ToUserBaseDTO(*e.CreatedUser)
|
||||||
createdUser = &mapped
|
createdUser = &mapped
|
||||||
}
|
}
|
||||||
var pfk *ProjectFlockKandangDTO
|
|
||||||
if e.ProjectFlockKandang.Id != 0 {
|
|
||||||
mapped := ToProjectFlockKandangDTO(e.ProjectFlockKandang)
|
|
||||||
pfk = &mapped
|
|
||||||
}
|
|
||||||
return ChickinListDTO{
|
return ChickinListDTO{
|
||||||
ChickinBaseDTO: ToChickinBaseDTO(e),
|
ChickinBaseDTO: ToChickinBaseDTO(e),
|
||||||
ProjectFlockKandang: pfk,
|
CreatedUser: createdUser,
|
||||||
CreatedUser: createdUser,
|
CreatedAt: e.CreatedAt,
|
||||||
CreatedAt: e.CreatedAt,
|
UpdatedAt: e.UpdatedAt,
|
||||||
UpdatedAt: e.UpdatedAt,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,7 +214,31 @@ func ToChickinSimpleDTOs(e []entity.ProjectChickin) []ChickinSimpleDTO {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ToChickinDetailDTO(e entity.ProjectChickin) ChickinDetailDTO {
|
func ToChickinDetailDTO(e entity.ProjectChickin) ChickinDetailDTO {
|
||||||
|
var createdUser *userBaseDTO.UserBaseDTO
|
||||||
|
if e.CreatedUser != nil && e.CreatedUser.Id != 0 {
|
||||||
|
mapped := userBaseDTO.ToUserBaseDTO(*e.CreatedUser)
|
||||||
|
createdUser = &mapped
|
||||||
|
}
|
||||||
|
|
||||||
return ChickinDetailDTO{
|
return ChickinDetailDTO{
|
||||||
ChickinListDTO: ToChickinListDTO(e),
|
Id: e.Id,
|
||||||
|
ProjectFlockKandangId: e.ProjectFlockKandangId,
|
||||||
|
ChickInDate: e.ChickInDate,
|
||||||
|
ProductWarehouseId: e.ProductWarehouseId,
|
||||||
|
UsageQty: e.UsageQty,
|
||||||
|
PendingUsageQty: e.PendingUsageQty,
|
||||||
|
Notes: e.Notes,
|
||||||
|
CreatedBy: e.CreatedBy,
|
||||||
|
CreatedUser: createdUser,
|
||||||
|
CreatedAt: e.CreatedAt,
|
||||||
|
UpdatedAt: e.UpdatedAt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ToChickinDetailDTOs(e []entity.ProjectChickin) []ChickinDetailDTO {
|
||||||
|
result := make([]ChickinDetailDTO, len(e))
|
||||||
|
for i, r := range e {
|
||||||
|
result[i] = ToChickinDetailDTO(r)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
package chickins
|
package chickins
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/go-playground/validator/v10"
|
"github.com/go-playground/validator/v10"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
|
commonRepo "gitlab.com/mbugroup/lti-api.git/internal/common/repository"
|
||||||
|
commonSvc "gitlab.com/mbugroup/lti-api.git/internal/common/service"
|
||||||
|
|
||||||
rProductWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/repositories"
|
rProductWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/repositories"
|
||||||
rKandang "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/repositories"
|
rKandang "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/repositories"
|
||||||
rWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/repositories"
|
rWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/repositories"
|
||||||
@@ -15,6 +20,8 @@ import (
|
|||||||
|
|
||||||
rUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/repositories"
|
rUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/repositories"
|
||||||
sUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
sUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
||||||
|
|
||||||
|
utils "gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ChickinModule struct{}
|
type ChickinModule struct{}
|
||||||
@@ -32,6 +39,12 @@ func (ChickinModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate *
|
|||||||
|
|
||||||
userRepo := rUser.NewUserRepository(db)
|
userRepo := rUser.NewUserRepository(db)
|
||||||
|
|
||||||
|
approvalRepo := commonRepo.NewApprovalRepository(db)
|
||||||
|
approvalService := commonSvc.NewApprovalService(approvalRepo)
|
||||||
|
if err := approvalService.RegisterWorkflowSteps(utils.ApprovalWorkflowProjectFlockKandang, utils.ProjectFlockKandangApprovalSteps); err != nil {
|
||||||
|
panic(fmt.Sprintf("failed to register project flock kandang approval workflow: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
chickinService := sChickin.NewChickinService(chickinRepo, kandangRepo, warehouseRepo, productWarehouseRepo, projectFlockRepo, projectflockkandangrepo, projectflockpopulationrepo, chickinDetailRepo, validate)
|
chickinService := sChickin.NewChickinService(chickinRepo, kandangRepo, warehouseRepo, productWarehouseRepo, projectFlockRepo, projectflockkandangrepo, projectflockpopulationrepo, chickinDetailRepo, validate)
|
||||||
userService := sUser.NewUserService(userRepo, validate)
|
userService := sUser.NewUserService(userRepo, validate)
|
||||||
|
|
||||||
|
|||||||
@@ -11,21 +11,26 @@ import (
|
|||||||
type ProjectChickinRepository interface {
|
type ProjectChickinRepository interface {
|
||||||
repository.BaseRepository[entity.ProjectChickin]
|
repository.BaseRepository[entity.ProjectChickin]
|
||||||
GetFirstByProjectFlockID(ctx context.Context, projectFlockID uint) (*entity.ProjectChickin, error)
|
GetFirstByProjectFlockID(ctx context.Context, projectFlockID uint) (*entity.ProjectChickin, error)
|
||||||
|
GetByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) ([]entity.ProjectChickin, error)
|
||||||
|
GetPendingByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) ([]entity.ProjectChickin, error)
|
||||||
|
GetTotalPendingUsageQtyByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) (float64, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChickinRepositoryImpl struct {
|
type ChickinRepositoryImpl struct {
|
||||||
*repository.BaseRepositoryImpl[entity.ProjectChickin]
|
*repository.BaseRepositoryImpl[entity.ProjectChickin]
|
||||||
|
db *gorm.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewChickinRepository(db *gorm.DB) ProjectChickinRepository {
|
func NewChickinRepository(db *gorm.DB) ProjectChickinRepository {
|
||||||
return &ChickinRepositoryImpl{
|
return &ChickinRepositoryImpl{
|
||||||
BaseRepositoryImpl: repository.NewBaseRepository[entity.ProjectChickin](db),
|
BaseRepositoryImpl: repository.NewBaseRepository[entity.ProjectChickin](db),
|
||||||
|
db: db,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ChickinRepositoryImpl) GetFirstByProjectFlockID(ctx context.Context, projectFlockID uint) (*entity.ProjectChickin, error) {
|
func (r *ChickinRepositoryImpl) GetFirstByProjectFlockID(ctx context.Context, projectFlockID uint) (*entity.ProjectChickin, error) {
|
||||||
var chickin entity.ProjectChickin
|
var chickin entity.ProjectChickin
|
||||||
err := r.DB().WithContext(ctx).
|
err := r.db.WithContext(ctx).
|
||||||
Where("project_floc_id = ?", projectFlockID).
|
Where("project_floc_id = ?", projectFlockID).
|
||||||
Where("deleted_at IS NULL").
|
Where("deleted_at IS NULL").
|
||||||
First(&chickin).Error
|
First(&chickin).Error
|
||||||
@@ -34,3 +39,43 @@ func (r *ChickinRepositoryImpl) GetFirstByProjectFlockID(ctx context.Context, pr
|
|||||||
}
|
}
|
||||||
return &chickin, nil
|
return &chickin, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *ChickinRepositoryImpl) GetByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) ([]entity.ProjectChickin, error) {
|
||||||
|
var chickins []entity.ProjectChickin
|
||||||
|
err := r.db.WithContext(ctx).
|
||||||
|
Where("project_flock_kandang_id = ?", projectFlockKandangID).
|
||||||
|
Order("created_at DESC").
|
||||||
|
Find(&chickins).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return chickins, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ChickinRepositoryImpl) GetPendingByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) ([]entity.ProjectChickin, error) {
|
||||||
|
var chickins []entity.ProjectChickin
|
||||||
|
err := r.db.WithContext(ctx).
|
||||||
|
Where("project_flock_kandang_id = ?", projectFlockKandangID).
|
||||||
|
Where("usage_qty = 0").
|
||||||
|
Where("pending_usage_qty > 0").
|
||||||
|
Order("created_at DESC").
|
||||||
|
Find(&chickins).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return chickins, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ChickinRepositoryImpl) GetTotalPendingUsageQtyByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) (float64, error) {
|
||||||
|
var total float64
|
||||||
|
err := r.db.WithContext(ctx).
|
||||||
|
Model(&entity.ProjectChickin{}).
|
||||||
|
Where("project_flock_kandang_id = ?", projectFlockKandangID).
|
||||||
|
Where("pending_usage_qty > 0").
|
||||||
|
Select("COALESCE(SUM(pending_usage_qty), 0)").
|
||||||
|
Row().Scan(&total)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return total, nil
|
||||||
|
}
|
||||||
|
|||||||
+25
@@ -1,6 +1,8 @@
|
|||||||
package repository
|
package repository
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
"gitlab.com/mbugroup/lti-api.git/internal/common/repository"
|
"gitlab.com/mbugroup/lti-api.git/internal/common/repository"
|
||||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
@@ -8,6 +10,10 @@ import (
|
|||||||
|
|
||||||
type ProjectChickinDetailRepository interface {
|
type ProjectChickinDetailRepository interface {
|
||||||
repository.BaseRepository[entity.ProjectChickinDetail]
|
repository.BaseRepository[entity.ProjectChickinDetail]
|
||||||
|
CreateOne(ctx context.Context, entity *entity.ProjectChickinDetail, modifier func(*gorm.DB) *gorm.DB) error
|
||||||
|
DeleteMany(ctx context.Context, modifier func(*gorm.DB) *gorm.DB) error
|
||||||
|
GetByProjectChickinID(ctx context.Context, projectChickinID uint) ([]entity.ProjectChickinDetail, error)
|
||||||
|
WithTxRepo(tx *gorm.DB) ProjectChickinDetailRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChickinDetailRepositoryImpl struct {
|
type ChickinDetailRepositoryImpl struct {
|
||||||
@@ -19,3 +25,22 @@ func NewChickinDetailRepository(db *gorm.DB) ProjectChickinDetailRepository {
|
|||||||
BaseRepositoryImpl: repository.NewBaseRepository[entity.ProjectChickinDetail](db),
|
BaseRepositoryImpl: repository.NewBaseRepository[entity.ProjectChickinDetail](db),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *ChickinDetailRepositoryImpl) WithTxRepo(tx *gorm.DB) ProjectChickinDetailRepository {
|
||||||
|
return &ChickinDetailRepositoryImpl{BaseRepositoryImpl: repository.NewBaseRepository[entity.ProjectChickinDetail](tx)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ChickinDetailRepositoryImpl) DB() *gorm.DB {
|
||||||
|
return r.BaseRepositoryImpl.DB()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ChickinDetailRepositoryImpl) GetByProjectChickinID(ctx context.Context, projectChickinID uint) ([]entity.ProjectChickinDetail, error) {
|
||||||
|
var records []entity.ProjectChickinDetail
|
||||||
|
if err := r.DB().WithContext(ctx).Where("project_chickin_id = ?", projectChickinID).Find(&records).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(records) == 0 {
|
||||||
|
return nil, gorm.ErrRecordNotFound
|
||||||
|
}
|
||||||
|
return records, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package chickins
|
package chickins
|
||||||
|
|
||||||
import (
|
import (
|
||||||
m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
// m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
||||||
controller "gitlab.com/mbugroup/lti-api.git/internal/modules/production/chickins/controllers"
|
controller "gitlab.com/mbugroup/lti-api.git/internal/modules/production/chickins/controllers"
|
||||||
chickin "gitlab.com/mbugroup/lti-api.git/internal/modules/production/chickins/services"
|
chickin "gitlab.com/mbugroup/lti-api.git/internal/modules/production/chickins/services"
|
||||||
user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
||||||
@@ -13,12 +13,17 @@ func ChickinRoutes(v1 fiber.Router, u user.UserService, s chickin.ChickinService
|
|||||||
ctrl := controller.NewChickinController(s)
|
ctrl := controller.NewChickinController(s)
|
||||||
|
|
||||||
route := v1.Group("/chickins")
|
route := v1.Group("/chickins")
|
||||||
route.Use(m.Auth(u))
|
|
||||||
|
|
||||||
route.Get("/", ctrl.GetAll)
|
// route.Get("/", m.Auth(u), ctrl.GetAll)
|
||||||
|
// route.Post("/", m.Auth(u), ctrl.CreateOne)
|
||||||
|
// route.Get("/:id", m.Auth(u), ctrl.GetOne)
|
||||||
|
// route.Patch("/:id", m.Auth(u), ctrl.UpdateOne)
|
||||||
|
// route.Delete("/:id", m.Auth(u), ctrl.DeleteOne)
|
||||||
|
|
||||||
|
// route.Get("/", ctrl.GetAll)
|
||||||
route.Post("/", ctrl.CreateOne)
|
route.Post("/", ctrl.CreateOne)
|
||||||
route.Get("/:id", ctrl.GetOne)
|
route.Get("/:id", ctrl.GetOne)
|
||||||
route.Patch("/:id", ctrl.UpdateOne)
|
// route.Patch("/:id", ctrl.UpdateOne)
|
||||||
route.Delete("/:id", ctrl.DeleteOne)
|
// route.Delete("/:id", ctrl.DeleteOne)
|
||||||
route.Post("/:id/approve", ctrl.Approve)
|
route.Post("/approvals", ctrl.Approval)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,11 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
commonRepo "gitlab.com/mbugroup/lti-api.git/internal/common/repository"
|
||||||
|
commonSvc "gitlab.com/mbugroup/lti-api.git/internal/common/service"
|
||||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||||
rProductWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/repositories"
|
rProductWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/repositories"
|
||||||
KandangRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/repositories"
|
KandangRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/repositories"
|
||||||
@@ -21,10 +25,10 @@ import (
|
|||||||
type ChickinService interface {
|
type ChickinService interface {
|
||||||
GetAll(ctx *fiber.Ctx, params *validation.Query) ([]entity.ProjectChickin, int64, error)
|
GetAll(ctx *fiber.Ctx, params *validation.Query) ([]entity.ProjectChickin, int64, error)
|
||||||
GetOne(ctx *fiber.Ctx, id uint) (*entity.ProjectChickin, error)
|
GetOne(ctx *fiber.Ctx, id uint) (*entity.ProjectChickin, error)
|
||||||
CreateOne(ctx *fiber.Ctx, req *validation.Create) (*entity.ProjectChickin, error)
|
CreateOne(ctx *fiber.Ctx, req *validation.Create) ([]entity.ProjectChickin, error)
|
||||||
UpdateOne(ctx *fiber.Ctx, req *validation.Update, id uint) (*entity.ProjectChickin, error)
|
UpdateOne(ctx *fiber.Ctx, req *validation.Update, id uint) (*entity.ProjectChickin, error)
|
||||||
DeleteOne(ctx *fiber.Ctx, id uint) error
|
DeleteOne(ctx *fiber.Ctx, id uint) error
|
||||||
Approve(ctx *fiber.Ctx, id uint) error
|
Approval(ctx *fiber.Ctx, req *validation.Approve) ([]entity.ProjectChickin, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type chickinService struct {
|
type chickinService struct {
|
||||||
@@ -76,6 +80,7 @@ func (s chickinService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity
|
|||||||
}
|
}
|
||||||
|
|
||||||
offset := (params.Page - 1) * params.Limit
|
offset := (params.Page - 1) * params.Limit
|
||||||
|
|
||||||
chickins, total, err := s.Repository.GetAll(c.Context(), offset, params.Limit, func(db *gorm.DB) *gorm.DB {
|
chickins, total, err := s.Repository.GetAll(c.Context(), offset, params.Limit, func(db *gorm.DB) *gorm.DB {
|
||||||
db = s.withRelations(db)
|
db = s.withRelations(db)
|
||||||
if params.ProjectFlockKandangId != 0 {
|
if params.ProjectFlockKandangId != 0 {
|
||||||
@@ -103,112 +108,164 @@ func (s chickinService) GetOne(c *fiber.Ctx, id uint) (*entity.ProjectChickin, e
|
|||||||
return chickin, nil
|
return chickin, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *chickinService) CreateOne(c *fiber.Ctx, req *validation.Create) (*entity.ProjectChickin, error) {
|
func (s *chickinService) CreateOne(c *fiber.Ctx, req *validation.Create) ([]entity.ProjectChickin, error) {
|
||||||
if err := s.Validate.Struct(req); err != nil {
|
if err := s.Validate.Struct(req); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
projectflockkandang, err := s.ProjectflockKandangRepo.GetByID(c.Context(), req.ProjectFlockKandangId)
|
projectFlockKandang, err := s.ProjectflockKandangRepo.GetByID(c.Context(), req.ProjectFlockKandangId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.Log.Errorf("Failed to get projectflock kandang: %+v", err)
|
return nil, fiber.NewError(fiber.StatusNotFound, "Project Flock Kandang not found")
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
warehouse, err := s.WarehouseRepo.GetByKandangID(c.Context(), projectflockkandang.KandangId)
|
warehouse, err := s.WarehouseRepo.GetByKandangID(c.Context(), projectFlockKandang.KandangId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.Log.Errorf("Failed to get warehouse: %+v", err)
|
return nil, fiber.NewError(fiber.StatusNotFound, "Warehouse for Kandang not found")
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// move complex DB query into repository for cleaner service
|
var productWarehouses []entity.ProductWarehouse
|
||||||
productWarehouses, err := s.ProductWarehouseRepo.GetByCategoryCodeAndWarehouseID(c.Context(), "DOC", warehouse.Id)
|
category := strings.ToUpper(strings.TrimSpace(projectFlockKandang.ProjectFlock.Category))
|
||||||
if err != nil {
|
|
||||||
s.Log.Errorf("Failed to get product warehouses: %+v", err)
|
var productCategoryCode string
|
||||||
return nil, err
|
switch category {
|
||||||
}
|
case string(utils.ProjectFlockCategoryGrowing):
|
||||||
if len(productWarehouses) == 0 {
|
productCategoryCode = "DOC"
|
||||||
return nil, fiber.NewError(fiber.StatusNotFound, "Product Warehouse not found for the given Project Flock and Warehouse")
|
case string(utils.ProjectFlockCategoryLaying):
|
||||||
}
|
productCategoryCode = "PULLET"
|
||||||
totalQuantity := 0.0
|
default:
|
||||||
for _, pw := range productWarehouses {
|
return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Unknown category: %s", category))
|
||||||
totalQuantity += pw.Quantity
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if totalQuantity < 1 {
|
productWarehouses, err = s.ProductWarehouseRepo.GetByCategoryCodeAndWarehouseID(c.Context(), productCategoryCode, warehouse.Id)
|
||||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Insufficient quantity in Product Warehouses")
|
if err != nil || len(productWarehouses) == 0 {
|
||||||
|
return nil, fiber.NewError(fiber.StatusNotFound, fmt.Sprintf("Product for %s category in the Kandang's warehouse not found", strings.ToLower(category)))
|
||||||
}
|
}
|
||||||
|
|
||||||
chickinDate, err := utils.ParseDateString(req.ChickInDate)
|
chickinDate, err := utils.ParseDateString(req.ChickInDate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.Log.Errorf("Failed to parse chickin date: %+v", err)
|
|
||||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid ChickInDate format")
|
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid ChickInDate format")
|
||||||
}
|
}
|
||||||
newChickin := &entity.ProjectChickin{
|
|
||||||
ProjectFlockKandangId: projectflockkandang.Id,
|
|
||||||
ChickInDate: chickinDate,
|
|
||||||
Quantity: totalQuantity,
|
|
||||||
Note: req.Note,
|
|
||||||
CreatedBy: 1, //todo: ganti dengan user login
|
|
||||||
}
|
|
||||||
err = s.Repository.CreateOne(c.Context(), newChickin, nil)
|
|
||||||
if err != nil {
|
|
||||||
s.Log.Errorf("Failed to create chickin: %+v", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update semua product warehouse: set quantity jadi 0
|
actorID := uint(1) // todo nanti ambil dari auth context
|
||||||
for _, pw := range productWarehouses {
|
newChikins := make([]*entity.ProjectChickin, 0)
|
||||||
err = s.ProductWarehouseRepo.PatchOne(c.Context(), pw.Id, map[string]any{
|
for _, productWarehouse := range productWarehouses {
|
||||||
"quantity": 0,
|
availableQty, err := s.calculateAvailableQuantity(c, req.ProjectFlockKandangId, &productWarehouse, category)
|
||||||
}, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.Log.Errorf("Failed to update product warehouse quantity: %+v", err)
|
return nil, fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("Failed to calculate available quantity for product warehouse %d", productWarehouse.Id))
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
newChickinDetail := &entity.ProjectChickinDetail{
|
if availableQty <= 0 {
|
||||||
ProjectChickinId: newChickin.Id,
|
continue
|
||||||
ProductWarehouseId: pw.Id,
|
|
||||||
Quantity: pw.Quantity,
|
|
||||||
CreatedBy: 1, // todo: ganti dengan user login
|
|
||||||
}
|
}
|
||||||
err = s.ProjectChickinDetailRepo.CreateOne(c.Context(), newChickinDetail, nil)
|
|
||||||
if err != nil {
|
|
||||||
s.Log.Errorf("Failed to create chickin detail: %+v", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
existingPopulation, err := s.ProjectflockPopulationRepo.GetByProjectFlockKandangID(c.Context(), req.ProjectFlockKandangId)
|
newChickin := &entity.ProjectChickin{
|
||||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
||||||
s.Log.Errorf("Failed to get project flock population: %+v", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if existingPopulation != nil {
|
|
||||||
|
|
||||||
err = s.ProjectflockPopulationRepo.PatchOne(c.Context(), existingPopulation.Id, map[string]any{
|
|
||||||
"reserved_quantity": newChickin.Quantity + existingPopulation.ReservedQuantity,
|
|
||||||
}, nil)
|
|
||||||
if err != nil {
|
|
||||||
s.Log.Errorf("Failed to update project flock population: %+v", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
newPopulation := &entity.ProjectFlockPopulation{
|
|
||||||
ProjectFlockKandangId: req.ProjectFlockKandangId,
|
ProjectFlockKandangId: req.ProjectFlockKandangId,
|
||||||
InitialQuantity: 0,
|
ChickInDate: chickinDate,
|
||||||
CurrentQuantity: 0,
|
UsageQty: 0,
|
||||||
ReservedQuantity: newChickin.Quantity,
|
PendingUsageQty: availableQty,
|
||||||
CreatedBy: 1, // todo: ganti dengan user login
|
ProductWarehouseId: productWarehouse.Id,
|
||||||
}
|
Notes: req.Note,
|
||||||
err = s.ProjectflockPopulationRepo.CreateOne(c.Context(), newPopulation, nil)
|
CreatedBy: actorID,
|
||||||
if err != nil {
|
|
||||||
s.Log.Errorf("Failed to create project flock population: %+v", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
newChikins = append(newChikins, newChickin)
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.GetOne(c, newChickin.Id)
|
if len(newChikins) == 0 {
|
||||||
|
return nil, fiber.NewError(fiber.StatusBadRequest, "No chickins to create")
|
||||||
|
}
|
||||||
|
|
||||||
|
existingChikins, err := s.Repository.GetByProjectFlockKandangID(c.Context(), req.ProjectFlockKandangId)
|
||||||
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to check existing chickins")
|
||||||
|
}
|
||||||
|
|
||||||
|
isFirstTime := len(existingChikins) == 0
|
||||||
|
|
||||||
|
err = s.Repository.DB().WithContext(c.Context()).Transaction(func(dbTransaction *gorm.DB) error {
|
||||||
|
|
||||||
|
approvalSvcTx := commonSvc.NewApprovalService(commonRepo.NewApprovalRepository(dbTransaction))
|
||||||
|
productWarehouseTx := s.ProductWarehouseRepo.WithTx(dbTransaction)
|
||||||
|
|
||||||
|
if err := s.Repository.WithTx(dbTransaction).CreateMany(c.Context(), newChikins, nil); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to create chickins")
|
||||||
|
}
|
||||||
|
|
||||||
|
latest, err := approvalSvcTx.LatestByTarget(c.Context(), utils.ApprovalWorkflowProjectFlockKandang, projectFlockKandang.Id, nil)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get latest approval")
|
||||||
|
}
|
||||||
|
|
||||||
|
if category == string(utils.ProjectFlockCategoryLaying) {
|
||||||
|
for _, chickin := range newChikins {
|
||||||
|
updates := map[string]any{"quantity": gorm.Expr("quantity - ?", chickin.PendingUsageQty)}
|
||||||
|
|
||||||
|
if err := productWarehouseTx.PatchOne(c.Context(), chickin.ProductWarehouseId, updates, nil); err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, fmt.Sprintf("Product warehouse %d not found", chickin.ProductWarehouseId))
|
||||||
|
}
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update product warehouse quantity")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var approvalAction entity.ApprovalAction
|
||||||
|
if isFirstTime {
|
||||||
|
approvalAction = entity.ApprovalActionCreated
|
||||||
|
} else {
|
||||||
|
approvalAction = entity.ApprovalActionUpdated
|
||||||
|
}
|
||||||
|
|
||||||
|
if latest == nil {
|
||||||
|
if _, err := approvalSvcTx.CreateApproval(
|
||||||
|
c.Context(),
|
||||||
|
utils.ApprovalWorkflowProjectFlockKandang,
|
||||||
|
projectFlockKandang.Id,
|
||||||
|
utils.ProjectFlockKandangStepPengajuan,
|
||||||
|
&approvalAction,
|
||||||
|
actorID,
|
||||||
|
nil); err != nil {
|
||||||
|
if !errors.Is(err, gorm.ErrDuplicatedKey) {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to create approval")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if latest.StepNumber != uint16(utils.ProjectFlockKandangStepPengajuan) {
|
||||||
|
if _, err := approvalSvcTx.CreateApproval(
|
||||||
|
c.Context(),
|
||||||
|
utils.ApprovalWorkflowProjectFlockKandang,
|
||||||
|
projectFlockKandang.Id,
|
||||||
|
utils.ProjectFlockKandangStepPengajuan,
|
||||||
|
&approvalAction,
|
||||||
|
actorID,
|
||||||
|
nil); err != nil {
|
||||||
|
if !errors.Is(err, gorm.ErrDuplicatedKey) {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to create approval")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
if fiberErr, ok := err.(*fiber.Error); ok {
|
||||||
|
return nil, fiberErr
|
||||||
|
}
|
||||||
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to create chickins")
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]entity.ProjectChickin, 0, len(newChikins))
|
||||||
|
for _, chickin := range newChikins {
|
||||||
|
loaded, err := s.GetOne(c, chickin.Id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("Failed to reload chickin %d with relations: %v", chickin.Id, err))
|
||||||
|
}
|
||||||
|
result = append(result, *loaded)
|
||||||
|
}
|
||||||
|
if len(result) == 0 {
|
||||||
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to load created chickins")
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s chickinService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uint) (*entity.ProjectChickin, error) {
|
func (s chickinService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uint) (*entity.ProjectChickin, error) {
|
||||||
@@ -222,7 +279,7 @@ func (s chickinService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uint)
|
|||||||
updateBody["chick_in_date"] = req.ChickInDate
|
updateBody["chick_in_date"] = req.ChickInDate
|
||||||
}
|
}
|
||||||
if req.Note != "" {
|
if req.Note != "" {
|
||||||
updateBody["note"] = req.Note
|
updateBody["notes"] = req.Note
|
||||||
}
|
}
|
||||||
if len(updateBody) == 0 {
|
if len(updateBody) == 0 {
|
||||||
return s.GetOne(c, id)
|
return s.GetOne(c, id)
|
||||||
@@ -240,174 +297,335 @@ func (s chickinService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uint)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s chickinService) DeleteOne(c *fiber.Ctx, id uint) error {
|
func (s chickinService) DeleteOne(c *fiber.Ctx, id uint) error {
|
||||||
db := s.Repository.DB()
|
|
||||||
|
|
||||||
tx := db.WithContext(c.Context()).Begin()
|
if err := s.Repository.DeleteOne(c.Context(), id); err != nil {
|
||||||
if tx.Error != nil {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
s.Log.Errorf("Failed to begin transaction: %+v", tx.Error)
|
return fiber.NewError(fiber.StatusNotFound, "Chickin not found")
|
||||||
return tx.Error
|
|
||||||
}
|
|
||||||
rollback := func(err error) error {
|
|
||||||
if rerr := tx.Rollback().Error; rerr != nil {
|
|
||||||
s.Log.Errorf("Rollback failed: %+v", rerr)
|
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
chickinRepoTx := s.Repository.WithTx(tx)
|
return nil
|
||||||
pfkRepoTx := s.ProjectflockKandangRepo.WithTx(tx)
|
}
|
||||||
productWarehouseRepoTx := s.ProductWarehouseRepo.WithTx(tx)
|
|
||||||
|
|
||||||
chickin, err := chickinRepoTx.GetByID(c.Context(), id, nil)
|
func (s chickinService) calculateAvailableQuantity(ctx *fiber.Ctx, projectFlockKandangID uint, productWarehouse *entity.ProductWarehouse, category string) (float64, error) {
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
availableQty := productWarehouse.Quantity
|
||||||
return rollback(fiber.NewError(fiber.StatusNotFound, "Chickin not found"))
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
s.Log.Errorf("Failed get chickin by id: %+v", err)
|
|
||||||
return rollback(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var population entity.ProjectFlockPopulation
|
if category == string(utils.ProjectFlockCategoryGrowing) {
|
||||||
if err := tx.WithContext(c.Context()).Where("project_flock_kandang_id = ?", chickin.ProjectFlockKandangId).First(&population).Error; err != nil {
|
var totalPendingQty float64
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
||||||
return rollback(fiber.NewError(fiber.StatusNotFound, "Project flock population not found"))
|
|
||||||
}
|
|
||||||
s.Log.Errorf("Failed to get project flock population: %+v", err)
|
|
||||||
return rollback(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
newReserved := population.ReservedQuantity - chickin.Quantity
|
chickins, err := s.Repository.GetByProjectFlockKandangID(ctx.Context(), projectFlockKandangID)
|
||||||
if newReserved < 0 {
|
if err == nil {
|
||||||
newReserved = 0
|
for _, chickin := range chickins {
|
||||||
}
|
|
||||||
if err := tx.WithContext(c.Context()).Model(&entity.ProjectFlockPopulation{}).Where("id = ?", population.Id).Updates(map[string]any{"reserved_quantity": newReserved}).Error; err != nil {
|
|
||||||
s.Log.Errorf("Failed to update project flock population: %+v", err)
|
|
||||||
return rollback(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
restoreFromDetails := func() (bool, error) {
|
if chickin.ProductWarehouseId == productWarehouse.Id && chickin.DeletedAt.Time.IsZero() && chickin.PendingUsageQty > 0 {
|
||||||
var details []entity.ProjectChickinDetail
|
totalPendingQty += chickin.PendingUsageQty
|
||||||
if err := tx.WithContext(c.Context()).Where("project_chickin_id = ?", chickin.Id).Find(&details).Error; err != nil {
|
}
|
||||||
return false, err
|
}
|
||||||
}
|
|
||||||
if len(details) == 0 {
|
|
||||||
return false, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, d := range details {
|
availableQty = productWarehouse.Quantity - totalPendingQty
|
||||||
var pw entity.ProductWarehouse
|
if availableQty < 0 {
|
||||||
if err := tx.WithContext(c.Context()).Where("id = ?", d.ProductWarehouseId).First(&pw).Error; err != nil {
|
availableQty = 0
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
}
|
||||||
|
} else if category == string(utils.ProjectFlockCategoryLaying) {
|
||||||
|
var totalPopulation float64
|
||||||
|
var totalPendingQty float64
|
||||||
|
|
||||||
|
populations, err := s.ProjectflockPopulationRepo.GetByProjectFlockKandangIDAndProductWarehouseID(ctx.Context(), projectFlockKandangID, productWarehouse.Id)
|
||||||
|
if err == nil {
|
||||||
|
for _, pop := range populations {
|
||||||
|
totalPopulation += pop.TotalQty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chickins, err := s.Repository.GetByProjectFlockKandangID(ctx.Context(), projectFlockKandangID)
|
||||||
|
if err == nil {
|
||||||
|
for _, chickin := range chickins {
|
||||||
|
|
||||||
|
if chickin.ProductWarehouseId == productWarehouse.Id && chickin.DeletedAt.Time.IsZero() && chickin.PendingUsageQty > 0 {
|
||||||
|
totalPendingQty += chickin.PendingUsageQty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
availableQty = productWarehouse.Quantity - totalPopulation - totalPendingQty
|
||||||
|
if availableQty < 0 {
|
||||||
|
availableQty = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return availableQty, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s chickinService) Approval(c *fiber.Ctx, req *validation.Approve) ([]entity.ProjectChickin, error) {
|
||||||
|
if err := s.Validate.Struct(req); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
approvalSvc := commonSvc.NewApprovalService(commonRepo.NewApprovalRepository(s.Repository.DB()))
|
||||||
|
|
||||||
|
var action entity.ApprovalAction
|
||||||
|
switch strings.ToUpper(strings.TrimSpace(req.Action)) {
|
||||||
|
case string(entity.ApprovalActionRejected):
|
||||||
|
action = entity.ApprovalActionRejected
|
||||||
|
case string(entity.ApprovalActionApproved):
|
||||||
|
action = entity.ApprovalActionApproved
|
||||||
|
default:
|
||||||
|
return nil, fiber.NewError(fiber.StatusBadRequest, "action must be APPROVED or REJECTED")
|
||||||
|
}
|
||||||
|
|
||||||
|
approvableIDs := utils.UniqueUintSlice(req.ApprovableIds)
|
||||||
|
if len(approvableIDs) == 0 {
|
||||||
|
return nil, fiber.NewError(fiber.StatusBadRequest, "approvable_ids must contain at least one id")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, id := range approvableIDs {
|
||||||
|
idCopy := id
|
||||||
|
if err := commonSvc.EnsureRelations(c.Context(), commonSvc.RelationCheck{Name: "ProjectFlockKandang", ID: &idCopy, Exists: s.ProjectflockKandangRepo.IdExists}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
latestApproval, err := approvalSvc.LatestByTarget(c.Context(), utils.ApprovalWorkflowProjectFlockKandang, id, nil)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to check approval status")
|
||||||
|
}
|
||||||
|
if latestApproval == nil {
|
||||||
|
return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("No approval found for ProjectFlockKandang %d - chickins must be created first", id))
|
||||||
|
}
|
||||||
|
if latestApproval.StepNumber != uint16(utils.ProjectFlockKandangStepPengajuan) {
|
||||||
|
return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("ProjectFlockKandang %d cannot be approved - current status is not in PENGAJUAN stage", id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
step := utils.ProjectFlockKandangStepPengajuan
|
||||||
|
if action == entity.ApprovalActionApproved {
|
||||||
|
step = utils.ProjectFlockKandangStepDisetujui
|
||||||
|
}
|
||||||
|
|
||||||
|
err := s.Repository.DB().WithContext(c.Context()).Transaction(func(dbTransaction *gorm.DB) error {
|
||||||
|
|
||||||
|
approvalSvc := commonSvc.NewApprovalService(commonRepo.NewApprovalRepository(dbTransaction))
|
||||||
|
chickinRepoTx := repository.NewChickinRepository(dbTransaction)
|
||||||
|
productWarehouseTx := s.ProductWarehouseRepo.WithTx(dbTransaction)
|
||||||
|
|
||||||
|
for _, approvableID := range approvableIDs {
|
||||||
|
actorID := uint(1) // todo nanti ambil dari auth context
|
||||||
|
if _, err := approvalSvc.CreateApproval(
|
||||||
|
c.Context(),
|
||||||
|
utils.ApprovalWorkflowProjectFlockKandang,
|
||||||
|
approvableID,
|
||||||
|
step,
|
||||||
|
&action,
|
||||||
|
actorID,
|
||||||
|
req.Notes,
|
||||||
|
); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to create approval")
|
||||||
|
}
|
||||||
|
|
||||||
|
if action == entity.ApprovalActionApproved {
|
||||||
|
chickins, err := chickinRepoTx.GetByProjectFlockKandangID(c.Context(), approvableID)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("Failed to get chickins for approval %d", approvableID))
|
||||||
|
}
|
||||||
|
|
||||||
|
kandangForApproval, err := s.ProjectflockKandangRepo.GetByID(c.Context(), approvableID)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, fmt.Sprintf("ProjectFlockKandang %d not found", approvableID))
|
||||||
|
}
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get ProjectFlockKandang")
|
||||||
|
}
|
||||||
|
|
||||||
|
category := strings.ToUpper(strings.TrimSpace(kandangForApproval.ProjectFlock.Category))
|
||||||
|
|
||||||
|
if category == string(utils.ProjectFlockCategoryGrowing) {
|
||||||
|
warehouse, err := s.WarehouseRepo.GetByKandangID(c.Context(), kandangForApproval.KandangId)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, fmt.Sprintf("Warehouse for kandang %d not found", kandangForApproval.KandangId))
|
||||||
|
}
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get warehouse")
|
||||||
|
}
|
||||||
|
|
||||||
|
targetPW, err := s.getOrCreateProductWarehouse(c, warehouse.Id, "PULLET", dbTransaction, actorID)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get/create PULLET product warehouse")
|
||||||
|
}
|
||||||
|
if err := s.convertChickinsToTarget(c, chickins, targetPW, dbTransaction, actorID); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to convert chickins to target")
|
||||||
|
}
|
||||||
|
} else if category == string(utils.ProjectFlockCategoryLaying) {
|
||||||
|
warehouse, err := s.WarehouseRepo.GetByKandangID(c.Context(), kandangForApproval.KandangId)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, fmt.Sprintf("Warehouse for kandang %d not found", kandangForApproval.KandangId))
|
||||||
|
}
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get warehouse")
|
||||||
|
}
|
||||||
|
|
||||||
|
targetPW, err := s.getOrCreateProductWarehouse(c, warehouse.Id, "PULLET", dbTransaction, actorID)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get/create PULLET product warehouse")
|
||||||
|
}
|
||||||
|
if err := s.convertChickinsToTarget(c, chickins, targetPW, dbTransaction, actorID); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to convert chickins to target")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if action == entity.ApprovalActionRejected {
|
||||||
|
chickins, err := chickinRepoTx.GetPendingByProjectFlockKandangID(c.Context(), approvableID)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("Failed to get pending chickins for rejection %d", approvableID))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(chickins) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
updatedQuantity := pw.Quantity + d.Quantity
|
kandangForRejection, err := s.ProjectflockKandangRepo.GetByID(c.Context(), approvableID)
|
||||||
if err := productWarehouseRepoTx.PatchOne(c.Context(), pw.Id, map[string]any{"quantity": updatedQuantity}, nil); err != nil {
|
if err != nil {
|
||||||
return false, err
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, fmt.Sprintf("ProjectFlockKandang %d not found", approvableID))
|
||||||
|
}
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get ProjectFlockKandang")
|
||||||
|
}
|
||||||
|
|
||||||
|
categoryForRejection := strings.ToUpper(strings.TrimSpace(kandangForRejection.ProjectFlock.Category))
|
||||||
|
|
||||||
|
for _, chickin := range chickins {
|
||||||
|
|
||||||
|
if categoryForRejection == string(utils.ProjectFlockCategoryGrowing) {
|
||||||
|
updates := map[string]any{"quantity": gorm.Expr("quantity + ?", chickin.PendingUsageQty)}
|
||||||
|
|
||||||
|
if err := productWarehouseTx.PatchOne(c.Context(), chickin.ProductWarehouseId, updates, nil); err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, fmt.Sprintf("Product warehouse %d not found during rejection", chickin.ProductWarehouseId))
|
||||||
|
}
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("Failed to restore product warehouse quantity for chickin %d", chickin.Id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := chickinRepoTx.DeleteOne(c.Context(), chickin.Id); err != nil {
|
||||||
|
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("Failed to delete rejected chickin %d", chickin.Id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
if err := tx.WithContext(c.Context()).Where("project_chickin_id = ?", chickin.Id).Delete(&entity.ProjectChickinDetail{}).Error; err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
restored, err := restoreFromDetails()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.Log.Errorf("Failed to restore from chickin details: %+v", err)
|
if fiberErr, ok := err.(*fiber.Error); ok {
|
||||||
return rollback(err)
|
return nil, fiberErr
|
||||||
|
}
|
||||||
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to record approval")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !restored {
|
updated := make([]entity.ProjectChickin, 0)
|
||||||
|
for _, kandangID := range approvableIDs {
|
||||||
|
var chickins []entity.ProjectChickin
|
||||||
|
if err := s.Repository.DB().WithContext(c.Context()).Where("project_flock_kandang_id = ?", kandangID).Find(&chickins).Error; err != nil {
|
||||||
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to load approved chickins")
|
||||||
|
}
|
||||||
|
updated = append(updated, chickins...)
|
||||||
|
}
|
||||||
|
|
||||||
projectflockkandang, err := pfkRepoTx.GetByID(c.Context(), population.ProjectFlockKandangId)
|
return updated, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *chickinService) getOrCreateProductWarehouse(ctx *fiber.Ctx, warehouseId uint, categoryCode string, dbTransaction *gorm.DB, actorID uint) (*entity.ProductWarehouse, error) {
|
||||||
|
|
||||||
|
products, err := s.ProductWarehouseRepo.GetByFlagAndWarehouseID(ctx.Context(), categoryCode, warehouseId)
|
||||||
|
if err == nil && len(products) > 0 {
|
||||||
|
return &products[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
product, err := s.ProductWarehouseRepo.GetFirstProductByFlag(ctx.Context(), categoryCode)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get %s product: %w", categoryCode, err)
|
||||||
|
}
|
||||||
|
if product == nil {
|
||||||
|
return nil, fmt.Errorf("no %s product found in system", categoryCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
newPW := &entity.ProductWarehouse{
|
||||||
|
ProductId: product.Id,
|
||||||
|
WarehouseId: warehouseId,
|
||||||
|
Quantity: 0,
|
||||||
|
CreatedBy: actorID,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.ProductWarehouseRepo.WithTx(dbTransaction).CreateOne(ctx.Context(), newPW, nil); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create %s product warehouse: %w", categoryCode, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return newPW, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *chickinService) convertChickinsToTarget(ctx *fiber.Ctx, chickins []entity.ProjectChickin, targetPW *entity.ProductWarehouse, dbTransaction *gorm.DB, actorID uint) error {
|
||||||
|
|
||||||
|
if targetPW == nil || targetPW.Id == 0 {
|
||||||
|
return fmt.Errorf("invalid target product warehouse")
|
||||||
|
}
|
||||||
|
|
||||||
|
chickinRepoTx := repository.NewChickinRepository(dbTransaction)
|
||||||
|
productWarehouseTx := s.ProductWarehouseRepo.WithTx(dbTransaction)
|
||||||
|
ProjectFlockPopulationRepotx := s.ProjectflockPopulationRepo.WithTx(dbTransaction)
|
||||||
|
|
||||||
|
for _, chickin := range chickins {
|
||||||
|
|
||||||
|
populationExists, err := ProjectFlockPopulationRepotx.ExistsByProjectChickinID(ctx.Context(), chickin.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.Log.Errorf("Failed to get projectflock kandang: %+v", err)
|
return fmt.Errorf("failed to check population existence for chickin %d: %w", chickin.Id, err)
|
||||||
return rollback(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var warehouse entity.Warehouse
|
if populationExists {
|
||||||
if err := tx.WithContext(c.Context()).Where("kandang_id = ?", projectflockkandang.KandangId).First(&warehouse).Error; err != nil {
|
s.Log.Infof("population already exists for chickin %d, skipping", chickin.Id)
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
continue
|
||||||
return rollback(fiber.NewError(fiber.StatusNotFound, "Warehouse not found for kandang"))
|
}
|
||||||
|
|
||||||
|
quantityToConvert := chickin.PendingUsageQty
|
||||||
|
|
||||||
|
if err := chickinRepoTx.PatchOne(ctx.Context(), chickin.Id, map[string]any{
|
||||||
|
"usage_qty": quantityToConvert,
|
||||||
|
"pending_usage_qty": 0,
|
||||||
|
}, nil); err != nil {
|
||||||
|
return fmt.Errorf("failed to update chickin %d qty: %w", chickin.Id, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if chickin.ProductWarehouseId != targetPW.Id {
|
||||||
|
if err := productWarehouseTx.PatchOne(ctx.Context(), chickin.ProductWarehouseId, map[string]any{
|
||||||
|
"quantity": gorm.Expr("quantity - ?", quantityToConvert),
|
||||||
|
}, nil); err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, fmt.Sprintf("Source product warehouse %d not found", chickin.ProductWarehouseId))
|
||||||
|
}
|
||||||
|
return fmt.Errorf("failed to deduct source warehouse quantity for chickin %d: %w", chickin.Id, err)
|
||||||
}
|
}
|
||||||
s.Log.Errorf("Failed to get warehouse: %+v", err)
|
|
||||||
return rollback(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
productWarehouse, err := s.ProductWarehouseRepo.GetLatestByCategoryCodeAndWarehouseID(
|
if err := productWarehouseTx.PatchOne(ctx.Context(), targetPW.Id, map[string]any{
|
||||||
c.Context(),
|
"quantity": gorm.Expr("quantity + ?", quantityToConvert),
|
||||||
"DOC",
|
}, nil); err != nil {
|
||||||
warehouse.Id,
|
|
||||||
tx,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return rollback(fiber.NewError(fiber.StatusNotFound, "Product Warehouse not found for the given Project Flock and Warehouse"))
|
return fiber.NewError(fiber.StatusNotFound, fmt.Sprintf("Target product warehouse %d not found", targetPW.Id))
|
||||||
}
|
}
|
||||||
s.Log.Errorf("Failed to get product warehouse: %+v", err)
|
return fmt.Errorf("failed to update target warehouse quantity: %w", err)
|
||||||
return rollback(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updatedQuantity := productWarehouse.Quantity + chickin.Quantity
|
population := &entity.ProjectFlockPopulation{
|
||||||
if err := productWarehouseRepoTx.PatchOne(c.Context(), productWarehouse.Id, map[string]any{"quantity": updatedQuantity}, nil); err != nil {
|
ProjectChickinId: chickin.Id,
|
||||||
s.Log.Errorf("Failed to update product warehouse quantity: %+v", err)
|
ProductWarehouseId: targetPW.Id,
|
||||||
return rollback(err)
|
TotalQty: quantityToConvert,
|
||||||
|
TotalUsedQty: 0,
|
||||||
|
Notes: chickin.Notes,
|
||||||
|
CreatedBy: actorID,
|
||||||
}
|
}
|
||||||
}
|
if err := ProjectFlockPopulationRepotx.CreateOne(ctx.Context(), population, nil); err != nil {
|
||||||
|
return err
|
||||||
// delete chickin (single place)
|
|
||||||
if err := chickinRepoTx.DeleteOne(c.Context(), id); err != nil {
|
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
||||||
return rollback(fiber.NewError(fiber.StatusNotFound, "Chickin not found"))
|
|
||||||
}
|
}
|
||||||
s.Log.Errorf("Failed to delete chickin: %+v", err)
|
|
||||||
return rollback(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := tx.Commit().Error; err != nil {
|
|
||||||
s.Log.Errorf("Failed to commit transaction: %+v", err)
|
|
||||||
return rollback(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *chickinService) Approve(c *fiber.Ctx, id uint) error {
|
|
||||||
|
|
||||||
// todo: ini contoh akhir jika sudah approved
|
|
||||||
|
|
||||||
chickin, err := s.Repository.GetByID(
|
|
||||||
c.Context(),
|
|
||||||
id,
|
|
||||||
nil,
|
|
||||||
)
|
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
||||||
return fiber.NewError(fiber.StatusNotFound, "Chickin not found")
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
s.Log.Errorf("Failed get chickin by id: %+v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
population, err := s.ProjectflockPopulationRepo.GetByProjectFlockKandangID(c.Context(), chickin.ProjectFlockKandangId)
|
|
||||||
if err != nil {
|
|
||||||
s.Log.Errorf("Failed to get project flock population: %+v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = s.ProjectflockPopulationRepo.PatchOne(c.Context(), population.Id, map[string]any{
|
|
||||||
"reserved_quantity": population.ReservedQuantity - chickin.Quantity,
|
|
||||||
"initial_quantity": population.InitialQuantity + chickin.Quantity,
|
|
||||||
"current_quantity": population.CurrentQuantity + chickin.Quantity,
|
|
||||||
}, nil)
|
|
||||||
if err != nil {
|
|
||||||
s.Log.Errorf("Failed to update project flock population: %+v", err)
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package validation
|
|||||||
type Create struct {
|
type Create struct {
|
||||||
ProjectFlockKandangId uint `json:"project_flock_kandang_id" validate:"required,number,min=1"`
|
ProjectFlockKandangId uint `json:"project_flock_kandang_id" validate:"required,number,min=1"`
|
||||||
ChickInDate string `json:"chick_in_date" validate:"required,datetime=2006-01-02"`
|
ChickInDate string `json:"chick_in_date" validate:"required,datetime=2006-01-02"`
|
||||||
Note string `json:"note" validate:"omitempty`
|
Note string `json:"note" validate:"omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Update struct {
|
type Update struct {
|
||||||
@@ -16,3 +16,9 @@ type Query struct {
|
|||||||
Limit int `query:"limit" validate:"omitempty,number,min=1,max=100"`
|
Limit int `query:"limit" validate:"omitempty,number,min=1,max=100"`
|
||||||
ProjectFlockKandangId uint `query:"project_flock_kandang_id" validate:"omitempty,number,min=1"`
|
ProjectFlockKandangId uint `query:"project_flock_kandang_id" validate:"omitempty,number,min=1"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Approve struct {
|
||||||
|
Action string `json:"action" validate:"required_strict"`
|
||||||
|
ApprovableIds []uint `json:"approvable_ids" validate:"required_strict,min=1,dive,gt=0"`
|
||||||
|
Notes *string `json:"notes,omitempty" validate:"omitempty,max=500"`
|
||||||
|
}
|
||||||
|
|||||||
+76
@@ -0,0 +1,76 @@
|
|||||||
|
package controller
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"gitlab.com/mbugroup/lti-api.git/internal/modules/production/project-flock-kandangs/dto"
|
||||||
|
service "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project-flock-kandangs/services"
|
||||||
|
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project-flock-kandangs/validations"
|
||||||
|
"gitlab.com/mbugroup/lti-api.git/internal/response"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ProjectFlockKandangController struct {
|
||||||
|
ProjectFlockKandangService service.ProjectFlockKandangService
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewProjectFlockKandangController(projectFlockKandangService service.ProjectFlockKandangService) *ProjectFlockKandangController {
|
||||||
|
return &ProjectFlockKandangController{
|
||||||
|
ProjectFlockKandangService: projectFlockKandangService,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *ProjectFlockKandangController) GetAll(c *fiber.Ctx) error {
|
||||||
|
query := &validation.Query{
|
||||||
|
Page: c.QueryInt("page", 1),
|
||||||
|
Limit: c.QueryInt("limit", 10),
|
||||||
|
Search: c.Query("search", ""),
|
||||||
|
}
|
||||||
|
|
||||||
|
if query.Page < 1 || query.Limit < 1 {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "page and limit must be greater than 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
result, totalResults, err := u.ProjectFlockKandangService.GetAll(c, query)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Status(fiber.StatusOK).
|
||||||
|
JSON(response.SuccessWithPaginate[dto.ProjectFlockKandangListDTO]{
|
||||||
|
Code: fiber.StatusOK,
|
||||||
|
Status: "success",
|
||||||
|
Message: "Get all projectFlockKandangs successfully",
|
||||||
|
Meta: response.Meta{
|
||||||
|
Page: query.Page,
|
||||||
|
Limit: query.Limit,
|
||||||
|
TotalPages: int64(math.Ceil(float64(totalResults) / float64(query.Limit))),
|
||||||
|
TotalResults: totalResults,
|
||||||
|
},
|
||||||
|
Data: dto.ToProjectFlockKandangListDTOs(result),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *ProjectFlockKandangController) GetOne(c *fiber.Ctx) error {
|
||||||
|
param := c.Params("id")
|
||||||
|
|
||||||
|
id, err := strconv.Atoi(param)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "Invalid Id")
|
||||||
|
}
|
||||||
|
|
||||||
|
result, availableQtys, err := u.ProjectFlockKandangService.GetOne(c, uint(id))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Status(fiber.StatusOK).
|
||||||
|
JSON(response.Success{
|
||||||
|
Code: fiber.StatusOK,
|
||||||
|
Status: "success",
|
||||||
|
Message: "Get projectFlockKandang successfully",
|
||||||
|
Data: dto.ToProjectFlockKandangListDTOWithAvailableQty(*result, availableQtys),
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,330 @@
|
|||||||
|
package dto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||||
|
approvalDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/approvals/dto"
|
||||||
|
areaDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/areas/dto"
|
||||||
|
fcrDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/fcrs/dto"
|
||||||
|
flockDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/flocks/dto"
|
||||||
|
locationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/locations/dto"
|
||||||
|
productDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/products/dto"
|
||||||
|
warehouseDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/dto"
|
||||||
|
chickinDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/production/chickins/dto"
|
||||||
|
projectFlockDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/dto"
|
||||||
|
userDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/users/dto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// === DTO Structs (ordered) ===
|
||||||
|
|
||||||
|
type ProjectFlockKandangBaseDTO struct {
|
||||||
|
Id uint `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProjectFlockDTO struct {
|
||||||
|
Id uint `json:"id"`
|
||||||
|
Period int `json:"period"`
|
||||||
|
Flock *flockDTO.FlockBaseDTO `json:"flock,omitempty"`
|
||||||
|
Area *areaDTO.AreaBaseDTO `json:"area,omitempty"`
|
||||||
|
Category string `json:"category"`
|
||||||
|
Fcr *fcrDTO.FcrBaseDTO `json:"fcr,omitempty"`
|
||||||
|
Location *locationDTO.LocationBaseDTO `json:"location,omitempty"`
|
||||||
|
CreatedUser *userDTO.UserBaseDTO `json:"created_user,omitempty"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type KandangDTO struct {
|
||||||
|
Id uint `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProductWarehouseDTO struct {
|
||||||
|
Id uint `json:"id"`
|
||||||
|
Product *productDTO.ProductBaseDTO `json:"product,omitempty"`
|
||||||
|
Warehouse *warehouseDTO.WarehouseBaseDTO `json:"warehouse,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AvailableQtyDTO struct {
|
||||||
|
AvailableQty float64 `json:"available_qty"`
|
||||||
|
ProductWarehouse *ProductWarehouseDTO `json:"product_warehouse,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProjectFlockKandangListDTO struct {
|
||||||
|
ProjectFlockKandangBaseDTO
|
||||||
|
ProjectFlock *ProjectFlockDTO `json:"project_flock,omitempty"`
|
||||||
|
Kandang *KandangDTO `json:"kandang,omitempty"`
|
||||||
|
Chickins []chickinDTO.ChickinBaseDTO `json:"chickins,omitempty"`
|
||||||
|
AvailableQtys []AvailableQtyDTO `json:"available_qtys,omitempty"`
|
||||||
|
CreatedUser *userDTO.UserBaseDTO `json:"created_user,omitempty"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
Approval *approvalDTO.ApprovalBaseDTO `json:"approval,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProjectFlockKandangDetailDTO struct {
|
||||||
|
ProjectFlockKandangListDTO
|
||||||
|
}
|
||||||
|
|
||||||
|
// === Mapper Functions (ordered) ===
|
||||||
|
|
||||||
|
func ToProjectFlockKandangBaseDTO(e entity.ProjectFlockKandang) ProjectFlockKandangBaseDTO {
|
||||||
|
return ProjectFlockKandangBaseDTO{
|
||||||
|
Id: e.Id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func toProjectFlockDTO(pf *projectFlockDTO.ProjectFlockListDTO) *ProjectFlockDTO {
|
||||||
|
if pf == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ProjectFlockDTO{
|
||||||
|
Id: pf.Id,
|
||||||
|
Period: pf.Period,
|
||||||
|
Area: pf.Area,
|
||||||
|
Category: pf.Category,
|
||||||
|
Fcr: pf.Fcr,
|
||||||
|
Location: pf.Location,
|
||||||
|
CreatedUser: pf.CreatedUser,
|
||||||
|
CreatedAt: pf.CreatedAt,
|
||||||
|
UpdatedAt: pf.UpdatedAt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToProjectFlockKandangListDTOWithAvailableQty(e entity.ProjectFlockKandang, availableQtysRaw []map[string]interface{}) ProjectFlockKandangListDTO {
|
||||||
|
var projectFlockSummary *projectFlockDTO.ProjectFlockListDTO
|
||||||
|
if e.ProjectFlock.Id != 0 {
|
||||||
|
mapped := projectFlockDTO.ToProjectFlockListDTO(e.ProjectFlock)
|
||||||
|
projectFlockSummary = &mapped
|
||||||
|
}
|
||||||
|
|
||||||
|
return ProjectFlockKandangListDTO{
|
||||||
|
ProjectFlockKandangBaseDTO: ToProjectFlockKandangBaseDTO(e),
|
||||||
|
ProjectFlock: toProjectFlockDTO(projectFlockSummary),
|
||||||
|
Kandang: toKandangDTO(e.Kandang),
|
||||||
|
Chickins: toChickinDTOs(e.Chickins),
|
||||||
|
AvailableQtys: toAvailableQtyDTOsFromRaw(availableQtysRaw),
|
||||||
|
CreatedAt: e.CreatedAt,
|
||||||
|
CreatedUser: toCreatedUserDTO(e.ProjectFlock),
|
||||||
|
Approval: toApprovalDTO(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func toKandangDTO(kandang entity.Kandang) *KandangDTO {
|
||||||
|
if kandang.Id == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &KandangDTO{
|
||||||
|
Id: kandang.Id,
|
||||||
|
Name: kandang.Name,
|
||||||
|
Status: kandang.Status,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func toApprovalDTO(e entity.ProjectFlockKandang) *approvalDTO.ApprovalBaseDTO {
|
||||||
|
if e.LatestApproval != nil {
|
||||||
|
mapped := approvalDTO.ToApprovalDTO(*e.LatestApproval)
|
||||||
|
return &mapped
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToProjectFlockKandangListDTO(e entity.ProjectFlockKandang) ProjectFlockKandangListDTO {
|
||||||
|
var projectFlockSummary *projectFlockDTO.ProjectFlockListDTO
|
||||||
|
if e.ProjectFlock.Id != 0 {
|
||||||
|
mapped := projectFlockDTO.ToProjectFlockListDTO(e.ProjectFlock)
|
||||||
|
projectFlockSummary = &mapped
|
||||||
|
}
|
||||||
|
|
||||||
|
return ProjectFlockKandangListDTO{
|
||||||
|
ProjectFlockKandangBaseDTO: ToProjectFlockKandangBaseDTO(e),
|
||||||
|
ProjectFlock: toProjectFlockDTO(projectFlockSummary),
|
||||||
|
Kandang: toKandangDTO(e.Kandang),
|
||||||
|
Chickins: toChickinDTOs(e.Chickins),
|
||||||
|
AvailableQtys: toAvailableQtyDTOs(e.Chickins),
|
||||||
|
CreatedAt: e.CreatedAt,
|
||||||
|
CreatedUser: toCreatedUserDTO(e.ProjectFlock),
|
||||||
|
Approval: toApprovalDTO(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToProjectFlockKandangListDTOs(e []entity.ProjectFlockKandang) []ProjectFlockKandangListDTO {
|
||||||
|
result := make([]ProjectFlockKandangListDTO, len(e))
|
||||||
|
for i, r := range e {
|
||||||
|
result[i] = ToProjectFlockKandangListDTO(r)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToProjectFlockKandangDetailDTO(e entity.ProjectFlockKandang) ProjectFlockKandangDetailDTO {
|
||||||
|
return ProjectFlockKandangDetailDTO{
|
||||||
|
ProjectFlockKandangListDTO: ToProjectFlockKandangListDTO(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// === Helper Functions (ordered) ===
|
||||||
|
|
||||||
|
func toProductWarehouseDTO(pwData map[string]interface{}) *ProductWarehouseDTO {
|
||||||
|
if pwData == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
dto := &ProductWarehouseDTO{}
|
||||||
|
|
||||||
|
if id, ok := pwData["id"].(float64); ok {
|
||||||
|
dto.Id = uint(id)
|
||||||
|
} else if id, ok := pwData["id"].(uint); ok {
|
||||||
|
dto.Id = id
|
||||||
|
}
|
||||||
|
|
||||||
|
if pData, ok := pwData["product"].(map[string]interface{}); ok {
|
||||||
|
dto.Product = toProductDTO(pData)
|
||||||
|
}
|
||||||
|
|
||||||
|
if wData, ok := pwData["warehouse"].(map[string]interface{}); ok {
|
||||||
|
dto.Warehouse = toWarehouseDTO(wData)
|
||||||
|
}
|
||||||
|
|
||||||
|
return dto
|
||||||
|
}
|
||||||
|
|
||||||
|
func toProductDTO(pData map[string]interface{}) *productDTO.ProductBaseDTO {
|
||||||
|
if pData == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
product := &productDTO.ProductBaseDTO{}
|
||||||
|
if id, ok := pData["id"].(float64); ok {
|
||||||
|
product.Id = uint(id)
|
||||||
|
} else if id, ok := pData["id"].(uint); ok {
|
||||||
|
product.Id = id
|
||||||
|
}
|
||||||
|
if name, ok := pData["name"].(string); ok {
|
||||||
|
product.Name = name
|
||||||
|
}
|
||||||
|
return product
|
||||||
|
}
|
||||||
|
|
||||||
|
func toWarehouseDTO(wData map[string]interface{}) *warehouseDTO.WarehouseBaseDTO {
|
||||||
|
if wData == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
warehouse := &warehouseDTO.WarehouseBaseDTO{}
|
||||||
|
if id, ok := wData["id"].(float64); ok {
|
||||||
|
warehouse.Id = uint(id)
|
||||||
|
} else if id, ok := wData["id"].(uint); ok {
|
||||||
|
warehouse.Id = id
|
||||||
|
}
|
||||||
|
if name, ok := wData["name"].(string); ok {
|
||||||
|
warehouse.Name = name
|
||||||
|
}
|
||||||
|
if wType, ok := wData["type"].(string); ok {
|
||||||
|
warehouse.Type = wType
|
||||||
|
}
|
||||||
|
return warehouse
|
||||||
|
}
|
||||||
|
|
||||||
|
func toCreatedUserDTO(pf entity.ProjectFlock) *userDTO.UserBaseDTO {
|
||||||
|
if pf.CreatedUser.Id != 0 {
|
||||||
|
mapped := userDTO.ToUserBaseDTO(pf.CreatedUser)
|
||||||
|
return &mapped
|
||||||
|
} else if pf.CreatedBy != 0 {
|
||||||
|
return &userDTO.UserBaseDTO{
|
||||||
|
Id: pf.CreatedBy,
|
||||||
|
IdUser: int64(pf.CreatedBy),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func toChickinDTOs(chickins []entity.ProjectChickin) []chickinDTO.ChickinBaseDTO {
|
||||||
|
if len(chickins) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]chickinDTO.ChickinBaseDTO, len(chickins))
|
||||||
|
for i, ch := range chickins {
|
||||||
|
result[i] = chickinDTO.ToChickinBaseDTO(ch)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func toAvailableQtyDTOs(chickins []entity.ProjectChickin) []AvailableQtyDTO {
|
||||||
|
if len(chickins) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
availableQtyMap := make(map[uint]AvailableQtyDTO)
|
||||||
|
for _, ch := range chickins {
|
||||||
|
if ch.ProductWarehouse == nil || ch.ProductWarehouse.Quantity <= 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, exists := availableQtyMap[ch.ProductWarehouseId]; exists {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
pwDTO := &ProductWarehouseDTO{
|
||||||
|
Id: ch.ProductWarehouse.Id,
|
||||||
|
}
|
||||||
|
|
||||||
|
if ch.ProductWarehouse.Product.Id != 0 {
|
||||||
|
pwDTO.Product = &productDTO.ProductBaseDTO{
|
||||||
|
Id: ch.ProductWarehouse.Product.Id,
|
||||||
|
Name: ch.ProductWarehouse.Product.Name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ch.ProductWarehouse.Warehouse.Id != 0 {
|
||||||
|
pwDTO.Warehouse = &warehouseDTO.WarehouseBaseDTO{
|
||||||
|
Id: ch.ProductWarehouse.Warehouse.Id,
|
||||||
|
Name: ch.ProductWarehouse.Warehouse.Name,
|
||||||
|
Type: ch.ProductWarehouse.Warehouse.Type,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
availableQtyMap[ch.ProductWarehouseId] = AvailableQtyDTO{
|
||||||
|
ProductWarehouse: pwDTO,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(availableQtyMap) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]AvailableQtyDTO, 0, len(availableQtyMap))
|
||||||
|
for _, v := range availableQtyMap {
|
||||||
|
result = append(result, v)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func toAvailableQtyDTOsFromRaw(availableQtysRaw []map[string]interface{}) []AvailableQtyDTO {
|
||||||
|
if len(availableQtysRaw) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]AvailableQtyDTO, len(availableQtysRaw))
|
||||||
|
for i, v := range availableQtysRaw {
|
||||||
|
pwData, ok := v["product_warehouse"].(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
pwDTO := toProductWarehouseDTO(pwData)
|
||||||
|
availableQty := 0.0
|
||||||
|
if qty, ok := v["available_qty"].(float64); ok {
|
||||||
|
availableQty = qty
|
||||||
|
}
|
||||||
|
|
||||||
|
result[i] = AvailableQtyDTO{
|
||||||
|
AvailableQty: availableQty,
|
||||||
|
ProductWarehouse: pwDTO,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package project_flock_kandangs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/go-playground/validator/v10"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
|
||||||
|
sProjectFlockKandang "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project-flock-kandangs/services"
|
||||||
|
rProjectFlockKandang "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/repositories"
|
||||||
|
|
||||||
|
commonRepo "gitlab.com/mbugroup/lti-api.git/internal/common/repository"
|
||||||
|
commonSvc "gitlab.com/mbugroup/lti-api.git/internal/common/service"
|
||||||
|
rProductWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/repositories"
|
||||||
|
rWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/repositories"
|
||||||
|
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||||
|
|
||||||
|
rUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/repositories"
|
||||||
|
sUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ProjectFlockKandangModule struct{}
|
||||||
|
|
||||||
|
func (ProjectFlockKandangModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate *validator.Validate) {
|
||||||
|
projectFlockKandangRepo := rProjectFlockKandang.NewProjectFlockKandangRepository(db)
|
||||||
|
projectFlockPopulationRepo := rProjectFlockKandang.NewProjectFlockPopulationRepository(db)
|
||||||
|
userRepo := rUser.NewUserRepository(db)
|
||||||
|
warehouseRepo := rWarehouse.NewWarehouseRepository(db)
|
||||||
|
productWarehouseRepo := rProductWarehouse.NewProductWarehouseRepository(db)
|
||||||
|
|
||||||
|
approvalRepo := commonRepo.NewApprovalRepository(db)
|
||||||
|
approvalService := commonSvc.NewApprovalService(approvalRepo)
|
||||||
|
// register workflow steps for project flock kandang approvals
|
||||||
|
if err := approvalService.RegisterWorkflowSteps(utils.ApprovalWorkflowProjectFlockKandang, utils.ProjectFlockKandangApprovalSteps); err != nil {
|
||||||
|
panic(fmt.Sprintf("failed to register project flock kandang approval workflow: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
projectFlockKandangService := sProjectFlockKandang.NewProjectFlockKandangService(projectFlockKandangRepo, approvalService, warehouseRepo, productWarehouseRepo, projectFlockPopulationRepo, validate)
|
||||||
|
userService := sUser.NewUserService(userRepo, validate)
|
||||||
|
|
||||||
|
ProjectFlockKandangRoutes(router, userService, projectFlockKandangService)
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package project_flock_kandangs
|
||||||
|
|
||||||
|
import (
|
||||||
|
// m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
||||||
|
controller "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project-flock-kandangs/controllers"
|
||||||
|
projectFlockKandang "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project-flock-kandangs/services"
|
||||||
|
user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ProjectFlockKandangRoutes(v1 fiber.Router, u user.UserService, s projectFlockKandang.ProjectFlockKandangService) {
|
||||||
|
ctrl := controller.NewProjectFlockKandangController(s)
|
||||||
|
|
||||||
|
route := v1.Group("/project-flock-kandangs")
|
||||||
|
|
||||||
|
// route.Get("/", m.Auth(u), ctrl.GetAll)
|
||||||
|
// route.Post("/", m.Auth(u), ctrl.CreateOne)
|
||||||
|
// route.Get("/:id", m.Auth(u), ctrl.GetOne)
|
||||||
|
// route.Patch("/:id", m.Auth(u), ctrl.UpdateOne)
|
||||||
|
// route.Delete("/:id", m.Auth(u), ctrl.DeleteOne)
|
||||||
|
|
||||||
|
route.Get("/", ctrl.GetAll)
|
||||||
|
route.Get("/:id", ctrl.GetOne)
|
||||||
|
|
||||||
|
}
|
||||||
+195
@@ -0,0 +1,195 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
commonSvc "gitlab.com/mbugroup/lti-api.git/internal/common/service"
|
||||||
|
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||||
|
rProductWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/repositories"
|
||||||
|
rWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/repositories"
|
||||||
|
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project-flock-kandangs/validations"
|
||||||
|
repository "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/repositories"
|
||||||
|
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||||
|
|
||||||
|
"github.com/go-playground/validator/v10"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ProjectFlockKandangService interface {
|
||||||
|
GetAll(ctx *fiber.Ctx, params *validation.Query) ([]entity.ProjectFlockKandang, int64, error)
|
||||||
|
GetOne(ctx *fiber.Ctx, id uint) (*entity.ProjectFlockKandang, []map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type projectFlockKandangService struct {
|
||||||
|
Log *logrus.Logger
|
||||||
|
Validate *validator.Validate
|
||||||
|
Repository repository.ProjectFlockKandangRepository
|
||||||
|
ApprovalSvc commonSvc.ApprovalService
|
||||||
|
WarehouseRepo rWarehouse.WarehouseRepository
|
||||||
|
ProductWarehouseRepo rProductWarehouse.ProductWarehouseRepository
|
||||||
|
PopulationRepo repository.ProjectFlockPopulationRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewProjectFlockKandangService(repo repository.ProjectFlockKandangRepository, approvalSvc commonSvc.ApprovalService, warehouseRepo rWarehouse.WarehouseRepository, productWarehouseRepo rProductWarehouse.ProductWarehouseRepository, populationRepo repository.ProjectFlockPopulationRepository, validate *validator.Validate) ProjectFlockKandangService {
|
||||||
|
return &projectFlockKandangService{
|
||||||
|
Log: utils.Log,
|
||||||
|
Validate: validate,
|
||||||
|
Repository: repo,
|
||||||
|
ApprovalSvc: approvalSvc,
|
||||||
|
WarehouseRepo: warehouseRepo,
|
||||||
|
ProductWarehouseRepo: productWarehouseRepo,
|
||||||
|
PopulationRepo: populationRepo,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s projectFlockKandangService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity.ProjectFlockKandang, int64, error) {
|
||||||
|
if err := s.Validate.Struct(params); err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
projectFlockKandangs, err := s.Repository.GetAll(c.Context())
|
||||||
|
if err != nil {
|
||||||
|
s.Log.Errorf("Failed to get projectFlockKandangs: %+v", err)
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
total := int64(len(projectFlockKandangs))
|
||||||
|
|
||||||
|
return projectFlockKandangs, total, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s projectFlockKandangService) GetOne(c *fiber.Ctx, id uint) (*entity.ProjectFlockKandang, []map[string]interface{}, error) {
|
||||||
|
projectFlockKandang, err := s.Repository.GetByID(c.Context(), id)
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, nil, fiber.NewError(fiber.StatusNotFound, "ProjectFlockKandang not found")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
s.Log.Errorf("Failed get projectFlockKandang by id: %+v", err)
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(projectFlockKandang.Chickins) > 0 && s.ApprovalSvc != nil {
|
||||||
|
latest, err := s.ApprovalSvc.LatestByTarget(c.Context(), utils.ApprovalWorkflowProjectFlockKandang, projectFlockKandang.Id, nil)
|
||||||
|
if err != nil {
|
||||||
|
s.Log.Errorf("Failed to fetch latest kandang approval for projectFlockKandang %d: %+v", projectFlockKandang.Id, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if latest != nil {
|
||||||
|
projectFlockKandang.LatestApproval = latest
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
availableQtys, err := s.getAvailableQuantities(c, projectFlockKandang)
|
||||||
|
if err != nil {
|
||||||
|
s.Log.Errorf("Failed to fetch available quantities for kandang %d: %+v", projectFlockKandang.Kandang.Id, err)
|
||||||
|
availableQtys = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return projectFlockKandang, availableQtys, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s projectFlockKandangService) getAvailableQuantities(c *fiber.Ctx, projectFlockKandang *entity.ProjectFlockKandang) ([]map[string]interface{}, error) {
|
||||||
|
if projectFlockKandang.Kandang.Id == 0 || s.WarehouseRepo == nil || s.ProductWarehouseRepo == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
warehouse, err := s.WarehouseRepo.GetByKandangID(c.Context(), projectFlockKandang.Kandang.Id)
|
||||||
|
if err != nil || warehouse == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var productCategoryCode string
|
||||||
|
if projectFlockKandang.ProjectFlock.Category == string(utils.ProjectFlockCategoryGrowing) {
|
||||||
|
productCategoryCode = "DOC"
|
||||||
|
} else if projectFlockKandang.ProjectFlock.Category == string(utils.ProjectFlockCategoryLaying) {
|
||||||
|
productCategoryCode = "PULLET"
|
||||||
|
} else {
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
products, err := s.ProductWarehouseRepo.GetByCategoryCodeAndWarehouseID(c.Context(), productCategoryCode, warehouse.Id)
|
||||||
|
if err != nil || len(products) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var result []map[string]interface{}
|
||||||
|
for _, pw := range products {
|
||||||
|
availableQty, err := s.calculateAvailableQuantityForProductWarehouse(c, projectFlockKandang, &pw)
|
||||||
|
if err != nil {
|
||||||
|
s.Log.Warnf("Failed to calculate available quantity for product warehouse %d: %v", pw.Id, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only include product warehouse if available_qty > 0
|
||||||
|
if availableQty <= 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
productData := map[string]interface{}{
|
||||||
|
"id": pw.Product.Id,
|
||||||
|
"name": pw.Product.Name,
|
||||||
|
}
|
||||||
|
|
||||||
|
warehouseData := map[string]interface{}{
|
||||||
|
"id": pw.Warehouse.Id,
|
||||||
|
"name": pw.Warehouse.Name,
|
||||||
|
"type": pw.Warehouse.Type,
|
||||||
|
}
|
||||||
|
|
||||||
|
productWarehouseData := map[string]interface{}{
|
||||||
|
"id": pw.Id,
|
||||||
|
"product": productData,
|
||||||
|
"warehouse": warehouseData,
|
||||||
|
}
|
||||||
|
|
||||||
|
result = append(result, map[string]interface{}{
|
||||||
|
"available_qty": availableQty,
|
||||||
|
"product_warehouse": productWarehouseData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s projectFlockKandangService) calculateAvailableQuantityForProductWarehouse(c *fiber.Ctx, projectFlockKandang *entity.ProjectFlockKandang, productWarehouse *entity.ProductWarehouse) (float64, error) {
|
||||||
|
availableQty := productWarehouse.Quantity
|
||||||
|
|
||||||
|
if projectFlockKandang.ProjectFlock.Category == string(utils.ProjectFlockCategoryGrowing) {
|
||||||
|
var totalPendingQty float64
|
||||||
|
|
||||||
|
for _, chickin := range projectFlockKandang.Chickins {
|
||||||
|
if chickin.ProductWarehouseId == productWarehouse.Id && chickin.DeletedAt.Time.IsZero() && chickin.PendingUsageQty > 0 {
|
||||||
|
totalPendingQty += chickin.PendingUsageQty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
availableQty = productWarehouse.Quantity - totalPendingQty
|
||||||
|
if availableQty < 0 {
|
||||||
|
availableQty = 0
|
||||||
|
}
|
||||||
|
} else if projectFlockKandang.ProjectFlock.Category == string(utils.ProjectFlockCategoryLaying) {
|
||||||
|
var totalPopulation float64
|
||||||
|
var totalPendingQty float64
|
||||||
|
|
||||||
|
populations, err := s.PopulationRepo.GetByProjectFlockKandangIDAndProductWarehouseID(c.Context(), projectFlockKandang.Id, productWarehouse.Id)
|
||||||
|
if err == nil {
|
||||||
|
for _, pop := range populations {
|
||||||
|
totalPopulation += pop.TotalQty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, chickin := range projectFlockKandang.Chickins {
|
||||||
|
if chickin.ProductWarehouseId == productWarehouse.Id && chickin.DeletedAt.Time.IsZero() && chickin.PendingUsageQty > 0 {
|
||||||
|
totalPendingQty += chickin.PendingUsageQty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
availableQty = productWarehouse.Quantity - totalPopulation - totalPendingQty
|
||||||
|
if availableQty < 0 {
|
||||||
|
availableQty = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return availableQty, nil
|
||||||
|
}
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
package validation
|
||||||
|
|
||||||
|
type Create struct {
|
||||||
|
ProjectFlockId uint `json:"project_flock_id" validate:"required"`
|
||||||
|
KandangId uint `json:"kandang_id" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Update struct {
|
||||||
|
ProjectFlockId *uint `json:"project_flock_id,omitempty" validate:"omitempty"`
|
||||||
|
KandangId *uint `json:"kandang_id,omitempty" validate:"omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Query struct {
|
||||||
|
Page int `query:"page" validate:"omitempty,number,min=1,gt=0"`
|
||||||
|
Limit int `query:"limit" validate:"omitempty,number,min=1,max=100,gt=0"`
|
||||||
|
Search string `query:"search" validate:"omitempty,max=50"`
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
flockDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/flocks/dto"
|
flockDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/flocks/dto"
|
||||||
kandangDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/dto"
|
kandangDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/dto"
|
||||||
locationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/locations/dto"
|
locationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/locations/dto"
|
||||||
|
|
||||||
// pfutils "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/utils"
|
// pfutils "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/utils"
|
||||||
userDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/users/dto"
|
userDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/users/dto"
|
||||||
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||||
@@ -22,6 +23,13 @@ type ProjectFlockBaseDTO struct {
|
|||||||
FlockName string `json:"flock_name"`
|
FlockName string `json:"flock_name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type KandangWithProjectFlockIdDTO struct {
|
||||||
|
Id uint `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
ProjectFlockKandangId uint `json:"project_flock_kandang_id"`
|
||||||
|
}
|
||||||
|
|
||||||
type ProjectFlockListDTO struct {
|
type ProjectFlockListDTO struct {
|
||||||
ProjectFlockBaseDTO
|
ProjectFlockBaseDTO
|
||||||
// Flock *flockDTO.FlockBaseDTO `json:"flock,omitempty"`
|
// Flock *flockDTO.FlockBaseDTO `json:"flock,omitempty"`
|
||||||
@@ -93,15 +101,15 @@ func ToProjectFlockListDTO(e entity.ProjectFlock) ProjectFlockListDTO {
|
|||||||
return ProjectFlockListDTO{
|
return ProjectFlockListDTO{
|
||||||
ProjectFlockBaseDTO: createProjectFlockBaseDTO(e),
|
ProjectFlockBaseDTO: createProjectFlockBaseDTO(e),
|
||||||
// Flock: flockSummary,
|
// Flock: flockSummary,
|
||||||
Area: areaSummary,
|
Area: areaSummary,
|
||||||
Kandangs: kandangSummaries,
|
Kandangs: kandangSummaries,
|
||||||
Category: e.Category,
|
Category: e.Category,
|
||||||
Fcr: fcrSummary,
|
Fcr: fcrSummary,
|
||||||
Location: locationSummary,
|
Location: locationSummary,
|
||||||
CreatedAt: e.CreatedAt,
|
CreatedAt: e.CreatedAt,
|
||||||
UpdatedAt: e.UpdatedAt,
|
UpdatedAt: e.UpdatedAt,
|
||||||
CreatedUser: createdUser,
|
CreatedUser: createdUser,
|
||||||
Approval: latestApproval,
|
Approval: latestApproval,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+65
-7
@@ -9,8 +9,19 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type ProjectFlockPopulationRepository interface {
|
type ProjectFlockPopulationRepository interface {
|
||||||
repository.BaseRepository[entity.ProjectFlockPopulation]
|
// domain-specific
|
||||||
GetByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) (*entity.ProjectFlockPopulation, error)
|
GetByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) ([]entity.ProjectFlockPopulation, error)
|
||||||
|
ExistsByProjectChickinID(ctx context.Context, projectChickinID uint) (bool, error)
|
||||||
|
GetByProjectChickinIDAndProductWarehouseID(ctx context.Context, projectChickinID uint, productWarehouseID uint) ([]entity.ProjectFlockPopulation, error)
|
||||||
|
GetByProjectFlockKandangIDAndProductWarehouseID(ctx context.Context, projectFlockKandangID uint, productWarehouseID uint) ([]entity.ProjectFlockPopulation, error)
|
||||||
|
|
||||||
|
// subset of base repository methods used by services
|
||||||
|
CreateOne(ctx context.Context, entity *entity.ProjectFlockPopulation, modifier func(*gorm.DB) *gorm.DB) error
|
||||||
|
PatchOne(ctx context.Context, id uint, updates map[string]any, modifier func(*gorm.DB) *gorm.DB) error
|
||||||
|
|
||||||
|
// transaction helpers
|
||||||
|
WithTx(tx *gorm.DB) ProjectFlockPopulationRepository
|
||||||
|
DB() *gorm.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
type projectFlockPopulationRepositoryImpl struct {
|
type projectFlockPopulationRepositoryImpl struct {
|
||||||
@@ -23,13 +34,60 @@ func NewProjectFlockPopulationRepository(db *gorm.DB) ProjectFlockPopulationRepo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *projectFlockPopulationRepositoryImpl) GetByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) (*entity.ProjectFlockPopulation, error) {
|
func (r *projectFlockPopulationRepositoryImpl) WithTx(tx *gorm.DB) ProjectFlockPopulationRepository {
|
||||||
var record entity.ProjectFlockPopulation
|
return &projectFlockPopulationRepositoryImpl{
|
||||||
|
BaseRepositoryImpl: repository.NewBaseRepository[entity.ProjectFlockPopulation](tx),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *projectFlockPopulationRepositoryImpl) DB() *gorm.DB {
|
||||||
|
return r.BaseRepositoryImpl.DB()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *projectFlockPopulationRepositoryImpl) GetByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) ([]entity.ProjectFlockPopulation, error) {
|
||||||
|
var records []entity.ProjectFlockPopulation
|
||||||
err := r.DB().WithContext(ctx).
|
err := r.DB().WithContext(ctx).
|
||||||
Where("project_flock_kandang_id = ?", projectFlockKandangID).
|
Joins("JOIN project_chickins ON project_chickins.id = project_flock_populations.project_chickin_id").
|
||||||
First(&record).Error
|
Where("project_chickins.project_flock_kandang_id = ?", projectFlockKandangID).
|
||||||
|
Preload("ProjectChickin").
|
||||||
|
Find(&records).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &record, nil
|
return records, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *projectFlockPopulationRepositoryImpl) ExistsByProjectChickinID(ctx context.Context, projectChickinID uint) (bool, error) {
|
||||||
|
var count int64
|
||||||
|
err := r.DB().WithContext(ctx).
|
||||||
|
Where("project_chickin_id = ?", projectChickinID).
|
||||||
|
Model(&entity.ProjectFlockPopulation{}).
|
||||||
|
Count(&count).Error
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return count > 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *projectFlockPopulationRepositoryImpl) GetByProjectChickinIDAndProductWarehouseID(ctx context.Context, projectChickinID uint, productWarehouseID uint) ([]entity.ProjectFlockPopulation, error) {
|
||||||
|
var records []entity.ProjectFlockPopulation
|
||||||
|
err := r.DB().WithContext(ctx).
|
||||||
|
Where("project_chickin_id = ? AND product_warehouse_id = ?", projectChickinID, productWarehouseID).
|
||||||
|
Find(&records).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return records, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *projectFlockPopulationRepositoryImpl) GetByProjectFlockKandangIDAndProductWarehouseID(ctx context.Context, projectFlockKandangID uint, productWarehouseID uint) ([]entity.ProjectFlockPopulation, error) {
|
||||||
|
var records []entity.ProjectFlockPopulation
|
||||||
|
err := r.DB().WithContext(ctx).
|
||||||
|
Joins("JOIN project_chickins ON project_chickins.id = project_flock_populations.project_chickin_id").
|
||||||
|
Where("project_chickins.project_flock_kandang_id = ? AND project_flock_populations.product_warehouse_id = ?", projectFlockKandangID, productWarehouseID).
|
||||||
|
Find(&records).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return records, nil
|
||||||
}
|
}
|
||||||
|
|||||||
+14
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"gitlab.com/mbugroup/lti-api.git/internal/common/repository"
|
||||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
@@ -19,6 +20,7 @@ type ProjectFlockKandangRepository interface {
|
|||||||
FindKandangsWithRecordings(ctx context.Context, projectFlockID uint, kandangIDs []uint) ([]entity.Kandang, error)
|
FindKandangsWithRecordings(ctx context.Context, projectFlockID uint, kandangIDs []uint) ([]entity.Kandang, error)
|
||||||
MaxPeriodByBaseName(ctx context.Context, baseName string) (int, error)
|
MaxPeriodByBaseName(ctx context.Context, baseName string) (int, error)
|
||||||
WithTx(tx *gorm.DB) ProjectFlockKandangRepository
|
WithTx(tx *gorm.DB) ProjectFlockKandangRepository
|
||||||
|
IdExists(ctx context.Context, id uint) (bool, error)
|
||||||
DB() *gorm.DB
|
DB() *gorm.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,6 +61,9 @@ func (r *projectFlockKandangRepositoryImpl) GetAll(ctx context.Context) ([]entit
|
|||||||
Preload("ProjectFlock.Kandangs").
|
Preload("ProjectFlock.Kandangs").
|
||||||
Preload("ProjectFlock.KandangHistory").
|
Preload("ProjectFlock.KandangHistory").
|
||||||
Preload("Kandang").
|
Preload("Kandang").
|
||||||
|
Preload("Chickins").
|
||||||
|
Preload("Chickins.CreatedUser").
|
||||||
|
Preload("Chickins.ProductWarehouse").
|
||||||
Order("project_flock_id ASC, created_at ASC").
|
Order("project_flock_id ASC, created_at ASC").
|
||||||
Find(&records).Error; err != nil {
|
Find(&records).Error; err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -73,6 +78,9 @@ func (r *projectFlockKandangRepositoryImpl) WithTx(tx *gorm.DB) ProjectFlockKand
|
|||||||
func (r *projectFlockKandangRepositoryImpl) DB() *gorm.DB {
|
func (r *projectFlockKandangRepositoryImpl) DB() *gorm.DB {
|
||||||
return r.db
|
return r.db
|
||||||
}
|
}
|
||||||
|
func (r *projectFlockKandangRepositoryImpl) IdExists(ctx context.Context, id uint) (bool, error) {
|
||||||
|
return repository.Exists[entity.ProjectFlockKandang](ctx, r.db, id)
|
||||||
|
}
|
||||||
|
|
||||||
func (r *projectFlockKandangRepositoryImpl) GetByID(ctx context.Context, id uint) (*entity.ProjectFlockKandang, error) {
|
func (r *projectFlockKandangRepositoryImpl) GetByID(ctx context.Context, id uint) (*entity.ProjectFlockKandang, error) {
|
||||||
record := new(entity.ProjectFlockKandang)
|
record := new(entity.ProjectFlockKandang)
|
||||||
@@ -85,6 +93,9 @@ func (r *projectFlockKandangRepositoryImpl) GetByID(ctx context.Context, id uint
|
|||||||
Preload("ProjectFlock.Kandangs").
|
Preload("ProjectFlock.Kandangs").
|
||||||
Preload("ProjectFlock.KandangHistory").
|
Preload("ProjectFlock.KandangHistory").
|
||||||
Preload("Kandang").
|
Preload("Kandang").
|
||||||
|
Preload("Chickins").
|
||||||
|
Preload("Chickins.CreatedUser").
|
||||||
|
Preload("Chickins.ProductWarehouse").
|
||||||
First(record, id).Error; err != nil {
|
First(record, id).Error; err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -103,6 +114,9 @@ func (r *projectFlockKandangRepositoryImpl) GetByProjectFlockAndKandang(ctx cont
|
|||||||
Preload("ProjectFlock.Kandangs").
|
Preload("ProjectFlock.Kandangs").
|
||||||
Preload("ProjectFlock.KandangHistory").
|
Preload("ProjectFlock.KandangHistory").
|
||||||
Preload("Kandang").
|
Preload("Kandang").
|
||||||
|
Preload("Chickins").
|
||||||
|
Preload("Chickins.CreatedUser").
|
||||||
|
Preload("Chickins.ProductWarehouse").
|
||||||
First(record).Error; err != nil {
|
First(record).Error; err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package project_flocks
|
package project_flocks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
// m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
||||||
controller "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/controllers"
|
controller "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/controllers"
|
||||||
projectflock "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/services"
|
projectflock "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/services"
|
||||||
user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
||||||
@@ -12,8 +12,13 @@ import (
|
|||||||
func ProjectflockRoutes(v1 fiber.Router, u user.UserService, s projectflock.ProjectflockService) {
|
func ProjectflockRoutes(v1 fiber.Router, u user.UserService, s projectflock.ProjectflockService) {
|
||||||
ctrl := controller.NewProjectflockController(s)
|
ctrl := controller.NewProjectflockController(s)
|
||||||
|
|
||||||
route := v1.Group("/project-flocks")
|
route := v1.Group("/project_flocks")
|
||||||
route.Use(m.Auth(u))
|
|
||||||
|
// route.Get("/", m.Auth(u), ctrl.GetAll)
|
||||||
|
// route.Post("/", m.Auth(u), ctrl.CreateOne)
|
||||||
|
// route.Get("/:id", m.Auth(u), ctrl.GetOne)
|
||||||
|
// route.Patch("/:id", m.Auth(u), ctrl.UpdateOne)
|
||||||
|
// route.Delete("/:id", m.Auth(u), ctrl.DeleteOne)
|
||||||
|
|
||||||
route.Get("/", ctrl.GetAll)
|
route.Get("/", ctrl.GetAll)
|
||||||
route.Post("/", ctrl.CreateOne)
|
route.Post("/", ctrl.CreateOne)
|
||||||
@@ -23,5 +28,5 @@ func ProjectflockRoutes(v1 fiber.Router, u user.UserService, s projectflock.Proj
|
|||||||
route.Get("/kandangs/lookup", ctrl.LookupProjectFlockKandang)
|
route.Get("/kandangs/lookup", ctrl.LookupProjectFlockKandang)
|
||||||
route.Post("/approvals", ctrl.Approval)
|
route.Post("/approvals", ctrl.Approval)
|
||||||
route.Get("/kandangs/:project_flock_kandang_id/periods", ctrl.GetFlockPeriodSummary)
|
route.Get("/kandangs/:project_flock_kandang_id/periods", ctrl.GetFlockPeriodSummary)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
commonRepo "gitlab.com/mbugroup/lti-api.git/internal/common/repository"
|
commonRepo "gitlab.com/mbugroup/lti-api.git/internal/common/repository"
|
||||||
commonSvc "gitlab.com/mbugroup/lti-api.git/internal/common/service"
|
commonSvc "gitlab.com/mbugroup/lti-api.git/internal/common/service"
|
||||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||||
authmiddleware "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
|
||||||
productWarehouseRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/repositories"
|
productWarehouseRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/repositories"
|
||||||
flockRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/master/flocks/repositories"
|
flockRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/master/flocks/repositories"
|
||||||
kandangRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/repositories"
|
kandangRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/repositories"
|
||||||
@@ -81,6 +80,18 @@ func NewProjectflockService(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s projectflockService) withRelations(db *gorm.DB) *gorm.DB {
|
||||||
|
return db.
|
||||||
|
Preload("CreatedUser").
|
||||||
|
Preload("Flock").
|
||||||
|
Preload("Area").
|
||||||
|
Preload("Fcr").
|
||||||
|
Preload("Location").
|
||||||
|
Preload("Kandangs").
|
||||||
|
Preload("KandangHistory").
|
||||||
|
Preload("KandangHistory.Kandang")
|
||||||
|
}
|
||||||
|
|
||||||
func (s projectflockService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity.ProjectFlock, int64, error) {
|
func (s projectflockService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity.ProjectFlock, int64, error) {
|
||||||
if err := s.Validate.Struct(params); err != nil {
|
if err := s.Validate.Struct(params); err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
@@ -159,11 +170,6 @@ func (s *projectflockService) CreateOne(c *fiber.Ctx, req *validation.Create) (*
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
actorID, err := actorIDFromContext(c)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
cat := strings.ToUpper(req.Category)
|
cat := strings.ToUpper(req.Category)
|
||||||
if !utils.IsValidProjectFlockCategory(cat) {
|
if !utils.IsValidProjectFlockCategory(cat) {
|
||||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid category")
|
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid category")
|
||||||
@@ -188,7 +194,7 @@ func (s *projectflockService) CreateOne(c *fiber.Ctx, req *validation.Create) (*
|
|||||||
|
|
||||||
canonicalBase := baseName
|
canonicalBase := baseName
|
||||||
if s.FlockRepo != nil {
|
if s.FlockRepo != nil {
|
||||||
baseFlock, err := s.ensureFlockByName(c.Context(), actorID, baseName)
|
baseFlock, err := s.ensureFlockByName(c.Context(), baseName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -218,7 +224,7 @@ func (s *projectflockService) CreateOne(c *fiber.Ctx, req *validation.Create) (*
|
|||||||
Category: cat,
|
Category: cat,
|
||||||
FcrId: req.FcrId,
|
FcrId: req.FcrId,
|
||||||
LocationId: req.LocationId,
|
LocationId: req.LocationId,
|
||||||
CreatedBy: actorID,
|
CreatedBy: 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.Repository.DB().WithContext(c.Context()).Transaction(func(dbTransaction *gorm.DB) error {
|
err = s.Repository.DB().WithContext(c.Context()).Transaction(func(dbTransaction *gorm.DB) error {
|
||||||
@@ -243,6 +249,7 @@ func (s *projectflockService) CreateOne(c *fiber.Ctx, req *validation.Create) (*
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
actorID := uint(1) //TODO: Change From Auth
|
||||||
action := entity.ApprovalActionCreated
|
action := entity.ApprovalActionCreated
|
||||||
approvalSvcTx := commonSvc.NewApprovalService(commonRepo.NewApprovalRepository(dbTransaction))
|
approvalSvcTx := commonSvc.NewApprovalService(commonRepo.NewApprovalRepository(dbTransaction))
|
||||||
_, err = approvalSvcTx.CreateApproval(
|
_, err = approvalSvcTx.CreateApproval(
|
||||||
@@ -276,11 +283,6 @@ func (s projectflockService) UpdateOne(c *fiber.Ctx, req *validation.Update, id
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
actorID, err := actorIDFromContext(c)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
existing, err := s.Repository.GetByID(c.Context(), id, s.Repository.WithDefaultRelations())
|
existing, err := s.Repository.GetByID(c.Context(), id, s.Repository.WithDefaultRelations())
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return nil, fiber.NewError(fiber.StatusNotFound, "Projectflock not found")
|
return nil, fiber.NewError(fiber.StatusNotFound, "Projectflock not found")
|
||||||
@@ -303,7 +305,7 @@ func (s projectflockService) UpdateOne(c *fiber.Ctx, req *validation.Update, id
|
|||||||
}
|
}
|
||||||
canonicalBase := trimmed
|
canonicalBase := trimmed
|
||||||
if s.FlockRepo != nil {
|
if s.FlockRepo != nil {
|
||||||
flockEntity, err := s.ensureFlockByName(c.Context(), actorID, trimmed)
|
flockEntity, err := s.ensureFlockByName(c.Context(), trimmed)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -462,6 +464,7 @@ func (s projectflockService) UpdateOne(c *fiber.Ctx, req *validation.Update, id
|
|||||||
}
|
}
|
||||||
|
|
||||||
if hasChanges {
|
if hasChanges {
|
||||||
|
actorID := uint(1) //TODO: Change From Auth
|
||||||
approvalSvc := commonSvc.NewApprovalService(commonRepo.NewApprovalRepository(dbTransaction))
|
approvalSvc := commonSvc.NewApprovalService(commonRepo.NewApprovalRepository(dbTransaction))
|
||||||
if approvalSvc != nil {
|
if approvalSvc != nil {
|
||||||
latestBeforeReset, err := approvalSvc.LatestByTarget(c.Context(), s.approvalWorkflow, id, nil)
|
latestBeforeReset, err := approvalSvc.LatestByTarget(c.Context(), s.approvalWorkflow, id, nil)
|
||||||
@@ -515,11 +518,7 @@ func (s projectflockService) Approval(c *fiber.Ctx, req *validation.Approve) ([]
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
actorID, err := actorIDFromContext(c)
|
actorID := uint(1) // TODO: change from auth context
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var action entity.ApprovalAction
|
var action entity.ApprovalAction
|
||||||
switch strings.ToUpper(strings.TrimSpace(req.Action)) {
|
switch strings.ToUpper(strings.TrimSpace(req.Action)) {
|
||||||
case string(entity.ApprovalActionRejected):
|
case string(entity.ApprovalActionRejected):
|
||||||
@@ -540,7 +539,7 @@ func (s projectflockService) Approval(c *fiber.Ctx, req *validation.Approve) ([]
|
|||||||
step = utils.ProjectFlockStepAktif
|
step = utils.ProjectFlockStepAktif
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.Repository.DB().WithContext(c.Context()).Transaction(func(dbTransaction *gorm.DB) error {
|
err := s.Repository.DB().WithContext(c.Context()).Transaction(func(dbTransaction *gorm.DB) error {
|
||||||
approvalSvc := commonSvc.NewApprovalService(commonRepo.NewApprovalRepository(dbTransaction))
|
approvalSvc := commonSvc.NewApprovalService(commonRepo.NewApprovalRepository(dbTransaction))
|
||||||
kandangRepoTx := kandangRepository.NewKandangRepository(dbTransaction)
|
kandangRepoTx := kandangRepository.NewKandangRepository(dbTransaction)
|
||||||
projectRepoTx := repository.NewProjectflockRepository(dbTransaction)
|
projectRepoTx := repository.NewProjectflockRepository(dbTransaction)
|
||||||
@@ -827,7 +826,7 @@ func (s projectflockService) generateSequentialFlockName(ctx context.Context, re
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s projectflockService) ensureFlockByName(ctx context.Context, actorID uint, name string) (*entity.Flock, error) {
|
func (s projectflockService) ensureFlockByName(ctx context.Context, name string) (*entity.Flock, error) {
|
||||||
trimmed := strings.TrimSpace(name)
|
trimmed := strings.TrimSpace(name)
|
||||||
if trimmed == "" {
|
if trimmed == "" {
|
||||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Flock name cannot be empty")
|
return nil, fiber.NewError(fiber.StatusBadRequest, "Flock name cannot be empty")
|
||||||
@@ -844,7 +843,7 @@ func (s projectflockService) ensureFlockByName(ctx context.Context, actorID uint
|
|||||||
|
|
||||||
newFlock := &entity.Flock{
|
newFlock := &entity.Flock{
|
||||||
Name: trimmed,
|
Name: trimmed,
|
||||||
CreatedBy: actorID,
|
CreatedBy: 1, // TODO: replace with authenticated user
|
||||||
}
|
}
|
||||||
if err := s.FlockRepo.CreateOne(ctx, newFlock, nil); err != nil {
|
if err := s.FlockRepo.CreateOne(ctx, newFlock, nil); err != nil {
|
||||||
if errors.Is(err, gorm.ErrDuplicatedKey) {
|
if errors.Is(err, gorm.ErrDuplicatedKey) {
|
||||||
@@ -963,11 +962,3 @@ func (s projectflockService) kandangRepoWithTx(tx *gorm.DB) kandangRepository.Ka
|
|||||||
}
|
}
|
||||||
return kandangRepository.NewKandangRepository(s.Repository.DB())
|
return kandangRepository.NewKandangRepository(s.Repository.DB())
|
||||||
}
|
}
|
||||||
|
|
||||||
func actorIDFromContext(c *fiber.Ctx) (uint, error) {
|
|
||||||
user, ok := authmiddleware.AuthenticatedUser(c)
|
|
||||||
if !ok || user == nil || user.Id == 0 {
|
|
||||||
return 0, fiber.NewError(fiber.StatusUnauthorized, "Please authenticate")
|
|
||||||
}
|
|
||||||
return user.Id, nil
|
|
||||||
}
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user