update expense item name
This commit is contained in:
parent
f7399fd0e7
commit
1b7bec4f81
@ -7,7 +7,6 @@ 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"`
|
||||
@ -20,12 +19,12 @@ type CreateExpenseRequest struct {
|
||||
|
||||
type CreateExpenseItemRequest struct {
|
||||
ChartOfAccountID string `json:"chart_of_account_id" validate:"required"`
|
||||
Item string `json:"item" validate:"required"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
Amount float64 `json:"amount" validate:"required"`
|
||||
}
|
||||
|
||||
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"`
|
||||
@ -39,6 +38,7 @@ type UpdateExpenseRequest struct {
|
||||
|
||||
type UpdateExpenseItemRequest struct {
|
||||
ChartOfAccountID *string `json:"chart_of_account_id,omitempty"`
|
||||
Item *string `json:"item,omitempty"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
Amount *float64 `json:"amount,omitempty"`
|
||||
}
|
||||
@ -47,7 +47,6 @@ 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"`
|
||||
@ -65,6 +64,7 @@ type ExpenseItemResponse struct {
|
||||
ExpenseID uuid.UUID `json:"expense_id"`
|
||||
ChartOfAccountID uuid.UUID `json:"chart_of_account_id"`
|
||||
ChartOfAccountName string `json:"chart_of_account_name,omitempty"`
|
||||
Item string `json:"item"`
|
||||
Description *string `json:"description"`
|
||||
Amount float64 `json:"amount"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
|
||||
@ -129,6 +129,6 @@ type ExpenseCategoryTotal struct {
|
||||
}
|
||||
|
||||
type OperationalExpenseItem struct {
|
||||
Description string
|
||||
Item string
|
||||
Amount float64
|
||||
}
|
||||
|
||||
@ -12,7 +12,6 @@ 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"`
|
||||
|
||||
@ -12,6 +12,7 @@ type ExpenseItem struct {
|
||||
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
|
||||
ExpenseID uuid.UUID `gorm:"type:uuid;not null;index" json:"expense_id"`
|
||||
ChartOfAccountID uuid.UUID `gorm:"type:uuid;not null;index" json:"chart_of_account_id"`
|
||||
Item string `gorm:"not null;size:255" json:"item"`
|
||||
Description *string `gorm:"type:text" json:"description"`
|
||||
Amount float64 `gorm:"type:decimal(15,2);not null;default:0" json:"amount"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
||||
|
||||
@ -14,7 +14,6 @@ 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,
|
||||
@ -36,7 +35,6 @@ 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,
|
||||
@ -58,7 +56,6 @@ 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,
|
||||
@ -98,6 +95,7 @@ func ExpenseItemEntityToResponse(entity *entities.ExpenseItem) *models.ExpenseIt
|
||||
ID: entity.ID,
|
||||
ExpenseID: entity.ExpenseID,
|
||||
ChartOfAccountID: entity.ChartOfAccountID,
|
||||
Item: entity.Item,
|
||||
Description: entity.Description,
|
||||
Amount: entity.Amount,
|
||||
CreatedAt: entity.CreatedAt,
|
||||
|
||||
@ -10,7 +10,6 @@ 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"`
|
||||
@ -26,6 +25,7 @@ type ExpenseItem struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
ExpenseID uuid.UUID `json:"expense_id"`
|
||||
ChartOfAccountID uuid.UUID `json:"chart_of_account_id"`
|
||||
Item string `json:"item"`
|
||||
Description *string `json:"description"`
|
||||
Amount float64 `json:"amount"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
@ -36,7 +36,6 @@ 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"`
|
||||
@ -54,6 +53,7 @@ type ExpenseItemResponse struct {
|
||||
ExpenseID uuid.UUID `json:"expense_id"`
|
||||
ChartOfAccountID uuid.UUID `json:"chart_of_account_id"`
|
||||
ChartOfAccountName string `json:"chart_of_account_name,omitempty"`
|
||||
Item string `json:"item"`
|
||||
Description *string `json:"description"`
|
||||
Amount float64 `json:"amount"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
@ -61,7 +61,6 @@ 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"`
|
||||
@ -74,12 +73,12 @@ type CreateExpenseRequest struct {
|
||||
|
||||
type CreateExpenseItemRequest struct {
|
||||
ChartOfAccountID string `json:"chart_of_account_id"`
|
||||
Item string `json:"item"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
Amount float64 `json:"amount"`
|
||||
}
|
||||
|
||||
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"`
|
||||
@ -93,6 +92,7 @@ type UpdateExpenseRequest struct {
|
||||
|
||||
type UpdateExpenseItemRequest struct {
|
||||
ChartOfAccountID *string `json:"chart_of_account_id,omitempty"`
|
||||
Item *string `json:"item,omitempty"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
Amount *float64 `json:"amount,omitempty"`
|
||||
}
|
||||
|
||||
@ -498,7 +498,7 @@ func (p *AnalyticsProcessorImpl) GetProfitLossAnalytics(ctx context.Context, req
|
||||
var opsTotal float64
|
||||
for i, item := range result.OperationalExpenseItems {
|
||||
opsItems[i] = models.OperationalExpenseItem{
|
||||
Item: item.Description,
|
||||
Item: item.Item,
|
||||
Nominal: item.Amount,
|
||||
}
|
||||
opsTotal += item.Amount
|
||||
|
||||
@ -44,7 +44,6 @@ 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,
|
||||
@ -67,6 +66,7 @@ func (p *ExpenseProcessorImpl) CreateExpense(ctx context.Context, organizationID
|
||||
itemEntity := &entities.ExpenseItem{
|
||||
ExpenseID: expenseEntity.ID,
|
||||
ChartOfAccountID: chartOfAccountID,
|
||||
Item: itemReq.Item,
|
||||
Description: itemReq.Description,
|
||||
Amount: itemReq.Amount,
|
||||
}
|
||||
@ -91,9 +91,6 @@ 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
|
||||
}
|
||||
@ -146,10 +143,15 @@ func (p *ExpenseProcessorImpl) UpdateExpense(ctx context.Context, id, organizati
|
||||
if itemReq.Amount != nil {
|
||||
amount = *itemReq.Amount
|
||||
}
|
||||
item := ""
|
||||
if itemReq.Item != nil {
|
||||
item = *itemReq.Item
|
||||
}
|
||||
|
||||
itemEntity := &entities.ExpenseItem{
|
||||
ExpenseID: expenseEntity.ID,
|
||||
ChartOfAccountID: chartOfAccountID,
|
||||
Item: item,
|
||||
Description: itemReq.Description,
|
||||
Amount: amount,
|
||||
}
|
||||
|
||||
@ -531,7 +531,7 @@ func (r *AnalyticsRepositoryImpl) getOperationalExpenseItems(ctx context.Context
|
||||
|
||||
query := r.db.WithContext(ctx).
|
||||
Table("expense_items ei").
|
||||
Select(`COALESCE(ei.description, coa.name) as description, COALESCE(SUM(ei.amount), 0) as amount`).
|
||||
Select(`COALESCE(NULLIF(ei.item, ''), ei.description, coa.name) as item, COALESCE(SUM(ei.amount), 0) as amount`).
|
||||
Joins("JOIN expenses e ON ei.expense_id = e.id").
|
||||
Joins("JOIN chart_of_accounts coa ON ei.chart_of_account_id = coa.id").
|
||||
Where("e.organization_id = ?", organizationID).
|
||||
@ -542,7 +542,7 @@ func (r *AnalyticsRepositoryImpl) getOperationalExpenseItems(ctx context.Context
|
||||
}
|
||||
|
||||
err := query.
|
||||
Group("COALESCE(ei.description, coa.name)").
|
||||
Group("COALESCE(NULLIF(ei.item, ''), ei.description, coa.name)").
|
||||
Order("amount DESC").
|
||||
Scan(&results).Error
|
||||
|
||||
|
||||
@ -68,8 +68,17 @@ 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(expense_name) LIKE ? OR LOWER(receiver) LIKE ? OR LOWER(code_number) LIKE ? OR LOWER(description) LIKE ?",
|
||||
searchPattern, searchPattern, searchPattern, searchPattern)
|
||||
query = query.Where(`
|
||||
LOWER(receiver) LIKE ?
|
||||
OR LOWER(code_number) LIKE ?
|
||||
OR LOWER(description) LIKE ?
|
||||
OR EXISTS (
|
||||
SELECT 1
|
||||
FROM expense_items ei
|
||||
WHERE ei.expense_id = expenses.id
|
||||
AND LOWER(ei.item) LIKE ?
|
||||
)
|
||||
`, searchPattern, searchPattern, searchPattern, searchPattern)
|
||||
}
|
||||
case "outlet_id":
|
||||
if outletID, ok := value.(uuid.UUID); ok {
|
||||
|
||||
@ -12,7 +12,6 @@ func CreateExpenseRequestToModel(req *contract.CreateExpenseRequest) *models.Cre
|
||||
}
|
||||
|
||||
return &models.CreateExpenseRequest{
|
||||
ExpenseName: req.ExpenseName,
|
||||
Receiver: req.Receiver,
|
||||
TransactionDate: req.TransactionDate,
|
||||
CodeNumber: req.CodeNumber,
|
||||
@ -27,6 +26,7 @@ func CreateExpenseRequestToModel(req *contract.CreateExpenseRequest) *models.Cre
|
||||
func CreateExpenseItemRequestToModel(req *contract.CreateExpenseItemRequest) models.CreateExpenseItemRequest {
|
||||
return models.CreateExpenseItemRequest{
|
||||
ChartOfAccountID: req.ChartOfAccountID,
|
||||
Item: req.Item,
|
||||
Description: req.Description,
|
||||
Amount: req.Amount,
|
||||
}
|
||||
@ -34,7 +34,6 @@ 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,
|
||||
@ -59,6 +58,7 @@ func UpdateExpenseRequestToModel(req *contract.UpdateExpenseRequest) *models.Upd
|
||||
func UpdateExpenseItemRequestToModel(req *contract.UpdateExpenseItemRequest) models.UpdateExpenseItemRequest {
|
||||
return models.UpdateExpenseItemRequest{
|
||||
ChartOfAccountID: req.ChartOfAccountID,
|
||||
Item: req.Item,
|
||||
Description: req.Description,
|
||||
Amount: req.Amount,
|
||||
}
|
||||
@ -89,7 +89,6 @@ 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,
|
||||
@ -109,6 +108,7 @@ func ExpenseItemModelResponseToResponse(item *models.ExpenseItemResponse) contra
|
||||
ExpenseID: item.ExpenseID,
|
||||
ChartOfAccountID: item.ChartOfAccountID,
|
||||
ChartOfAccountName: item.ChartOfAccountName,
|
||||
Item: item.Item,
|
||||
Description: item.Description,
|
||||
Amount: item.Amount,
|
||||
CreatedAt: item.CreatedAt,
|
||||
|
||||
@ -28,10 +28,6 @@ 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
|
||||
}
|
||||
@ -68,6 +64,9 @@ func (v *ExpenseValidatorImpl) ValidateCreateExpenseRequest(req *contract.Create
|
||||
if strings.TrimSpace(item.ChartOfAccountID) == "" {
|
||||
return fmt.Errorf("item %d: chart_of_account_id is required", i), constants.MissingFieldErrorCode
|
||||
}
|
||||
if strings.TrimSpace(item.Item) == "" {
|
||||
return fmt.Errorf("item %d: item is required", i), constants.MissingFieldErrorCode
|
||||
}
|
||||
if _, err := uuid.Parse(item.ChartOfAccountID); err != nil {
|
||||
return fmt.Errorf("item %d: chart_of_account_id must be a valid UUID", i), constants.MalformedFieldErrorCode
|
||||
}
|
||||
@ -84,10 +83,6 @@ 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
|
||||
}
|
||||
@ -123,6 +118,9 @@ func (v *ExpenseValidatorImpl) ValidateUpdateExpenseRequest(req *contract.Update
|
||||
return fmt.Errorf("item %d: chart_of_account_id must be a valid UUID", i), constants.MalformedFieldErrorCode
|
||||
}
|
||||
}
|
||||
if item.Item != nil && strings.TrimSpace(*item.Item) == "" {
|
||||
return fmt.Errorf("item %d: item cannot be empty", i), constants.MalformedFieldErrorCode
|
||||
}
|
||||
if item.Amount != nil && *item.Amount <= 0 {
|
||||
return fmt.Errorf("item %d: amount must be greater than 0", i), constants.MalformedFieldErrorCode
|
||||
}
|
||||
|
||||
@ -1,2 +1,16 @@
|
||||
DROP INDEX IF EXISTS idx_expenses_expense_name;
|
||||
ALTER TABLE expenses DROP COLUMN expense_name;
|
||||
ALTER TABLE expenses ADD COLUMN IF NOT EXISTS expense_name VARCHAR(255) NOT NULL DEFAULT '';
|
||||
|
||||
UPDATE expenses e
|
||||
SET expense_name = first_item.item
|
||||
FROM (
|
||||
SELECT DISTINCT ON (expense_id) expense_id, item
|
||||
FROM expense_items
|
||||
WHERE COALESCE(item, '') != ''
|
||||
ORDER BY expense_id, created_at ASC
|
||||
) first_item
|
||||
WHERE e.id = first_item.expense_id
|
||||
AND COALESCE(e.expense_name, '') = '';
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_expenses_expense_name ON expenses(expense_name);
|
||||
DROP INDEX IF EXISTS idx_expense_items_item;
|
||||
ALTER TABLE expense_items DROP COLUMN IF EXISTS item;
|
||||
|
||||
@ -1,2 +1,21 @@
|
||||
ALTER TABLE expenses ADD COLUMN expense_name VARCHAR(255) NOT NULL DEFAULT '';
|
||||
CREATE INDEX idx_expenses_expense_name ON expenses(expense_name);
|
||||
ALTER TABLE expense_items ADD COLUMN IF NOT EXISTS item VARCHAR(255) NOT NULL DEFAULT '';
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (
|
||||
SELECT 1
|
||||
FROM information_schema.columns
|
||||
WHERE table_name = 'expenses'
|
||||
AND column_name = 'expense_name'
|
||||
) THEN
|
||||
UPDATE expense_items ei
|
||||
SET item = e.expense_name
|
||||
FROM expenses e
|
||||
WHERE ei.expense_id = e.id
|
||||
AND COALESCE(ei.item, '') = '';
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DROP INDEX IF EXISTS idx_expenses_expense_name;
|
||||
ALTER TABLE expenses DROP COLUMN IF EXISTS expense_name;
|
||||
CREATE INDEX IF NOT EXISTS idx_expense_items_item ON expense_items(item);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user