Feat[BE-261]: creating Entity and repository for each table expenses

This commit is contained in:
aguhh18
2025-11-18 08:18:37 +07:00
parent 5c25c84f7f
commit 1dac74e25b
10 changed files with 193 additions and 39 deletions
+19 -7
View File
@@ -1,18 +1,30 @@
package entities
import (
"database/sql"
"time"
"gorm.io/gorm"
)
type Expense struct {
Id uint `gorm:"primaryKey"`
Name string `gorm:"not null;uniqueIndex:idx_name,where:deleted_at IS NULL"`
CreatedBy uint `gorm:"not null"`
CreatedAt time.Time `gorm:"autoCreateTime"`
UpdatedAt time.Time `gorm:"autoUpdateTime"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
Id uint64 `gorm:"primaryKey;autoIncrement"`
ReferenceNumber *string `gorm:"type:varchar(50)"`
SupplierId *uint64 `gorm:""`
Category string `gorm:"type:varchar(50);not null"`
PoNumber string `gorm:"uniqueIndex;not null;type:varchar(50)"`
DocumentPath sql.NullString `gorm:"type:json"`
ExpenseDate time.Time `gorm:"type:date;not null"`
GrandTotal float64 `gorm:"type:numeric(15,3);default:0"`
Note *string `gorm:"type:text"`
CreatedBy *uint64 `gorm:""`
CreatedAt time.Time `gorm:"autoCreateTime"`
UpdatedAt time.Time `gorm:"autoUpdateTime"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
CreatedUser User `gorm:"foreignKey:CreatedBy;references:Id"`
// Relations
Supplier *Supplier `gorm:"foreignKey:SupplierId;references:Id"`
CreatedUser *User `gorm:"foreignKey:CreatedBy;references:Id"`
Nonstocks []ExpenseNonstock `gorm:"foreignKey:ExpenseId;references:Id"`
LatestApproval *Approval `gorm:"-" json:"latest_approval,omitempty"`
}
+27
View File
@@ -0,0 +1,27 @@
package entities
import (
"time"
"gorm.io/gorm"
)
type ExpenseNonstock struct {
Id uint64 `gorm:"primaryKey;autoIncrement"`
ExpenseId *uint64 `gorm:""`
ProjectFlockKandangId *uint64 `gorm:""`
NonstockId *uint64 `gorm:""`
Qty float64 `gorm:"type:numeric(15,3);not null"`
UnitPrice float64 `gorm:"type:numeric(15,3);not null"`
TotalPrice 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" json:"-"`
// Relations
Expense *Expense `gorm:"foreignKey:ExpenseId;references:Id"`
ProjectFlockKandang *ProjectFlockKandang `gorm:"foreignKey:ProjectFlockKandangId;references:Id"`
Nonstock *Nonstock `gorm:"foreignKey:NonstockId;references:Id"`
Realizations []ExpenseRealization `gorm:"foreignKey:ExpenseNonstockId;references:Id"`
}
+25
View File
@@ -0,0 +1,25 @@
package entities
import (
"time"
"gorm.io/gorm"
)
type ExpenseRealization struct {
Id uint64 `gorm:"primaryKey;autoIncrement"`
ExpenseNonstockId *uint64 `gorm:""`
RealizationQty float64 `gorm:"type:numeric(15,3);not null"`
RealizationUnitPrice float64 `gorm:"type:numeric(15,3);not null"`
RealizationTotalPrice float64 `gorm:"type:numeric(15,3);not null"`
RealizationDate time.Time `gorm:"type:date;not null"`
Note *string `gorm:"type:text"`
CreatedBy *uint64 `gorm:""`
CreatedAt time.Time `gorm:"autoCreateTime"`
UpdatedAt time.Time `gorm:"autoUpdateTime"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
// Relations
ExpenseNonstock *ExpenseNonstock `gorm:"foreignKey:ExpenseNonstockId;references:Id"`
CreatedUser *User `gorm:"foreignKey:CreatedBy;references:Id"`
}
+27 -25
View File
@@ -10,44 +10,52 @@ import (
// === DTO Structs ===
type ExpenseBaseDTO struct {
Id uint `json:"id"`
Name string `json:"name"`
Id uint64 `json:"id"`
PoNumber string `json:"po_number"`
ExpenseDate time.Time `json:"expense_date"`
GrandTotal float64 `json:"grand_total"`
}
type ExpenseListDTO struct {
Id uint `json:"id"`
Name string `json:"name"`
CreatedUser *userDTO.UserBaseDTO `json:"created_user"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type ExpenseDetailDTO struct {
ExpenseListDTO
Id uint64 `json:"id"`
ReferenceNumber string `json:"reference_number"`
PoNumber string `json:"po_number"`
Category string `json:"category"`
ExpenseDate time.Time `json:"expense_date"`
GrandTotal float64 `json:"grand_total"`
CreatedUser *userDTO.UserBaseDTO `json:"created_user,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// === Mapper Functions ===
func ToExpenseBaseDTO(e entity.Expense) ExpenseBaseDTO {
return ExpenseBaseDTO{
Id: e.Id,
Name: e.Name,
Id: e.Id,
PoNumber: e.PoNumber,
ExpenseDate: e.ExpenseDate,
GrandTotal: e.GrandTotal,
}
}
func ToExpenseListDTO(e entity.Expense) ExpenseListDTO {
var createdUser *userDTO.UserBaseDTO
if e.CreatedUser.Id != 0 {
mapped := userDTO.ToUserBaseDTO(e.CreatedUser)
mapped := userDTO.ToUserBaseDTO(*e.CreatedUser)
createdUser = &mapped
}
return ExpenseListDTO{
Id: e.Id,
Name: e.Name,
CreatedAt: e.CreatedAt,
UpdatedAt: e.UpdatedAt,
CreatedUser: createdUser,
Id: e.Id,
ReferenceNumber: *e.ReferenceNumber,
PoNumber: e.PoNumber,
Category: e.Category,
ExpenseDate: e.ExpenseDate,
GrandTotal: e.GrandTotal,
CreatedAt: e.CreatedAt,
UpdatedAt: e.UpdatedAt,
CreatedUser: createdUser,
}
}
@@ -58,9 +66,3 @@ func ToExpenseListDTOs(e []entity.Expense) []ExpenseListDTO {
}
return result
}
func ToExpenseDetailDTO(e entity.Expense) ExpenseDetailDTO {
return ExpenseDetailDTO{
ExpenseListDTO: ToExpenseListDTO(e),
}
}
@@ -1,13 +1,16 @@
package repository
import (
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
"context"
"gitlab.com/mbugroup/lti-api.git/internal/common/repository"
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
"gorm.io/gorm"
)
type ExpenseRepository interface {
repository.BaseRepository[entity.Expense]
IdExists(ctx context.Context, id uint64) (bool, error)
}
type ExpenseRepositoryImpl struct {
@@ -19,3 +22,7 @@ func NewExpenseRepository(db *gorm.DB) ExpenseRepository {
BaseRepositoryImpl: repository.NewBaseRepository[entity.Expense](db),
}
}
func (r *ExpenseRepositoryImpl) IdExists(ctx context.Context, id uint64) (bool, error) {
return repository.Exists[entity.Expense](ctx, r.DB(), uint(id))
}
@@ -0,0 +1,28 @@
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 ExpenseNonstockRepository interface {
repository.BaseRepository[entity.ExpenseNonstock]
IdExists(ctx context.Context, id uint64) (bool, error)
}
type ExpenseNonstockRepositoryImpl struct {
*repository.BaseRepositoryImpl[entity.ExpenseNonstock]
}
func NewExpenseNonstockRepository(db *gorm.DB) ExpenseNonstockRepository {
return &ExpenseNonstockRepositoryImpl{
BaseRepositoryImpl: repository.NewBaseRepository[entity.ExpenseNonstock](db),
}
}
func (r *ExpenseNonstockRepositoryImpl) IdExists(ctx context.Context, id uint64) (bool, error) {
return repository.Exists[entity.ExpenseNonstock](ctx, r.DB(), uint(id))
}
@@ -0,0 +1,28 @@
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 ExpenseRealizationRepository interface {
repository.BaseRepository[entity.ExpenseRealization]
IdExists(ctx context.Context, id uint64) (bool, error)
}
type ExpenseRealizationRepositoryImpl struct {
*repository.BaseRepositoryImpl[entity.ExpenseRealization]
}
func NewExpenseRealizationRepository(db *gorm.DB) ExpenseRealizationRepository {
return &ExpenseRealizationRepositoryImpl{
BaseRepositoryImpl: repository.NewBaseRepository[entity.ExpenseRealization](db),
}
}
func (r *ExpenseRealizationRepositoryImpl) IdExists(ctx context.Context, id uint64) (bool, error) {
return repository.Exists[entity.ExpenseRealization](ctx, r.DB(), uint(id))
}
@@ -79,8 +79,11 @@ func (s *expenseService) CreateOne(c *fiber.Ctx, req *validation.Create) (*entit
return nil, err
}
createdBy := uint64(1)
createBody := &entity.Expense{
Name: req.Name,
PoNumber: req.PoNumber,
Category: req.Category,
CreatedBy: &createdBy,
}
if err := s.Repository.CreateOne(c.Context(), createBody, nil); err != nil {
@@ -88,7 +91,7 @@ func (s *expenseService) CreateOne(c *fiber.Ctx, req *validation.Create) (*entit
return nil, err
}
return s.GetOne(c, createBody.Id)
return s.GetOne(c, uint(createBody.Id))
}
func (s expenseService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uint) (*entity.Expense, error) {
@@ -98,8 +101,11 @@ func (s expenseService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uint)
updateBody := make(map[string]any)
if req.Name != nil {
updateBody["name"] = *req.Name
if req.PoNumber != nil {
updateBody["po_number"] = *req.PoNumber
}
if req.Category != nil {
updateBody["category"] = *req.Category
}
if len(updateBody) == 0 {
@@ -1,11 +1,13 @@
package validation
type Create struct {
Name string `json:"name" validate:"required_strict,min=3"`
PoNumber string `json:"po_number" validate:"required,max=50"`
Category string `json:"category" validate:"required,max=50"`
}
type Update struct {
Name *string `json:"name,omitempty" validate:"omitempty"`
PoNumber *string `json:"po_number,omitempty" validate:"omitempty,max=50"`
Category *string `json:"category,omitempty" validate:"omitempty,max=50"`
}
type Query struct {
+17
View File
@@ -241,6 +241,23 @@ var MarketingApprovalSteps = map[approvalutils.ApprovalStep]string{
MarketingDeliveryOrder: "Delivery Order",
}
// -------------------------------------------------------------------
// Expense Approval
// -------------------------------------------------------------------
const (
ApprovalWorkflowExpense approvalutils.ApprovalWorkflowKey = approvalutils.ApprovalWorkflowKey("EXPENSES")
ExpenseStepPengajuan approvalutils.ApprovalStep = 1
ExpenseStepManager approvalutils.ApprovalStep = 2
ExpenseStepFinance approvalutils.ApprovalStep = 3
)
var ExpenseApprovalSteps = map[approvalutils.ApprovalStep]string{
ExpenseStepPengajuan: "Pengajuan",
ExpenseStepManager: "Manager",
ExpenseStepFinance: "Finance",
}
// -------------------------------------------------------------------
// Validators
// -------------------------------------------------------------------