feat: product_attribute list with prices
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
|||||||
"git.ma-al.com/goc_daniel/b2b/app/config"
|
"git.ma-al.com/goc_daniel/b2b/app/config"
|
||||||
"git.ma-al.com/goc_daniel/b2b/app/model"
|
"git.ma-al.com/goc_daniel/b2b/app/model"
|
||||||
"git.ma-al.com/goc_daniel/b2b/app/service/productService"
|
"git.ma-al.com/goc_daniel/b2b/app/service/productService"
|
||||||
|
constdata "git.ma-al.com/goc_daniel/b2b/app/utils/const_data"
|
||||||
"git.ma-al.com/goc_daniel/b2b/app/utils/i18n"
|
"git.ma-al.com/goc_daniel/b2b/app/utils/i18n"
|
||||||
"git.ma-al.com/goc_daniel/b2b/app/utils/localeExtractor"
|
"git.ma-al.com/goc_daniel/b2b/app/utils/localeExtractor"
|
||||||
"git.ma-al.com/goc_daniel/b2b/app/utils/nullable"
|
"git.ma-al.com/goc_daniel/b2b/app/utils/nullable"
|
||||||
@@ -34,6 +35,7 @@ func ProductsHandlerRoutes(r fiber.Router) fiber.Router {
|
|||||||
|
|
||||||
r.Get("/:id/:country_id/:quantity", handler.GetProductJson)
|
r.Get("/:id/:country_id/:quantity", handler.GetProductJson)
|
||||||
r.Get("/list", handler.ListProducts)
|
r.Get("/list", handler.ListProducts)
|
||||||
|
r.Get("/list-variants/:product_id", handler.ListProductVariants)
|
||||||
|
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
@@ -84,13 +86,13 @@ func (h *ProductsHandler) ListProducts(c fiber.Ctx) error {
|
|||||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||||
}
|
}
|
||||||
|
|
||||||
id_lang, ok := localeExtractor.GetLangID(c)
|
customer, ok := localeExtractor.GetCustomer(c)
|
||||||
if !ok {
|
if !ok || customer == nil {
|
||||||
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
||||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||||
}
|
}
|
||||||
|
|
||||||
list, err := h.productService.Find(id_lang, paging, filters)
|
list, err := h.productService.Find(customer.LangID, paging, filters, customer, 1, constdata.SHOP_ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||||
@@ -107,3 +109,27 @@ var columnMappingListProducts map[string]string = map[string]string{
|
|||||||
"category_id": "cp.id_category",
|
"category_id": "cp.id_category",
|
||||||
"quantity": "sa.quantity",
|
"quantity": "sa.quantity",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *ProductsHandler) ListProductVariants(c fiber.Ctx) error {
|
||||||
|
productIDStr := c.Params("product_id")
|
||||||
|
|
||||||
|
productID, err := strconv.Atoi(productIDStr)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
customer, ok := localeExtractor.GetCustomer(c)
|
||||||
|
if !ok || customer == nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||||
|
}
|
||||||
|
|
||||||
|
list, err := h.productService.GetProductAttributes(customer.LangID, uint(productID), constdata.SHOP_ID, customer.ID, customer.CountryID, 1)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(response.Make(&list, len(list), i18n.T_(c, response.Message_OK)))
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import "git.ma-al.com/goc_daniel/b2b/app/model/dbmodel"
|
|
||||||
|
|
||||||
// Represents a country together with its associated currency
|
// Represents a country together with its associated currency
|
||||||
type Country struct {
|
type Country struct {
|
||||||
ID uint `gorm:"primaryKey;column:id" json:"id"`
|
ID uint `gorm:"primaryKey;column:id" json:"id"`
|
||||||
Name string `gorm:"column:name" json:"name"`
|
Name string `gorm:"column:name" json:"name"`
|
||||||
Flag string `gorm:"size:16;not null;column:flag" json:"flag"`
|
Flag string `gorm:"size:16;not null;column:flag" json:"flag"`
|
||||||
|
|
||||||
PSCurrencyID uint `gorm:"column:currency_id" json:"currency_id"`
|
CurrencyID uint `gorm:"column:b2b_id_currency" json:"currency_id"`
|
||||||
PSCurrency *dbmodel.PsCurrency `gorm:"foreignKey:PSCurrencyID;references:IDCurrency" json:"ps_currency"`
|
Currency *Currency `gorm:"foreignKey:CurrencyID" json:"currency,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (Country) TableName() string {
|
func (Country) TableName() string {
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ type Customer struct {
|
|||||||
LastLoginAt *time.Time `json:"last_login_at,omitempty"`
|
LastLoginAt *time.Time `json:"last_login_at,omitempty"`
|
||||||
LangID uint `gorm:"default:2" json:"lang_id"` // User's preferred language
|
LangID uint `gorm:"default:2" json:"lang_id"` // User's preferred language
|
||||||
CountryID uint `gorm:"default:2" json:"country_id"` // User's selected country
|
CountryID uint `gorm:"default:2" json:"country_id"` // User's selected country
|
||||||
|
Country *Country `gorm:"foreignKey:CountryID" json:"country,omitempty"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||||||
|
|||||||
@@ -62,14 +62,16 @@ type Product struct {
|
|||||||
DeliveryDays uint `gorm:"column:delivery_days" json:"delivery_days" form:"delivery_days"`
|
DeliveryDays uint `gorm:"column:delivery_days" json:"delivery_days" form:"delivery_days"`
|
||||||
}
|
}
|
||||||
type ProductInList struct {
|
type ProductInList struct {
|
||||||
ProductID uint `gorm:"column:product_id" json:"product_id" form:"product_id"`
|
ProductID uint `gorm:"column:product_id" json:"product_id" form:"product_id"`
|
||||||
Name string `gorm:"column:name" json:"name" form:"name"`
|
Name string `gorm:"column:name" json:"name" form:"name"`
|
||||||
LinkRewrite string `gorm:"column:link_rewrite" json:"link_rewrite"`
|
LinkRewrite string `gorm:"column:link_rewrite" json:"link_rewrite"`
|
||||||
ImageLink string `gorm:"column:image_link" json:"image_link"`
|
ImageLink string `gorm:"column:image_link" json:"image_link"`
|
||||||
CategoryName string `gorm:"column:category_name" json:"category_name" form:"category_name"`
|
CategoryName string `gorm:"column:category_name" json:"category_name" form:"category_name"`
|
||||||
Reference string `gorm:"column:reference" json:"reference"`
|
Reference string `gorm:"column:reference" json:"reference"`
|
||||||
VariantsNumber uint `gorm:"column:variants_number" json:"variants_number"`
|
VariantsNumber uint `gorm:"column:variants_number" json:"variants_number"`
|
||||||
Quantity int64 `gorm:"column:quantity" json:"quantity"`
|
Quantity int64 `gorm:"column:quantity" json:"quantity"`
|
||||||
|
PriceTaxExcl float64 `gorm:"column:price_tax_excl" json:"price_tax_excl"`
|
||||||
|
PriceTaxIncl float64 `gorm:"column:price_tax_incl" json:"price_tax_incl"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProductFilters struct {
|
type ProductFilters struct {
|
||||||
|
|||||||
@@ -10,13 +10,15 @@ import (
|
|||||||
"git.ma-al.com/goc_daniel/b2b/app/model/dbmodel"
|
"git.ma-al.com/goc_daniel/b2b/app/model/dbmodel"
|
||||||
"git.ma-al.com/goc_daniel/b2b/app/utils/query/filters"
|
"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/query/find"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/view"
|
||||||
"git.ma-al.com/goc_marek/gormcol"
|
"git.ma-al.com/goc_marek/gormcol"
|
||||||
"github.com/WinterYukky/gorm-extra-clause-plugin/exclause"
|
"github.com/WinterYukky/gorm-extra-clause-plugin/exclause"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UIProductsRepo interface {
|
type UIProductsRepo interface {
|
||||||
GetJSON(p_id_product, p_id_shop, p_id_lang, p_id_customer, b2b_id_country, p_quantity int) (*json.RawMessage, error)
|
GetJSON(p_id_product, p_id_shop, p_id_lang, p_id_customer, b2b_id_country, p_quantity int) (*json.RawMessage, error)
|
||||||
Find(id_lang uint, p find.Paging, filt *filters.FiltersList) (find.Found[model.ProductInList], error)
|
Find(id_lang uint, p find.Paging, filt *filters.FiltersList) (*find.Found[model.ProductInList], error)
|
||||||
|
GetProductVariants(langID uint, productID uint, shopID uint, customerID uint, countryID uint, quantity uint) ([]view.ProductAttribute, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProductsRepo struct{}
|
type ProductsRepo struct{}
|
||||||
@@ -46,10 +48,7 @@ func (repo *ProductsRepo) GetJSON(p_id_product, p_id_shop, p_id_lang, p_id_custo
|
|||||||
return &raw, nil
|
return &raw, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (repo *ProductsRepo) Find(id_lang uint, p find.Paging, filt *filters.FiltersList) (find.Found[model.ProductInList], error) {
|
func (repo *ProductsRepo) Find(langID uint, p find.Paging, filt *filters.FiltersList) (*find.Found[model.ProductInList], error) {
|
||||||
var list []model.ProductInList
|
|
||||||
var total int64
|
|
||||||
|
|
||||||
query := db.Get().
|
query := db.Get().
|
||||||
Table(gormcol.Field.Tab(dbmodel.PsProductShopCols.Active)+" AS ps").
|
Table(gormcol.Field.Tab(dbmodel.PsProductShopCols.Active)+" AS ps").
|
||||||
Select(`
|
Select(`
|
||||||
@@ -63,9 +62,9 @@ func (repo *ProductsRepo) Find(id_lang uint, p find.Paging, filt *filters.Filter
|
|||||||
sa.quantity AS quantity
|
sa.quantity AS quantity
|
||||||
`, config.Get().Image.ImagePrefix).
|
`, config.Get().Image.ImagePrefix).
|
||||||
Joins("JOIN "+dbmodel.PsProductCols.IDProduct.Tab()+" p ON p.id_product = ps.id_product").
|
Joins("JOIN "+dbmodel.PsProductCols.IDProduct.Tab()+" p ON p.id_product = ps.id_product").
|
||||||
Joins("JOIN ps_product_lang pl ON pl.id_product = ps.id_product AND pl.id_lang = ?", id_lang).
|
Joins("JOIN ps_product_lang pl ON pl.id_product = ps.id_product AND pl.id_lang = ?", langID).
|
||||||
Joins("JOIN ps_image_shop ims ON ims.id_product = ps.id_product AND ims.cover = 1").
|
Joins("JOIN ps_image_shop ims ON ims.id_product = ps.id_product AND ims.cover = 1").
|
||||||
Joins("JOIN ps_category_lang cl ON cl.id_category = ps.id_category_default AND cl.id_lang = ?", id_lang).
|
Joins("JOIN ps_category_lang cl ON cl.id_category = ps.id_category_default AND cl.id_lang = ?", langID).
|
||||||
Joins("JOIN ps_category_product cp ON cp.id_product = ps.id_product").
|
Joins("JOIN ps_category_product cp ON cp.id_product = ps.id_product").
|
||||||
Joins("LEFT JOIN variants v ON v.id_product = ps.id_product").
|
Joins("LEFT JOIN variants v ON v.id_product = ps.id_product").
|
||||||
Joins("LEFT JOIN ps_stock_available sa ON sa.id_product = ps.id_product AND sa.id_product_attribute = 0").
|
Joins("LEFT JOIN ps_stock_available sa ON sa.id_product = ps.id_product AND sa.id_product_attribute = 0").
|
||||||
@@ -79,27 +78,62 @@ func (repo *ProductsRepo) Find(id_lang uint, p find.Paging, filt *filters.Filter
|
|||||||
}}).
|
}}).
|
||||||
Order("ps.id_product DESC")
|
Order("ps.id_product DESC")
|
||||||
|
|
||||||
// Apply all filters
|
query = query.Scopes(filt.All()...)
|
||||||
if filt != nil {
|
|
||||||
filt.ApplyAll(query)
|
|
||||||
}
|
|
||||||
|
|
||||||
// run counter first as query is without limit and offset
|
list, err := find.Paginate[model.ProductInList](langID, p, query)
|
||||||
err := query.Count(&total).Error
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return find.Found[model.ProductInList]{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
return &list, nil
|
||||||
err = query.
|
}
|
||||||
Limit(p.Limit()).
|
|
||||||
Offset(p.Offset()).
|
func (repo *ProductsRepo) GetProductVariants(langID uint, productID uint, shopID uint, customerID uint, countryID uint, quantity uint) ([]view.ProductAttribute, error) {
|
||||||
Find(&list).Error
|
var result []view.ProductAttribute
|
||||||
if err != nil {
|
err := db.DB.
|
||||||
return find.Found[model.ProductInList]{}, err
|
Raw(`
|
||||||
}
|
CALL get_product_attributes_with_price(?, ?, ?, ?, ?, ?)
|
||||||
|
`,
|
||||||
return find.Found[model.ProductInList]{
|
langID,
|
||||||
Items: list,
|
productID,
|
||||||
Count: uint(total),
|
shopID,
|
||||||
}, nil
|
customerID,
|
||||||
|
countryID,
|
||||||
|
quantity,
|
||||||
|
).
|
||||||
|
Scan(&result).Error
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *ProductsRepo) PopulateProductPrice(product *model.ProductInList, targetCustomer *model.Customer, quantity int, shopID uint) error {
|
||||||
|
row := db.Get().Raw(
|
||||||
|
"CALL get_product_price(?, ?, ?, ?, ?)",
|
||||||
|
product.ProductID,
|
||||||
|
shopID,
|
||||||
|
targetCustomer.ID,
|
||||||
|
targetCustomer.CountryID,
|
||||||
|
quantity,
|
||||||
|
).Row()
|
||||||
|
|
||||||
|
var (
|
||||||
|
id uint
|
||||||
|
base float64
|
||||||
|
excl float64
|
||||||
|
incl float64
|
||||||
|
tax float64
|
||||||
|
)
|
||||||
|
|
||||||
|
err := row.Scan(&id, &base, &excl, &incl, &tax)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
product.PriceTaxExcl = excl
|
||||||
|
product.PriceTaxIncl = incl
|
||||||
|
return nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import (
|
|||||||
"github.com/golang-jwt/jwt/v5"
|
"github.com/golang-jwt/jwt/v5"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
"gorm.io/gorm/clause"
|
||||||
)
|
)
|
||||||
|
|
||||||
// JWTClaims represents the JWT claims
|
// JWTClaims represents the JWT claims
|
||||||
@@ -436,7 +437,7 @@ func (s *AuthService) RevokeAllRefreshTokens(userID uint) {
|
|||||||
// GetUserByID retrieves a user by ID
|
// GetUserByID retrieves a user by ID
|
||||||
func (s *AuthService) GetUserByID(userID uint) (*model.Customer, error) {
|
func (s *AuthService) GetUserByID(userID uint) (*model.Customer, error) {
|
||||||
var user model.Customer
|
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) {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return nil, responseErrors.ErrUserNotFound
|
return nil, responseErrors.ErrUserNotFound
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,15 @@ package productService
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/db"
|
||||||
"git.ma-al.com/goc_daniel/b2b/app/model"
|
"git.ma-al.com/goc_daniel/b2b/app/model"
|
||||||
"git.ma-al.com/goc_daniel/b2b/app/repos/productsRepo"
|
"git.ma-al.com/goc_daniel/b2b/app/repos/productsRepo"
|
||||||
constdata "git.ma-al.com/goc_daniel/b2b/app/utils/const_data"
|
constdata "git.ma-al.com/goc_daniel/b2b/app/utils/const_data"
|
||||||
"git.ma-al.com/goc_daniel/b2b/app/utils/query/filters"
|
"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/query/find"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/view"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ProductService struct {
|
type ProductService struct {
|
||||||
@@ -29,6 +32,64 @@ func (s *ProductService) GetJSON(p_id_product, p_id_lang, p_id_customer, b2b_id_
|
|||||||
return products, nil
|
return products, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ProductService) Find(id_lang uint, p find.Paging, filters *filters.FiltersList) (find.Found[model.ProductInList], error) {
|
func (s *ProductService) Find(id_lang uint, p find.Paging, filters *filters.FiltersList, customer *model.Customer, quantity int, shopID uint) (*find.Found[model.ProductInList], error) {
|
||||||
return s.productsRepo.Find(id_lang, p, filters)
|
if customer == nil || customer.Country == nil {
|
||||||
|
return nil, errors.New("customer is nil or is missing fields")
|
||||||
|
}
|
||||||
|
// customer.ID, customer.CountryID, uint(customer.Country.CurrencyID), quantity, shopID
|
||||||
|
|
||||||
|
found, err := s.productsRepo.Find(id_lang, p, filters)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for i := range found.Items {
|
||||||
|
if found.Items[i].VariantsNumber <= 0 {
|
||||||
|
err := s.PopulateProductPrice(&found.Items[i], customer, quantity, shopID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return found, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ProductService) PopulateProductPrice(product *model.ProductInList, targetCustomer *model.Customer, quantity int, shopID uint) error {
|
||||||
|
row := db.Get().Raw(
|
||||||
|
"CALL get_product_price(?, ?, ?, ?, ?)",
|
||||||
|
product.ProductID,
|
||||||
|
shopID,
|
||||||
|
targetCustomer.ID,
|
||||||
|
targetCustomer.CountryID,
|
||||||
|
quantity,
|
||||||
|
).Row()
|
||||||
|
|
||||||
|
var (
|
||||||
|
id uint
|
||||||
|
base float64
|
||||||
|
excl float64
|
||||||
|
incl float64
|
||||||
|
tax float64
|
||||||
|
)
|
||||||
|
|
||||||
|
err := row.Scan(&id, &base, &excl, &incl, &tax)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
product.PriceTaxExcl = excl
|
||||||
|
product.PriceTaxIncl = incl
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ProductService) GetProductAttributes(
|
||||||
|
langID uint,
|
||||||
|
productID uint,
|
||||||
|
shopID uint,
|
||||||
|
customerID uint,
|
||||||
|
countryID uint,
|
||||||
|
quantity uint,
|
||||||
|
) ([]view.ProductAttribute, error) {
|
||||||
|
return s.productsRepo.GetProductVariants(langID, productID, shopID, customerID, countryID, quantity)
|
||||||
}
|
}
|
||||||
|
|||||||
13
app/view/product.go
Normal file
13
app/view/product.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package view
|
||||||
|
|
||||||
|
import "encoding/json"
|
||||||
|
|
||||||
|
type ProductAttribute struct {
|
||||||
|
IDProductAttribute int64 `gorm:"column:id_product_attribute" json:"id_product_attribute"`
|
||||||
|
Reference string `gorm:"column:reference" json:"reference"`
|
||||||
|
BasePrice float64 `gorm:"column:base_price" json:"base_price"`
|
||||||
|
PriceTaxExcl float64 `gorm:"column:price_tax_excl" json:"price_tax_excl"`
|
||||||
|
PriceTaxIncl float64 `gorm:"column:price_tax_incl" json:"price_tax_incl"`
|
||||||
|
Quantity int64 `gorm:"column:quantity" json:"quantity"`
|
||||||
|
Attributes json.RawMessage `gorm:"column:attributes" json:"attributes"`
|
||||||
|
}
|
||||||
22
bruno/api_v1/product/Product Variants List.yml
Normal file
22
bruno/api_v1/product/Product Variants List.yml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
info:
|
||||||
|
name: Product Variants List
|
||||||
|
type: http
|
||||||
|
seq: 3
|
||||||
|
|
||||||
|
http:
|
||||||
|
method: GET
|
||||||
|
url: "{{bas_url}}/restricted/product/list-variants/{{product_id}}"
|
||||||
|
body:
|
||||||
|
type: json
|
||||||
|
data: ""
|
||||||
|
|
||||||
|
runtime:
|
||||||
|
variables:
|
||||||
|
- name: product_id
|
||||||
|
value: "2361"
|
||||||
|
|
||||||
|
settings:
|
||||||
|
encodeUrl: true
|
||||||
|
timeout: 0
|
||||||
|
followRedirects: true
|
||||||
|
maxRedirects: 5
|
||||||
@@ -377,4 +377,537 @@ LIMIT
|
|||||||
END //
|
END //
|
||||||
|
|
||||||
DELIMITER ;
|
DELIMITER ;
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
DELIMITER //
|
||||||
|
|
||||||
|
DROP PROCEDURE IF EXISTS get_product_attributes_with_price //
|
||||||
|
CREATE PROCEDURE get_product_attributes_with_price(
|
||||||
|
IN p_id_lang INT UNSIGNED,
|
||||||
|
IN p_id_product INT UNSIGNED,
|
||||||
|
IN p_id_shop INT UNSIGNED,
|
||||||
|
IN p_id_customer INT UNSIGNED,
|
||||||
|
IN p_id_country INT UNSIGNED,
|
||||||
|
IN p_quantity INT UNSIGNED
|
||||||
|
)
|
||||||
|
BEGIN
|
||||||
|
|
||||||
|
DECLARE v_tax_rate DECIMAL(10,4) DEFAULT 0;
|
||||||
|
DECLARE v_currency_rate DECIMAL(10,4) DEFAULT 1;
|
||||||
|
|
||||||
|
-- =========================================================
|
||||||
|
-- TAX
|
||||||
|
-- =========================================================
|
||||||
|
SELECT COALESCE(t.rate, 0)
|
||||||
|
INTO v_tax_rate
|
||||||
|
FROM ps_tax_rule tr
|
||||||
|
INNER JOIN ps_tax t ON t.id_tax = tr.id_tax
|
||||||
|
LEFT JOIN b2b_countries c ON c.id = p_id_country
|
||||||
|
WHERE tr.id_tax_rules_group = (
|
||||||
|
SELECT ps.id_tax_rules_group
|
||||||
|
FROM ps_product_shop ps
|
||||||
|
WHERE ps.id_product = p_id_product
|
||||||
|
AND ps.id_shop = p_id_shop
|
||||||
|
LIMIT 1
|
||||||
|
)
|
||||||
|
AND tr.id_country = c.ps_id_country
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
|
-- =========================================================
|
||||||
|
-- CURRENCY
|
||||||
|
-- =========================================================
|
||||||
|
SELECT COALESCE(r.conversion_rate, 1)
|
||||||
|
INTO v_currency_rate
|
||||||
|
FROM b2b_countries c
|
||||||
|
LEFT JOIN b2b_currencies cur ON cur.id = c.b2b_id_currency
|
||||||
|
LEFT JOIN b2b_currency_rates r ON r.b2b_id_currency = cur.id
|
||||||
|
WHERE c.id = p_id_country
|
||||||
|
ORDER BY r.created_at DESC
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
|
-- =========================================================
|
||||||
|
-- MAIN RESULT
|
||||||
|
-- =========================================================
|
||||||
|
SELECT
|
||||||
|
pa.id_product_attribute,
|
||||||
|
pa.reference,
|
||||||
|
|
||||||
|
-- =====================================================
|
||||||
|
-- BASE PRICE (product + attribute impact)
|
||||||
|
-- =====================================================
|
||||||
|
(
|
||||||
|
(COALESCE(ps.price, p.price) + COALESCE(pas.price, 0))
|
||||||
|
* v_currency_rate
|
||||||
|
) AS base_price,
|
||||||
|
|
||||||
|
-- =====================================================
|
||||||
|
-- FINAL PRICE EXCL (FULL RULE ENGINE)
|
||||||
|
-- =====================================================
|
||||||
|
COALESCE(sp.price_tax_excl,
|
||||||
|
(COALESCE(ps.price, p.price) + COALESCE(pas.price, 0)) * v_currency_rate
|
||||||
|
) AS price_tax_excl,
|
||||||
|
|
||||||
|
-- =====================================================
|
||||||
|
-- FINAL PRICE INCL
|
||||||
|
-- =====================================================
|
||||||
|
(
|
||||||
|
COALESCE(sp.price_tax_excl,
|
||||||
|
(COALESCE(ps.price, p.price) + COALESCE(pas.price, 0)) * v_currency_rate
|
||||||
|
)
|
||||||
|
) * (1 + v_tax_rate / 100) AS price_tax_incl,
|
||||||
|
|
||||||
|
-- =====================================================
|
||||||
|
-- STOCK
|
||||||
|
-- =====================================================
|
||||||
|
IFNULL(sa.quantity, 0) AS quantity,
|
||||||
|
|
||||||
|
-- =====================================================
|
||||||
|
-- ATTRIBUTES
|
||||||
|
-- =====================================================
|
||||||
|
(
|
||||||
|
SELECT JSON_ARRAYAGG(
|
||||||
|
JSON_OBJECT(
|
||||||
|
'group', agl.name,
|
||||||
|
'attribute', al.name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
FROM ps_product_attribute_combination pac
|
||||||
|
JOIN ps_attribute a ON a.id_attribute = pac.id_attribute
|
||||||
|
JOIN ps_attribute_lang al
|
||||||
|
ON al.id_attribute = a.id_attribute AND al.id_lang = p_id_lang
|
||||||
|
JOIN ps_attribute_group_lang agl
|
||||||
|
ON agl.id_attribute_group = a.id_attribute_group AND agl.id_lang = p_id_lang
|
||||||
|
WHERE pac.id_product_attribute = pa.id_product_attribute
|
||||||
|
) AS attributes
|
||||||
|
|
||||||
|
FROM ps_product_attribute pa
|
||||||
|
|
||||||
|
JOIN ps_product_attribute_shop pas
|
||||||
|
ON pas.id_product_attribute = pa.id_product_attribute
|
||||||
|
AND pas.id_shop = p_id_shop
|
||||||
|
|
||||||
|
JOIN ps_product p
|
||||||
|
ON p.id_product = pa.id_product
|
||||||
|
|
||||||
|
LEFT JOIN ps_product_shop ps
|
||||||
|
ON ps.id_product = p.id_product
|
||||||
|
AND ps.id_shop = p_id_shop
|
||||||
|
|
||||||
|
LEFT JOIN ps_stock_available sa
|
||||||
|
ON sa.id_product = pa.id_product
|
||||||
|
AND sa.id_product_attribute = pa.id_product_attribute
|
||||||
|
AND sa.id_shop = p_id_shop
|
||||||
|
|
||||||
|
-- =====================================================
|
||||||
|
-- FULL SPECIFIC PRICE ENGINE (IDENTICAL RULES TO MAIN)
|
||||||
|
-- =====================================================
|
||||||
|
LEFT JOIN (
|
||||||
|
|
||||||
|
SELECT *
|
||||||
|
FROM (
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
pa.id_product_attribute,
|
||||||
|
|
||||||
|
-- FINAL PRICE
|
||||||
|
CASE
|
||||||
|
WHEN bsp.reduction_type = 'amount' THEN
|
||||||
|
CASE
|
||||||
|
WHEN bsp.b2b_id_currency IS NULL THEN bsp.price
|
||||||
|
ELSE bsp.price
|
||||||
|
END
|
||||||
|
|
||||||
|
WHEN bsp.reduction_type = 'percentage' THEN
|
||||||
|
(
|
||||||
|
(COALESCE(ps.price, p.price) + COALESCE(pas.price, 0))
|
||||||
|
* v_currency_rate
|
||||||
|
) * (1 - bsp.percentage_reduction / 100)
|
||||||
|
|
||||||
|
ELSE
|
||||||
|
(
|
||||||
|
(COALESCE(ps.price, p.price) + COALESCE(pas.price, 0))
|
||||||
|
* v_currency_rate
|
||||||
|
)
|
||||||
|
END AS price_tax_excl,
|
||||||
|
|
||||||
|
ROW_NUMBER() OVER (
|
||||||
|
PARTITION BY pa.id_product_attribute
|
||||||
|
ORDER BY
|
||||||
|
bsp.scope = 'product' DESC,
|
||||||
|
bsp.scope = 'category' DESC,
|
||||||
|
bsp.scope = 'shop' DESC,
|
||||||
|
bsp.from_quantity DESC,
|
||||||
|
bsp.id DESC
|
||||||
|
) AS rn
|
||||||
|
|
||||||
|
FROM ps_product_attribute pa
|
||||||
|
|
||||||
|
JOIN ps_product p ON p.id_product = pa.id_product
|
||||||
|
|
||||||
|
LEFT JOIN ps_product_shop ps
|
||||||
|
ON ps.id_product = p.id_product
|
||||||
|
AND ps.id_shop = p_id_shop
|
||||||
|
|
||||||
|
LEFT JOIN ps_product_attribute_shop pas
|
||||||
|
ON pas.id_product_attribute = pa.id_product_attribute
|
||||||
|
|
||||||
|
JOIN b2b_specific_price bsp ON bsp.is_active = TRUE
|
||||||
|
|
||||||
|
LEFT JOIN b2b_specific_price_product bsp_p
|
||||||
|
ON bsp_p.b2b_specific_price_id = bsp.id
|
||||||
|
|
||||||
|
LEFT JOIN b2b_specific_price_category bsp_c
|
||||||
|
ON bsp_c.b2b_specific_price_id = bsp.id
|
||||||
|
|
||||||
|
WHERE pa.id_product = p_id_product
|
||||||
|
|
||||||
|
-- =========================
|
||||||
|
-- SCOPE
|
||||||
|
-- =========================
|
||||||
|
AND (
|
||||||
|
(bsp.scope = 'product' AND bsp_p.id_product = p_id_product)
|
||||||
|
|
||||||
|
OR (
|
||||||
|
bsp.scope = 'category'
|
||||||
|
AND bsp_c.id_category IN (
|
||||||
|
SELECT id_category
|
||||||
|
FROM ps_category_product
|
||||||
|
WHERE id_product = p_id_product
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
OR (bsp.scope = 'shop')
|
||||||
|
)
|
||||||
|
|
||||||
|
-- =========================
|
||||||
|
-- CUSTOMER
|
||||||
|
-- =========================
|
||||||
|
AND (
|
||||||
|
NOT EXISTS (
|
||||||
|
SELECT 1 FROM b2b_specific_price_customer c
|
||||||
|
WHERE c.b2b_specific_price_id = bsp.id
|
||||||
|
)
|
||||||
|
OR EXISTS (
|
||||||
|
SELECT 1 FROM b2b_specific_price_customer c
|
||||||
|
WHERE c.b2b_specific_price_id = bsp.id
|
||||||
|
AND c.b2b_id_customer = p_id_customer
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
-- =========================
|
||||||
|
-- COUNTRY
|
||||||
|
-- =========================
|
||||||
|
AND (
|
||||||
|
NOT EXISTS (
|
||||||
|
SELECT 1 FROM b2b_specific_price_country c
|
||||||
|
WHERE c.b2b_specific_price_id = bsp.id
|
||||||
|
)
|
||||||
|
OR EXISTS (
|
||||||
|
SELECT 1 FROM b2b_specific_price_country c
|
||||||
|
WHERE c.b2b_specific_price_id = bsp.id
|
||||||
|
AND c.b2b_id_country = p_id_country
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
-- =========================
|
||||||
|
-- ATTRIBUTE
|
||||||
|
-- =========================
|
||||||
|
AND (
|
||||||
|
NOT EXISTS (
|
||||||
|
SELECT 1 FROM b2b_specific_price_product_attribute a
|
||||||
|
WHERE a.b2b_specific_price_id = bsp.id
|
||||||
|
)
|
||||||
|
OR EXISTS (
|
||||||
|
SELECT 1 FROM b2b_specific_price_product_attribute a
|
||||||
|
WHERE a.b2b_specific_price_id = bsp.id
|
||||||
|
AND a.id_product_attribute = pa.id_product_attribute
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
-- =========================
|
||||||
|
-- QUANTITY
|
||||||
|
-- =========================
|
||||||
|
AND bsp.from_quantity <= p_quantity
|
||||||
|
|
||||||
|
-- =========================
|
||||||
|
-- DATE
|
||||||
|
-- =========================
|
||||||
|
AND (
|
||||||
|
bsp.has_expiration_date = FALSE
|
||||||
|
OR (
|
||||||
|
(bsp.valid_from IS NULL OR bsp.valid_from <= NOW())
|
||||||
|
AND (bsp.valid_till IS NULL OR bsp.valid_till >= NOW())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
) ranked
|
||||||
|
|
||||||
|
WHERE ranked.rn = 1
|
||||||
|
|
||||||
|
) sp ON sp.id_product_attribute = pa.id_product_attribute
|
||||||
|
|
||||||
|
WHERE pa.id_product = p_id_product;
|
||||||
|
|
||||||
|
END //
|
||||||
|
|
||||||
|
DELIMITER ;
|
||||||
|
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
DELIMITER //
|
||||||
|
|
||||||
|
DROP PROCEDURE IF EXISTS get_product_price //
|
||||||
|
CREATE PROCEDURE get_product_price(
|
||||||
|
IN p_id_product INT UNSIGNED,
|
||||||
|
IN p_id_shop INT UNSIGNED,
|
||||||
|
IN p_id_customer INT UNSIGNED,
|
||||||
|
IN p_id_country INT UNSIGNED,
|
||||||
|
IN p_quantity INT UNSIGNED
|
||||||
|
)
|
||||||
|
BEGIN
|
||||||
|
|
||||||
|
DECLARE v_tax_rate DECIMAL(10,4) DEFAULT 0;
|
||||||
|
DECLARE v_currency_rate DECIMAL(10,4) DEFAULT 1;
|
||||||
|
|
||||||
|
DECLARE v_base_price DECIMAL(20,6) DEFAULT 0;
|
||||||
|
DECLARE v_final_excl DECIMAL(20,6) DEFAULT 0;
|
||||||
|
DECLARE v_final_incl DECIMAL(20,6) DEFAULT 0;
|
||||||
|
|
||||||
|
DECLARE v_has_specific INT DEFAULT 0;
|
||||||
|
DECLARE v_reduction_type VARCHAR(20);
|
||||||
|
DECLARE v_percentage DECIMAL(10,4);
|
||||||
|
DECLARE v_fixed_price DECIMAL(20,6);
|
||||||
|
DECLARE v_specific_currency_id INT;
|
||||||
|
|
||||||
|
-- =========================
|
||||||
|
-- 1. TAX RATE
|
||||||
|
-- =========================
|
||||||
|
SELECT COALESCE(t.rate, 0)
|
||||||
|
INTO v_tax_rate
|
||||||
|
FROM ps_tax_rule tr
|
||||||
|
INNER JOIN ps_tax t ON t.id_tax = tr.id_tax
|
||||||
|
LEFT JOIN b2b_countries c ON c.id = p_id_country
|
||||||
|
WHERE tr.id_tax_rules_group = (
|
||||||
|
SELECT ps.id_tax_rules_group
|
||||||
|
FROM ps_product_shop ps
|
||||||
|
WHERE ps.id_product = p_id_product
|
||||||
|
AND ps.id_shop = p_id_shop
|
||||||
|
LIMIT 1
|
||||||
|
)
|
||||||
|
AND tr.id_country = c.ps_id_country
|
||||||
|
ORDER BY tr.id_state DESC, tr.zipcode_from DESC, tr.id_tax_rule DESC
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
|
-- =========================
|
||||||
|
-- 2. CURRENCY RATE
|
||||||
|
-- =========================
|
||||||
|
SELECT COALESCE(r.conversion_rate, 1)
|
||||||
|
INTO v_currency_rate
|
||||||
|
FROM b2b_countries c
|
||||||
|
LEFT JOIN b2b_currencies cur ON cur.id = c.b2b_id_currency
|
||||||
|
LEFT JOIN b2b_currency_rates r ON r.b2b_id_currency = cur.id
|
||||||
|
WHERE c.id = p_id_country
|
||||||
|
ORDER BY r.created_at DESC
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
|
-- =========================
|
||||||
|
-- 3. BASE PRICE
|
||||||
|
-- =========================
|
||||||
|
SELECT COALESCE(ps.price, p.price) * v_currency_rate
|
||||||
|
INTO v_base_price
|
||||||
|
FROM ps_product p
|
||||||
|
LEFT JOIN ps_product_shop ps
|
||||||
|
ON ps.id_product = p.id_product
|
||||||
|
AND ps.id_shop = p_id_shop
|
||||||
|
WHERE p.id_product = p_id_product
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
|
-- =========================
|
||||||
|
-- 4. SPECIFIC PRICE (correct wildcard-aware match)
|
||||||
|
-- =========================
|
||||||
|
SELECT
|
||||||
|
1,
|
||||||
|
bsp.reduction_type,
|
||||||
|
bsp.percentage_reduction,
|
||||||
|
bsp.price,
|
||||||
|
bsp.b2b_id_currency
|
||||||
|
INTO
|
||||||
|
v_has_specific,
|
||||||
|
v_reduction_type,
|
||||||
|
v_percentage,
|
||||||
|
v_fixed_price,
|
||||||
|
v_specific_currency_id
|
||||||
|
FROM b2b_specific_price bsp
|
||||||
|
|
||||||
|
LEFT JOIN b2b_specific_price_product bsp_p
|
||||||
|
ON bsp_p.b2b_specific_price_id = bsp.id
|
||||||
|
|
||||||
|
LEFT JOIN b2b_specific_price_category bsp_c
|
||||||
|
ON bsp_c.b2b_specific_price_id = bsp.id
|
||||||
|
|
||||||
|
LEFT JOIN b2b_specific_price_customer bsp_u
|
||||||
|
ON bsp_u.b2b_specific_price_id = bsp.id
|
||||||
|
|
||||||
|
LEFT JOIN b2b_specific_price_country bsp_ct
|
||||||
|
ON bsp_ct.b2b_specific_price_id = bsp.id
|
||||||
|
|
||||||
|
LEFT JOIN b2b_specific_price_product_attribute bsp_pa
|
||||||
|
ON bsp_pa.b2b_specific_price_id = bsp.id
|
||||||
|
|
||||||
|
WHERE bsp.is_active = TRUE
|
||||||
|
|
||||||
|
-- =========================
|
||||||
|
-- PRODUCT / CATEGORY / SHOP SCOPE
|
||||||
|
-- =========================
|
||||||
|
AND (
|
||||||
|
(bsp.scope = 'product' AND bsp_p.id_product = p_id_product)
|
||||||
|
|
||||||
|
OR (
|
||||||
|
bsp.scope = 'category'
|
||||||
|
AND bsp_c.id_category IN (
|
||||||
|
SELECT id_category
|
||||||
|
FROM ps_category_product
|
||||||
|
WHERE id_product = p_id_product
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
OR (bsp.scope = 'shop')
|
||||||
|
)
|
||||||
|
|
||||||
|
-- =========================
|
||||||
|
-- CUSTOMER RULE (wildcard-aware)
|
||||||
|
-- =========================
|
||||||
|
AND (
|
||||||
|
NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM b2b_specific_price_customer c
|
||||||
|
WHERE c.b2b_specific_price_id = bsp.id
|
||||||
|
)
|
||||||
|
OR EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM b2b_specific_price_customer c
|
||||||
|
WHERE c.b2b_specific_price_id = bsp.id
|
||||||
|
AND c.b2b_id_customer = p_id_customer
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
-- =========================
|
||||||
|
-- COUNTRY RULE (wildcard-aware)
|
||||||
|
-- =========================
|
||||||
|
AND (
|
||||||
|
NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM b2b_specific_price_country c
|
||||||
|
WHERE c.b2b_specific_price_id = bsp.id
|
||||||
|
)
|
||||||
|
OR EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM b2b_specific_price_country c
|
||||||
|
WHERE c.b2b_specific_price_id = bsp.id
|
||||||
|
AND c.b2b_id_country = p_id_country
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
-- =========================
|
||||||
|
-- PRODUCT ATTRIBUTE RULE (wildcard-aware)
|
||||||
|
-- =========================
|
||||||
|
AND (
|
||||||
|
NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM b2b_specific_price_product_attribute a
|
||||||
|
WHERE a.b2b_specific_price_id = bsp.id
|
||||||
|
)
|
||||||
|
OR EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM b2b_specific_price_product_attribute a
|
||||||
|
WHERE a.b2b_specific_price_id = bsp.id
|
||||||
|
AND a.id_product_attribute = 0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
-- =========================
|
||||||
|
-- QUANTITY RULE
|
||||||
|
-- =========================
|
||||||
|
AND bsp.from_quantity <= p_quantity
|
||||||
|
|
||||||
|
-- =========================
|
||||||
|
-- DATE RULE (FIXED precedence bug)
|
||||||
|
-- =========================
|
||||||
|
AND (
|
||||||
|
bsp.has_expiration_date = FALSE
|
||||||
|
OR (
|
||||||
|
(bsp.valid_from IS NULL OR bsp.valid_from <= NOW())
|
||||||
|
AND (bsp.valid_till IS NULL OR bsp.valid_till >= NOW())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
-- =========================
|
||||||
|
-- PRIORITY ORDERING (IMPROVED)
|
||||||
|
-- =========================
|
||||||
|
ORDER BY
|
||||||
|
-- strongest match wins
|
||||||
|
(EXISTS (
|
||||||
|
SELECT 1 FROM b2b_specific_price_customer c
|
||||||
|
WHERE c.b2b_specific_price_id = bsp.id
|
||||||
|
)) DESC,
|
||||||
|
|
||||||
|
(EXISTS (
|
||||||
|
SELECT 1 FROM b2b_specific_price_country c
|
||||||
|
WHERE c.b2b_specific_price_id = bsp.id
|
||||||
|
)) DESC,
|
||||||
|
|
||||||
|
bsp.scope = 'product' DESC,
|
||||||
|
bsp.scope = 'category' DESC,
|
||||||
|
bsp.scope = 'shop' DESC,
|
||||||
|
|
||||||
|
bsp.from_quantity DESC,
|
||||||
|
bsp.id DESC
|
||||||
|
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
|
-- =========================
|
||||||
|
-- 5. APPLY SPECIFIC PRICE
|
||||||
|
-- =========================
|
||||||
|
SET v_final_excl = v_base_price;
|
||||||
|
|
||||||
|
IF v_has_specific = 1 THEN
|
||||||
|
|
||||||
|
IF v_reduction_type = 'amount' THEN
|
||||||
|
|
||||||
|
IF v_specific_currency_id IS NULL THEN
|
||||||
|
SET v_final_excl = v_fixed_price;
|
||||||
|
ELSE
|
||||||
|
SET v_final_excl = v_fixed_price; -- assume already converted or pre-handled
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
ELSEIF v_reduction_type = 'percentage' THEN
|
||||||
|
SET v_final_excl = v_base_price * (1 - v_percentage / 100);
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- =========================
|
||||||
|
-- 6. TAX
|
||||||
|
-- =========================
|
||||||
|
SET v_final_incl = v_final_excl * (1 + v_tax_rate / 100);
|
||||||
|
|
||||||
|
-- =========================
|
||||||
|
-- 7. RETURN RESULT
|
||||||
|
-- =========================
|
||||||
|
SELECT
|
||||||
|
p_id_product AS id_product,
|
||||||
|
v_base_price AS price_base,
|
||||||
|
v_final_excl AS price_tax_excl,
|
||||||
|
v_final_incl AS price_tax_incl,
|
||||||
|
v_tax_rate AS tax_rate;
|
||||||
|
|
||||||
|
END //
|
||||||
|
|
||||||
|
DELIMITER ;
|
||||||
|
|
||||||
|
|
||||||
-- +goose Down
|
-- +goose Down
|
||||||
|
|||||||
Reference in New Issue
Block a user