update
This commit is contained in:
@@ -83,7 +83,6 @@ func (u *AuthServiceImpl) AuthenticateUser(ctx context.Context, email, password
|
||||
}
|
||||
|
||||
func (u *AuthServiceImpl) SendPasswordResetLink(ctx context.Context, email string) error {
|
||||
// Check if the user exists
|
||||
user, err := u.authRepo.CheckExistsUserAccount(ctx, email)
|
||||
if err != nil {
|
||||
logger.ContextLogger(ctx).Error("error when getting user", zap.Error(err))
|
||||
|
||||
@@ -162,7 +162,6 @@ func (s *memberSvc) ResendOTP(
|
||||
) (*entity.ResendOTPResponse, error) {
|
||||
logger.ContextLogger(ctx).Info("resending OTP", zap.String("token", token))
|
||||
|
||||
// Get registration by token
|
||||
registration, err := s.repo.GetRegistrationByToken(ctx, token)
|
||||
if err != nil {
|
||||
logger.ContextLogger(ctx).Error("failed to get registration", zap.Error(err))
|
||||
@@ -211,7 +210,7 @@ func (s *memberSvc) sendRegistrationOTP(
|
||||
Recipient: registration.Email,
|
||||
Subject: "Enaklo - Registration Verification Code",
|
||||
TemplateName: "member_registration_otp",
|
||||
TemplatePath: "/templates/member_registration_otp.html",
|
||||
TemplatePath: "templates/member_registration_otp.html",
|
||||
Data: emailData,
|
||||
})
|
||||
|
||||
|
||||
@@ -240,32 +240,6 @@ func (s *OrderService) Execute(ctx mycontext.Context, req *entity.OrderExecuteRe
|
||||
Order: order,
|
||||
}
|
||||
|
||||
if order.PaymentType != "CASH" {
|
||||
if order.PaymentType == "VA" {
|
||||
paymentResponse, err := s.processVAPayment(ctx, order, partnerID, req.CreatedBy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.VirtualAccount = paymentResponse.VirtualAccountNumber
|
||||
resp.BankName = paymentResponse.BankName
|
||||
resp.BankCode = paymentResponse.BankCode
|
||||
}
|
||||
if order.PaymentType == "QRIS" {
|
||||
paymentResponse, err := s.processQRPayment(ctx, order, partnerID, req.CreatedBy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.QRCode = paymentResponse.QRCodeURL
|
||||
} else {
|
||||
paymentResponse, err := s.processNonCashPayment(ctx, order, partnerID, req.CreatedBy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.PaymentToken = paymentResponse.Token
|
||||
resp.RedirectURL = paymentResponse.RedirectURL
|
||||
}
|
||||
}
|
||||
|
||||
order.SetExecutePaymentStatus()
|
||||
order, err = s.repo.Update(ctx, order)
|
||||
if err != nil {
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"enaklo-pos-be/internal/services/transaction"
|
||||
"enaklo-pos-be/internal/services/users"
|
||||
customerSvc "enaklo-pos-be/internal/services/v2/customer"
|
||||
"enaklo-pos-be/internal/services/v2/inprogress_order"
|
||||
orderSvc "enaklo-pos-be/internal/services/v2/order"
|
||||
productSvc "enaklo-pos-be/internal/services/v2/product"
|
||||
|
||||
@@ -47,12 +48,14 @@ type ServiceManagerImpl struct {
|
||||
CustomerV2Svc customerSvc.Service
|
||||
ProductV2Svc productSvc.Service
|
||||
MemberRegistrationSvc member.RegistrationService
|
||||
InProgressSvc inprogress_order.InProgressOrderService
|
||||
}
|
||||
|
||||
func NewServiceManagerImpl(cfg *config.Config, repo *repository.RepoManagerImpl) *ServiceManagerImpl {
|
||||
|
||||
custSvcV2 := customerSvc.New(repo.CustomerRepo)
|
||||
custSvcV2 := customerSvc.New(repo.CustomerRepo, repo.EmailService)
|
||||
productSvcV2 := productSvc.New(repo.ProductRepo)
|
||||
inprogressOrder := inprogress_order.NewInProgressOrderService(repo.InProgressOrderRepo)
|
||||
|
||||
return &ServiceManagerImpl{
|
||||
AuthSvc: auth.New(repo.Auth, repo.Crypto, repo.User, repo.EmailService, cfg.Email, repo.Trx, repo.License),
|
||||
@@ -72,6 +75,7 @@ func NewServiceManagerImpl(cfg *config.Config, repo *repository.RepoManagerImpl)
|
||||
OrderV2Svc: orderSvc.New(repo.OrderRepo, productSvcV2, custSvcV2, repo.TransactionRepo, repo.Crypto, &cfg.Order, repo.EmailService),
|
||||
MemberRegistrationSvc: member.NewMemberRegistrationService(repo.MemberRepository, repo.EmailService, custSvcV2),
|
||||
CustomerV2Svc: custSvcV2,
|
||||
InProgressSvc: inprogressOrder,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package customer
|
||||
|
||||
import (
|
||||
"context"
|
||||
errors2 "enaklo-pos-be/internal/common/errors"
|
||||
"enaklo-pos-be/internal/common/logger"
|
||||
"enaklo-pos-be/internal/common/mycontext"
|
||||
"enaklo-pos-be/internal/constants"
|
||||
@@ -8,7 +10,9 @@ import (
|
||||
"enaklo-pos-be/internal/utils"
|
||||
"github.com/pkg/errors"
|
||||
"go.uber.org/zap"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Repository interface {
|
||||
@@ -16,26 +20,35 @@ type Repository interface {
|
||||
FindByID(ctx mycontext.Context, id int64) (*entity.Customer, error)
|
||||
FindByPhone(ctx mycontext.Context, phone string) (*entity.Customer, error)
|
||||
FindByEmail(ctx mycontext.Context, email string) (*entity.Customer, error)
|
||||
AddPoints(ctx mycontext.Context, id int64, points int) error
|
||||
AddPoints(ctx mycontext.Context, id int64, points int, reference string) error
|
||||
FindSequence(ctx mycontext.Context, partnerID int64) (int64, error)
|
||||
GetAllCustomers(ctx mycontext.Context, req entity.MemberSearch) (entity.MemberList, int, error)
|
||||
VerifyOTP(ctx mycontext.Context, verificationHash string, otpCode string) (int64, error)
|
||||
}
|
||||
|
||||
type Service interface {
|
||||
ResolveCustomer(ctx mycontext.Context, req *entity.CustomerResolutionRequest) (int64, error)
|
||||
AddPoints(ctx mycontext.Context, customerID int64, points int) error
|
||||
AddPoints(ctx mycontext.Context, customerID int64, points int, reference string) error
|
||||
GetCustomer(ctx mycontext.Context, id int64) (*entity.Customer, error)
|
||||
CustomerCheck(ctx mycontext.Context, req *entity.CustomerResolutionRequest) (*entity.CustomerCheckResponse, error)
|
||||
GetAllCustomers(ctx mycontext.Context, req *entity.MemberSearch) (*entity.MemberList, int, error)
|
||||
RegistrationMember(ctx mycontext.Context, req *entity.Customer) (*entity.Customer, error)
|
||||
VerifyOTP(ctx mycontext.Context, verificationID, otpCode string) error
|
||||
}
|
||||
|
||||
type EmailService interface {
|
||||
SendEmailTransactional(ctx context.Context, param entity.SendEmailNotificationParam) error
|
||||
}
|
||||
|
||||
type customerSvc struct {
|
||||
repo Repository
|
||||
repo Repository
|
||||
notification EmailService
|
||||
}
|
||||
|
||||
func New(repo Repository) Service {
|
||||
func New(repo Repository, notification EmailService) Service {
|
||||
return &customerSvc{
|
||||
repo: repo,
|
||||
repo: repo,
|
||||
notification: notification,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,12 +119,68 @@ func (s *customerSvc) ResolveCustomer(ctx mycontext.Context, req *entity.Custome
|
||||
return customer.ID, nil
|
||||
}
|
||||
|
||||
func (s *customerSvc) AddPoints(ctx mycontext.Context, customerID int64, points int) error {
|
||||
func (s *customerSvc) RegistrationMember(ctx mycontext.Context, req *entity.Customer) (*entity.Customer, error) {
|
||||
if req.Email == "" && req.PhoneNumber == "" {
|
||||
return nil, errors2.ErrorPhoneNumberEmailIsRequired
|
||||
}
|
||||
|
||||
if req.PhoneNumber != "" {
|
||||
customer, err := s.repo.FindByPhone(ctx, req.PhoneNumber)
|
||||
if err != nil && !strings.Contains(err.Error(), "not found") {
|
||||
return nil, errors2.ErrorInternalServer
|
||||
}
|
||||
|
||||
if customer != nil {
|
||||
return nil, errors2.ErrorPhoneNumberIsAlreadyRegistered
|
||||
}
|
||||
}
|
||||
|
||||
if req.Email != "" {
|
||||
customer, err := s.repo.FindByEmail(ctx, req.Email)
|
||||
if err != nil && !strings.Contains(err.Error(), "not found") {
|
||||
return nil, errors2.ErrorInternalServer
|
||||
}
|
||||
|
||||
if customer != nil {
|
||||
return nil, errors2.ErrorEmailIsAlreadyRegistered
|
||||
}
|
||||
}
|
||||
|
||||
newCustomer := &entity.Customer{
|
||||
Name: req.Name,
|
||||
Email: req.Email,
|
||||
Phone: req.PhoneNumber,
|
||||
CreatedAt: constants.TimeNow(),
|
||||
UpdatedAt: constants.TimeNow(),
|
||||
BirthDate: req.BirthDate,
|
||||
Password: req.HashedPassword(),
|
||||
}
|
||||
|
||||
customer, err := s.repo.Create(ctx, newCustomer)
|
||||
if err != nil {
|
||||
logger.ContextLogger(ctx).Error("failed to create customer", zap.Error(err))
|
||||
return nil, errors2.ErrorInternalServer
|
||||
}
|
||||
|
||||
errs := s.sendRegistrationOTP(ctx, &entity.MemberRegistration{
|
||||
Name: customer.Name,
|
||||
Email: customer.Email,
|
||||
OTP: customer.OTP,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
logger.ContextLogger(ctx).Error("failed to send OTP", zap.Error(errs))
|
||||
}
|
||||
|
||||
return customer, nil
|
||||
}
|
||||
|
||||
func (s *customerSvc) AddPoints(ctx mycontext.Context, customerID int64, points int, reference string) error {
|
||||
if points <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := s.repo.AddPoints(ctx, customerID, points)
|
||||
err := s.repo.AddPoints(ctx, customerID, points, reference)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to add points to customer")
|
||||
}
|
||||
@@ -202,3 +271,75 @@ func (s *customerSvc) GetAllCustomers(ctx mycontext.Context, req *entity.MemberS
|
||||
|
||||
return &customers, totalCount, nil
|
||||
}
|
||||
|
||||
func (s *customerSvc) sendRegistrationOTP(
|
||||
ctx mycontext.Context,
|
||||
registration *entity.MemberRegistration,
|
||||
) error {
|
||||
emailData := map[string]interface{}{
|
||||
"UserName": registration.Name,
|
||||
"OTPCode": registration.OTP,
|
||||
}
|
||||
|
||||
err := s.notification.SendEmailTransactional(ctx, entity.SendEmailNotificationParam{
|
||||
Sender: "noreply@enaklo.co.id",
|
||||
Recipient: registration.Email,
|
||||
Subject: "Enaklo - Registration Verification Code",
|
||||
TemplateName: "member_registration_otp",
|
||||
TemplatePath: "templates/member_registration_otp.html",
|
||||
Data: emailData,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *customerSvc) VerifyOTP(ctx mycontext.Context, verificationID, otpCode string) error {
|
||||
customerID, err := s.repo.VerifyOTP(ctx, verificationID, otpCode)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "verification failed")
|
||||
}
|
||||
|
||||
customer, _ := s.repo.FindByID(ctx, customerID)
|
||||
|
||||
go func(customer *entity.Customer) {
|
||||
newCtx := context.Background()
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
log.Printf("Recovered from panic in sendWelcomeEmail: %v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
s.sendWelcomeEmail(newCtx, customer)
|
||||
}(customer)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *customerSvc) sendWelcomeEmail(
|
||||
ctx context.Context,
|
||||
customer *entity.Customer,
|
||||
) error {
|
||||
|
||||
welcomeData := map[string]interface{}{
|
||||
"UserName": customer.Name,
|
||||
"MemberID": customer.CustomerID,
|
||||
"PointsName": "EnakPoint",
|
||||
"PointsBalance": customer.Points,
|
||||
"RedeemLink": "https://enaklo.co.id/redeem",
|
||||
"CurrentDate": time.Now().Format("01-2006"),
|
||||
}
|
||||
|
||||
return s.notification.SendEmailTransactional(ctx, entity.SendEmailNotificationParam{
|
||||
Sender: "noreply@enaklo.co.id",
|
||||
Recipient: customer.Email,
|
||||
Subject: "Welcome to Enaklo Membership Program",
|
||||
TemplateName: "welcome_member",
|
||||
TemplatePath: "templates/welcome_member.html",
|
||||
Data: welcomeData,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package inprogress_order
|
||||
|
||||
import (
|
||||
"enaklo-pos-be/internal/common/logger"
|
||||
"enaklo-pos-be/internal/common/mycontext"
|
||||
"enaklo-pos-be/internal/entity"
|
||||
"enaklo-pos-be/internal/repository"
|
||||
"github.com/pkg/errors"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type InProgressOrderService interface {
|
||||
Save(ctx mycontext.Context, order *entity.InProgressOrder) (*entity.InProgressOrder, error)
|
||||
GetOrdersByPartnerID(ctx mycontext.Context, partnerID int64, limit, offset int) ([]*entity.InProgressOrder, error)
|
||||
}
|
||||
|
||||
type inProgressOrderSvc struct {
|
||||
repo repository.InProgressOrderRepository
|
||||
}
|
||||
|
||||
func NewInProgressOrderService(repo repository.InProgressOrderRepository) InProgressOrderService {
|
||||
return &inProgressOrderSvc{
|
||||
repo: repo,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *inProgressOrderSvc) Save(ctx mycontext.Context, order *entity.InProgressOrder) (*entity.InProgressOrder, error) {
|
||||
createdOrder, err := s.repo.CreateOrUpdate(ctx, order)
|
||||
if err != nil {
|
||||
logger.ContextLogger(ctx).Error("failed to create in-progress order",
|
||||
zap.Error(err),
|
||||
zap.Int64("partnerID", order.PartnerID))
|
||||
return nil, errors.Wrap(err, "failed to create in-progress order")
|
||||
}
|
||||
|
||||
return createdOrder, nil
|
||||
}
|
||||
|
||||
func (s *inProgressOrderSvc) GetOrdersByPartnerID(ctx mycontext.Context, partnerID int64, limit, offset int) ([]*entity.InProgressOrder, error) {
|
||||
orders, err := s.repo.GetListByPartnerID(ctx, partnerID, limit, offset)
|
||||
if err != nil {
|
||||
logger.ContextLogger(ctx).Error("failed to get in-progress orders by partner ID",
|
||||
zap.Error(err),
|
||||
zap.Int64("partnerID", partnerID),
|
||||
zap.Int("limit", limit),
|
||||
zap.Int("offset", offset))
|
||||
return nil, errors.Wrap(err, "failed to get in-progress orders")
|
||||
}
|
||||
|
||||
return orders, nil
|
||||
}
|
||||
@@ -51,6 +51,9 @@ func (s *orderSvc) CreateOrderInquiry(ctx mycontext.Context,
|
||||
req.CustomerName,
|
||||
req.CustomerPhoneNumber,
|
||||
req.CustomerEmail,
|
||||
req.PaymentProvider,
|
||||
req.TableNumber,
|
||||
req.OrderType,
|
||||
)
|
||||
|
||||
for _, item := range req.OrderItems {
|
||||
|
||||
@@ -10,13 +10,14 @@ import (
|
||||
)
|
||||
|
||||
func (s *orderSvc) ExecuteOrderInquiry(ctx mycontext.Context,
|
||||
token string, paymentMethod string) (*entity.OrderResponse, error) {
|
||||
token string, paymentMethod, paymentProvider, inprogressOrderID string) (*entity.OrderResponse, error) {
|
||||
inquiry, err := s.validateInquiry(ctx, token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
order := inquiry.ToOrder(paymentMethod)
|
||||
order := inquiry.ToOrder(paymentMethod, paymentProvider)
|
||||
order.InProgressOrderID = inprogressOrderID
|
||||
|
||||
savedOrder, err := s.repo.Create(ctx, order)
|
||||
if err != nil {
|
||||
@@ -51,7 +52,7 @@ func (s *orderSvc) processPostOrderActions(
|
||||
}
|
||||
|
||||
if order.CustomerID != nil && *order.CustomerID > 0 {
|
||||
err = s.addCustomerPoints(ctx, *order.CustomerID, int(order.Total/1000))
|
||||
err = s.addCustomerPoints(ctx, *order.CustomerID, int(order.Total/1000), fmt.Sprintf("TRX #%s", trx.ID))
|
||||
if err != nil {
|
||||
logger.ContextLogger(ctx).Error("error when adding points", zap.Error(err))
|
||||
}
|
||||
@@ -78,8 +79,8 @@ func (s *orderSvc) createTransaction(ctx mycontext.Context, order *entity.Order,
|
||||
return transaction, err
|
||||
}
|
||||
|
||||
func (s *orderSvc) addCustomerPoints(ctx mycontext.Context, customerID int64, points int) error {
|
||||
return s.customer.AddPoints(ctx, customerID, points)
|
||||
func (s *orderSvc) addCustomerPoints(ctx mycontext.Context, customerID int64, points int, reference string) error {
|
||||
return s.customer.AddPoints(ctx, customerID, points, reference)
|
||||
}
|
||||
|
||||
func (s *orderSvc) sendTransactionReceipt(ctx mycontext.Context, order *entity.Order, transaction *entity.Transaction, paymentMethod string) error {
|
||||
|
||||
@@ -21,7 +21,7 @@ type ProductService interface {
|
||||
|
||||
type CustomerService interface {
|
||||
ResolveCustomer(ctx mycontext.Context, req *entity.CustomerResolutionRequest) (int64, error)
|
||||
AddPoints(ctx mycontext.Context, customerID int64, points int) error
|
||||
AddPoints(ctx mycontext.Context, customerID int64, points int, reference string) error
|
||||
GetCustomer(ctx mycontext.Context, id int64) (*entity.Customer, error)
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ type Service interface {
|
||||
CreateOrderInquiry(ctx mycontext.Context,
|
||||
req *entity.OrderRequest) (*entity.OrderInquiryResponse, error)
|
||||
ExecuteOrderInquiry(ctx mycontext.Context,
|
||||
token string, paymentMethod string) (*entity.OrderResponse, error)
|
||||
token string, paymentMethod, paymentProvider, inProgressOrderID string) (*entity.OrderResponse, error)
|
||||
}
|
||||
|
||||
type Config interface {
|
||||
|
||||
Reference in New Issue
Block a user