mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-25 07:45:44 +00:00
Feat[BE]: integrate document service into expense module and update related DTOs for document handling
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
package entities
|
package entities
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
@@ -13,8 +12,6 @@ type Expense struct {
|
|||||||
SupplierId uint64 `gorm:""`
|
SupplierId uint64 `gorm:""`
|
||||||
Category string `gorm:"type:varchar(50);not null"`
|
Category string `gorm:"type:varchar(50);not null"`
|
||||||
PoNumber string `gorm:"type:varchar(50)"`
|
PoNumber string `gorm:"type:varchar(50)"`
|
||||||
DocumentPath sql.NullString `gorm:"type:json"`
|
|
||||||
RealizationDocumentPath sql.NullString `gorm:"type:json;column:realization_document_path"`
|
|
||||||
RealizationDate time.Time `gorm:"type:date;column:realization_date"`
|
RealizationDate time.Time `gorm:"type:date;column:realization_date"`
|
||||||
TransactionDate time.Time `gorm:"type:date;not null"`
|
TransactionDate time.Time `gorm:"type:date;not null"`
|
||||||
Notes string `gorm:"type:text;column:notes"`
|
Notes string `gorm:"type:text;column:notes"`
|
||||||
@@ -23,8 +20,10 @@ type Expense struct {
|
|||||||
UpdatedAt time.Time `gorm:"autoUpdateTime"`
|
UpdatedAt time.Time `gorm:"autoUpdateTime"`
|
||||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||||||
|
|
||||||
Supplier *Supplier `gorm:"foreignKey:SupplierId;references:Id"`
|
Supplier *Supplier `gorm:"foreignKey:SupplierId;references:Id"`
|
||||||
CreatedUser *User `gorm:"foreignKey:CreatedBy;references:Id"`
|
CreatedUser *User `gorm:"foreignKey:CreatedBy;references:Id"`
|
||||||
Nonstocks []ExpenseNonstock `gorm:"foreignKey:ExpenseId;references:Id"`
|
Nonstocks []ExpenseNonstock `gorm:"foreignKey:ExpenseId;references:Id"`
|
||||||
LatestApproval *Approval `gorm:"-" json:"latest_approval,omitempty"`
|
Documents []Document `gorm:"foreignKey:DocumentableId;references:Id"`
|
||||||
|
RealizationDocuments []Document `gorm:"foreignKey:DocumentableId;references:Id"`
|
||||||
|
LatestApproval *Approval `gorm:"-" json:"latest_approval,omitempty"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package dto
|
package dto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||||
@@ -41,8 +40,8 @@ type ExpenseListDTO struct {
|
|||||||
|
|
||||||
type ExpenseDetailDTO struct {
|
type ExpenseDetailDTO struct {
|
||||||
ExpenseBaseDTO
|
ExpenseBaseDTO
|
||||||
Documents []DocumentDTO `json:"documents,omitempty"`
|
Documents []DocumentDTO `json:"documents"`
|
||||||
RealizationDocs []DocumentDTO `json:"realization_docs,omitempty"`
|
RealizationDocs []DocumentDTO `json:"realization_docs"`
|
||||||
Kandangs []KandangGroupDTO `json:"kandangs,omitempty"`
|
Kandangs []KandangGroupDTO `json:"kandangs,omitempty"`
|
||||||
TotalPengajuan float64 `json:"total_pengajuan"`
|
TotalPengajuan float64 `json:"total_pengajuan"`
|
||||||
TotalRealisasi float64 `json:"total_realisasi"`
|
TotalRealisasi float64 `json:"total_realisasi"`
|
||||||
@@ -179,12 +178,20 @@ func ToExpenseDetailDTO(e *entity.Expense) ExpenseDetailDTO {
|
|||||||
var pengajuans []ExpenseNonstockDTO
|
var pengajuans []ExpenseNonstockDTO
|
||||||
var realisasi []ExpenseRealizationDTO
|
var realisasi []ExpenseRealizationDTO
|
||||||
|
|
||||||
if e.DocumentPath.Valid && e.DocumentPath.String != "" {
|
// Map documents from Document service
|
||||||
json.Unmarshal([]byte(e.DocumentPath.String), &documents)
|
for _, doc := range e.Documents {
|
||||||
|
documents = append(documents, DocumentDTO{
|
||||||
|
ID: uint64(doc.Id),
|
||||||
|
Path: doc.Path,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if e.RealizationDocumentPath.Valid && e.RealizationDocumentPath.String != "" {
|
// Map realization documents from Document service
|
||||||
json.Unmarshal([]byte(e.RealizationDocumentPath.String), &realizationDocs)
|
for _, doc := range e.RealizationDocuments {
|
||||||
|
realizationDocs = append(realizationDocs, DocumentDTO{
|
||||||
|
ID: uint64(doc.Id),
|
||||||
|
Path: doc.Path,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(e.Nonstocks) > 0 {
|
if len(e.Nonstocks) > 0 {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package expenses
|
package expenses
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/go-playground/validator/v10"
|
"github.com/go-playground/validator/v10"
|
||||||
@@ -31,15 +32,20 @@ func (ExpenseModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate *
|
|||||||
approvalRepo := commonRepo.NewApprovalRepository(db)
|
approvalRepo := commonRepo.NewApprovalRepository(db)
|
||||||
realizationRepo := rExpense.NewExpenseRealizationRepository(db)
|
realizationRepo := rExpense.NewExpenseRealizationRepository(db)
|
||||||
projectFlockKandangRepo := rProjectFlockKandang.NewProjectFlockKandangRepository(db)
|
projectFlockKandangRepo := rProjectFlockKandang.NewProjectFlockKandangRepository(db)
|
||||||
|
documentRepo := commonRepo.NewDocumentRepository(db)
|
||||||
|
|
||||||
approvalSvc := commonSvc.NewApprovalService(approvalRepo)
|
approvalSvc := commonSvc.NewApprovalService(approvalRepo)
|
||||||
|
documentSvc, err := commonSvc.NewDocumentServiceFromConfig(context.Background(), documentRepo)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("failed to create document service: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
// Register workflow steps for EXPENSES approval
|
// Register workflow steps for EXPENSES approval
|
||||||
if err := approvalSvc.RegisterWorkflowSteps(utils.ApprovalWorkflowExpense, utils.ExpenseApprovalSteps); err != nil {
|
if err := approvalSvc.RegisterWorkflowSteps(utils.ApprovalWorkflowExpense, utils.ExpenseApprovalSteps); err != nil {
|
||||||
panic(fmt.Sprintf("failed to register expense approval workflow: %v", err))
|
panic(fmt.Sprintf("failed to register expense approval workflow: %v", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
expenseService := sExpense.NewExpenseService(expenseRepo, supplierRepo, nonstockRepo, approvalSvc, realizationRepo, projectFlockKandangRepo, validate)
|
expenseService := sExpense.NewExpenseService(expenseRepo, supplierRepo, nonstockRepo, approvalSvc, realizationRepo, projectFlockKandangRepo, documentSvc, validate)
|
||||||
userService := sUser.NewUserService(userRepo, validate)
|
userService := sUser.NewUserService(userRepo, validate)
|
||||||
|
|
||||||
ExpenseRoutes(router, userService, expenseService)
|
ExpenseRoutes(router, userService, expenseService)
|
||||||
|
|||||||
@@ -2,11 +2,8 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"mime/multipart"
|
|
||||||
|
|
||||||
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"
|
||||||
@@ -49,9 +46,10 @@ type expenseService struct {
|
|||||||
ApprovalSvc commonSvc.ApprovalService
|
ApprovalSvc commonSvc.ApprovalService
|
||||||
RealizationRepository repository.ExpenseRealizationRepository
|
RealizationRepository repository.ExpenseRealizationRepository
|
||||||
ProjectFlockKandangRepo projectFlockKandangRepo.ProjectFlockKandangRepository
|
ProjectFlockKandangRepo projectFlockKandangRepo.ProjectFlockKandangRepository
|
||||||
|
DocumentSvc commonSvc.DocumentService
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewExpenseService(repo repository.ExpenseRepository, supplierRepo supplierRepo.SupplierRepository, nonstockRepo nonstockRepo.NonstockRepository, approvalSvc commonSvc.ApprovalService, realizationRepo repository.ExpenseRealizationRepository, projectFlockKandangRepo projectFlockKandangRepo.ProjectFlockKandangRepository, validate *validator.Validate) ExpenseService {
|
func NewExpenseService(repo repository.ExpenseRepository, supplierRepo supplierRepo.SupplierRepository, nonstockRepo nonstockRepo.NonstockRepository, approvalSvc commonSvc.ApprovalService, realizationRepo repository.ExpenseRealizationRepository, projectFlockKandangRepo projectFlockKandangRepo.ProjectFlockKandangRepository, documentSvc commonSvc.DocumentService, validate *validator.Validate) ExpenseService {
|
||||||
return &expenseService{
|
return &expenseService{
|
||||||
Log: utils.Log,
|
Log: utils.Log,
|
||||||
Validate: validate,
|
Validate: validate,
|
||||||
@@ -61,6 +59,7 @@ func NewExpenseService(repo repository.ExpenseRepository, supplierRepo supplierR
|
|||||||
ApprovalSvc: approvalSvc,
|
ApprovalSvc: approvalSvc,
|
||||||
RealizationRepository: realizationRepo,
|
RealizationRepository: realizationRepo,
|
||||||
ProjectFlockKandangRepo: projectFlockKandangRepo,
|
ProjectFlockKandangRepo: projectFlockKandangRepo,
|
||||||
|
DocumentSvc: documentSvc,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,7 +71,13 @@ func (s expenseService) withRelations(db *gorm.DB) *gorm.DB {
|
|||||||
Preload("Nonstocks.Realization").
|
Preload("Nonstocks.Realization").
|
||||||
Preload("Nonstocks.ProjectFlockKandang.Kandang.Location").
|
Preload("Nonstocks.ProjectFlockKandang.Kandang.Location").
|
||||||
Preload("Nonstocks.Kandang").
|
Preload("Nonstocks.Kandang").
|
||||||
Preload("Nonstocks.Kandang.Location")
|
Preload("Nonstocks.Kandang.Location").
|
||||||
|
Preload("Documents", func(db *gorm.DB) *gorm.DB {
|
||||||
|
return db.Where("documentable_type = ?", string(utils.DocumentableTypeExpense))
|
||||||
|
}).
|
||||||
|
Preload("RealizationDocuments", func(db *gorm.DB) *gorm.DB {
|
||||||
|
return db.Where("documentable_type = ?", string(utils.DocumentableTypeExpenseRealization))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s expenseService) GetAll(c *fiber.Ctx, params *validation.Query) ([]expenseDto.ExpenseListDTO, int64, error) {
|
func (s expenseService) GetAll(c *fiber.Ctx, params *validation.Query) ([]expenseDto.ExpenseListDTO, int64, error) {
|
||||||
@@ -269,9 +274,23 @@ func (s *expenseService) CreateOne(c *fiber.Ctx, req *validation.Create) (*expen
|
|||||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to create initial approval")
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to create initial approval")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(req.Documents) > 0 {
|
if s.DocumentSvc != nil && len(req.Documents) > 0 {
|
||||||
if err := s.processDocuments(c, expenseRepoTx, uint(expense.Id), req.Documents, false); err != nil {
|
documentFiles := make([]commonSvc.DocumentFile, 0, len(req.Documents))
|
||||||
return err
|
for idx, file := range req.Documents {
|
||||||
|
documentFiles = append(documentFiles, commonSvc.DocumentFile{
|
||||||
|
File: file,
|
||||||
|
Type: string(utils.DocumentTypeExpense),
|
||||||
|
Index: &idx,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_, err := s.DocumentSvc.UploadDocuments(c.Context(), commonSvc.DocumentUploadRequest{
|
||||||
|
DocumentableType: string(utils.DocumentableTypeExpense),
|
||||||
|
DocumentableID: expense.Id,
|
||||||
|
CreatedBy: &createdByUint,
|
||||||
|
Files: documentFiles,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to upload expense documents")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -527,9 +546,23 @@ func (s expenseService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uint)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(req.Documents) > 0 {
|
if s.DocumentSvc != nil && len(req.Documents) > 0 {
|
||||||
if err := s.processDocuments(c, expenseRepoTx, id, req.Documents, false); err != nil {
|
documentFiles := make([]commonSvc.DocumentFile, 0, len(req.Documents))
|
||||||
return err
|
for idx, file := range req.Documents {
|
||||||
|
documentFiles = append(documentFiles, commonSvc.DocumentFile{
|
||||||
|
File: file,
|
||||||
|
Type: string(utils.DocumentTypeExpense),
|
||||||
|
Index: &idx,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_, err := s.DocumentSvc.UploadDocuments(c.Context(), commonSvc.DocumentUploadRequest{
|
||||||
|
DocumentableType: string(utils.DocumentableTypeExpense),
|
||||||
|
DocumentableID: uint64(id),
|
||||||
|
CreatedBy: &actorID,
|
||||||
|
Files: documentFiles,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to upload expense documents")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -658,9 +691,24 @@ func (s *expenseService) CreateRealization(c *fiber.Ctx, expenseID uint, req *va
|
|||||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update realization date")
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update realization date")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(req.Documents) > 0 {
|
if s.DocumentSvc != nil && len(req.Documents) > 0 {
|
||||||
if err := s.processDocuments(c, expenseRepoTx, expenseID, req.Documents, true); err != nil {
|
documentFiles := make([]commonSvc.DocumentFile, 0, len(req.Documents))
|
||||||
return err
|
for idx, file := range req.Documents {
|
||||||
|
documentFiles = append(documentFiles, commonSvc.DocumentFile{
|
||||||
|
File: file,
|
||||||
|
Type: string(utils.DocumentTypeExpenseRealization),
|
||||||
|
Index: &idx,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
actorID := uint(1) // TODO: replace with authenticated user id
|
||||||
|
_, err := s.DocumentSvc.UploadDocuments(c.Context(), commonSvc.DocumentUploadRequest{
|
||||||
|
DocumentableType: string(utils.DocumentableTypeExpenseRealization),
|
||||||
|
DocumentableID: uint64(expenseID),
|
||||||
|
CreatedBy: &actorID,
|
||||||
|
Files: documentFiles,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to upload expense realization documents")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -833,9 +881,24 @@ func (s *expenseService) UpdateRealization(c *fiber.Ctx, expenseID uint, req *va
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(req.Documents) > 0 {
|
if s.DocumentSvc != nil && len(req.Documents) > 0 {
|
||||||
if err := s.processDocuments(c, expenseRepoTx, expenseID, req.Documents, true); err != nil {
|
documentFiles := make([]commonSvc.DocumentFile, 0, len(req.Documents))
|
||||||
return err
|
for idx, file := range req.Documents {
|
||||||
|
documentFiles = append(documentFiles, commonSvc.DocumentFile{
|
||||||
|
File: file,
|
||||||
|
Type: string(utils.DocumentTypeExpenseRealization),
|
||||||
|
Index: &idx,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
actorID := uint(1) // TODO: replace with authenticated user id
|
||||||
|
_, err := s.DocumentSvc.UploadDocuments(c.Context(), commonSvc.DocumentUploadRequest{
|
||||||
|
DocumentableType: string(utils.DocumentableTypeExpenseRealization),
|
||||||
|
DocumentableID: uint64(expenseID),
|
||||||
|
CreatedBy: &actorID,
|
||||||
|
Files: documentFiles,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to upload expense realization documents")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -870,79 +933,6 @@ func (s *expenseService) UpdateRealization(c *fiber.Ctx, expenseID uint, req *va
|
|||||||
return responseDTO, nil
|
return responseDTO, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *expenseService) processDocuments(ctx *fiber.Ctx, expenseRepoTx repository.ExpenseRepository, expenseID uint, documents []*multipart.FileHeader, isRealization bool) error {
|
|
||||||
|
|
||||||
if len(documents) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var existingDocuments []expenseDto.DocumentDTO
|
|
||||||
var fieldName string
|
|
||||||
|
|
||||||
if isRealization {
|
|
||||||
fieldName = "realization_document_path"
|
|
||||||
} else {
|
|
||||||
fieldName = "document_path"
|
|
||||||
}
|
|
||||||
|
|
||||||
expense, err := expenseRepoTx.GetByID(ctx.Context(), expenseID, nil)
|
|
||||||
if err != nil {
|
|
||||||
|
|
||||||
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get expense for document processing")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
|
|
||||||
var documentField sql.NullString
|
|
||||||
if isRealization {
|
|
||||||
documentField = expense.RealizationDocumentPath
|
|
||||||
} else {
|
|
||||||
documentField = expense.DocumentPath
|
|
||||||
}
|
|
||||||
|
|
||||||
if documentField.Valid && documentField.String != "" {
|
|
||||||
if err := json.Unmarshal([]byte(documentField.String), &existingDocuments); err != nil {
|
|
||||||
existingDocuments = []expenseDto.DocumentDTO{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var startID uint64 = 1
|
|
||||||
if len(existingDocuments) > 0 {
|
|
||||||
|
|
||||||
maxID := uint64(0)
|
|
||||||
for _, doc := range existingDocuments {
|
|
||||||
if doc.ID > maxID {
|
|
||||||
maxID = doc.ID
|
|
||||||
}
|
|
||||||
}
|
|
||||||
startID = maxID + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, doc := range documents {
|
|
||||||
documentPath := doc.Filename
|
|
||||||
|
|
||||||
document := expenseDto.DocumentDTO{
|
|
||||||
ID: startID + uint64(i),
|
|
||||||
Path: documentPath,
|
|
||||||
}
|
|
||||||
existingDocuments = append(existingDocuments, document)
|
|
||||||
}
|
|
||||||
|
|
||||||
documentJSON, err := json.Marshal(existingDocuments)
|
|
||||||
if err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to save documents")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := expenseRepoTx.PatchOne(ctx.Context(), expenseID, map[string]interface{}{
|
|
||||||
fieldName: string(documentJSON),
|
|
||||||
}, nil); err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to save documents")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *expenseService) DeleteDocument(ctx *fiber.Ctx, expenseID uint, documentID uint64, isRealization bool) error {
|
func (s *expenseService) DeleteDocument(ctx *fiber.Ctx, expenseID uint, documentID uint64, isRealization bool) error {
|
||||||
|
|
||||||
if err := commonSvc.EnsureRelations(ctx.Context(),
|
if err := commonSvc.EnsureRelations(ctx.Context(),
|
||||||
@@ -951,62 +941,40 @@ func (s *expenseService) DeleteDocument(ctx *fiber.Ctx, expenseID uint, document
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.Repository.DB().WithContext(ctx.Context()).Transaction(func(tx *gorm.DB) error {
|
if s.DocumentSvc == nil {
|
||||||
expenseRepoTx := repository.NewExpenseRepository(tx)
|
return fiber.NewError(fiber.StatusInternalServerError, "Document service not available")
|
||||||
|
}
|
||||||
|
|
||||||
expense, err := expenseRepoTx.GetByID(ctx.Context(), expenseID, nil)
|
// Verify document exists and belongs to the expense
|
||||||
if err != nil {
|
var documentableType string
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get expense for document deletion")
|
if isRealization {
|
||||||
|
documentableType = string(utils.DocumentableTypeExpenseRealization)
|
||||||
|
} else {
|
||||||
|
documentableType = string(utils.DocumentableTypeExpense)
|
||||||
|
}
|
||||||
|
|
||||||
|
documents, err := s.DocumentSvc.ListByTarget(ctx.Context(), documentableType, uint64(expenseID))
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve documents")
|
||||||
|
}
|
||||||
|
|
||||||
|
documentFound := false
|
||||||
|
var documentIDsToDelete []uint
|
||||||
|
for _, doc := range documents {
|
||||||
|
if uint64(doc.Id) == documentID {
|
||||||
|
documentFound = true
|
||||||
|
documentIDsToDelete = append(documentIDsToDelete, doc.Id)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var existingDocuments []expenseDto.DocumentDTO
|
if !documentFound {
|
||||||
var fieldName string
|
return fiber.NewError(fiber.StatusNotFound, "Document not found")
|
||||||
|
}
|
||||||
|
|
||||||
if isRealization {
|
// Delete document from database and storage
|
||||||
fieldName = "realization_document_path"
|
if err := s.DocumentSvc.DeleteDocuments(ctx.Context(), documentIDsToDelete, true); err != nil {
|
||||||
if expense.RealizationDocumentPath.Valid && expense.RealizationDocumentPath.String != "" {
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to delete document")
|
||||||
if err := json.Unmarshal([]byte(expense.RealizationDocumentPath.String), &existingDocuments); err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to parse existing realization documents")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fieldName = "document_path"
|
|
||||||
if expense.DocumentPath.Valid && expense.DocumentPath.String != "" {
|
|
||||||
if err := json.Unmarshal([]byte(expense.DocumentPath.String), &existingDocuments); err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to parse existing documents")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var updatedDocuments []expenseDto.DocumentDTO
|
|
||||||
documentFound := false
|
|
||||||
|
|
||||||
for _, doc := range existingDocuments {
|
|
||||||
if doc.ID == documentID {
|
|
||||||
documentFound = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
updatedDocuments = append(updatedDocuments, doc)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !documentFound {
|
|
||||||
return fiber.NewError(fiber.StatusNotFound, "Document not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
documentJSON, err := json.Marshal(updatedDocuments)
|
|
||||||
if err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to save documents")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := expenseRepoTx.PatchOne(ctx.Context(), expenseID, map[string]interface{}{
|
|
||||||
fieldName: string(documentJSON),
|
|
||||||
}, nil); err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to save documents")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package purchases
|
package purchases
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/go-playground/validator/v10"
|
"github.com/go-playground/validator/v10"
|
||||||
@@ -41,6 +42,11 @@ func (PurchaseModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate
|
|||||||
|
|
||||||
approvalRepo := commonRepo.NewApprovalRepository(db)
|
approvalRepo := commonRepo.NewApprovalRepository(db)
|
||||||
approvalService := commonSvc.NewApprovalService(approvalRepo)
|
approvalService := commonSvc.NewApprovalService(approvalRepo)
|
||||||
|
documentRepo := commonRepo.NewDocumentRepository(db)
|
||||||
|
documentSvc, err := commonSvc.NewDocumentServiceFromConfig(context.Background(), documentRepo)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("failed to create document service: %v", err))
|
||||||
|
}
|
||||||
if err := approvalService.RegisterWorkflowSteps(utils.ApprovalWorkflowPurchase, utils.PurchaseApprovalSteps); err != nil {
|
if err := approvalService.RegisterWorkflowSteps(utils.ApprovalWorkflowPurchase, utils.PurchaseApprovalSteps); err != nil {
|
||||||
panic(fmt.Sprintf("failed to register purchase approval workflow: %v", err))
|
panic(fmt.Sprintf("failed to register purchase approval workflow: %v", err))
|
||||||
}
|
}
|
||||||
@@ -54,6 +60,7 @@ func (PurchaseModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate
|
|||||||
approvalService,
|
approvalService,
|
||||||
expenseRealizationRepo,
|
expenseRealizationRepo,
|
||||||
projectFlockKandangRepository,
|
projectFlockKandangRepository,
|
||||||
|
documentSvc,
|
||||||
validate,
|
validate,
|
||||||
)
|
)
|
||||||
expenseBridge := service.NewExpenseBridge(
|
expenseBridge := service.NewExpenseBridge(
|
||||||
|
|||||||
@@ -408,9 +408,13 @@ type DocumentType string
|
|||||||
type DocumentableType string
|
type DocumentableType string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DocumentTypeTransfer DocumentType = "STOCK_TRANSFER_DOCUMENT"
|
DocumentTypeTransfer DocumentType = "STOCK_TRANSFER_DOCUMENT"
|
||||||
|
DocumentTypeExpense DocumentType = "EXPENSE_DOCUMENT"
|
||||||
|
DocumentTypeExpenseRealization DocumentType = "EXPENSE_REALIZATION_DOCUMENT"
|
||||||
|
|
||||||
DocumentableTypeTransfer DocumentableType = "STOCK_TRANSFER"
|
DocumentableTypeTransfer DocumentableType = "STOCK_TRANSFER"
|
||||||
|
DocumentableTypeExpense DocumentableType = "EXPENSE"
|
||||||
|
DocumentableTypeExpenseRealization DocumentableType = "EXPENSE_REALIZATION"
|
||||||
)
|
)
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user