feat(BE-117): implement CRUD endpoints for project

This commit is contained in:
aguhh18
2025-10-20 11:25:42 +07:00
parent 5c3787886b
commit 7b99b39529
14 changed files with 233 additions and 119 deletions
@@ -23,6 +23,11 @@ type WarehouseSimpleDTO struct {
Name string `json:"name"`
}
type ProductSimpleDTO struct {
Id uint `json:"id"`
Name string `json:"name"`
}
type AreaDTO struct {
Id uint `json:"id"`
Name string `json:"name"`
@@ -33,6 +38,11 @@ type LocationDTO struct {
Name string `json:"name"`
}
type SuplierSimpleDTO struct {
Id uint `json:"id"`
Name string `json:"name"`
}
type WarehouseDetailDTO struct {
Id uint `json:"id"`
Name string `json:"name"`
@@ -57,17 +67,15 @@ type TransferDetailDTO struct {
// Detail produk
type TransferDetailItemDTO struct {
Id uint64 `json:"id"`
ProductId uint64 `json:"product_id"`
Quantity float64 `json:"quantity"`
BeforeQuantity float64 `json:"before_quantity"`
AfterQuantity float64 `json:"after_quantity"`
Id uint64 `json:"id"`
Proudct ProductSimpleDTO `json:"product"`
Quantity float64 `json:"quantity"`
}
// Delivery ekspedisi
type TransferDeliveryDTO struct {
Id uint64 `json:"id"`
SupplierId uint64 `json:"supplier_id"`
Suplier SuplierSimpleDTO `json:"suplier"`
VehiclePlate string `json:"vehicle_plate"`
DriverName string `json:"driver_name"`
DocumentNumber string `json:"document_number"`
@@ -146,9 +154,12 @@ func ToTransferListDTO(e entity.StockTransfer) TransferListDTO {
var details []TransferDetailItemDTO
for _, d := range e.Details {
details = append(details, TransferDetailItemDTO{
Id: d.Id,
ProductId: d.ProductId,
Quantity: d.Quantity,
Id: d.Id,
Proudct: ProductSimpleDTO{
Id: d.Product.Id,
Name: d.Product.Name,
},
Quantity: d.Quantity,
})
}
// Map deliveries
@@ -164,8 +175,11 @@ func ToTransferListDTO(e entity.StockTransfer) TransferListDTO {
})
}
deliveries = append(deliveries, TransferDeliveryDTO{
Id: del.Id,
SupplierId: del.SupplierId,
Id: del.Id,
Suplier: SuplierSimpleDTO{
Id: del.Supplier.Id,
Name: del.Supplier.Name,
},
VehiclePlate: del.VehiclePlate,
DriverName: del.DriverName,
DocumentNumber: del.DocumentNumber,
@@ -198,17 +212,23 @@ func ToTransferDetailDTO(e entity.StockTransfer) TransferDetailDTO {
var details []TransferDetailItemDTO
for _, d := range e.Details {
details = append(details, TransferDetailItemDTO{
Id: d.Id,
ProductId: d.ProductId,
Quantity: d.Quantity,
Id: d.Id,
Proudct: ProductSimpleDTO{
Id: d.Product.Id,
Name: d.Product.Name,
},
Quantity: d.Quantity,
})
}
// Map deliveries
var deliveries []TransferDeliveryDTO
for _, del := range e.Deliveries {
deliveries = append(deliveries, TransferDeliveryDTO{
Id: del.Id,
SupplierId: del.SupplierId,
Id: del.Id,
Suplier: SuplierSimpleDTO{
Id: del.Supplier.Id,
Name: del.Supplier.Name,
},
VehiclePlate: del.VehiclePlate,
DriverName: del.DriverName,
DocumentNumber: del.DocumentNumber,
@@ -60,7 +60,9 @@ func (s transferService) withRelations(db *gorm.DB) *gorm.DB {
Preload("ToWarehouse.Location").
Preload("ToWarehouse.Area").
Preload("Details").
Preload("Deliveries.Items")
Preload("Details.Product").
Preload("Deliveries.Items").
Preload("Deliveries.Supplier")
}
func (s transferService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity.StockTransfer, int64, error) {
@@ -24,9 +24,9 @@ func NewChickinController(chickinService service.ChickinService) *ChickinControl
func (u *ChickinController) GetAll(c *fiber.Ctx) error {
query := &validation.Query{
Page: c.QueryInt("page", 1),
Limit: c.QueryInt("limit", 10),
Search: c.Query("search", ""),
Page: c.QueryInt("page", 1),
Limit: c.QueryInt("limit", 10),
ProjectFlockKandangId: uint(c.QueryInt("project_flock_kandang_id", 0)),
}
result, totalResults, err := u.ChickinService.GetAll(c, query)
@@ -88,7 +88,7 @@ func (u *ChickinController) CreateOne(c *fiber.Ctx) error {
Code: fiber.StatusCreated,
Status: "success",
Message: "Create chickin successfully",
Data: result,
Data: dto.ToChickinListDTO(*result),
})
}
@@ -68,12 +68,12 @@ type ChickinBaseDTO struct {
}
type ChickinSimpleDTO struct {
Id uint `json:"id"`
ProjectFlocKandangId uint `json:"project_floc_kandang_id"`
ChickInDate time.Time `json:"chick_in_date"`
Quantity float64 `json:"quantity"`
Note string `json:"note"`
CreatedBy uint `json:"created_by"`
Id uint `json:"id"`
ProjectFlockKandangId uint `json:"project_flock_kandang_id"`
ChickInDate time.Time `json:"chick_in_date"`
Quantity float64 `json:"quantity"`
Note string `json:"note"`
CreatedBy uint `json:"created_by"`
}
type ChickinListDTO struct {
@@ -197,7 +197,7 @@ func ToUserBaseDTO(e entity.User) UserBaseDTO {
func ToChickinBaseDTO(e entity.ProjectChickin) ChickinBaseDTO {
return ChickinBaseDTO{
Id: e.Id,
ProjectFlocKandangId: e.ProjectFlocKandangId,
ProjectFlocKandangId: e.ProjectFlockKandangId,
ChickInDate: e.ChickInDate,
Quantity: e.Quantity,
Note: e.Note,
@@ -206,12 +206,12 @@ func ToChickinBaseDTO(e entity.ProjectChickin) ChickinBaseDTO {
func ToChickinSimpleDTO(e entity.ProjectChickin) ChickinSimpleDTO {
return ChickinSimpleDTO{
Id: e.Id,
ProjectFlocKandangId: e.ProjectFlocKandangId,
ChickInDate: e.ChickInDate,
Quantity: e.Quantity,
Note: e.Note,
CreatedBy: e.CreatedBy,
Id: e.Id,
ProjectFlockKandangId: e.ProjectFlockKandangId,
ChickInDate: e.ChickInDate,
Quantity: e.Quantity,
Note: e.Note,
CreatedBy: e.CreatedBy,
}
}
@@ -26,12 +26,13 @@ func (ChickinModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate *
auditlogrepo := rAuditLog.NewAuditLogRepository(db)
warehouseRepo := rWarehouse.NewWarehouseRepository(db)
projectflockkandangrepo := rProjectFlock.NewProjectFlockKandangRepository(db)
projectflockpopulationrepo := rProjectFlock.NewProjectFlockPopulationRepository(db)
projectFlockRepo := rProjectFlock.NewProjectflockRepository(db)
productWarehouseRepo := rProductWarehouse.NewProductWarehouseRepository(db)
userRepo := rUser.NewUserRepository(db)
chickinService := sChickin.NewChickinService(chickinRepo, kandangRepo, warehouseRepo, productWarehouseRepo, projectFlockRepo, auditlogrepo, projectflockkandangrepo, validate)
chickinService := sChickin.NewChickinService(chickinRepo, kandangRepo, warehouseRepo, productWarehouseRepo, projectFlockRepo, auditlogrepo, projectflockkandangrepo, projectflockpopulationrepo, validate)
userService := sUser.NewUserService(userRepo, validate)
ChickinRoutes(router, userService, chickinService)
@@ -31,28 +31,30 @@ type ChickinService interface {
}
type chickinService struct {
Log *logrus.Logger
Validate *validator.Validate
Repository repository.ProjectChickinRepository
KandangRepo KandangRepo.KandangRepository
WarehouseRepo rWarehouse.WarehouseRepository
ProductWarehouseRepo rProductWarehouse.ProductWarehouseRepository
ProjectFlockRepo rProjectFlock.ProjectflockRepository
AuditLogRepo AuditLogRepo.AuditLogRepository
ProjectflockKandangRepo rProjectFlockKandang.ProjectFlockKandangRepository
Log *logrus.Logger
Validate *validator.Validate
Repository repository.ProjectChickinRepository
KandangRepo KandangRepo.KandangRepository
WarehouseRepo rWarehouse.WarehouseRepository
ProductWarehouseRepo rProductWarehouse.ProductWarehouseRepository
ProjectFlockRepo rProjectFlock.ProjectflockRepository
AuditLogRepo AuditLogRepo.AuditLogRepository
ProjectflockKandangRepo rProjectFlockKandang.ProjectFlockKandangRepository
ProjectflockPopulationRepo rProjectFlock.ProjectFlockPopulationRepository
}
func NewChickinService(repo repository.ProjectChickinRepository, kandangRepo KandangRepo.KandangRepository, warehouseRepo rWarehouse.WarehouseRepository, productWarehouseRepo rProductWarehouse.ProductWarehouseRepository, projectFlockRepo rProjectFlock.ProjectflockRepository, auditLogRepo AuditLogRepo.AuditLogRepository, projectflockkandangRepo rProjectFlockKandang.ProjectFlockKandangRepository, validate *validator.Validate) ChickinService {
func NewChickinService(repo repository.ProjectChickinRepository, kandangRepo KandangRepo.KandangRepository, warehouseRepo rWarehouse.WarehouseRepository, productWarehouseRepo rProductWarehouse.ProductWarehouseRepository, projectFlockRepo rProjectFlock.ProjectflockRepository, auditLogRepo AuditLogRepo.AuditLogRepository, projectflockkandangRepo rProjectFlockKandang.ProjectFlockKandangRepository, projectflockpopulationRepo rProjectFlock.ProjectFlockPopulationRepository, validate *validator.Validate) ChickinService {
return &chickinService{
Log: utils.Log,
Validate: validate,
Repository: repo,
KandangRepo: kandangRepo,
WarehouseRepo: warehouseRepo,
ProductWarehouseRepo: productWarehouseRepo,
ProjectFlockRepo: projectFlockRepo,
AuditLogRepo: auditLogRepo,
ProjectflockKandangRepo: projectflockkandangRepo,
Log: utils.Log,
Validate: validate,
Repository: repo,
KandangRepo: kandangRepo,
WarehouseRepo: warehouseRepo,
ProductWarehouseRepo: productWarehouseRepo,
ProjectFlockRepo: projectFlockRepo,
AuditLogRepo: auditLogRepo,
ProjectflockKandangRepo: projectflockkandangRepo,
ProjectflockPopulationRepo: projectflockpopulationRepo,
}
}
@@ -78,8 +80,9 @@ func (s chickinService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity
chickins, total, err := s.Repository.GetAll(c.Context(), offset, params.Limit, func(db *gorm.DB) *gorm.DB {
db = s.withRelations(db)
if params.Search != "" {
return db.Where("name LIKE ?", "%"+params.Search+"%")
if params.ProjectFlockKandangId != 0 {
return db.Where("project_flock_kandang_id = ?", params.ProjectFlockKandangId)
}
return db.Order("created_at DESC").Order("updated_at DESC")
})
@@ -163,11 +166,11 @@ func (s *chickinService) CreateOne(c *fiber.Ctx, req *validation.Create) (*entit
}
newChickin := &entity.ProjectChickin{
ProjectFlocKandangId: projectflockkandang.ProjectFlockId,
ChickInDate: chickinDate,
Quantity: productWarehouse.Quantity,
Note: "",
CreatedBy: 1, //todo: ganti dengan
ProjectFlockKandangId: projectflockkandang.ProjectFlockId,
ChickInDate: chickinDate,
Quantity: productWarehouse.Quantity,
Note: "",
CreatedBy: 1, //todo: ganti dengan
}
err = s.Repository.CreateOne(c.Context(), newChickin, nil)
@@ -188,45 +191,37 @@ func (s *chickinService) CreateOne(c *fiber.Ctx, req *validation.Create) (*entit
s.Log.Errorf("Failed to update product warehouse quantity: %+v", err)
return nil, err
}
// masukan check apakah stock availability ada, jika ada update, jika tidak buat baru
stockAvailability := &entity.StockAvailability{
EntityType: entity.EntityTypeProjectFlockKandang,
ReservedQuantity: productWarehouse.Quantity,
EntityId: projectflockkandang.Id,
ProductId: productWarehouse.ProductId,
// masukan data nya ke project flock population
// check apakah sudah ada
existingPopulation, err := s.ProjectflockPopulationRepo.GetByProjectFlockKandangID(c.Context(), req.ProjectFlockKandangId)
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 {
// update quantity
var existingStockAvailability entity.StockAvailability
err = s.ProductWarehouseRepo.DB().WithContext(c.Context()).
Where("entity_type = ? AND entity_id = ? AND product_id = ?", stockAvailability.EntityType, stockAvailability.EntityId, stockAvailability.ProductId).
First(&existingStockAvailability).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
// buat baru
stockAvailability.ReservedQuantity = newChickin.Quantity
stockAvailability.Quantity = 0
err = s.ProductWarehouseRepo.DB().WithContext(c.Context()).Create(stockAvailability).Error
if err != nil {
s.Log.Errorf("Failed to create stock availability: %+v", err)
return nil, err
}
} else {
s.Log.Errorf("Failed to get stock availability: %+v", err)
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 {
// update existing
newQuantity := existingStockAvailability.ReservedQuantity + newChickin.Quantity
err = s.ProductWarehouseRepo.DB().WithContext(c.Context()).
Model(&existingStockAvailability).
Update("reserved_quantity", newQuantity).Error
// create new population
newPopulation := &entity.ProjectFlockPopulation{
ProjectFlockKandangId: req.ProjectFlockKandangId,
InitialQuantity: 0,
CurrentQuantity: 0,
ReservedQuantity: newChickin.Quantity,
CreatedBy: 1, // todo: ganti dengan user login
}
err = s.ProjectflockPopulationRepo.CreateOne(c.Context(), newPopulation, nil)
if err != nil {
s.Log.Errorf("Failed to update stock availability: %+v", err)
s.Log.Errorf("Failed to create project flock population: %+v", err)
return nil, err
}
}
return s.GetOne(c, newChickin.Id)
@@ -283,20 +278,21 @@ func (s *chickinService) Approve(c *fiber.Ctx, id uint) error {
return err
}
//pindahkan stock dari reserved ke actual stock
// get stock avaibility untuk di update
var stockAvailability entity.StockAvailability
err = s.ProductWarehouseRepo.DB().WithContext(c.Context()).
Where("entity_type = ? AND entity_id = ? ", entity.EntityTypeProjectFlockKandang, chickin.ProjectFlocKandangId).
First(&stockAvailability).Error
//pindahkan stock dari reserved ke actual stock pada table project flock population
population, err := s.ProjectflockPopulationRepo.GetByProjectFlockKandangID(c.Context(), chickin.ProjectFlockKandangId)
if err != nil {
s.Log.Errorf("Failed to get stock availability: %+v", err)
s.Log.Errorf("Failed to get project flock population: %+v", err)
return err
}
newReservedQuantity := stockAvailability.ReservedQuantity - chickin.Quantity
if newReservedQuantity < 0 {
newReservedQuantity = 0
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
@@ -10,7 +10,7 @@ type Update struct {
}
type Query struct {
Page int `query:"page" validate:"omitempty,number,min=1"`
Limit int `query:"limit" validate:"omitempty,number,min=1,max=100"`
Search string `query:"search" validate:"omitempty,max=50"`
Page int `query:"page" validate:"omitempty,number,min=1"`
Limit int `query:"limit" validate:"omitempty,number,min=1,max=100"`
ProjectFlockKandangId uint `query:"project_flock_kandang_id" validate:"omitempty,number,min=1"`
}
@@ -8,6 +8,7 @@ import (
rFlock "gitlab.com/mbugroup/lti-api.git/internal/modules/master/flocks/repositories"
rKandang "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/repositories"
rProjectflock "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/repositories"
sProjectflock "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/services"
rUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/repositories"
@@ -0,0 +1,35 @@
package repository
import (
"context"
"gitlab.com/mbugroup/lti-api.git/internal/common/repository"
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
"gorm.io/gorm"
)
type ProjectFlockPopulationRepository interface {
repository.BaseRepository[entity.ProjectFlockPopulation]
GetByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) (*entity.ProjectFlockPopulation, error)
}
type projectFlockPopulationRepositoryImpl struct {
*repository.BaseRepositoryImpl[entity.ProjectFlockPopulation]
}
func NewProjectFlockPopulationRepository(db *gorm.DB) ProjectFlockPopulationRepository {
return &projectFlockPopulationRepositoryImpl{
BaseRepositoryImpl: repository.NewBaseRepository[entity.ProjectFlockPopulation](db),
}
}
func (r *projectFlockPopulationRepositoryImpl) GetByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) (*entity.ProjectFlockPopulation, error) {
var record entity.ProjectFlockPopulation
err := r.DB().WithContext(ctx).
Where("project_flock_kandang_id = ?", projectFlockKandangID).
First(&record).Error
if err != nil {
return nil, err
}
return &record, nil
}