Merge remote-tracking branch 'origin/main' into front-styles

This commit is contained in:
2026-04-14 08:18:19 +02:00
44 changed files with 1631 additions and 539 deletions

View File

@@ -21,6 +21,7 @@ import (
"github.com/golang-jwt/jwt/v5"
"golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
// JWTClaims represents the JWT claims
@@ -436,7 +437,7 @@ func (s *AuthService) RevokeAllRefreshTokens(userID uint) {
// GetUserByID retrieves a user by ID
func (s *AuthService) GetUserByID(userID uint) (*model.Customer, error) {
var user model.Customer
if err := s.db.Preload("Role.Permissions").First(&user, userID).Error; err != nil {
if err := s.db.Preload("Role.Permissions").Preload(clause.Associations).First(&user, userID).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, responseErrors.ErrUserNotFound
}

View File

@@ -2,6 +2,7 @@ package productService
import (
"encoding/json"
"errors"
"git.ma-al.com/goc_daniel/b2b/app/model"
"git.ma-al.com/goc_daniel/b2b/app/repos/productsRepo"
@@ -9,6 +10,7 @@ import (
"git.ma-al.com/goc_daniel/b2b/app/utils/query/filters"
"git.ma-al.com/goc_daniel/b2b/app/utils/query/find"
"git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors"
"git.ma-al.com/goc_daniel/b2b/app/view"
)
type ProductService struct {
@@ -21,17 +23,108 @@ func New() *ProductService {
}
}
func (s *ProductService) GetJSON(p_id_product, p_id_lang, p_id_customer, b2b_id_country, p_quantity int) (*json.RawMessage, error) {
products, err := s.productsRepo.GetJSON(p_id_product, constdata.SHOP_ID, p_id_lang, p_id_customer, b2b_id_country, p_quantity)
func (s *ProductService) Get(
p_id_product, p_id_lang, p_id_customer, b2b_id_country, p_quantity uint,
) (*json.RawMessage, error) {
product, err := s.productsRepo.GetBase(p_id_product, constdata.SHOP_ID, p_id_lang)
if err != nil {
return products, err
return nil, err
}
return products, nil
price, err := s.productsRepo.GetPrice(p_id_product, nil, constdata.SHOP_ID, p_id_customer, b2b_id_country, p_quantity)
if err != nil {
return nil, err
}
variants, err := s.productsRepo.GetVariants(p_id_product, constdata.SHOP_ID, p_id_lang, p_id_customer, b2b_id_country, p_quantity)
if err != nil {
return nil, err
}
result := view.ProductFull{
Product: product,
Price: price,
Variants: variants,
}
if len(variants) > 0 {
result.Variants = variants
}
jsonBytes, err := json.Marshal(result)
if err != nil {
return nil, err
}
raw := json.RawMessage(jsonBytes)
return &raw, nil
}
func (s *ProductService) Find(id_lang, userID uint, p find.Paging, filters *filters.FiltersList) (find.Found[model.ProductInList], error) {
return s.productsRepo.Find(id_lang, userID, p, filters)
func (s *ProductService) Find(
idLang uint,
userID uint,
p find.Paging,
filters *filters.FiltersList,
customer *model.Customer,
quantity uint,
shopID uint,
) (*find.Found[model.ProductInList], error) {
if customer == nil || customer.Country == nil {
return nil, errors.New("customer is nil or missing fields")
}
found, err := s.productsRepo.Find(idLang, userID, p, filters)
if err != nil {
return nil, err
}
// 1. collect simple products (no variants)
simpleProductIndexes := make([]int, 0, len(found.Items))
for i := range found.Items {
if found.Items[i].VariantsNumber <= 0 {
simpleProductIndexes = append(simpleProductIndexes, i)
}
}
// 2. resolve prices ONLY for simple products
for _, i := range simpleProductIndexes {
price, err := s.productsRepo.GetPrice(
found.Items[i].ProductID,
nil,
shopID,
customer.ID,
customer.CountryID,
quantity,
)
if err != nil {
return nil, err
}
found.Items[i].PriceTaxExcl = price.FinalTaxExcl
found.Items[i].PriceTaxIncl = price.FinalTaxIncl
}
return found, nil
}
func (s *ProductService) GetProductAttributes(
langID uint,
productID uint,
shopID uint,
customerID uint,
countryID uint,
quantity uint,
) ([]view.ProductAttribute, error) {
variants, err := s.productsRepo.GetVariants(productID, constdata.SHOP_ID, langID, customerID, countryID, quantity)
if err != nil {
return nil, err
}
return variants, nil
}
func (s *ProductService) AddToFavorites(userID uint, productID uint) error {

View File

@@ -0,0 +1,124 @@
package specificPriceService
import (
"context"
"git.ma-al.com/goc_daniel/b2b/app/model"
"git.ma-al.com/goc_daniel/b2b/app/repos/specificPriceRepo"
"git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors"
)
type SpecificPriceService struct {
specificPriceRepo specificPriceRepo.UISpecificPriceRepo
}
func New() *SpecificPriceService {
return &SpecificPriceService{
specificPriceRepo: specificPriceRepo.New(),
}
}
func (s *SpecificPriceService) Create(ctx context.Context, pr *model.SpecificPrice) (*model.SpecificPrice, error) {
if err := s.validateRequest(pr); err != nil {
return nil, err
}
if err := s.specificPriceRepo.Create(ctx, pr); err != nil {
return nil, err
}
return pr, nil
}
func (s *SpecificPriceService) Update(ctx context.Context, id uint64, pr *model.SpecificPrice) (*model.SpecificPrice, error) {
existing, err := s.specificPriceRepo.GetByID(ctx, id)
if err != nil {
return nil, err
}
if existing == nil {
return nil, responseErrors.ErrSpecificPriceNotFound
}
if err := s.validateUpdateRequest(pr); err != nil {
return nil, err
}
pr.ID = id
if err := s.specificPriceRepo.Update(ctx, pr); err != nil {
return nil, err
}
return pr, nil
}
func (s *SpecificPriceService) GetByID(ctx context.Context, id uint64) (*model.SpecificPrice, error) {
pr, err := s.specificPriceRepo.GetByID(ctx, id)
if err != nil {
return nil, err
}
if pr == nil {
return nil, responseErrors.ErrSpecificPriceNotFound
}
return pr, nil
}
func (s *SpecificPriceService) List(ctx context.Context) ([]*model.SpecificPrice, error) {
return s.specificPriceRepo.List(ctx)
}
func (s *SpecificPriceService) SetActive(ctx context.Context, id uint64, active bool) error {
pr, err := s.specificPriceRepo.GetByID(ctx, id)
if err != nil {
return err
}
if pr == nil {
return responseErrors.ErrSpecificPriceNotFound
}
return s.specificPriceRepo.SetActive(ctx, id, active)
}
func (s *SpecificPriceService) Delete(ctx context.Context, id uint64) error {
pr, err := s.specificPriceRepo.GetByID(ctx, id)
if err != nil {
return err
}
if pr == nil {
return responseErrors.ErrSpecificPriceNotFound
}
return s.specificPriceRepo.Delete(ctx, id)
}
func (s *SpecificPriceService) validateRequest(pr *model.SpecificPrice) error {
if pr.ReductionType != "amount" && pr.ReductionType != "percentage" {
return responseErrors.ErrInvalidReductionType
}
if pr.ReductionType == "percentage" && pr.PercentageReduction == nil {
return responseErrors.ErrPercentageRequired
}
if pr.ReductionType == "amount" && pr.Price == nil {
return responseErrors.ErrPriceRequired
}
return nil
}
func (s *SpecificPriceService) validateUpdateRequest(pr *model.SpecificPrice) error {
if pr.ReductionType != "" && pr.ReductionType != "amount" && pr.ReductionType != "percentage" {
return responseErrors.ErrInvalidReductionType
}
if pr.ReductionType == "percentage" && pr.PercentageReduction == nil {
return responseErrors.ErrPercentageRequired
}
if pr.ReductionType == "amount" && pr.Price == nil {
return responseErrors.ErrPriceRequired
}
return nil
}