mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-24 15:25:43 +00:00
Merge branch 'dev/teguh' into 'development'
fix(BE): fix error get location id when only attach to location in expense See merge request mbugroup/lti-api!119
This commit is contained in:
+20
@@ -0,0 +1,20 @@
|
|||||||
|
-- Drop CASCADE constraint
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_constraint
|
||||||
|
WHERE conname = 'fk_project_chickins_kandang'
|
||||||
|
AND conrelid = 'project_chickins'::regclass
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE project_chickins
|
||||||
|
DROP CONSTRAINT fk_project_chickins_kandang;
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
-- Recreate foreign key constraint with RESTRICT (original behavior)
|
||||||
|
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;
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
-- Drop existing foreign key constraint with RESTRICT
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_constraint
|
||||||
|
WHERE conname = 'fk_project_chickins_kandang'
|
||||||
|
AND conrelid = 'project_chickins'::regclass
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE project_chickins
|
||||||
|
DROP CONSTRAINT fk_project_chickins_kandang;
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
-- Add new foreign key constraint with CASCADE delete
|
||||||
|
ALTER TABLE project_chickins
|
||||||
|
ADD CONSTRAINT fk_project_chickins_kandang
|
||||||
|
FOREIGN KEY (project_flock_kandang_id)
|
||||||
|
REFERENCES project_flock_kandangs(id)
|
||||||
|
ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
@@ -203,12 +203,12 @@ func (u *ExpenseController) UpdateOne(c *fiber.Ctx) error {
|
|||||||
func (u *ExpenseController) DeleteOne(c *fiber.Ctx) error {
|
func (u *ExpenseController) DeleteOne(c *fiber.Ctx) error {
|
||||||
param := c.Params("id")
|
param := c.Params("id")
|
||||||
|
|
||||||
id, err := strconv.Atoi(param)
|
id64, err := strconv.ParseUint(param, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid Id")
|
return fiber.NewError(fiber.StatusBadRequest, "Invalid Id")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := u.ExpenseService.DeleteOne(c, uint(id)); err != nil {
|
if err := u.ExpenseService.DeleteOne(c, id64); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -105,11 +105,9 @@ func ToExpenseBaseDTO(e *entity.Expense) ExpenseBaseDTO {
|
|||||||
realizationDate = &e.RealizationDate
|
realizationDate = &e.RealizationDate
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(e.Nonstocks) > 0 && e.Nonstocks[0].Kandang != nil {
|
if e.Location != nil && e.Location.Id != 0 {
|
||||||
if e.Nonstocks[0].Kandang.Location.Id != 0 {
|
mapped := locationDTO.ToLocationRelationDTO(*e.Location)
|
||||||
mapped := locationDTO.ToLocationRelationDTO(e.Nonstocks[0].Kandang.Location)
|
location = &mapped
|
||||||
location = &mapped
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if e.Supplier != nil && e.Supplier.Id != 0 {
|
if e.Supplier != nil && e.Supplier.Id != 0 {
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package repository
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"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"
|
||||||
@@ -17,6 +19,7 @@ type ExpenseRepository interface {
|
|||||||
GetWithSupplier(ctx context.Context, id uint64) (*entity.Expense, error)
|
GetWithSupplier(ctx context.Context, id uint64) (*entity.Expense, error)
|
||||||
WithProjectFlockKandangFilter(pfkID, kandangID uint) func(*gorm.DB) *gorm.DB
|
WithProjectFlockKandangFilter(pfkID, kandangID uint) func(*gorm.DB) *gorm.DB
|
||||||
CountUnfinishedByProjectFlockKandang(ctx context.Context, pfkID, kandangID uint, isFinished func(*entity.Approval) bool) (int64, error)
|
CountUnfinishedByProjectFlockKandang(ctx context.Context, pfkID, kandangID uint, isFinished func(*entity.Approval) bool) (int64, error)
|
||||||
|
DeleteOne(ctx context.Context, id uint) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExpenseRepositoryImpl struct {
|
type ExpenseRepositoryImpl struct {
|
||||||
@@ -107,3 +110,23 @@ func (r *ExpenseRepositoryImpl) CountUnfinishedByProjectFlockKandang(ctx context
|
|||||||
}
|
}
|
||||||
return unfinished, nil
|
return unfinished, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *ExpenseRepositoryImpl) DeleteOne(ctx context.Context, id uint) error {
|
||||||
|
// Cast to uint64 to match entity.Id type
|
||||||
|
id64 := uint64(id)
|
||||||
|
deletedAt := time.Now()
|
||||||
|
|
||||||
|
// Use raw SQL with interpolated integer to avoid type issues
|
||||||
|
// Interpolate id directly as integer literal (safe because it's uint64)
|
||||||
|
result := r.DB().WithContext(ctx).
|
||||||
|
Exec(`UPDATE "expenses" SET "deleted_at" = $1 WHERE "id" = `+fmt.Sprintf("%d", id64)+` AND "deleted_at" IS NULL`,
|
||||||
|
deletedAt)
|
||||||
|
|
||||||
|
if result.Error != nil {
|
||||||
|
return result.Error
|
||||||
|
}
|
||||||
|
if result.RowsAffected == 0 {
|
||||||
|
return gorm.ErrRecordNotFound
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,16 +22,16 @@ func ExpenseRoutes(v1 fiber.Router, u user.UserService, s expense.ExpenseService
|
|||||||
// route.Patch("/:id", m.Auth(u), ctrl.UpdateOne)
|
// route.Patch("/:id", m.Auth(u), ctrl.UpdateOne)
|
||||||
// route.Delete("/:id", m.Auth(u), ctrl.DeleteOne)
|
// route.Delete("/:id", m.Auth(u), ctrl.DeleteOne)
|
||||||
|
|
||||||
route.Get("/",m.RequirePermissions(m.P_ExpenseGetAll), ctrl.GetAll)
|
route.Get("/", m.RequirePermissions(m.P_ExpenseGetAll), ctrl.GetAll)
|
||||||
route.Post("/",m.RequirePermissions(m.P_ExpenseCreateOne), ctrl.CreateOne)
|
route.Post("/", m.RequirePermissions(m.P_ExpenseCreateOne), ctrl.CreateOne)
|
||||||
route.Get("/:id",m.RequirePermissions(m.P_ExpenseGetOne), ctrl.GetOne)
|
route.Get("/:id", m.RequirePermissions(m.P_ExpenseGetOne), ctrl.GetOne)
|
||||||
route.Patch("/:id",m.RequirePermissions(m.P_ExpenseUpdateOne), ctrl.UpdateOne)
|
route.Patch("/:id", m.RequirePermissions(m.P_ExpenseUpdateOne), ctrl.UpdateOne)
|
||||||
route.Delete("/:id",m.RequirePermissions(m.P_ExpenseDeleteOne), ctrl.DeleteOne)
|
route.Delete("/:id", m.RequirePermissions(m.P_ExpenseDeleteOne), ctrl.DeleteOne)
|
||||||
route.Post("/approvals/manager",m.RequirePermissions(m.P_ExpenseApprovalManager), ctrl.Approval)
|
route.Post("/approvals/manager", m.RequirePermissions(m.P_ExpenseApprovalManager), ctrl.Approval)
|
||||||
route.Post("/approvals/finance",m.RequirePermissions(m.P_ExpenseApprovalFinance), ctrl.Approval)
|
route.Post("/approvals/finance", m.RequirePermissions(m.P_ExpenseApprovalFinance), ctrl.Approval)
|
||||||
route.Post("/:id/realizations",m.RequirePermissions(m.P_ExpenseCreateRealizations), ctrl.CreateRealization)
|
route.Post("/:id/realizations", m.RequirePermissions(m.P_ExpenseCreateRealizations), ctrl.CreateRealization)
|
||||||
route.Patch("/:id/realizations",m.RequirePermissions(m.P_ExpenseUpdateRealizations), ctrl.UpdateRealization)
|
route.Patch("/:id/realizations", m.RequirePermissions(m.P_ExpenseUpdateRealizations), ctrl.UpdateRealization)
|
||||||
route.Post("/:id/complete",m.RequirePermissions(m.P_ExpenseCompleteExpense), ctrl.CompleteExpense)
|
route.Post("/:id/complete", m.RequirePermissions(m.P_ExpenseCompleteExpense), ctrl.CompleteExpense)
|
||||||
route.Delete("/:id/documents/:documentId",m.RequirePermissions(m.P_ExpenseDocument), ctrl.DeleteDocument)
|
route.Delete("/:id/documents/:documentId", m.RequirePermissions(m.P_ExpenseDocument), ctrl.DeleteDocument)
|
||||||
route.Delete("/:id/realization-documents/:documentId",m.RequirePermissions(m.P_ExpenseDocumentRealizations), ctrl.DeleteRealizationDocument)
|
route.Delete("/:id/realization-documents/:documentId", m.RequirePermissions(m.P_ExpenseDocumentRealizations), ctrl.DeleteRealizationDocument)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ type ExpenseService interface {
|
|||||||
GetOne(ctx *fiber.Ctx, id uint) (*expenseDto.ExpenseDetailDTO, error)
|
GetOne(ctx *fiber.Ctx, id uint) (*expenseDto.ExpenseDetailDTO, error)
|
||||||
CreateOne(ctx *fiber.Ctx, req *validation.Create) (*expenseDto.ExpenseDetailDTO, error)
|
CreateOne(ctx *fiber.Ctx, req *validation.Create) (*expenseDto.ExpenseDetailDTO, error)
|
||||||
UpdateOne(ctx *fiber.Ctx, req *validation.Update, id uint) (*expenseDto.ExpenseDetailDTO, error)
|
UpdateOne(ctx *fiber.Ctx, req *validation.Update, id uint) (*expenseDto.ExpenseDetailDTO, error)
|
||||||
DeleteOne(ctx *fiber.Ctx, id uint) error
|
DeleteOne(ctx *fiber.Ctx, id uint64) error
|
||||||
CreateRealization(ctx *fiber.Ctx, expenseID uint, req *validation.CreateRealization) (*expenseDto.ExpenseDetailDTO, error)
|
CreateRealization(ctx *fiber.Ctx, expenseID uint, req *validation.CreateRealization) (*expenseDto.ExpenseDetailDTO, error)
|
||||||
CompleteExpense(ctx *fiber.Ctx, id uint, notes *string) (*expenseDto.ExpenseDetailDTO, error)
|
CompleteExpense(ctx *fiber.Ctx, id uint, notes *string) (*expenseDto.ExpenseDetailDTO, error)
|
||||||
UpdateRealization(ctx *fiber.Ctx, expenseID uint, req *validation.UpdateRealization) (*expenseDto.ExpenseDetailDTO, error)
|
UpdateRealization(ctx *fiber.Ctx, expenseID uint, req *validation.UpdateRealization) (*expenseDto.ExpenseDetailDTO, error)
|
||||||
@@ -68,6 +68,7 @@ func (s expenseService) withRelations(db *gorm.DB) *gorm.DB {
|
|||||||
return db.
|
return db.
|
||||||
Preload("CreatedUser").
|
Preload("CreatedUser").
|
||||||
Preload("Supplier").
|
Preload("Supplier").
|
||||||
|
Preload("Location").
|
||||||
Preload("Nonstocks.Nonstock").
|
Preload("Nonstocks.Nonstock").
|
||||||
Preload("Nonstocks.Realization").
|
Preload("Nonstocks.Realization").
|
||||||
Preload("Nonstocks.ProjectFlockKandang.Kandang.Location").
|
Preload("Nonstocks.ProjectFlockKandang.Kandang.Location").
|
||||||
@@ -621,14 +622,15 @@ func (s expenseService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uint)
|
|||||||
return responseDTO, nil
|
return responseDTO, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s expenseService) DeleteOne(c *fiber.Ctx, id uint) error {
|
func (s expenseService) DeleteOne(c *fiber.Ctx, id uint64) error {
|
||||||
|
idUint := uint(id)
|
||||||
|
|
||||||
if err := commonSvc.EnsureRelations(c.Context(),
|
if err := commonSvc.EnsureRelations(c.Context(),
|
||||||
commonSvc.RelationCheck{Name: "Expense", ID: &id, Exists: s.Repository.IdExists},
|
commonSvc.RelationCheck{Name: "Expense", ID: &idUint, Exists: s.Repository.IdExists},
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
expense, err := s.Repository.GetByID(c.Context(), id, func(db *gorm.DB) *gorm.DB {
|
expense, err := s.Repository.GetByID(c.Context(), idUint, func(db *gorm.DB) *gorm.DB {
|
||||||
return db.Preload("Nonstocks")
|
return db.Preload("Nonstocks")
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -643,7 +645,7 @@ func (s expenseService) DeleteOne(c *fiber.Ctx, id uint) error {
|
|||||||
if err := s.ensureProjectFlockNotClosedForExpense(c.Context(), expense); err != nil {
|
if err := s.ensureProjectFlockNotClosedForExpense(c.Context(), expense); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := s.Repository.DeleteOne(c.Context(), id); err != nil {
|
if err := s.Repository.DeleteOne(c.Context(), idUint); err != nil {
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
s.Log.Errorf("Expense not found for ID %d: %+v", id, err)
|
s.Log.Errorf("Expense not found for ID %d: %+v", id, err)
|
||||||
return fiber.NewError(fiber.StatusNotFound, "Expense not found")
|
return fiber.NewError(fiber.StatusNotFound, "Expense not found")
|
||||||
|
|||||||
@@ -118,10 +118,9 @@ func (s *adjustmentService) Adjustment(c *fiber.Ctx, req *validation.Create) (*e
|
|||||||
var createdLogId uint
|
var createdLogId uint
|
||||||
|
|
||||||
var projectFlockKandangID *uint
|
var projectFlockKandangID *uint
|
||||||
pfk, err := s.ProjectFlockKandangRepo.GetActiveByKandangID(ctx, uint(req.WarehouseID))
|
pfkID, err := s.getActiveProjectFlockKandangID(ctx, uint(req.WarehouseID))
|
||||||
if err == nil && pfk != nil {
|
if err == nil && pfkID > 0 {
|
||||||
idCopy := uint(pfk.Id)
|
projectFlockKandangID = &pfkID
|
||||||
projectFlockKandangID = &idCopy
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pw, err := s.ProductWarehouseRepo.FindByProductWarehouseAndPfk(
|
pw, err := s.ProductWarehouseRepo.FindByProductWarehouseAndPfk(
|
||||||
|
|||||||
@@ -59,6 +59,14 @@ func (s nonstockService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entit
|
|||||||
|
|
||||||
nonstocks, total, err := s.Repository.GetAll(c.Context(), offset, params.Limit, func(db *gorm.DB) *gorm.DB {
|
nonstocks, 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.SupplierID != nil {
|
||||||
|
supplierID := *params.SupplierID
|
||||||
|
db = db.Joins("JOIN nonstock_suppliers ON nonstock_suppliers.nonstock_id = nonstocks.id").
|
||||||
|
Where("nonstock_suppliers.supplier_id = ?", supplierID).
|
||||||
|
Group("nonstocks.id") // Prevent duplicates from join
|
||||||
|
}
|
||||||
|
|
||||||
if params.Search != "" {
|
if params.Search != "" {
|
||||||
return db.Where("name LIKE ?", "%"+params.Search+"%")
|
return db.Where("name LIKE ?", "%"+params.Search+"%")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ type Update struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Query struct {
|
type Query struct {
|
||||||
Page int `query:"page" validate:"omitempty,number,min=1"`
|
Page int `query:"page" validate:"omitempty,number,min=1"`
|
||||||
Limit int `query:"limit" validate:"omitempty,number,min=1,max=100"`
|
Limit int `query:"limit" validate:"omitempty,number,min=1,max=100"`
|
||||||
Search string `query:"search" validate:"omitempty,max=50"`
|
Search string `query:"search" validate:"omitempty,max=50"`
|
||||||
|
SupplierID *uint `query:"supplier_id" validate:"omitempty,gt=0"`
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user