From a55a3f4ee2be0d0c8a37e7816f061daa1dfffe8a Mon Sep 17 00:00:00 2001 From: ryan Date: Tue, 26 May 2026 15:25:47 +0700 Subject: [PATCH] add expense_name --- internal/contract/expense_contract.go | 3 +++ internal/entities/expense.go | 1 + internal/mappers/expense_mapper.go | 3 +++ internal/models/expense.go | 4 ++++ internal/processor/expense_processor.go | 4 ++++ internal/repository/expense_repository.go | 4 ++-- internal/transformer/expense_transformer.go | 3 +++ internal/validator/expense_validator.go | 8 ++++++++ migrations/000071_add_expense_name_to_expenses.down.sql | 2 ++ migrations/000071_add_expense_name_to_expenses.up.sql | 2 ++ 10 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 migrations/000071_add_expense_name_to_expenses.down.sql create mode 100644 migrations/000071_add_expense_name_to_expenses.up.sql diff --git a/internal/contract/expense_contract.go b/internal/contract/expense_contract.go index ef1cedf..ddc6f9e 100644 --- a/internal/contract/expense_contract.go +++ b/internal/contract/expense_contract.go @@ -7,6 +7,7 @@ import ( ) type CreateExpenseRequest struct { + ExpenseName string `json:"expense_name" validate:"required"` Receiver string `json:"receiver" validate:"required"` TransactionDate string `json:"transaction_date" validate:"required"` CodeNumber string `json:"code_number" validate:"required"` @@ -24,6 +25,7 @@ type CreateExpenseItemRequest struct { } type UpdateExpenseRequest struct { + ExpenseName *string `json:"expense_name,omitempty"` Receiver *string `json:"receiver,omitempty"` TransactionDate *string `json:"transaction_date,omitempty"` CodeNumber *string `json:"code_number,omitempty"` @@ -45,6 +47,7 @@ type ExpenseResponse struct { ID uuid.UUID `json:"id"` OrganizationID uuid.UUID `json:"organization_id"` OutletID uuid.UUID `json:"outlet_id"` + ExpenseName string `json:"expense_name"` Receiver string `json:"receiver"` TransactionDate time.Time `json:"transaction_date"` CodeNumber string `json:"code_number"` diff --git a/internal/entities/expense.go b/internal/entities/expense.go index a9ba53e..622b955 100644 --- a/internal/entities/expense.go +++ b/internal/entities/expense.go @@ -12,6 +12,7 @@ type Expense struct { ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"` OrganizationID uuid.UUID `gorm:"type:uuid;not null;index" json:"organization_id"` OutletID uuid.UUID `gorm:"type:uuid;not null;index" json:"outlet_id"` + ExpenseName string `gorm:"not null;size:255" json:"expense_name"` Receiver string `gorm:"not null;size:255" json:"receiver"` TransactionDate time.Time `gorm:"type:date;not null" json:"transaction_date"` CodeNumber string `gorm:"not null;size:50" json:"code_number"` diff --git a/internal/mappers/expense_mapper.go b/internal/mappers/expense_mapper.go index 83078c2..17324a6 100644 --- a/internal/mappers/expense_mapper.go +++ b/internal/mappers/expense_mapper.go @@ -14,6 +14,7 @@ func ExpenseEntityToModel(entity *entities.Expense) *models.Expense { ID: entity.ID, OrganizationID: entity.OrganizationID, OutletID: entity.OutletID, + ExpenseName: entity.ExpenseName, Receiver: entity.Receiver, TransactionDate: entity.TransactionDate, CodeNumber: entity.CodeNumber, @@ -35,6 +36,7 @@ func ExpenseModelToEntity(model *models.Expense) *entities.Expense { ID: model.ID, OrganizationID: model.OrganizationID, OutletID: model.OutletID, + ExpenseName: model.ExpenseName, Receiver: model.Receiver, TransactionDate: model.TransactionDate, CodeNumber: model.CodeNumber, @@ -56,6 +58,7 @@ func ExpenseEntityToResponse(entity *entities.Expense) *models.ExpenseResponse { ID: entity.ID, OrganizationID: entity.OrganizationID, OutletID: entity.OutletID, + ExpenseName: entity.ExpenseName, Receiver: entity.Receiver, TransactionDate: entity.TransactionDate, CodeNumber: entity.CodeNumber, diff --git a/internal/models/expense.go b/internal/models/expense.go index b1e6ae8..37b3b13 100644 --- a/internal/models/expense.go +++ b/internal/models/expense.go @@ -10,6 +10,7 @@ type Expense struct { ID uuid.UUID `json:"id"` OrganizationID uuid.UUID `json:"organization_id"` OutletID uuid.UUID `json:"outlet_id"` + ExpenseName string `json:"expense_name"` Receiver string `json:"receiver"` TransactionDate time.Time `json:"transaction_date"` CodeNumber string `json:"code_number"` @@ -35,6 +36,7 @@ type ExpenseResponse struct { ID uuid.UUID `json:"id"` OrganizationID uuid.UUID `json:"organization_id"` OutletID uuid.UUID `json:"outlet_id"` + ExpenseName string `json:"expense_name"` Receiver string `json:"receiver"` TransactionDate time.Time `json:"transaction_date"` CodeNumber string `json:"code_number"` @@ -59,6 +61,7 @@ type ExpenseItemResponse struct { } type CreateExpenseRequest struct { + ExpenseName string `json:"expense_name"` Receiver string `json:"receiver"` TransactionDate string `json:"transaction_date"` CodeNumber string `json:"code_number"` @@ -76,6 +79,7 @@ type CreateExpenseItemRequest struct { } type UpdateExpenseRequest struct { + ExpenseName *string `json:"expense_name,omitempty"` Receiver *string `json:"receiver,omitempty"` TransactionDate *string `json:"transaction_date,omitempty"` CodeNumber *string `json:"code_number,omitempty"` diff --git a/internal/processor/expense_processor.go b/internal/processor/expense_processor.go index 94e73f9..76b30a8 100644 --- a/internal/processor/expense_processor.go +++ b/internal/processor/expense_processor.go @@ -44,6 +44,7 @@ func (p *ExpenseProcessorImpl) CreateExpense(ctx context.Context, organizationID expenseEntity := &entities.Expense{ OrganizationID: organizationID, OutletID: outletID, + ExpenseName: req.ExpenseName, Receiver: req.Receiver, TransactionDate: transactionDate, CodeNumber: req.CodeNumber, @@ -90,6 +91,9 @@ func (p *ExpenseProcessorImpl) UpdateExpense(ctx context.Context, id, organizati return nil, fmt.Errorf("expense not found: %w", err) } + if req.ExpenseName != nil { + expenseEntity.ExpenseName = *req.ExpenseName + } if req.Receiver != nil { expenseEntity.Receiver = *req.Receiver } diff --git a/internal/repository/expense_repository.go b/internal/repository/expense_repository.go index 355163a..1bfcad0 100644 --- a/internal/repository/expense_repository.go +++ b/internal/repository/expense_repository.go @@ -67,8 +67,8 @@ func (r *ExpenseRepositoryImpl) List(ctx context.Context, organizationID uuid.UU case "search": if searchStr, ok := value.(string); ok && searchStr != "" { searchPattern := "%" + strings.ToLower(searchStr) + "%" - query = query.Where("LOWER(receiver) LIKE ? OR LOWER(code_number) LIKE ? OR LOWER(description) LIKE ?", - searchPattern, searchPattern, searchPattern) + query = query.Where("LOWER(expense_name) LIKE ? OR LOWER(receiver) LIKE ? OR LOWER(code_number) LIKE ? OR LOWER(description) LIKE ?", + searchPattern, searchPattern, searchPattern, searchPattern) } case "outlet_id": if outletID, ok := value.(uuid.UUID); ok { diff --git a/internal/transformer/expense_transformer.go b/internal/transformer/expense_transformer.go index 695c0cc..4b1d059 100644 --- a/internal/transformer/expense_transformer.go +++ b/internal/transformer/expense_transformer.go @@ -12,6 +12,7 @@ func CreateExpenseRequestToModel(req *contract.CreateExpenseRequest) *models.Cre } return &models.CreateExpenseRequest{ + ExpenseName: req.ExpenseName, Receiver: req.Receiver, TransactionDate: req.TransactionDate, CodeNumber: req.CodeNumber, @@ -33,6 +34,7 @@ func CreateExpenseItemRequestToModel(req *contract.CreateExpenseItemRequest) mod func UpdateExpenseRequestToModel(req *contract.UpdateExpenseRequest) *models.UpdateExpenseRequest { modelReq := &models.UpdateExpenseRequest{ + ExpenseName: req.ExpenseName, Receiver: req.Receiver, TransactionDate: req.TransactionDate, CodeNumber: req.CodeNumber, @@ -84,6 +86,7 @@ func ExpenseModelResponseToResponse(expense *models.ExpenseResponse) *contract.E ID: expense.ID, OrganizationID: expense.OrganizationID, OutletID: expense.OutletID, + ExpenseName: expense.ExpenseName, Receiver: expense.Receiver, TransactionDate: expense.TransactionDate, CodeNumber: expense.CodeNumber, diff --git a/internal/validator/expense_validator.go b/internal/validator/expense_validator.go index b7c5aae..b0cb81d 100644 --- a/internal/validator/expense_validator.go +++ b/internal/validator/expense_validator.go @@ -28,6 +28,10 @@ func (v *ExpenseValidatorImpl) ValidateCreateExpenseRequest(req *contract.Create return errors.New("request body is required"), constants.MissingFieldErrorCode } + if strings.TrimSpace(req.ExpenseName) == "" { + return errors.New("expense_name is required"), constants.MissingFieldErrorCode + } + if strings.TrimSpace(req.Receiver) == "" { return errors.New("receiver is required"), constants.MissingFieldErrorCode } @@ -80,6 +84,10 @@ func (v *ExpenseValidatorImpl) ValidateUpdateExpenseRequest(req *contract.Update return errors.New("request body is required"), constants.MissingFieldErrorCode } + if req.ExpenseName != nil && strings.TrimSpace(*req.ExpenseName) == "" { + return errors.New("expense_name cannot be empty"), constants.MalformedFieldErrorCode + } + if req.Receiver != nil && strings.TrimSpace(*req.Receiver) == "" { return errors.New("receiver cannot be empty"), constants.MalformedFieldErrorCode } diff --git a/migrations/000071_add_expense_name_to_expenses.down.sql b/migrations/000071_add_expense_name_to_expenses.down.sql new file mode 100644 index 0000000..553fdc9 --- /dev/null +++ b/migrations/000071_add_expense_name_to_expenses.down.sql @@ -0,0 +1,2 @@ +DROP INDEX IF EXISTS idx_expenses_expense_name; +ALTER TABLE expenses DROP COLUMN expense_name; diff --git a/migrations/000071_add_expense_name_to_expenses.up.sql b/migrations/000071_add_expense_name_to_expenses.up.sql new file mode 100644 index 0000000..9cb91e7 --- /dev/null +++ b/migrations/000071_add_expense_name_to_expenses.up.sql @@ -0,0 +1,2 @@ +ALTER TABLE expenses ADD COLUMN expense_name VARCHAR(255) NOT NULL DEFAULT ''; +CREATE INDEX idx_expenses_expense_name ON expenses(expense_name);