add app setting letter out
This commit is contained in:
parent
6da48504fa
commit
592fa97be7
@ -131,64 +131,96 @@ type repositories struct {
|
|||||||
departmentRepo *repository.DepartmentRepository
|
departmentRepo *repository.DepartmentRepository
|
||||||
userDeptRepo *repository.UserDepartmentRepository
|
userDeptRepo *repository.UserDepartmentRepository
|
||||||
// letter outgoing repos
|
// letter outgoing repos
|
||||||
letterOutgoingRepo *repository.LetterOutgoingRepository
|
letterOutgoingRepo *repository.LetterOutgoingRepository
|
||||||
letterOutgoingAttachmentRepo *repository.LetterOutgoingAttachmentRepository
|
letterOutgoingAttachmentRepo *repository.LetterOutgoingAttachmentRepository
|
||||||
letterOutgoingRecipientRepo *repository.LetterOutgoingRecipientRepository
|
letterOutgoingRecipientRepo *repository.LetterOutgoingRecipientRepository
|
||||||
letterOutgoingDiscussionRepo *repository.LetterOutgoingDiscussionRepository
|
letterOutgoingDiscussionRepo *repository.LetterOutgoingDiscussionRepository
|
||||||
letterOutgoingDiscussionAttachRepo *repository.LetterOutgoingDiscussionAttachmentRepository
|
letterOutgoingDiscussionAttachRepo *repository.LetterOutgoingDiscussionAttachmentRepository
|
||||||
letterOutgoingActivityLogRepo *repository.LetterOutgoingActivityLogRepository
|
letterOutgoingActivityLogRepo *repository.LetterOutgoingActivityLogRepository
|
||||||
approvalFlowRepo *repository.ApprovalFlowRepository
|
approvalFlowRepo *repository.ApprovalFlowRepository
|
||||||
letterOutgoingApprovalRepo *repository.LetterOutgoingApprovalRepository
|
letterOutgoingApprovalRepo *repository.LetterOutgoingApprovalRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) initRepositories() *repositories {
|
func (a *App) initRepositories() *repositories {
|
||||||
return &repositories{
|
return &repositories{
|
||||||
userRepo: repository.NewUserRepository(a.db),
|
userRepo: repository.NewUserRepository(a.db),
|
||||||
userProfileRepo: repository.NewUserProfileRepository(a.db),
|
userProfileRepo: repository.NewUserProfileRepository(a.db),
|
||||||
titleRepo: repository.NewTitleRepository(a.db),
|
titleRepo: repository.NewTitleRepository(a.db),
|
||||||
rbacRepo: repository.NewRBACRepository(a.db),
|
rbacRepo: repository.NewRBACRepository(a.db),
|
||||||
labelRepo: repository.NewLabelRepository(a.db),
|
labelRepo: repository.NewLabelRepository(a.db),
|
||||||
priorityRepo: repository.NewPriorityRepository(a.db),
|
priorityRepo: repository.NewPriorityRepository(a.db),
|
||||||
institutionRepo: repository.NewInstitutionRepository(a.db),
|
institutionRepo: repository.NewInstitutionRepository(a.db),
|
||||||
dispRepo: repository.NewDispositionActionRepository(a.db),
|
dispRepo: repository.NewDispositionActionRepository(a.db),
|
||||||
letterRepo: repository.NewLetterIncomingRepository(a.db),
|
letterRepo: repository.NewLetterIncomingRepository(a.db),
|
||||||
letterAttachRepo: repository.NewLetterIncomingAttachmentRepository(a.db),
|
letterAttachRepo: repository.NewLetterIncomingAttachmentRepository(a.db),
|
||||||
activityLogRepo: repository.NewLetterIncomingActivityLogRepository(a.db),
|
activityLogRepo: repository.NewLetterIncomingActivityLogRepository(a.db),
|
||||||
dispositionRouteRepo: repository.NewDispositionRouteRepository(a.db),
|
dispositionRouteRepo: repository.NewDispositionRouteRepository(a.db),
|
||||||
letterDispositionRepo: repository.NewLetterIncomingDispositionRepository(a.db),
|
letterDispositionRepo: repository.NewLetterIncomingDispositionRepository(a.db),
|
||||||
letterDispositionDeptRepo: repository.NewLetterIncomingDispositionDepartmentRepository(a.db),
|
letterDispositionDeptRepo: repository.NewLetterIncomingDispositionDepartmentRepository(a.db),
|
||||||
letterDispActionSelRepo: repository.NewLetterDispositionActionSelectionRepository(a.db),
|
letterDispActionSelRepo: repository.NewLetterDispositionActionSelectionRepository(a.db),
|
||||||
dispositionNoteRepo: repository.NewDispositionNoteRepository(a.db),
|
dispositionNoteRepo: repository.NewDispositionNoteRepository(a.db),
|
||||||
letterDiscussionRepo: repository.NewLetterDiscussionRepository(a.db),
|
letterDiscussionRepo: repository.NewLetterDiscussionRepository(a.db),
|
||||||
settingRepo: repository.NewAppSettingRepository(a.db),
|
settingRepo: repository.NewAppSettingRepository(a.db),
|
||||||
recipientRepo: repository.NewLetterIncomingRecipientRepository(a.db),
|
recipientRepo: repository.NewLetterIncomingRecipientRepository(a.db),
|
||||||
departmentRepo: repository.NewDepartmentRepository(a.db),
|
departmentRepo: repository.NewDepartmentRepository(a.db),
|
||||||
userDeptRepo: repository.NewUserDepartmentRepository(a.db),
|
userDeptRepo: repository.NewUserDepartmentRepository(a.db),
|
||||||
// letter outgoing repos
|
letterOutgoingRepo: repository.NewLetterOutgoingRepository(a.db),
|
||||||
letterOutgoingRepo: repository.NewLetterOutgoingRepository(a.db),
|
letterOutgoingAttachmentRepo: repository.NewLetterOutgoingAttachmentRepository(a.db),
|
||||||
letterOutgoingAttachmentRepo: repository.NewLetterOutgoingAttachmentRepository(a.db),
|
letterOutgoingRecipientRepo: repository.NewLetterOutgoingRecipientRepository(a.db),
|
||||||
letterOutgoingRecipientRepo: repository.NewLetterOutgoingRecipientRepository(a.db),
|
letterOutgoingDiscussionRepo: repository.NewLetterOutgoingDiscussionRepository(a.db),
|
||||||
letterOutgoingDiscussionRepo: repository.NewLetterOutgoingDiscussionRepository(a.db),
|
letterOutgoingDiscussionAttachRepo: repository.NewLetterOutgoingDiscussionAttachmentRepository(a.db),
|
||||||
letterOutgoingDiscussionAttachRepo: repository.NewLetterOutgoingDiscussionAttachmentRepository(a.db),
|
letterOutgoingActivityLogRepo: repository.NewLetterOutgoingActivityLogRepository(a.db),
|
||||||
letterOutgoingActivityLogRepo: repository.NewLetterOutgoingActivityLogRepository(a.db),
|
approvalFlowRepo: repository.NewApprovalFlowRepository(a.db),
|
||||||
approvalFlowRepo: repository.NewApprovalFlowRepository(a.db),
|
letterOutgoingApprovalRepo: repository.NewLetterOutgoingApprovalRepository(a.db),
|
||||||
letterOutgoingApprovalRepo: repository.NewLetterOutgoingApprovalRepository(a.db),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type processors struct {
|
type processors struct {
|
||||||
userProcessor *processor.UserProcessorImpl
|
userProcessor *processor.UserProcessorImpl
|
||||||
letterProcessor *processor.LetterProcessorImpl
|
letterProcessor *processor.LetterProcessorImpl
|
||||||
activityLogger *processor.ActivityLogProcessorImpl
|
letterOutgoingProcessor *processor.LetterOutgoingProcessorImpl
|
||||||
|
activityLogger *processor.ActivityLogProcessorImpl
|
||||||
|
letterNumberGenerator *processor.LetterNumberGeneratorImpl
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) initProcessors(cfg *config.Config, repos *repositories) *processors {
|
func (a *App) initProcessors(cfg *config.Config, repos *repositories) *processors {
|
||||||
txMgr := repository.NewTxManager(a.db)
|
txMgr := repository.NewTxManager(a.db)
|
||||||
activity := processor.NewActivityLogProcessor(repos.activityLogRepo)
|
activity := processor.NewActivityLogProcessor(repos.activityLogRepo)
|
||||||
|
|
||||||
|
// Create the letter number generator
|
||||||
|
letterNumberGen := processor.NewLetterNumberGenerator(repos.settingRepo)
|
||||||
|
|
||||||
|
// Create letter processors with the number generator
|
||||||
|
letterProc := processor.NewLetterProcessor(
|
||||||
|
repos.letterRepo, repos.letterAttachRepo, txMgr, activity,
|
||||||
|
repos.letterDispositionRepo, repos.letterDispositionDeptRepo,
|
||||||
|
repos.letterDispActionSelRepo, repos.dispositionNoteRepo,
|
||||||
|
repos.letterDiscussionRepo, repos.settingRepo,
|
||||||
|
repos.recipientRepo, repos.departmentRepo, repos.userDeptRepo,
|
||||||
|
repos.priorityRepo, repos.institutionRepo, repos.dispRepo,
|
||||||
|
letterNumberGen,
|
||||||
|
)
|
||||||
|
|
||||||
|
letterOutgoingProc := processor.NewLetterOutgoingProcessor(
|
||||||
|
a.db,
|
||||||
|
repos.letterOutgoingRepo,
|
||||||
|
repos.letterOutgoingAttachmentRepo,
|
||||||
|
repos.letterOutgoingRecipientRepo,
|
||||||
|
repos.letterOutgoingDiscussionRepo,
|
||||||
|
repos.letterOutgoingDiscussionAttachRepo,
|
||||||
|
repos.letterOutgoingActivityLogRepo,
|
||||||
|
repos.approvalFlowRepo,
|
||||||
|
repos.letterOutgoingApprovalRepo,
|
||||||
|
letterNumberGen,
|
||||||
|
txMgr,
|
||||||
|
)
|
||||||
|
|
||||||
return &processors{
|
return &processors{
|
||||||
userProcessor: processor.NewUserProcessor(repos.userRepo, repos.userProfileRepo),
|
userProcessor: processor.NewUserProcessor(repos.userRepo, repos.userProfileRepo),
|
||||||
letterProcessor: processor.NewLetterProcessor(repos.letterRepo, repos.letterAttachRepo, txMgr, activity, repos.letterDispositionRepo, repos.letterDispositionDeptRepo, repos.letterDispActionSelRepo, repos.dispositionNoteRepo, repos.letterDiscussionRepo, repos.settingRepo, repos.recipientRepo, repos.departmentRepo, repos.userDeptRepo, repos.priorityRepo, repos.institutionRepo, repos.dispRepo),
|
letterProcessor: letterProc,
|
||||||
activityLogger: activity,
|
letterOutgoingProcessor: letterOutgoingProc,
|
||||||
|
activityLogger: activity,
|
||||||
|
letterNumberGenerator: letterNumberGen,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,25 +249,14 @@ func (a *App) initServices(processors *processors, repos *repositories, cfg *con
|
|||||||
|
|
||||||
rbacSvc := service.NewRBACService(repos.rbacRepo)
|
rbacSvc := service.NewRBACService(repos.rbacRepo)
|
||||||
|
|
||||||
masterSvc := service.NewMasterService(repos.labelRepo, repos.priorityRepo, repos.institutionRepo, repos.dispRepo)
|
masterSvc := service.NewMasterService(repos.labelRepo, repos.priorityRepo, repos.institutionRepo, repos.dispRepo, repos.departmentRepo)
|
||||||
|
|
||||||
letterSvc := service.NewLetterService(processors.letterProcessor)
|
letterSvc := service.NewLetterService(processors.letterProcessor)
|
||||||
dispRouteSvc := service.NewDispositionRouteService(repos.dispositionRouteRepo)
|
dispRouteSvc := service.NewDispositionRouteService(repos.dispositionRouteRepo)
|
||||||
|
|
||||||
txManager := repository.NewTxManager(a.db)
|
txManager := repository.NewTxManager(a.db)
|
||||||
letterOutgoingSvc := service.NewLetterOutgoingService(
|
letterOutgoingSvc := service.NewLetterOutgoingService(processors.letterOutgoingProcessor)
|
||||||
a.db,
|
|
||||||
repos.letterOutgoingRepo,
|
|
||||||
repos.letterOutgoingAttachmentRepo,
|
|
||||||
repos.letterOutgoingRecipientRepo,
|
|
||||||
repos.letterOutgoingDiscussionRepo,
|
|
||||||
repos.letterOutgoingDiscussionAttachRepo,
|
|
||||||
repos.letterOutgoingActivityLogRepo,
|
|
||||||
repos.approvalFlowRepo,
|
|
||||||
repos.letterOutgoingApprovalRepo,
|
|
||||||
txManager,
|
|
||||||
)
|
|
||||||
|
|
||||||
approvalFlowStepRepo := repository.NewApprovalFlowStepRepository(a.db)
|
approvalFlowStepRepo := repository.NewApprovalFlowStepRepository(a.db)
|
||||||
approvalFlowSvc := service.NewApprovalFlowService(
|
approvalFlowSvc := service.NewApprovalFlowService(
|
||||||
a.db,
|
a.db,
|
||||||
|
|||||||
@ -6,6 +6,8 @@ const (
|
|||||||
SettingIncomingLetterPrefix = "INCOMING_LETTER_PREFIX"
|
SettingIncomingLetterPrefix = "INCOMING_LETTER_PREFIX"
|
||||||
SettingIncomingLetterSequence = "INCOMING_LETTER_SEQUENCE"
|
SettingIncomingLetterSequence = "INCOMING_LETTER_SEQUENCE"
|
||||||
SettingIncomingLetterRecipients = "INCOMING_LETTER_RECIPIENTS"
|
SettingIncomingLetterRecipients = "INCOMING_LETTER_RECIPIENTS"
|
||||||
|
SettingOutgoingLetterPrefix = "OUTGOING_LETTER_PREFIX"
|
||||||
|
SettingOutgoingLetterSequence = "OUTGOING_LETTER_SEQUENCE"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ErrorResponse struct {
|
type ErrorResponse struct {
|
||||||
|
|||||||
@ -27,8 +27,6 @@ type CreateOutgoingLetterRequest struct {
|
|||||||
PriorityID *uuid.UUID `json:"priority_id,omitempty"`
|
PriorityID *uuid.UUID `json:"priority_id,omitempty"`
|
||||||
ReceiverInstitutionID *uuid.UUID `json:"receiver_institution_id,omitempty"`
|
ReceiverInstitutionID *uuid.UUID `json:"receiver_institution_id,omitempty"`
|
||||||
IssueDate time.Time `json:"issue_date" validate:"required"`
|
IssueDate time.Time `json:"issue_date" validate:"required"`
|
||||||
ApprovalFlowID *uuid.UUID `json:"approval_flow_id,omitempty"`
|
|
||||||
Recipients []CreateOutgoingLetterRecipient `json:"recipients,omitempty"`
|
|
||||||
Attachments []CreateOutgoingLetterAttachment `json:"attachments,omitempty"`
|
Attachments []CreateOutgoingLetterAttachment `json:"attachments,omitempty"`
|
||||||
UserID uuid.UUID
|
UserID uuid.UUID
|
||||||
}
|
}
|
||||||
|
|||||||
@ -86,6 +86,19 @@ type DepartmentResponse struct {
|
|||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ListDepartmentsRequest struct {
|
||||||
|
Search string `json:"search,omitempty" form:"search"`
|
||||||
|
Page int `json:"page,omitempty" form:"page"`
|
||||||
|
Limit int `json:"limit,omitempty" form:"limit"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListDepartmentsResponse struct {
|
||||||
|
Departments []DepartmentResponse `json:"departments"`
|
||||||
|
Total int64 `json:"total"`
|
||||||
|
Page int `json:"page"`
|
||||||
|
Limit int `json:"limit"`
|
||||||
|
}
|
||||||
|
|
||||||
type UserProfileResponse struct {
|
type UserProfileResponse struct {
|
||||||
UserID uuid.UUID `json:"user_id"`
|
UserID uuid.UUID `json:"user_id"`
|
||||||
FullName string `json:"full_name"`
|
FullName string `json:"full_name"`
|
||||||
|
|||||||
@ -26,161 +26,241 @@ type LetterService interface {
|
|||||||
UpdateDiscussion(ctx context.Context, letterID uuid.UUID, discussionID uuid.UUID, req *contract.UpdateLetterDiscussionRequest) (*contract.LetterDiscussionResponse, error)
|
UpdateDiscussion(ctx context.Context, letterID uuid.UUID, discussionID uuid.UUID, req *contract.UpdateLetterDiscussionRequest) (*contract.LetterDiscussionResponse, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type LetterHandler struct{ svc LetterService }
|
type LetterHandler struct {
|
||||||
|
svc LetterService
|
||||||
|
}
|
||||||
|
|
||||||
func NewLetterHandler(svc LetterService) *LetterHandler { return &LetterHandler{svc: svc} }
|
func NewLetterHandler(svc LetterService) *LetterHandler {
|
||||||
|
return &LetterHandler{svc: svc}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper functions for common patterns
|
||||||
|
func (h *LetterHandler) parseUUID(c *gin.Context, param string) (uuid.UUID, bool) {
|
||||||
|
id, err := uuid.Parse(c.Param(param))
|
||||||
|
if err != nil {
|
||||||
|
h.respondError(c, http.StatusBadRequest, "invalid "+param)
|
||||||
|
return uuid.Nil, false
|
||||||
|
}
|
||||||
|
return id, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *LetterHandler) bindJSON(c *gin.Context, req interface{}) bool {
|
||||||
|
if err := c.ShouldBindJSON(req); err != nil {
|
||||||
|
h.respondError(c, http.StatusBadRequest, "invalid request body")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *LetterHandler) bindQuery(c *gin.Context, req interface{}) bool {
|
||||||
|
if err := c.ShouldBindQuery(req); err != nil {
|
||||||
|
h.respondError(c, http.StatusBadRequest, "invalid query parameters")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *LetterHandler) respondError(c *gin.Context, code int, message string) {
|
||||||
|
c.JSON(code, &contract.ErrorResponse{
|
||||||
|
Error: message,
|
||||||
|
Code: code,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *LetterHandler) respondSuccess(c *gin.Context, code int, data interface{}) {
|
||||||
|
c.JSON(code, contract.BuildSuccessResponse(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *LetterHandler) handleServiceError(c *gin.Context, err error) {
|
||||||
|
if err != nil {
|
||||||
|
h.respondError(c, http.StatusInternalServerError, err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (h *LetterHandler) CreateIncomingLetter(c *gin.Context) {
|
func (h *LetterHandler) CreateIncomingLetter(c *gin.Context) {
|
||||||
var req contract.CreateIncomingLetterRequest
|
var req contract.CreateIncomingLetterRequest
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if !h.bindJSON(c, &req) {
|
||||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid body", Code: http.StatusBadRequest})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := h.svc.CreateIncomingLetter(c.Request.Context(), &req)
|
resp, err := h.svc.CreateIncomingLetter(c.Request.Context(), &req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
h.handleServiceError(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.JSON(http.StatusCreated, contract.BuildSuccessResponse(resp))
|
|
||||||
|
h.respondSuccess(c, http.StatusCreated, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *LetterHandler) GetIncomingLetter(c *gin.Context) {
|
func (h *LetterHandler) GetIncomingLetter(c *gin.Context) {
|
||||||
id, err := uuid.Parse(c.Param("id"))
|
id, ok := h.parseUUID(c, "id")
|
||||||
if err != nil {
|
if !ok {
|
||||||
c.JSON(400, &contract.ErrorResponse{Error: "invalid id", Code: 400})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := h.svc.GetIncomingLetterByID(c.Request.Context(), id)
|
resp, err := h.svc.GetIncomingLetterByID(c.Request.Context(), id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(500, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
h.handleServiceError(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.JSON(200, contract.BuildSuccessResponse(resp))
|
|
||||||
|
h.respondSuccess(c, http.StatusOK, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *LetterHandler) ListIncomingLetters(c *gin.Context) {
|
func (h *LetterHandler) ListIncomingLetters(c *gin.Context) {
|
||||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
req := h.parseListRequest(c)
|
||||||
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "10"))
|
|
||||||
status := c.Query("status")
|
|
||||||
query := c.Query("q")
|
|
||||||
var statusPtr *string
|
|
||||||
var queryPtr *string
|
|
||||||
if status != "" {
|
|
||||||
statusPtr = &status
|
|
||||||
}
|
|
||||||
if query != "" {
|
|
||||||
queryPtr = &query
|
|
||||||
}
|
|
||||||
req := &contract.ListIncomingLettersRequest{Page: page, Limit: limit, Status: statusPtr, Query: queryPtr}
|
|
||||||
resp, err := h.svc.ListIncomingLetters(c.Request.Context(), req)
|
resp, err := h.svc.ListIncomingLetters(c.Request.Context(), req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(500, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
h.handleServiceError(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.JSON(200, contract.BuildSuccessResponse(resp))
|
|
||||||
|
h.respondSuccess(c, http.StatusOK, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *LetterHandler) parseListRequest(c *gin.Context) *contract.ListIncomingLettersRequest {
|
||||||
|
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||||
|
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "10"))
|
||||||
|
|
||||||
|
// Ensure valid pagination values
|
||||||
|
if page < 1 {
|
||||||
|
page = 1
|
||||||
|
}
|
||||||
|
if limit < 1 {
|
||||||
|
limit = 10
|
||||||
|
}
|
||||||
|
if limit > 100 {
|
||||||
|
limit = 100
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &contract.ListIncomingLettersRequest{
|
||||||
|
Page: page,
|
||||||
|
Limit: limit,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle optional query parameters
|
||||||
|
if status := c.Query("status"); status != "" {
|
||||||
|
req.Status = &status
|
||||||
|
}
|
||||||
|
if query := c.Query("q"); query != "" {
|
||||||
|
req.Query = &query
|
||||||
|
}
|
||||||
|
|
||||||
|
return req
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *LetterHandler) UpdateIncomingLetter(c *gin.Context) {
|
func (h *LetterHandler) UpdateIncomingLetter(c *gin.Context) {
|
||||||
id, err := uuid.Parse(c.Param("id"))
|
id, ok := h.parseUUID(c, "id")
|
||||||
if err != nil {
|
if !ok {
|
||||||
c.JSON(400, &contract.ErrorResponse{Error: "invalid id", Code: 400})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var req contract.UpdateIncomingLetterRequest
|
var req contract.UpdateIncomingLetterRequest
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if !h.bindJSON(c, &req) {
|
||||||
c.JSON(400, &contract.ErrorResponse{Error: "invalid body", Code: 400})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := h.svc.UpdateIncomingLetter(c.Request.Context(), id, &req)
|
resp, err := h.svc.UpdateIncomingLetter(c.Request.Context(), id, &req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(500, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
h.handleServiceError(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.JSON(200, contract.BuildSuccessResponse(resp))
|
|
||||||
|
h.respondSuccess(c, http.StatusOK, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *LetterHandler) DeleteIncomingLetter(c *gin.Context) {
|
func (h *LetterHandler) DeleteIncomingLetter(c *gin.Context) {
|
||||||
id, err := uuid.Parse(c.Param("id"))
|
id, ok := h.parseUUID(c, "id")
|
||||||
if err != nil {
|
if !ok {
|
||||||
c.JSON(400, &contract.ErrorResponse{Error: "invalid id", Code: 400})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := h.svc.SoftDeleteIncomingLetter(c.Request.Context(), id); err != nil {
|
if err := h.svc.SoftDeleteIncomingLetter(c.Request.Context(), id); err != nil {
|
||||||
c.JSON(500, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
h.handleServiceError(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.JSON(200, &contract.SuccessResponse{Message: "deleted"})
|
|
||||||
|
h.respondSuccess(c, http.StatusOK, &contract.SuccessResponse{
|
||||||
|
Message: "Letter deleted successfully",
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *LetterHandler) CreateDispositions(c *gin.Context) {
|
func (h *LetterHandler) CreateDispositions(c *gin.Context) {
|
||||||
appCtx := appcontext.FromGinContext(c.Request.Context())
|
|
||||||
var req contract.CreateLetterDispositionRequest
|
var req contract.CreateLetterDispositionRequest
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if !h.bindJSON(c, &req) {
|
||||||
c.JSON(400, &contract.ErrorResponse{Error: "invalid body", Code: 400})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Extract department ID from context
|
||||||
|
appCtx := appcontext.FromGinContext(c.Request.Context())
|
||||||
req.FromDepartment = appCtx.DepartmentID
|
req.FromDepartment = appCtx.DepartmentID
|
||||||
|
|
||||||
resp, err := h.svc.CreateDispositions(c.Request.Context(), &req)
|
resp, err := h.svc.CreateDispositions(c.Request.Context(), &req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(500, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
h.handleServiceError(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.JSON(201, contract.BuildSuccessResponse(resp))
|
|
||||||
|
h.respondSuccess(c, http.StatusCreated, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *LetterHandler) GetEnhancedDispositionsByLetter(c *gin.Context) {
|
func (h *LetterHandler) GetEnhancedDispositionsByLetter(c *gin.Context) {
|
||||||
letterID, err := uuid.Parse(c.Param("letter_id"))
|
letterID, ok := h.parseUUID(c, "letter_id")
|
||||||
if err != nil {
|
if !ok {
|
||||||
c.JSON(400, &contract.ErrorResponse{Error: "invalid letter_id", Code: 400})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := h.svc.GetEnhancedDispositionsByLetter(c.Request.Context(), letterID)
|
resp, err := h.svc.GetEnhancedDispositionsByLetter(c.Request.Context(), letterID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(500, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
h.handleServiceError(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.JSON(200, contract.BuildSuccessResponse(resp))
|
|
||||||
|
h.respondSuccess(c, http.StatusOK, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *LetterHandler) CreateDiscussion(c *gin.Context) {
|
func (h *LetterHandler) CreateDiscussion(c *gin.Context) {
|
||||||
letterID, err := uuid.Parse(c.Param("letter_id"))
|
letterID, ok := h.parseUUID(c, "letter_id")
|
||||||
if err != nil {
|
if !ok {
|
||||||
c.JSON(400, &contract.ErrorResponse{Error: "invalid letter_id", Code: 400})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var req contract.CreateLetterDiscussionRequest
|
var req contract.CreateLetterDiscussionRequest
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if !h.bindJSON(c, &req) {
|
||||||
c.JSON(400, &contract.ErrorResponse{Error: "invalid body", Code: 400})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := h.svc.CreateDiscussion(c.Request.Context(), letterID, &req)
|
resp, err := h.svc.CreateDiscussion(c.Request.Context(), letterID, &req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(500, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
h.handleServiceError(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.JSON(201, contract.BuildSuccessResponse(resp))
|
|
||||||
|
h.respondSuccess(c, http.StatusCreated, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *LetterHandler) UpdateDiscussion(c *gin.Context) {
|
func (h *LetterHandler) UpdateDiscussion(c *gin.Context) {
|
||||||
letterID, err := uuid.Parse(c.Param("letter_id"))
|
letterID, ok := h.parseUUID(c, "letter_id")
|
||||||
if err != nil {
|
if !ok {
|
||||||
c.JSON(400, &contract.ErrorResponse{Error: "invalid letter_id", Code: 400})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
discussionID, err := uuid.Parse(c.Param("discussion_id"))
|
|
||||||
if err != nil {
|
discussionID, ok := h.parseUUID(c, "discussion_id")
|
||||||
c.JSON(400, &contract.ErrorResponse{Error: "invalid discussion_id", Code: 400})
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var req contract.UpdateLetterDiscussionRequest
|
var req contract.UpdateLetterDiscussionRequest
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if !h.bindJSON(c, &req) {
|
||||||
c.JSON(400, &contract.ErrorResponse{Error: "invalid body", Code: 400})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := h.svc.UpdateDiscussion(c.Request.Context(), letterID, discussionID, &req)
|
resp, err := h.svc.UpdateDiscussion(c.Request.Context(), letterID, discussionID, &req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(500, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
h.handleServiceError(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.JSON(200, contract.BuildSuccessResponse(resp))
|
|
||||||
|
h.respondSuccess(c, http.StatusOK, resp)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,6 +30,8 @@ type MasterService interface {
|
|||||||
UpdateDispositionAction(ctx context.Context, id uuid.UUID, req *contract.UpdateDispositionActionRequest) (*contract.DispositionActionResponse, error)
|
UpdateDispositionAction(ctx context.Context, id uuid.UUID, req *contract.UpdateDispositionActionRequest) (*contract.DispositionActionResponse, error)
|
||||||
DeleteDispositionAction(ctx context.Context, id uuid.UUID) error
|
DeleteDispositionAction(ctx context.Context, id uuid.UUID) error
|
||||||
ListDispositionActions(ctx context.Context) (*contract.ListDispositionActionsResponse, error)
|
ListDispositionActions(ctx context.Context) (*contract.ListDispositionActionsResponse, error)
|
||||||
|
|
||||||
|
ListDepartments(ctx context.Context, req *contract.ListDepartmentsRequest) (*contract.ListDepartmentsResponse, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type MasterHandler struct{ svc MasterService }
|
type MasterHandler struct{ svc MasterService }
|
||||||
@ -256,3 +258,21 @@ func (h *MasterHandler) ListDispositionActions(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
c.JSON(200, contract.BuildSuccessResponse(resp))
|
c.JSON(200, contract.BuildSuccessResponse(resp))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Departments
|
||||||
|
func (h *MasterHandler) ListDepartments(c *gin.Context) {
|
||||||
|
var req contract.ListDepartmentsRequest
|
||||||
|
|
||||||
|
// Parse query parameters
|
||||||
|
if err := c.ShouldBindQuery(&req); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid query parameters", Code: 400})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := h.svc.ListDepartments(c.Request.Context(), &req)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, contract.BuildSuccessResponse(resp))
|
||||||
|
}
|
||||||
|
|||||||
71
internal/processor/letter_number_generator.go
Normal file
71
internal/processor/letter_number_generator.go
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
package processor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"eslogad-be/internal/entities"
|
||||||
|
"eslogad-be/internal/repository"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LetterNumberGenerator handles generation of letter numbers with configurable prefix and sequence
|
||||||
|
type LetterNumberGenerator interface {
|
||||||
|
GenerateNumber(ctx context.Context, prefixKey, sequenceKey string, defaultPrefix string) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type LetterNumberGeneratorImpl struct {
|
||||||
|
settingRepo *repository.AppSettingRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLetterNumberGenerator(settingRepo *repository.AppSettingRepository) *LetterNumberGeneratorImpl {
|
||||||
|
return &LetterNumberGeneratorImpl{
|
||||||
|
settingRepo: settingRepo,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *LetterNumberGeneratorImpl) GenerateNumber(ctx context.Context, prefixKey, sequenceKey string, defaultPrefix string) (string, error) {
|
||||||
|
prefix := defaultPrefix
|
||||||
|
if s, err := g.settingRepo.Get(ctx, prefixKey); err == nil {
|
||||||
|
if v, ok := s.Value["value"].(string); ok && v != "" {
|
||||||
|
prefix = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
seq := 0
|
||||||
|
if s, err := g.settingRepo.Get(ctx, sequenceKey); err == nil {
|
||||||
|
if v, ok := s.Value["value"].(float64); ok {
|
||||||
|
seq = int(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
seq = seq + 1
|
||||||
|
|
||||||
|
letterNumber := fmt.Sprintf("%s%04d", prefix, seq)
|
||||||
|
|
||||||
|
if err := g.settingRepo.Upsert(ctx, sequenceKey, entities.JSONB{"value": seq}); err != nil {
|
||||||
|
}
|
||||||
|
|
||||||
|
return letterNumber, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCurrentSequence returns the current sequence number for a given key
|
||||||
|
func (g *LetterNumberGeneratorImpl) GetCurrentSequence(ctx context.Context, sequenceKey string) (int, error) {
|
||||||
|
seq := 0
|
||||||
|
if s, err := g.settingRepo.Get(ctx, sequenceKey); err == nil {
|
||||||
|
if v, ok := s.Value["value"].(float64); ok {
|
||||||
|
seq = int(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return seq, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPrefix returns the configured prefix for a given key
|
||||||
|
func (g *LetterNumberGeneratorImpl) GetPrefix(ctx context.Context, prefixKey string, defaultPrefix string) string {
|
||||||
|
prefix := defaultPrefix
|
||||||
|
if s, err := g.settingRepo.Get(ctx, prefixKey); err == nil {
|
||||||
|
if v, ok := s.Value["value"].(string); ok && v != "" {
|
||||||
|
prefix = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return prefix
|
||||||
|
}
|
||||||
539
internal/processor/letter_outgoing_processor.go
Normal file
539
internal/processor/letter_outgoing_processor.go
Normal file
@ -0,0 +1,539 @@
|
|||||||
|
package processor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"eslogad-be/internal/contract"
|
||||||
|
"eslogad-be/internal/entities"
|
||||||
|
"eslogad-be/internal/repository"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LetterOutgoingProcessor interface {
|
||||||
|
CreateOutgoingLetter(ctx context.Context, letter *entities.LetterOutgoing, attachments []entities.LetterOutgoingAttachment, userID, departmentID uuid.UUID) error
|
||||||
|
GetOutgoingLetterByID(ctx context.Context, id uuid.UUID) (*entities.LetterOutgoing, error)
|
||||||
|
ListOutgoingLetters(ctx context.Context, filter repository.ListOutgoingLettersFilter, limit, offset int) ([]entities.LetterOutgoing, int64, error)
|
||||||
|
UpdateOutgoingLetter(ctx context.Context, letter *entities.LetterOutgoing, userID uuid.UUID) error
|
||||||
|
DeleteOutgoingLetter(ctx context.Context, id uuid.UUID, userID uuid.UUID) error
|
||||||
|
|
||||||
|
UpdateLetterStatus(ctx context.Context, letterID uuid.UUID, status entities.LetterOutgoingStatus, userID uuid.UUID, fromStatus, toStatus *string) error
|
||||||
|
|
||||||
|
ProcessApprovalSubmission(ctx context.Context, letterID uuid.UUID, approvalFlowID uuid.UUID, userID uuid.UUID) error
|
||||||
|
ProcessApproval(ctx context.Context, letterID uuid.UUID, approval *entities.LetterOutgoingApproval, userID uuid.UUID, allApproved bool) error
|
||||||
|
ProcessRejection(ctx context.Context, letterID uuid.UUID, approval *entities.LetterOutgoingApproval, userID uuid.UUID) error
|
||||||
|
|
||||||
|
AddRecipients(ctx context.Context, letterID uuid.UUID, recipients []entities.LetterOutgoingRecipient, userID uuid.UUID) error
|
||||||
|
UpdateRecipient(ctx context.Context, recipient *entities.LetterOutgoingRecipient) error
|
||||||
|
RemoveRecipient(ctx context.Context, letterID uuid.UUID, recipientID uuid.UUID, userID uuid.UUID) error
|
||||||
|
|
||||||
|
AddAttachments(ctx context.Context, letterID uuid.UUID, attachments []entities.LetterOutgoingAttachment, userID uuid.UUID) error
|
||||||
|
RemoveAttachment(ctx context.Context, letterID uuid.UUID, attachmentID uuid.UUID, userID uuid.UUID) error
|
||||||
|
|
||||||
|
CreateDiscussion(ctx context.Context, discussion *entities.LetterOutgoingDiscussion, attachments []entities.LetterOutgoingDiscussionAttachment, userID uuid.UUID) error
|
||||||
|
GetDiscussionByID(ctx context.Context, id uuid.UUID) (*entities.LetterOutgoingDiscussion, error)
|
||||||
|
UpdateDiscussion(ctx context.Context, discussion *entities.LetterOutgoingDiscussion) error
|
||||||
|
DeleteDiscussion(ctx context.Context, id uuid.UUID) error
|
||||||
|
|
||||||
|
GetApprovalsByLetter(ctx context.Context, letterID uuid.UUID) ([]entities.LetterOutgoingApproval, error)
|
||||||
|
GetApprovalFlow(ctx context.Context, flowID uuid.UUID) (*entities.ApprovalFlow, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type LetterOutgoingProcessorImpl struct {
|
||||||
|
db *gorm.DB
|
||||||
|
letterRepo *repository.LetterOutgoingRepository
|
||||||
|
attachmentRepo *repository.LetterOutgoingAttachmentRepository
|
||||||
|
recipientRepo *repository.LetterOutgoingRecipientRepository
|
||||||
|
discussionRepo *repository.LetterOutgoingDiscussionRepository
|
||||||
|
discussionAttachmentRepo *repository.LetterOutgoingDiscussionAttachmentRepository
|
||||||
|
activityLogRepo *repository.LetterOutgoingActivityLogRepository
|
||||||
|
approvalFlowRepo *repository.ApprovalFlowRepository
|
||||||
|
approvalRepo *repository.LetterOutgoingApprovalRepository
|
||||||
|
numberGenerator *LetterNumberGeneratorImpl
|
||||||
|
txManager *repository.TxManager
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLetterOutgoingProcessor(
|
||||||
|
db *gorm.DB,
|
||||||
|
letterRepo *repository.LetterOutgoingRepository,
|
||||||
|
attachmentRepo *repository.LetterOutgoingAttachmentRepository,
|
||||||
|
recipientRepo *repository.LetterOutgoingRecipientRepository,
|
||||||
|
discussionRepo *repository.LetterOutgoingDiscussionRepository,
|
||||||
|
discussionAttachmentRepo *repository.LetterOutgoingDiscussionAttachmentRepository,
|
||||||
|
activityLogRepo *repository.LetterOutgoingActivityLogRepository,
|
||||||
|
approvalFlowRepo *repository.ApprovalFlowRepository,
|
||||||
|
approvalRepo *repository.LetterOutgoingApprovalRepository,
|
||||||
|
numberGenerator *LetterNumberGeneratorImpl,
|
||||||
|
txManager *repository.TxManager,
|
||||||
|
) *LetterOutgoingProcessorImpl {
|
||||||
|
return &LetterOutgoingProcessorImpl{
|
||||||
|
db: db,
|
||||||
|
letterRepo: letterRepo,
|
||||||
|
attachmentRepo: attachmentRepo,
|
||||||
|
recipientRepo: recipientRepo,
|
||||||
|
discussionRepo: discussionRepo,
|
||||||
|
discussionAttachmentRepo: discussionAttachmentRepo,
|
||||||
|
activityLogRepo: activityLogRepo,
|
||||||
|
approvalFlowRepo: approvalFlowRepo,
|
||||||
|
approvalRepo: approvalRepo,
|
||||||
|
numberGenerator: numberGenerator,
|
||||||
|
txManager: txManager,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *LetterOutgoingProcessorImpl) CreateOutgoingLetter(ctx context.Context, letter *entities.LetterOutgoing, attachments []entities.LetterOutgoingAttachment, userID, departmentID uuid.UUID) error {
|
||||||
|
return p.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
|
||||||
|
// Step 1: Assign approval flow from department if not provided
|
||||||
|
if err := p.assignApprovalFlowFromDepartment(txCtx, letter, departmentID); err != nil {
|
||||||
|
// Log error but continue - approval flow is optional
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: Set status based on approval flow
|
||||||
|
if letter.ApprovalFlowID != nil {
|
||||||
|
letter.Status = entities.LetterOutgoingStatusPendingApproval
|
||||||
|
} else {
|
||||||
|
letter.Status = entities.LetterOutgoingStatusApproved
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3: Generate and assign letter number
|
||||||
|
if err := p.assignLetterNumber(txCtx, letter); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 4: Create the letter
|
||||||
|
if err := p.letterRepo.Create(txCtx, letter); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 5: Create recipients from approval flow
|
||||||
|
if err := p.createRecipientsFromApprovalFlow(txCtx, letter); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 6: Create attachments
|
||||||
|
if err := p.createAttachments(txCtx, letter.ID, attachments); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 7: Log the activity
|
||||||
|
return p.logActivity(txCtx, letter.ID, entities.LetterOutgoingActionCreated, userID)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *LetterOutgoingProcessorImpl) assignApprovalFlowFromDepartment(ctx context.Context, letter *entities.LetterOutgoing, departmentID uuid.UUID) error {
|
||||||
|
if letter.ApprovalFlowID != nil || departmentID == uuid.Nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
flow, err := p.approvalFlowRepo.GetByDepartment(ctx, departmentID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if flow != nil {
|
||||||
|
letter.ApprovalFlowID = &flow.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *LetterOutgoingProcessorImpl) assignLetterNumber(ctx context.Context, letter *entities.LetterOutgoing) error {
|
||||||
|
letterNumber, err := p.numberGenerator.GenerateNumber(
|
||||||
|
ctx,
|
||||||
|
contract.SettingOutgoingLetterPrefix,
|
||||||
|
contract.SettingOutgoingLetterSequence,
|
||||||
|
"ESLO",
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
letter.LetterNumber = letterNumber
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *LetterOutgoingProcessorImpl) createRecipientsFromApprovalFlow(ctx context.Context, letter *entities.LetterOutgoing) error {
|
||||||
|
if letter.ApprovalFlowID == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
flow, err := p.approvalFlowRepo.Get(ctx, *letter.ApprovalFlowID)
|
||||||
|
if err != nil || flow == nil || len(flow.Steps) == 0 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
recipient := p.createRecipientFromApprovalStep(flow.Steps[0], letter.ID)
|
||||||
|
if recipient == nil {
|
||||||
|
return nil // No valid recipient could be created
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.recipientRepo.CreateBulk(ctx, []entities.LetterOutgoingRecipient{*recipient})
|
||||||
|
}
|
||||||
|
|
||||||
|
// createRecipientFromApprovalStep creates a recipient from an approval flow step
|
||||||
|
func (p *LetterOutgoingProcessorImpl) createRecipientFromApprovalStep(step entities.ApprovalFlowStep, letterID uuid.UUID) *entities.LetterOutgoingRecipient {
|
||||||
|
recipient := &entities.LetterOutgoingRecipient{
|
||||||
|
LetterID: letterID,
|
||||||
|
IsPrimary: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if step.ApproverUser != nil {
|
||||||
|
recipient.RecipientName = step.ApproverUser.Name
|
||||||
|
recipient.RecipientEmail = &step.ApproverUser.Email
|
||||||
|
|
||||||
|
// Extract position from user profile if available
|
||||||
|
if step.ApproverUser.Profile != nil && step.ApproverUser.Profile.JobTitle != nil {
|
||||||
|
recipient.RecipientPosition = step.ApproverUser.Profile.JobTitle
|
||||||
|
}
|
||||||
|
} else if step.ApproverRole != nil {
|
||||||
|
recipient.RecipientName = step.ApproverRole.Name
|
||||||
|
position := "Role: " + step.ApproverRole.Name
|
||||||
|
recipient.RecipientPosition = &position
|
||||||
|
} else {
|
||||||
|
return nil // No valid approver found
|
||||||
|
}
|
||||||
|
|
||||||
|
return recipient
|
||||||
|
}
|
||||||
|
|
||||||
|
// createAttachments creates letter attachments
|
||||||
|
func (p *LetterOutgoingProcessorImpl) createAttachments(ctx context.Context, letterID uuid.UUID, attachments []entities.LetterOutgoingAttachment) error {
|
||||||
|
if len(attachments) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update letter IDs for all attachments
|
||||||
|
for i := range attachments {
|
||||||
|
attachments[i].LetterID = letterID
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.attachmentRepo.CreateBulk(ctx, attachments)
|
||||||
|
}
|
||||||
|
|
||||||
|
// logActivity logs an activity for the letter
|
||||||
|
func (p *LetterOutgoingProcessorImpl) logActivity(ctx context.Context, letterID uuid.UUID, actionType string, userID uuid.UUID) error {
|
||||||
|
activityLog := &entities.LetterOutgoingActivityLog{
|
||||||
|
LetterID: letterID,
|
||||||
|
ActionType: actionType,
|
||||||
|
ActorUserID: &userID,
|
||||||
|
}
|
||||||
|
return p.activityLogRepo.Create(ctx, activityLog)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *LetterOutgoingProcessorImpl) GetOutgoingLetterByID(ctx context.Context, id uuid.UUID) (*entities.LetterOutgoing, error) {
|
||||||
|
return p.letterRepo.Get(ctx, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *LetterOutgoingProcessorImpl) ListOutgoingLetters(ctx context.Context, filter repository.ListOutgoingLettersFilter, limit, offset int) ([]entities.LetterOutgoing, int64, error) {
|
||||||
|
return p.letterRepo.List(ctx, filter, limit, offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *LetterOutgoingProcessorImpl) UpdateOutgoingLetter(ctx context.Context, letter *entities.LetterOutgoing, userID uuid.UUID) error {
|
||||||
|
return p.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
|
||||||
|
if err := p.letterRepo.Update(txCtx, letter); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
activityLog := &entities.LetterOutgoingActivityLog{
|
||||||
|
LetterID: letter.ID,
|
||||||
|
ActionType: entities.LetterOutgoingActionUpdated,
|
||||||
|
ActorUserID: &userID,
|
||||||
|
}
|
||||||
|
if err := p.activityLogRepo.Create(txCtx, activityLog); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *LetterOutgoingProcessorImpl) DeleteOutgoingLetter(ctx context.Context, id uuid.UUID, userID uuid.UUID) error {
|
||||||
|
return p.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
|
||||||
|
if err := p.letterRepo.SoftDelete(txCtx, id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
letter, _ := p.letterRepo.Get(txCtx, id)
|
||||||
|
activityLog := &entities.LetterOutgoingActivityLog{
|
||||||
|
LetterID: letter.ID,
|
||||||
|
ActionType: entities.LetterOutgoingActionDeleted,
|
||||||
|
ActorUserID: &userID,
|
||||||
|
}
|
||||||
|
if err := p.activityLogRepo.Create(txCtx, activityLog); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *LetterOutgoingProcessorImpl) UpdateLetterStatus(ctx context.Context, letterID uuid.UUID, status entities.LetterOutgoingStatus, userID uuid.UUID, fromStatus, toStatus *string) error {
|
||||||
|
return p.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
|
||||||
|
if err := p.letterRepo.UpdateStatus(txCtx, letterID, status); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
activityLog := &entities.LetterOutgoingActivityLog{
|
||||||
|
LetterID: letterID,
|
||||||
|
ActorUserID: &userID,
|
||||||
|
FromStatus: fromStatus,
|
||||||
|
ToStatus: toStatus,
|
||||||
|
}
|
||||||
|
|
||||||
|
switch status {
|
||||||
|
case entities.LetterOutgoingStatusPendingApproval:
|
||||||
|
activityLog.ActionType = entities.LetterOutgoingActionSubmittedApproval
|
||||||
|
case entities.LetterOutgoingStatusApproved:
|
||||||
|
activityLog.ActionType = entities.LetterOutgoingActionApproved
|
||||||
|
case entities.LetterOutgoingStatusSent:
|
||||||
|
activityLog.ActionType = entities.LetterOutgoingActionSent
|
||||||
|
case entities.LetterOutgoingStatusArchived:
|
||||||
|
activityLog.ActionType = entities.LetterOutgoingActionArchived
|
||||||
|
default:
|
||||||
|
activityLog.ActionType = entities.LetterOutgoingActionUpdated
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := p.activityLogRepo.Create(txCtx, activityLog); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *LetterOutgoingProcessorImpl) ProcessApprovalSubmission(ctx context.Context, letterID uuid.UUID, approvalFlowID uuid.UUID, userID uuid.UUID) error {
|
||||||
|
flow, err := p.approvalFlowRepo.Get(ctx, approvalFlowID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
|
||||||
|
approvals := make([]entities.LetterOutgoingApproval, len(flow.Steps))
|
||||||
|
for i, step := range flow.Steps {
|
||||||
|
approvals[i] = entities.LetterOutgoingApproval{
|
||||||
|
LetterID: letterID,
|
||||||
|
StepID: step.ID,
|
||||||
|
Status: entities.ApprovalStatusPending,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := p.approvalRepo.CreateBulk(txCtx, approvals); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := p.letterRepo.UpdateStatus(txCtx, letterID, entities.LetterOutgoingStatusPendingApproval); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fromStatus := string(entities.LetterOutgoingStatusDraft)
|
||||||
|
toStatus := string(entities.LetterOutgoingStatusPendingApproval)
|
||||||
|
activityLog := &entities.LetterOutgoingActivityLog{
|
||||||
|
LetterID: letterID,
|
||||||
|
ActionType: entities.LetterOutgoingActionSubmittedApproval,
|
||||||
|
ActorUserID: &userID,
|
||||||
|
FromStatus: &fromStatus,
|
||||||
|
ToStatus: &toStatus,
|
||||||
|
}
|
||||||
|
if err := p.activityLogRepo.Create(txCtx, activityLog); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *LetterOutgoingProcessorImpl) ProcessApproval(ctx context.Context, letterID uuid.UUID, approval *entities.LetterOutgoingApproval, userID uuid.UUID, allApproved bool) error {
|
||||||
|
return p.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
|
||||||
|
now := time.Now()
|
||||||
|
approval.Status = entities.ApprovalStatusApproved
|
||||||
|
approval.ApproverID = &userID
|
||||||
|
approval.ActedAt = &now
|
||||||
|
|
||||||
|
if err := p.approvalRepo.Update(txCtx, approval); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if allApproved {
|
||||||
|
if err := p.letterRepo.UpdateStatus(txCtx, letterID, entities.LetterOutgoingStatusApproved); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
activityLog := &entities.LetterOutgoingActivityLog{
|
||||||
|
LetterID: letterID,
|
||||||
|
ActionType: entities.LetterOutgoingActionApproved,
|
||||||
|
ActorUserID: &userID,
|
||||||
|
TargetID: &approval.ID,
|
||||||
|
}
|
||||||
|
if err := p.activityLogRepo.Create(txCtx, activityLog); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *LetterOutgoingProcessorImpl) ProcessRejection(ctx context.Context, letterID uuid.UUID, approval *entities.LetterOutgoingApproval, userID uuid.UUID) error {
|
||||||
|
return p.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
|
||||||
|
now := time.Now()
|
||||||
|
approval.Status = entities.ApprovalStatusRejected
|
||||||
|
approval.ApproverID = &userID
|
||||||
|
approval.ActedAt = &now
|
||||||
|
|
||||||
|
if err := p.approvalRepo.Update(txCtx, approval); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := p.letterRepo.UpdateStatus(txCtx, letterID, entities.LetterOutgoingStatusDraft); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fromStatus := string(entities.LetterOutgoingStatusPendingApproval)
|
||||||
|
toStatus := string(entities.LetterOutgoingStatusDraft)
|
||||||
|
activityLog := &entities.LetterOutgoingActivityLog{
|
||||||
|
LetterID: letterID,
|
||||||
|
ActionType: entities.LetterOutgoingActionRejected,
|
||||||
|
ActorUserID: &userID,
|
||||||
|
TargetID: &approval.ID,
|
||||||
|
FromStatus: &fromStatus,
|
||||||
|
ToStatus: &toStatus,
|
||||||
|
}
|
||||||
|
if err := p.activityLogRepo.Create(txCtx, activityLog); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *LetterOutgoingProcessorImpl) AddRecipients(ctx context.Context, letterID uuid.UUID, recipients []entities.LetterOutgoingRecipient, userID uuid.UUID) error {
|
||||||
|
return p.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
|
||||||
|
if err := p.recipientRepo.CreateBulk(txCtx, recipients); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
activityLog := &entities.LetterOutgoingActivityLog{
|
||||||
|
LetterID: letterID,
|
||||||
|
ActionType: entities.LetterOutgoingActionRecipientAdded,
|
||||||
|
ActorUserID: &userID,
|
||||||
|
}
|
||||||
|
if err := p.activityLogRepo.Create(txCtx, activityLog); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *LetterOutgoingProcessorImpl) UpdateRecipient(ctx context.Context, recipient *entities.LetterOutgoingRecipient) error {
|
||||||
|
return p.recipientRepo.Update(ctx, recipient)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *LetterOutgoingProcessorImpl) RemoveRecipient(ctx context.Context, letterID uuid.UUID, recipientID uuid.UUID, userID uuid.UUID) error {
|
||||||
|
return p.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
|
||||||
|
if err := p.recipientRepo.Delete(txCtx, recipientID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
activityLog := &entities.LetterOutgoingActivityLog{
|
||||||
|
LetterID: letterID,
|
||||||
|
ActionType: entities.LetterOutgoingActionRecipientRemoved,
|
||||||
|
ActorUserID: &userID,
|
||||||
|
TargetID: &recipientID,
|
||||||
|
}
|
||||||
|
if err := p.activityLogRepo.Create(txCtx, activityLog); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *LetterOutgoingProcessorImpl) AddAttachments(ctx context.Context, letterID uuid.UUID, attachments []entities.LetterOutgoingAttachment, userID uuid.UUID) error {
|
||||||
|
return p.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
|
||||||
|
if err := p.attachmentRepo.CreateBulk(txCtx, attachments); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
activityLog := &entities.LetterOutgoingActivityLog{
|
||||||
|
LetterID: letterID,
|
||||||
|
ActionType: entities.LetterOutgoingActionAttachmentAdded,
|
||||||
|
ActorUserID: &userID,
|
||||||
|
}
|
||||||
|
if err := p.activityLogRepo.Create(txCtx, activityLog); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *LetterOutgoingProcessorImpl) RemoveAttachment(ctx context.Context, letterID uuid.UUID, attachmentID uuid.UUID, userID uuid.UUID) error {
|
||||||
|
return p.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
|
||||||
|
if err := p.attachmentRepo.Delete(txCtx, attachmentID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
activityLog := &entities.LetterOutgoingActivityLog{
|
||||||
|
LetterID: letterID,
|
||||||
|
ActionType: entities.LetterOutgoingActionAttachmentRemoved,
|
||||||
|
ActorUserID: &userID,
|
||||||
|
TargetID: &attachmentID,
|
||||||
|
}
|
||||||
|
if err := p.activityLogRepo.Create(txCtx, activityLog); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *LetterOutgoingProcessorImpl) CreateDiscussion(ctx context.Context, discussion *entities.LetterOutgoingDiscussion, attachments []entities.LetterOutgoingDiscussionAttachment, userID uuid.UUID) error {
|
||||||
|
return p.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
|
||||||
|
if err := p.discussionRepo.Create(txCtx, discussion); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(attachments) > 0 {
|
||||||
|
if err := p.discussionAttachmentRepo.CreateBulk(txCtx, attachments); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
activityLog := &entities.LetterOutgoingActivityLog{
|
||||||
|
LetterID: discussion.LetterID,
|
||||||
|
ActionType: entities.LetterOutgoingActionDiscussionAdded,
|
||||||
|
ActorUserID: &userID,
|
||||||
|
TargetID: &discussion.ID,
|
||||||
|
}
|
||||||
|
if err := p.activityLogRepo.Create(txCtx, activityLog); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *LetterOutgoingProcessorImpl) GetDiscussionByID(ctx context.Context, id uuid.UUID) (*entities.LetterOutgoingDiscussion, error) {
|
||||||
|
return p.discussionRepo.Get(ctx, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *LetterOutgoingProcessorImpl) UpdateDiscussion(ctx context.Context, discussion *entities.LetterOutgoingDiscussion) error {
|
||||||
|
return p.discussionRepo.Update(ctx, discussion)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *LetterOutgoingProcessorImpl) DeleteDiscussion(ctx context.Context, id uuid.UUID) error {
|
||||||
|
return p.discussionRepo.Delete(ctx, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *LetterOutgoingProcessorImpl) GetApprovalsByLetter(ctx context.Context, letterID uuid.UUID) ([]entities.LetterOutgoingApproval, error) {
|
||||||
|
return p.approvalRepo.ListByLetter(ctx, letterID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *LetterOutgoingProcessorImpl) GetApprovalFlow(ctx context.Context, flowID uuid.UUID) (*entities.ApprovalFlow, error) {
|
||||||
|
return p.approvalFlowRepo.Get(ctx, flowID)
|
||||||
|
}
|
||||||
@ -2,7 +2,6 @@ package processor
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"eslogad-be/internal/appcontext"
|
"eslogad-be/internal/appcontext"
|
||||||
@ -31,10 +30,11 @@ type LetterProcessorImpl struct {
|
|||||||
priorityRepo *repository.PriorityRepository
|
priorityRepo *repository.PriorityRepository
|
||||||
institutionRepo *repository.InstitutionRepository
|
institutionRepo *repository.InstitutionRepository
|
||||||
dispActionRepo *repository.DispositionActionRepository
|
dispActionRepo *repository.DispositionActionRepository
|
||||||
|
numberGenerator *LetterNumberGeneratorImpl
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLetterProcessor(letterRepo *repository.LetterIncomingRepository, attachRepo *repository.LetterIncomingAttachmentRepository, txManager *repository.TxManager, activity *ActivityLogProcessorImpl, dispRepo *repository.LetterIncomingDispositionRepository, dispDeptRepo *repository.LetterIncomingDispositionDepartmentRepository, dispSelRepo *repository.LetterDispositionActionSelectionRepository, noteRepo *repository.DispositionNoteRepository, discussionRepo *repository.LetterDiscussionRepository, settingRepo *repository.AppSettingRepository, recipientRepo *repository.LetterIncomingRecipientRepository, departmentRepo *repository.DepartmentRepository, userDeptRepo *repository.UserDepartmentRepository, priorityRepo *repository.PriorityRepository, institutionRepo *repository.InstitutionRepository, dispActionRepo *repository.DispositionActionRepository) *LetterProcessorImpl {
|
func NewLetterProcessor(letterRepo *repository.LetterIncomingRepository, attachRepo *repository.LetterIncomingAttachmentRepository, txManager *repository.TxManager, activity *ActivityLogProcessorImpl, dispRepo *repository.LetterIncomingDispositionRepository, dispDeptRepo *repository.LetterIncomingDispositionDepartmentRepository, dispSelRepo *repository.LetterDispositionActionSelectionRepository, noteRepo *repository.DispositionNoteRepository, discussionRepo *repository.LetterDiscussionRepository, settingRepo *repository.AppSettingRepository, recipientRepo *repository.LetterIncomingRecipientRepository, departmentRepo *repository.DepartmentRepository, userDeptRepo *repository.UserDepartmentRepository, priorityRepo *repository.PriorityRepository, institutionRepo *repository.InstitutionRepository, dispActionRepo *repository.DispositionActionRepository, numberGenerator *LetterNumberGeneratorImpl) *LetterProcessorImpl {
|
||||||
return &LetterProcessorImpl{letterRepo: letterRepo, attachRepo: attachRepo, txManager: txManager, activity: activity, dispositionRepo: dispRepo, dispositionDeptRepo: dispDeptRepo, dispositionActionSelRepo: dispSelRepo, dispositionNoteRepo: noteRepo, discussionRepo: discussionRepo, settingRepo: settingRepo, recipientRepo: recipientRepo, departmentRepo: departmentRepo, userDeptRepo: userDeptRepo, priorityRepo: priorityRepo, institutionRepo: institutionRepo, dispActionRepo: dispActionRepo}
|
return &LetterProcessorImpl{letterRepo: letterRepo, attachRepo: attachRepo, txManager: txManager, activity: activity, dispositionRepo: dispRepo, dispositionDeptRepo: dispDeptRepo, dispositionActionSelRepo: dispSelRepo, dispositionNoteRepo: noteRepo, discussionRepo: discussionRepo, settingRepo: settingRepo, recipientRepo: recipientRepo, departmentRepo: departmentRepo, userDeptRepo: userDeptRepo, priorityRepo: priorityRepo, institutionRepo: institutionRepo, dispActionRepo: dispActionRepo, numberGenerator: numberGenerator}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *LetterProcessorImpl) CreateIncomingLetter(ctx context.Context, req *contract.CreateIncomingLetterRequest) (*contract.IncomingLetterResponse, error) {
|
func (p *LetterProcessorImpl) CreateIncomingLetter(ctx context.Context, req *contract.CreateIncomingLetterRequest) (*contract.IncomingLetterResponse, error) {
|
||||||
@ -42,20 +42,15 @@ func (p *LetterProcessorImpl) CreateIncomingLetter(ctx context.Context, req *con
|
|||||||
err := p.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
|
err := p.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
|
||||||
userID := appcontext.FromGinContext(txCtx).UserID
|
userID := appcontext.FromGinContext(txCtx).UserID
|
||||||
|
|
||||||
prefix := "ESLI"
|
letterNumber, err := p.numberGenerator.GenerateNumber(
|
||||||
seq := 0
|
txCtx,
|
||||||
if s, err := p.settingRepo.Get(txCtx, contract.SettingIncomingLetterPrefix); err == nil {
|
contract.SettingIncomingLetterPrefix,
|
||||||
if v, ok := s.Value["value"].(string); ok && v != "" {
|
contract.SettingIncomingLetterSequence,
|
||||||
prefix = v
|
"ESLI",
|
||||||
}
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
if s, err := p.settingRepo.Get(txCtx, contract.SettingIncomingLetterSequence); err == nil {
|
|
||||||
if v, ok := s.Value["value"].(float64); ok {
|
|
||||||
seq = int(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
seq = seq + 1
|
|
||||||
letterNumber := fmt.Sprintf("%s%04d", prefix, seq)
|
|
||||||
|
|
||||||
entity := &entities.LetterIncoming{
|
entity := &entities.LetterIncoming{
|
||||||
ReferenceNumber: req.ReferenceNumber,
|
ReferenceNumber: req.ReferenceNumber,
|
||||||
@ -73,8 +68,6 @@ func (p *LetterProcessorImpl) CreateIncomingLetter(ctx context.Context, req *con
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = p.settingRepo.Upsert(txCtx, contract.SettingIncomingLetterSequence, entities.JSONB{"value": seq})
|
|
||||||
|
|
||||||
defaultDeptCodes := []string{}
|
defaultDeptCodes := []string{}
|
||||||
if s, err := p.settingRepo.Get(txCtx, contract.SettingIncomingLetterRecipients); err == nil {
|
if s, err := p.settingRepo.Get(txCtx, contract.SettingIncomingLetterRecipients); err == nil {
|
||||||
if arr, ok := s.Value["department_codes"].([]interface{}); ok {
|
if arr, ok := s.Value["department_codes"].([]interface{}); ok {
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"eslogad-be/internal/contract"
|
|
||||||
"eslogad-be/internal/entities"
|
"eslogad-be/internal/entities"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
|||||||
28
internal/repository/app_setting_repository.go
Normal file
28
internal/repository/app_setting_repository.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"eslogad-be/internal/entities"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AppSettingRepository struct{ db *gorm.DB }
|
||||||
|
|
||||||
|
func NewAppSettingRepository(db *gorm.DB) *AppSettingRepository {
|
||||||
|
return &AppSettingRepository{
|
||||||
|
db: db,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *AppSettingRepository) Get(ctx context.Context, key string) (*entities.AppSetting, error) {
|
||||||
|
db := DBFromContext(ctx, r.db)
|
||||||
|
var e entities.AppSetting
|
||||||
|
if err := db.WithContext(ctx).First(&e, "key = ?", key).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &e, nil
|
||||||
|
}
|
||||||
|
func (r *AppSettingRepository) Upsert(ctx context.Context, key string, value entities.JSONB) error {
|
||||||
|
db := DBFromContext(ctx, r.db)
|
||||||
|
return db.WithContext(ctx).Exec("INSERT INTO app_settings(key, value) VALUES(?, ?) ON CONFLICT(key) DO UPDATE SET value = EXCLUDED.value, updated_at = CURRENT_TIMESTAMP", key, value).Error
|
||||||
|
}
|
||||||
@ -278,22 +278,6 @@ func (r *LetterDiscussionRepository) GetUsersByIDs(ctx context.Context, userIDs
|
|||||||
return users, nil
|
return users, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type AppSettingRepository struct{ db *gorm.DB }
|
|
||||||
|
|
||||||
func NewAppSettingRepository(db *gorm.DB) *AppSettingRepository { return &AppSettingRepository{db: db} }
|
|
||||||
func (r *AppSettingRepository) Get(ctx context.Context, key string) (*entities.AppSetting, error) {
|
|
||||||
db := DBFromContext(ctx, r.db)
|
|
||||||
var e entities.AppSetting
|
|
||||||
if err := db.WithContext(ctx).First(&e, "key = ?", key).Error; err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &e, nil
|
|
||||||
}
|
|
||||||
func (r *AppSettingRepository) Upsert(ctx context.Context, key string, value entities.JSONB) error {
|
|
||||||
db := DBFromContext(ctx, r.db)
|
|
||||||
return db.WithContext(ctx).Exec("INSERT INTO app_settings(key, value) VALUES(?, ?) ON CONFLICT(key) DO UPDATE SET value = EXCLUDED.value, updated_at = CURRENT_TIMESTAMP", key, value).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
// recipients
|
// recipients
|
||||||
|
|
||||||
type LetterIncomingRecipientRepository struct{ db *gorm.DB }
|
type LetterIncomingRecipientRepository struct{ db *gorm.DB }
|
||||||
|
|||||||
@ -159,3 +159,32 @@ func (r *DepartmentRepository) Get(ctx context.Context, id uuid.UUID) (*entities
|
|||||||
}
|
}
|
||||||
return &dep, nil
|
return &dep, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *DepartmentRepository) List(ctx context.Context, search string, limit, offset int) ([]entities.Department, int64, error) {
|
||||||
|
db := DBFromContext(ctx, r.db)
|
||||||
|
|
||||||
|
query := db.WithContext(ctx).Model(&entities.Department{})
|
||||||
|
|
||||||
|
// Add search filter if provided
|
||||||
|
if search != "" {
|
||||||
|
query = query.Where("name ILIKE ?", "%"+search+"%")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get total count
|
||||||
|
var total int64
|
||||||
|
if err := query.Count(&total).Error; err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get paginated results
|
||||||
|
var list []entities.Department
|
||||||
|
if err := query.
|
||||||
|
Order("name ASC").
|
||||||
|
Limit(limit).
|
||||||
|
Offset(offset).
|
||||||
|
Find(&list).Error; err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return list, total, nil
|
||||||
|
}
|
||||||
|
|||||||
@ -59,6 +59,8 @@ type MasterHandler interface {
|
|||||||
UpdateDispositionAction(c *gin.Context)
|
UpdateDispositionAction(c *gin.Context)
|
||||||
DeleteDispositionAction(c *gin.Context)
|
DeleteDispositionAction(c *gin.Context)
|
||||||
ListDispositionActions(c *gin.Context)
|
ListDispositionActions(c *gin.Context)
|
||||||
|
// departments
|
||||||
|
ListDepartments(c *gin.Context)
|
||||||
}
|
}
|
||||||
|
|
||||||
type LetterHandler interface {
|
type LetterHandler interface {
|
||||||
|
|||||||
@ -143,6 +143,8 @@ func (r *Router) addAppRoutes(rg *gin.Engine) {
|
|||||||
master.POST("/disposition-actions", r.masterHandler.CreateDispositionAction)
|
master.POST("/disposition-actions", r.masterHandler.CreateDispositionAction)
|
||||||
master.PUT("/disposition-actions/:id", r.masterHandler.UpdateDispositionAction)
|
master.PUT("/disposition-actions/:id", r.masterHandler.UpdateDispositionAction)
|
||||||
master.DELETE("/disposition-actions/:id", r.masterHandler.DeleteDispositionAction)
|
master.DELETE("/disposition-actions/:id", r.masterHandler.DeleteDispositionAction)
|
||||||
|
|
||||||
|
master.GET("/departments", r.masterHandler.ListDepartments)
|
||||||
}
|
}
|
||||||
|
|
||||||
lettersch := v1.Group("/letters")
|
lettersch := v1.Group("/letters")
|
||||||
|
|||||||
@ -2,10 +2,11 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"time"
|
|
||||||
|
|
||||||
|
"eslogad-be/internal/appcontext"
|
||||||
"eslogad-be/internal/contract"
|
"eslogad-be/internal/contract"
|
||||||
"eslogad-be/internal/entities"
|
"eslogad-be/internal/entities"
|
||||||
|
"eslogad-be/internal/processor"
|
||||||
"eslogad-be/internal/repository"
|
"eslogad-be/internal/repository"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
@ -38,53 +39,24 @@ type LetterOutgoingService interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type LetterOutgoingServiceImpl struct {
|
type LetterOutgoingServiceImpl struct {
|
||||||
db *gorm.DB
|
processor processor.LetterOutgoingProcessor
|
||||||
letterRepo *repository.LetterOutgoingRepository
|
|
||||||
attachmentRepo *repository.LetterOutgoingAttachmentRepository
|
|
||||||
recipientRepo *repository.LetterOutgoingRecipientRepository
|
|
||||||
discussionRepo *repository.LetterOutgoingDiscussionRepository
|
|
||||||
discussionAttachmentRepo *repository.LetterOutgoingDiscussionAttachmentRepository
|
|
||||||
activityLogRepo *repository.LetterOutgoingActivityLogRepository
|
|
||||||
approvalFlowRepo *repository.ApprovalFlowRepository
|
|
||||||
approvalRepo *repository.LetterOutgoingApprovalRepository
|
|
||||||
txManager *repository.TxManager
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLetterOutgoingService(
|
func NewLetterOutgoingService(processor processor.LetterOutgoingProcessor) *LetterOutgoingServiceImpl {
|
||||||
db *gorm.DB,
|
|
||||||
letterRepo *repository.LetterOutgoingRepository,
|
|
||||||
attachmentRepo *repository.LetterOutgoingAttachmentRepository,
|
|
||||||
recipientRepo *repository.LetterOutgoingRecipientRepository,
|
|
||||||
discussionRepo *repository.LetterOutgoingDiscussionRepository,
|
|
||||||
discussionAttachmentRepo *repository.LetterOutgoingDiscussionAttachmentRepository,
|
|
||||||
activityLogRepo *repository.LetterOutgoingActivityLogRepository,
|
|
||||||
approvalFlowRepo *repository.ApprovalFlowRepository,
|
|
||||||
approvalRepo *repository.LetterOutgoingApprovalRepository,
|
|
||||||
txManager *repository.TxManager,
|
|
||||||
) *LetterOutgoingServiceImpl {
|
|
||||||
return &LetterOutgoingServiceImpl{
|
return &LetterOutgoingServiceImpl{
|
||||||
db: db,
|
processor: processor,
|
||||||
letterRepo: letterRepo,
|
|
||||||
attachmentRepo: attachmentRepo,
|
|
||||||
recipientRepo: recipientRepo,
|
|
||||||
discussionRepo: discussionRepo,
|
|
||||||
discussionAttachmentRepo: discussionAttachmentRepo,
|
|
||||||
activityLogRepo: activityLogRepo,
|
|
||||||
approvalFlowRepo: approvalFlowRepo,
|
|
||||||
approvalRepo: approvalRepo,
|
|
||||||
txManager: txManager,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LetterOutgoingServiceImpl) CreateOutgoingLetter(ctx context.Context, req *contract.CreateOutgoingLetterRequest) (*contract.OutgoingLetterResponse, error) {
|
func (s *LetterOutgoingServiceImpl) CreateOutgoingLetter(ctx context.Context, req *contract.CreateOutgoingLetterRequest) (*contract.OutgoingLetterResponse, error) {
|
||||||
|
departmentID := getDepartmentIDFromContext(ctx)
|
||||||
|
|
||||||
letter := &entities.LetterOutgoing{
|
letter := &entities.LetterOutgoing{
|
||||||
Subject: req.Subject,
|
Subject: req.Subject,
|
||||||
Description: req.Description,
|
Description: req.Description,
|
||||||
PriorityID: req.PriorityID,
|
PriorityID: req.PriorityID,
|
||||||
ReceiverInstitutionID: req.ReceiverInstitutionID,
|
ReceiverInstitutionID: req.ReceiverInstitutionID,
|
||||||
IssueDate: req.IssueDate,
|
IssueDate: req.IssueDate,
|
||||||
Status: entities.LetterOutgoingStatusDraft,
|
|
||||||
ApprovalFlowID: req.ApprovalFlowID,
|
|
||||||
CreatedBy: req.UserID,
|
CreatedBy: req.UserID,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,61 +64,25 @@ func (s *LetterOutgoingServiceImpl) CreateOutgoingLetter(ctx context.Context, re
|
|||||||
letter.ReferenceNumber = req.ReferenceNumber
|
letter.ReferenceNumber = req.ReferenceNumber
|
||||||
}
|
}
|
||||||
|
|
||||||
err := s.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
|
var attachments []entities.LetterOutgoingAttachment
|
||||||
if err := s.letterRepo.Create(txCtx, letter); err != nil {
|
if len(req.Attachments) > 0 {
|
||||||
return err
|
attachments = make([]entities.LetterOutgoingAttachment, len(req.Attachments))
|
||||||
}
|
for i, a := range req.Attachments {
|
||||||
|
attachments[i] = entities.LetterOutgoingAttachment{
|
||||||
if len(req.Recipients) > 0 {
|
FileURL: a.FileURL,
|
||||||
recipients := make([]entities.LetterOutgoingRecipient, len(req.Recipients))
|
FileName: a.FileName,
|
||||||
for i, r := range req.Recipients {
|
FileType: a.FileType,
|
||||||
recipients[i] = entities.LetterOutgoingRecipient{
|
UploadedBy: &req.UserID,
|
||||||
LetterID: letter.ID,
|
|
||||||
RecipientName: r.Name,
|
|
||||||
RecipientEmail: r.Email,
|
|
||||||
RecipientPosition: r.Position,
|
|
||||||
RecipientInstitution: r.Institution,
|
|
||||||
IsPrimary: r.IsPrimary,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := s.recipientRepo.CreateBulk(txCtx, recipients); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if len(req.Attachments) > 0 {
|
err := s.processor.CreateOutgoingLetter(ctx, letter, attachments, req.UserID, departmentID)
|
||||||
attachments := make([]entities.LetterOutgoingAttachment, len(req.Attachments))
|
|
||||||
for i, a := range req.Attachments {
|
|
||||||
attachments[i] = entities.LetterOutgoingAttachment{
|
|
||||||
LetterID: letter.ID,
|
|
||||||
FileURL: a.FileURL,
|
|
||||||
FileName: a.FileName,
|
|
||||||
FileType: a.FileType,
|
|
||||||
UploadedBy: &req.UserID,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := s.attachmentRepo.CreateBulk(txCtx, attachments); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
activityLog := &entities.LetterOutgoingActivityLog{
|
|
||||||
LetterID: letter.ID,
|
|
||||||
ActionType: entities.LetterOutgoingActionCreated,
|
|
||||||
ActorUserID: &req.UserID,
|
|
||||||
}
|
|
||||||
if err := s.activityLogRepo.Create(txCtx, activityLog); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := s.letterRepo.Get(ctx, letter.ID)
|
result, err := s.processor.GetOutgoingLetterByID(ctx, letter.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -155,7 +91,7 @@ func (s *LetterOutgoingServiceImpl) CreateOutgoingLetter(ctx context.Context, re
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *LetterOutgoingServiceImpl) GetOutgoingLetterByID(ctx context.Context, id uuid.UUID) (*contract.OutgoingLetterResponse, error) {
|
func (s *LetterOutgoingServiceImpl) GetOutgoingLetterByID(ctx context.Context, id uuid.UUID) (*contract.OutgoingLetterResponse, error) {
|
||||||
letter, err := s.letterRepo.Get(ctx, id)
|
letter, err := s.processor.GetOutgoingLetterByID(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -173,7 +109,7 @@ func (s *LetterOutgoingServiceImpl) ListOutgoingLetters(ctx context.Context, req
|
|||||||
ToDate: req.ToDate,
|
ToDate: req.ToDate,
|
||||||
}
|
}
|
||||||
|
|
||||||
letters, total, err := s.letterRepo.List(ctx, filter, req.Limit, req.Offset)
|
letters, total, err := s.processor.ListOutgoingLetters(ctx, filter, req.Limit, req.Offset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -192,7 +128,7 @@ func (s *LetterOutgoingServiceImpl) ListOutgoingLetters(ctx context.Context, req
|
|||||||
func (s *LetterOutgoingServiceImpl) UpdateOutgoingLetter(ctx context.Context, id uuid.UUID, req *contract.UpdateOutgoingLetterRequest) (*contract.OutgoingLetterResponse, error) {
|
func (s *LetterOutgoingServiceImpl) UpdateOutgoingLetter(ctx context.Context, id uuid.UUID, req *contract.UpdateOutgoingLetterRequest) (*contract.OutgoingLetterResponse, error) {
|
||||||
userID := getUserIDFromContext(ctx)
|
userID := getUserIDFromContext(ctx)
|
||||||
|
|
||||||
letter, err := s.letterRepo.Get(ctx, id)
|
letter, err := s.processor.GetOutgoingLetterByID(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -220,28 +156,12 @@ func (s *LetterOutgoingServiceImpl) UpdateOutgoingLetter(ctx context.Context, id
|
|||||||
letter.ReferenceNumber = req.ReferenceNumber
|
letter.ReferenceNumber = req.ReferenceNumber
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
|
err = s.processor.UpdateOutgoingLetter(ctx, letter, userID)
|
||||||
if err := s.letterRepo.Update(txCtx, letter); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
activityLog := &entities.LetterOutgoingActivityLog{
|
|
||||||
LetterID: letter.ID,
|
|
||||||
ActionType: entities.LetterOutgoingActionUpdated,
|
|
||||||
ActorUserID: &userID,
|
|
||||||
}
|
|
||||||
if err := s.activityLogRepo.Create(txCtx, activityLog); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := s.letterRepo.Get(ctx, id)
|
result, err := s.processor.GetOutgoingLetterByID(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -252,7 +172,7 @@ func (s *LetterOutgoingServiceImpl) UpdateOutgoingLetter(ctx context.Context, id
|
|||||||
func (s *LetterOutgoingServiceImpl) DeleteOutgoingLetter(ctx context.Context, id uuid.UUID) error {
|
func (s *LetterOutgoingServiceImpl) DeleteOutgoingLetter(ctx context.Context, id uuid.UUID) error {
|
||||||
userID := getUserIDFromContext(ctx)
|
userID := getUserIDFromContext(ctx)
|
||||||
|
|
||||||
letter, err := s.letterRepo.Get(ctx, id)
|
letter, err := s.processor.GetOutgoingLetterByID(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -261,28 +181,13 @@ func (s *LetterOutgoingServiceImpl) DeleteOutgoingLetter(ctx context.Context, id
|
|||||||
return gorm.ErrInvalidData
|
return gorm.ErrInvalidData
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
|
return s.processor.DeleteOutgoingLetter(ctx, id, userID)
|
||||||
if err := s.letterRepo.SoftDelete(txCtx, id); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
activityLog := &entities.LetterOutgoingActivityLog{
|
|
||||||
LetterID: letter.ID,
|
|
||||||
ActionType: entities.LetterOutgoingActionDeleted,
|
|
||||||
ActorUserID: &userID,
|
|
||||||
}
|
|
||||||
if err := s.activityLogRepo.Create(txCtx, activityLog); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LetterOutgoingServiceImpl) SubmitForApproval(ctx context.Context, letterID uuid.UUID) error {
|
func (s *LetterOutgoingServiceImpl) SubmitForApproval(ctx context.Context, letterID uuid.UUID) error {
|
||||||
userID := getUserIDFromContext(ctx)
|
userID := getUserIDFromContext(ctx)
|
||||||
|
|
||||||
letter, err := s.letterRepo.Get(ctx, letterID)
|
letter, err := s.processor.GetOutgoingLetterByID(ctx, letterID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -295,48 +200,13 @@ func (s *LetterOutgoingServiceImpl) SubmitForApproval(ctx context.Context, lette
|
|||||||
return gorm.ErrInvalidData
|
return gorm.ErrInvalidData
|
||||||
}
|
}
|
||||||
|
|
||||||
flow, err := s.approvalFlowRepo.Get(ctx, *letter.ApprovalFlowID)
|
return s.processor.ProcessApprovalSubmission(ctx, letterID, *letter.ApprovalFlowID, userID)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
|
|
||||||
approvals := make([]entities.LetterOutgoingApproval, len(flow.Steps))
|
|
||||||
for i, step := range flow.Steps {
|
|
||||||
approvals[i] = entities.LetterOutgoingApproval{
|
|
||||||
LetterID: letterID,
|
|
||||||
StepID: step.ID,
|
|
||||||
Status: entities.ApprovalStatusPending,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := s.approvalRepo.CreateBulk(txCtx, approvals); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := s.letterRepo.UpdateStatus(txCtx, letterID, entities.LetterOutgoingStatusPendingApproval); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
activityLog := &entities.LetterOutgoingActivityLog{
|
|
||||||
LetterID: letterID,
|
|
||||||
ActionType: entities.LetterOutgoingActionSubmittedApproval,
|
|
||||||
ActorUserID: &userID,
|
|
||||||
FromStatus: ptr(string(entities.LetterOutgoingStatusDraft)),
|
|
||||||
ToStatus: ptr(string(entities.LetterOutgoingStatusPendingApproval)),
|
|
||||||
}
|
|
||||||
if err := s.activityLogRepo.Create(txCtx, activityLog); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LetterOutgoingServiceImpl) ApproveOutgoingLetter(ctx context.Context, letterID uuid.UUID, req *contract.ApproveLetterRequest) error {
|
func (s *LetterOutgoingServiceImpl) ApproveOutgoingLetter(ctx context.Context, letterID uuid.UUID, req *contract.ApproveLetterRequest) error {
|
||||||
userID := getUserIDFromContext(ctx)
|
userID := getUserIDFromContext(ctx)
|
||||||
|
|
||||||
letter, err := s.letterRepo.Get(ctx, letterID)
|
letter, err := s.processor.GetOutgoingLetterByID(ctx, letterID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -345,7 +215,7 @@ func (s *LetterOutgoingServiceImpl) ApproveOutgoingLetter(ctx context.Context, l
|
|||||||
return gorm.ErrInvalidData
|
return gorm.ErrInvalidData
|
||||||
}
|
}
|
||||||
|
|
||||||
approvals, err := s.approvalRepo.ListByLetter(ctx, letterID)
|
approvals, err := s.processor.GetApprovalsByLetter(ctx, letterID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -366,49 +236,23 @@ func (s *LetterOutgoingServiceImpl) ApproveOutgoingLetter(ctx context.Context, l
|
|||||||
return gorm.ErrInvalidData
|
return gorm.ErrInvalidData
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
|
currentApproval.Remarks = req.Remarks
|
||||||
now := time.Now()
|
|
||||||
currentApproval.Status = entities.ApprovalStatusApproved
|
|
||||||
currentApproval.ApproverID = &userID
|
|
||||||
currentApproval.ActedAt = &now
|
|
||||||
currentApproval.Remarks = req.Remarks
|
|
||||||
|
|
||||||
if err := s.approvalRepo.Update(txCtx, currentApproval); err != nil {
|
allApproved := true
|
||||||
return err
|
for _, approval := range approvals {
|
||||||
|
if approval.ID != currentApproval.ID && approval.Status == entities.ApprovalStatusPending {
|
||||||
|
allApproved = false
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
allApproved := true
|
return s.processor.ProcessApproval(ctx, letterID, currentApproval, userID, allApproved)
|
||||||
for _, approval := range approvals {
|
|
||||||
if approval.ID != currentApproval.ID && approval.Status == entities.ApprovalStatusPending {
|
|
||||||
allApproved = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if allApproved {
|
|
||||||
if err := s.letterRepo.UpdateStatus(txCtx, letterID, entities.LetterOutgoingStatusApproved); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
activityLog := &entities.LetterOutgoingActivityLog{
|
|
||||||
LetterID: letterID,
|
|
||||||
ActionType: entities.LetterOutgoingActionApproved,
|
|
||||||
ActorUserID: &userID,
|
|
||||||
TargetID: ¤tApproval.ID,
|
|
||||||
}
|
|
||||||
if err := s.activityLogRepo.Create(txCtx, activityLog); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LetterOutgoingServiceImpl) RejectOutgoingLetter(ctx context.Context, letterID uuid.UUID, req *contract.RejectLetterRequest) error {
|
func (s *LetterOutgoingServiceImpl) RejectOutgoingLetter(ctx context.Context, letterID uuid.UUID, req *contract.RejectLetterRequest) error {
|
||||||
userID := getUserIDFromContext(ctx)
|
userID := getUserIDFromContext(ctx)
|
||||||
|
|
||||||
letter, err := s.letterRepo.Get(ctx, letterID)
|
letter, err := s.processor.GetOutgoingLetterByID(ctx, letterID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -417,7 +261,7 @@ func (s *LetterOutgoingServiceImpl) RejectOutgoingLetter(ctx context.Context, le
|
|||||||
return gorm.ErrInvalidData
|
return gorm.ErrInvalidData
|
||||||
}
|
}
|
||||||
|
|
||||||
approvals, err := s.approvalRepo.ListByLetter(ctx, letterID)
|
approvals, err := s.processor.GetApprovalsByLetter(ctx, letterID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -438,41 +282,15 @@ func (s *LetterOutgoingServiceImpl) RejectOutgoingLetter(ctx context.Context, le
|
|||||||
return gorm.ErrInvalidData
|
return gorm.ErrInvalidData
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
|
currentApproval.Remarks = &req.Reason
|
||||||
now := time.Now()
|
|
||||||
currentApproval.Status = entities.ApprovalStatusRejected
|
|
||||||
currentApproval.ApproverID = &userID
|
|
||||||
currentApproval.ActedAt = &now
|
|
||||||
currentApproval.Remarks = &req.Reason
|
|
||||||
|
|
||||||
if err := s.approvalRepo.Update(txCtx, currentApproval); err != nil {
|
return s.processor.ProcessRejection(ctx, letterID, currentApproval, userID)
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := s.letterRepo.UpdateStatus(txCtx, letterID, entities.LetterOutgoingStatusDraft); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
activityLog := &entities.LetterOutgoingActivityLog{
|
|
||||||
LetterID: letterID,
|
|
||||||
ActionType: entities.LetterOutgoingActionRejected,
|
|
||||||
ActorUserID: &userID,
|
|
||||||
TargetID: ¤tApproval.ID,
|
|
||||||
FromStatus: ptr(string(entities.LetterOutgoingStatusPendingApproval)),
|
|
||||||
ToStatus: ptr(string(entities.LetterOutgoingStatusDraft)),
|
|
||||||
}
|
|
||||||
if err := s.activityLogRepo.Create(txCtx, activityLog); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LetterOutgoingServiceImpl) SendOutgoingLetter(ctx context.Context, letterID uuid.UUID) error {
|
func (s *LetterOutgoingServiceImpl) SendOutgoingLetter(ctx context.Context, letterID uuid.UUID) error {
|
||||||
userID := getUserIDFromContext(ctx)
|
userID := getUserIDFromContext(ctx)
|
||||||
|
|
||||||
letter, err := s.letterRepo.Get(ctx, letterID)
|
letter, err := s.processor.GetOutgoingLetterByID(ctx, letterID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -481,30 +299,15 @@ func (s *LetterOutgoingServiceImpl) SendOutgoingLetter(ctx context.Context, lett
|
|||||||
return gorm.ErrInvalidData
|
return gorm.ErrInvalidData
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
|
fromStatus := string(entities.LetterOutgoingStatusApproved)
|
||||||
if err := s.letterRepo.UpdateStatus(txCtx, letterID, entities.LetterOutgoingStatusSent); err != nil {
|
toStatus := string(entities.LetterOutgoingStatusSent)
|
||||||
return err
|
return s.processor.UpdateLetterStatus(ctx, letterID, entities.LetterOutgoingStatusSent, userID, &fromStatus, &toStatus)
|
||||||
}
|
|
||||||
|
|
||||||
activityLog := &entities.LetterOutgoingActivityLog{
|
|
||||||
LetterID: letterID,
|
|
||||||
ActionType: entities.LetterOutgoingActionSent,
|
|
||||||
ActorUserID: &userID,
|
|
||||||
FromStatus: ptr(string(entities.LetterOutgoingStatusApproved)),
|
|
||||||
ToStatus: ptr(string(entities.LetterOutgoingStatusSent)),
|
|
||||||
}
|
|
||||||
if err := s.activityLogRepo.Create(txCtx, activityLog); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LetterOutgoingServiceImpl) ArchiveOutgoingLetter(ctx context.Context, letterID uuid.UUID) error {
|
func (s *LetterOutgoingServiceImpl) ArchiveOutgoingLetter(ctx context.Context, letterID uuid.UUID) error {
|
||||||
userID := getUserIDFromContext(ctx)
|
userID := getUserIDFromContext(ctx)
|
||||||
|
|
||||||
letter, err := s.letterRepo.Get(ctx, letterID)
|
letter, err := s.processor.GetOutgoingLetterByID(ctx, letterID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -513,30 +316,15 @@ func (s *LetterOutgoingServiceImpl) ArchiveOutgoingLetter(ctx context.Context, l
|
|||||||
return gorm.ErrInvalidData
|
return gorm.ErrInvalidData
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
|
fromStatus := string(entities.LetterOutgoingStatusSent)
|
||||||
if err := s.letterRepo.UpdateStatus(txCtx, letterID, entities.LetterOutgoingStatusArchived); err != nil {
|
toStatus := string(entities.LetterOutgoingStatusArchived)
|
||||||
return err
|
return s.processor.UpdateLetterStatus(ctx, letterID, entities.LetterOutgoingStatusArchived, userID, &fromStatus, &toStatus)
|
||||||
}
|
|
||||||
|
|
||||||
activityLog := &entities.LetterOutgoingActivityLog{
|
|
||||||
LetterID: letterID,
|
|
||||||
ActionType: entities.LetterOutgoingActionArchived,
|
|
||||||
ActorUserID: &userID,
|
|
||||||
FromStatus: ptr(string(entities.LetterOutgoingStatusSent)),
|
|
||||||
ToStatus: ptr(string(entities.LetterOutgoingStatusArchived)),
|
|
||||||
}
|
|
||||||
if err := s.activityLogRepo.Create(txCtx, activityLog); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LetterOutgoingServiceImpl) AddRecipients(ctx context.Context, letterID uuid.UUID, req *contract.AddRecipientsRequest) error {
|
func (s *LetterOutgoingServiceImpl) AddRecipients(ctx context.Context, letterID uuid.UUID, req *contract.AddRecipientsRequest) error {
|
||||||
userID := getUserIDFromContext(ctx)
|
userID := getUserIDFromContext(ctx)
|
||||||
|
|
||||||
letter, err := s.letterRepo.Get(ctx, letterID)
|
letter, err := s.processor.GetOutgoingLetterByID(ctx, letterID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -557,26 +345,11 @@ func (s *LetterOutgoingServiceImpl) AddRecipients(ctx context.Context, letterID
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
|
return s.processor.AddRecipients(ctx, letterID, recipients, userID)
|
||||||
if err := s.recipientRepo.CreateBulk(txCtx, recipients); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
activityLog := &entities.LetterOutgoingActivityLog{
|
|
||||||
LetterID: letterID,
|
|
||||||
ActionType: entities.LetterOutgoingActionRecipientAdded,
|
|
||||||
ActorUserID: &userID,
|
|
||||||
}
|
|
||||||
if err := s.activityLogRepo.Create(txCtx, activityLog); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LetterOutgoingServiceImpl) UpdateRecipient(ctx context.Context, letterID uuid.UUID, recipientID uuid.UUID, req *contract.UpdateRecipientRequest) error {
|
func (s *LetterOutgoingServiceImpl) UpdateRecipient(ctx context.Context, letterID uuid.UUID, recipientID uuid.UUID, req *contract.UpdateRecipientRequest) error {
|
||||||
letter, err := s.letterRepo.Get(ctx, letterID)
|
letter, err := s.processor.GetOutgoingLetterByID(ctx, letterID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -594,13 +367,13 @@ func (s *LetterOutgoingServiceImpl) UpdateRecipient(ctx context.Context, letterI
|
|||||||
IsPrimary: req.IsPrimary,
|
IsPrimary: req.IsPrimary,
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.recipientRepo.Update(ctx, recipient)
|
return s.processor.UpdateRecipient(ctx, recipient)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LetterOutgoingServiceImpl) RemoveRecipient(ctx context.Context, letterID uuid.UUID, recipientID uuid.UUID) error {
|
func (s *LetterOutgoingServiceImpl) RemoveRecipient(ctx context.Context, letterID uuid.UUID, recipientID uuid.UUID) error {
|
||||||
userID := getUserIDFromContext(ctx)
|
userID := getUserIDFromContext(ctx)
|
||||||
|
|
||||||
letter, err := s.letterRepo.Get(ctx, letterID)
|
letter, err := s.processor.GetOutgoingLetterByID(ctx, letterID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -609,29 +382,13 @@ func (s *LetterOutgoingServiceImpl) RemoveRecipient(ctx context.Context, letterI
|
|||||||
return gorm.ErrInvalidData
|
return gorm.ErrInvalidData
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
|
return s.processor.RemoveRecipient(ctx, letterID, recipientID, userID)
|
||||||
if err := s.recipientRepo.Delete(txCtx, recipientID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
activityLog := &entities.LetterOutgoingActivityLog{
|
|
||||||
LetterID: letterID,
|
|
||||||
ActionType: entities.LetterOutgoingActionRecipientRemoved,
|
|
||||||
ActorUserID: &userID,
|
|
||||||
TargetID: &recipientID,
|
|
||||||
}
|
|
||||||
if err := s.activityLogRepo.Create(txCtx, activityLog); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LetterOutgoingServiceImpl) AddAttachments(ctx context.Context, letterID uuid.UUID, req *contract.AddAttachmentsRequest) error {
|
func (s *LetterOutgoingServiceImpl) AddAttachments(ctx context.Context, letterID uuid.UUID, req *contract.AddAttachmentsRequest) error {
|
||||||
userID := getUserIDFromContext(ctx)
|
userID := getUserIDFromContext(ctx)
|
||||||
|
|
||||||
letter, err := s.letterRepo.Get(ctx, letterID)
|
letter, err := s.processor.GetOutgoingLetterByID(ctx, letterID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -651,28 +408,13 @@ func (s *LetterOutgoingServiceImpl) AddAttachments(ctx context.Context, letterID
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
|
return s.processor.AddAttachments(ctx, letterID, attachments, userID)
|
||||||
if err := s.attachmentRepo.CreateBulk(txCtx, attachments); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
activityLog := &entities.LetterOutgoingActivityLog{
|
|
||||||
LetterID: letterID,
|
|
||||||
ActionType: entities.LetterOutgoingActionAttachmentAdded,
|
|
||||||
ActorUserID: &userID,
|
|
||||||
}
|
|
||||||
if err := s.activityLogRepo.Create(txCtx, activityLog); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LetterOutgoingServiceImpl) RemoveAttachment(ctx context.Context, letterID uuid.UUID, attachmentID uuid.UUID) error {
|
func (s *LetterOutgoingServiceImpl) RemoveAttachment(ctx context.Context, letterID uuid.UUID, attachmentID uuid.UUID) error {
|
||||||
userID := getUserIDFromContext(ctx)
|
userID := getUserIDFromContext(ctx)
|
||||||
|
|
||||||
letter, err := s.letterRepo.Get(ctx, letterID)
|
letter, err := s.processor.GetOutgoingLetterByID(ctx, letterID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -681,23 +423,7 @@ func (s *LetterOutgoingServiceImpl) RemoveAttachment(ctx context.Context, letter
|
|||||||
return gorm.ErrInvalidData
|
return gorm.ErrInvalidData
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
|
return s.processor.RemoveAttachment(ctx, letterID, attachmentID, userID)
|
||||||
if err := s.attachmentRepo.Delete(txCtx, attachmentID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
activityLog := &entities.LetterOutgoingActivityLog{
|
|
||||||
LetterID: letterID,
|
|
||||||
ActionType: entities.LetterOutgoingActionAttachmentRemoved,
|
|
||||||
ActorUserID: &userID,
|
|
||||||
TargetID: &attachmentID,
|
|
||||||
}
|
|
||||||
if err := s.activityLogRepo.Create(txCtx, activityLog); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LetterOutgoingServiceImpl) CreateDiscussion(ctx context.Context, letterID uuid.UUID, req *contract.CreateDiscussionRequest) (*contract.DiscussionResponse, error) {
|
func (s *LetterOutgoingServiceImpl) CreateDiscussion(ctx context.Context, letterID uuid.UUID, req *contract.CreateDiscussionRequest) (*contract.DiscussionResponse, error) {
|
||||||
@ -714,45 +440,27 @@ func (s *LetterOutgoingServiceImpl) CreateDiscussion(ctx context.Context, letter
|
|||||||
discussion.Mentions = req.Mentions
|
discussion.Mentions = req.Mentions
|
||||||
}
|
}
|
||||||
|
|
||||||
err := s.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
|
var attachments []entities.LetterOutgoingDiscussionAttachment
|
||||||
if err := s.discussionRepo.Create(txCtx, discussion); err != nil {
|
if len(req.Attachments) > 0 {
|
||||||
return err
|
attachments = make([]entities.LetterOutgoingDiscussionAttachment, len(req.Attachments))
|
||||||
}
|
for i, a := range req.Attachments {
|
||||||
|
attachments[i] = entities.LetterOutgoingDiscussionAttachment{
|
||||||
if len(req.Attachments) > 0 {
|
DiscussionID: discussion.ID,
|
||||||
attachments := make([]entities.LetterOutgoingDiscussionAttachment, len(req.Attachments))
|
FileURL: a.FileURL,
|
||||||
for i, a := range req.Attachments {
|
FileName: a.FileName,
|
||||||
attachments[i] = entities.LetterOutgoingDiscussionAttachment{
|
FileType: a.FileType,
|
||||||
DiscussionID: discussion.ID,
|
UploadedBy: &userID,
|
||||||
FileURL: a.FileURL,
|
|
||||||
FileName: a.FileName,
|
|
||||||
FileType: a.FileType,
|
|
||||||
UploadedBy: &userID,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := s.discussionAttachmentRepo.CreateBulk(txCtx, attachments); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
activityLog := &entities.LetterOutgoingActivityLog{
|
err := s.processor.CreateDiscussion(ctx, discussion, attachments, userID)
|
||||||
LetterID: letterID,
|
|
||||||
ActionType: entities.LetterOutgoingActionDiscussionAdded,
|
|
||||||
ActorUserID: &userID,
|
|
||||||
TargetID: &discussion.ID,
|
|
||||||
}
|
|
||||||
if err := s.activityLogRepo.Create(txCtx, activityLog); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := s.discussionRepo.Get(ctx, discussion.ID)
|
result, err := s.processor.GetDiscussionByID(ctx, discussion.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -761,7 +469,7 @@ func (s *LetterOutgoingServiceImpl) CreateDiscussion(ctx context.Context, letter
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *LetterOutgoingServiceImpl) UpdateDiscussion(ctx context.Context, discussionID uuid.UUID, req *contract.UpdateDiscussionRequest) error {
|
func (s *LetterOutgoingServiceImpl) UpdateDiscussion(ctx context.Context, discussionID uuid.UUID, req *contract.UpdateDiscussionRequest) error {
|
||||||
discussion, err := s.discussionRepo.Get(ctx, discussionID)
|
discussion, err := s.processor.GetDiscussionByID(ctx, discussionID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -776,11 +484,11 @@ func (s *LetterOutgoingServiceImpl) UpdateDiscussion(ctx context.Context, discus
|
|||||||
discussion.Mentions = req.Mentions
|
discussion.Mentions = req.Mentions
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.discussionRepo.Update(ctx, discussion)
|
return s.processor.UpdateDiscussion(ctx, discussion)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LetterOutgoingServiceImpl) DeleteDiscussion(ctx context.Context, discussionID uuid.UUID) error {
|
func (s *LetterOutgoingServiceImpl) DeleteDiscussion(ctx context.Context, discussionID uuid.UUID) error {
|
||||||
discussion, err := s.discussionRepo.Get(ctx, discussionID)
|
discussion, err := s.processor.GetDiscussionByID(ctx, discussionID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -790,21 +498,29 @@ func (s *LetterOutgoingServiceImpl) DeleteDiscussion(ctx context.Context, discus
|
|||||||
return gorm.ErrInvalidData
|
return gorm.ErrInvalidData
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.discussionRepo.Delete(ctx, discussionID)
|
return s.processor.DeleteDiscussion(ctx, discussionID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getUserIDFromContext(ctx context.Context) uuid.UUID {
|
func getUserIDFromContext(ctx context.Context) uuid.UUID {
|
||||||
|
appCtx := appcontext.FromGinContext(ctx)
|
||||||
|
if appCtx != nil {
|
||||||
|
return appCtx.UserID
|
||||||
|
}
|
||||||
return uuid.New()
|
return uuid.New()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getDepartmentIDFromContext(ctx context.Context) uuid.UUID {
|
||||||
|
appCtx := appcontext.FromGinContext(ctx)
|
||||||
|
if appCtx != nil {
|
||||||
|
return appCtx.DepartmentID
|
||||||
|
}
|
||||||
|
return uuid.Nil
|
||||||
|
}
|
||||||
|
|
||||||
func userHasRole(ctx context.Context, roleID uuid.UUID) bool {
|
func userHasRole(ctx context.Context, roleID uuid.UUID) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func ptr(s string) *string {
|
|
||||||
return &s
|
|
||||||
}
|
|
||||||
|
|
||||||
func transformLetterToResponse(letter *entities.LetterOutgoing) *contract.OutgoingLetterResponse {
|
func transformLetterToResponse(letter *entities.LetterOutgoing) *contract.OutgoingLetterResponse {
|
||||||
resp := &contract.OutgoingLetterResponse{
|
resp := &contract.OutgoingLetterResponse{
|
||||||
ID: letter.ID,
|
ID: letter.ID,
|
||||||
|
|||||||
@ -16,10 +16,11 @@ type MasterServiceImpl struct {
|
|||||||
priorityRepo *repository.PriorityRepository
|
priorityRepo *repository.PriorityRepository
|
||||||
institutionRepo *repository.InstitutionRepository
|
institutionRepo *repository.InstitutionRepository
|
||||||
dispRepo *repository.DispositionActionRepository
|
dispRepo *repository.DispositionActionRepository
|
||||||
|
departmentRepo *repository.DepartmentRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMasterService(label *repository.LabelRepository, priority *repository.PriorityRepository, institution *repository.InstitutionRepository, disp *repository.DispositionActionRepository) *MasterServiceImpl {
|
func NewMasterService(label *repository.LabelRepository, priority *repository.PriorityRepository, institution *repository.InstitutionRepository, disp *repository.DispositionActionRepository, department *repository.DepartmentRepository) *MasterServiceImpl {
|
||||||
return &MasterServiceImpl{labelRepo: label, priorityRepo: priority, institutionRepo: institution, dispRepo: disp}
|
return &MasterServiceImpl{labelRepo: label, priorityRepo: priority, institutionRepo: institution, dispRepo: disp, departmentRepo: department}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Labels
|
// Labels
|
||||||
@ -212,3 +213,34 @@ func (s *MasterServiceImpl) ListDispositionActions(ctx context.Context) (*contra
|
|||||||
}
|
}
|
||||||
return &contract.ListDispositionActionsResponse{Actions: transformer.DispositionActionsToContract(list)}, nil
|
return &contract.ListDispositionActionsResponse{Actions: transformer.DispositionActionsToContract(list)}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Departments
|
||||||
|
func (s *MasterServiceImpl) ListDepartments(ctx context.Context, req *contract.ListDepartmentsRequest) (*contract.ListDepartmentsResponse, error) {
|
||||||
|
// Set default values if not provided
|
||||||
|
page := req.Page
|
||||||
|
if page < 1 {
|
||||||
|
page = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
limit := req.Limit
|
||||||
|
if limit < 1 {
|
||||||
|
limit = 10
|
||||||
|
}
|
||||||
|
if limit > 100 {
|
||||||
|
limit = 100 // Max limit to prevent performance issues
|
||||||
|
}
|
||||||
|
|
||||||
|
offset := (page - 1) * limit
|
||||||
|
|
||||||
|
list, total, err := s.departmentRepo.List(ctx, req.Search, limit, offset)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &contract.ListDepartmentsResponse{
|
||||||
|
Departments: transformer.DepartmentsToContract(list),
|
||||||
|
Total: total,
|
||||||
|
Page: page,
|
||||||
|
Limit: limit,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|||||||
@ -84,13 +84,23 @@ func DepartmentsToContract(positions []entities.Department) []contract.Departmen
|
|||||||
}
|
}
|
||||||
res := make([]contract.DepartmentResponse, 0, len(positions))
|
res := make([]contract.DepartmentResponse, 0, len(positions))
|
||||||
for _, p := range positions {
|
for _, p := range positions {
|
||||||
res = append(res, contract.DepartmentResponse{ID: p.ID, Name: p.Name, Code: p.Code, Path: p.Path})
|
res = append(res, contract.DepartmentResponse{
|
||||||
|
ID: p.ID,
|
||||||
|
Name: p.Name,
|
||||||
|
Code: p.Code,
|
||||||
|
Path: p.Path,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func DepartmentToContract(p entities.Department) contract.DepartmentResponse {
|
func DepartmentToContract(p entities.Department) contract.DepartmentResponse {
|
||||||
return contract.DepartmentResponse{ID: p.ID, Name: p.Name, Code: p.Code, Path: p.Path}
|
return contract.DepartmentResponse{
|
||||||
|
ID: p.ID,
|
||||||
|
Name: p.Name,
|
||||||
|
Code: p.Code,
|
||||||
|
Path: p.Path,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ProfileEntityToContract(p *entities.UserProfile) *contract.UserProfileResponse {
|
func ProfileEntityToContract(p *entities.UserProfile) *contract.UserProfileResponse {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user