Compare commits
20 Commits
66df535317
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 931840c243 | |||
|
|
d73dad8975 | ||
|
|
7995177fe1 | ||
| 70e0e23ace | |||
| 9961d90fa7 | |||
|
|
f435a8839b | ||
| 7bce04e05a | |||
| 9a90de3f11 | |||
|
|
754bf2fe01 | ||
|
|
2ca07f03ce | ||
| 6efb39edf7 | |||
| e9af4bf311 | |||
| cc570cc6a8 | |||
| 1bf706dcd0 | |||
| 84b4c70ffb | |||
|
|
2fd9472db1 | ||
| e31ecda582 | |||
| 8e063978a8 | |||
| 31a2744131 | |||
| f55d59a0fd |
3
.env
3
.env
@@ -64,3 +64,6 @@ IMAGE_PREFIX=https://www.naluconcept.com # remove prefix to serv them from same
|
||||
CORS_ORGIN=https://www.naluconcept.com
|
||||
|
||||
DSN=root:Maal12345678@tcp(localhost:3306)/nalu
|
||||
|
||||
LOG_LEVEL=warn
|
||||
LOG_COLORIZE=true
|
||||
@@ -4,8 +4,11 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"git.ma-al.com/goc_daniel/b2b/app/config"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/delivery/web"
|
||||
|
||||
"git.ma-al.com/goc_daniel/b2b/app/service/langsService"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/logger"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/version"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -21,6 +24,8 @@ var (
|
||||
return
|
||||
}
|
||||
|
||||
logger.Init("b2b", nil, config.Get().Log.LogLevel, config.Get().Log.LogColorize)
|
||||
|
||||
// Create and setup the server
|
||||
server := web.New()
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ type Config struct {
|
||||
Cors CorsConfig
|
||||
MeiliSearch MeiliSearchConfig
|
||||
Storage StorageConfig
|
||||
Log LogConfig
|
||||
}
|
||||
|
||||
type I18n struct {
|
||||
@@ -87,6 +88,11 @@ type AppConfig struct {
|
||||
BaseURL string `env:"APP_BASE_URL,http://localhost:5173"`
|
||||
}
|
||||
|
||||
type LogConfig struct {
|
||||
LogLevel string `env:"LOG_LEVEL,warn"`
|
||||
LogColorize bool `env:"LOG_COLORIZE,true"`
|
||||
}
|
||||
|
||||
type EmailConfig struct {
|
||||
SMTPHost string `env:"EMAIL_SMTP_HOST,localhost"`
|
||||
SMTPPort int `env:"EMAIL_SMTP_PORT,587"`
|
||||
@@ -209,6 +215,11 @@ func load() *Config {
|
||||
if err != nil {
|
||||
slog.Error("not possible to load env variables for storage : ", err.Error(), "")
|
||||
}
|
||||
|
||||
err = loadEnv(&cfg.Log)
|
||||
if err != nil {
|
||||
slog.Error("not possible to load env variables for logger : ", err.Error(), "")
|
||||
}
|
||||
cfg.Storage.RootFolder = ResolveRelativePath(cfg.Storage.RootFolder)
|
||||
|
||||
return cfg
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package public
|
||||
|
||||
import (
|
||||
"log"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
@@ -11,6 +10,7 @@ import (
|
||||
"git.ma-al.com/goc_daniel/b2b/app/service/authService"
|
||||
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/logger"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/nullable"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/response"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors"
|
||||
@@ -178,7 +178,13 @@ func (h *AuthHandler) ForgotPassword(c fiber.Ctx) error {
|
||||
// Request password reset - always return success to prevent email enumeration
|
||||
err := h.authService.RequestPasswordReset(req.Email)
|
||||
if err != nil {
|
||||
log.Printf("Password reset request error: %v", err)
|
||||
|
||||
logger.Warn("password reset request failed",
|
||||
"handler", "AuthHandler.ForgotPassword",
|
||||
|
||||
"email", req.Email,
|
||||
"error", err.Error(),
|
||||
)
|
||||
}
|
||||
|
||||
return c.JSON(fiber.Map{
|
||||
@@ -307,7 +313,6 @@ func (h *AuthHandler) Register(c fiber.Ctx) error {
|
||||
// Attempt registration
|
||||
err := h.authService.Register(&req)
|
||||
if err != nil {
|
||||
log.Printf("Register error: %v", err)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).JSON(fiber.Map{
|
||||
"error": responseErrors.GetErrorCode(c, err),
|
||||
})
|
||||
@@ -447,7 +452,6 @@ func (h *AuthHandler) GoogleCallback(c fiber.Ctx) error {
|
||||
|
||||
response, rawRefreshToken, err := h.authService.HandleGoogleCallback(code)
|
||||
if err != nil {
|
||||
log.Printf("Google OAuth callback error: %v", err)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).JSON(fiber.Map{
|
||||
"error": responseErrors.GetErrorCode(c, err),
|
||||
})
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"git.ma-al.com/goc_daniel/b2b/app/service/addressesService"
|
||||
"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/logger"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/nullable"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/response"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors"
|
||||
@@ -45,6 +46,13 @@ func (h *AddressesHandler) GetTemplate(c fiber.Ctx) error {
|
||||
|
||||
template, err := h.addressesService.GetTemplate(uint(country_id))
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to get address template",
|
||||
"handler", "AddressesHandler.GetTemplate",
|
||||
|
||||
"country_id", country_id,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
@@ -74,6 +82,13 @@ func (h *AddressesHandler) AddNewAddress(c fiber.Ctx) error {
|
||||
|
||||
err = h.addressesService.AddNewAddress(userID, address_info, uint(country_id))
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to add new address",
|
||||
"handler", "AddressesHandler.AddNewAddress",
|
||||
|
||||
"user_id", userID,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
@@ -110,6 +125,14 @@ func (h *AddressesHandler) ModifyAddress(c fiber.Ctx) error {
|
||||
|
||||
err = h.addressesService.ModifyAddress(userID, uint(address_id), address_info, uint(country_id))
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to modify address",
|
||||
"handler", "AddressesHandler.ModifyAddress",
|
||||
|
||||
"user_id", userID,
|
||||
"address_id", address_id,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
@@ -126,6 +149,13 @@ func (h *AddressesHandler) RetrieveAddressesInfo(c fiber.Ctx) error {
|
||||
|
||||
addresses, err := h.addressesService.RetrieveAddresses(userID)
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to retrieve addresses",
|
||||
"handler", "AddressesHandler.RetrieveAddressesInfo",
|
||||
|
||||
"user_id", userID,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
@@ -149,6 +179,14 @@ func (h *AddressesHandler) DeleteAddress(c fiber.Ctx) error {
|
||||
|
||||
err = h.addressesService.DeleteAddress(userID, uint(address_id))
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to delete address",
|
||||
"handler", "AddressesHandler.DeleteAddress",
|
||||
|
||||
"user_id", userID,
|
||||
"address_id", address_id,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"git.ma-al.com/goc_daniel/b2b/app/service/cartsService"
|
||||
"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/logger"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/nullable"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/response"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors"
|
||||
@@ -46,8 +47,16 @@ func (h *CartsHandler) AddNewCart(c fiber.Ctx) error {
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody)))
|
||||
}
|
||||
|
||||
new_cart, err := h.cartsService.CreateNewCart(userID)
|
||||
name := c.Query("name")
|
||||
new_cart, err := h.cartsService.CreateNewCart(userID, name)
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to create cart",
|
||||
"handler", "CartsHandler.AddNewCart",
|
||||
|
||||
"user_id", userID,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
@@ -71,6 +80,14 @@ func (h *CartsHandler) RemoveCart(c fiber.Ctx) error {
|
||||
|
||||
err = h.cartsService.RemoveCart(userID, uint(cart_id))
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to remove cart",
|
||||
"handler", "CartsHandler.RemoveCart",
|
||||
|
||||
"user_id", userID,
|
||||
"cart_id", cart_id,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
@@ -96,6 +113,14 @@ func (h *CartsHandler) ChangeCartName(c fiber.Ctx) error {
|
||||
|
||||
err = h.cartsService.UpdateCartName(userID, uint(cart_id), new_name)
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to update cart name",
|
||||
"handler", "CartsHandler.ChangeCartName",
|
||||
|
||||
"user_id", userID,
|
||||
"cart_id", cart_id,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
@@ -112,6 +137,13 @@ func (h *CartsHandler) RetrieveCartsInfo(c fiber.Ctx) error {
|
||||
|
||||
carts_info, err := h.cartsService.RetrieveCartsInfo(userID)
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to retrieve carts info",
|
||||
"handler", "CartsHandler.RetrieveCartsInfo",
|
||||
|
||||
"user_id", userID,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
@@ -135,6 +167,14 @@ func (h *CartsHandler) RetrieveCart(c fiber.Ctx) error {
|
||||
|
||||
cart, err := h.cartsService.RetrieveCart(userID, uint(cart_id))
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to retrieve cart",
|
||||
"handler", "CartsHandler.RetrieveCart",
|
||||
|
||||
"user_id", userID,
|
||||
"cart_id", cart_id,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
@@ -194,6 +234,15 @@ func (h *CartsHandler) AddProduct(c fiber.Ctx) error {
|
||||
|
||||
err = h.cartsService.AddProduct(userID, uint(cart_id), uint(product_id), product_attribute_id, amount, set_amount)
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to add product to cart",
|
||||
"handler", "CartsHandler.AddProduct",
|
||||
|
||||
"user_id", userID,
|
||||
"cart_id", cart_id,
|
||||
"product_id", product_id,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
@@ -239,6 +288,15 @@ func (h *CartsHandler) RemoveProduct(c fiber.Ctx) error {
|
||||
|
||||
err = h.cartsService.RemoveProduct(userID, uint(cart_id), uint(product_id), product_attribute_id)
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to remove product from cart",
|
||||
"handler", "CartsHandler.RemoveProduct",
|
||||
|
||||
"user_id", userID,
|
||||
"cart_id", cart_id,
|
||||
"product_id", product_id,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"git.ma-al.com/goc_daniel/b2b/app/model"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/service/currencyService"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/i18n"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/logger"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/nullable"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/response"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors"
|
||||
@@ -46,6 +47,12 @@ func (h *CurrencyHandler) PostCurrencyRate(c fiber.Ctx) error {
|
||||
|
||||
err := h.CurrencyService.CreateCurrencyRate(¤cyRate)
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to create currency rate",
|
||||
"handler", "CurrencyHandler.PostCurrencyRate",
|
||||
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
@@ -63,6 +70,13 @@ func (h *CurrencyHandler) GetCurrencyRate(c fiber.Ctx) error {
|
||||
|
||||
currency, err := h.CurrencyService.GetCurrency(uint(id))
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to get currency",
|
||||
"handler", "CurrencyHandler.GetCurrencyRate",
|
||||
|
||||
"currency_id", id,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"git.ma-al.com/goc_daniel/b2b/app/service/customerService"
|
||||
"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/logger"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/nullable"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/query/query_params"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/response"
|
||||
@@ -32,6 +33,7 @@ func CustomerHandlerRoutes(r fiber.Router) fiber.Router {
|
||||
|
||||
r.Get("", handler.customerData)
|
||||
r.Get("/list", middleware.Require(perms.UserReadAny), handler.listCustomers)
|
||||
r.Patch("/no-vat", middleware.Require(perms.UserWriteAny), handler.setCustomerNoVatStatus)
|
||||
return r
|
||||
}
|
||||
|
||||
@@ -63,6 +65,13 @@ func (h *customerHandler) customerData(fc fiber.Ctx) error {
|
||||
|
||||
customer, err := h.service.GetById(customerId)
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to get customer",
|
||||
"handler", "customerHandler.customerData",
|
||||
|
||||
"customer_id", customerId,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return fc.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(fc, err)))
|
||||
}
|
||||
@@ -87,6 +96,12 @@ func (h *customerHandler) listCustomers(fc fiber.Ctx) error {
|
||||
|
||||
customer, err := h.service.Find(user.LangID, p, filt, search)
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to list customers",
|
||||
"handler", "customerHandler.listCustomers",
|
||||
|
||||
"error", err.Error(),
|
||||
)
|
||||
return fc.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(fc, err)))
|
||||
}
|
||||
@@ -100,3 +115,35 @@ var columnMappingListUsers map[string]string = map[string]string{
|
||||
"first_name": "users.first_name",
|
||||
"last_name": "users.last_name",
|
||||
}
|
||||
|
||||
func (h *customerHandler) setCustomerNoVatStatus(fc fiber.Ctx) error {
|
||||
user, ok := localeExtractor.GetCustomer(fc)
|
||||
if !ok || user == nil {
|
||||
return fc.Status(responseErrors.GetErrorStatus(responseErrors.ErrInvalidBody)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(fc, responseErrors.ErrInvalidBody)))
|
||||
}
|
||||
|
||||
var req struct {
|
||||
CustomerID uint `json:"customer_id"`
|
||||
IsNoVat bool `json:"is_no_vat"`
|
||||
}
|
||||
|
||||
if err := fc.Bind().Body(&req); err != nil {
|
||||
return fc.Status(responseErrors.GetErrorStatus(responseErrors.ErrJSONBody)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(fc, responseErrors.ErrJSONBody)))
|
||||
}
|
||||
|
||||
if err := h.service.SetCustomerNoVatStatus(req.CustomerID, req.IsNoVat); err != nil {
|
||||
|
||||
logger.Error("failed to set customer no vat status",
|
||||
"handler", "customerHandler.setCustomerNoVatStatus",
|
||||
|
||||
"customer_id", req.CustomerID,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return fc.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(fc, err)))
|
||||
}
|
||||
|
||||
return fc.JSON(response.Make(nullable.GetNil(""), 0, i18n.T_(fc, response.Message_OK)))
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package restricted
|
||||
import (
|
||||
"git.ma-al.com/goc_daniel/b2b/app/service/localeSelectorService"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/i18n"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/logger"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/nullable"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/response"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors"
|
||||
@@ -34,6 +35,12 @@ func LocaleSelectorHandlerRoutes(r fiber.Router) fiber.Router {
|
||||
func (h *LocaleSelectorHandler) GetLanguages(c fiber.Ctx) error {
|
||||
languages, err := h.localeSelectorService.GetLanguages()
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to get languages",
|
||||
"handler", "LocaleSelectorHandler.GetLanguages",
|
||||
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
@@ -44,6 +51,12 @@ func (h *LocaleSelectorHandler) GetLanguages(c fiber.Ctx) error {
|
||||
func (h *LocaleSelectorHandler) GetCountries(c fiber.Ctx) error {
|
||||
countries, err := h.localeSelectorService.GetCountriesAndCurrencies()
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to get countries",
|
||||
"handler", "LocaleSelectorHandler.GetCountries",
|
||||
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"git.ma-al.com/goc_daniel/b2b/app/service/menuService"
|
||||
"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/logger"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/nullable"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/response"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors"
|
||||
@@ -49,6 +50,12 @@ func (h *MenuHandler) GetCategoryTree(c fiber.Ctx) error {
|
||||
|
||||
category_tree, err := h.menuService.GetCategoryTree(uint(root_category_id), lang_id)
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to get category tree",
|
||||
"handler", "MenuHandler.GetCategoryTree",
|
||||
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
@@ -79,6 +86,12 @@ func (h *MenuHandler) GetBreadcrumb(c fiber.Ctx) error {
|
||||
|
||||
breadcrumb, err := h.menuService.GetBreadcrumb(uint(root_category_id), uint(category_id), lang_id)
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to get breadcrumb",
|
||||
"handler", "MenuHandler.GetBreadcrumb",
|
||||
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
@@ -94,6 +107,12 @@ func (h *MenuHandler) GetTopMenu(c fiber.Ctx) error {
|
||||
}
|
||||
menu, err := h.menuService.GetTopMenu(customer.LangID, customer.RoleID)
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to get top menu",
|
||||
"handler", "MenuHandler.GetTopMenu",
|
||||
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"git.ma-al.com/goc_daniel/b2b/app/service/orderService"
|
||||
"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/logger"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/nullable"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/query/query_params"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/response"
|
||||
@@ -53,6 +54,12 @@ func (h *OrdersHandler) ListOrders(c fiber.Ctx) error {
|
||||
|
||||
list, err := h.ordersService.Find(user, paging, filters)
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to list orders",
|
||||
"handler", "OrdersHandler.ListOrders",
|
||||
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
@@ -66,6 +73,11 @@ var columnMappingListOrders map[string]string = map[string]string{
|
||||
"name": "b2b_customer_orders.name",
|
||||
"country_id": "b2b_customer_orders.country_id",
|
||||
"status": "b2b_customer_orders.status",
|
||||
"base_price": "b2b_customer_orders.base_price",
|
||||
"tax_incl": "b2b_customer_orders.tax_incl",
|
||||
"tax_excl": "b2b_customer_orders.tax_excl",
|
||||
"created_at": "b2b_customer_orders.created_at",
|
||||
"updated_at": "b2b_customer_orders.updated_at",
|
||||
}
|
||||
|
||||
func (h *OrdersHandler) PlaceNewOrder(c fiber.Ctx) error {
|
||||
@@ -99,6 +111,13 @@ func (h *OrdersHandler) PlaceNewOrder(c fiber.Ctx) error {
|
||||
|
||||
err = h.ordersService.PlaceNewOrder(userID, uint(cart_id), name, uint(country_id), address_info)
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to place order",
|
||||
"handler", "OrdersHandler.PlaceNewOrder",
|
||||
|
||||
"user_id", userID,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
@@ -136,6 +155,13 @@ func (h *OrdersHandler) ChangeOrderAddress(c fiber.Ctx) error {
|
||||
|
||||
err = h.ordersService.ChangeOrderAddress(user, uint(order_id), uint(country_id), address_info)
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to change order address",
|
||||
"handler", "OrdersHandler.ChangeOrderAddress",
|
||||
|
||||
"order_id", order_id,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
@@ -163,6 +189,13 @@ func (h *OrdersHandler) ChangeOrderStatus(c fiber.Ctx) error {
|
||||
|
||||
err = h.ordersService.ChangeOrderStatus(user, uint(order_id), status)
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to change order status",
|
||||
"handler", "OrdersHandler.ChangeOrderStatus",
|
||||
|
||||
"order_id", order_id,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
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/localeExtractor"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/logger"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/nullable"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/query/query_params"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/response"
|
||||
@@ -74,6 +75,15 @@ func (h *ProductsHandler) GetProductJson(c fiber.Ctx) error {
|
||||
}
|
||||
productJson, err := h.productService.Get(uint(p_id_product), customer.LangID, customer.ID, uint(b2b_id_country), uint(p_quantity))
|
||||
if err != nil {
|
||||
logger.Error("failed to get product",
|
||||
"handler", "ProductsHandler.GetProductJson",
|
||||
"product_id", p_id_product,
|
||||
"lang_id", customer.LangID,
|
||||
"customer_id", customer.ID,
|
||||
"b2b_id_country", b2b_id_country,
|
||||
"quantity", p_quantity,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
@@ -96,6 +106,13 @@ func (h *ProductsHandler) ListProducts(c fiber.Ctx) error {
|
||||
|
||||
list, err := h.productService.Find(customer.LangID, customer.ID, paging, filters, customer, constdata.DEFAULT_PRODUCT_QUANTITY, constdata.SHOP_ID)
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to list products",
|
||||
"handler", "ProductsHandler.ListProducts",
|
||||
"lang_id", customer.LangID,
|
||||
"customer_id", customer.ID,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
@@ -112,6 +129,7 @@ var columnMappingListProducts map[string]string = map[string]string{
|
||||
"quantity": "bp.quantity",
|
||||
"is_favorite": "bp.is_favorite",
|
||||
"is_new": "bp.is_new",
|
||||
"is_oem": "bp.is_oem",
|
||||
}
|
||||
|
||||
func (h *ProductsHandler) AddToFavorites(c fiber.Ctx) error {
|
||||
@@ -131,6 +149,13 @@ func (h *ProductsHandler) AddToFavorites(c fiber.Ctx) error {
|
||||
|
||||
err = h.productService.AddToFavorites(userID, uint(productID))
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to add to favorites",
|
||||
"handler", "ProductsHandler.AddToFavorites",
|
||||
"user_id", userID,
|
||||
"product_id", productID,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
@@ -155,6 +180,12 @@ func (h *ProductsHandler) RemoveFromFavorites(c fiber.Ctx) error {
|
||||
|
||||
err = h.productService.RemoveFromFavorites(userID, uint(productID))
|
||||
if err != nil {
|
||||
logger.Error("failed to remove from favorites",
|
||||
"handler", "ProductsHandler.RemoveFromFavorites",
|
||||
"user_id", userID,
|
||||
"product_id", productID,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
@@ -179,6 +210,15 @@ func (h *ProductsHandler) ListProductVariants(c fiber.Ctx) error {
|
||||
|
||||
list, err := h.productService.GetProductAttributes(customer.LangID, uint(productID), constdata.SHOP_ID, customer.ID, customer.CountryID, constdata.DEFAULT_PRODUCT_QUANTITY)
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to list product variants",
|
||||
"handler", "ProductsHandler.ListProductVariants",
|
||||
"product_id", productID,
|
||||
"customer_id", customer.ID,
|
||||
"lang_id", customer.LangID,
|
||||
"country_id", customer.CountryID,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"git.ma-al.com/goc_daniel/b2b/app/service/productTranslationService"
|
||||
"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/logger"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/nullable"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/response"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors"
|
||||
@@ -66,6 +67,13 @@ func (h *ProductTranslationHandler) GetProductDescription(c fiber.Ctx) error {
|
||||
|
||||
description, err := h.productTranslationService.GetProductDescription(userID, uint(productID), uint(productLangID))
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to get product description",
|
||||
"handler", "ProductTranslationHandler.GetProductDescription",
|
||||
|
||||
"product_id", productID,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
@@ -103,6 +111,13 @@ func (h *ProductTranslationHandler) SaveProductDescription(c fiber.Ctx) error {
|
||||
|
||||
err = h.productTranslationService.SaveProductDescription(userID, uint(productID), uint(productLangID), updates)
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to save product description",
|
||||
"handler", "ProductTranslationHandler.SaveProductDescription",
|
||||
|
||||
"product_id", productID,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
@@ -147,6 +162,13 @@ func (h *ProductTranslationHandler) TranslateProductDescription(c fiber.Ctx) err
|
||||
|
||||
description, err := h.productTranslationService.TranslateProductDescription(userID, uint(productID), uint(productFromLangID), uint(productToLangID), aiModel)
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to translate product description",
|
||||
"handler", "ProductTranslationHandler.TranslateProductDescription",
|
||||
|
||||
"product_id", productID,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package restricted
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"git.ma-al.com/goc_daniel/b2b/app/delivery/middleware"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/delivery/middleware/perms"
|
||||
@@ -10,6 +9,7 @@ import (
|
||||
searchservice "git.ma-al.com/goc_daniel/b2b/app/service/searchService"
|
||||
"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/logger"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/nullable"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/response"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors"
|
||||
@@ -47,7 +47,13 @@ func (h *MeiliSearchHandler) CreateIndex(c fiber.Ctx) error {
|
||||
|
||||
err := h.meiliService.CreateIndex(id_lang)
|
||||
if err != nil {
|
||||
fmt.Printf("CreateIndex error: %v\n", err)
|
||||
|
||||
logger.Error("failed to create search index",
|
||||
"handler", "MeiliSearchHandler.CreateIndex",
|
||||
|
||||
"lang_id", id_lang,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
@@ -72,6 +78,13 @@ func (h *MeiliSearchHandler) Search(c fiber.Ctx) error {
|
||||
|
||||
result, err := h.searchService.Search(index, c.Body(), id_lang)
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to search",
|
||||
"handler", "MeiliSearchHandler.Search",
|
||||
|
||||
"index", index,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
@@ -80,6 +93,13 @@ func (h *MeiliSearchHandler) Search(c fiber.Ctx) error {
|
||||
if createErr := h.meiliService.CreateIndex(id_lang); createErr == nil {
|
||||
result, err = h.searchService.Search(index, c.Body(), id_lang)
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to search after index creation",
|
||||
"handler", "MeiliSearchHandler.Search",
|
||||
|
||||
"index", index,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
@@ -100,6 +120,13 @@ func (h *MeiliSearchHandler) GetSettings(c fiber.Ctx) error {
|
||||
|
||||
result, err := h.searchService.GetIndexSettings(index)
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to get index settings",
|
||||
"handler", "MeiliSearchHandler.GetSettings",
|
||||
|
||||
"index", index,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"git.ma-al.com/goc_daniel/b2b/app/model"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/service/specificPriceService"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/i18n"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/logger"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/nullable"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/response"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors"
|
||||
@@ -51,6 +52,12 @@ func (h *SpecificPriceHandler) Create(c fiber.Ctx) error {
|
||||
|
||||
result, err := h.SpecificPriceService.Create(c.Context(), &pr)
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to create specific price",
|
||||
"handler", "SpecificPriceHandler.Create",
|
||||
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
@@ -74,6 +81,13 @@ func (h *SpecificPriceHandler) Update(c fiber.Ctx) error {
|
||||
|
||||
result, err := h.SpecificPriceService.Update(c.Context(), id, &pr)
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to update specific price",
|
||||
"handler", "SpecificPriceHandler.Update",
|
||||
|
||||
"specific_price_id", id,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
@@ -84,6 +98,12 @@ func (h *SpecificPriceHandler) Update(c fiber.Ctx) error {
|
||||
func (h *SpecificPriceHandler) List(c fiber.Ctx) error {
|
||||
result, err := h.SpecificPriceService.List(c.Context())
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to list specific prices",
|
||||
"handler", "SpecificPriceHandler.List",
|
||||
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
@@ -101,6 +121,13 @@ func (h *SpecificPriceHandler) GetByID(c fiber.Ctx) error {
|
||||
|
||||
result, err := h.SpecificPriceService.GetByID(c.Context(), id)
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to get specific price",
|
||||
"handler", "SpecificPriceHandler.GetByID",
|
||||
|
||||
"specific_price_id", id,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
@@ -118,6 +145,13 @@ func (h *SpecificPriceHandler) Activate(c fiber.Ctx) error {
|
||||
|
||||
err = h.SpecificPriceService.SetActive(c.Context(), id, true)
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to activate specific price",
|
||||
"handler", "SpecificPriceHandler.Activate",
|
||||
|
||||
"specific_price_id", id,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
@@ -135,6 +169,13 @@ func (h *SpecificPriceHandler) Deactivate(c fiber.Ctx) error {
|
||||
|
||||
err = h.SpecificPriceService.SetActive(c.Context(), id, false)
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to deactivate specific price",
|
||||
"handler", "SpecificPriceHandler.Deactivate",
|
||||
|
||||
"specific_price_id", id,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
@@ -152,6 +193,13 @@ func (h *SpecificPriceHandler) Delete(c fiber.Ctx) error {
|
||||
|
||||
err = h.SpecificPriceService.Delete(c.Context(), id)
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to delete specific price",
|
||||
"handler", "SpecificPriceHandler.Delete",
|
||||
|
||||
"specific_price_id", id,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"git.ma-al.com/goc_daniel/b2b/app/service/storageService"
|
||||
"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/logger"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/nullable"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/response"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors"
|
||||
@@ -52,6 +53,12 @@ func (h *StorageHandler) ListContent(c fiber.Ctx) error {
|
||||
entries_in_list, err := h.storageService.ListContent(abs_path)
|
||||
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to list storage content",
|
||||
"handler", "StorageHandler.ListContent",
|
||||
"path", abs_path,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
@@ -68,6 +75,12 @@ func (h *StorageHandler) DownloadFile(c fiber.Ctx) error {
|
||||
|
||||
f, filename, filesize, err := h.storageService.DownloadFilePrep(abs_path)
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to prepare file download",
|
||||
"handler", "StorageHandler.DownloadFile",
|
||||
"path", abs_path,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
@@ -87,6 +100,12 @@ func (h *StorageHandler) CreateNewWebdavToken(c fiber.Ctx) error {
|
||||
|
||||
new_token, err := h.storageService.NewWebdavToken(userID)
|
||||
if err != nil {
|
||||
|
||||
logger.Error("failed to create webdav token",
|
||||
"handler", "StorageHandler.CreateNewWebdavToken",
|
||||
"user_id", userID,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ type Customer struct {
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||||
IsNoVat bool `gorm:"default:false" json:"is_no_vat"`
|
||||
}
|
||||
|
||||
func (u *Customer) HasPermission(permission perms.Permission) bool {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
type CustomerOrder struct {
|
||||
OrderID uint `gorm:"column:order_id;primaryKey;autoIncrement" json:"order_id"`
|
||||
UserID uint `gorm:"column:user_id;not null;index" json:"user_id"`
|
||||
@@ -8,6 +10,11 @@ type CustomerOrder struct {
|
||||
AddressString string `gorm:"column:address_string;not null" json:"address_string"`
|
||||
AddressUnparsed *AddressUnparsed `gorm:"-" json:"address_unparsed"`
|
||||
Status string `gorm:"column:status;size:50;not null" json:"status"`
|
||||
BasePrice float64 `gorm:"column:base_price;type:decimal(10,2);not null" json:"base_price"`
|
||||
TaxIncl float64 `gorm:"column:tax_incl;type:decimal(10,2);not null" json:"tax_incl"`
|
||||
TaxExcl float64 `gorm:"column:tax_excl;type:decimal(10,2);not null" json:"tax_excl"`
|
||||
CreatedAt time.Time `gorm:"column:created_at;not null" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at;not null" json:"updated_at"`
|
||||
Products []OrderProduct `gorm:"foreignKey:OrderID;references:OrderID" json:"products"`
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,8 @@ type ProductInList struct {
|
||||
PriceTaxExcl float64 `gorm:"column:price_tax_excl" json:"price_tax_excl"`
|
||||
PriceTaxIncl float64 `gorm:"column:price_tax_incl" json:"price_tax_incl"`
|
||||
IsFavorite bool `gorm:"column:is_favorite" json:"is_favorite"`
|
||||
IsNew uint `gorm:"column:is_new" json:"is_new"`
|
||||
IsNew bool `gorm:"column:is_new" json:"is_new"`
|
||||
IsOEM bool `gorm:"column:is_oem" json:"is_oem"`
|
||||
}
|
||||
|
||||
type ProductFilters struct {
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
|
||||
type UICartsRepo interface {
|
||||
CartsAmount(user_id uint) (uint, error)
|
||||
CreateNewCart(user_id uint) (model.CustomerCart, error)
|
||||
CreateNewCart(user_id uint, name string) (model.CustomerCart, error)
|
||||
RemoveCart(user_id uint, cart_id uint) error
|
||||
UserHasCart(user_id uint, cart_id uint) (bool, error)
|
||||
UpdateCartName(user_id uint, cart_id uint, new_name string) error
|
||||
@@ -42,10 +42,7 @@ func (repo *CartsRepo) CartsAmount(user_id uint) (uint, error) {
|
||||
return amt, err
|
||||
}
|
||||
|
||||
func (repo *CartsRepo) CreateNewCart(user_id uint) (model.CustomerCart, error) {
|
||||
var name string
|
||||
name = constdata.DEFAULT_NEW_CART_NAME
|
||||
|
||||
func (repo *CartsRepo) CreateNewCart(user_id uint, name string) (model.CustomerCart, error) {
|
||||
cart := model.CustomerCart{
|
||||
UserID: user_id,
|
||||
Name: &name,
|
||||
|
||||
@@ -17,6 +17,7 @@ type UICustomerRepo interface {
|
||||
Find(langId uint, p find.Paging, filt *filters.FiltersList, search string) (*find.Found[model.UserInList], error)
|
||||
Save(customer *model.Customer) error
|
||||
Create(customer *model.Customer) error
|
||||
SetCustomerNoVatStatus(customerID uint, isNoVat bool) error
|
||||
}
|
||||
|
||||
type CustomerRepo struct{}
|
||||
@@ -114,3 +115,7 @@ func (repo *CustomerRepo) Save(customer *model.Customer) error {
|
||||
func (repo *CustomerRepo) Create(customer *model.Customer) error {
|
||||
return db.DB.Create(customer).Error
|
||||
}
|
||||
|
||||
func (repo *CustomerRepo) SetCustomerNoVatStatus(customerID uint, isNoVat bool) error {
|
||||
return db.DB.Model(&model.Customer{}).Where("id = ?", customerID).Update("is_no_vat", isNoVat).Error
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package localeSelectorRepo
|
||||
import (
|
||||
"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/dbmodel"
|
||||
)
|
||||
|
||||
type UILocaleSelectorRepo interface {
|
||||
@@ -25,7 +26,9 @@ func (r *LocaleSelectorRepo) GetLanguages() ([]model.Language, error) {
|
||||
func (r *LocaleSelectorRepo) GetCountriesAndCurrencies() ([]model.Country, error) {
|
||||
var countries []model.Country
|
||||
err := db.Get().
|
||||
Preload("PSCurrency").
|
||||
Select("*").
|
||||
Preload("Currency").
|
||||
Joins("LEFT JOIN " + dbmodel.TableNamePsCountryLang + " AS cl ON cl." + dbmodel.PsCountryLangCols.IDCountry.Col() + " = b2b_countries.ps_id_country AND cl." + dbmodel.PsCountryLangCols.IDLang.Col() + " = 2").
|
||||
Find(&countries).Error
|
||||
return countries, err
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package ordersRepo
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"git.ma-al.com/goc_daniel/b2b/app/db"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/model"
|
||||
constdata "git.ma-al.com/goc_daniel/b2b/app/utils/const_data"
|
||||
@@ -11,7 +13,7 @@ import (
|
||||
type UIOrdersRepo interface {
|
||||
UserHasOrder(user_id uint, order_id uint) (bool, error)
|
||||
Find(user_id uint, p find.Paging, filt *filters.FiltersList) (*find.Found[model.CustomerOrder], error)
|
||||
PlaceNewOrder(cart *model.CustomerCart, name string, country_id uint, address_info string) error
|
||||
PlaceNewOrder(cart *model.CustomerCart, name string, country_id uint, address_info string, base_price float64, tax_incl float64, tax_excl float64) error
|
||||
ChangeOrderAddress(order_id uint, country_id uint, address_info string) error
|
||||
ChangeOrderStatus(order_id uint, status string) error
|
||||
}
|
||||
@@ -69,7 +71,7 @@ func (repo *OrdersRepo) Find(user_id uint, p find.Paging, filt *filters.FiltersL
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (repo *OrdersRepo) PlaceNewOrder(cart *model.CustomerCart, name string, country_id uint, address_info string) error {
|
||||
func (repo *OrdersRepo) PlaceNewOrder(cart *model.CustomerCart, name string, country_id uint, address_info string, base_price float64, tax_incl float64, tax_excl float64) error {
|
||||
order := model.CustomerOrder{
|
||||
UserID: cart.UserID,
|
||||
Name: name,
|
||||
@@ -87,6 +89,12 @@ func (repo *OrdersRepo) PlaceNewOrder(cart *model.CustomerCart, name string, cou
|
||||
})
|
||||
}
|
||||
|
||||
order.CreatedAt = time.Now()
|
||||
order.UpdatedAt = time.Now()
|
||||
order.BasePrice = base_price
|
||||
order.TaxIncl = tax_incl
|
||||
order.TaxExcl = tax_excl
|
||||
|
||||
return db.DB.Create(&order).Error
|
||||
}
|
||||
|
||||
@@ -97,6 +105,7 @@ func (repo *OrdersRepo) ChangeOrderAddress(order_id uint, country_id uint, addre
|
||||
Updates(map[string]interface{}{
|
||||
"country_id": country_id,
|
||||
"address_string": address_info,
|
||||
"updated_at": time.Now(),
|
||||
}).
|
||||
Error
|
||||
}
|
||||
@@ -105,6 +114,9 @@ func (repo *OrdersRepo) ChangeOrderStatus(order_id uint, status string) error {
|
||||
return db.DB.
|
||||
Table("b2b_customer_orders").
|
||||
Where("order_id = ?", order_id).
|
||||
Update("status", status).
|
||||
Updates(map[string]interface{}{
|
||||
"status": status,
|
||||
"updated_at": time.Now(),
|
||||
}).
|
||||
Error
|
||||
}
|
||||
|
||||
@@ -122,6 +122,19 @@ func (repo *ProductsRepo) Find(langID uint, userID uint, p find.Paging, filt *fi
|
||||
Group("product_id"),
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "oems",
|
||||
Subquery: exclause.Subquery{
|
||||
DB: db.DB.
|
||||
Table("b2b_oems").
|
||||
Select(`
|
||||
product_id AS product_id,
|
||||
COUNT(*) > 0 AS is_customers_oem
|
||||
`).
|
||||
Where("user_id = ?", userID).
|
||||
Group("product_id"),
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "new_product_days",
|
||||
Subquery: exclause.Subquery{
|
||||
@@ -150,6 +163,7 @@ func (repo *ProductsRepo) Find(langID uint, userID uint, p find.Paging, filt *fi
|
||||
pl.name AS name,
|
||||
ps.id_category_default AS category_id,
|
||||
p.reference AS reference,
|
||||
p.is_oem AS is_oem,
|
||||
sa.quantity AS quantity,
|
||||
COALESCE(f.is_favorite, 0) AS is_favorite,
|
||||
CASE
|
||||
@@ -166,7 +180,9 @@ func (repo *ProductsRepo) Find(langID uint, userID uint, p find.Paging, filt *fi
|
||||
Joins("LEFT JOIN favorites f ON f.product_id = 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 new_product_days npd ON 1 = 1").
|
||||
Joins("LEFT JOIN oems ON oems.product_id = ps.id_product").
|
||||
Where("ps.active = ?", 1).
|
||||
Where("(p.is_oem = 0 OR oems.is_customers_oem > 0)").
|
||||
Group("ps.id_product"),
|
||||
},
|
||||
},
|
||||
@@ -182,7 +198,8 @@ func (repo *ProductsRepo) Find(langID uint, userID uint, p find.Paging, filt *fi
|
||||
COALESCE(v.variants_number, 0) AS variants_number,
|
||||
bp.quantity AS quantity,
|
||||
bp.is_favorite AS is_favorite,
|
||||
bp.is_new AS is_new
|
||||
bp.is_new AS is_new,
|
||||
bp.is_oem AS is_oem
|
||||
`, config.Get().Image.ImagePrefix).
|
||||
Joins("JOIN ps_product_lang pl ON pl.id_product = bp.product_id AND pl.id_lang = ?", langID).
|
||||
Joins("JOIN ps_image_shop ims ON ims.id_product = bp.product_id AND ims.cover = 1").
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
roleRepo "git.ma-al.com/goc_daniel/b2b/app/repos/rolesRepo"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/service/emailService"
|
||||
constdata "git.ma-al.com/goc_daniel/b2b/app/utils/const_data"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/logger"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors"
|
||||
|
||||
"github.com/dlclark/regexp2"
|
||||
@@ -68,22 +69,47 @@ func (s *AuthService) Login(req *model.LoginRequest) (*model.AuthResponse, strin
|
||||
// Find user by email
|
||||
if err := s.db.Preload("Role.Permissions").Where("email = ?", req.Email).First(&user).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
logger.Info("login failed - invalid credentials",
|
||||
"service", "AuthService.Login",
|
||||
"email", req.Email,
|
||||
"reason", "user not found",
|
||||
)
|
||||
return nil, "", responseErrors.ErrInvalidCredentials
|
||||
}
|
||||
logger.Error("login failed - database error",
|
||||
"service", "AuthService.Login",
|
||||
"email", req.Email,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return nil, "", fmt.Errorf("database error: %w", err)
|
||||
}
|
||||
// Check if user is active
|
||||
if !user.IsActive {
|
||||
logger.Info("login failed - user inactive",
|
||||
"service", "AuthService.Login",
|
||||
"email", req.Email,
|
||||
"reason", "user account is inactive",
|
||||
)
|
||||
return nil, "", responseErrors.ErrUserInactive
|
||||
}
|
||||
|
||||
// Check if email is verified
|
||||
if !user.EmailVerified {
|
||||
logger.Info("login failed - email not verified",
|
||||
"service", "AuthService.Login",
|
||||
"email", req.Email,
|
||||
"reason", "email not verified",
|
||||
)
|
||||
return nil, "", responseErrors.ErrEmailNotVerified
|
||||
}
|
||||
|
||||
// Verify password
|
||||
if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.Password)); err != nil {
|
||||
logger.Info("login failed - invalid credentials",
|
||||
"service", "AuthService.Login",
|
||||
"email", req.Email,
|
||||
"reason", "wrong password",
|
||||
)
|
||||
return nil, "", responseErrors.ErrInvalidCredentials
|
||||
}
|
||||
|
||||
@@ -94,22 +120,38 @@ func (s *AuthService) Login(req *model.LoginRequest) (*model.AuthResponse, strin
|
||||
if req.LangID != nil {
|
||||
_, err := s.GetLangISOCode(*req.LangID)
|
||||
if err != nil {
|
||||
logger.Warn("login failed - invalid language ID",
|
||||
"service", "AuthService.Login",
|
||||
"email", req.Email,
|
||||
"reason", "invalid language ID",
|
||||
)
|
||||
return nil, "", responseErrors.ErrBadLangID
|
||||
}
|
||||
user.LangID = *req.LangID
|
||||
}
|
||||
|
||||
user.Country = nil
|
||||
s.db.Save(&user)
|
||||
|
||||
// Generate access token (JWT)
|
||||
accessToken, err := s.generateAccessToken(&user)
|
||||
if err != nil {
|
||||
logger.Error("login failed - token generation error",
|
||||
"service", "AuthService.Login",
|
||||
"email", req.Email,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return nil, "", fmt.Errorf("failed to generate access token: %w", err)
|
||||
}
|
||||
|
||||
// Generate opaque refresh token and store in DB
|
||||
rawRefreshToken, err := s.createRefreshToken(user.ID)
|
||||
if err != nil {
|
||||
logger.Error("login failed - refresh token creation error",
|
||||
"service", "AuthService.Login",
|
||||
"email", req.Email,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return nil, "", fmt.Errorf("failed to create refresh token: %w", err)
|
||||
}
|
||||
|
||||
@@ -170,6 +212,11 @@ func (s *AuthService) Register(req *model.RegisterRequest) error {
|
||||
}
|
||||
|
||||
if err := s.db.Create(&user).Error; err != nil {
|
||||
logger.Error("registration failed - database error",
|
||||
"service", "AuthService.Register",
|
||||
"email", req.Email,
|
||||
"error", err.Error(),
|
||||
)
|
||||
return fmt.Errorf("failed to create user: %w", err)
|
||||
}
|
||||
|
||||
@@ -181,8 +228,11 @@ func (s *AuthService) Register(req *model.RegisterRequest) error {
|
||||
}
|
||||
|
||||
if err := s.email.SendVerificationEmail(user.Email, user.EmailVerificationToken, baseURL, lang); err != nil {
|
||||
// Log error but don't fail registration - user can request resend
|
||||
_ = err
|
||||
logger.Warn("failed to send verification email",
|
||||
"service", "AuthService.Register",
|
||||
"email", req.Email,
|
||||
"error", err.Error(),
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -210,6 +260,7 @@ func (s *AuthService) CompleteRegistration(req *model.CompleteRegistrationReques
|
||||
user.EmailVerificationToken = ""
|
||||
user.EmailVerificationExpires = nil
|
||||
|
||||
user.Country = nil
|
||||
if err := s.db.Save(&user).Error; err != nil {
|
||||
return nil, "", fmt.Errorf("failed to update user: %w", err)
|
||||
}
|
||||
@@ -278,6 +329,7 @@ func (s *AuthService) RequestPasswordReset(emailAddr string) error {
|
||||
user.PasswordResetToken = token
|
||||
user.PasswordResetExpires = &expiresAt
|
||||
user.LastPasswordResetRequest = &now
|
||||
user.Country = nil
|
||||
if err := s.db.Save(&user).Error; err != nil {
|
||||
return fmt.Errorf("failed to save reset token: %w", err)
|
||||
}
|
||||
@@ -304,6 +356,10 @@ func (s *AuthService) ResetPassword(token, newPassword string) error {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return responseErrors.ErrInvalidResetToken
|
||||
}
|
||||
logger.Error("password reset failed - database error",
|
||||
"service", "AuthService.ResetPassword",
|
||||
"error", err.Error(),
|
||||
)
|
||||
return fmt.Errorf("database error: %w", err)
|
||||
}
|
||||
|
||||
@@ -328,7 +384,12 @@ func (s *AuthService) ResetPassword(token, newPassword string) error {
|
||||
user.PasswordResetToken = ""
|
||||
user.PasswordResetExpires = nil
|
||||
|
||||
user.Country = nil
|
||||
if err := s.db.Save(&user).Error; err != nil {
|
||||
logger.Error("password reset failed - database error",
|
||||
"service", "AuthService.ResetPassword",
|
||||
"error", err.Error(),
|
||||
)
|
||||
return fmt.Errorf("failed to update password: %w", err)
|
||||
}
|
||||
|
||||
@@ -539,6 +600,7 @@ func (s *AuthService) UpdateJWTToken(user *model.Customer) (string, error) {
|
||||
}
|
||||
|
||||
// Save the updated user
|
||||
user.Country = nil
|
||||
if err := s.db.Save(user).Error; err != nil {
|
||||
return "", fmt.Errorf("database error: %w", err)
|
||||
}
|
||||
|
||||
@@ -8,10 +8,12 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"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/utils/logger"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/view"
|
||||
"golang.org/x/oauth2"
|
||||
@@ -77,12 +79,20 @@ func (s *AuthService) HandleGoogleCallback(code string) (*model.AuthResponse, st
|
||||
// Find or create user
|
||||
user, err := s.findOrCreateGoogleUser(userInfo)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "database") {
|
||||
logger.Error("google oauth callback failed - database error",
|
||||
"service", "AuthService.HandleGoogleCallback",
|
||||
"email", userInfo.Email,
|
||||
"error", err.Error(),
|
||||
)
|
||||
}
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
// Update last login
|
||||
now := time.Now()
|
||||
user.LastLoginAt = &now
|
||||
user.Country = nil
|
||||
s.db.Save(user)
|
||||
|
||||
// Generate access token (JWT)
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"git.ma-al.com/goc_daniel/b2b/app/model"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/repos/cartsRepo"
|
||||
constdata "git.ma-al.com/goc_daniel/b2b/app/utils/const_data"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/logger"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors"
|
||||
)
|
||||
|
||||
@@ -17,7 +18,7 @@ func New() *CartsService {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *CartsService) CreateNewCart(user_id uint) (model.CustomerCart, error) {
|
||||
func (s *CartsService) CreateNewCart(user_id uint, name string) (model.CustomerCart, error) {
|
||||
var cart model.CustomerCart
|
||||
|
||||
customers_carts_amount, err := s.repo.CartsAmount(user_id)
|
||||
@@ -28,8 +29,21 @@ func (s *CartsService) CreateNewCart(user_id uint) (model.CustomerCart, error) {
|
||||
return cart, responseErrors.ErrMaxAmtOfCartsReached
|
||||
}
|
||||
|
||||
if name == "" {
|
||||
name = constdata.DEFAULT_NEW_CART_NAME
|
||||
}
|
||||
|
||||
// create new cart for customer
|
||||
cart, err = s.repo.CreateNewCart(user_id)
|
||||
cart, err = s.repo.CreateNewCart(user_id, name)
|
||||
if err != nil {
|
||||
return cart, err
|
||||
}
|
||||
|
||||
logger.Info("cart created",
|
||||
"service", "cartsService",
|
||||
"user_id", user_id,
|
||||
"cart_id", cart.CartID,
|
||||
)
|
||||
|
||||
return cart, nil
|
||||
}
|
||||
|
||||
@@ -24,3 +24,7 @@ func (s *CustomerService) GetById(id uint) (*model.Customer, error) {
|
||||
func (s *CustomerService) Find(langId uint, p find.Paging, filt *filters.FiltersList, search string) (*find.Found[model.UserInList], error) {
|
||||
return s.repo.Find(langId, p, filt, search)
|
||||
}
|
||||
|
||||
func (s *CustomerService) SetCustomerNoVatStatus(customerID uint, isNoVat bool) error {
|
||||
return s.repo.SetCustomerNoVatStatus(customerID, isNoVat)
|
||||
}
|
||||
|
||||
@@ -231,21 +231,54 @@ func (s *MenuService) GetTopMenu(languageId uint, roleId uint) ([]*model.B2BTopM
|
||||
|
||||
func (s *MenuService) appendAdditional(all_categories *[]model.ScannedCategory, id_lang uint, iso_code string) {
|
||||
for i := 0; i < len(*all_categories); i++ {
|
||||
(*all_categories)[i].Filter = "category_id_in=" + strconv.Itoa(int((*all_categories)[i].CategoryID))
|
||||
(*all_categories)[i].Filter = "category_id_eq=" + strconv.Itoa(int((*all_categories)[i].CategoryID))
|
||||
}
|
||||
|
||||
var additional model.ScannedCategory
|
||||
additional.CategoryID = 10001
|
||||
additional.Name = "New Products"
|
||||
additional.Active = 1
|
||||
additional.Position = 10
|
||||
additional.ParentID = 2
|
||||
additional.IsRoot = 0
|
||||
additional.LinkRewrite = i18n.T___(id_lang, "category.new_products")
|
||||
additional.IsoCode = iso_code
|
||||
// the new products category
|
||||
var new_products_category model.ScannedCategory
|
||||
new_products_category.CategoryID = constdata.ADDITIONAL_CATEGORIES_INDEX + 1
|
||||
new_products_category.Name = "New Products"
|
||||
new_products_category.Active = 1
|
||||
new_products_category.Position = 10
|
||||
new_products_category.ParentID = 2
|
||||
new_products_category.IsRoot = 0
|
||||
new_products_category.LinkRewrite = i18n.T___(id_lang, "category.new_products")
|
||||
new_products_category.IsoCode = iso_code
|
||||
|
||||
additional.Visited = false
|
||||
additional.Filter = "is_new_in=true"
|
||||
new_products_category.Visited = false
|
||||
new_products_category.Filter = "is_new_eq=true"
|
||||
|
||||
*all_categories = append(*all_categories, additional)
|
||||
*all_categories = append(*all_categories, new_products_category)
|
||||
|
||||
// the oem products category
|
||||
var oem_products_category model.ScannedCategory
|
||||
oem_products_category.CategoryID = constdata.ADDITIONAL_CATEGORIES_INDEX + 2
|
||||
oem_products_category.Name = "OEM Products"
|
||||
oem_products_category.Active = 1
|
||||
oem_products_category.Position = 11
|
||||
oem_products_category.ParentID = 2
|
||||
oem_products_category.IsRoot = 0
|
||||
oem_products_category.LinkRewrite = i18n.T___(id_lang, "category.oem_products")
|
||||
oem_products_category.IsoCode = iso_code
|
||||
|
||||
oem_products_category.Visited = false
|
||||
oem_products_category.Filter = "is_oem_eq=true"
|
||||
|
||||
*all_categories = append(*all_categories, oem_products_category)
|
||||
|
||||
// the favorite products category
|
||||
var favorite_products_category model.ScannedCategory
|
||||
favorite_products_category.CategoryID = constdata.ADDITIONAL_CATEGORIES_INDEX + 3
|
||||
favorite_products_category.Name = "Favourite Products" // British English version.
|
||||
favorite_products_category.Active = 1
|
||||
favorite_products_category.Position = 12
|
||||
favorite_products_category.ParentID = 2
|
||||
favorite_products_category.IsRoot = 0
|
||||
favorite_products_category.LinkRewrite = i18n.T___(id_lang, "category.favorite_products")
|
||||
favorite_products_category.IsoCode = iso_code
|
||||
|
||||
favorite_products_category.Visited = false
|
||||
favorite_products_category.Filter = "is_favorite_eq=true"
|
||||
|
||||
*all_categories = append(*all_categories, favorite_products_category)
|
||||
}
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
package orderService
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"git.ma-al.com/goc_daniel/b2b/app/delivery/middleware/perms"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/model"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/repos/cartsRepo"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/repos/ordersRepo"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/repos/productsRepo"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/service/addressesService"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/service/emailService"
|
||||
constdata "git.ma-al.com/goc_daniel/b2b/app/utils/const_data"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/logger"
|
||||
"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"
|
||||
@@ -18,6 +20,7 @@ import (
|
||||
type OrderService struct {
|
||||
ordersRepo ordersRepo.UIOrdersRepo
|
||||
cartsRepo cartsRepo.UICartsRepo
|
||||
productsRepo productsRepo.UIProductsRepo
|
||||
addressesService *addressesService.AddressesService
|
||||
emailService *emailService.EmailService
|
||||
}
|
||||
@@ -26,6 +29,7 @@ func New() *OrderService {
|
||||
return &OrderService{
|
||||
ordersRepo: ordersRepo.New(),
|
||||
cartsRepo: cartsRepo.New(),
|
||||
productsRepo: productsRepo.New(),
|
||||
addressesService: addressesService.New(),
|
||||
emailService: emailService.NewEmailService(),
|
||||
}
|
||||
@@ -45,9 +49,12 @@ func (s *OrderService) Find(user *model.Customer, p find.Paging, filt *filters.F
|
||||
|
||||
for i := 0; i < len(list.Items); i++ {
|
||||
address_unparsed, err := s.addressesService.ValidateAddressJson(list.Items[i].AddressString, list.Items[i].CountryID)
|
||||
// log such errors
|
||||
if err != nil {
|
||||
fmt.Printf("err: %v\n", err)
|
||||
logger.Warn("failed to validate address",
|
||||
"service", "orderService",
|
||||
"order_id", list.Items[i].OrderID,
|
||||
"error", err.Error(),
|
||||
)
|
||||
}
|
||||
|
||||
list.Items[i].AddressUnparsed = &address_unparsed
|
||||
@@ -82,8 +89,10 @@ func (s *OrderService) PlaceNewOrder(user_id uint, cart_id uint, name string, co
|
||||
name = *cart.Name
|
||||
}
|
||||
|
||||
base_price, tax_incl, tax_excl, err := s.getOrderTotalPrice(user_id, cart_id, country_id)
|
||||
|
||||
// all checks passed
|
||||
err = s.ordersRepo.PlaceNewOrder(cart, name, country_id, address_info)
|
||||
err = s.ordersRepo.PlaceNewOrder(cart, name, country_id, address_info, base_price, tax_incl, tax_excl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -92,16 +101,23 @@ func (s *OrderService) PlaceNewOrder(user_id uint, cart_id uint, name string, co
|
||||
// if no error is returned, remove the cart. This should be smooth
|
||||
err = s.cartsRepo.RemoveCart(user_id, cart_id)
|
||||
if err != nil {
|
||||
// Log error but don't fail placing order
|
||||
_ = err
|
||||
logger.Warn("failed to remove cart after order placement",
|
||||
"service", "orderService",
|
||||
"user_id", user_id,
|
||||
"cart_id", cart_id,
|
||||
"error", err.Error(),
|
||||
)
|
||||
}
|
||||
|
||||
// send email to admin
|
||||
go func(user_id uint) {
|
||||
err := s.emailService.SendNewOrderPlacedNotification(user_id)
|
||||
if err != nil {
|
||||
// Log error but don't fail placing order
|
||||
_ = err
|
||||
logger.Warn("failed to send new order notification",
|
||||
"service", "orderService",
|
||||
"user_id", user_id,
|
||||
"error", err.Error(),
|
||||
)
|
||||
}
|
||||
}(user_id)
|
||||
|
||||
@@ -143,3 +159,27 @@ func (s *OrderService) ChangeOrderStatus(user *model.Customer, order_id uint, st
|
||||
|
||||
return s.ordersRepo.ChangeOrderStatus(order_id, status)
|
||||
}
|
||||
|
||||
func (s *OrderService) getOrderTotalPrice(user_id uint, cart_id uint, country_id uint) (float64, float64, float64, error) {
|
||||
cart, err := s.cartsRepo.RetrieveCart(user_id, cart_id)
|
||||
if err != nil {
|
||||
return 0.0, 0.0, 0.0, err
|
||||
}
|
||||
|
||||
base_price := 0.0
|
||||
tax_incl := 0.0
|
||||
tax_excl := 0.0
|
||||
|
||||
for _, product := range cart.Products {
|
||||
prices, err := s.productsRepo.GetPrice(product.ProductID, product.ProductAttributeID, constdata.SHOP_ID, user_id, country_id, product.Amount)
|
||||
if err != nil {
|
||||
return 0.0, 0.0, 0.0, err
|
||||
}
|
||||
|
||||
base_price += prices.Base
|
||||
tax_incl += prices.FinalTaxIncl
|
||||
tax_excl += prices.FinalTaxExcl
|
||||
}
|
||||
|
||||
return base_price, tax_incl, tax_excl, nil
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ const ADMIN_NOTIFICATION_LANGUAGE = 2
|
||||
|
||||
// CATEGORY_TREE_ROOT_ID corresponds to id_category in ps_category which has is_root_category=1
|
||||
const CATEGORY_TREE_ROOT_ID = 2
|
||||
const ADDITIONAL_CATEGORIES_INDEX = 10000
|
||||
|
||||
// since arrays can not be const
|
||||
var CATEGORY_BLACKLIST = []uint{250}
|
||||
|
||||
151
app/utils/logger/logger.go
Normal file
151
app/utils/logger/logger.go
Normal file
@@ -0,0 +1,151 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var L *slog.Logger
|
||||
|
||||
const (
|
||||
reset = "\033[0m"
|
||||
red = "\033[31m"
|
||||
yellow = "\033[33m"
|
||||
green = "\033[32m"
|
||||
blue = "\033[36m"
|
||||
gray = "\033[90m"
|
||||
)
|
||||
|
||||
type consoleHandler struct {
|
||||
w io.Writer
|
||||
colorize bool
|
||||
}
|
||||
|
||||
func (h *consoleHandler) Enabled(ctx context.Context, level slog.Level) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (h *consoleHandler) Handle(ctx context.Context, r slog.Record) error {
|
||||
level := r.Level.String()
|
||||
color := reset
|
||||
|
||||
switch r.Level {
|
||||
case slog.LevelError:
|
||||
color = red
|
||||
case slog.LevelWarn:
|
||||
color = yellow
|
||||
case slog.LevelInfo:
|
||||
color = blue
|
||||
case slog.LevelDebug:
|
||||
color = gray
|
||||
}
|
||||
|
||||
var msg string
|
||||
if h.colorize {
|
||||
msg = fmt.Sprintf("%s%s%s %s%s%s %s%s%s",
|
||||
reset, r.Time.Format("15:04:05"), reset,
|
||||
color, level, reset,
|
||||
reset, r.Message, reset)
|
||||
} else {
|
||||
msg = fmt.Sprintf("%s %s %s", r.Time.Format("15:04:05"), level, r.Message)
|
||||
}
|
||||
|
||||
var pairs []string
|
||||
r.Attrs(func(attr slog.Attr) bool {
|
||||
if h.colorize {
|
||||
pairs = append(pairs, fmt.Sprintf("%s%s=%s%v%s", green, attr.Key, reset, attr.Value, reset))
|
||||
} else {
|
||||
pairs = append(pairs, fmt.Sprintf("%s=%v", attr.Key, attr.Value))
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
if len(pairs) > 0 {
|
||||
msg += " " + strings.Join(pairs, " ")
|
||||
}
|
||||
|
||||
fmt.Fprintln(h.w, msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *consoleHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
|
||||
return &consoleHandler{w: h.w, colorize: h.colorize}
|
||||
}
|
||||
|
||||
func (h *consoleHandler) WithGroup(name string) slog.Handler {
|
||||
return &consoleHandler{w: h.w, colorize: h.colorize}
|
||||
}
|
||||
|
||||
func Init(service string, output io.Writer, level string, colorize bool) {
|
||||
if output == nil {
|
||||
output = os.Stderr
|
||||
}
|
||||
|
||||
var lvl slog.Level
|
||||
switch level {
|
||||
case "debug":
|
||||
lvl = slog.LevelDebug
|
||||
case "warn":
|
||||
lvl = slog.LevelWarn
|
||||
case "error":
|
||||
lvl = slog.LevelError
|
||||
default:
|
||||
lvl = slog.LevelInfo
|
||||
}
|
||||
|
||||
if colorize {
|
||||
L = slog.New(&consoleHandler{w: output, colorize: true}).With("service", service, "level", lvl.String())
|
||||
} else {
|
||||
L = slog.New(slog.NewJSONHandler(output, &slog.HandlerOptions{
|
||||
Level: lvl,
|
||||
AddSource: false,
|
||||
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
|
||||
if a.Key == slog.TimeKey && groups == nil {
|
||||
a.Key = "timestamp"
|
||||
}
|
||||
if a.Key == slog.LevelKey {
|
||||
a.Key = "level"
|
||||
}
|
||||
if a.Key == slog.MessageKey {
|
||||
a.Key = "message"
|
||||
}
|
||||
return a
|
||||
},
|
||||
})).With("service", service)
|
||||
}
|
||||
}
|
||||
|
||||
func Info(msg string, args ...any) {
|
||||
if L != nil {
|
||||
L.Info(msg, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func Warn(msg string, args ...any) {
|
||||
if L != nil {
|
||||
L.Warn(msg, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func Error(msg string, args ...any) {
|
||||
if L != nil {
|
||||
L.Error(msg, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func Debug(msg string, args ...any) {
|
||||
if L != nil {
|
||||
L.Debug(msg, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func With(args ...any) *slog.Logger {
|
||||
if L == nil {
|
||||
return nil
|
||||
}
|
||||
return L.With(args...)
|
||||
}
|
||||
@@ -95,4 +95,6 @@ type Product struct {
|
||||
Category string `gorm:"column:category" json:"category"`
|
||||
|
||||
IsFavorite bool `gorm:"column:is_favorite" json:"is_favorite"`
|
||||
IsOEM bool `gorm:"column:is_oem" json:"is_oem"`
|
||||
IsNew bool `gorm:"column:is_new" json:"is_new"`
|
||||
}
|
||||
|
||||
22
bruno/api_v1/customer/Set is_no_vat.yml
Normal file
22
bruno/api_v1/customer/Set is_no_vat.yml
Normal file
@@ -0,0 +1,22 @@
|
||||
info:
|
||||
name: Set is_no_vat
|
||||
type: http
|
||||
seq: 4
|
||||
|
||||
http:
|
||||
method: PATCH
|
||||
url: "{{bas_url}}/restricted/customer/no-vat"
|
||||
body:
|
||||
type: json
|
||||
data: |-
|
||||
{
|
||||
"customer_id":1,
|
||||
"is_no_vat": false
|
||||
}
|
||||
auth: inherit
|
||||
|
||||
settings:
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
followRedirects: true
|
||||
maxRedirects: 5
|
||||
@@ -5,7 +5,7 @@ info:
|
||||
|
||||
http:
|
||||
method: GET
|
||||
url: "{{bas_url}}/restricted/product/list?p=1&elems=30&reference=~NC100"
|
||||
url: "{{bas_url}}/restricted/product/list?p=1&elems=30&reference=~NC100&is_new_eq=0&is_favorite_eq=false&is_oem_eq=FALSE"
|
||||
params:
|
||||
- name: p
|
||||
value: "1"
|
||||
@@ -27,11 +27,12 @@ http:
|
||||
- name: is_new_eq
|
||||
value: "0"
|
||||
type: query
|
||||
disabled: true
|
||||
- name: is_favorite_eq
|
||||
value: "false"
|
||||
type: query
|
||||
disabled: true
|
||||
- name: is_oem_eq
|
||||
value: "FALSE"
|
||||
type: query
|
||||
body:
|
||||
type: json
|
||||
data: ""
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
info:
|
||||
name: addresses
|
||||
type: folder
|
||||
seq: 10
|
||||
seq: 9
|
||||
|
||||
request:
|
||||
auth: inherit
|
||||
|
||||
@@ -5,15 +5,14 @@ info:
|
||||
|
||||
http:
|
||||
method: POST
|
||||
url: http://localhost:3000/api/v1/public/auth/update-choice?lang_id=0&country_id=1
|
||||
url: http://localhost:3000/api/v1/public/auth/update-choice?lang_id=1&country_id=1
|
||||
params:
|
||||
- name: lang_id
|
||||
value: "0"
|
||||
value: "1"
|
||||
type: query
|
||||
- name: country_id
|
||||
value: "1"
|
||||
type: query
|
||||
auth: inherit
|
||||
|
||||
settings:
|
||||
encodeUrl: true
|
||||
|
||||
@@ -5,7 +5,11 @@ info:
|
||||
|
||||
http:
|
||||
method: GET
|
||||
url: http://localhost:3000/api/v1/restricted/carts/add-new-cart
|
||||
url: http://localhost:3000/api/v1/restricted/carts/add-new-cart?name=carttt
|
||||
params:
|
||||
- name: name
|
||||
value: carttt
|
||||
type: query
|
||||
auth: inherit
|
||||
|
||||
settings:
|
||||
|
||||
@@ -5,7 +5,7 @@ info:
|
||||
|
||||
http:
|
||||
method: GET
|
||||
url: http://localhost:3000/api/v1/restricted/carts/add-product-to-cart?cart_id=1&product_id=51&product_attribute_id=1115&amount=1&set_amount=true
|
||||
url: http://localhost:3000/api/v1/restricted/carts/add-product-to-cart?cart_id=1&product_id=51&product_attribute_id=1115&amount=2&set_amount=true
|
||||
params:
|
||||
- name: cart_id
|
||||
value: "1"
|
||||
@@ -17,7 +17,7 @@ http:
|
||||
value: "1115"
|
||||
type: query
|
||||
- name: amount
|
||||
value: "1"
|
||||
value: "2"
|
||||
type: query
|
||||
- name: set_amount
|
||||
value: "true"
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
info:
|
||||
name: list
|
||||
type: folder
|
||||
seq: 3
|
||||
|
||||
request:
|
||||
auth: inherit
|
||||
@@ -1,24 +0,0 @@
|
||||
info:
|
||||
name: list-products
|
||||
type: http
|
||||
seq: 1
|
||||
|
||||
http:
|
||||
method: GET
|
||||
url: http://localhost:3000/api/v1/restricted/list/list-products?p=1&elems=10&target_user_id=2
|
||||
params:
|
||||
- name: p
|
||||
value: "1"
|
||||
type: query
|
||||
- name: elems
|
||||
value: "10"
|
||||
type: query
|
||||
- name: target_user_id
|
||||
value: "2"
|
||||
type: query
|
||||
|
||||
settings:
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
followRedirects: true
|
||||
maxRedirects: 5
|
||||
@@ -1,21 +0,0 @@
|
||||
info:
|
||||
name: list-users
|
||||
type: http
|
||||
seq: 1
|
||||
|
||||
http:
|
||||
method: GET
|
||||
url: http://localhost:3000/api/v1/restricted/list/list-users?p=1&elems=10
|
||||
params:
|
||||
- name: p
|
||||
value: "1"
|
||||
type: query
|
||||
- name: elems
|
||||
value: "10"
|
||||
type: query
|
||||
|
||||
settings:
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
followRedirects: true
|
||||
maxRedirects: 5
|
||||
@@ -1,7 +1,7 @@
|
||||
info:
|
||||
name: orders
|
||||
type: folder
|
||||
seq: 11
|
||||
seq: 10
|
||||
|
||||
request:
|
||||
auth: inherit
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
info:
|
||||
name: product-translation
|
||||
type: folder
|
||||
seq: 2
|
||||
seq: 3
|
||||
|
||||
request:
|
||||
auth: inherit
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
info:
|
||||
name: storage-old
|
||||
type: folder
|
||||
seq: 1
|
||||
seq: 2
|
||||
|
||||
request:
|
||||
auth: inherit
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
info:
|
||||
name: storage-restricted
|
||||
type: folder
|
||||
seq: 9
|
||||
seq: 8
|
||||
|
||||
request:
|
||||
auth: inherit
|
||||
|
||||
@@ -112,7 +112,8 @@ CREATE TABLE IF NOT EXISTS b2b_customers (
|
||||
country_id INT NULL DEFAULT 2,
|
||||
created_at DATETIME(6) NULL,
|
||||
updated_at DATETIME(6) NULL,
|
||||
deleted_at DATETIME(6) NULL
|
||||
deleted_at DATETIME(6) NULL,
|
||||
is_no_vat TINYINT(1) NOT NULL DEFAULT 0
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_customers_email
|
||||
@@ -161,6 +162,16 @@ CREATE TABLE IF NOT EXISTS b2b_favorites (
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
|
||||
|
||||
|
||||
-- oems
|
||||
CREATE TABLE IF NOT EXISTS b2b_oems (
|
||||
user_id BIGINT UNSIGNED NOT NULL,
|
||||
product_id INT UNSIGNED NOT NULL,
|
||||
PRIMARY KEY (user_id, product_id),
|
||||
CONSTRAINT fk_oems_customer FOREIGN KEY (user_id) REFERENCES b2b_customers(id) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT fk_oems_product FOREIGN KEY (product_id) REFERENCES ps_product(id_product) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
|
||||
|
||||
|
||||
-- refresh_tokens
|
||||
CREATE TABLE IF NOT EXISTS b2b_refresh_tokens (
|
||||
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
@@ -240,6 +251,11 @@ CREATE TABLE IF NOT EXISTS b2b_customer_orders (
|
||||
country_id BIGINT UNSIGNED NOT NULL,
|
||||
address_string TEXT NOT NULL,
|
||||
status VARCHAR(50) NOT NULL,
|
||||
created_at DATETIME NOT NULL,
|
||||
updated_at DATETIME NOT NULL,
|
||||
base_price DECIMAL(10, 2) NOT NULL,
|
||||
tax_incl DECIMAL(10, 2) NOT NULL,
|
||||
tax_excl DECIMAL(10, 2) NOT NULL,
|
||||
CONSTRAINT fk_customer_orders_customers FOREIGN KEY (user_id) REFERENCES b2b_customers(id) ON DELETE NO ACTION ON UPDATE CASCADE,
|
||||
CONSTRAINT fk_customer_orders_countries FOREIGN KEY (country_id) REFERENCES b2b_countries(id) ON DELETE NO ACTION ON UPDATE CASCADE
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
|
||||
|
||||
@@ -16,6 +16,7 @@ READS SQL DATA
|
||||
BEGIN
|
||||
|
||||
DECLARE v_tax_rate DECIMAL(10,4) DEFAULT 0;
|
||||
DECLARE v_tax_group INT;
|
||||
|
||||
DECLARE v_base_raw DECIMAL(20,6);
|
||||
DECLARE v_base DECIMAL(20,6);
|
||||
@@ -29,45 +30,54 @@ BEGIN
|
||||
|
||||
DECLARE v_has_specific INT DEFAULT 0;
|
||||
|
||||
-- currency
|
||||
DECLARE v_target_currency BIGINT;
|
||||
DECLARE v_target_rate DECIMAL(13,6) DEFAULT 1;
|
||||
DECLARE v_specific_rate DECIMAL(13,6) DEFAULT 1;
|
||||
|
||||
DECLARE v_is_no_vat TINYINT DEFAULT 0;
|
||||
|
||||
SET p_id_product_attribute = NULLIF(p_id_product_attribute, 0);
|
||||
|
||||
-- ================= CUSTOMER VAT =================
|
||||
SELECT COALESCE(c.is_no_vat, 0)
|
||||
INTO v_is_no_vat
|
||||
FROM b2b_customers c
|
||||
WHERE c.id = p_id_customer
|
||||
LIMIT 1;
|
||||
|
||||
-- ================= TAX GROUP =================
|
||||
SELECT ps.id_tax_rules_group
|
||||
INTO v_tax_group
|
||||
FROM ps_product_shop ps
|
||||
WHERE ps.id_product = p_id_product
|
||||
AND ps.id_shop = p_id_shop
|
||||
LIMIT 1;
|
||||
|
||||
-- ================= TAX =================
|
||||
SELECT COALESCE(t.rate, 0)
|
||||
INTO v_tax_rate
|
||||
FROM ps_tax_rule tr
|
||||
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
|
||||
WHERE tr.id_tax_rules_group = v_tax_group
|
||||
AND tr.id_country = c.ps_id_country
|
||||
LIMIT 1;
|
||||
|
||||
-- ================= TARGET CURRENCY =================
|
||||
SELECT c.b2b_id_currency
|
||||
INTO v_target_currency
|
||||
IF v_is_no_vat = 1 THEN
|
||||
SET v_tax_rate = 0;
|
||||
END IF;
|
||||
|
||||
-- ================= CURRENCY =================
|
||||
SELECT c.b2b_id_currency, r.conversion_rate
|
||||
INTO v_target_currency, v_target_rate
|
||||
FROM b2b_countries c
|
||||
LEFT JOIN b2b_currency_rates r
|
||||
ON r.b2b_id_currency = c.b2b_id_currency
|
||||
WHERE c.id = p_id_country
|
||||
LIMIT 1;
|
||||
|
||||
-- latest target rate
|
||||
SELECT r.conversion_rate
|
||||
INTO v_target_rate
|
||||
FROM b2b_currency_rates r
|
||||
WHERE r.b2b_id_currency = v_target_currency
|
||||
ORDER BY r.created_at DESC
|
||||
LIMIT 1;
|
||||
|
||||
-- ================= BASE PRICE (RAW) =================
|
||||
-- ================= BASE PRICE =================
|
||||
SELECT
|
||||
COALESCE(ps.price, p.price) + COALESCE(pas.price, 0)
|
||||
INTO v_base_raw
|
||||
@@ -79,8 +89,8 @@ BEGIN
|
||||
AND pas.id_shop = p_id_shop
|
||||
WHERE p.id_product = p_id_product;
|
||||
|
||||
-- convert base to target currency
|
||||
SET v_base = v_base_raw * v_target_rate;
|
||||
SET v_excl = v_base;
|
||||
|
||||
-- ================= RULE SELECTION =================
|
||||
SELECT
|
||||
@@ -99,71 +109,67 @@ BEGIN
|
||||
|
||||
FROM b2b_specific_price bsp
|
||||
|
||||
LEFT JOIN b2b_specific_price_product spp
|
||||
ON spp.b2b_specific_price_id = bsp.id
|
||||
AND spp.id_product = p_id_product
|
||||
|
||||
LEFT JOIN b2b_specific_price_product_attribute spa
|
||||
ON spa.b2b_specific_price_id = bsp.id
|
||||
AND spa.id_product_attribute = p_id_product_attribute
|
||||
|
||||
LEFT JOIN b2b_specific_price_customer spc
|
||||
ON spc.b2b_specific_price_id = bsp.id
|
||||
AND spc.b2b_id_customer = p_id_customer
|
||||
|
||||
LEFT JOIN b2b_specific_price_country spco
|
||||
ON spco.b2b_specific_price_id = bsp.id
|
||||
AND spco.b2b_id_country = p_id_country
|
||||
|
||||
LEFT JOIN b2b_specific_price_category spcat
|
||||
ON spcat.b2b_specific_price_id = bsp.id
|
||||
|
||||
LEFT JOIN ps_category_product cp
|
||||
ON cp.id_category = spcat.id_category
|
||||
AND cp.id_product = p_id_product
|
||||
|
||||
WHERE bsp.is_active = 1
|
||||
AND bsp.from_quantity <= p_quantity
|
||||
|
||||
-- intersection rules (unchanged)
|
||||
AND (
|
||||
NOT EXISTS (SELECT 1 FROM b2b_specific_price_product x WHERE x.b2b_specific_price_id = bsp.id)
|
||||
OR EXISTS (SELECT 1 FROM b2b_specific_price_product x WHERE x.b2b_specific_price_id = bsp.id AND x.id_product = p_id_product)
|
||||
)
|
||||
AND (spp.id_product IS NOT NULL OR NOT EXISTS (
|
||||
SELECT 1 FROM b2b_specific_price_product x WHERE x.b2b_specific_price_id = bsp.id
|
||||
))
|
||||
|
||||
AND (
|
||||
NOT EXISTS (SELECT 1 FROM b2b_specific_price_product_attribute x WHERE x.b2b_specific_price_id = bsp.id)
|
||||
OR EXISTS (SELECT 1 FROM b2b_specific_price_product_attribute x WHERE x.b2b_specific_price_id = bsp.id AND x.id_product_attribute = p_id_product_attribute)
|
||||
)
|
||||
AND (spa.id_product_attribute IS NOT NULL OR NOT EXISTS (
|
||||
SELECT 1 FROM b2b_specific_price_product_attribute x WHERE x.b2b_specific_price_id = bsp.id
|
||||
))
|
||||
|
||||
AND (
|
||||
NOT EXISTS (SELECT 1 FROM b2b_specific_price_category x WHERE x.b2b_specific_price_id = bsp.id)
|
||||
OR EXISTS (
|
||||
SELECT 1 FROM b2b_specific_price_category x
|
||||
JOIN ps_category_product cp ON cp.id_category = x.id_category
|
||||
WHERE x.b2b_specific_price_id = bsp.id AND cp.id_product = p_id_product
|
||||
)
|
||||
)
|
||||
AND (spc.b2b_id_customer IS NOT NULL OR NOT EXISTS (
|
||||
SELECT 1 FROM b2b_specific_price_customer x WHERE x.b2b_specific_price_id = bsp.id
|
||||
))
|
||||
|
||||
AND (
|
||||
NOT EXISTS (SELECT 1 FROM b2b_specific_price_customer x WHERE x.b2b_specific_price_id = bsp.id)
|
||||
OR EXISTS (SELECT 1 FROM b2b_specific_price_customer x WHERE x.b2b_specific_price_id = bsp.id AND x.b2b_id_customer = p_id_customer)
|
||||
)
|
||||
AND (spco.b2b_id_country IS NOT NULL OR NOT EXISTS (
|
||||
SELECT 1 FROM b2b_specific_price_country x WHERE x.b2b_specific_price_id = bsp.id
|
||||
))
|
||||
|
||||
AND (
|
||||
NOT EXISTS (SELECT 1 FROM b2b_specific_price_country x WHERE x.b2b_specific_price_id = bsp.id)
|
||||
OR EXISTS (SELECT 1 FROM b2b_specific_price_country x WHERE x.b2b_specific_price_id = bsp.id AND x.b2b_id_country = p_id_country)
|
||||
)
|
||||
AND (cp.id_product IS NOT NULL OR NOT EXISTS (
|
||||
SELECT 1 FROM b2b_specific_price_category x WHERE x.b2b_specific_price_id = bsp.id
|
||||
))
|
||||
|
||||
ORDER BY
|
||||
-- customer wins
|
||||
(EXISTS (SELECT 1 FROM b2b_specific_price_customer x WHERE x.b2b_specific_price_id = bsp.id AND x.b2b_id_customer = p_id_customer)) DESC,
|
||||
|
||||
-- attribute
|
||||
(EXISTS (SELECT 1 FROM b2b_specific_price_product_attribute x WHERE x.b2b_specific_price_id = bsp.id AND x.id_product_attribute = p_id_product_attribute)) DESC,
|
||||
|
||||
-- product
|
||||
(EXISTS (SELECT 1 FROM b2b_specific_price_product x WHERE x.b2b_specific_price_id = bsp.id AND x.id_product = p_id_product)) DESC,
|
||||
|
||||
-- category
|
||||
(EXISTS (
|
||||
SELECT 1 FROM b2b_specific_price_category x
|
||||
JOIN ps_category_product cp ON cp.id_category = x.id_category
|
||||
WHERE x.b2b_specific_price_id = bsp.id AND cp.id_product = p_id_product
|
||||
)) DESC,
|
||||
|
||||
-- country
|
||||
(EXISTS (SELECT 1 FROM b2b_specific_price_country x WHERE x.b2b_specific_price_id = bsp.id AND x.b2b_id_country = p_id_country)) DESC,
|
||||
|
||||
(spc.b2b_id_customer IS NOT NULL) DESC,
|
||||
(spa.id_product_attribute IS NOT NULL) DESC,
|
||||
(spp.id_product IS NOT NULL) DESC,
|
||||
(cp.id_product IS NOT NULL) DESC,
|
||||
(spco.b2b_id_country IS NOT NULL) DESC,
|
||||
bsp.id DESC
|
||||
|
||||
LIMIT 1;
|
||||
|
||||
-- ================= APPLY =================
|
||||
SET v_excl = v_base;
|
||||
|
||||
IF v_has_specific = 1 THEN
|
||||
|
||||
IF v_reduction_type = 'amount' THEN
|
||||
|
||||
-- convert specific price currency if needed
|
||||
IF v_specific_currency_id IS NOT NULL AND v_specific_currency_id != v_target_currency THEN
|
||||
|
||||
SELECT r.conversion_rate
|
||||
@@ -173,7 +179,6 @@ BEGIN
|
||||
ORDER BY r.created_at DESC
|
||||
LIMIT 1;
|
||||
|
||||
-- normalize → then convert to target
|
||||
SET v_excl = (v_fixed_price / v_specific_rate) * v_target_rate;
|
||||
|
||||
ELSE
|
||||
@@ -379,10 +384,19 @@ BEGIN
|
||||
m.name AS manufacturer,
|
||||
cl.name AS category,
|
||||
|
||||
p.is_oem,
|
||||
EXISTS(
|
||||
SELECT 1 FROM b2b_favorites f
|
||||
WHERE f.user_id = p_id_customer AND f.product_id = p_id_product
|
||||
) AS is_favorite
|
||||
) AS is_favorite,
|
||||
CASE
|
||||
WHEN ps.date_add >= DATE_SUB(
|
||||
NOW(),
|
||||
INTERVAL COALESCE(CAST(ps_configuration.value AS SIGNED), 20) DAY
|
||||
) AND ps.active = 1
|
||||
THEN 1
|
||||
ELSE 0
|
||||
END AS is_new
|
||||
|
||||
|
||||
|
||||
@@ -400,6 +414,8 @@ BEGIN
|
||||
AND cl.id_shop = p_id_shop
|
||||
LEFT JOIN ps_manufacturer m
|
||||
ON m.id_manufacturer = p.id_manufacturer
|
||||
LEFT JOIN ps_configuration
|
||||
ON ps_configuration.name = 'PS_NB_DAYS_NEW_PRODUCT'
|
||||
|
||||
WHERE p.id_product = p_id_product
|
||||
LIMIT 1;
|
||||
|
||||
Reference in New Issue
Block a user