Add coa purchase and vendors
This commit is contained in:
@@ -0,0 +1,207 @@
|
||||
package processor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"apskel-pos-be/internal/appcontext"
|
||||
"apskel-pos-be/internal/entities"
|
||||
"apskel-pos-be/internal/mappers"
|
||||
"apskel-pos-be/internal/models"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
|
||||
type AccountProcessor interface {
|
||||
CreateAccount(ctx context.Context, req *models.CreateAccountRequest) (*models.AccountResponse, error)
|
||||
GetAccountByID(ctx context.Context, id uuid.UUID) (*models.AccountResponse, error)
|
||||
UpdateAccount(ctx context.Context, id uuid.UUID, req *models.UpdateAccountRequest) (*models.AccountResponse, error)
|
||||
DeleteAccount(ctx context.Context, id uuid.UUID) error
|
||||
ListAccounts(ctx context.Context, req *models.ListAccountsRequest) ([]models.AccountResponse, int, error)
|
||||
GetAccountsByOrganization(ctx context.Context, organizationID uuid.UUID, outletID *uuid.UUID) ([]models.AccountResponse, error)
|
||||
GetAccountsByChartOfAccount(ctx context.Context, chartOfAccountID uuid.UUID) ([]models.AccountResponse, error)
|
||||
UpdateAccountBalance(ctx context.Context, id uuid.UUID, amount float64) error
|
||||
GetAccountBalance(ctx context.Context, id uuid.UUID) (float64, error)
|
||||
}
|
||||
|
||||
type AccountProcessorImpl struct {
|
||||
accountRepo AccountRepository
|
||||
chartOfAccountRepo ChartOfAccountRepository
|
||||
}
|
||||
|
||||
func NewAccountProcessorImpl(accountRepo AccountRepository, chartOfAccountRepo ChartOfAccountRepository) *AccountProcessorImpl {
|
||||
return &AccountProcessorImpl{
|
||||
accountRepo: accountRepo,
|
||||
chartOfAccountRepo: chartOfAccountRepo,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *AccountProcessorImpl) CreateAccount(ctx context.Context, req *models.CreateAccountRequest) (*models.AccountResponse, error) {
|
||||
// Get organization and outlet from context
|
||||
appCtx := appcontext.FromGinContext(ctx)
|
||||
organizationID := appCtx.OrganizationID
|
||||
var outletID *uuid.UUID
|
||||
if appCtx.OutletID != uuid.Nil {
|
||||
outletID = &appCtx.OutletID
|
||||
}
|
||||
|
||||
// Check if account number already exists for this organization/outlet
|
||||
existing, err := p.accountRepo.GetByNumber(ctx, organizationID, req.Number, outletID)
|
||||
if err == nil && existing != nil {
|
||||
return nil, fmt.Errorf("account with number %s already exists", req.Number)
|
||||
}
|
||||
|
||||
// Validate chart of account exists
|
||||
_, err = p.chartOfAccountRepo.GetByID(ctx, req.ChartOfAccountID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("chart of account not found: %w", err)
|
||||
}
|
||||
|
||||
entity := mappers.AccountCreateRequestToEntity(req, organizationID, outletID)
|
||||
err = p.accountRepo.Create(ctx, entity)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create account: %w", err)
|
||||
}
|
||||
|
||||
return mappers.AccountEntityToResponse(entity), nil
|
||||
}
|
||||
|
||||
func (p *AccountProcessorImpl) GetAccountByID(ctx context.Context, id uuid.UUID) (*models.AccountResponse, error) {
|
||||
entity, err := p.accountRepo.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("account not found: %w", err)
|
||||
}
|
||||
|
||||
return mappers.AccountEntityToResponse(entity), nil
|
||||
}
|
||||
|
||||
func (p *AccountProcessorImpl) UpdateAccount(ctx context.Context, id uuid.UUID, req *models.UpdateAccountRequest) (*models.AccountResponse, error) {
|
||||
entity, err := p.accountRepo.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("account not found: %w", err)
|
||||
}
|
||||
|
||||
// Check if new number already exists (if number is being updated)
|
||||
if req.Number != nil && *req.Number != entity.Number {
|
||||
existing, err := p.accountRepo.GetByNumber(ctx, entity.OrganizationID, *req.Number, entity.OutletID)
|
||||
if err == nil && existing != nil {
|
||||
return nil, fmt.Errorf("account with number %s already exists", *req.Number)
|
||||
}
|
||||
}
|
||||
|
||||
// Validate chart of account exists if provided
|
||||
if req.ChartOfAccountID != nil {
|
||||
_, err = p.chartOfAccountRepo.GetByID(ctx, *req.ChartOfAccountID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("chart of account not found: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
mappers.AccountUpdateRequestToEntity(entity, req)
|
||||
err = p.accountRepo.Update(ctx, entity)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to update account: %w", err)
|
||||
}
|
||||
|
||||
return mappers.AccountEntityToResponse(entity), nil
|
||||
}
|
||||
|
||||
func (p *AccountProcessorImpl) DeleteAccount(ctx context.Context, id uuid.UUID) error {
|
||||
entity, err := p.accountRepo.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("account not found: %w", err)
|
||||
}
|
||||
|
||||
// Prevent deletion of system accounts
|
||||
if entity.IsSystem {
|
||||
return fmt.Errorf("cannot delete system account")
|
||||
}
|
||||
|
||||
err = p.accountRepo.Delete(ctx, id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete account: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *AccountProcessorImpl) ListAccounts(ctx context.Context, req *models.ListAccountsRequest) ([]models.AccountResponse, int, error) {
|
||||
// Get organization and outlet from context
|
||||
appCtx := appcontext.FromGinContext(ctx)
|
||||
organizationID := appCtx.OrganizationID
|
||||
var outletID *uuid.UUID
|
||||
if appCtx.OutletID != uuid.Nil {
|
||||
outletID = &appCtx.OutletID
|
||||
}
|
||||
|
||||
filterEntity := &entities.Account{
|
||||
OrganizationID: organizationID,
|
||||
OutletID: outletID,
|
||||
ChartOfAccountID: *req.ChartOfAccountID,
|
||||
AccountType: entities.AccountType(*req.AccountType),
|
||||
}
|
||||
|
||||
entities, total, err := p.accountRepo.List(ctx, filterEntity)
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("failed to list accounts: %w", err)
|
||||
}
|
||||
|
||||
responses := make([]models.AccountResponse, len(entities))
|
||||
for i, entity := range entities {
|
||||
responses[i] = *mappers.AccountEntityToResponse(entity)
|
||||
}
|
||||
|
||||
return responses, total, nil
|
||||
}
|
||||
|
||||
func (p *AccountProcessorImpl) GetAccountsByOrganization(ctx context.Context, organizationID uuid.UUID, outletID *uuid.UUID) ([]models.AccountResponse, error) {
|
||||
entities, err := p.accountRepo.GetByOrganization(ctx, organizationID, outletID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get accounts by organization: %w", err)
|
||||
}
|
||||
|
||||
responses := make([]models.AccountResponse, len(entities))
|
||||
for i, entity := range entities {
|
||||
responses[i] = *mappers.AccountEntityToResponse(entity)
|
||||
}
|
||||
|
||||
return responses, nil
|
||||
}
|
||||
|
||||
func (p *AccountProcessorImpl) GetAccountsByChartOfAccount(ctx context.Context, chartOfAccountID uuid.UUID) ([]models.AccountResponse, error) {
|
||||
entities, err := p.accountRepo.GetByChartOfAccount(ctx, chartOfAccountID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get accounts by chart of account: %w", err)
|
||||
}
|
||||
|
||||
responses := make([]models.AccountResponse, len(entities))
|
||||
for i, entity := range entities {
|
||||
responses[i] = *mappers.AccountEntityToResponse(entity)
|
||||
}
|
||||
|
||||
return responses, nil
|
||||
}
|
||||
|
||||
func (p *AccountProcessorImpl) UpdateAccountBalance(ctx context.Context, id uuid.UUID, amount float64) error {
|
||||
_, err := p.accountRepo.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("account not found: %w", err)
|
||||
}
|
||||
|
||||
err = p.accountRepo.UpdateBalance(ctx, id, amount)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update account balance: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *AccountProcessorImpl) GetAccountBalance(ctx context.Context, id uuid.UUID) (float64, error) {
|
||||
balance, err := p.accountRepo.GetBalance(ctx, id)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to get account balance: %w", err)
|
||||
}
|
||||
|
||||
return balance, nil
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
package processor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"apskel-pos-be/internal/appcontext"
|
||||
"apskel-pos-be/internal/entities"
|
||||
"apskel-pos-be/internal/mappers"
|
||||
"apskel-pos-be/internal/models"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
|
||||
type ChartOfAccountProcessor interface {
|
||||
CreateChartOfAccount(ctx context.Context, req *models.CreateChartOfAccountRequest) (*models.ChartOfAccountResponse, error)
|
||||
GetChartOfAccountByID(ctx context.Context, id uuid.UUID) (*models.ChartOfAccountResponse, error)
|
||||
UpdateChartOfAccount(ctx context.Context, id uuid.UUID, req *models.UpdateChartOfAccountRequest) (*models.ChartOfAccountResponse, error)
|
||||
DeleteChartOfAccount(ctx context.Context, id uuid.UUID) error
|
||||
ListChartOfAccounts(ctx context.Context, req *models.ListChartOfAccountsRequest) ([]models.ChartOfAccountResponse, int, error)
|
||||
GetChartOfAccountsByOrganization(ctx context.Context, organizationID uuid.UUID, outletID *uuid.UUID) ([]models.ChartOfAccountResponse, error)
|
||||
GetChartOfAccountsByType(ctx context.Context, organizationID uuid.UUID, chartOfAccountTypeID uuid.UUID, outletID *uuid.UUID) ([]models.ChartOfAccountResponse, error)
|
||||
CreateDefaultChartOfAccounts(ctx context.Context, organizationID uuid.UUID, outletID *uuid.UUID) error
|
||||
}
|
||||
|
||||
type ChartOfAccountProcessorImpl struct {
|
||||
chartOfAccountRepo ChartOfAccountRepository
|
||||
chartOfAccountTypeRepo ChartOfAccountTypeRepository
|
||||
}
|
||||
|
||||
func NewChartOfAccountProcessorImpl(chartOfAccountRepo ChartOfAccountRepository, chartOfAccountTypeRepo ChartOfAccountTypeRepository) *ChartOfAccountProcessorImpl {
|
||||
return &ChartOfAccountProcessorImpl{
|
||||
chartOfAccountRepo: chartOfAccountRepo,
|
||||
chartOfAccountTypeRepo: chartOfAccountTypeRepo,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ChartOfAccountProcessorImpl) CreateChartOfAccount(ctx context.Context, req *models.CreateChartOfAccountRequest) (*models.ChartOfAccountResponse, error) {
|
||||
// Get organization and outlet from context
|
||||
appCtx := appcontext.FromGinContext(ctx)
|
||||
organizationID := appCtx.OrganizationID
|
||||
var outletID *uuid.UUID
|
||||
if appCtx.OutletID != uuid.Nil {
|
||||
outletID = &appCtx.OutletID
|
||||
}
|
||||
|
||||
// Check if code already exists for this organization/outlet
|
||||
existing, err := p.chartOfAccountRepo.GetByCode(ctx, organizationID, req.Code, outletID)
|
||||
if err == nil && existing != nil {
|
||||
return nil, fmt.Errorf("chart of account with code %s already exists", req.Code)
|
||||
}
|
||||
|
||||
// Validate parent exists if provided
|
||||
if req.ParentID != nil {
|
||||
_, err := p.chartOfAccountRepo.GetByID(ctx, *req.ParentID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parent chart of account not found: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Validate chart of account type exists
|
||||
_, err = p.chartOfAccountTypeRepo.GetByID(ctx, req.ChartOfAccountTypeID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("chart of account type not found: %w", err)
|
||||
}
|
||||
|
||||
entity := mappers.ChartOfAccountCreateRequestToEntity(req, organizationID, outletID)
|
||||
err = p.chartOfAccountRepo.Create(ctx, entity)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create chart of account: %w", err)
|
||||
}
|
||||
|
||||
return mappers.ChartOfAccountEntityToResponse(entity), nil
|
||||
}
|
||||
|
||||
func (p *ChartOfAccountProcessorImpl) GetChartOfAccountByID(ctx context.Context, id uuid.UUID) (*models.ChartOfAccountResponse, error) {
|
||||
entity, err := p.chartOfAccountRepo.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("chart of account not found: %w", err)
|
||||
}
|
||||
|
||||
return mappers.ChartOfAccountEntityToResponse(entity), nil
|
||||
}
|
||||
|
||||
func (p *ChartOfAccountProcessorImpl) UpdateChartOfAccount(ctx context.Context, id uuid.UUID, req *models.UpdateChartOfAccountRequest) (*models.ChartOfAccountResponse, error) {
|
||||
entity, err := p.chartOfAccountRepo.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("chart of account not found: %w", err)
|
||||
}
|
||||
|
||||
// Check if new code already exists (if code is being updated)
|
||||
if req.Code != nil && *req.Code != entity.Code {
|
||||
existing, err := p.chartOfAccountRepo.GetByCode(ctx, entity.OrganizationID, *req.Code, entity.OutletID)
|
||||
if err == nil && existing != nil {
|
||||
return nil, fmt.Errorf("chart of account with code %s already exists", *req.Code)
|
||||
}
|
||||
}
|
||||
|
||||
// Validate parent exists if provided
|
||||
if req.ParentID != nil {
|
||||
_, err := p.chartOfAccountRepo.GetByID(ctx, *req.ParentID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parent chart of account not found: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Validate chart of account type exists if provided
|
||||
if req.ChartOfAccountTypeID != nil {
|
||||
_, err = p.chartOfAccountTypeRepo.GetByID(ctx, *req.ChartOfAccountTypeID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("chart of account type not found: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
mappers.ChartOfAccountUpdateRequestToEntity(entity, req)
|
||||
err = p.chartOfAccountRepo.Update(ctx, entity)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to update chart of account: %w", err)
|
||||
}
|
||||
|
||||
return mappers.ChartOfAccountEntityToResponse(entity), nil
|
||||
}
|
||||
|
||||
func (p *ChartOfAccountProcessorImpl) DeleteChartOfAccount(ctx context.Context, id uuid.UUID) error {
|
||||
entity, err := p.chartOfAccountRepo.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("chart of account not found: %w", err)
|
||||
}
|
||||
|
||||
// Prevent deletion of system accounts
|
||||
if entity.IsSystem {
|
||||
return fmt.Errorf("cannot delete system chart of account")
|
||||
}
|
||||
|
||||
err = p.chartOfAccountRepo.Delete(ctx, id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete chart of account: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *ChartOfAccountProcessorImpl) ListChartOfAccounts(ctx context.Context, req *models.ListChartOfAccountsRequest) ([]models.ChartOfAccountResponse, int, error) {
|
||||
// Get organization and outlet from context
|
||||
appCtx := appcontext.FromGinContext(ctx)
|
||||
organizationID := appCtx.OrganizationID
|
||||
var outletID *uuid.UUID
|
||||
if appCtx.OutletID != uuid.Nil {
|
||||
outletID = &appCtx.OutletID
|
||||
}
|
||||
|
||||
filterEntity := &entities.ChartOfAccount{
|
||||
OrganizationID: organizationID,
|
||||
OutletID: outletID,
|
||||
ChartOfAccountTypeID: *req.ChartOfAccountTypeID,
|
||||
ParentID: req.ParentID,
|
||||
}
|
||||
|
||||
entities, total, err := p.chartOfAccountRepo.List(ctx, filterEntity)
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("failed to list chart of accounts: %w", err)
|
||||
}
|
||||
|
||||
responses := make([]models.ChartOfAccountResponse, len(entities))
|
||||
for i, entity := range entities {
|
||||
responses[i] = *mappers.ChartOfAccountEntityToResponse(entity)
|
||||
}
|
||||
|
||||
return responses, total, nil
|
||||
}
|
||||
|
||||
func (p *ChartOfAccountProcessorImpl) GetChartOfAccountsByOrganization(ctx context.Context, organizationID uuid.UUID, outletID *uuid.UUID) ([]models.ChartOfAccountResponse, error) {
|
||||
entities, err := p.chartOfAccountRepo.GetByOrganization(ctx, organizationID, outletID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get chart of accounts by organization: %w", err)
|
||||
}
|
||||
|
||||
responses := make([]models.ChartOfAccountResponse, len(entities))
|
||||
for i, entity := range entities {
|
||||
responses[i] = *mappers.ChartOfAccountEntityToResponse(entity)
|
||||
}
|
||||
|
||||
return responses, nil
|
||||
}
|
||||
|
||||
func (p *ChartOfAccountProcessorImpl) GetChartOfAccountsByType(ctx context.Context, organizationID uuid.UUID, chartOfAccountTypeID uuid.UUID, outletID *uuid.UUID) ([]models.ChartOfAccountResponse, error) {
|
||||
entities, err := p.chartOfAccountRepo.GetByType(ctx, organizationID, chartOfAccountTypeID, outletID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get chart of accounts by type: %w", err)
|
||||
}
|
||||
|
||||
responses := make([]models.ChartOfAccountResponse, len(entities))
|
||||
for i, entity := range entities {
|
||||
responses[i] = *mappers.ChartOfAccountEntityToResponse(entity)
|
||||
}
|
||||
|
||||
return responses, nil
|
||||
}
|
||||
|
||||
func (p *ChartOfAccountProcessorImpl) CreateDefaultChartOfAccounts(ctx context.Context, organizationID uuid.UUID, outletID *uuid.UUID) error {
|
||||
// This method will be implemented to create default chart of accounts
|
||||
// based on the JSON template when a new organization/outlet is created
|
||||
// For now, we'll return nil as this is a placeholder
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package processor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"apskel-pos-be/internal/mappers"
|
||||
"apskel-pos-be/internal/models"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
|
||||
type ChartOfAccountTypeProcessor interface {
|
||||
CreateChartOfAccountType(ctx context.Context, req *models.CreateChartOfAccountTypeRequest) (*models.ChartOfAccountTypeResponse, error)
|
||||
GetChartOfAccountTypeByID(ctx context.Context, id uuid.UUID) (*models.ChartOfAccountTypeResponse, error)
|
||||
UpdateChartOfAccountType(ctx context.Context, id uuid.UUID, req *models.UpdateChartOfAccountTypeRequest) (*models.ChartOfAccountTypeResponse, error)
|
||||
DeleteChartOfAccountType(ctx context.Context, id uuid.UUID) error
|
||||
ListChartOfAccountTypes(ctx context.Context, filters map[string]interface{}, page, limit int) ([]models.ChartOfAccountTypeResponse, int, error)
|
||||
}
|
||||
|
||||
type ChartOfAccountTypeProcessorImpl struct {
|
||||
chartOfAccountTypeRepo ChartOfAccountTypeRepository
|
||||
}
|
||||
|
||||
func NewChartOfAccountTypeProcessorImpl(chartOfAccountTypeRepo ChartOfAccountTypeRepository) *ChartOfAccountTypeProcessorImpl {
|
||||
return &ChartOfAccountTypeProcessorImpl{
|
||||
chartOfAccountTypeRepo: chartOfAccountTypeRepo,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ChartOfAccountTypeProcessorImpl) CreateChartOfAccountType(ctx context.Context, req *models.CreateChartOfAccountTypeRequest) (*models.ChartOfAccountTypeResponse, error) {
|
||||
// Check if code already exists
|
||||
existing, err := p.chartOfAccountTypeRepo.GetByCode(ctx, req.Code)
|
||||
if err == nil && existing != nil {
|
||||
return nil, fmt.Errorf("chart of account type with code %s already exists", req.Code)
|
||||
}
|
||||
|
||||
entity := mappers.ChartOfAccountTypeCreateRequestToEntity(req)
|
||||
err = p.chartOfAccountTypeRepo.Create(ctx, entity)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create chart of account type: %w", err)
|
||||
}
|
||||
|
||||
return mappers.ChartOfAccountTypeEntityToResponse(entity), nil
|
||||
}
|
||||
|
||||
func (p *ChartOfAccountTypeProcessorImpl) GetChartOfAccountTypeByID(ctx context.Context, id uuid.UUID) (*models.ChartOfAccountTypeResponse, error) {
|
||||
entity, err := p.chartOfAccountTypeRepo.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("chart of account type not found: %w", err)
|
||||
}
|
||||
|
||||
return mappers.ChartOfAccountTypeEntityToResponse(entity), nil
|
||||
}
|
||||
|
||||
func (p *ChartOfAccountTypeProcessorImpl) UpdateChartOfAccountType(ctx context.Context, id uuid.UUID, req *models.UpdateChartOfAccountTypeRequest) (*models.ChartOfAccountTypeResponse, error) {
|
||||
entity, err := p.chartOfAccountTypeRepo.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("chart of account type not found: %w", err)
|
||||
}
|
||||
|
||||
// Check if new code already exists (if code is being updated)
|
||||
if req.Code != nil && *req.Code != entity.Code {
|
||||
existing, err := p.chartOfAccountTypeRepo.GetByCode(ctx, *req.Code)
|
||||
if err == nil && existing != nil {
|
||||
return nil, fmt.Errorf("chart of account type with code %s already exists", *req.Code)
|
||||
}
|
||||
}
|
||||
|
||||
mappers.ChartOfAccountTypeUpdateRequestToEntity(entity, req)
|
||||
err = p.chartOfAccountTypeRepo.Update(ctx, entity)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to update chart of account type: %w", err)
|
||||
}
|
||||
|
||||
return mappers.ChartOfAccountTypeEntityToResponse(entity), nil
|
||||
}
|
||||
|
||||
func (p *ChartOfAccountTypeProcessorImpl) DeleteChartOfAccountType(ctx context.Context, id uuid.UUID) error {
|
||||
_, err := p.chartOfAccountTypeRepo.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("chart of account type not found: %w", err)
|
||||
}
|
||||
|
||||
err = p.chartOfAccountTypeRepo.Delete(ctx, id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete chart of account type: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *ChartOfAccountTypeProcessorImpl) ListChartOfAccountTypes(ctx context.Context, filters map[string]interface{}, page, limit int) ([]models.ChartOfAccountTypeResponse, int, error) {
|
||||
entities, total, err := p.chartOfAccountTypeRepo.List(ctx, filters, page, limit)
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("failed to list chart of account types: %w", err)
|
||||
}
|
||||
|
||||
responses := make([]models.ChartOfAccountTypeResponse, len(entities))
|
||||
for i, entity := range entities {
|
||||
responses[i] = *mappers.ChartOfAccountTypeEntityToResponse(entity)
|
||||
}
|
||||
|
||||
return responses, total, nil
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
package processor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"apskel-pos-be/internal/entities"
|
||||
"apskel-pos-be/internal/mappers"
|
||||
"apskel-pos-be/internal/models"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type IngredientUnitConverterRepository interface {
|
||||
Create(ctx context.Context, converter *entities.IngredientUnitConverter) error
|
||||
GetByID(ctx context.Context, id, organizationID uuid.UUID) (*entities.IngredientUnitConverter, error)
|
||||
GetByIDAndOrganizationID(ctx context.Context, id, organizationID uuid.UUID) (*entities.IngredientUnitConverter, error)
|
||||
Update(ctx context.Context, converter *entities.IngredientUnitConverter) error
|
||||
Delete(ctx context.Context, id, organizationID uuid.UUID) error
|
||||
List(ctx context.Context, organizationID uuid.UUID, filters map[string]interface{}, page, limit int) ([]*entities.IngredientUnitConverter, int, error)
|
||||
GetByIngredientAndUnits(ctx context.Context, ingredientID, fromUnitID, toUnitID, organizationID uuid.UUID) (*entities.IngredientUnitConverter, error)
|
||||
GetConvertersForIngredient(ctx context.Context, ingredientID, organizationID uuid.UUID) ([]*entities.IngredientUnitConverter, error)
|
||||
GetActiveConverters(ctx context.Context, organizationID uuid.UUID) ([]*entities.IngredientUnitConverter, error)
|
||||
ConvertQuantity(ctx context.Context, ingredientID, fromUnitID, toUnitID, organizationID uuid.UUID, quantity float64) (float64, error)
|
||||
}
|
||||
|
||||
type IngredientUnitConverterProcessor interface {
|
||||
CreateIngredientUnitConverter(ctx context.Context, organizationID, userID uuid.UUID, req *models.CreateIngredientUnitConverterRequest) (*models.IngredientUnitConverterResponse, error)
|
||||
UpdateIngredientUnitConverter(ctx context.Context, id, organizationID, userID uuid.UUID, req *models.UpdateIngredientUnitConverterRequest) (*models.IngredientUnitConverterResponse, error)
|
||||
DeleteIngredientUnitConverter(ctx context.Context, id, organizationID uuid.UUID) error
|
||||
GetIngredientUnitConverterByID(ctx context.Context, id, organizationID uuid.UUID) (*models.IngredientUnitConverterResponse, error)
|
||||
ListIngredientUnitConverters(ctx context.Context, organizationID uuid.UUID, filters map[string]interface{}, page, limit int) ([]*models.IngredientUnitConverterResponse, int, error)
|
||||
GetConvertersForIngredient(ctx context.Context, ingredientID, organizationID uuid.UUID) ([]*models.IngredientUnitConverterResponse, error)
|
||||
ConvertUnit(ctx context.Context, organizationID uuid.UUID, req *models.ConvertUnitRequest) (*models.ConvertUnitResponse, error)
|
||||
}
|
||||
|
||||
type IngredientUnitConverterProcessorImpl struct {
|
||||
converterRepo IngredientUnitConverterRepository
|
||||
ingredientRepo IngredientRepository
|
||||
unitRepo UnitRepository
|
||||
}
|
||||
|
||||
func NewIngredientUnitConverterProcessorImpl(
|
||||
converterRepo IngredientUnitConverterRepository,
|
||||
ingredientRepo IngredientRepository,
|
||||
unitRepo UnitRepository,
|
||||
) *IngredientUnitConverterProcessorImpl {
|
||||
return &IngredientUnitConverterProcessorImpl{
|
||||
converterRepo: converterRepo,
|
||||
ingredientRepo: ingredientRepo,
|
||||
unitRepo: unitRepo,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *IngredientUnitConverterProcessorImpl) CreateIngredientUnitConverter(ctx context.Context, organizationID, userID uuid.UUID, req *models.CreateIngredientUnitConverterRequest) (*models.IngredientUnitConverterResponse, error) {
|
||||
// Validate ingredient exists
|
||||
_, err := p.ingredientRepo.GetByID(ctx, req.IngredientID, organizationID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ingredient not found: %w", err)
|
||||
}
|
||||
|
||||
// Validate units exist
|
||||
_, err = p.unitRepo.GetByID(ctx, req.FromUnitID, organizationID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("from unit not found: %w", err)
|
||||
}
|
||||
|
||||
_, err = p.unitRepo.GetByID(ctx, req.ToUnitID, organizationID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("to unit not found: %w", err)
|
||||
}
|
||||
|
||||
// Check if converter already exists
|
||||
existingConverter, err := p.converterRepo.GetByIngredientAndUnits(ctx, req.IngredientID, req.FromUnitID, req.ToUnitID, organizationID)
|
||||
if err == nil && existingConverter != nil {
|
||||
return nil, fmt.Errorf("converter already exists for this ingredient and unit combination")
|
||||
}
|
||||
|
||||
// Set default values
|
||||
isActive := true
|
||||
if req.IsActive != nil {
|
||||
isActive = *req.IsActive
|
||||
}
|
||||
|
||||
// Create entity
|
||||
converter := &entities.IngredientUnitConverter{
|
||||
OrganizationID: organizationID,
|
||||
IngredientID: req.IngredientID,
|
||||
FromUnitID: req.FromUnitID,
|
||||
ToUnitID: req.ToUnitID,
|
||||
ConversionFactor: req.ConversionFactor,
|
||||
IsActive: isActive,
|
||||
CreatedBy: userID,
|
||||
UpdatedBy: userID,
|
||||
}
|
||||
|
||||
err = p.converterRepo.Create(ctx, converter)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create ingredient unit converter: %w", err)
|
||||
}
|
||||
|
||||
// Get the created converter with relationships
|
||||
createdConverter, err := p.converterRepo.GetByID(ctx, converter.ID, organizationID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get created converter: %w", err)
|
||||
}
|
||||
|
||||
return mappers.IngredientUnitConverterEntityToResponse(createdConverter), nil
|
||||
}
|
||||
|
||||
func (p *IngredientUnitConverterProcessorImpl) UpdateIngredientUnitConverter(ctx context.Context, id, organizationID, userID uuid.UUID, req *models.UpdateIngredientUnitConverterRequest) (*models.IngredientUnitConverterResponse, error) {
|
||||
// Get existing converter
|
||||
converter, err := p.converterRepo.GetByID(ctx, id, organizationID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ingredient unit converter not found: %w", err)
|
||||
}
|
||||
|
||||
// Update fields if provided
|
||||
if req.FromUnitID != nil {
|
||||
// Validate new unit exists
|
||||
_, err = p.unitRepo.GetByID(ctx, *req.FromUnitID, organizationID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("from unit not found: %w", err)
|
||||
}
|
||||
converter.FromUnitID = *req.FromUnitID
|
||||
}
|
||||
|
||||
if req.ToUnitID != nil {
|
||||
// Validate new unit exists
|
||||
_, err = p.unitRepo.GetByID(ctx, *req.ToUnitID, organizationID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("to unit not found: %w", err)
|
||||
}
|
||||
converter.ToUnitID = *req.ToUnitID
|
||||
}
|
||||
|
||||
if req.ConversionFactor != nil {
|
||||
converter.ConversionFactor = *req.ConversionFactor
|
||||
}
|
||||
|
||||
if req.IsActive != nil {
|
||||
converter.IsActive = *req.IsActive
|
||||
}
|
||||
|
||||
converter.UpdatedBy = userID
|
||||
|
||||
err = p.converterRepo.Update(ctx, converter)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to update ingredient unit converter: %w", err)
|
||||
}
|
||||
|
||||
// Get the updated converter with relationships
|
||||
updatedConverter, err := p.converterRepo.GetByID(ctx, converter.ID, organizationID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get updated converter: %w", err)
|
||||
}
|
||||
|
||||
return mappers.IngredientUnitConverterEntityToResponse(updatedConverter), nil
|
||||
}
|
||||
|
||||
func (p *IngredientUnitConverterProcessorImpl) DeleteIngredientUnitConverter(ctx context.Context, id, organizationID uuid.UUID) error {
|
||||
// Check if converter exists
|
||||
_, err := p.converterRepo.GetByID(ctx, id, organizationID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ingredient unit converter not found: %w", err)
|
||||
}
|
||||
|
||||
err = p.converterRepo.Delete(ctx, id, organizationID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete ingredient unit converter: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *IngredientUnitConverterProcessorImpl) GetIngredientUnitConverterByID(ctx context.Context, id, organizationID uuid.UUID) (*models.IngredientUnitConverterResponse, error) {
|
||||
converter, err := p.converterRepo.GetByID(ctx, id, organizationID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ingredient unit converter not found: %w", err)
|
||||
}
|
||||
|
||||
return mappers.IngredientUnitConverterEntityToResponse(converter), nil
|
||||
}
|
||||
|
||||
func (p *IngredientUnitConverterProcessorImpl) ListIngredientUnitConverters(ctx context.Context, organizationID uuid.UUID, filters map[string]interface{}, page, limit int) ([]*models.IngredientUnitConverterResponse, int, error) {
|
||||
converters, total, err := p.converterRepo.List(ctx, organizationID, filters, page, limit)
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("failed to list ingredient unit converters: %w", err)
|
||||
}
|
||||
|
||||
responses := make([]*models.IngredientUnitConverterResponse, len(converters))
|
||||
for i, converter := range converters {
|
||||
responses[i] = mappers.IngredientUnitConverterEntityToResponse(converter)
|
||||
}
|
||||
|
||||
return responses, total, nil
|
||||
}
|
||||
|
||||
func (p *IngredientUnitConverterProcessorImpl) GetConvertersForIngredient(ctx context.Context, ingredientID, organizationID uuid.UUID) ([]*models.IngredientUnitConverterResponse, error) {
|
||||
converters, err := p.converterRepo.GetConvertersForIngredient(ctx, ingredientID, organizationID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get converters for ingredient: %w", err)
|
||||
}
|
||||
|
||||
responses := make([]*models.IngredientUnitConverterResponse, len(converters))
|
||||
for i, converter := range converters {
|
||||
responses[i] = mappers.IngredientUnitConverterEntityToResponse(converter)
|
||||
}
|
||||
|
||||
return responses, nil
|
||||
}
|
||||
|
||||
func (p *IngredientUnitConverterProcessorImpl) ConvertUnit(ctx context.Context, organizationID uuid.UUID, req *models.ConvertUnitRequest) (*models.ConvertUnitResponse, error) {
|
||||
// Get ingredient and units for response
|
||||
ingredient, err := p.ingredientRepo.GetByID(ctx, req.IngredientID, organizationID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ingredient not found: %w", err)
|
||||
}
|
||||
|
||||
fromUnit, err := p.unitRepo.GetByID(ctx, req.FromUnitID, organizationID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("from unit not found: %w", err)
|
||||
}
|
||||
|
||||
toUnit, err := p.unitRepo.GetByID(ctx, req.ToUnitID, organizationID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("to unit not found: %w", err)
|
||||
}
|
||||
|
||||
// Convert quantity
|
||||
convertedQuantity, err := p.converterRepo.ConvertQuantity(ctx, req.IngredientID, req.FromUnitID, req.ToUnitID, organizationID, req.Quantity)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert quantity: %w", err)
|
||||
}
|
||||
|
||||
// Get conversion factor for response
|
||||
converter, err := p.converterRepo.GetByIngredientAndUnits(ctx, req.IngredientID, req.FromUnitID, req.ToUnitID, organizationID)
|
||||
var conversionFactor float64
|
||||
if err == nil {
|
||||
conversionFactor = converter.ConversionFactor
|
||||
} else {
|
||||
// Try reverse converter
|
||||
reverseConverter, err := p.converterRepo.GetByIngredientAndUnits(ctx, req.IngredientID, req.ToUnitID, req.FromUnitID, organizationID)
|
||||
if err == nil {
|
||||
conversionFactor = 1.0 / reverseConverter.ConversionFactor
|
||||
}
|
||||
}
|
||||
|
||||
response := &models.ConvertUnitResponse{
|
||||
FromQuantity: req.Quantity,
|
||||
FromUnit: mappers.MapUnitEntityToResponse(fromUnit),
|
||||
ToQuantity: convertedQuantity,
|
||||
ToUnit: mappers.MapUnitEntityToResponse(toUnit),
|
||||
ConversionFactor: conversionFactor,
|
||||
Ingredient: mappers.MapIngredientEntityToResponse(ingredient),
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
@@ -0,0 +1,441 @@
|
||||
package processor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"apskel-pos-be/internal/entities"
|
||||
"apskel-pos-be/internal/mappers"
|
||||
"apskel-pos-be/internal/models"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type PurchaseOrderProcessor interface {
|
||||
CreatePurchaseOrder(ctx context.Context, organizationID uuid.UUID, req *models.CreatePurchaseOrderRequest) (*models.PurchaseOrderResponse, error)
|
||||
UpdatePurchaseOrder(ctx context.Context, id, organizationID uuid.UUID, req *models.UpdatePurchaseOrderRequest) (*models.PurchaseOrderResponse, error)
|
||||
DeletePurchaseOrder(ctx context.Context, id, organizationID uuid.UUID) error
|
||||
GetPurchaseOrderByID(ctx context.Context, id, organizationID uuid.UUID) (*models.PurchaseOrderResponse, error)
|
||||
ListPurchaseOrders(ctx context.Context, organizationID uuid.UUID, filters map[string]interface{}, page, limit int) ([]*models.PurchaseOrderResponse, int, error)
|
||||
GetPurchaseOrdersByStatus(ctx context.Context, organizationID uuid.UUID, status string) ([]*models.PurchaseOrderResponse, error)
|
||||
GetOverduePurchaseOrders(ctx context.Context, organizationID uuid.UUID) ([]*models.PurchaseOrderResponse, error)
|
||||
UpdatePurchaseOrderStatus(ctx context.Context, id, organizationID, userID, outletID uuid.UUID, status string) (*models.PurchaseOrderResponse, error)
|
||||
}
|
||||
|
||||
type PurchaseOrderProcessorImpl struct {
|
||||
purchaseOrderRepo PurchaseOrderRepository
|
||||
vendorRepo VendorRepository
|
||||
ingredientRepo IngredientRepository
|
||||
unitRepo UnitRepository
|
||||
fileRepo FileRepository
|
||||
inventoryMovementService InventoryMovementService
|
||||
unitConverterRepo IngredientUnitConverterRepository
|
||||
}
|
||||
|
||||
func NewPurchaseOrderProcessorImpl(
|
||||
purchaseOrderRepo PurchaseOrderRepository,
|
||||
vendorRepo VendorRepository,
|
||||
ingredientRepo IngredientRepository,
|
||||
unitRepo UnitRepository,
|
||||
fileRepo FileRepository,
|
||||
inventoryMovementService InventoryMovementService,
|
||||
unitConverterRepo IngredientUnitConverterRepository,
|
||||
) *PurchaseOrderProcessorImpl {
|
||||
return &PurchaseOrderProcessorImpl{
|
||||
purchaseOrderRepo: purchaseOrderRepo,
|
||||
vendorRepo: vendorRepo,
|
||||
ingredientRepo: ingredientRepo,
|
||||
unitRepo: unitRepo,
|
||||
fileRepo: fileRepo,
|
||||
inventoryMovementService: inventoryMovementService,
|
||||
unitConverterRepo: unitConverterRepo,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PurchaseOrderProcessorImpl) CreatePurchaseOrder(ctx context.Context, organizationID uuid.UUID, req *models.CreatePurchaseOrderRequest) (*models.PurchaseOrderResponse, error) {
|
||||
// Check if vendor exists and belongs to organization
|
||||
_, err := p.vendorRepo.GetByIDAndOrganizationID(ctx, req.VendorID, organizationID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("vendor not found: %w", err)
|
||||
}
|
||||
|
||||
// Check if PO number already exists in organization
|
||||
existingPO, err := p.purchaseOrderRepo.GetByPONumber(ctx, req.PONumber, organizationID)
|
||||
if err == nil && existingPO != nil {
|
||||
return nil, fmt.Errorf("purchase order with PO number %s already exists in this organization", req.PONumber)
|
||||
}
|
||||
|
||||
// Validate ingredients and units exist
|
||||
for i, item := range req.Items {
|
||||
_, err := p.ingredientRepo.GetByID(ctx, item.IngredientID, organizationID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ingredient not found for item %d: %w", i, err)
|
||||
}
|
||||
|
||||
_, err = p.unitRepo.GetByID(ctx, item.UnitID, organizationID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unit not found for item %d: %w", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate total amount
|
||||
totalAmount := 0.0
|
||||
for _, item := range req.Items {
|
||||
totalAmount += item.Amount
|
||||
}
|
||||
|
||||
// Create purchase order entity
|
||||
poEntity := &entities.PurchaseOrder{
|
||||
OrganizationID: organizationID,
|
||||
VendorID: req.VendorID,
|
||||
PONumber: req.PONumber,
|
||||
TransactionDate: req.TransactionDate,
|
||||
DueDate: req.DueDate,
|
||||
Reference: req.Reference,
|
||||
Status: "draft", // Default status
|
||||
Message: req.Message,
|
||||
TotalAmount: totalAmount,
|
||||
}
|
||||
|
||||
if req.Status != nil {
|
||||
poEntity.Status = *req.Status
|
||||
}
|
||||
|
||||
// Create purchase order
|
||||
err = p.purchaseOrderRepo.Create(ctx, poEntity)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create purchase order: %w", err)
|
||||
}
|
||||
|
||||
// Create purchase order items
|
||||
for _, itemReq := range req.Items {
|
||||
itemEntity := &entities.PurchaseOrderItem{
|
||||
PurchaseOrderID: poEntity.ID,
|
||||
IngredientID: itemReq.IngredientID,
|
||||
Description: itemReq.Description,
|
||||
Quantity: itemReq.Quantity,
|
||||
UnitID: itemReq.UnitID,
|
||||
Amount: itemReq.Amount,
|
||||
}
|
||||
|
||||
err = p.purchaseOrderRepo.CreateItem(ctx, itemEntity)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create purchase order item: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Create attachments if provided
|
||||
for _, fileID := range req.AttachmentFileIDs {
|
||||
attachmentEntity := &entities.PurchaseOrderAttachment{
|
||||
PurchaseOrderID: poEntity.ID,
|
||||
FileID: fileID,
|
||||
}
|
||||
|
||||
err = p.purchaseOrderRepo.CreateAttachment(ctx, attachmentEntity)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create purchase order attachment: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Get the created purchase order with all relations
|
||||
createdPO, err := p.purchaseOrderRepo.GetByID(ctx, poEntity.ID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get created purchase order: %w", err)
|
||||
}
|
||||
|
||||
return mappers.PurchaseOrderEntityToResponse(createdPO), nil
|
||||
}
|
||||
|
||||
func (p *PurchaseOrderProcessorImpl) UpdatePurchaseOrder(ctx context.Context, id, organizationID uuid.UUID, req *models.UpdatePurchaseOrderRequest) (*models.PurchaseOrderResponse, error) {
|
||||
// Get existing purchase order
|
||||
poEntity, err := p.purchaseOrderRepo.GetByIDAndOrganizationID(ctx, id, organizationID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("purchase order not found: %w", err)
|
||||
}
|
||||
|
||||
// Check if vendor exists and belongs to organization (if vendor is being updated)
|
||||
if req.VendorID != nil {
|
||||
_, err := p.vendorRepo.GetByIDAndOrganizationID(ctx, *req.VendorID, organizationID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("vendor not found: %w", err)
|
||||
}
|
||||
poEntity.VendorID = *req.VendorID
|
||||
}
|
||||
|
||||
// Check if PO number already exists (if PO number is being updated)
|
||||
if req.PONumber != nil && *req.PONumber != poEntity.PONumber {
|
||||
existingPO, err := p.purchaseOrderRepo.GetByPONumber(ctx, *req.PONumber, organizationID)
|
||||
if err == nil && existingPO != nil {
|
||||
return nil, fmt.Errorf("purchase order with PO number %s already exists in this organization", *req.PONumber)
|
||||
}
|
||||
poEntity.PONumber = *req.PONumber
|
||||
}
|
||||
|
||||
// Update other fields
|
||||
if req.TransactionDate != nil {
|
||||
poEntity.TransactionDate = *req.TransactionDate
|
||||
}
|
||||
if req.DueDate != nil {
|
||||
poEntity.DueDate = *req.DueDate
|
||||
}
|
||||
if req.Reference != nil {
|
||||
poEntity.Reference = req.Reference
|
||||
}
|
||||
if req.Status != nil {
|
||||
poEntity.Status = *req.Status
|
||||
}
|
||||
if req.Message != nil {
|
||||
poEntity.Message = req.Message
|
||||
}
|
||||
|
||||
// Update items if provided
|
||||
if req.Items != nil {
|
||||
// Delete existing items
|
||||
err = p.purchaseOrderRepo.DeleteItemsByPurchaseOrderID(ctx, poEntity.ID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to delete existing items: %w", err)
|
||||
}
|
||||
|
||||
// Create new items
|
||||
totalAmount := 0.0
|
||||
for _, itemReq := range req.Items {
|
||||
// Validate ingredients and units exist
|
||||
if itemReq.IngredientID != nil {
|
||||
_, err := p.ingredientRepo.GetByID(ctx, *itemReq.IngredientID, organizationID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ingredient not found: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if itemReq.UnitID != nil {
|
||||
_, err := p.unitRepo.GetByID(ctx, *itemReq.UnitID, organizationID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unit not found: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Use existing values if not provided
|
||||
ingredientID := poEntity.Items[0].IngredientID // This is a simplified approach
|
||||
unitID := poEntity.Items[0].UnitID
|
||||
quantity := poEntity.Items[0].Quantity
|
||||
amount := poEntity.Items[0].Amount
|
||||
description := poEntity.Items[0].Description
|
||||
|
||||
if itemReq.IngredientID != nil {
|
||||
ingredientID = *itemReq.IngredientID
|
||||
}
|
||||
if itemReq.UnitID != nil {
|
||||
unitID = *itemReq.UnitID
|
||||
}
|
||||
if itemReq.Quantity != nil {
|
||||
quantity = *itemReq.Quantity
|
||||
}
|
||||
if itemReq.Amount != nil {
|
||||
amount = *itemReq.Amount
|
||||
}
|
||||
if itemReq.Description != nil {
|
||||
description = itemReq.Description
|
||||
}
|
||||
|
||||
itemEntity := &entities.PurchaseOrderItem{
|
||||
PurchaseOrderID: poEntity.ID,
|
||||
IngredientID: ingredientID,
|
||||
Description: description,
|
||||
Quantity: quantity,
|
||||
UnitID: unitID,
|
||||
Amount: amount,
|
||||
}
|
||||
|
||||
err = p.purchaseOrderRepo.CreateItem(ctx, itemEntity)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create purchase order item: %w", err)
|
||||
}
|
||||
|
||||
totalAmount += amount
|
||||
}
|
||||
|
||||
poEntity.TotalAmount = totalAmount
|
||||
}
|
||||
|
||||
// Update attachments if provided
|
||||
if req.AttachmentFileIDs != nil {
|
||||
// Delete existing attachments
|
||||
err = p.purchaseOrderRepo.DeleteAttachmentsByPurchaseOrderID(ctx, poEntity.ID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to delete existing attachments: %w", err)
|
||||
}
|
||||
|
||||
// Create new attachments
|
||||
for _, fileID := range req.AttachmentFileIDs {
|
||||
attachmentEntity := &entities.PurchaseOrderAttachment{
|
||||
PurchaseOrderID: poEntity.ID,
|
||||
FileID: fileID,
|
||||
}
|
||||
|
||||
err = p.purchaseOrderRepo.CreateAttachment(ctx, attachmentEntity)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create purchase order attachment: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update purchase order
|
||||
err = p.purchaseOrderRepo.Update(ctx, poEntity)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to update purchase order: %w", err)
|
||||
}
|
||||
|
||||
// Get the updated purchase order with all relations
|
||||
updatedPO, err := p.purchaseOrderRepo.GetByID(ctx, poEntity.ID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get updated purchase order: %w", err)
|
||||
}
|
||||
|
||||
return mappers.PurchaseOrderEntityToResponse(updatedPO), nil
|
||||
}
|
||||
|
||||
func (p *PurchaseOrderProcessorImpl) DeletePurchaseOrder(ctx context.Context, id, organizationID uuid.UUID) error {
|
||||
_, err := p.purchaseOrderRepo.GetByIDAndOrganizationID(ctx, id, organizationID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("purchase order not found: %w", err)
|
||||
}
|
||||
|
||||
err = p.purchaseOrderRepo.Delete(ctx, id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete purchase order: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *PurchaseOrderProcessorImpl) GetPurchaseOrderByID(ctx context.Context, id, organizationID uuid.UUID) (*models.PurchaseOrderResponse, error) {
|
||||
poEntity, err := p.purchaseOrderRepo.GetByIDAndOrganizationID(ctx, id, organizationID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("purchase order not found: %w", err)
|
||||
}
|
||||
|
||||
return mappers.PurchaseOrderEntityToResponse(poEntity), nil
|
||||
}
|
||||
|
||||
func (p *PurchaseOrderProcessorImpl) ListPurchaseOrders(ctx context.Context, organizationID uuid.UUID, filters map[string]interface{}, page, limit int) ([]*models.PurchaseOrderResponse, int, error) {
|
||||
offset := (page - 1) * limit
|
||||
poEntities, total, err := p.purchaseOrderRepo.List(ctx, organizationID, filters, limit, offset)
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("failed to list purchase orders: %w", err)
|
||||
}
|
||||
|
||||
poResponses := make([]*models.PurchaseOrderResponse, len(poEntities))
|
||||
for i, poEntity := range poEntities {
|
||||
poResponses[i] = mappers.PurchaseOrderEntityToResponse(poEntity)
|
||||
}
|
||||
|
||||
totalPages := int((total + int64(limit) - 1) / int64(limit))
|
||||
return poResponses, totalPages, nil
|
||||
}
|
||||
|
||||
func (p *PurchaseOrderProcessorImpl) GetPurchaseOrdersByStatus(ctx context.Context, organizationID uuid.UUID, status string) ([]*models.PurchaseOrderResponse, error) {
|
||||
poEntities, err := p.purchaseOrderRepo.GetByStatus(ctx, organizationID, status)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get purchase orders by status: %w", err)
|
||||
}
|
||||
|
||||
poResponses := make([]*models.PurchaseOrderResponse, len(poEntities))
|
||||
for i, poEntity := range poEntities {
|
||||
poResponses[i] = mappers.PurchaseOrderEntityToResponse(poEntity)
|
||||
}
|
||||
|
||||
return poResponses, nil
|
||||
}
|
||||
|
||||
func (p *PurchaseOrderProcessorImpl) GetOverduePurchaseOrders(ctx context.Context, organizationID uuid.UUID) ([]*models.PurchaseOrderResponse, error) {
|
||||
poEntities, err := p.purchaseOrderRepo.GetOverdue(ctx, organizationID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get overdue purchase orders: %w", err)
|
||||
}
|
||||
|
||||
poResponses := make([]*models.PurchaseOrderResponse, len(poEntities))
|
||||
for i, poEntity := range poEntities {
|
||||
poResponses[i] = mappers.PurchaseOrderEntityToResponse(poEntity)
|
||||
}
|
||||
|
||||
return poResponses, nil
|
||||
}
|
||||
|
||||
func (p *PurchaseOrderProcessorImpl) UpdatePurchaseOrderStatus(ctx context.Context, id, organizationID, userID, outletID uuid.UUID, status string) (*models.PurchaseOrderResponse, error) {
|
||||
// Get the purchase order with items to check current status
|
||||
po, err := p.purchaseOrderRepo.GetByIDAndOrganizationID(ctx, id, organizationID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("purchase order not found: %w", err)
|
||||
}
|
||||
|
||||
// Check if status is changing to "received" and current status is not "received"
|
||||
if status == "received" && po.Status != "received" {
|
||||
// Get purchase order with items for inventory update
|
||||
poWithItems, err := p.purchaseOrderRepo.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get purchase order with items: %w", err)
|
||||
}
|
||||
|
||||
// Update inventory for each item
|
||||
for _, item := range poWithItems.Items {
|
||||
// Get ingredient to find its base unit
|
||||
ingredient, err := p.ingredientRepo.GetByID(ctx, item.IngredientID, organizationID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get ingredient %s: %w", item.IngredientID, err)
|
||||
}
|
||||
|
||||
// Convert quantity to ingredient's base unit if needed
|
||||
quantityToAdd := item.Quantity
|
||||
if item.UnitID != ingredient.UnitID {
|
||||
// Convert from purchase unit to ingredient's base unit
|
||||
convertedQuantity, err := p.unitConverterRepo.ConvertQuantity(ctx, item.IngredientID, item.UnitID, ingredient.UnitID, organizationID, item.Quantity)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert quantity for ingredient %s from unit %s to %s: %w", item.IngredientID, item.UnitID, ingredient.UnitID, err)
|
||||
}
|
||||
quantityToAdd = convertedQuantity
|
||||
}
|
||||
|
||||
// Calculate unit cost in ingredient's base unit
|
||||
unitCost := 0.0
|
||||
if quantityToAdd > 0 {
|
||||
unitCost = item.Amount / quantityToAdd
|
||||
}
|
||||
|
||||
// Create inventory movement for ingredient purchase
|
||||
reason := fmt.Sprintf("Purchase order %s received", po.PONumber)
|
||||
referenceType := entities.InventoryMovementReferenceTypePurchaseOrder
|
||||
referenceID := &id
|
||||
|
||||
err = p.inventoryMovementService.CreateIngredientMovement(
|
||||
ctx,
|
||||
item.IngredientID,
|
||||
organizationID,
|
||||
outletID,
|
||||
userID,
|
||||
entities.InventoryMovementTypePurchase,
|
||||
quantityToAdd,
|
||||
unitCost,
|
||||
reason,
|
||||
&referenceType,
|
||||
referenceID,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create inventory movement for ingredient %s: %w", item.IngredientID, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update the purchase order status
|
||||
err = p.purchaseOrderRepo.UpdateStatus(ctx, id, status)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to update purchase order status: %w", err)
|
||||
}
|
||||
|
||||
// Get the updated purchase order
|
||||
updatedPO, err := p.purchaseOrderRepo.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get updated purchase order: %w", err)
|
||||
}
|
||||
|
||||
return mappers.PurchaseOrderEntityToResponse(updatedPO), nil
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package processor
|
||||
|
||||
import (
|
||||
"apskel-pos-be/internal/entities"
|
||||
"context"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type PurchaseOrderRepository interface {
|
||||
Create(ctx context.Context, po *entities.PurchaseOrder) error
|
||||
GetByID(ctx context.Context, id uuid.UUID) (*entities.PurchaseOrder, error)
|
||||
GetByIDAndOrganizationID(ctx context.Context, id, organizationID uuid.UUID) (*entities.PurchaseOrder, error)
|
||||
Update(ctx context.Context, po *entities.PurchaseOrder) error
|
||||
Delete(ctx context.Context, id uuid.UUID) error
|
||||
List(ctx context.Context, organizationID uuid.UUID, filters map[string]interface{}, limit, offset int) ([]*entities.PurchaseOrder, int64, error)
|
||||
Count(ctx context.Context, organizationID uuid.UUID, filters map[string]interface{}) (int64, error)
|
||||
GetByPONumber(ctx context.Context, poNumber string, organizationID uuid.UUID) (*entities.PurchaseOrder, error)
|
||||
GetByStatus(ctx context.Context, organizationID uuid.UUID, status string) ([]*entities.PurchaseOrder, error)
|
||||
GetOverdue(ctx context.Context, organizationID uuid.UUID) ([]*entities.PurchaseOrder, error)
|
||||
UpdateStatus(ctx context.Context, id uuid.UUID, status string) error
|
||||
UpdateTotalAmount(ctx context.Context, id uuid.UUID, totalAmount float64) error
|
||||
CreateItem(ctx context.Context, item *entities.PurchaseOrderItem) error
|
||||
UpdateItem(ctx context.Context, item *entities.PurchaseOrderItem) error
|
||||
DeleteItem(ctx context.Context, id uuid.UUID) error
|
||||
DeleteItemsByPurchaseOrderID(ctx context.Context, purchaseOrderID uuid.UUID) error
|
||||
GetItemsByPurchaseOrderID(ctx context.Context, purchaseOrderID uuid.UUID) ([]*entities.PurchaseOrderItem, error)
|
||||
CreateAttachment(ctx context.Context, attachment *entities.PurchaseOrderAttachment) error
|
||||
DeleteAttachment(ctx context.Context, id uuid.UUID) error
|
||||
DeleteAttachmentsByPurchaseOrderID(ctx context.Context, purchaseOrderID uuid.UUID) error
|
||||
GetAttachmentsByPurchaseOrderID(ctx context.Context, purchaseOrderID uuid.UUID) ([]*entities.PurchaseOrderAttachment, error)
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package processor
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"apskel-pos-be/internal/entities"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// Repository interfaces for processors
|
||||
type ChartOfAccountTypeRepository interface {
|
||||
Create(ctx context.Context, chartOfAccountType *entities.ChartOfAccountType) error
|
||||
GetByID(ctx context.Context, id uuid.UUID) (*entities.ChartOfAccountType, error)
|
||||
GetByCode(ctx context.Context, code string) (*entities.ChartOfAccountType, error)
|
||||
Update(ctx context.Context, chartOfAccountType *entities.ChartOfAccountType) error
|
||||
Delete(ctx context.Context, id uuid.UUID) error
|
||||
List(ctx context.Context, filters map[string]interface{}, page, limit int) ([]*entities.ChartOfAccountType, int, error)
|
||||
GetActive(ctx context.Context) ([]*entities.ChartOfAccountType, error)
|
||||
}
|
||||
|
||||
type ChartOfAccountRepository interface {
|
||||
Create(ctx context.Context, chartOfAccount *entities.ChartOfAccount) error
|
||||
GetByID(ctx context.Context, id uuid.UUID) (*entities.ChartOfAccount, error)
|
||||
Update(ctx context.Context, chartOfAccount *entities.ChartOfAccount) error
|
||||
Delete(ctx context.Context, id uuid.UUID) error
|
||||
List(ctx context.Context, req *entities.ChartOfAccount) ([]*entities.ChartOfAccount, int, error)
|
||||
GetByOrganization(ctx context.Context, organizationID uuid.UUID, outletID *uuid.UUID) ([]*entities.ChartOfAccount, error)
|
||||
GetByType(ctx context.Context, organizationID uuid.UUID, chartOfAccountTypeID uuid.UUID, outletID *uuid.UUID) ([]*entities.ChartOfAccount, error)
|
||||
GetByCode(ctx context.Context, organizationID uuid.UUID, code string, outletID *uuid.UUID) (*entities.ChartOfAccount, error)
|
||||
}
|
||||
|
||||
type AccountRepository interface {
|
||||
Create(ctx context.Context, account *entities.Account) error
|
||||
GetByID(ctx context.Context, id uuid.UUID) (*entities.Account, error)
|
||||
Update(ctx context.Context, account *entities.Account) error
|
||||
Delete(ctx context.Context, id uuid.UUID) error
|
||||
List(ctx context.Context, req *entities.Account) ([]*entities.Account, int, error)
|
||||
GetByOrganization(ctx context.Context, organizationID uuid.UUID, outletID *uuid.UUID) ([]*entities.Account, error)
|
||||
GetByChartOfAccount(ctx context.Context, chartOfAccountID uuid.UUID) ([]*entities.Account, error)
|
||||
GetByNumber(ctx context.Context, organizationID uuid.UUID, number string, outletID *uuid.UUID) (*entities.Account, error)
|
||||
UpdateBalance(ctx context.Context, id uuid.UUID, amount float64) error
|
||||
GetBalance(ctx context.Context, id uuid.UUID) (float64, error)
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
package processor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"apskel-pos-be/internal/entities"
|
||||
"apskel-pos-be/internal/mappers"
|
||||
"apskel-pos-be/internal/models"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type VendorProcessor interface {
|
||||
CreateVendor(ctx context.Context, organizationID uuid.UUID, req *models.CreateVendorRequest) (*models.VendorResponse, error)
|
||||
UpdateVendor(ctx context.Context, id, organizationID uuid.UUID, req *models.UpdateVendorRequest) (*models.VendorResponse, error)
|
||||
DeleteVendor(ctx context.Context, id, organizationID uuid.UUID) error
|
||||
GetVendorByID(ctx context.Context, id, organizationID uuid.UUID) (*models.VendorResponse, error)
|
||||
ListVendors(ctx context.Context, organizationID uuid.UUID, filters map[string]interface{}, page, limit int) ([]*models.VendorResponse, int, error)
|
||||
GetActiveVendors(ctx context.Context, organizationID uuid.UUID) ([]*models.VendorResponse, error)
|
||||
}
|
||||
|
||||
type VendorProcessorImpl struct {
|
||||
vendorRepo VendorRepository
|
||||
}
|
||||
|
||||
func NewVendorProcessorImpl(vendorRepo VendorRepository) *VendorProcessorImpl {
|
||||
return &VendorProcessorImpl{
|
||||
vendorRepo: vendorRepo,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *VendorProcessorImpl) CreateVendor(ctx context.Context, organizationID uuid.UUID, req *models.CreateVendorRequest) (*models.VendorResponse, error) {
|
||||
// Check if vendor with same name already exists in organization
|
||||
if req.Name != "" {
|
||||
existingVendor, err := p.vendorRepo.GetByName(ctx, req.Name, organizationID)
|
||||
if err == nil && existingVendor != nil {
|
||||
return nil, fmt.Errorf("vendor with name %s already exists in this organization", req.Name)
|
||||
}
|
||||
}
|
||||
|
||||
// Check if vendor with same email already exists in organization
|
||||
if req.Email != nil && *req.Email != "" {
|
||||
existingVendor, err := p.vendorRepo.GetByEmail(ctx, *req.Email, organizationID)
|
||||
if err == nil && existingVendor != nil {
|
||||
return nil, fmt.Errorf("vendor with email %s already exists in this organization", *req.Email)
|
||||
}
|
||||
}
|
||||
|
||||
vendorEntity := &entities.Vendor{
|
||||
OrganizationID: organizationID,
|
||||
Name: req.Name,
|
||||
Email: req.Email,
|
||||
PhoneNumber: req.PhoneNumber,
|
||||
Address: req.Address,
|
||||
ContactPerson: req.ContactPerson,
|
||||
TaxNumber: req.TaxNumber,
|
||||
PaymentTerms: req.PaymentTerms,
|
||||
Notes: req.Notes,
|
||||
IsActive: true, // Default to active
|
||||
}
|
||||
|
||||
if req.IsActive != nil {
|
||||
vendorEntity.IsActive = *req.IsActive
|
||||
}
|
||||
|
||||
err := p.vendorRepo.Create(ctx, vendorEntity)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create vendor: %w", err)
|
||||
}
|
||||
|
||||
return mappers.VendorEntityToResponse(vendorEntity), nil
|
||||
}
|
||||
|
||||
func (p *VendorProcessorImpl) UpdateVendor(ctx context.Context, id, organizationID uuid.UUID, req *models.UpdateVendorRequest) (*models.VendorResponse, error) {
|
||||
vendorEntity, err := p.vendorRepo.GetByIDAndOrganizationID(ctx, id, organizationID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("vendor not found: %w", err)
|
||||
}
|
||||
|
||||
// Check if vendor with same name already exists (excluding current vendor)
|
||||
if req.Name != nil && *req.Name != "" && *req.Name != vendorEntity.Name {
|
||||
existingVendor, err := p.vendorRepo.GetByName(ctx, *req.Name, organizationID)
|
||||
if err == nil && existingVendor != nil && existingVendor.ID != id {
|
||||
return nil, fmt.Errorf("vendor with name %s already exists in this organization", *req.Name)
|
||||
}
|
||||
}
|
||||
|
||||
// Check if vendor with same email already exists (excluding current vendor)
|
||||
if req.Email != nil && *req.Email != "" && (vendorEntity.Email == nil || *req.Email != *vendorEntity.Email) {
|
||||
existingVendor, err := p.vendorRepo.GetByEmail(ctx, *req.Email, organizationID)
|
||||
if err == nil && existingVendor != nil && existingVendor.ID != id {
|
||||
return nil, fmt.Errorf("vendor with email %s already exists in this organization", *req.Email)
|
||||
}
|
||||
}
|
||||
|
||||
// Update fields
|
||||
if req.Name != nil {
|
||||
vendorEntity.Name = *req.Name
|
||||
}
|
||||
if req.Email != nil {
|
||||
vendorEntity.Email = req.Email
|
||||
}
|
||||
if req.PhoneNumber != nil {
|
||||
vendorEntity.PhoneNumber = req.PhoneNumber
|
||||
}
|
||||
if req.Address != nil {
|
||||
vendorEntity.Address = req.Address
|
||||
}
|
||||
if req.ContactPerson != nil {
|
||||
vendorEntity.ContactPerson = req.ContactPerson
|
||||
}
|
||||
if req.TaxNumber != nil {
|
||||
vendorEntity.TaxNumber = req.TaxNumber
|
||||
}
|
||||
if req.PaymentTerms != nil {
|
||||
vendorEntity.PaymentTerms = req.PaymentTerms
|
||||
}
|
||||
if req.Notes != nil {
|
||||
vendorEntity.Notes = req.Notes
|
||||
}
|
||||
if req.IsActive != nil {
|
||||
vendorEntity.IsActive = *req.IsActive
|
||||
}
|
||||
|
||||
err = p.vendorRepo.Update(ctx, vendorEntity)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to update vendor: %w", err)
|
||||
}
|
||||
|
||||
return mappers.VendorEntityToResponse(vendorEntity), nil
|
||||
}
|
||||
|
||||
func (p *VendorProcessorImpl) DeleteVendor(ctx context.Context, id, organizationID uuid.UUID) error {
|
||||
_, err := p.vendorRepo.GetByIDAndOrganizationID(ctx, id, organizationID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("vendor not found: %w", err)
|
||||
}
|
||||
|
||||
err = p.vendorRepo.Delete(ctx, id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete vendor: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *VendorProcessorImpl) GetVendorByID(ctx context.Context, id, organizationID uuid.UUID) (*models.VendorResponse, error) {
|
||||
vendorEntity, err := p.vendorRepo.GetByIDAndOrganizationID(ctx, id, organizationID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("vendor not found: %w", err)
|
||||
}
|
||||
|
||||
return mappers.VendorEntityToResponse(vendorEntity), nil
|
||||
}
|
||||
|
||||
func (p *VendorProcessorImpl) ListVendors(ctx context.Context, organizationID uuid.UUID, filters map[string]interface{}, page, limit int) ([]*models.VendorResponse, int, error) {
|
||||
offset := (page - 1) * limit
|
||||
vendorEntities, total, err := p.vendorRepo.List(ctx, organizationID, filters, limit, offset)
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("failed to list vendors: %w", err)
|
||||
}
|
||||
|
||||
vendorResponses := mappers.VendorEntitiesToResponses(vendorEntities)
|
||||
totalPages := int((total + int64(limit) - 1) / int64(limit))
|
||||
|
||||
return vendorResponses, totalPages, nil
|
||||
}
|
||||
|
||||
func (p *VendorProcessorImpl) GetActiveVendors(ctx context.Context, organizationID uuid.UUID) ([]*models.VendorResponse, error) {
|
||||
vendorEntities, err := p.vendorRepo.GetActiveVendors(ctx, organizationID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get active vendors: %w", err)
|
||||
}
|
||||
|
||||
return mappers.VendorEntitiesToResponses(vendorEntities), nil
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package processor
|
||||
|
||||
import (
|
||||
"apskel-pos-be/internal/entities"
|
||||
"context"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type VendorRepository interface {
|
||||
Create(ctx context.Context, vendor *entities.Vendor) error
|
||||
GetByID(ctx context.Context, id uuid.UUID) (*entities.Vendor, error)
|
||||
GetByIDAndOrganizationID(ctx context.Context, id, organizationID uuid.UUID) (*entities.Vendor, error)
|
||||
Update(ctx context.Context, vendor *entities.Vendor) error
|
||||
Delete(ctx context.Context, id uuid.UUID) error
|
||||
List(ctx context.Context, organizationID uuid.UUID, filters map[string]interface{}, limit, offset int) ([]*entities.Vendor, int64, error)
|
||||
Count(ctx context.Context, organizationID uuid.UUID, filters map[string]interface{}) (int64, error)
|
||||
GetByEmail(ctx context.Context, email string, organizationID uuid.UUID) (*entities.Vendor, error)
|
||||
GetByName(ctx context.Context, name string, organizationID uuid.UUID) (*entities.Vendor, error)
|
||||
GetActiveVendors(ctx context.Context, organizationID uuid.UUID) ([]*entities.Vendor, error)
|
||||
}
|
||||
Reference in New Issue
Block a user