Compare commits
87 Commits
7eee0bd032
...
is_oem
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
754bf2fe01 | ||
|
|
2ca07f03ce | ||
| 84b4c70ffb | |||
|
|
2fd9472db1 | ||
| 66df535317 | |||
|
|
e0a86febc4 | ||
|
|
40154ec861 | ||
|
|
bb507036db | ||
| 80a1314dc0 | |||
|
|
100a9f57d4 | ||
| 773e7d3c20 | |||
| 03a0e5ea64 | |||
| ce8c19f715 | |||
| 4edcb0a852 | |||
| a4120dafa2 | |||
| 5e1a8e898c | |||
|
|
c610ce38cc | ||
| 8e3e41d6fe | |||
| b33da9d072 | |||
|
|
604247b7c8 | ||
| e5988a85f3 | |||
|
|
0cb5cc47bb | ||
|
|
1efc5417be | ||
|
|
a0c3dd8ec8 | ||
| ab783b599d | |||
| d173af29fe | |||
| f14d60d67b | |||
| 967b101f9b | |||
| 97ca510b99 | |||
| 26cbdeec0a | |||
|
|
ce4cadaa16 | ||
|
|
7f05d39b38 | ||
| 83b7cd49dd | |||
|
|
88255776f3 | ||
| 38cb07f3d4 | |||
|
|
1f6d5ecb72 | ||
| 2e61cde742 | |||
|
|
d4d55e2757 | ||
| 54608410ea | |||
|
|
80d26bba12 | ||
|
|
33e9d016e9 | ||
|
|
a03a2b461f | ||
|
|
134bc4ea53 | ||
|
|
8595969c6e | ||
|
|
a6aa06faa0 | ||
|
|
4f4b32b131 | ||
|
|
dfdf8b4db9 | ||
|
|
438a13c04c | ||
| 8024d9f739 | |||
|
|
c5832c0cf5 | ||
|
|
61ccd32c4a | ||
|
|
f7f56c2928 | ||
|
|
0a5ce5d9c2 | ||
|
|
f1f5daa82b | ||
|
|
393de36cb2 | ||
| 9fb8e034fc | |||
|
|
1083ab7a61 | ||
| 75af44b0df | |||
| 75997ab15b | |||
|
|
569a805a13 | ||
|
|
578d8c6cac | ||
|
|
cbd0baaa50 | ||
| 92ba9c5f07 | |||
| a121ddc246 | |||
| d56650ae5d | |||
| 1a6311dc3d | |||
| 2e645f3368 | |||
| de3f2d1777 | |||
| 9187297367 | |||
| 813d1f4879 | |||
| c5cc4f7a48 | |||
| 76ca2a2eed | |||
| 84388792f0 | |||
|
|
7264a11ba6 | ||
| 61dc240c38 | |||
| 701004d005 | |||
| c31964c41b | |||
| 0ed9d792b6 | |||
| 6428ddb527 | |||
| 05bfa6e8b8 | |||
| f4ad8e02b4 | |||
| bd97ed1a3b | |||
| df14eb5ae4 | |||
| f5d524d45b | |||
| 78bdac8ff0 | |||
| 2c128a4b36 | |||
| dd806bbb1e |
14
.vscode/launch.json
vendored
Normal file
14
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Launch Package",
|
||||||
|
"type": "go",
|
||||||
|
"request": "launch",
|
||||||
|
"mode": "auto",
|
||||||
|
"program": "./app/cmd/main.go",
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"envFile": "${workspaceFolder}/.env"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"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/delivery/middleware/perms"
|
||||||
"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/authService"
|
"git.ma-al.com/goc_daniel/b2b/app/service/authService"
|
||||||
constdata "git.ma-al.com/goc_daniel/b2b/app/utils/const_data"
|
constdata "git.ma-al.com/goc_daniel/b2b/app/utils/const_data"
|
||||||
@@ -16,9 +17,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// AuthMiddleware creates authentication middleware
|
// AuthMiddleware creates authentication middleware
|
||||||
func AuthMiddleware() fiber.Handler {
|
func Authenticate() fiber.Handler {
|
||||||
authService := authService.NewAuthService()
|
authService := authService.NewAuthService()
|
||||||
|
|
||||||
return func(c fiber.Ctx) error {
|
return func(c fiber.Ctx) error {
|
||||||
// Get token from Authorization header
|
// Get token from Authorization header
|
||||||
authHeader := c.Get("Authorization")
|
authHeader := c.Get("Authorization")
|
||||||
@@ -26,17 +26,13 @@ func AuthMiddleware() fiber.Handler {
|
|||||||
// Try to get from cookie
|
// Try to get from cookie
|
||||||
authHeader = c.Cookies("access_token")
|
authHeader = c.Cookies("access_token")
|
||||||
if authHeader == "" {
|
if authHeader == "" {
|
||||||
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
|
return c.Next()
|
||||||
"error": "authorization token required",
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Extract token from "Bearer <token>"
|
// Extract token from "Bearer <token>"
|
||||||
parts := strings.Split(authHeader, " ")
|
parts := strings.Split(authHeader, " ")
|
||||||
if len(parts) != 2 || parts[0] != "Bearer" {
|
if len(parts) != 2 || parts[0] != "Bearer" {
|
||||||
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
|
return c.Next()
|
||||||
"error": "invalid authorization header format",
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
authHeader = parts[1]
|
authHeader = parts[1]
|
||||||
}
|
}
|
||||||
@@ -44,24 +40,18 @@ func AuthMiddleware() fiber.Handler {
|
|||||||
// Validate token
|
// Validate token
|
||||||
claims, err := authService.ValidateToken(authHeader)
|
claims, err := authService.ValidateToken(authHeader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
|
return c.Next()
|
||||||
"error": "invalid or expired token",
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get user from database
|
// Get user from database
|
||||||
user, err := authService.GetUserByID(claims.UserID)
|
user, err := authService.GetUserByID(claims.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
|
return c.Next()
|
||||||
"error": "user not found",
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if user is active
|
// Check if user is active
|
||||||
if !user.IsActive {
|
if !user.IsActive {
|
||||||
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{
|
return c.Next()
|
||||||
"error": "user account is inactive",
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create locale. LangID is overwritten by auth Token
|
// Create locale. LangID is overwritten by auth Token
|
||||||
@@ -79,10 +69,8 @@ func AuthMiddleware() fiber.Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// We now populate the target user
|
// We now populate the target user
|
||||||
if user.Role != model.RoleAdmin {
|
if !userLocale.OriginalUser.HasPermission(perms.Teleport) {
|
||||||
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{
|
return c.Next()
|
||||||
"error": "admin access required",
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
targetUserID, err := strconv.Atoi(targetUserIDAttribute)
|
targetUserID, err := strconv.Atoi(targetUserIDAttribute)
|
||||||
@@ -115,22 +103,14 @@ func AuthMiddleware() fiber.Handler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RequireAdmin creates admin-only middleware
|
func Authorize() fiber.Handler {
|
||||||
func RequireAdmin() fiber.Handler {
|
|
||||||
return func(c fiber.Ctx) error {
|
return func(c fiber.Ctx) error {
|
||||||
originalUserRole, ok := localeExtractor.GetOriginalUserRole(c)
|
_, ok := localeExtractor.GetUserID(c)
|
||||||
if !ok {
|
if !ok {
|
||||||
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
|
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
|
||||||
"error": "not authenticated",
|
"error": "not authenticated",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if originalUserRole != model.RoleAdmin {
|
|
||||||
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{
|
|
||||||
"error": "admin access required",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.Next()
|
return c.Next()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
28
app/delivery/middleware/permissions.go
Normal file
28
app/delivery/middleware/permissions.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/delivery/middleware/perms"
|
||||||
|
"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/response"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors"
|
||||||
|
"github.com/gofiber/fiber/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Require(p perms.Permission) fiber.Handler {
|
||||||
|
return func(c fiber.Ctx) error {
|
||||||
|
user, ok := localeExtractor.GetCustomer(c)
|
||||||
|
if !ok {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrInvalidBody)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody)))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, perm := range user.Role.Permissions {
|
||||||
|
if perm.Name == p {
|
||||||
|
return c.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrForbidden)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrForbidden)))
|
||||||
|
}
|
||||||
|
}
|
||||||
18
app/delivery/middleware/perms/permissions.go
Normal file
18
app/delivery/middleware/perms/permissions.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package perms
|
||||||
|
|
||||||
|
type Permission string
|
||||||
|
|
||||||
|
const (
|
||||||
|
UserReadAny Permission = "user.read.any"
|
||||||
|
UserWriteAny Permission = "user.write.any"
|
||||||
|
UserDeleteAny Permission = "user.delete.any"
|
||||||
|
CurrencyWrite Permission = "currency.write"
|
||||||
|
SpecificPriceManage Permission = "specific_price.manage"
|
||||||
|
WebdavCreateToken Permission = "webdav.create_token"
|
||||||
|
ProductTranslationSave Permission = "product_translation.save"
|
||||||
|
ProductTranslationTranslate Permission = "product_translation.translate"
|
||||||
|
SearchCreateIndex Permission = "search.create_index"
|
||||||
|
OrdersViewAll Permission = "orders.view_all"
|
||||||
|
OrdersModifyAll Permission = "orders.modify_all"
|
||||||
|
Teleport Permission = "teleport"
|
||||||
|
)
|
||||||
@@ -49,7 +49,7 @@ func AuthHandlerRoutes(r fiber.Router) fiber.Router {
|
|||||||
r.Get("/google", handler.GoogleLogin)
|
r.Get("/google", handler.GoogleLogin)
|
||||||
r.Get("/google/callback", handler.GoogleCallback)
|
r.Get("/google/callback", handler.GoogleCallback)
|
||||||
|
|
||||||
authProtected := r.Group("", middleware.AuthMiddleware())
|
authProtected := r.Group("", middleware.Authorize())
|
||||||
authProtected.Get("/me", handler.Me)
|
authProtected.Get("/me", handler.Me)
|
||||||
authProtected.Post("/update-choice", handler.UpdateJWTToken)
|
authProtected.Post("/update-choice", handler.UpdateJWTToken)
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package public
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"git.ma-al.com/goc_daniel/b2b/app/service/menuService"
|
"git.ma-al.com/goc_daniel/b2b/app/service/menuService"
|
||||||
|
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"
|
||||||
@@ -31,12 +32,21 @@ func RoutingHandlerRoutes(r fiber.Router) fiber.Router {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *RoutingHandler) GetRouting(c fiber.Ctx) error {
|
func (h *RoutingHandler) GetRouting(c fiber.Ctx) error {
|
||||||
lang_id, ok := localeExtractor.GetLangID(c)
|
langId, ok := localeExtractor.GetLangID(c)
|
||||||
if !ok {
|
if !ok {
|
||||||
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrInvalidBody)).
|
||||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody)))
|
||||||
}
|
}
|
||||||
menu, err := h.menuService.GetRoutes(lang_id)
|
|
||||||
|
var roleId uint
|
||||||
|
customer, ok := localeExtractor.GetCustomer(c)
|
||||||
|
if !ok {
|
||||||
|
roleId = constdata.UNLOGGED_USER_ROLE_ID
|
||||||
|
} else {
|
||||||
|
roleId = customer.RoleID
|
||||||
|
}
|
||||||
|
|
||||||
|
menu, err := h.menuService.GetRoutes(langId, roleId)
|
||||||
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)))
|
||||||
|
|||||||
157
app/delivery/web/api/restricted/addresses.go
Normal file
157
app/delivery/web/api/restricted/addresses.go
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
package restricted
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"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/nullable"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/utils/response"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors"
|
||||||
|
"github.com/gofiber/fiber/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AddressesHandler struct {
|
||||||
|
addressesService *addressesService.AddressesService
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAddressesHandler() *AddressesHandler {
|
||||||
|
addressesService := addressesService.New()
|
||||||
|
return &AddressesHandler{
|
||||||
|
addressesService: addressesService,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddressesHandlerRoutes(r fiber.Router) fiber.Router {
|
||||||
|
handler := NewAddressesHandler()
|
||||||
|
|
||||||
|
r.Get("/get-template", handler.GetTemplate)
|
||||||
|
r.Post("/add-new-address", handler.AddNewAddress)
|
||||||
|
r.Post("/modify-address", handler.ModifyAddress)
|
||||||
|
r.Get("/retrieve-addresses", handler.RetrieveAddressesInfo)
|
||||||
|
r.Delete("/delete-address", handler.DeleteAddress)
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *AddressesHandler) GetTemplate(c fiber.Ctx) error {
|
||||||
|
country_id_attribute := c.Query("country_id")
|
||||||
|
country_id, err := strconv.Atoi(country_id_attribute)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||||
|
}
|
||||||
|
|
||||||
|
template, err := h.addressesService.GetTemplate(uint(country_id))
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(response.Make(&template, 0, i18n.T_(c, response.Message_OK)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *AddressesHandler) AddNewAddress(c fiber.Ctx) error {
|
||||||
|
userID, ok := localeExtractor.GetUserID(c)
|
||||||
|
if !ok {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrInvalidBody)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody)))
|
||||||
|
}
|
||||||
|
|
||||||
|
address_info := string(c.Body())
|
||||||
|
if address_info == "" {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrInvalidBody)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody)))
|
||||||
|
}
|
||||||
|
|
||||||
|
country_id_attribute := c.Query("country_id")
|
||||||
|
country_id, err := strconv.Atoi(country_id_attribute)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||||
|
}
|
||||||
|
|
||||||
|
err = h.addressesService.AddNewAddress(userID, address_info, uint(country_id))
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(response.Make(nullable.GetNil(""), 0, i18n.T_(c, response.Message_OK)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *AddressesHandler) ModifyAddress(c fiber.Ctx) error {
|
||||||
|
userID, ok := localeExtractor.GetUserID(c)
|
||||||
|
if !ok {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrInvalidBody)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody)))
|
||||||
|
}
|
||||||
|
|
||||||
|
address_id_attribute := c.Query("address_id")
|
||||||
|
address_id, err := strconv.Atoi(address_id_attribute)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||||
|
}
|
||||||
|
|
||||||
|
address_info := string(c.Body())
|
||||||
|
if address_info == "" {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrInvalidBody)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody)))
|
||||||
|
}
|
||||||
|
|
||||||
|
country_id_attribute := c.Query("country_id")
|
||||||
|
country_id, err := strconv.Atoi(country_id_attribute)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||||
|
}
|
||||||
|
|
||||||
|
err = h.addressesService.ModifyAddress(userID, uint(address_id), address_info, uint(country_id))
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(response.Make(nullable.GetNil(""), 0, i18n.T_(c, response.Message_OK)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *AddressesHandler) RetrieveAddressesInfo(c fiber.Ctx) error {
|
||||||
|
userID, ok := localeExtractor.GetUserID(c)
|
||||||
|
if !ok {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrInvalidBody)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody)))
|
||||||
|
}
|
||||||
|
|
||||||
|
addresses, err := h.addressesService.RetrieveAddresses(userID)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(response.Make(addresses, 0, i18n.T_(c, response.Message_OK)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *AddressesHandler) DeleteAddress(c fiber.Ctx) error {
|
||||||
|
userID, ok := localeExtractor.GetUserID(c)
|
||||||
|
if !ok {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrInvalidBody)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody)))
|
||||||
|
}
|
||||||
|
|
||||||
|
address_id_attribute := c.Query("address_id")
|
||||||
|
address_id, err := strconv.Atoi(address_id_attribute)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||||
|
}
|
||||||
|
|
||||||
|
err = h.addressesService.DeleteAddress(userID, uint(address_id))
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(response.Make(nullable.GetNil(""), 0, i18n.T_(c, response.Message_OK)))
|
||||||
|
}
|
||||||
@@ -29,10 +29,12 @@ func CartsHandlerRoutes(r fiber.Router) fiber.Router {
|
|||||||
handler := NewCartsHandler()
|
handler := NewCartsHandler()
|
||||||
|
|
||||||
r.Get("/add-new-cart", handler.AddNewCart)
|
r.Get("/add-new-cart", handler.AddNewCart)
|
||||||
|
r.Delete("/remove-cart", handler.RemoveCart)
|
||||||
r.Get("/change-cart-name", handler.ChangeCartName)
|
r.Get("/change-cart-name", handler.ChangeCartName)
|
||||||
r.Get("/retrieve-carts-info", handler.RetrieveCartsInfo)
|
r.Get("/retrieve-carts-info", handler.RetrieveCartsInfo)
|
||||||
r.Get("/retrieve-cart", handler.RetrieveCart)
|
r.Get("/retrieve-cart", handler.RetrieveCart)
|
||||||
r.Get("/add-product-to-cart", handler.AddProduct)
|
r.Get("/add-product-to-cart", handler.AddProduct)
|
||||||
|
r.Delete("/remove-product-from-cart", handler.RemoveProduct)
|
||||||
|
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
@@ -44,7 +46,8 @@ func (h *CartsHandler) AddNewCart(c fiber.Ctx) error {
|
|||||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody)))
|
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 {
|
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)))
|
||||||
@@ -53,6 +56,29 @@ func (h *CartsHandler) AddNewCart(c fiber.Ctx) error {
|
|||||||
return c.JSON(response.Make(&new_cart, 0, i18n.T_(c, response.Message_OK)))
|
return c.JSON(response.Make(&new_cart, 0, i18n.T_(c, response.Message_OK)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *CartsHandler) RemoveCart(c fiber.Ctx) error {
|
||||||
|
userID, ok := localeExtractor.GetUserID(c)
|
||||||
|
if !ok {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrInvalidBody)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody)))
|
||||||
|
}
|
||||||
|
|
||||||
|
cart_id_attribute := c.Query("cart_id")
|
||||||
|
cart_id, err := strconv.Atoi(cart_id_attribute)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||||
|
}
|
||||||
|
|
||||||
|
err = h.cartsService.RemoveCart(userID, uint(cart_id))
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(response.Make(nullable.GetNil(""), 0, i18n.T_(c, response.Message_OK)))
|
||||||
|
}
|
||||||
|
|
||||||
func (h *CartsHandler) ChangeCartName(c fiber.Ctx) error {
|
func (h *CartsHandler) ChangeCartName(c fiber.Ctx) error {
|
||||||
userID, ok := localeExtractor.GetUserID(c)
|
userID, ok := localeExtractor.GetUserID(c)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -117,6 +143,7 @@ func (h *CartsHandler) RetrieveCart(c fiber.Ctx) error {
|
|||||||
return c.JSON(response.Make(cart, 0, i18n.T_(c, response.Message_OK)))
|
return c.JSON(response.Make(cart, 0, i18n.T_(c, response.Message_OK)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// adds or sets given amount of products to the cart
|
||||||
func (h *CartsHandler) AddProduct(c fiber.Ctx) error {
|
func (h *CartsHandler) AddProduct(c fiber.Ctx) error {
|
||||||
userID, ok := localeExtractor.GetUserID(c)
|
userID, ok := localeExtractor.GetUserID(c)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -159,7 +186,59 @@ func (h *CartsHandler) AddProduct(c fiber.Ctx) error {
|
|||||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||||
}
|
}
|
||||||
|
|
||||||
err = h.cartsService.AddProduct(userID, uint(cart_id), uint(product_id), product_attribute_id, uint(amount))
|
set_amount_attribute := c.Query("set_amount")
|
||||||
|
set_amount, err := strconv.ParseBool(set_amount_attribute)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||||
|
}
|
||||||
|
|
||||||
|
err = h.cartsService.AddProduct(userID, uint(cart_id), uint(product_id), product_attribute_id, amount, set_amount)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(response.Make(nullable.GetNil(""), 0, i18n.T_(c, response.Message_OK)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// removes product from the cart.
|
||||||
|
func (h *CartsHandler) RemoveProduct(c fiber.Ctx) error {
|
||||||
|
userID, ok := localeExtractor.GetUserID(c)
|
||||||
|
if !ok {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrInvalidBody)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody)))
|
||||||
|
}
|
||||||
|
|
||||||
|
cart_id_attribute := c.Query("cart_id")
|
||||||
|
cart_id, err := strconv.Atoi(cart_id_attribute)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||||
|
}
|
||||||
|
|
||||||
|
product_id_attribute := c.Query("product_id")
|
||||||
|
product_id, err := strconv.Atoi(product_id_attribute)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||||
|
}
|
||||||
|
|
||||||
|
product_attribute_id_attribute := c.Query("product_attribute_id")
|
||||||
|
var product_attribute_id *uint
|
||||||
|
if product_attribute_id_attribute == "" {
|
||||||
|
product_attribute_id = nil
|
||||||
|
} else {
|
||||||
|
val, err := strconv.Atoi(product_attribute_id_attribute)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||||
|
}
|
||||||
|
uval := uint(val)
|
||||||
|
product_attribute_id = &uval
|
||||||
|
}
|
||||||
|
|
||||||
|
err = h.cartsService.RemoveProduct(userID, uint(cart_id), uint(product_id), product_attribute_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)))
|
||||||
|
|||||||
70
app/delivery/web/api/restricted/currency.go
Normal file
70
app/delivery/web/api/restricted/currency.go
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
package restricted
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/config"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/delivery/middleware"
|
||||||
|
"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/service/currencyService"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/utils/i18n"
|
||||||
|
"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"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CurrencyHandler struct {
|
||||||
|
CurrencyService *currencyService.CurrencyService
|
||||||
|
config *config.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCurrencyHandler() *CurrencyHandler {
|
||||||
|
currencyService := currencyService.New()
|
||||||
|
return &CurrencyHandler{
|
||||||
|
CurrencyService: currencyService,
|
||||||
|
config: config.Get(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func CurrencyHandlerRoutes(r fiber.Router) fiber.Router {
|
||||||
|
handler := NewCurrencyHandler()
|
||||||
|
|
||||||
|
r.Post("/currency-rate", middleware.Require(perms.CurrencyWrite), handler.PostCurrencyRate)
|
||||||
|
r.Get("/currency-rate/:id", handler.GetCurrencyRate)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *CurrencyHandler) PostCurrencyRate(c fiber.Ctx) error {
|
||||||
|
var currencyRate model.CurrencyRate
|
||||||
|
if err := c.Bind().Body(¤cyRate); err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrJSONBody)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrJSONBody)))
|
||||||
|
}
|
||||||
|
|
||||||
|
err := h.CurrencyService.CreateCurrencyRate(¤cyRate)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(response.Make(nullable.GetNil(""), 1, i18n.T_(c, response.Message_OK)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *CurrencyHandler) GetCurrencyRate(c fiber.Ctx) error {
|
||||||
|
idStr := c.Params("id")
|
||||||
|
id, err := strconv.Atoi(idStr)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(err)).JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
currency, err := h.CurrencyService.GetCurrency(uint(id))
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(err)).JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(response.Make(currency, 0, i18n.T_(c, response.Message_OK)))
|
||||||
|
}
|
||||||
102
app/delivery/web/api/restricted/customer.go
Normal file
102
app/delivery/web/api/restricted/customer.go
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
package restricted
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/delivery/middleware"
|
||||||
|
"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/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/nullable"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/utils/query/query_params"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/utils/response"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors"
|
||||||
|
"github.com/gofiber/fiber/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
type customerHandler struct {
|
||||||
|
service *customerService.CustomerService
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCustomerHandler() *customerHandler {
|
||||||
|
customerService := customerService.New()
|
||||||
|
return &customerHandler{
|
||||||
|
service: customerService,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func CustomerHandlerRoutes(r fiber.Router) fiber.Router {
|
||||||
|
handler := NewCustomerHandler()
|
||||||
|
|
||||||
|
r.Get("", handler.customerData)
|
||||||
|
r.Get("/list", middleware.Require(perms.UserReadAny), handler.listCustomers)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *customerHandler) customerData(fc fiber.Ctx) error {
|
||||||
|
var customerId uint
|
||||||
|
|
||||||
|
user, ok := localeExtractor.GetCustomer(fc)
|
||||||
|
if !ok || user == nil {
|
||||||
|
return fc.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(fc, responseErrors.ErrBadAttribute)))
|
||||||
|
}
|
||||||
|
|
||||||
|
customerIdStr := fc.Query("id")
|
||||||
|
if customerIdStr != "" {
|
||||||
|
id, err := strconv.ParseUint(customerIdStr, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.ErrBadRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.ID != uint(id) && !user.HasPermission(perms.UserReadAny) {
|
||||||
|
return fc.Status(fiber.StatusForbidden).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(fc, responseErrors.ErrForbidden)))
|
||||||
|
}
|
||||||
|
|
||||||
|
customerId = uint(id)
|
||||||
|
} else {
|
||||||
|
customerId = user.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
customer, err := h.service.GetById(customerId)
|
||||||
|
if err != nil {
|
||||||
|
return fc.Status(responseErrors.GetErrorStatus(err)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(fc, err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return fc.JSON(response.Make(&customer, 0, i18n.T_(fc, response.Message_OK)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *customerHandler) listCustomers(fc fiber.Ctx) error {
|
||||||
|
user, ok := localeExtractor.GetCustomer(fc)
|
||||||
|
if !ok || user == nil {
|
||||||
|
return fc.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(fc, responseErrors.ErrBadAttribute)))
|
||||||
|
}
|
||||||
|
|
||||||
|
p, filt, err := query_params.ParseFilters[model.Customer](fc, columnMappingListUsers)
|
||||||
|
if err != nil {
|
||||||
|
return fc.Status(responseErrors.GetErrorStatus(err)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(fc, err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
search := fc.Query("search")
|
||||||
|
|
||||||
|
customer, err := h.service.Find(user.LangID, p, filt, search)
|
||||||
|
if err != nil {
|
||||||
|
return fc.Status(responseErrors.GetErrorStatus(err)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(fc, err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return fc.JSON(response.Make(&customer, 0, i18n.T_(fc, response.Message_OK)))
|
||||||
|
}
|
||||||
|
|
||||||
|
var columnMappingListUsers map[string]string = map[string]string{
|
||||||
|
"user_id": "users.id",
|
||||||
|
"email": "users.email",
|
||||||
|
"first_name": "users.first_name",
|
||||||
|
"last_name": "users.last_name",
|
||||||
|
}
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
package restricted
|
|
||||||
|
|
||||||
import (
|
|
||||||
"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/service/listService"
|
|
||||||
"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/nullable"
|
|
||||||
"git.ma-al.com/goc_daniel/b2b/app/utils/query/query_params"
|
|
||||||
"git.ma-al.com/goc_daniel/b2b/app/utils/response"
|
|
||||||
"git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors"
|
|
||||||
"github.com/gofiber/fiber/v3"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ListHandler handles endpoints that list various things (e.g. products or users)
|
|
||||||
type ListHandler struct {
|
|
||||||
listService *listService.ListService
|
|
||||||
config *config.Config
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewListHandler creates a new ListHandler instance
|
|
||||||
func NewListHandler() *ListHandler {
|
|
||||||
listService := listService.New()
|
|
||||||
return &ListHandler{
|
|
||||||
listService: listService,
|
|
||||||
config: config.Get(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ListHandlerRoutes(r fiber.Router) fiber.Router {
|
|
||||||
handler := NewListHandler()
|
|
||||||
|
|
||||||
r.Get("/list-products", handler.ListProducts)
|
|
||||||
r.Get("/list-users", handler.ListUsers)
|
|
||||||
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *ListHandler) ListProducts(c fiber.Ctx) error {
|
|
||||||
paging, filters, err := query_params.ParseFilters[model.Product](c, columnMappingListProducts)
|
|
||||||
if err != nil {
|
|
||||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
|
||||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
|
||||||
}
|
|
||||||
|
|
||||||
id_lang, ok := localeExtractor.GetLangID(c)
|
|
||||||
if !ok {
|
|
||||||
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
|
||||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
|
||||||
}
|
|
||||||
|
|
||||||
list, err := h.listService.ListProducts(id_lang, paging, filters)
|
|
||||||
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.Items, int(list.Count), i18n.T_(c, response.Message_OK)))
|
|
||||||
}
|
|
||||||
|
|
||||||
var columnMappingListProducts map[string]string = map[string]string{
|
|
||||||
"product_id": "ps.id_product",
|
|
||||||
"name": "pl.name",
|
|
||||||
"reference": "p.reference",
|
|
||||||
"category_name": "cl.name",
|
|
||||||
"category_id": "cp.id_category",
|
|
||||||
"quantity": "sa.quantity",
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *ListHandler) ListUsers(c fiber.Ctx) error {
|
|
||||||
paging, filters, err := query_params.ParseFilters[model.Customer](c, columnMappingListUsers)
|
|
||||||
if err != nil {
|
|
||||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
|
||||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
|
||||||
}
|
|
||||||
|
|
||||||
id_lang, ok := localeExtractor.GetLangID(c)
|
|
||||||
if !ok {
|
|
||||||
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
|
||||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
|
||||||
}
|
|
||||||
|
|
||||||
list, err := h.listService.ListUsers(id_lang, paging, filters)
|
|
||||||
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.Items, int(list.Count), i18n.T_(c, response.Message_OK)))
|
|
||||||
}
|
|
||||||
|
|
||||||
var columnMappingListUsers map[string]string = map[string]string{
|
|
||||||
"user_id": "users.id",
|
|
||||||
"email": "users.email",
|
|
||||||
"first_name": "users.first_name",
|
|
||||||
"second_name": "users.second_name",
|
|
||||||
"role": "users.role",
|
|
||||||
}
|
|
||||||
@@ -87,12 +87,12 @@ func (h *MenuHandler) GetBreadcrumb(c fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *MenuHandler) GetTopMenu(c fiber.Ctx) error {
|
func (h *MenuHandler) GetTopMenu(c fiber.Ctx) error {
|
||||||
lang_id, 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)))
|
||||||
}
|
}
|
||||||
menu, err := h.menuService.GetTopMenu(lang_id)
|
menu, err := h.menuService.GetTopMenu(customer.LangID, customer.RoleID)
|
||||||
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)))
|
||||||
|
|||||||
171
app/delivery/web/api/restricted/orders.go
Normal file
171
app/delivery/web/api/restricted/orders.go
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
package restricted
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/model"
|
||||||
|
"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/nullable"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/utils/query/query_params"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/utils/response"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors"
|
||||||
|
"github.com/gofiber/fiber/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
type OrdersHandler struct {
|
||||||
|
ordersService *orderService.OrderService
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewOrdersHandler() *OrdersHandler {
|
||||||
|
ordersService := orderService.New()
|
||||||
|
return &OrdersHandler{
|
||||||
|
ordersService: ordersService,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func OrdersHandlerRoutes(r fiber.Router) fiber.Router {
|
||||||
|
handler := NewOrdersHandler()
|
||||||
|
|
||||||
|
r.Get("/list", handler.ListOrders)
|
||||||
|
r.Post("/place-new-order", handler.PlaceNewOrder)
|
||||||
|
r.Post("/change-order-address", handler.ChangeOrderAddress)
|
||||||
|
r.Get("/change-order-status", handler.ChangeOrderStatus)
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// when a user (not admin) wants to list orders, we automatically append filter to only view his orders.
|
||||||
|
// we base permissions and user based on target user only.
|
||||||
|
func (h *OrdersHandler) ListOrders(c fiber.Ctx) error {
|
||||||
|
user, ok := localeExtractor.GetCustomer(c)
|
||||||
|
if !ok {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrInvalidBody)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody)))
|
||||||
|
}
|
||||||
|
|
||||||
|
paging, filters, err := query_params.ParseFilters[model.CustomerOrder](c, columnMappingListOrders)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
list, err := h.ordersService.Find(user, paging, filters)
|
||||||
|
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.Items, int(list.Count), i18n.T_(c, response.Message_OK)))
|
||||||
|
}
|
||||||
|
|
||||||
|
var columnMappingListOrders map[string]string = map[string]string{
|
||||||
|
"order_id": "b2b_customer_orders.order_id",
|
||||||
|
"user_id": "b2b_customer_orders.user_id",
|
||||||
|
"name": "b2b_customer_orders.name",
|
||||||
|
"country_id": "b2b_customer_orders.country_id",
|
||||||
|
"status": "b2b_customer_orders.status",
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *OrdersHandler) PlaceNewOrder(c fiber.Ctx) error {
|
||||||
|
userID, ok := localeExtractor.GetUserID(c)
|
||||||
|
if !ok {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrInvalidBody)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody)))
|
||||||
|
}
|
||||||
|
|
||||||
|
cart_id_attribute := c.Query("cart_id")
|
||||||
|
cart_id, err := strconv.Atoi(cart_id_attribute)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||||
|
}
|
||||||
|
|
||||||
|
country_id_attribute := c.Query("country_id")
|
||||||
|
country_id, err := strconv.Atoi(country_id_attribute)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||||
|
}
|
||||||
|
|
||||||
|
address_info := string(c.Body())
|
||||||
|
if address_info == "" {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrInvalidBody)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody)))
|
||||||
|
}
|
||||||
|
|
||||||
|
name := c.Query("name")
|
||||||
|
|
||||||
|
err = h.ordersService.PlaceNewOrder(userID, uint(cart_id), name, uint(country_id), address_info)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(response.Make(nullable.GetNil(""), 0, i18n.T_(c, response.Message_OK)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// we base permissions and user based on target user only.
|
||||||
|
func (h *OrdersHandler) ChangeOrderAddress(c fiber.Ctx) error {
|
||||||
|
user, ok := localeExtractor.GetCustomer(c)
|
||||||
|
if !ok {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrInvalidBody)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody)))
|
||||||
|
}
|
||||||
|
|
||||||
|
order_id_attribute := c.Query("order_id")
|
||||||
|
order_id, err := strconv.Atoi(order_id_attribute)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||||
|
}
|
||||||
|
|
||||||
|
country_id_attribute := c.Query("country_id")
|
||||||
|
country_id, err := strconv.Atoi(country_id_attribute)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||||
|
}
|
||||||
|
|
||||||
|
address_info := string(c.Body())
|
||||||
|
if address_info == "" {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrInvalidBody)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody)))
|
||||||
|
}
|
||||||
|
|
||||||
|
err = h.ordersService.ChangeOrderAddress(user, uint(order_id), uint(country_id), address_info)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(response.Make(nullable.GetNil(""), 0, i18n.T_(c, response.Message_OK)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// we base permissions and user based on target user only.
|
||||||
|
// TODO: well, permissions and all that.
|
||||||
|
func (h *OrdersHandler) ChangeOrderStatus(c fiber.Ctx) error {
|
||||||
|
user, ok := localeExtractor.GetCustomer(c)
|
||||||
|
if !ok {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrInvalidBody)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody)))
|
||||||
|
}
|
||||||
|
|
||||||
|
order_id_attribute := c.Query("order_id")
|
||||||
|
order_id, err := strconv.Atoi(order_id_attribute)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||||
|
}
|
||||||
|
|
||||||
|
status := c.Query("status")
|
||||||
|
|
||||||
|
err = h.ordersService.ChangeOrderStatus(user, uint(order_id), status)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(response.Make(nullable.GetNil(""), 0, i18n.T_(c, response.Message_OK)))
|
||||||
|
}
|
||||||
188
app/delivery/web/api/restricted/product.go
Normal file
188
app/delivery/web/api/restricted/product.go
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
package restricted
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/config"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/model/dbmodel"
|
||||||
|
"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/localeExtractor"
|
||||||
|
"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"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors"
|
||||||
|
"github.com/gofiber/fiber/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ProductsHandler struct {
|
||||||
|
productService *productService.ProductService
|
||||||
|
config *config.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewListProductsHandler creates a new ListProductsHandler instance
|
||||||
|
func NewProductsHandler() *ProductsHandler {
|
||||||
|
productService := productService.New()
|
||||||
|
return &ProductsHandler{
|
||||||
|
productService: productService,
|
||||||
|
config: config.Get(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProductsHandlerRoutes(r fiber.Router) fiber.Router {
|
||||||
|
handler := NewProductsHandler()
|
||||||
|
|
||||||
|
r.Get("/:id/:country_id/:quantity", handler.GetProductJson)
|
||||||
|
r.Get("/list", handler.ListProducts)
|
||||||
|
r.Get("/list-variants/:product_id", handler.ListProductVariants)
|
||||||
|
r.Post("/favorite/:product_id", handler.AddToFavorites)
|
||||||
|
r.Delete("/favorite/:product_id", handler.RemoveFromFavorites)
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *ProductsHandler) GetProductJson(c fiber.Ctx) error {
|
||||||
|
idStr := c.Params("id")
|
||||||
|
|
||||||
|
p_id_product, err := strconv.Atoi(idStr)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
country_idStr := c.Params("country_id")
|
||||||
|
|
||||||
|
b2b_id_country, err := strconv.Atoi(country_idStr)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
quantityStr := c.Params("quantity")
|
||||||
|
|
||||||
|
p_quantity, err := strconv.Atoi(quantityStr)
|
||||||
|
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)))
|
||||||
|
}
|
||||||
|
productJson, err := h.productService.Get(uint(p_id_product), customer.LangID, customer.ID, uint(b2b_id_country), uint(p_quantity))
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(response.Make(&productJson, 1, i18n.T_(c, response.Message_OK)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *ProductsHandler) ListProducts(c fiber.Ctx) error {
|
||||||
|
paging, filters, err := query_params.ParseFilters[dbmodel.PsProduct](c, columnMappingListProducts)
|
||||||
|
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.Find(customer.LangID, customer.ID, paging, filters, customer, constdata.DEFAULT_PRODUCT_QUANTITY, constdata.SHOP_ID)
|
||||||
|
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.Items, int(list.Count), i18n.T_(c, response.Message_OK)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// These are all the filterable fields
|
||||||
|
var columnMappingListProducts map[string]string = map[string]string{
|
||||||
|
"product_id": "bp.product_id",
|
||||||
|
"name": "bp.name",
|
||||||
|
"reference": "bp.reference",
|
||||||
|
"category_id": "bp.category_id",
|
||||||
|
"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 {
|
||||||
|
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)))
|
||||||
|
}
|
||||||
|
|
||||||
|
userID, ok := localeExtractor.GetUserID(c)
|
||||||
|
if !ok {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrInvalidBody)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody)))
|
||||||
|
}
|
||||||
|
|
||||||
|
err = h.productService.AddToFavorites(userID, uint(productID))
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(response.Make(nullable.GetNil(""), 0, i18n.T_(c, response.Message_OK)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *ProductsHandler) RemoveFromFavorites(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)))
|
||||||
|
}
|
||||||
|
|
||||||
|
userID, ok := localeExtractor.GetUserID(c)
|
||||||
|
if !ok {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrInvalidBody)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody)))
|
||||||
|
}
|
||||||
|
|
||||||
|
err = h.productService.RemoveFromFavorites(userID, uint(productID))
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(response.Make(nullable.GetNil(""), 0, i18n.T_(c, response.Message_OK)))
|
||||||
|
}
|
||||||
|
|
||||||
|
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, constdata.DEFAULT_PRODUCT_QUANTITY)
|
||||||
|
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)))
|
||||||
|
}
|
||||||
@@ -4,7 +4,8 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"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/delivery/middleware"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/delivery/middleware/perms"
|
||||||
"git.ma-al.com/goc_daniel/b2b/app/service/productTranslationService"
|
"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/i18n"
|
||||||
"git.ma-al.com/goc_daniel/b2b/app/utils/localeExtractor"
|
"git.ma-al.com/goc_daniel/b2b/app/utils/localeExtractor"
|
||||||
@@ -35,8 +36,8 @@ func ProductTranslationHandlerRoutes(r fiber.Router) fiber.Router {
|
|||||||
handler := NewProductTranslationHandler()
|
handler := NewProductTranslationHandler()
|
||||||
|
|
||||||
r.Get("/get-product-description", handler.GetProductDescription)
|
r.Get("/get-product-description", handler.GetProductDescription)
|
||||||
r.Post("/save-product-description", handler.SaveProductDescription)
|
r.Post("/save-product-description", middleware.Require(perms.ProductTranslationSave), handler.SaveProductDescription)
|
||||||
r.Get("/translate-product-description", handler.TranslateProductDescription)
|
r.Get("/translate-product-description", middleware.Require(perms.ProductTranslationTranslate), handler.TranslateProductDescription)
|
||||||
|
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
@@ -80,12 +81,6 @@ func (h *ProductTranslationHandler) SaveProductDescription(c fiber.Ctx) error {
|
|||||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody)))
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody)))
|
||||||
}
|
}
|
||||||
|
|
||||||
userRole, ok := localeExtractor.GetOriginalUserRole(c)
|
|
||||||
if !ok || userRole != model.RoleAdmin {
|
|
||||||
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrAdminAccessRequired)).
|
|
||||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrAdminAccessRequired)))
|
|
||||||
}
|
|
||||||
|
|
||||||
productID_attribute := c.Query("productID")
|
productID_attribute := c.Query("productID")
|
||||||
productID, err := strconv.Atoi(productID_attribute)
|
productID, err := strconv.Atoi(productID_attribute)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -123,12 +118,6 @@ func (h *ProductTranslationHandler) TranslateProductDescription(c fiber.Ctx) err
|
|||||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody)))
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody)))
|
||||||
}
|
}
|
||||||
|
|
||||||
userRole, ok := localeExtractor.GetOriginalUserRole(c)
|
|
||||||
if !ok || userRole != model.RoleAdmin {
|
|
||||||
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrAdminAccessRequired)).
|
|
||||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrAdminAccessRequired)))
|
|
||||||
}
|
|
||||||
|
|
||||||
productID_attribute := c.Query("productID")
|
productID_attribute := c.Query("productID")
|
||||||
productID, err := strconv.Atoi(productID_attribute)
|
productID, err := strconv.Atoi(productID_attribute)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"git.ma-al.com/goc_daniel/b2b/app/model"
|
"git.ma-al.com/goc_daniel/b2b/app/delivery/middleware"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/delivery/middleware/perms"
|
||||||
"git.ma-al.com/goc_daniel/b2b/app/service/meiliService"
|
"git.ma-al.com/goc_daniel/b2b/app/service/meiliService"
|
||||||
searchservice "git.ma-al.com/goc_daniel/b2b/app/service/searchService"
|
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/i18n"
|
||||||
@@ -30,7 +31,7 @@ func NewMeiliSearchHandler() *MeiliSearchHandler {
|
|||||||
func MeiliSearchHandlerRoutes(r fiber.Router) fiber.Router {
|
func MeiliSearchHandlerRoutes(r fiber.Router) fiber.Router {
|
||||||
handler := NewMeiliSearchHandler()
|
handler := NewMeiliSearchHandler()
|
||||||
|
|
||||||
r.Get("/create-index", handler.CreateIndex)
|
r.Get("/create-index", middleware.Require(perms.SearchCreateIndex), handler.CreateIndex)
|
||||||
r.Post("/search", handler.Search)
|
r.Post("/search", handler.Search)
|
||||||
r.Post("/settings", handler.GetSettings)
|
r.Post("/settings", handler.GetSettings)
|
||||||
|
|
||||||
@@ -44,12 +45,6 @@ func (h *MeiliSearchHandler) CreateIndex(c fiber.Ctx) error {
|
|||||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||||
}
|
}
|
||||||
|
|
||||||
userRole, ok := localeExtractor.GetOriginalUserRole(c)
|
|
||||||
if !ok || userRole != model.RoleAdmin {
|
|
||||||
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrAdminAccessRequired)).
|
|
||||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrAdminAccessRequired)))
|
|
||||||
}
|
|
||||||
|
|
||||||
err := h.meiliService.CreateIndex(id_lang)
|
err := h.meiliService.CreateIndex(id_lang)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("CreateIndex error: %v\n", err)
|
fmt.Printf("CreateIndex error: %v\n", err)
|
||||||
|
|||||||
160
app/delivery/web/api/restricted/specificPrice.go
Normal file
160
app/delivery/web/api/restricted/specificPrice.go
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
package restricted
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/config"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/delivery/middleware"
|
||||||
|
"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/service/specificPriceService"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/utils/i18n"
|
||||||
|
"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"
|
||||||
|
"github.com/gofiber/fiber/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SpecificPriceHandler struct {
|
||||||
|
SpecificPriceService *specificPriceService.SpecificPriceService
|
||||||
|
config *config.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSpecificPriceHandler() *SpecificPriceHandler {
|
||||||
|
SpecificPriceService := specificPriceService.New()
|
||||||
|
return &SpecificPriceHandler{
|
||||||
|
SpecificPriceService: SpecificPriceService,
|
||||||
|
config: config.Get(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SpecificPriceHandlerRoutes(r fiber.Router) fiber.Router {
|
||||||
|
handler := NewSpecificPriceHandler()
|
||||||
|
|
||||||
|
r.Post("/", middleware.Require(perms.SpecificPriceManage), handler.Create)
|
||||||
|
r.Put("/:id", middleware.Require(perms.SpecificPriceManage), handler.Update)
|
||||||
|
r.Delete("/:id", middleware.Require(perms.SpecificPriceManage), handler.Delete)
|
||||||
|
r.Get("/", middleware.Require(perms.SpecificPriceManage), handler.List)
|
||||||
|
r.Get("/:id", middleware.Require(perms.SpecificPriceManage), handler.GetByID)
|
||||||
|
r.Patch("/:id/activate", middleware.Require(perms.SpecificPriceManage), handler.Activate)
|
||||||
|
r.Patch("/:id/deactivate", middleware.Require(perms.SpecificPriceManage), handler.Deactivate)
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *SpecificPriceHandler) Create(c fiber.Ctx) error {
|
||||||
|
var pr model.SpecificPrice
|
||||||
|
if err := c.Bind().Body(&pr); err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrInvalidBody)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody)))
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := h.SpecificPriceService.Create(c.Context(), &pr)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(response.Make(&result, 1, i18n.T_(c, response.Message_OK)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *SpecificPriceHandler) Update(c fiber.Ctx) error {
|
||||||
|
idStr := c.Params("id")
|
||||||
|
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||||
|
}
|
||||||
|
|
||||||
|
var pr model.SpecificPrice
|
||||||
|
if err := c.Bind().Body(&pr); err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrInvalidBody)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody)))
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := h.SpecificPriceService.Update(c.Context(), id, &pr)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(response.Make(&result, 1, i18n.T_(c, response.Message_OK)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *SpecificPriceHandler) List(c fiber.Ctx) error {
|
||||||
|
result, err := h.SpecificPriceService.List(c.Context())
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(response.Make(&result, 1, i18n.T_(c, response.Message_OK)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *SpecificPriceHandler) GetByID(c fiber.Ctx) error {
|
||||||
|
idStr := c.Params("id")
|
||||||
|
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := h.SpecificPriceService.GetByID(c.Context(), id)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(response.Make(&result, 1, i18n.T_(c, response.Message_OK)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *SpecificPriceHandler) Activate(c fiber.Ctx) error {
|
||||||
|
idStr := c.Params("id")
|
||||||
|
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||||
|
}
|
||||||
|
|
||||||
|
err = h.SpecificPriceService.SetActive(c.Context(), id, true)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(response.Make(nullable.GetNil(""), 0, i18n.T_(c, response.Message_OK)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *SpecificPriceHandler) Deactivate(c fiber.Ctx) error {
|
||||||
|
idStr := c.Params("id")
|
||||||
|
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||||
|
}
|
||||||
|
|
||||||
|
err = h.SpecificPriceService.SetActive(c.Context(), id, false)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(response.Make(nullable.GetNil(""), 0, i18n.T_(c, response.Message_OK)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *SpecificPriceHandler) Delete(c fiber.Ctx) error {
|
||||||
|
idStr := c.Params("id")
|
||||||
|
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||||
|
}
|
||||||
|
|
||||||
|
err = h.SpecificPriceService.Delete(c.Context(), id)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||||
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(response.Make(nullable.GetNil(""), 0, i18n.T_(c, response.Message_OK)))
|
||||||
|
}
|
||||||
@@ -4,7 +4,8 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"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/delivery/middleware"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/delivery/middleware/perms"
|
||||||
"git.ma-al.com/goc_daniel/b2b/app/service/storageService"
|
"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/i18n"
|
||||||
"git.ma-al.com/goc_daniel/b2b/app/utils/localeExtractor"
|
"git.ma-al.com/goc_daniel/b2b/app/utils/localeExtractor"
|
||||||
@@ -34,7 +35,7 @@ func StorageHandlerRoutes(r fiber.Router) fiber.Router {
|
|||||||
r.Get("/download-file/*", handler.DownloadFile)
|
r.Get("/download-file/*", handler.DownloadFile)
|
||||||
|
|
||||||
// for admins only
|
// for admins only
|
||||||
r.Get("/create-new-webdav-token", handler.CreateNewWebdavToken)
|
r.Get("/create-new-webdav-token", middleware.Require(perms.WebdavCreateToken), handler.CreateNewWebdavToken)
|
||||||
|
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
@@ -84,12 +85,6 @@ func (h *StorageHandler) CreateNewWebdavToken(c fiber.Ctx) error {
|
|||||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody)))
|
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody)))
|
||||||
}
|
}
|
||||||
|
|
||||||
userRole, ok := localeExtractor.GetOriginalUserRole(c)
|
|
||||||
if !ok || userRole != model.RoleAdmin {
|
|
||||||
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrAdminAccessRequired)).
|
|
||||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrAdminAccessRequired)))
|
|
||||||
}
|
|
||||||
|
|
||||||
new_token, err := h.storageService.NewWebdavToken(userID)
|
new_token, err := h.storageService.NewWebdavToken(userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||||
|
|||||||
@@ -86,9 +86,10 @@ func (s *Server) Setup() error {
|
|||||||
|
|
||||||
// API routes
|
// API routes
|
||||||
s.api = s.app.Group("/api/v1")
|
s.api = s.app.Group("/api/v1")
|
||||||
|
s.api.Use(middleware.Authenticate())
|
||||||
s.public = s.api.Group("/public")
|
s.public = s.api.Group("/public")
|
||||||
s.restricted = s.api.Group("/restricted")
|
s.restricted = s.api.Group("/restricted")
|
||||||
s.restricted.Use(middleware.AuthMiddleware())
|
s.restricted.Use(middleware.Authorize())
|
||||||
s.webdav = s.api.Group("/webdav")
|
s.webdav = s.api.Group("/webdav")
|
||||||
s.webdav.Use(middleware.Webdav())
|
s.webdav.Use(middleware.Webdav())
|
||||||
|
|
||||||
@@ -105,13 +106,15 @@ func (s *Server) Setup() error {
|
|||||||
menuRouting := s.public.Group("/menu")
|
menuRouting := s.public.Group("/menu")
|
||||||
public.RoutingHandlerRoutes(menuRouting)
|
public.RoutingHandlerRoutes(menuRouting)
|
||||||
|
|
||||||
|
pCustomer := s.restricted.Group("/customer")
|
||||||
|
restricted.CustomerHandlerRoutes(pCustomer)
|
||||||
|
|
||||||
// product translation routes (restricted)
|
// product translation routes (restricted)
|
||||||
productTranslation := s.restricted.Group("/product-translation")
|
productTranslation := s.restricted.Group("/product-translation")
|
||||||
restricted.ProductTranslationHandlerRoutes(productTranslation)
|
restricted.ProductTranslationHandlerRoutes(productTranslation)
|
||||||
|
|
||||||
// lists of things routes (restricted)
|
product := s.restricted.Group("/product")
|
||||||
list := s.restricted.Group("/list")
|
restricted.ProductsHandlerRoutes(product)
|
||||||
restricted.ListHandlerRoutes(list)
|
|
||||||
|
|
||||||
// locale selector (restricted)
|
// locale selector (restricted)
|
||||||
// this is basically for changing user's selected language and country
|
// this is basically for changing user's selected language and country
|
||||||
@@ -130,12 +133,25 @@ func (s *Server) Setup() error {
|
|||||||
carts := s.restricted.Group("/carts")
|
carts := s.restricted.Group("/carts")
|
||||||
restricted.CartsHandlerRoutes(carts)
|
restricted.CartsHandlerRoutes(carts)
|
||||||
|
|
||||||
|
// orders (restricted)
|
||||||
|
orders := s.restricted.Group("/orders")
|
||||||
|
restricted.OrdersHandlerRoutes(orders)
|
||||||
|
|
||||||
|
specificPrice := s.restricted.Group("/specific-price")
|
||||||
|
restricted.SpecificPriceHandlerRoutes(specificPrice)
|
||||||
|
|
||||||
|
// addresses (restricted)
|
||||||
|
addresses := s.restricted.Group("/addresses")
|
||||||
|
restricted.AddressesHandlerRoutes(addresses)
|
||||||
|
|
||||||
// storage (uses various authorization means)
|
// storage (uses various authorization means)
|
||||||
restrictedStorage := s.restricted.Group("/storage")
|
restrictedStorage := s.restricted.Group("/storage")
|
||||||
webdavStorage := s.webdav.Group("/storage")
|
webdavStorage := s.webdav.Group("/storage")
|
||||||
restricted.StorageHandlerRoutes(restrictedStorage)
|
restricted.StorageHandlerRoutes(restrictedStorage)
|
||||||
webdav.StorageHandlerRoutes(webdavStorage)
|
webdav.StorageHandlerRoutes(webdavStorage)
|
||||||
|
|
||||||
|
restricted.CurrencyHandlerRoutes(s.restricted)
|
||||||
|
|
||||||
s.api.All("*", func(c fiber.Ctx) error {
|
s.api.All("*", func(c fiber.Ctx) error {
|
||||||
return c.SendStatus(fiber.StatusNotFound)
|
return c.SendStatus(fiber.StatusNotFound)
|
||||||
})
|
})
|
||||||
@@ -151,16 +167,6 @@ func (s *Server) Setup() error {
|
|||||||
// })
|
// })
|
||||||
// })
|
// })
|
||||||
|
|
||||||
// // Admin routes example
|
|
||||||
// admin := s.api.Group("/admin")
|
|
||||||
// admin.Use(middleware.AuthMiddleware())
|
|
||||||
// admin.Use(middleware.RequireAdmin())
|
|
||||||
// admin.Get("/users", func(c fiber.Ctx) error {
|
|
||||||
// return c.JSON(fiber.Map{
|
|
||||||
// "message": "Admin area - user management",
|
|
||||||
// })
|
|
||||||
// })
|
|
||||||
|
|
||||||
// keep this at the end because its wilderange
|
// keep this at the end because its wilderange
|
||||||
general.InitBo(s.App())
|
general.InitBo(s.App())
|
||||||
|
|
||||||
|
|||||||
72
app/model/address.go
Normal file
72
app/model/address.go
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
type Address struct {
|
||||||
|
ID uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
|
||||||
|
CustomerID uint `gorm:"column:b2b_customer_id;not null;index" json:"customer_id"`
|
||||||
|
AddressString string `gorm:"column:address_string;not null" json:"address_string"`
|
||||||
|
AddressUnparsed *AddressUnparsed `gorm:"-" json:"address_unparsed"`
|
||||||
|
CountryID uint `gorm:"column:b2b_country_id;not null" json:"country_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (Address) TableName() string {
|
||||||
|
return "b2b_addresses"
|
||||||
|
}
|
||||||
|
|
||||||
|
type AddressUnparsed interface{}
|
||||||
|
|
||||||
|
// Address template in Poland
|
||||||
|
type AddressPL struct {
|
||||||
|
PostalCode string `json:"postal_code"` // format: 00-000
|
||||||
|
City string `json:"city"` // e.g. Kraków
|
||||||
|
Voivodeship string `json:"voivodeship"` // e.g. małopolskie (optional but useful)
|
||||||
|
|
||||||
|
Street string `json:"street"` // e.g. Marszałkowska
|
||||||
|
BuildingNo string `json:"building_no"` // e.g. 10, 221B, 12A
|
||||||
|
ApartmentNo string `json:"apartment_no"` // e.g. 5, 12B
|
||||||
|
|
||||||
|
AddressLine2 string `json:"address_line2"` // optional extra info
|
||||||
|
|
||||||
|
Recipient string `json:"recipient"` // name/company
|
||||||
|
}
|
||||||
|
|
||||||
|
// Address template in Great Britain
|
||||||
|
type AddressGB struct {
|
||||||
|
PostalCode string `json:"postal_code"` // e.g. SW1A 1AA
|
||||||
|
PostTown string `json:"post_town"` // e.g. London
|
||||||
|
County string `json:"county"` // optional
|
||||||
|
|
||||||
|
Thoroughfare string `json:"thoroughfare"` // street name, e.g. Baker Street
|
||||||
|
BuildingNo string `json:"building_no"` // e.g. 221B
|
||||||
|
BuildingName string `json:"building_name"` // e.g. Flatiron House
|
||||||
|
SubBuilding string `json:"sub_building"` // e.g. Flat 5, Apt 2
|
||||||
|
|
||||||
|
AddressLine2 string `json:"address_line2"`
|
||||||
|
Recipient string `json:"recipient"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Address template in Czech Republic
|
||||||
|
type AddressCZ struct {
|
||||||
|
PostalCode string `json:"postal_code"` // usually 110 00 or 11000
|
||||||
|
City string `json:"city"` // e.g. Praha
|
||||||
|
Region string `json:"region"`
|
||||||
|
|
||||||
|
Street string `json:"street"` // may be omitted in some village-style addresses
|
||||||
|
HouseNumber string `json:"house_number"` // descriptive / conscription no.
|
||||||
|
OrientationNumber string `json:"orientation_number"` // optional, often after slash
|
||||||
|
|
||||||
|
AddressLine2 string `json:"address_line2"`
|
||||||
|
Recipient string `json:"recipient"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Address template in Germany
|
||||||
|
type AddressDE struct {
|
||||||
|
PostalCode string `json:"postal_code"` // e.g. 10115
|
||||||
|
City string `json:"city"` // e.g. Berlin
|
||||||
|
State string `json:"state"` // Bundesland, optional
|
||||||
|
|
||||||
|
Street string `json:"street"` // e.g. Unter den Linden
|
||||||
|
HouseNumber string `json:"house_number"` // e.g. 77, 12a
|
||||||
|
|
||||||
|
AddressLine2 string `json:"address_line2"` // extra details
|
||||||
|
Recipient string `json:"recipient"`
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ type ScannedCategory struct {
|
|||||||
IsoCode string `gorm:"column:iso_code"`
|
IsoCode string `gorm:"column:iso_code"`
|
||||||
|
|
||||||
Visited bool // this is for internal backend use only
|
Visited bool // this is for internal backend use only
|
||||||
|
Filter string // filter applicable to this category
|
||||||
}
|
}
|
||||||
|
|
||||||
type Category struct {
|
type Category struct {
|
||||||
@@ -25,6 +26,7 @@ type CategoryParams struct {
|
|||||||
CategoryID uint `json:"category_id" form:"category_id"`
|
CategoryID uint `json:"category_id" form:"category_id"`
|
||||||
LinkRewrite string `json:"link_rewrite" form:"link_rewrite"`
|
LinkRewrite string `json:"link_rewrite" form:"link_rewrite"`
|
||||||
Locale string `json:"locale" form:"locale"`
|
Locale string `json:"locale" form:"locale"`
|
||||||
|
Filter string `json:"filter" form:"filter"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type CategoryInBreadcrumb struct {
|
type CategoryInBreadcrumb struct {
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
25
app/model/currency.go
Normal file
25
app/model/currency.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type Currency struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
PsIDCurrency uint `json:"ps_id_currency"`
|
||||||
|
IsDefault bool `json:"is_default"`
|
||||||
|
IsActive bool `json:"is_active"`
|
||||||
|
ConversionRate *float64 `json:"conversion_rate,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (Currency) TableName() string {
|
||||||
|
return "b2b_currencies"
|
||||||
|
}
|
||||||
|
|
||||||
|
type CurrencyRate struct {
|
||||||
|
B2bIdCurrency uint `json:"b2b_id_currency"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
ConversionRate *float64 `json:"conversion_rate,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (CurrencyRate) TableName() string {
|
||||||
|
return "b2b_currency_rates"
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ package model
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/delivery/middleware/perms"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -13,7 +14,8 @@ type Customer struct {
|
|||||||
Password string `gorm:"size:255" json:"-"` // Hashed password, not exposed in JSON
|
Password string `gorm:"size:255" json:"-"` // Hashed password, not exposed in JSON
|
||||||
FirstName string `gorm:"size:100" json:"first_name"`
|
FirstName string `gorm:"size:100" json:"first_name"`
|
||||||
LastName string `gorm:"size:100" json:"last_name"`
|
LastName string `gorm:"size:100" json:"last_name"`
|
||||||
Role CustomerRole `gorm:"type:varchar(20);default:'user'" json:"role"`
|
RoleID uint `gorm:"column:role_id;not null;default:1" json:"-"`
|
||||||
|
Role *Role `gorm:"foreignKey:RoleID" json:"role,omitempty"`
|
||||||
Provider AuthProvider `gorm:"type:varchar(20);default:'local'" json:"provider"`
|
Provider AuthProvider `gorm:"type:varchar(20);default:'local'" json:"provider"`
|
||||||
ProviderID string `gorm:"size:255" json:"provider_id,omitempty"` // ID from OAuth provider
|
ProviderID string `gorm:"size:255" json:"provider_id,omitempty"` // ID from OAuth provider
|
||||||
AvatarURL string `gorm:"size:500" json:"avatar_url,omitempty"`
|
AvatarURL string `gorm:"size:500" json:"avatar_url,omitempty"`
|
||||||
@@ -29,18 +31,20 @@ 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:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CustomerRole represents the role of a user
|
func (u *Customer) HasPermission(permission perms.Permission) bool {
|
||||||
type CustomerRole string
|
for _, p := range u.Role.Permissions {
|
||||||
|
if p.Name == permission {
|
||||||
const (
|
return true
|
||||||
RoleUser CustomerRole = "user"
|
}
|
||||||
RoleAdmin CustomerRole = "admin"
|
}
|
||||||
)
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// AuthProvider represents the authentication provider
|
// AuthProvider represents the authentication provider
|
||||||
type AuthProvider string
|
type AuthProvider string
|
||||||
@@ -55,16 +59,6 @@ func (Customer) TableName() string {
|
|||||||
return "b2b_customers"
|
return "b2b_customers"
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsAdmin checks if the user has admin role
|
|
||||||
func (u *Customer) IsAdmin() bool {
|
|
||||||
return u.Role == RoleAdmin
|
|
||||||
}
|
|
||||||
|
|
||||||
// CanManageUsers checks if the user can manage other users
|
|
||||||
func (u *Customer) CanManageUsers() bool {
|
|
||||||
return u.Role == RoleAdmin
|
|
||||||
}
|
|
||||||
|
|
||||||
// FullName returns the user's full name
|
// FullName returns the user's full name
|
||||||
func (u *Customer) FullName() string {
|
func (u *Customer) FullName() string {
|
||||||
if u.FirstName == "" && u.LastName == "" {
|
if u.FirstName == "" && u.LastName == "" {
|
||||||
@@ -78,10 +72,21 @@ type UserSession struct {
|
|||||||
UserID uint `json:"user_id"`
|
UserID uint `json:"user_id"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
Role CustomerRole `json:"role"`
|
RoleID uint `json:"role_id"`
|
||||||
|
RoleName string `json:"role_name"`
|
||||||
LangID uint `json:"lang_id"`
|
LangID uint `json:"lang_id"`
|
||||||
CountryID uint `json:"country_id"`
|
CountryID uint `json:"country_id"`
|
||||||
IsActive bool `json:"is_active"`
|
IsActive bool `json:"is_active"`
|
||||||
|
Permissions []perms.Permission `json:"permissions"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (us *UserSession) HasPermission(permission perms.Permission) bool {
|
||||||
|
for _, p := range us.Permissions {
|
||||||
|
if p == permission {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserLocale struct {
|
type UserLocale struct {
|
||||||
@@ -95,16 +100,29 @@ type UserLocale struct {
|
|||||||
|
|
||||||
// ToSession converts User to UserSession
|
// ToSession converts User to UserSession
|
||||||
func (u *Customer) ToSession() *UserSession {
|
func (u *Customer) ToSession() *UserSession {
|
||||||
|
|
||||||
return &UserSession{
|
return &UserSession{
|
||||||
UserID: u.ID,
|
UserID: u.ID,
|
||||||
Email: u.Email,
|
Email: u.Email,
|
||||||
Role: u.Role,
|
RoleID: u.Role.ID,
|
||||||
|
RoleName: u.Role.Name,
|
||||||
|
Permissions: BuildPermissionSlice(u),
|
||||||
LangID: u.LangID,
|
LangID: u.LangID,
|
||||||
CountryID: u.CountryID,
|
CountryID: u.CountryID,
|
||||||
IsActive: u.IsActive,
|
IsActive: u.IsActive,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BuildPermissionSlice(user *Customer) []perms.Permission {
|
||||||
|
var perms []perms.Permission
|
||||||
|
|
||||||
|
for _, p := range user.Role.Permissions {
|
||||||
|
perms = append(perms, p.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return perms
|
||||||
|
}
|
||||||
|
|
||||||
// LoginRequest represents the login form data
|
// LoginRequest represents the login form data
|
||||||
type LoginRequest struct {
|
type LoginRequest struct {
|
||||||
Email string `json:"email" form:"email"`
|
Email string `json:"email" form:"email"`
|
||||||
@@ -162,5 +180,4 @@ type UserInList struct {
|
|||||||
Email string `gorm:"column:email" json:"email"`
|
Email string `gorm:"column:email" json:"email"`
|
||||||
FirstName string `gorm:"column:first_name" json:"first_name"`
|
FirstName string `gorm:"column:first_name" json:"first_name"`
|
||||||
LastName string `gorm:"column:last_name" json:"last_name"`
|
LastName string `gorm:"column:last_name" json:"last_name"`
|
||||||
Role string `gorm:"column:role" json:"role"`
|
|
||||||
}
|
}
|
||||||
|
|||||||
18
app/model/model.go
Normal file
18
app/model/model.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Model struct {
|
||||||
|
ID uint `gorm:"primarykey;autoIncrement" swaggerignore:"true" json:"id,omitempty" hidden:"true"`
|
||||||
|
CreatedAt time.Time `gorm:"not null;autoCreateTime" swaggerignore:"true" json:"-"`
|
||||||
|
UpdatedAt time.Time `gorm:"autoUpdateTime" swaggerignore:"true" json:"-"`
|
||||||
|
DeletedAt gorm.DeletedAt `gorm:"index" swaggerignore:"true" json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Makes all objects embedding db.Model implementators of ModelWithID interface
|
||||||
|
func (m Model) ModelWithID() {
|
||||||
|
}
|
||||||
27
app/model/order.go
Normal file
27
app/model/order.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
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"`
|
||||||
|
Name string `gorm:"column:name;not null" json:"name"`
|
||||||
|
CountryID uint `gorm:"column:country_id;not null" json:"country_id"`
|
||||||
|
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"`
|
||||||
|
Products []OrderProduct `gorm:"foreignKey:OrderID;references:OrderID" json:"products"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (CustomerOrder) TableName() string {
|
||||||
|
return "b2b_customer_orders"
|
||||||
|
}
|
||||||
|
|
||||||
|
type OrderProduct struct {
|
||||||
|
OrderID uint `gorm:"column:order_id;not null;index" json:"-"`
|
||||||
|
ProductID uint `gorm:"column:product_id;not null" json:"product_id"`
|
||||||
|
ProductAttributeID *uint `gorm:"column:product_attribute_id" json:"product_attribute_id,omitempty"`
|
||||||
|
Amount uint `gorm:"column:amount;not null" json:"amount"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (OrderProduct) TableName() string {
|
||||||
|
return "b2b_orders_products"
|
||||||
|
}
|
||||||
12
app/model/permission.go
Normal file
12
app/model/permission.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import "git.ma-al.com/goc_daniel/b2b/app/delivery/middleware/perms"
|
||||||
|
|
||||||
|
type Permission struct {
|
||||||
|
ID uint
|
||||||
|
Name perms.Permission
|
||||||
|
}
|
||||||
|
|
||||||
|
func (Permission) TableName() string {
|
||||||
|
return "b2b_permissions"
|
||||||
|
}
|
||||||
@@ -1,66 +1,5 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
// Product contains each and every column from the table ps_product.
|
|
||||||
type Product struct {
|
|
||||||
ProductID uint `gorm:"column:id_product;primaryKey" json:"product_id" form:"product_id"`
|
|
||||||
SupplierID uint `gorm:"column:id_supplier" json:"supplier_id" form:"supplier_id"`
|
|
||||||
ManufacturerID uint `gorm:"column:id_manufacturer" json:"manufacturer_id" form:"manufacturer_id"`
|
|
||||||
CategoryDefaultID uint `gorm:"column:id_category_default" json:"category_default_id" form:"category_default_id"`
|
|
||||||
ShopDefaultID uint `gorm:"column:id_shop_default" json:"shop_default_id" form:"shop_default_id"`
|
|
||||||
TaxRulesGroupID uint `gorm:"column:id_tax_rules_group" json:"tax_rules_group_id" form:"tax_rules_group_id"`
|
|
||||||
OnSale uint `gorm:"column:on_sale" json:"on_sale" form:"on_sale"`
|
|
||||||
OnlineOnly uint `gorm:"column:online_only" json:"online_only" form:"online_only"`
|
|
||||||
EAN13 string `gorm:"column:ean13;type:varchar(13)" json:"ean13" form:"ean13"`
|
|
||||||
ISBN string `gorm:"column:isbn;type:varchar(32)" json:"isbn" form:"isbn"`
|
|
||||||
UPC string `gorm:"column:upc;type:varchar(12)" json:"upc" form:"upc"`
|
|
||||||
EkoTax float32 `gorm:"column:eko_tax;type:decimal(20,6)" json:"eko_tax" form:"eko_tax"`
|
|
||||||
Quantity uint `gorm:"column:quantity" json:"quantity" form:"quantity"`
|
|
||||||
MinimalQuantity uint `gorm:"column:minimal_quantity" json:"minimal_quantity" form:"minimal_quantity"`
|
|
||||||
LowStockThreshold uint `gorm:"column:low_stock_threshold" json:"low_stock_threshold" form:"low_stock_threshold"`
|
|
||||||
LowStockAlert uint `gorm:"column:low_stock_alert" json:"low_stock_alert" form:"low_stock_alert"`
|
|
||||||
Price float32 `gorm:"column:price;type:decimal(20,6)" json:"price" form:"price"`
|
|
||||||
WholesalePrice float32 `gorm:"column:wholesale_price;type:decimal(20,6)" json:"wholesale_price" form:"wholesale_price"`
|
|
||||||
Unity string `gorm:"column:unity;type:varchar(255)" json:"unity" form:"unity"`
|
|
||||||
UnitPriceRatio float32 `gorm:"column:unit_price_ratio;type:decimal(20,6)" json:"unit_price_ratio" form:"unit_price_ratio"`
|
|
||||||
UnitID uint `gorm:"column:id_unit;primaryKey" json:"unit_id" form:"unit_id"`
|
|
||||||
AdditionalShippingCost float32 `gorm:"column:additional_shipping_cost;type:decimal(20,2)" json:"additional_shipping_cost" form:"additional_shipping_cost"`
|
|
||||||
Reference string `gorm:"column:reference;type:varchar(64)" json:"reference" form:"reference"`
|
|
||||||
SupplierReference string `gorm:"column:supplier_reference;type:varchar(64)" json:"supplier_reference" form:"supplier_reference"`
|
|
||||||
Location string `gorm:"column:location;type:varchar(64)" json:"location" form:"location"`
|
|
||||||
|
|
||||||
Width float32 `gorm:"column:width;type:decimal(20,6)" json:"width" form:"width"`
|
|
||||||
Height float32 `gorm:"column:height;type:decimal(20,6)" json:"height" form:"height"`
|
|
||||||
Depth float32 `gorm:"column:depth;type:decimal(20,6)" json:"depth" form:"depth"`
|
|
||||||
Weight float32 `gorm:"column:weight;type:decimal(20,6)" json:"weight" form:"weight"`
|
|
||||||
OutOfStock uint `gorm:"column:out_of_stock" json:"out_of_stock" form:"out_of_stock"`
|
|
||||||
AdditionalDeliveryTimes uint `gorm:"column:additional_delivery_times" json:"additional_delivery_times" form:"additional_delivery_times"`
|
|
||||||
QuantityDiscount uint `gorm:"column:quantity_discount" json:"quantity_discount" form:"quantity_discount"`
|
|
||||||
Customizable uint `gorm:"column:customizable" json:"customizable" form:"customizable"`
|
|
||||||
UploadableFiles uint `gorm:"column:uploadable_files" json:"uploadable_files" form:"uploadable_files"`
|
|
||||||
TextFields uint `gorm:"column:text_fields" json:"text_fields" form:"text_fields"`
|
|
||||||
|
|
||||||
Active uint `gorm:"column:active" json:"active" form:"active"`
|
|
||||||
RedirectType string `gorm:"column:redirect_type;type:enum('','404','301-product','302-product','301-category','302-category')" json:"redirect_type" form:"redirect_type"`
|
|
||||||
TypeRedirectedID int `gorm:"column:id_type_redirected" json:"type_redirected_id" form:"type_redirected_id"`
|
|
||||||
AvailableForOrder uint `gorm:"column:available_for_order" json:"available_for_order" form:"available_for_order"`
|
|
||||||
AvailableDate string `gorm:"column:available_date;type:date" json:"available_date" form:"available_date"`
|
|
||||||
ShowCondition uint `gorm:"column:show_condition" json:"show_condition" form:"show_condition"`
|
|
||||||
Condition string `gorm:"column:condition;type:enum('new','used','refurbished')" json:"condition" form:"condition"`
|
|
||||||
ShowPrice uint `gorm:"column:show_price" json:"show_price" form:"show_price"`
|
|
||||||
|
|
||||||
Indexed uint `gorm:"column:indexed" json:"indexed" form:"indexed"`
|
|
||||||
Visibility string `gorm:"column:visibility;type:enum('both','catalog','search','none')" json:"visibility" form:"visibility"`
|
|
||||||
CacheIsPack uint `gorm:"column:cache_is_pack" json:"cache_is_pack" form:"cache_is_pack"`
|
|
||||||
CacheHasAttachments uint `gorm:"column:cache_has_attachments" json:"cache_has_attachments" form:"cache_has_attachments"`
|
|
||||||
IsVirtual uint `gorm:"column:is_virtual" json:"is_virtual" form:"is_virtual"`
|
|
||||||
CacheDefaultAttribute uint `gorm:"column:cache_default_attribute" json:"cache_default_attribute" form:"cache_default_attribute"`
|
|
||||||
DateAdd string `gorm:"column:date_add;type:datetime" json:"date_add" form:"date_add"`
|
|
||||||
DateUpd string `gorm:"column:date_upd;type:datetime" json:"date_upd" form:"date_upd"`
|
|
||||||
AdvancedStockManagement uint `gorm:"column:advanced_stock_management" json:"advanced_stock_management" form:"advanced_stock_management"`
|
|
||||||
PackStockType uint `gorm:"column:pack_stock_type" json:"pack_stock_type" form:"pack_stock_type"`
|
|
||||||
State uint `gorm:"column:state" json:"state" form:"state"`
|
|
||||||
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"`
|
||||||
@@ -70,6 +9,11 @@ type ProductInList struct {
|
|||||||
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"`
|
||||||
|
IsFavorite bool `gorm:"column:is_favorite" json:"is_favorite"`
|
||||||
|
IsNew bool `gorm:"column:is_new" json:"is_new"`
|
||||||
|
IsOEM bool `gorm:"column:is_oem" json:"is_oem"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProductFilters struct {
|
type ProductFilters struct {
|
||||||
@@ -85,3 +29,12 @@ type ProductFilters struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type FeatVal = map[uint][]uint
|
type FeatVal = map[uint][]uint
|
||||||
|
|
||||||
|
type B2bFavorite struct {
|
||||||
|
UserID uint `gorm:"column:user_id;not null;primaryKey" json:"user_id"`
|
||||||
|
ProductID uint `gorm:"column:product_id;not null;primaryKey" json:"product_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*B2bFavorite) TableName() string {
|
||||||
|
return "b2b_favorites"
|
||||||
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ type ProductDescription struct {
|
|||||||
AvailableLater string `gorm:"column:available_later;type:varchar(255)" json:"available_later" form:"available_later"`
|
AvailableLater string `gorm:"column:available_later;type:varchar(255)" json:"available_later" form:"available_later"`
|
||||||
DeliveryInStock string `gorm:"column:delivery_in_stock;type:varchar(255)" json:"delivery_in_stock" form:"delivery_in_stock"`
|
DeliveryInStock string `gorm:"column:delivery_in_stock;type:varchar(255)" json:"delivery_in_stock" form:"delivery_in_stock"`
|
||||||
DeliveryOutStock string `gorm:"column:delivery_out_stock;type:varchar(255)" json:"delivery_out_stock" form:"delivery_out_stock"`
|
DeliveryOutStock string `gorm:"column:delivery_out_stock;type:varchar(255)" json:"delivery_out_stock" form:"delivery_out_stock"`
|
||||||
Usage string `gorm:"column:_usage_;type:text" json:"usage" form:"usage"`
|
Usage string `gorm:"column:usage;type:text" json:"usage" form:"usage"`
|
||||||
|
|
||||||
ImageLink string `gorm:"column:image_link" json:"image_link"`
|
ImageLink string `gorm:"column:image_link" json:"image_link"`
|
||||||
ExistsInDatabase bool `gorm:"-" json:"exists_in_database"`
|
ExistsInDatabase bool `gorm:"-" json:"exists_in_database"`
|
||||||
|
|||||||
19
app/model/role.go
Normal file
19
app/model/role.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
type Role struct {
|
||||||
|
ID uint `gorm:"primaryKey" json:"id"`
|
||||||
|
Name string `gorm:"size:64" json:"name"`
|
||||||
|
Permissions []Permission `gorm:"many2many:b2b_role_permissions;" json:"permissions"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (Role) TableName() string {
|
||||||
|
return "b2b_roles"
|
||||||
|
}
|
||||||
|
|
||||||
|
type CustomerRole string
|
||||||
|
|
||||||
|
const (
|
||||||
|
RoleUser CustomerRole = "user"
|
||||||
|
RoleAdmin CustomerRole = "admin"
|
||||||
|
RoleSuperAdmin CustomerRole = "super_admin"
|
||||||
|
)
|
||||||
@@ -7,7 +7,6 @@ type Route struct {
|
|||||||
Component string `gorm:"type:varchar(255);not null;comment:path to component file" json:"component"`
|
Component string `gorm:"type:varchar(255);not null;comment:path to component file" json:"component"`
|
||||||
Meta *string `gorm:"type:longtext;default:'{}'" json:"meta,omitempty"`
|
Meta *string `gorm:"type:longtext;default:'{}'" json:"meta,omitempty"`
|
||||||
Active *bool `gorm:"type:tinyint;default:1" json:"active,omitempty"`
|
Active *bool `gorm:"type:tinyint;default:1" json:"active,omitempty"`
|
||||||
SortOrder *int `gorm:"type:int;default:0" json:"sort_order,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (Route) TableName() string {
|
func (Route) TableName() string {
|
||||||
|
|||||||
29
app/model/specificPrice.go
Normal file
29
app/model/specificPrice.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type SpecificPrice struct {
|
||||||
|
ID uint64 `gorm:"primaryKey;autoIncrement" json:"id"`
|
||||||
|
Name string `gorm:"type:varchar(255);not null" json:"name"`
|
||||||
|
ValidFrom *time.Time `gorm:"null" json:"valid_from"`
|
||||||
|
ValidTill *time.Time `gorm:"null" json:"valid_till"`
|
||||||
|
HasExpirationDate bool `gorm:"default:false" json:"has_expiration_date"`
|
||||||
|
ReductionType string `gorm:"type:enum('amount','percentage');not null" json:"reduction_type"`
|
||||||
|
Price *float64 `gorm:"type:decimal(10,2);null" json:"price"`
|
||||||
|
CurrencyID *uint64 `gorm:"column:b2b_id_currency;null" json:"currency_id"`
|
||||||
|
PercentageReduction *float64 `gorm:"type:decimal(5,2);null" json:"percentage_reduction"`
|
||||||
|
FromQuantity uint32 `gorm:"default:1" json:"from_quantity"`
|
||||||
|
IsActive bool `gorm:"default:true" json:"is_active"`
|
||||||
|
CreatedAt *time.Time `gorm:"null" json:"created_at"`
|
||||||
|
UpdatedAt *time.Time `gorm:"null" json:"updated_at"`
|
||||||
|
|
||||||
|
ProductIDs []uint64 `gorm:"-" json:"product_ids"`
|
||||||
|
CategoryIDs []uint64 `gorm:"-" json:"category_ids"`
|
||||||
|
ProductAttributeIDs []uint64 `gorm:"-" json:"product_attribute_ids"`
|
||||||
|
CountryIDs []uint64 `gorm:"-" json:"country_ids"`
|
||||||
|
CustomerIDs []uint64 `gorm:"-" json:"customer_ids"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (SpecificPrice) TableName() string {
|
||||||
|
return "b2b_specific_price"
|
||||||
|
}
|
||||||
91
app/repos/addressesRepo/addressesRepo.go
Normal file
91
app/repos/addressesRepo/addressesRepo.go
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
package addressesRepo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/db"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UIAddressesRepo interface {
|
||||||
|
UserHasAddress(user_id uint, address_id uint) (uint, error)
|
||||||
|
UserAddressesAmt(user_id uint) (uint, error)
|
||||||
|
AddNewAddress(user_id uint, address_info string, country_id uint) error
|
||||||
|
UpdateAddress(user_id uint, address_id uint, address_info string, country_id uint) error
|
||||||
|
RetrieveAddresses(user_id uint) (*[]model.Address, error)
|
||||||
|
DeleteAddress(user_id uint, address_id uint) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type AddressesRepo struct{}
|
||||||
|
|
||||||
|
func New() UIAddressesRepo {
|
||||||
|
return &AddressesRepo{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *AddressesRepo) UserHasAddress(user_id uint, address_id uint) (uint, error) {
|
||||||
|
var amt uint
|
||||||
|
|
||||||
|
err := db.DB.
|
||||||
|
Table("b2b_addresses").
|
||||||
|
Select("COUNT(*) AS amt").
|
||||||
|
Where("id = ? AND b2b_customer_id = ?", address_id, user_id).
|
||||||
|
Scan(&amt).
|
||||||
|
Error
|
||||||
|
|
||||||
|
return amt, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *AddressesRepo) UserAddressesAmt(user_id uint) (uint, error) {
|
||||||
|
var amt uint
|
||||||
|
|
||||||
|
err := db.DB.
|
||||||
|
Table("b2b_addresses").
|
||||||
|
Select("COUNT(*) AS amt").
|
||||||
|
Where("b2b_customer_id = ?", user_id).
|
||||||
|
Scan(&amt).
|
||||||
|
Error
|
||||||
|
|
||||||
|
return amt, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *AddressesRepo) AddNewAddress(user_id uint, address_info string, country_id uint) error {
|
||||||
|
address := model.Address{
|
||||||
|
CustomerID: user_id,
|
||||||
|
AddressString: address_info,
|
||||||
|
CountryID: country_id,
|
||||||
|
}
|
||||||
|
|
||||||
|
return db.DB.
|
||||||
|
Create(&address).
|
||||||
|
Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *AddressesRepo) UpdateAddress(user_id uint, address_id uint, address_info string, country_id uint) error {
|
||||||
|
address := model.Address{
|
||||||
|
ID: address_id,
|
||||||
|
CustomerID: user_id,
|
||||||
|
AddressString: address_info,
|
||||||
|
CountryID: country_id,
|
||||||
|
}
|
||||||
|
|
||||||
|
return db.DB.
|
||||||
|
Where("id = ? AND b2b_customer_id = ?", address_id, user_id).
|
||||||
|
Updates(&address).
|
||||||
|
Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *AddressesRepo) RetrieveAddresses(user_id uint) (*[]model.Address, error) {
|
||||||
|
var addresses []model.Address
|
||||||
|
|
||||||
|
err := db.DB.
|
||||||
|
Where("b2b_customer_id = ?", user_id).
|
||||||
|
Find(&addresses).
|
||||||
|
Error
|
||||||
|
|
||||||
|
return &addresses, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *AddressesRepo) DeleteAddress(user_id uint, address_id uint) error {
|
||||||
|
return db.DB.
|
||||||
|
Where("id = ? AND b2b_customer_id = ?", address_id, user_id).
|
||||||
|
Delete(&model.Address{}).
|
||||||
|
Error
|
||||||
|
}
|
||||||
@@ -1,20 +1,26 @@
|
|||||||
package cartsRepo
|
package cartsRepo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
"git.ma-al.com/goc_daniel/b2b/app/db"
|
"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"
|
||||||
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/responseErrors"
|
||||||
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UICartsRepo interface {
|
type UICartsRepo interface {
|
||||||
CartsAmount(user_id uint) (uint, error)
|
CartsAmount(user_id uint) (uint, error)
|
||||||
CreateNewCart(user_id uint) (model.CustomerCart, error)
|
CreateNewCart(user_id uint, name string) (model.CustomerCart, error)
|
||||||
UserHasCart(user_id uint, cart_id uint) (uint, 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
|
UpdateCartName(user_id uint, cart_id uint, new_name string) error
|
||||||
RetrieveCartsInfo(user_id uint) ([]model.CustomerCart, error)
|
RetrieveCartsInfo(user_id uint) ([]model.CustomerCart, error)
|
||||||
RetrieveCart(user_id uint, cart_id uint) (*model.CustomerCart, error)
|
RetrieveCart(user_id uint, cart_id uint) (*model.CustomerCart, error)
|
||||||
CheckProductExists(product_id uint, product_attribute_id *uint) (uint, error)
|
CheckProductExists(product_id uint, product_attribute_id *uint) (bool, error)
|
||||||
AddProduct(user_id uint, cart_id uint, product_id uint, product_attribute_id *uint, amount uint) error
|
AddProduct(cart_id uint, product_id uint, product_attribute_id *uint, amount uint, set_amount bool) error
|
||||||
|
RemoveProduct(cart_id uint, product_id uint, product_attribute_id *uint) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type CartsRepo struct{}
|
type CartsRepo struct{}
|
||||||
@@ -36,10 +42,7 @@ func (repo *CartsRepo) CartsAmount(user_id uint) (uint, error) {
|
|||||||
return amt, err
|
return amt, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (repo *CartsRepo) CreateNewCart(user_id uint) (model.CustomerCart, error) {
|
func (repo *CartsRepo) CreateNewCart(user_id uint, name string) (model.CustomerCart, error) {
|
||||||
var name string
|
|
||||||
name = constdata.DEFAULT_NEW_CART_NAME
|
|
||||||
|
|
||||||
cart := model.CustomerCart{
|
cart := model.CustomerCart{
|
||||||
UserID: user_id,
|
UserID: user_id,
|
||||||
Name: &name,
|
Name: &name,
|
||||||
@@ -49,7 +52,15 @@ func (repo *CartsRepo) CreateNewCart(user_id uint) (model.CustomerCart, error) {
|
|||||||
return cart, err
|
return cart, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (repo *CartsRepo) UserHasCart(user_id uint, cart_id uint) (uint, error) {
|
func (repo *CartsRepo) RemoveCart(user_id uint, cart_id uint) error {
|
||||||
|
return db.DB.
|
||||||
|
Table("b2b_customer_carts").
|
||||||
|
Where("cart_id = ? AND user_id = ?", cart_id, user_id).
|
||||||
|
Delete(nil).
|
||||||
|
Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *CartsRepo) UserHasCart(user_id uint, cart_id uint) (bool, error) {
|
||||||
var amt uint
|
var amt uint
|
||||||
|
|
||||||
err := db.DB.
|
err := db.DB.
|
||||||
@@ -59,7 +70,7 @@ func (repo *CartsRepo) UserHasCart(user_id uint, cart_id uint) (uint, error) {
|
|||||||
Scan(&amt).
|
Scan(&amt).
|
||||||
Error
|
Error
|
||||||
|
|
||||||
return amt, err
|
return amt >= 1, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (repo *CartsRepo) UpdateCartName(user_id uint, cart_id uint, new_name string) error {
|
func (repo *CartsRepo) UpdateCartName(user_id uint, cart_id uint, new_name string) error {
|
||||||
@@ -96,7 +107,7 @@ func (repo *CartsRepo) RetrieveCart(user_id uint, cart_id uint) (*model.Customer
|
|||||||
return &cart, err
|
return &cart, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (repo *CartsRepo) CheckProductExists(product_id uint, product_attribute_id *uint) (uint, error) {
|
func (repo *CartsRepo) CheckProductExists(product_id uint, product_attribute_id *uint) (bool, error) {
|
||||||
var amt uint
|
var amt uint
|
||||||
|
|
||||||
if product_attribute_id == nil {
|
if product_attribute_id == nil {
|
||||||
@@ -106,7 +117,7 @@ func (repo *CartsRepo) CheckProductExists(product_id uint, product_attribute_id
|
|||||||
Where("id_product = ?", product_id).
|
Where("id_product = ?", product_id).
|
||||||
Scan(&amt).
|
Scan(&amt).
|
||||||
Error
|
Error
|
||||||
return amt, err
|
return amt >= 1, err
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
err := db.DB.
|
err := db.DB.
|
||||||
@@ -116,18 +127,65 @@ func (repo *CartsRepo) CheckProductExists(product_id uint, product_attribute_id
|
|||||||
Where("ps.id_product = ? AND pas.id_product_attribute = ?", product_id, *product_attribute_id).
|
Where("ps.id_product = ? AND pas.id_product_attribute = ?", product_id, *product_attribute_id).
|
||||||
Scan(&amt).
|
Scan(&amt).
|
||||||
Error
|
Error
|
||||||
return amt, err
|
return amt >= 1, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (repo *CartsRepo) AddProduct(user_id uint, cart_id uint, product_id uint, product_attribute_id *uint, amount uint) error {
|
func (repo *CartsRepo) AddProduct(cart_id uint, product_id uint, product_attribute_id *uint, amount uint, set_amount bool) error {
|
||||||
product := model.CartProduct{
|
var product model.CartProduct
|
||||||
|
|
||||||
|
err := db.DB.
|
||||||
|
Where(&model.CartProduct{
|
||||||
|
CartID: cart_id,
|
||||||
|
ProductID: product_id,
|
||||||
|
ProductAttributeID: product_attribute_id,
|
||||||
|
}).
|
||||||
|
First(&product).Error
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
if amount < 1 {
|
||||||
|
return responseErrors.ErrAmountMustBePositive
|
||||||
|
} else if amount > constdata.MAX_AMOUNT_OF_PRODUCT_IN_CART {
|
||||||
|
return responseErrors.ErrAmountMustBeReasonable
|
||||||
|
}
|
||||||
|
|
||||||
|
product = model.CartProduct{
|
||||||
CartID: cart_id,
|
CartID: cart_id,
|
||||||
ProductID: product_id,
|
ProductID: product_id,
|
||||||
ProductAttributeID: product_attribute_id,
|
ProductAttributeID: product_attribute_id,
|
||||||
Amount: amount,
|
Amount: amount,
|
||||||
}
|
}
|
||||||
err := db.DB.Create(&product).Error
|
|
||||||
|
|
||||||
|
return db.DB.Create(&product).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some other DB error
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Product already exists in cart
|
||||||
|
if set_amount {
|
||||||
|
product.Amount = amount
|
||||||
|
} else {
|
||||||
|
product.Amount = product.Amount + amount
|
||||||
|
}
|
||||||
|
|
||||||
|
if product.Amount < 1 {
|
||||||
|
return responseErrors.ErrAmountMustBePositive
|
||||||
|
} else if product.Amount > constdata.MAX_AMOUNT_OF_PRODUCT_IN_CART {
|
||||||
|
return responseErrors.ErrAmountMustBeReasonable
|
||||||
|
}
|
||||||
|
|
||||||
|
return db.DB.Save(&product).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *CartsRepo) RemoveProduct(cart_id uint, product_id uint, product_attribute_id *uint) error {
|
||||||
|
return db.DB.
|
||||||
|
Where(&model.CartProduct{
|
||||||
|
CartID: cart_id,
|
||||||
|
ProductID: product_id,
|
||||||
|
ProductAttributeID: product_attribute_id,
|
||||||
|
}).
|
||||||
|
Delete(&model.CartProduct{}).Error
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,48 +0,0 @@
|
|||||||
package categoriesRepo
|
|
||||||
|
|
||||||
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"
|
|
||||||
constdata "git.ma-al.com/goc_daniel/b2b/app/utils/const_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
type UICategoriesRepo interface {
|
|
||||||
GetAllCategories(idLang uint) ([]model.ScannedCategory, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type CategoriesRepo struct{}
|
|
||||||
|
|
||||||
func New() UICategoriesRepo {
|
|
||||||
return &CategoriesRepo{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *CategoriesRepo) GetAllCategories(idLang uint) ([]model.ScannedCategory, error) {
|
|
||||||
var allCategories []model.ScannedCategory
|
|
||||||
|
|
||||||
categoryTbl := (&dbmodel.PsCategory{}).TableName()
|
|
||||||
categoryLangTbl := (&dbmodel.PsCategoryLang{}).TableName()
|
|
||||||
categoryShopTbl := (&dbmodel.PsCategoryShop{}).TableName()
|
|
||||||
langTbl := (&dbmodel.PsLang{}).TableName()
|
|
||||||
|
|
||||||
err := db.Get().
|
|
||||||
Model(dbmodel.PsCategory{}).
|
|
||||||
Select(`
|
|
||||||
ps_category.id_category AS category_id,
|
|
||||||
ps_category_lang.name AS name,
|
|
||||||
ps_category.active AS active,
|
|
||||||
ps_category_shop.position AS position,
|
|
||||||
ps_category.id_parent AS id_parent,
|
|
||||||
ps_category.is_root_category AS is_root_category,
|
|
||||||
ps_category_lang.link_rewrite AS link_rewrite,
|
|
||||||
ps_lang.iso_code AS iso_code
|
|
||||||
`).
|
|
||||||
Joins(`LEFT JOIN `+categoryLangTbl+` ON `+categoryLangTbl+`.id_category = `+categoryTbl+`.id_category AND `+categoryLangTbl+`.id_shop = ? AND `+categoryLangTbl+`.id_lang = ?`,
|
|
||||||
constdata.SHOP_ID, idLang).
|
|
||||||
Joins(`LEFT JOIN `+categoryShopTbl+` ON `+categoryShopTbl+`.id_category = `+categoryTbl+`.id_category AND `+categoryShopTbl+`.id_shop = ?`,
|
|
||||||
constdata.SHOP_ID).
|
|
||||||
Joins(`JOIN ` + langTbl + ` ON ` + langTbl + `.id_lang = ` + categoryLangTbl + `.id_lang`).
|
|
||||||
Scan(&allCategories).Error
|
|
||||||
|
|
||||||
return allCategories, err
|
|
||||||
}
|
|
||||||
@@ -2,11 +2,14 @@ package categoryrepo
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"git.ma-al.com/goc_daniel/b2b/app/db"
|
"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"
|
"git.ma-al.com/goc_daniel/b2b/app/model/dbmodel"
|
||||||
|
constdata "git.ma-al.com/goc_daniel/b2b/app/utils/const_data"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UICategoryRepo interface {
|
type UICategoryRepo interface {
|
||||||
GetCategoryTranslations(ids []uint, idLang uint) (map[uint]string, error)
|
GetCategoryTranslations(ids []uint, idLang uint) (map[uint]string, error)
|
||||||
|
RetrieveMenuCategories(idLang uint) ([]model.ScannedCategory, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type CategoryRepo struct{}
|
type CategoryRepo struct{}
|
||||||
@@ -42,3 +45,33 @@ func (r *CategoryRepo) GetCategoryTranslations(ids []uint, idLang uint) (map[uin
|
|||||||
|
|
||||||
return translations, nil
|
return translations, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *CategoryRepo) RetrieveMenuCategories(idLang uint) ([]model.ScannedCategory, error) {
|
||||||
|
var allCategories []model.ScannedCategory
|
||||||
|
|
||||||
|
categoryTbl := (&dbmodel.PsCategory{}).TableName()
|
||||||
|
categoryLangTbl := (&dbmodel.PsCategoryLang{}).TableName()
|
||||||
|
categoryShopTbl := (&dbmodel.PsCategoryShop{}).TableName()
|
||||||
|
langTbl := (&dbmodel.PsLang{}).TableName()
|
||||||
|
|
||||||
|
err := db.Get().
|
||||||
|
Model(dbmodel.PsCategory{}).
|
||||||
|
Select(`
|
||||||
|
ps_category.id_category AS category_id,
|
||||||
|
ps_category_lang.name AS name,
|
||||||
|
ps_category.active AS active,
|
||||||
|
ps_category_shop.position AS position,
|
||||||
|
ps_category.id_parent AS id_parent,
|
||||||
|
ps_category.is_root_category AS is_root_category,
|
||||||
|
ps_category_lang.link_rewrite AS link_rewrite,
|
||||||
|
ps_lang.iso_code AS iso_code
|
||||||
|
`).
|
||||||
|
Joins(`LEFT JOIN `+categoryLangTbl+` ON `+categoryLangTbl+`.id_category = `+categoryTbl+`.id_category AND `+categoryLangTbl+`.id_shop = ? AND `+categoryLangTbl+`.id_lang = ?`,
|
||||||
|
constdata.SHOP_ID, idLang).
|
||||||
|
Joins(`LEFT JOIN `+categoryShopTbl+` ON `+categoryShopTbl+`.id_category = `+categoryTbl+`.id_category AND `+categoryShopTbl+`.id_shop = ?`,
|
||||||
|
constdata.SHOP_ID).
|
||||||
|
Joins(`JOIN ` + langTbl + ` ON ` + langTbl + `.id_lang = ` + categoryLangTbl + `.id_lang`).
|
||||||
|
Scan(&allCategories).Error
|
||||||
|
|
||||||
|
return allCategories, err
|
||||||
|
}
|
||||||
|
|||||||
53
app/repos/currencyRepo/currencyRepo.go
Normal file
53
app/repos/currencyRepo/currencyRepo.go
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
package currencyRepo
|
||||||
|
|
||||||
|
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/utils/query/filters"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/utils/query/find"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UICurrencyRepo interface {
|
||||||
|
CreateConversionRate(currencyRate *model.CurrencyRate) error
|
||||||
|
Get(id uint) (*model.Currency, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type CurrencyRepo struct{}
|
||||||
|
|
||||||
|
func New() UICurrencyRepo {
|
||||||
|
return &CurrencyRepo{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *CurrencyRepo) CreateConversionRate(currencyRate *model.CurrencyRate) error {
|
||||||
|
return db.DB.Create(currencyRate).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *CurrencyRepo) Get(id uint) (*model.Currency, error) {
|
||||||
|
var currency model.Currency
|
||||||
|
|
||||||
|
err := db.DB.Table("b2b_currencies c").
|
||||||
|
Select("c.*, r.conversion_rate").
|
||||||
|
Joins(`
|
||||||
|
LEFT JOIN b2b_currency_rates r
|
||||||
|
ON r.b2b_id_currency = c.id
|
||||||
|
AND r.created_at = (
|
||||||
|
SELECT MAX(created_at)
|
||||||
|
FROM b2b_currency_rates
|
||||||
|
WHERE b2b_id_currency = c.id
|
||||||
|
)
|
||||||
|
`).
|
||||||
|
Where("c.id = ?", id).
|
||||||
|
Scan(¤cy).Error
|
||||||
|
|
||||||
|
return ¤cy, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *CurrencyRepo) Find(langId uint, p find.Paging, filt *filters.FiltersList) (*find.Found[model.Currency], error) {
|
||||||
|
|
||||||
|
found, err := find.Paginate[model.Currency](langId, p, db.DB.
|
||||||
|
Model(&model.Currency{}).
|
||||||
|
Scopes(filt.All()...),
|
||||||
|
)
|
||||||
|
|
||||||
|
return &found, err
|
||||||
|
}
|
||||||
116
app/repos/customerRepo/customerRepo.go
Normal file
116
app/repos/customerRepo/customerRepo.go
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
package customerRepo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"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/utils/query/filters"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/utils/query/find"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UICustomerRepo interface {
|
||||||
|
Get(id uint) (*model.Customer, error)
|
||||||
|
GetByEmail(email string) (*model.Customer, error)
|
||||||
|
GetByExternalProviderId(provider model.AuthProvider, id string) (*model.Customer, error)
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
type CustomerRepo struct{}
|
||||||
|
|
||||||
|
func New() UICustomerRepo {
|
||||||
|
return &CustomerRepo{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *CustomerRepo) Get(id uint) (*model.Customer, error) {
|
||||||
|
var customer model.Customer
|
||||||
|
|
||||||
|
err := db.DB.
|
||||||
|
Preload("Role.Permissions").
|
||||||
|
First(&customer, id).
|
||||||
|
Error
|
||||||
|
|
||||||
|
return &customer, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *CustomerRepo) GetByEmail(email string) (*model.Customer, error) {
|
||||||
|
var customer model.Customer
|
||||||
|
|
||||||
|
err := db.DB.
|
||||||
|
Preload("Role.Permissions").
|
||||||
|
Where("email = ?", email).
|
||||||
|
First(&customer).
|
||||||
|
Error
|
||||||
|
|
||||||
|
return &customer, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *CustomerRepo) GetByExternalProviderId(provider model.AuthProvider, id string) (*model.Customer, error) {
|
||||||
|
var customer model.Customer
|
||||||
|
|
||||||
|
err := db.DB.
|
||||||
|
Preload("Role.Permissions").
|
||||||
|
Where("provider = ? AND provider_id = ?", provider, id).
|
||||||
|
First(&customer).
|
||||||
|
Error
|
||||||
|
|
||||||
|
return &customer, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *CustomerRepo) Find(langId uint, p find.Paging, filt *filters.FiltersList, search string) (*find.Found[model.UserInList], error) {
|
||||||
|
|
||||||
|
query := db.DB.
|
||||||
|
Table("b2b_customers AS users").
|
||||||
|
Select(`
|
||||||
|
users.id AS id,
|
||||||
|
users.email AS email,
|
||||||
|
users.first_name AS first_name,
|
||||||
|
users.last_name AS last_name
|
||||||
|
`)
|
||||||
|
|
||||||
|
if search != "" {
|
||||||
|
words := strings.Fields(search)
|
||||||
|
if len(words) > 5 {
|
||||||
|
words = words[:5]
|
||||||
|
}
|
||||||
|
var conditions []string
|
||||||
|
var args []interface{}
|
||||||
|
for _, word := range words {
|
||||||
|
|
||||||
|
conditions = append(conditions, `
|
||||||
|
(
|
||||||
|
id = ? OR
|
||||||
|
LOWER(first_name) LIKE ? OR
|
||||||
|
LOWER(last_name) LIKE ? OR
|
||||||
|
LOWER(email) LIKE ?)
|
||||||
|
`)
|
||||||
|
|
||||||
|
args = append(args, strings.ToLower(word))
|
||||||
|
for range 3 {
|
||||||
|
args = append(args, fmt.Sprintf("%%%s%%", strings.ToLower(word)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
conditionsQuery := strings.Join(conditions, " AND ")
|
||||||
|
|
||||||
|
query = query.Where(conditionsQuery, args...)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
query = query.Scopes(filt.All()...)
|
||||||
|
|
||||||
|
found, err := find.Paginate[model.UserInList](langId, p, query)
|
||||||
|
|
||||||
|
return &found, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *CustomerRepo) Save(customer *model.Customer) error {
|
||||||
|
return db.DB.Save(customer).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *CustomerRepo) Create(customer *model.Customer) error {
|
||||||
|
return db.DB.Create(customer).Error
|
||||||
|
}
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
package listRepo
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.ma-al.com/goc_daniel/b2b/app/config"
|
|
||||||
"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"
|
|
||||||
"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_marek/gormcol"
|
|
||||||
"github.com/WinterYukky/gorm-extra-clause-plugin/exclause"
|
|
||||||
)
|
|
||||||
|
|
||||||
type UIListRepo interface {
|
|
||||||
ListProducts(id_lang uint, p find.Paging, filt *filters.FiltersList) (find.Found[model.ProductInList], error)
|
|
||||||
ListUsers(id_lang uint, p find.Paging, filt *filters.FiltersList) (find.Found[model.UserInList], error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type ListRepo struct{}
|
|
||||||
|
|
||||||
func New() UIListRepo {
|
|
||||||
return &ListRepo{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (repo *ListRepo) ListProducts(id_lang uint, p find.Paging, filt *filters.FiltersList) (find.Found[model.ProductInList], error) {
|
|
||||||
var list []model.ProductInList
|
|
||||||
var total int64
|
|
||||||
|
|
||||||
query := db.Get().
|
|
||||||
Table(gormcol.Field.Tab(dbmodel.PsProductShopCols.Active)+" AS ps").
|
|
||||||
Select(`
|
|
||||||
ps.id_product AS product_id,
|
|
||||||
pl.name AS name,
|
|
||||||
pl.link_rewrite AS link_rewrite,
|
|
||||||
CONCAT(?, '/', ims.id_image, '-small_default/', pl.link_rewrite, '.webp') AS image_link,
|
|
||||||
cl.name AS category_name,
|
|
||||||
p.reference AS reference,
|
|
||||||
COALESCE(v.variants_number, 0) AS variants_number,
|
|
||||||
sa.quantity AS quantity
|
|
||||||
`, config.Get().Image.ImagePrefix).
|
|
||||||
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_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_product cp ON cp.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").
|
|
||||||
Where("ps.active = ?", 1).
|
|
||||||
Group("ps.id_product").
|
|
||||||
Clauses(exclause.With{CTEs: []exclause.CTE{
|
|
||||||
{
|
|
||||||
Name: "variants",
|
|
||||||
Subquery: exclause.Subquery{DB: db.Get().Model(&dbmodel.PsProductAttributeShop{}).Select("id_product", "COUNT(*) AS variants_number").Group("id_product")},
|
|
||||||
},
|
|
||||||
}})
|
|
||||||
|
|
||||||
// Apply all filters
|
|
||||||
if filt != nil {
|
|
||||||
filt.ApplyAll(query)
|
|
||||||
}
|
|
||||||
|
|
||||||
// run counter first as query is without limit and offset
|
|
||||||
err := query.Count(&total).Error
|
|
||||||
if err != nil {
|
|
||||||
return find.Found[model.ProductInList]{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = query.
|
|
||||||
Order("ps.id_product DESC").
|
|
||||||
Limit(p.Limit()).
|
|
||||||
Offset(p.Offset()).
|
|
||||||
Find(&list).Error
|
|
||||||
if err != nil {
|
|
||||||
return find.Found[model.ProductInList]{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return find.Found[model.ProductInList]{
|
|
||||||
Items: list,
|
|
||||||
Count: uint(total),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (repo *ListRepo) ListUsers(id_lang uint, p find.Paging, filt *filters.FiltersList) (find.Found[model.UserInList], error) {
|
|
||||||
var list []model.UserInList
|
|
||||||
var total int64
|
|
||||||
|
|
||||||
query := db.Get().
|
|
||||||
Table("b2b_customers AS users").
|
|
||||||
Select(`
|
|
||||||
users.id AS id,
|
|
||||||
users.email AS email,
|
|
||||||
users.first_name AS first_name,
|
|
||||||
users.last_name AS last_name,
|
|
||||||
users.role AS role
|
|
||||||
`)
|
|
||||||
|
|
||||||
// Apply all filters
|
|
||||||
if filt != nil {
|
|
||||||
filt.ApplyAll(query)
|
|
||||||
}
|
|
||||||
|
|
||||||
// run counter first as query is without limit and offset
|
|
||||||
err := query.Count(&total).Error
|
|
||||||
if err != nil {
|
|
||||||
return find.Found[model.UserInList]{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = query.
|
|
||||||
Order("users.id DESC").
|
|
||||||
Limit(p.Limit()).
|
|
||||||
Offset(p.Offset()).
|
|
||||||
Find(&list).Error
|
|
||||||
if err != nil {
|
|
||||||
return find.Found[model.UserInList]{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return find.Found[model.UserInList]{
|
|
||||||
Items: list,
|
|
||||||
Count: uint(total),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
@@ -3,6 +3,7 @@ package localeSelectorRepo
|
|||||||
import (
|
import (
|
||||||
"git.ma-al.com/goc_daniel/b2b/app/db"
|
"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/model/dbmodel"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UILocaleSelectorRepo interface {
|
type UILocaleSelectorRepo interface {
|
||||||
@@ -25,7 +26,9 @@ func (r *LocaleSelectorRepo) GetLanguages() ([]model.Language, error) {
|
|||||||
func (r *LocaleSelectorRepo) GetCountriesAndCurrencies() ([]model.Country, error) {
|
func (r *LocaleSelectorRepo) GetCountriesAndCurrencies() ([]model.Country, error) {
|
||||||
var countries []model.Country
|
var countries []model.Country
|
||||||
err := db.Get().
|
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
|
Find(&countries).Error
|
||||||
return countries, err
|
return countries, err
|
||||||
}
|
}
|
||||||
|
|||||||
110
app/repos/ordersRepo/ordersRepo.go
Normal file
110
app/repos/ordersRepo/ordersRepo.go
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
package ordersRepo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"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"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/utils/query/filters"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/utils/query/find"
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
ChangeOrderAddress(order_id uint, country_id uint, address_info string) error
|
||||||
|
ChangeOrderStatus(order_id uint, status string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type OrdersRepo struct{}
|
||||||
|
|
||||||
|
func New() UIOrdersRepo {
|
||||||
|
return &OrdersRepo{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *OrdersRepo) UserHasOrder(user_id uint, order_id uint) (bool, error) {
|
||||||
|
var amt uint
|
||||||
|
|
||||||
|
err := db.DB.
|
||||||
|
Table("b2b_customer_orders").
|
||||||
|
Select("COUNT(*) AS amt").
|
||||||
|
Where("user_id = ? AND order_id = ?", user_id, order_id).
|
||||||
|
Scan(&amt).
|
||||||
|
Error
|
||||||
|
|
||||||
|
return amt >= 1, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *OrdersRepo) Find(user_id uint, p find.Paging, filt *filters.FiltersList) (*find.Found[model.CustomerOrder], error) {
|
||||||
|
var list []model.CustomerOrder
|
||||||
|
var total int64
|
||||||
|
|
||||||
|
query := db.Get().
|
||||||
|
Model(&model.CustomerOrder{}).
|
||||||
|
Preload("Products").
|
||||||
|
Order("b2b_customer_orders.order_id DESC")
|
||||||
|
|
||||||
|
// Apply all filters
|
||||||
|
if filt != nil {
|
||||||
|
filt.ApplyAll(query)
|
||||||
|
}
|
||||||
|
|
||||||
|
// run counter first as query is without limit and offset
|
||||||
|
err := query.Count(&total).Error
|
||||||
|
if err != nil {
|
||||||
|
return &find.Found[model.CustomerOrder]{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = query.
|
||||||
|
Limit(p.Limit()).
|
||||||
|
Offset(p.Offset()).
|
||||||
|
Find(&list).Error
|
||||||
|
if err != nil {
|
||||||
|
return &find.Found[model.CustomerOrder]{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &find.Found[model.CustomerOrder]{
|
||||||
|
Items: list,
|
||||||
|
Count: uint(total),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *OrdersRepo) PlaceNewOrder(cart *model.CustomerCart, name string, country_id uint, address_info string) error {
|
||||||
|
order := model.CustomerOrder{
|
||||||
|
UserID: cart.UserID,
|
||||||
|
Name: name,
|
||||||
|
CountryID: country_id,
|
||||||
|
AddressString: address_info,
|
||||||
|
Status: constdata.NEW_ORDER_STATUS,
|
||||||
|
Products: make([]model.OrderProduct, 0, len(cart.Products)),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, product := range cart.Products {
|
||||||
|
order.Products = append(order.Products, model.OrderProduct{
|
||||||
|
ProductID: product.ProductID,
|
||||||
|
ProductAttributeID: product.ProductAttributeID,
|
||||||
|
Amount: product.Amount,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return db.DB.Create(&order).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *OrdersRepo) ChangeOrderAddress(order_id uint, country_id uint, address_info string) error {
|
||||||
|
return db.DB.
|
||||||
|
Table("b2b_customer_orders").
|
||||||
|
Where("order_id = ?", order_id).
|
||||||
|
Updates(map[string]interface{}{
|
||||||
|
"country_id": country_id,
|
||||||
|
"address_string": address_info,
|
||||||
|
}).
|
||||||
|
Error
|
||||||
|
}
|
||||||
|
|
||||||
|
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).
|
||||||
|
Error
|
||||||
|
}
|
||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
"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/model/dbmodel"
|
"git.ma-al.com/goc_daniel/b2b/app/model/dbmodel"
|
||||||
constdata "git.ma-al.com/goc_daniel/b2b/app/utils/const_data"
|
constdata "git.ma-al.com/goc_daniel/b2b/app/utils/const_data"
|
||||||
"github.com/WinterYukky/gorm-extra-clause-plugin/exclause"
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -17,7 +16,6 @@ type UIProductDescriptionRepo interface {
|
|||||||
GetProductDescription(productID uint, productid_lang uint) (*model.ProductDescription, error)
|
GetProductDescription(productID uint, productid_lang uint) (*model.ProductDescription, error)
|
||||||
CreateIfDoesNotExist(productID uint, productid_lang uint) error
|
CreateIfDoesNotExist(productID uint, productid_lang uint) error
|
||||||
UpdateFields(productID uint, productid_lang uint, updates map[string]string) error
|
UpdateFields(productID uint, productid_lang uint, updates map[string]string) error
|
||||||
GetMeiliProducts(id_lang uint, offset, limit int) ([]model.MeiliSearchProduct, error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProductDescriptionRepo struct{}
|
type ProductDescriptionRepo struct{}
|
||||||
@@ -52,7 +50,7 @@ func (r *ProductDescriptionRepo) GetProductDescription(productID uint, productid
|
|||||||
`+dbmodel.PsProductLangCols.AvailableLater.TabCol()+` AS available_later,
|
`+dbmodel.PsProductLangCols.AvailableLater.TabCol()+` AS available_later,
|
||||||
`+dbmodel.PsProductLangCols.DeliveryInStock.TabCol()+` AS delivery_in_stock,
|
`+dbmodel.PsProductLangCols.DeliveryInStock.TabCol()+` AS delivery_in_stock,
|
||||||
`+dbmodel.PsProductLangCols.DeliveryOutStock.TabCol()+` AS delivery_out_stock,
|
`+dbmodel.PsProductLangCols.DeliveryOutStock.TabCol()+` AS delivery_out_stock,
|
||||||
`+dbmodel.PsProductLangCols.Usage.TabCol()+` AS _usage_,
|
`+dbmodel.PsProductLangCols.Usage.TabCol()+` AS `+"`usage`"+`,
|
||||||
CONCAT(?, '/', `+dbmodel.PsImageShopCols.IDImage.TabCol()+`, '-large_default/', `+dbmodel.PsProductLangCols.LinkRewrite.TabCol()+`, '.webp') AS image_link
|
CONCAT(?, '/', `+dbmodel.PsImageShopCols.IDImage.TabCol()+`, '-large_default/', `+dbmodel.PsProductLangCols.LinkRewrite.TabCol()+`, '.webp') AS image_link
|
||||||
`, config.Get().Image.ImagePrefix).
|
`, config.Get().Image.ImagePrefix).
|
||||||
Joins("JOIN " + dbmodel.TableNamePsImageShop +
|
Joins("JOIN " + dbmodel.TableNamePsImageShop +
|
||||||
@@ -74,10 +72,10 @@ func (r *ProductDescriptionRepo) GetProductDescription(productID uint, productid
|
|||||||
|
|
||||||
// If it doesn't exist, returns an error.
|
// If it doesn't exist, returns an error.
|
||||||
func (r *ProductDescriptionRepo) CreateIfDoesNotExist(productID uint, productid_lang uint) error {
|
func (r *ProductDescriptionRepo) CreateIfDoesNotExist(productID uint, productid_lang uint) error {
|
||||||
record := model.ProductDescription{
|
record := dbmodel.PsProductLang{
|
||||||
ProductID: productID,
|
IDProduct: int32(productID),
|
||||||
ShopID: constdata.SHOP_ID,
|
IDShop: int32(constdata.SHOP_ID),
|
||||||
LangID: productid_lang,
|
IDLang: int32(productid_lang),
|
||||||
}
|
}
|
||||||
|
|
||||||
err := db.Get().
|
err := db.Get().
|
||||||
@@ -118,108 +116,3 @@ func (r *ProductDescriptionRepo) UpdateFields(productID uint, productid_lang uin
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMeiliProductsBatchedScanned returns a batch of products with LIMIT/OFFSET pagination
|
|
||||||
// The scanning is done inside the repo to keep the service layer cleaner
|
|
||||||
func (r *ProductDescriptionRepo) GetMeiliProducts(id_lang uint, offset, limit int) ([]model.MeiliSearchProduct, error) {
|
|
||||||
|
|
||||||
var products []model.MeiliSearchProduct
|
|
||||||
|
|
||||||
err := db.Get().
|
|
||||||
Table("ps_product_shop ps").
|
|
||||||
Select(`
|
|
||||||
ps.id_product AS id_product,
|
|
||||||
pl.name AS name,
|
|
||||||
TRIM(REGEXP_REPLACE(REGEXP_REPLACE(pl.description_short, '<[^>]*>', ' '), '[[:space:]]+', ' ')) AS description,
|
|
||||||
p.ean13,
|
|
||||||
p.reference,
|
|
||||||
ps.price,
|
|
||||||
ps.id_category_default AS id_category,
|
|
||||||
cl.name AS cat_name,
|
|
||||||
cl.link_rewrite AS l_rew,
|
|
||||||
COALESCE(vary.attributes, JSON_OBJECT()) AS attr,
|
|
||||||
COALESCE(feat.features, JSON_OBJECT()) AS feat,
|
|
||||||
img.id_image,
|
|
||||||
cat.category_ids,
|
|
||||||
(SELECT COUNT(*) FROM ps_product_attribute_shop pas2 WHERE pas2.id_product = ps.id_product AND pas2.id_shop = ?) AS variations
|
|
||||||
`, constdata.SHOP_ID).
|
|
||||||
Joins("JOIN ps_product p ON p.id_product = ps.id_product").
|
|
||||||
Joins("JOIN ps_product_lang pl ON pl.id_product = ps.id_product AND pl.id_shop = ? AND pl.id_lang = ?", constdata.SHOP_ID, id_lang).
|
|
||||||
Joins("JOIN ps_category_lang cl ON cl.id_category = ps.id_category_default AND cl.id_shop = ? AND cl.id_lang = ?", constdata.SHOP_ID, id_lang).
|
|
||||||
Joins("LEFT JOIN variations vary ON vary.id_product = ps.id_product").
|
|
||||||
Joins("LEFT JOIN features feat ON feat.id_product = ps.id_product").
|
|
||||||
Joins("LEFT JOIN images img ON img.id_product = ps.id_product").
|
|
||||||
Joins("LEFT JOIN categories cat ON cat.id_product = ps.id_product").
|
|
||||||
Joins("JOIN products_page pp ON pp.id_product = ps.id_product").
|
|
||||||
Where("ps.active = ?", 1).
|
|
||||||
Order("ps.id_product").
|
|
||||||
Clauses(exclause.With{CTEs: []exclause.CTE{
|
|
||||||
{
|
|
||||||
Name: "products_page",
|
|
||||||
Subquery: exclause.Subquery{
|
|
||||||
DB: db.Get().
|
|
||||||
Model(&dbmodel.PsProductShop{}).
|
|
||||||
Select("id_product, price").
|
|
||||||
Where("id_shop = ? AND active = 1", constdata.SHOP_ID).
|
|
||||||
Order("id_product").
|
|
||||||
Limit(limit).
|
|
||||||
Offset(offset),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "variation_attributes",
|
|
||||||
Subquery: exclause.Subquery{
|
|
||||||
DB: db.Get().
|
|
||||||
Table("ps_product_attribute_shop pas"). // <- explicit alias here
|
|
||||||
Select(`
|
|
||||||
pas.id_product,
|
|
||||||
pag.id_attribute_group AS attribute_name,
|
|
||||||
JSON_ARRAYAGG(DISTINCT pa.id_attribute) AS attribute_values
|
|
||||||
`).
|
|
||||||
Joins("JOIN ps_product_attribute_combination ppac ON ppac.id_product_attribute = pas.id_product_attribute").
|
|
||||||
Joins("JOIN ps_attribute pa ON pa.id_attribute = ppac.id_attribute").
|
|
||||||
Joins("JOIN ps_attribute_group pag ON pag.id_attribute_group = pa.id_attribute_group").
|
|
||||||
Where("pas.id_shop = ?", constdata.SHOP_ID).
|
|
||||||
Group("pas.id_product, pag.id_attribute_group"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "variations",
|
|
||||||
Subquery: exclause.Subquery{
|
|
||||||
DB: db.Get().
|
|
||||||
Table("variation_attributes").
|
|
||||||
Select("id_product, JSON_OBJECTAGG(attribute_name, attribute_values) AS attributes").
|
|
||||||
Group("id_product"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "features",
|
|
||||||
Subquery: exclause.Subquery{
|
|
||||||
DB: db.Get().
|
|
||||||
Table("ps_feature_product pfp"). // <- explicit alias
|
|
||||||
Select("pfp.id_product, JSON_OBJECTAGG(pfp.id_feature, pfp.id_feature_value) AS features").
|
|
||||||
Group("pfp.id_product"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "images",
|
|
||||||
Subquery: exclause.Subquery{
|
|
||||||
DB: db.Get().
|
|
||||||
Model(&dbmodel.PsImageShop{}).
|
|
||||||
Select("id_product, id_image").
|
|
||||||
Where("id_shop = ? AND cover = 1", constdata.SHOP_ID),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "categories",
|
|
||||||
Subquery: exclause.Subquery{
|
|
||||||
DB: db.Get().
|
|
||||||
Model(&dbmodel.PsCategoryProduct{}).
|
|
||||||
Select("id_product, JSON_ARRAYAGG(id_category) AS category_ids").
|
|
||||||
Group("id_product"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}}).Find(&products).Error
|
|
||||||
|
|
||||||
return products, err
|
|
||||||
}
|
|
||||||
|
|||||||
300
app/repos/productsRepo/productsRepo.go
Normal file
300
app/repos/productsRepo/productsRepo.go
Normal file
@@ -0,0 +1,300 @@
|
|||||||
|
package productsRepo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/config"
|
||||||
|
"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"
|
||||||
|
"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/view"
|
||||||
|
"git.ma-al.com/goc_marek/gormcol"
|
||||||
|
"github.com/WinterYukky/gorm-extra-clause-plugin/exclause"
|
||||||
|
)
|
||||||
|
|
||||||
|
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)
|
||||||
|
Find(id_lang uint, userID 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)
|
||||||
|
GetBase(p_id_product, p_id_shop, p_id_lang, p_id_customer uint) (view.Product, error)
|
||||||
|
GetPrice(p_id_product uint, productAttributeID *uint, p_id_shop uint, p_id_customer uint, p_id_country uint, p_quantity uint) (view.Price, error)
|
||||||
|
GetVariants(p_id_product, p_id_shop, p_id_lang, p_id_customer, p_id_country, p_quantity uint) ([]view.ProductAttribute, error)
|
||||||
|
AddToFavorites(userID uint, productID uint) error
|
||||||
|
RemoveFromFavorites(userID uint, productID uint) error
|
||||||
|
ExistsInFavorites(userID uint, productID uint) (bool, error)
|
||||||
|
ProductInDatabase(productID uint) (bool, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProductsRepo struct{}
|
||||||
|
|
||||||
|
func New() UIProductsRepo {
|
||||||
|
return &ProductsRepo{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *ProductsRepo) GetBase(p_id_product, p_id_shop, p_id_lang, p_id_customer uint) (view.Product, error) {
|
||||||
|
var result view.Product
|
||||||
|
|
||||||
|
err := db.DB.Raw(`CALL get_product_base(?,?,?,?)`,
|
||||||
|
p_id_product, p_id_shop, p_id_lang, p_id_customer).
|
||||||
|
Scan(&result).Error
|
||||||
|
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *ProductsRepo) GetPrice(
|
||||||
|
p_id_product uint, productAttributeID *uint, p_id_shop uint, p_id_customer uint, p_id_country uint, p_quantity uint,
|
||||||
|
) (view.Price, error) {
|
||||||
|
|
||||||
|
type row struct {
|
||||||
|
Price json.RawMessage `gorm:"column:price"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var r row
|
||||||
|
err := db.DB.Raw(`
|
||||||
|
SELECT fn_product_price(?,?,?,?,?,?) AS price`,
|
||||||
|
p_id_product, p_id_shop, p_id_customer, p_id_country, p_quantity, productAttributeID).
|
||||||
|
Scan(&r).Error
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return view.Price{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var temp struct {
|
||||||
|
Base json.Number `json:"base"`
|
||||||
|
FinalTaxExcl json.Number `json:"final_tax_excl"`
|
||||||
|
FinalTaxIncl json.Number `json:"final_tax_incl"`
|
||||||
|
TaxRate json.Number `json:"tax_rate"`
|
||||||
|
Priority json.Number `json:"priority"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(r.Price, &temp); err != nil {
|
||||||
|
return view.Price{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
price := view.Price{
|
||||||
|
Base: mustParseFloat(temp.Base),
|
||||||
|
FinalTaxExcl: mustParseFloat(temp.FinalTaxExcl),
|
||||||
|
FinalTaxIncl: mustParseFloat(temp.FinalTaxIncl),
|
||||||
|
TaxRate: mustParseFloat(temp.TaxRate),
|
||||||
|
Priority: mustParseInt(temp.Priority),
|
||||||
|
}
|
||||||
|
|
||||||
|
return price, nil
|
||||||
|
}
|
||||||
|
func mustParseFloat(n json.Number) float64 {
|
||||||
|
f, _ := n.Float64()
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
func mustParseInt(n json.Number) int {
|
||||||
|
i, _ := n.Int64()
|
||||||
|
return int(i)
|
||||||
|
}
|
||||||
|
func (repo *ProductsRepo) GetVariants(p_id_product, p_id_shop, p_id_lang, p_id_customer, p_id_country, p_quantity uint) ([]view.ProductAttribute, error) {
|
||||||
|
|
||||||
|
var results []view.ProductAttribute
|
||||||
|
|
||||||
|
err := db.DB.Raw(`
|
||||||
|
CALL get_product_variants(?,?,?,?,?,?)`,
|
||||||
|
p_id_product, p_id_shop, p_id_lang, p_id_customer, p_id_country, p_quantity).
|
||||||
|
Scan(&results).Error
|
||||||
|
|
||||||
|
return results, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *ProductsRepo) Find(langID uint, userID uint, p find.Paging, filt *filters.FiltersList) (*find.Found[model.ProductInList], error) {
|
||||||
|
query := db.DB.
|
||||||
|
Table("base_products AS bp").
|
||||||
|
Clauses(exclause.With{
|
||||||
|
CTEs: []exclause.CTE{
|
||||||
|
{
|
||||||
|
Name: "favorites",
|
||||||
|
Subquery: exclause.Subquery{
|
||||||
|
DB: db.DB.
|
||||||
|
Table("b2b_favorites").
|
||||||
|
Select(`
|
||||||
|
product_id AS product_id,
|
||||||
|
COUNT(*) > 0 AS is_favorite
|
||||||
|
`).
|
||||||
|
Where("user_id = ?", userID).
|
||||||
|
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{
|
||||||
|
DB: db.DB.
|
||||||
|
Table("ps_configuration").
|
||||||
|
Select("CAST(value AS SIGNED) AS days").
|
||||||
|
Where("name = ?", "PS_NB_DAYS_NEW_PRODUCT"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "variants",
|
||||||
|
Subquery: exclause.Subquery{
|
||||||
|
DB: db.DB.
|
||||||
|
Table("ps_product_attribute_shop").
|
||||||
|
Select("id_product, COUNT(*) AS variants_number").
|
||||||
|
Group("id_product"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "base_products",
|
||||||
|
Subquery: exclause.Subquery{
|
||||||
|
DB: db.DB.
|
||||||
|
Table(gormcol.Field.Tab(dbmodel.PsProductShopCols.Active)+" AS ps").
|
||||||
|
Select(`
|
||||||
|
ps.id_product AS product_id,
|
||||||
|
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
|
||||||
|
WHEN ps.date_add >= DATE_SUB(
|
||||||
|
NOW(),
|
||||||
|
INTERVAL COALESCE(npd.days, 20) DAY
|
||||||
|
) AND ps.active = 1
|
||||||
|
THEN 1
|
||||||
|
ELSE 0
|
||||||
|
END AS is_new
|
||||||
|
`).
|
||||||
|
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 = ?", langID).
|
||||||
|
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"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}).
|
||||||
|
Select(`
|
||||||
|
bp.product_id AS product_id,
|
||||||
|
bp.name AS name,
|
||||||
|
pl.link_rewrite AS link_rewrite,
|
||||||
|
CONCAT(?, '/', ims.id_image, '-small_default/', pl.link_rewrite, '.webp') AS image_link,
|
||||||
|
cl.name AS category_name,
|
||||||
|
bp.reference AS reference,
|
||||||
|
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_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").
|
||||||
|
Joins("JOIN ps_category_lang cl ON cl.id_category = bp.category_id AND cl.id_lang = ?", langID).
|
||||||
|
Joins("LEFT JOIN variants v ON v.id_product = bp.product_id").
|
||||||
|
Order("bp.product_id DESC")
|
||||||
|
|
||||||
|
query = query.Scopes(filt.All()...)
|
||||||
|
|
||||||
|
list, err := find.Paginate[model.ProductInList](langID, p, query)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &list, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *ProductsRepo) GetProductVariants(langID uint, productID uint, shopID uint, customerID uint, countryID uint, quantity uint) ([]view.ProductAttribute, error) {
|
||||||
|
var result []view.ProductAttribute
|
||||||
|
err := db.DB.
|
||||||
|
Raw(`
|
||||||
|
CALL get_product_attributes_with_price(?, ?, ?, ?, ?, ?)
|
||||||
|
`,
|
||||||
|
langID,
|
||||||
|
productID,
|
||||||
|
shopID,
|
||||||
|
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
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *ProductsRepo) AddToFavorites(userID uint, productID uint) error {
|
||||||
|
fav := model.B2bFavorite{
|
||||||
|
UserID: userID,
|
||||||
|
ProductID: productID,
|
||||||
|
}
|
||||||
|
return db.Get().Create(&fav).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *ProductsRepo) RemoveFromFavorites(userID uint, productID uint) error {
|
||||||
|
return db.Get().
|
||||||
|
Where("user_id = ? AND product_id = ?", userID, productID).
|
||||||
|
Delete(&model.B2bFavorite{}).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *ProductsRepo) ExistsInFavorites(userID uint, productID uint) (bool, error) {
|
||||||
|
var count int64
|
||||||
|
err := db.Get().
|
||||||
|
Table("b2b_favorites").
|
||||||
|
Where("user_id = ? AND product_id = ?", userID, productID).
|
||||||
|
Count(&count).Error
|
||||||
|
return count >= 1, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *ProductsRepo) ProductInDatabase(productID uint) (bool, error) {
|
||||||
|
var count int64
|
||||||
|
err := db.Get().
|
||||||
|
Table(dbmodel.TableNamePsProduct).
|
||||||
|
Where(dbmodel.PsProductCols.IDProduct.Col()+" = ?", productID).
|
||||||
|
Count(&count).Error
|
||||||
|
return count >= 1, err
|
||||||
|
}
|
||||||
22
app/repos/rolesRepo/rolesRepo.go
Normal file
22
app/repos/rolesRepo/rolesRepo.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package roleRepo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/db"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UIRolesRepo interface {
|
||||||
|
Get(id uint) (*model.Role, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type RolesRepo struct{}
|
||||||
|
|
||||||
|
func New() UIRolesRepo {
|
||||||
|
return &RolesRepo{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RolesRepo) Get(id uint) (*model.Role, error) {
|
||||||
|
var role model.Role
|
||||||
|
err := db.DB.First(&role, id).Error
|
||||||
|
return &role, err
|
||||||
|
}
|
||||||
@@ -7,8 +7,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type UIRoutesRepo interface {
|
type UIRoutesRepo interface {
|
||||||
GetRoutes(langId uint) ([]model.Route, error)
|
GetRoutes(langId uint, roleId uint) ([]model.Route, error)
|
||||||
GetTopMenu(id uint) ([]model.B2BTopMenu, error)
|
GetTopMenu(id uint, roleId uint) ([]model.B2BTopMenu, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type RoutesRepo struct{}
|
type RoutesRepo struct{}
|
||||||
@@ -17,21 +17,30 @@ func New() UIRoutesRepo {
|
|||||||
return &RoutesRepo{}
|
return &RoutesRepo{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *RoutesRepo) GetRoutes(langId uint) ([]model.Route, error) {
|
func (p *RoutesRepo) GetRoutes(langId uint, roleId uint) ([]model.Route, error) {
|
||||||
routes := []model.Route{}
|
routes := []model.Route{}
|
||||||
err := db.DB.Find(&routes, model.Route{Active: nullable.GetNil(true)}).Error
|
|
||||||
if err != nil {
|
err := db.
|
||||||
return nil, err
|
Get().
|
||||||
}
|
Model(model.Route{}).
|
||||||
return routes, nil
|
Joins("JOIN b2b_route_roles rr ON rr.route_id = b2b_routes.id").
|
||||||
|
Where(model.Route{Active: nullable.GetNil(true)}).
|
||||||
|
Where("rr.role_id = ?", roleId).
|
||||||
|
Find(&routes).Error
|
||||||
|
|
||||||
|
return routes, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *RoutesRepo) GetTopMenu(id uint) ([]model.B2BTopMenu, error) {
|
func (p *RoutesRepo) GetTopMenu(langId uint, roleId uint) ([]model.B2BTopMenu, error) {
|
||||||
var menus []model.B2BTopMenu
|
var menus []model.B2BTopMenu
|
||||||
|
|
||||||
err := db.Get().
|
err := db.
|
||||||
Where("active = ?", 1).
|
Get().
|
||||||
Order("parent_id ASC, position ASC").
|
Model(model.B2BTopMenu{}).
|
||||||
|
Joins("JOIN b2b_top_menu_roles tmr ON tmr.top_menu_id = b2b_top_menu.menu_id").
|
||||||
|
Where(model.B2BTopMenu{Active: 1}).
|
||||||
|
Where("tmr.role_id = ?", roleId).
|
||||||
|
Order("b2b_top_menu.parent_id ASC, b2b_top_menu.position ASC").
|
||||||
Find(&menus).Error
|
Find(&menus).Error
|
||||||
|
|
||||||
return menus, err
|
return menus, err
|
||||||
|
|||||||
@@ -7,7 +7,11 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"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/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/model/dbmodel"
|
||||||
|
constdata "git.ma-al.com/goc_daniel/b2b/app/utils/const_data"
|
||||||
|
"github.com/WinterYukky/gorm-extra-clause-plugin/exclause"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SearchProxyResponse struct {
|
type SearchProxyResponse struct {
|
||||||
@@ -17,6 +21,7 @@ type SearchProxyResponse struct {
|
|||||||
|
|
||||||
type UISearchRepo interface {
|
type UISearchRepo interface {
|
||||||
Search(index string, body []byte) (*SearchProxyResponse, error)
|
Search(index string, body []byte) (*SearchProxyResponse, error)
|
||||||
|
GetMeiliProducts(id_lang uint, offset, limit int) ([]model.MeiliSearchProduct, error)
|
||||||
GetIndexSettings(index string) (*SearchProxyResponse, error)
|
GetIndexSettings(index string) (*SearchProxyResponse, error)
|
||||||
GetRoutes(langId uint) ([]model.Route, error)
|
GetRoutes(langId uint) ([]model.Route, error)
|
||||||
}
|
}
|
||||||
@@ -80,3 +85,108 @@ func (r *SearchRepo) doRequest(method, url string, body []byte) (*SearchProxyRes
|
|||||||
func (r *SearchRepo) GetRoutes(langId uint) ([]model.Route, error) {
|
func (r *SearchRepo) GetRoutes(langId uint) ([]model.Route, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetMeiliProductsProducts returns a batch of products with LIMIT/OFFSET pagination
|
||||||
|
// The scanning is done inside the repo to keep the service layer cleaner
|
||||||
|
func (r *SearchRepo) GetMeiliProducts(id_lang uint, offset, limit int) ([]model.MeiliSearchProduct, error) {
|
||||||
|
|
||||||
|
var products []model.MeiliSearchProduct
|
||||||
|
|
||||||
|
err := db.Get().
|
||||||
|
Table("ps_product_shop ps").
|
||||||
|
Select(`
|
||||||
|
ps.id_product AS id_product,
|
||||||
|
pl.name AS name,
|
||||||
|
TRIM(REGEXP_REPLACE(REGEXP_REPLACE(pl.description_short, '<[^>]*>', ' '), '[[:space:]]+', ' ')) AS description,
|
||||||
|
p.ean13,
|
||||||
|
p.reference,
|
||||||
|
ps.price,
|
||||||
|
ps.id_category_default AS id_category,
|
||||||
|
cl.name AS cat_name,
|
||||||
|
cl.link_rewrite AS l_rew,
|
||||||
|
COALESCE(vary.attributes, JSON_OBJECT()) AS attr,
|
||||||
|
COALESCE(feat.features, JSON_OBJECT()) AS feat,
|
||||||
|
img.id_image,
|
||||||
|
cat.category_ids,
|
||||||
|
(SELECT COUNT(*) FROM ps_product_attribute_shop pas2 WHERE pas2.id_product = ps.id_product AND pas2.id_shop = ?) AS variations
|
||||||
|
`, constdata.SHOP_ID).
|
||||||
|
Joins("JOIN ps_product p ON p.id_product = ps.id_product").
|
||||||
|
Joins("JOIN ps_product_lang pl ON pl.id_product = ps.id_product AND pl.id_shop = ? AND pl.id_lang = ?", constdata.SHOP_ID, id_lang).
|
||||||
|
Joins("JOIN ps_category_lang cl ON cl.id_category = ps.id_category_default AND cl.id_shop = ? AND cl.id_lang = ?", constdata.SHOP_ID, id_lang).
|
||||||
|
Joins("LEFT JOIN variations vary ON vary.id_product = ps.id_product").
|
||||||
|
Joins("LEFT JOIN features feat ON feat.id_product = ps.id_product").
|
||||||
|
Joins("LEFT JOIN images img ON img.id_product = ps.id_product").
|
||||||
|
Joins("LEFT JOIN categories cat ON cat.id_product = ps.id_product").
|
||||||
|
Joins("JOIN products_page pp ON pp.id_product = ps.id_product").
|
||||||
|
Where("ps.active = ?", 1).
|
||||||
|
Order("ps.id_product").
|
||||||
|
Clauses(exclause.With{CTEs: []exclause.CTE{
|
||||||
|
{
|
||||||
|
Name: "products_page",
|
||||||
|
Subquery: exclause.Subquery{
|
||||||
|
DB: db.Get().
|
||||||
|
Model(&dbmodel.PsProductShop{}).
|
||||||
|
Select("id_product, price").
|
||||||
|
Where("id_shop = ? AND active = 1", constdata.SHOP_ID).
|
||||||
|
Order("id_product").
|
||||||
|
Limit(limit).
|
||||||
|
Offset(offset),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "variation_attributes",
|
||||||
|
Subquery: exclause.Subquery{
|
||||||
|
DB: db.Get().
|
||||||
|
Table("ps_product_attribute_shop pas"). // <- explicit alias here
|
||||||
|
Select(`
|
||||||
|
pas.id_product,
|
||||||
|
pag.id_attribute_group AS attribute_name,
|
||||||
|
JSON_ARRAYAGG(DISTINCT pa.id_attribute) AS attribute_values
|
||||||
|
`).
|
||||||
|
Joins("JOIN ps_product_attribute_combination ppac ON ppac.id_product_attribute = pas.id_product_attribute").
|
||||||
|
Joins("JOIN ps_attribute pa ON pa.id_attribute = ppac.id_attribute").
|
||||||
|
Joins("JOIN ps_attribute_group pag ON pag.id_attribute_group = pa.id_attribute_group").
|
||||||
|
Where("pas.id_shop = ?", constdata.SHOP_ID).
|
||||||
|
Group("pas.id_product, pag.id_attribute_group"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "variations",
|
||||||
|
Subquery: exclause.Subquery{
|
||||||
|
DB: db.Get().
|
||||||
|
Table("variation_attributes").
|
||||||
|
Select("id_product, JSON_OBJECTAGG(attribute_name, attribute_values) AS attributes").
|
||||||
|
Group("id_product"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "features",
|
||||||
|
Subquery: exclause.Subquery{
|
||||||
|
DB: db.Get().
|
||||||
|
Table("ps_feature_product pfp"). // <- explicit alias
|
||||||
|
Select("pfp.id_product, JSON_OBJECTAGG(pfp.id_feature, pfp.id_feature_value) AS features").
|
||||||
|
Group("pfp.id_product"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "images",
|
||||||
|
Subquery: exclause.Subquery{
|
||||||
|
DB: db.Get().
|
||||||
|
Model(&dbmodel.PsImageShop{}).
|
||||||
|
Select("id_product, id_image").
|
||||||
|
Where("id_shop = ? AND cover = 1", constdata.SHOP_ID),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "categories",
|
||||||
|
Subquery: exclause.Subquery{
|
||||||
|
DB: db.Get().
|
||||||
|
Model(&dbmodel.PsCategoryProduct{}).
|
||||||
|
Select("id_product, JSON_ARRAYAGG(id_category) AS category_ids").
|
||||||
|
Group("id_product"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}).Find(&products).Error
|
||||||
|
|
||||||
|
return products, err
|
||||||
|
}
|
||||||
|
|||||||
247
app/repos/specificPriceRepo/specificPriceRepo.go
Normal file
247
app/repos/specificPriceRepo/specificPriceRepo.go
Normal file
@@ -0,0 +1,247 @@
|
|||||||
|
package specificPriceRepo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/db"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/model"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UISpecificPriceRepo interface {
|
||||||
|
Create(ctx context.Context, pr *model.SpecificPrice) error
|
||||||
|
Update(ctx context.Context, pr *model.SpecificPrice) error
|
||||||
|
Delete(ctx context.Context, id uint64) error
|
||||||
|
GetByID(ctx context.Context, id uint64) (*model.SpecificPrice, error)
|
||||||
|
List(ctx context.Context) ([]*model.SpecificPrice, error)
|
||||||
|
SetActive(ctx context.Context, id uint64, active bool) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type SpecificPriceRepo struct{}
|
||||||
|
|
||||||
|
func New() UISpecificPriceRepo {
|
||||||
|
return &SpecificPriceRepo{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *SpecificPriceRepo) Create(ctx context.Context, pr *model.SpecificPrice) error {
|
||||||
|
return db.DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||||
|
now := time.Now()
|
||||||
|
pr.CreatedAt = &now
|
||||||
|
|
||||||
|
if err := tx.Create(pr).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return repo.insertRelations(tx, pr)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *SpecificPriceRepo) Update(ctx context.Context, pr *model.SpecificPrice) error {
|
||||||
|
return db.DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||||
|
now := time.Now()
|
||||||
|
pr.UpdatedAt = &now
|
||||||
|
|
||||||
|
if err := tx.Save(pr).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := repo.clearRelations(tx, pr.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return repo.insertRelations(tx, pr)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *SpecificPriceRepo) GetByID(ctx context.Context, id uint64) (*model.SpecificPrice, error) {
|
||||||
|
var pr model.SpecificPrice
|
||||||
|
err := db.DB.WithContext(ctx).Where("id = ?", id).First(&pr).Error
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := repo.loadRelations(ctx, &pr); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &pr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *SpecificPriceRepo) List(ctx context.Context) ([]*model.SpecificPrice, error) {
|
||||||
|
var specificPrices []*model.SpecificPrice
|
||||||
|
err := db.DB.WithContext(ctx).Find(&specificPrices).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range specificPrices {
|
||||||
|
if err := repo.loadRelations(ctx, specificPrices[i]); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return specificPrices, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *SpecificPriceRepo) SetActive(ctx context.Context, id uint64, active bool) error {
|
||||||
|
return db.DB.WithContext(ctx).Model(&model.SpecificPrice{}).Where("id = ?", id).Update("is_active", active).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *SpecificPriceRepo) Delete(ctx context.Context, id uint64) error {
|
||||||
|
return db.DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||||
|
if err := tx.Exec("DELETE FROM b2b_specific_price_product WHERE b2b_specific_price_id = ?", id).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := tx.Exec("DELETE FROM b2b_specific_price_category WHERE b2b_specific_price_id = ?", id).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := tx.Exec("DELETE FROM b2b_specific_price_product_attribute WHERE b2b_specific_price_id = ?", id).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := tx.Exec("DELETE FROM b2b_specific_price_country WHERE b2b_specific_price_id = ?", id).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := tx.Exec("DELETE FROM b2b_specific_price_customer WHERE b2b_specific_price_id = ?", id).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := tx.Delete(&model.SpecificPrice{}, "id = ?", id).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *SpecificPriceRepo) insertRelations(tx *gorm.DB, pr *model.SpecificPrice) error {
|
||||||
|
if len(pr.ProductIDs) > 0 {
|
||||||
|
for _, productID := range pr.ProductIDs {
|
||||||
|
if err := tx.Exec(`
|
||||||
|
INSERT INTO b2b_specific_price_product (b2b_specific_price_id, id_product) VALUES (?, ?)
|
||||||
|
`, pr.ID, productID).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(pr.CategoryIDs) > 0 {
|
||||||
|
for _, categoryID := range pr.CategoryIDs {
|
||||||
|
if err := tx.Exec(`
|
||||||
|
INSERT INTO b2b_specific_price_category (b2b_specific_price_id, id_category) VALUES (?, ?)
|
||||||
|
`, pr.ID, categoryID).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(pr.ProductAttributeIDs) > 0 {
|
||||||
|
for _, attrID := range pr.ProductAttributeIDs {
|
||||||
|
if err := tx.Exec(`
|
||||||
|
INSERT INTO b2b_specific_price_product_attribute (b2b_specific_price_id, id_product_attribute) VALUES (?, ?)
|
||||||
|
`, pr.ID, attrID).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(pr.CountryIDs) > 0 {
|
||||||
|
for _, countryID := range pr.CountryIDs {
|
||||||
|
if err := tx.Exec(`
|
||||||
|
INSERT INTO b2b_specific_price_country (b2b_specific_price_id, b2b_id_country) VALUES (?, ?)
|
||||||
|
`, pr.ID, countryID).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(pr.CustomerIDs) > 0 {
|
||||||
|
for _, customerID := range pr.CustomerIDs {
|
||||||
|
if err := tx.Exec(`
|
||||||
|
INSERT INTO b2b_specific_price_customer (b2b_specific_price_id, b2b_id_customer) VALUES (?, ?)
|
||||||
|
`, pr.ID, customerID).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *SpecificPriceRepo) clearRelations(tx *gorm.DB, id uint64) error {
|
||||||
|
if err := tx.Exec("DELETE FROM b2b_specific_price_product WHERE b2b_specific_price_id = ?", id).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := tx.Exec("DELETE FROM b2b_specific_price_category WHERE b2b_specific_price_id = ?", id).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := tx.Exec("DELETE FROM b2b_specific_price_product_attribute WHERE b2b_specific_price_id = ?", id).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := tx.Exec("DELETE FROM b2b_specific_price_country WHERE b2b_specific_price_id = ?", id).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := tx.Exec("DELETE FROM b2b_specific_price_customer WHERE b2b_specific_price_id = ?", id).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *SpecificPriceRepo) loadRelations(ctx context.Context, pr *model.SpecificPrice) error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
var productIDs []struct {
|
||||||
|
IDProduct uint64 `gorm:"column:id_product"`
|
||||||
|
}
|
||||||
|
if err = db.DB.WithContext(ctx).Table("b2b_specific_price_product").Where("b2b_specific_price_id = ?", pr.ID).Select("id_product").Scan(&productIDs).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, p := range productIDs {
|
||||||
|
pr.ProductIDs = append(pr.ProductIDs, p.IDProduct)
|
||||||
|
}
|
||||||
|
|
||||||
|
var categoryIDs []struct {
|
||||||
|
IDCategory uint64 `gorm:"column:id_category"`
|
||||||
|
}
|
||||||
|
if err = db.DB.WithContext(ctx).Table("b2b_specific_price_category").Where("b2b_specific_price_id = ?", pr.ID).Select("id_category").Scan(&categoryIDs).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, c := range categoryIDs {
|
||||||
|
pr.CategoryIDs = append(pr.CategoryIDs, c.IDCategory)
|
||||||
|
}
|
||||||
|
|
||||||
|
var attrIDs []struct {
|
||||||
|
IDAttr uint64 `gorm:"column:id_product_attribute"`
|
||||||
|
}
|
||||||
|
if err = db.DB.WithContext(ctx).Table("b2b_specific_price_product_attribute").Where("b2b_specific_price_id = ?", pr.ID).Select("id_product_attribute").Scan(&attrIDs).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, a := range attrIDs {
|
||||||
|
pr.ProductAttributeIDs = append(pr.ProductAttributeIDs, a.IDAttr)
|
||||||
|
}
|
||||||
|
|
||||||
|
var countryIDs []struct {
|
||||||
|
IDCountry uint64 `gorm:"column:b2b_id_country"`
|
||||||
|
}
|
||||||
|
if err = db.DB.WithContext(ctx).Table("b2b_specific_price_country").Where("b2b_specific_price_id = ?", pr.ID).Select("b2b_id_country").Scan(&countryIDs).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, c := range countryIDs {
|
||||||
|
pr.CountryIDs = append(pr.CountryIDs, c.IDCountry)
|
||||||
|
}
|
||||||
|
|
||||||
|
var customerIDs []struct {
|
||||||
|
IDCustomer uint64 `gorm:"column:b2b_id_customer"`
|
||||||
|
}
|
||||||
|
if err = db.DB.WithContext(ctx).Table("b2b_specific_price_customer").Where("b2b_specific_price_id = ?", pr.ID).Select("b2b_id_customer").Scan(&customerIDs).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, c := range customerIDs {
|
||||||
|
pr.CustomerIDs = append(pr.CustomerIDs, c.IDCustomer)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
145
app/service/addressesService/addressesService.go
Normal file
145
app/service/addressesService/addressesService.go
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
package addressesService
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/model"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/repos/addressesRepo"
|
||||||
|
constdata "git.ma-al.com/goc_daniel/b2b/app/utils/const_data"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AddressesService struct {
|
||||||
|
repo addressesRepo.UIAddressesRepo
|
||||||
|
}
|
||||||
|
|
||||||
|
func New() *AddressesService {
|
||||||
|
return &AddressesService{
|
||||||
|
repo: addressesRepo.New(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AddressesService) GetTemplate(country_id uint) (model.AddressUnparsed, error) {
|
||||||
|
switch country_id {
|
||||||
|
|
||||||
|
case 1: // Poland
|
||||||
|
return model.AddressPL{}, nil
|
||||||
|
|
||||||
|
case 2: // Great Britain
|
||||||
|
return model.AddressGB{}, nil
|
||||||
|
|
||||||
|
case 3: // Czech Republic
|
||||||
|
return model.AddressCZ{}, nil
|
||||||
|
|
||||||
|
case 4: // Germany
|
||||||
|
return model.AddressDE{}, nil
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, responseErrors.ErrInvalidCountryID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AddressesService) AddNewAddress(user_id uint, address_info string, country_id uint) error {
|
||||||
|
amt, err := s.repo.UserAddressesAmt(user_id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if amt >= constdata.MAX_AMOUNT_OF_ADDRESSES_PER_USER {
|
||||||
|
return responseErrors.ErrMaxAmtOfAddressesReached
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = s.ValidateAddressJson(address_info, country_id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.repo.AddNewAddress(user_id, address_info, country_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// country_id = 0 means that country_id remains unchanged
|
||||||
|
func (s *AddressesService) ModifyAddress(user_id uint, address_id uint, address_info string, country_id uint) error {
|
||||||
|
amt, err := s.repo.UserHasAddress(user_id, address_id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if amt != 1 {
|
||||||
|
return responseErrors.ErrUserHasNoSuchAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = s.ValidateAddressJson(address_info, country_id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.repo.UpdateAddress(user_id, address_id, address_info, country_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AddressesService) RetrieveAddresses(user_id uint) (*[]model.Address, error) {
|
||||||
|
addresses, err := s.repo.RetrieveAddresses(user_id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(*addresses); i++ {
|
||||||
|
address_unparsed, err := s.ValidateAddressJson((*addresses)[i].AddressString, (*addresses)[i].CountryID)
|
||||||
|
// log such errors
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("err: %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
(*addresses)[i].AddressUnparsed = &address_unparsed
|
||||||
|
}
|
||||||
|
|
||||||
|
return addresses, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AddressesService) DeleteAddress(user_id uint, address_id uint) error {
|
||||||
|
amt, err := s.repo.UserHasAddress(user_id, address_id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if amt != 1 {
|
||||||
|
return responseErrors.ErrUserHasNoSuchAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.repo.DeleteAddress(user_id, address_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateAddressJson makes sure that the info string represents a valid json of address in given country
|
||||||
|
func (s *AddressesService) ValidateAddressJson(info string, country_id uint) (model.AddressUnparsed, error) {
|
||||||
|
dec := json.NewDecoder(strings.NewReader(info))
|
||||||
|
dec.DisallowUnknownFields()
|
||||||
|
|
||||||
|
switch country_id {
|
||||||
|
|
||||||
|
case 1: // Poland
|
||||||
|
var address model.AddressPL
|
||||||
|
if err := dec.Decode(&address); err != nil {
|
||||||
|
return address, responseErrors.ErrInvalidAddressJSON
|
||||||
|
}
|
||||||
|
return address, nil
|
||||||
|
|
||||||
|
case 2: // Great Britain
|
||||||
|
var address model.AddressGB
|
||||||
|
if err := dec.Decode(&address); err != nil {
|
||||||
|
return address, responseErrors.ErrInvalidAddressJSON
|
||||||
|
}
|
||||||
|
return address, nil
|
||||||
|
|
||||||
|
case 3: // Czech Republic
|
||||||
|
var address model.AddressCZ
|
||||||
|
if err := dec.Decode(&address); err != nil {
|
||||||
|
return address, responseErrors.ErrInvalidAddressJSON
|
||||||
|
}
|
||||||
|
return address, nil
|
||||||
|
|
||||||
|
case 4: // Germany
|
||||||
|
var address model.AddressDE
|
||||||
|
if err := dec.Decode(&address); err != nil {
|
||||||
|
return address, responseErrors.ErrInvalidAddressJSON
|
||||||
|
}
|
||||||
|
return address, nil
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, responseErrors.ErrInvalidCountryID
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,8 @@ 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/db"
|
"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/customerRepo"
|
||||||
|
roleRepo "git.ma-al.com/goc_daniel/b2b/app/repos/rolesRepo"
|
||||||
"git.ma-al.com/goc_daniel/b2b/app/service/emailService"
|
"git.ma-al.com/goc_daniel/b2b/app/service/emailService"
|
||||||
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/responseErrors"
|
"git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors"
|
||||||
@@ -19,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
|
||||||
@@ -26,7 +29,7 @@ type JWTClaims struct {
|
|||||||
UserID uint `json:"user_id"`
|
UserID uint `json:"user_id"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
Role model.CustomerRole `json:"customer_role"`
|
Role string `json:"customer_role"`
|
||||||
CartsIDs []uint `json:"carts_ids"`
|
CartsIDs []uint `json:"carts_ids"`
|
||||||
LangID uint `json:"lang_id"`
|
LangID uint `json:"lang_id"`
|
||||||
CountryID uint `json:"country_id"`
|
CountryID uint `json:"country_id"`
|
||||||
@@ -38,6 +41,8 @@ type AuthService struct {
|
|||||||
db *gorm.DB
|
db *gorm.DB
|
||||||
config *config.AuthConfig
|
config *config.AuthConfig
|
||||||
email *emailService.EmailService
|
email *emailService.EmailService
|
||||||
|
customerRepo customerRepo.UICustomerRepo
|
||||||
|
roleRepo roleRepo.UIRolesRepo
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAuthService creates a new AuthService instance
|
// NewAuthService creates a new AuthService instance
|
||||||
@@ -46,6 +51,8 @@ func NewAuthService() *AuthService {
|
|||||||
db: db.Get(),
|
db: db.Get(),
|
||||||
config: &config.Get().Auth,
|
config: &config.Get().Auth,
|
||||||
email: emailService.NewEmailService(),
|
email: emailService.NewEmailService(),
|
||||||
|
customerRepo: customerRepo.New(),
|
||||||
|
roleRepo: roleRepo.New(),
|
||||||
}
|
}
|
||||||
// Auto-migrate the refresh_tokens table
|
// Auto-migrate the refresh_tokens table
|
||||||
if svc.db != nil {
|
if svc.db != nil {
|
||||||
@@ -59,7 +66,7 @@ func (s *AuthService) Login(req *model.LoginRequest) (*model.AuthResponse, strin
|
|||||||
var user model.Customer
|
var user model.Customer
|
||||||
|
|
||||||
// Find user by email
|
// Find user by email
|
||||||
if err := s.db.Where("email = ?", req.Email).First(&user).Error; err != nil {
|
if err := s.db.Preload("Role.Permissions").Where("email = ?", req.Email).First(&user).Error; err != nil {
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return nil, "", responseErrors.ErrInvalidCredentials
|
return nil, "", responseErrors.ErrInvalidCredentials
|
||||||
}
|
}
|
||||||
@@ -153,7 +160,6 @@ func (s *AuthService) Register(req *model.RegisterRequest) error {
|
|||||||
Password: string(hashedPassword),
|
Password: string(hashedPassword),
|
||||||
FirstName: req.FirstName,
|
FirstName: req.FirstName,
|
||||||
LastName: req.LastName,
|
LastName: req.LastName,
|
||||||
Role: model.RoleUser,
|
|
||||||
Provider: model.ProviderLocal,
|
Provider: model.ProviderLocal,
|
||||||
IsActive: false,
|
IsActive: false,
|
||||||
EmailVerified: false,
|
EmailVerified: false,
|
||||||
@@ -431,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.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
|
||||||
}
|
}
|
||||||
@@ -511,7 +517,7 @@ func (s *AuthService) generateAccessToken(user *model.Customer) (string, error)
|
|||||||
UserID: user.ID,
|
UserID: user.ID,
|
||||||
Email: user.Email,
|
Email: user.Email,
|
||||||
Username: user.Email,
|
Username: user.Email,
|
||||||
Role: user.Role,
|
Role: user.Role.Name,
|
||||||
CartsIDs: []uint{},
|
CartsIDs: []uint{},
|
||||||
LangID: user.LangID,
|
LangID: user.LangID,
|
||||||
CountryID: user.CountryID,
|
CountryID: user.CountryID,
|
||||||
|
|||||||
@@ -108,26 +108,32 @@ func (s *AuthService) HandleGoogleCallback(code string) (*model.AuthResponse, st
|
|||||||
// findOrCreateGoogleUser finds an existing user by Google provider ID or email,
|
// findOrCreateGoogleUser finds an existing user by Google provider ID or email,
|
||||||
// or creates a new one.
|
// or creates a new one.
|
||||||
func (s *AuthService) findOrCreateGoogleUser(info *view.GoogleUserInfo) (*model.Customer, error) {
|
func (s *AuthService) findOrCreateGoogleUser(info *view.GoogleUserInfo) (*model.Customer, error) {
|
||||||
var user model.Customer
|
var user *model.Customer
|
||||||
|
|
||||||
// Try to find by provider + provider_id
|
// Try to find by provider + provider_id
|
||||||
err := s.db.Where("provider = ? AND provider_id = ?", model.ProviderGoogle, info.ID).First(&user).Error
|
user, err := s.customerRepo.GetByExternalProviderId(model.ProviderGoogle, info.ID)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// Update avatar in case it changed
|
// Update avatar in case it changed
|
||||||
user.AvatarURL = info.Picture
|
user.AvatarURL = info.Picture
|
||||||
s.db.Save(&user)
|
err = s.customerRepo.Save(user)
|
||||||
return &user, nil
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to find by email (user may have registered locally before)
|
// Try to find by email (user may have registered locally before)
|
||||||
err = s.db.Where("email = ?", info.Email).First(&user).Error
|
user, err = s.customerRepo.GetByEmail(info.Email)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// Link Google provider to existing account
|
// Link Google provider to existing account
|
||||||
user.Provider = model.ProviderGoogle
|
user.Provider = model.ProviderGoogle
|
||||||
user.ProviderID = info.ID
|
user.ProviderID = info.ID
|
||||||
user.AvatarURL = info.Picture
|
user.AvatarURL = info.Picture
|
||||||
user.IsActive = true
|
user.IsActive = true
|
||||||
s.db.Save(&user)
|
err = s.customerRepo.Save(user)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// If email has not been verified yet, send email to admin.
|
// If email has not been verified yet, send email to admin.
|
||||||
if !user.EmailVerified {
|
if !user.EmailVerified {
|
||||||
@@ -139,7 +145,7 @@ func (s *AuthService) findOrCreateGoogleUser(info *view.GoogleUserInfo) (*model.
|
|||||||
}
|
}
|
||||||
user.EmailVerified = true
|
user.EmailVerified = true
|
||||||
|
|
||||||
return &user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create new user
|
// Create new user
|
||||||
@@ -148,16 +154,16 @@ func (s *AuthService) findOrCreateGoogleUser(info *view.GoogleUserInfo) (*model.
|
|||||||
FirstName: info.GivenName,
|
FirstName: info.GivenName,
|
||||||
LastName: info.FamilyName,
|
LastName: info.FamilyName,
|
||||||
Provider: model.ProviderGoogle,
|
Provider: model.ProviderGoogle,
|
||||||
|
RoleID: 1, // user
|
||||||
ProviderID: info.ID,
|
ProviderID: info.ID,
|
||||||
AvatarURL: info.Picture,
|
AvatarURL: info.Picture,
|
||||||
Role: model.RoleUser,
|
|
||||||
IsActive: true,
|
IsActive: true,
|
||||||
EmailVerified: true,
|
EmailVerified: true,
|
||||||
LangID: 2, // default is english
|
LangID: 2, // default is english
|
||||||
CountryID: 2, // default is England
|
CountryID: 2, // default is England
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.db.Create(&newUser).Error; err != nil {
|
if err := s.customerRepo.Create(&newUser); err != nil {
|
||||||
return nil, fmt.Errorf("failed to create user: %w", err)
|
return nil, fmt.Errorf("failed to create user: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,6 +176,13 @@ func (s *AuthService) findOrCreateGoogleUser(info *view.GoogleUserInfo) (*model.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var role *model.Role
|
||||||
|
role, err = s.roleRepo.Get(newUser.RoleID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
newUser.Role = role
|
||||||
|
|
||||||
return &newUser, nil
|
return &newUser, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,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
|
var cart model.CustomerCart
|
||||||
|
|
||||||
customers_carts_amount, err := s.repo.CartsAmount(user_id)
|
customers_carts_amount, err := s.repo.CartsAmount(user_id)
|
||||||
@@ -28,18 +28,34 @@ func (s *CartsService) CreateNewCart(user_id uint) (model.CustomerCart, error) {
|
|||||||
return cart, responseErrors.ErrMaxAmtOfCartsReached
|
return cart, responseErrors.ErrMaxAmtOfCartsReached
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if name == "" {
|
||||||
|
name = constdata.DEFAULT_NEW_CART_NAME
|
||||||
|
}
|
||||||
|
|
||||||
// create new cart for customer
|
// create new cart for customer
|
||||||
cart, err = s.repo.CreateNewCart(user_id)
|
cart, err = s.repo.CreateNewCart(user_id, name)
|
||||||
|
|
||||||
return cart, nil
|
return cart, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *CartsService) UpdateCartName(user_id uint, cart_id uint, new_name string) error {
|
func (s *CartsService) RemoveCart(user_id uint, cart_id uint) error {
|
||||||
amt, err := s.repo.UserHasCart(user_id, cart_id)
|
exists, err := s.repo.UserHasCart(user_id, cart_id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if amt != 1 {
|
if !exists {
|
||||||
|
return responseErrors.ErrUserHasNoSuchCart
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.repo.RemoveCart(user_id, cart_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CartsService) UpdateCartName(user_id uint, cart_id uint, new_name string) error {
|
||||||
|
exists, err := s.repo.UserHasCart(user_id, cart_id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
return responseErrors.ErrUserHasNoSuchCart
|
return responseErrors.ErrUserHasNoSuchCart
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,33 +67,45 @@ func (s *CartsService) RetrieveCartsInfo(user_id uint) ([]model.CustomerCart, er
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *CartsService) RetrieveCart(user_id uint, cart_id uint) (*model.CustomerCart, error) {
|
func (s *CartsService) RetrieveCart(user_id uint, cart_id uint) (*model.CustomerCart, error) {
|
||||||
amt, err := s.repo.UserHasCart(user_id, cart_id)
|
exists, err := s.repo.UserHasCart(user_id, cart_id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if amt != 1 {
|
if !exists {
|
||||||
return nil, responseErrors.ErrUserHasNoSuchCart
|
return nil, responseErrors.ErrUserHasNoSuchCart
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.repo.RetrieveCart(user_id, cart_id)
|
return s.repo.RetrieveCart(user_id, cart_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *CartsService) AddProduct(user_id uint, cart_id uint, product_id uint, product_attribute_id *uint, amount uint) error {
|
func (s *CartsService) AddProduct(user_id uint, cart_id uint, product_id uint, product_attribute_id *uint, amount int, set_amount bool) error {
|
||||||
amt, err := s.repo.UserHasCart(user_id, cart_id)
|
exists, err := s.repo.UserHasCart(user_id, cart_id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if amt != 1 {
|
if !exists {
|
||||||
return responseErrors.ErrUserHasNoSuchCart
|
return responseErrors.ErrUserHasNoSuchCart
|
||||||
}
|
}
|
||||||
|
|
||||||
amt, err = s.repo.CheckProductExists(product_id, product_attribute_id)
|
exists, err = s.repo.CheckProductExists(product_id, product_attribute_id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if amt != 1 {
|
if !exists {
|
||||||
return responseErrors.ErrProductOrItsVariationDoesNotExist
|
return responseErrors.ErrProductOrItsVariationDoesNotExist
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.repo.AddProduct(user_id, cart_id, product_id, product_attribute_id, amount)
|
return s.repo.AddProduct(cart_id, product_id, product_attribute_id, uint(amount), set_amount)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CartsService) RemoveProduct(user_id uint, cart_id uint, product_id uint, product_attribute_id *uint) error {
|
||||||
|
exists, err := s.repo.UserHasCart(user_id, cart_id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
return responseErrors.ErrUserHasNoSuchCart
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.repo.RemoveProduct(cart_id, product_id, product_attribute_id)
|
||||||
}
|
}
|
||||||
|
|||||||
25
app/service/currencyService/currencyService.go
Normal file
25
app/service/currencyService/currencyService.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package currencyService
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/model"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/repos/currencyRepo"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CurrencyService struct {
|
||||||
|
repo currencyRepo.UICurrencyRepo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CurrencyService) GetCurrency(id uint) (*model.Currency, error) {
|
||||||
|
return s.repo.Get(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CurrencyService) CreateCurrencyRate(currency *model.CurrencyRate) error {
|
||||||
|
return s.repo.CreateConversionRate(currency)
|
||||||
|
}
|
||||||
|
|
||||||
|
func New() *CurrencyService {
|
||||||
|
repo := currencyRepo.New()
|
||||||
|
return &CurrencyService{
|
||||||
|
repo: repo,
|
||||||
|
}
|
||||||
|
}
|
||||||
26
app/service/customerService/customerService.go
Normal file
26
app/service/customerService/customerService.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package customerService
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/model"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/repos/customerRepo"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/utils/query/filters"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/utils/query/find"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CustomerService struct {
|
||||||
|
repo customerRepo.UICustomerRepo
|
||||||
|
}
|
||||||
|
|
||||||
|
func New() *CustomerService {
|
||||||
|
return &CustomerService{
|
||||||
|
repo: customerRepo.New(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CustomerService) GetById(id uint) (*model.Customer, error) {
|
||||||
|
return s.repo.Get(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
@@ -117,6 +117,18 @@ func (s *EmailService) SendNewUserAdminNotification(userEmail, userName, baseURL
|
|||||||
return s.SendEmail(s.config.AdminEmail, subject, body)
|
return s.SendEmail(s.config.AdminEmail, subject, body)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendNewOrderPlacedNotification sends an email to admin when new order is placed
|
||||||
|
func (s *EmailService) SendNewOrderPlacedNotification(userID uint) error {
|
||||||
|
if s.config.AdminEmail == "" {
|
||||||
|
return nil // No admin email configured
|
||||||
|
}
|
||||||
|
|
||||||
|
subject := "New Order Created"
|
||||||
|
body := s.newOrderPlacedTemplate(userID)
|
||||||
|
|
||||||
|
return s.SendEmail(s.config.AdminEmail, subject, body)
|
||||||
|
}
|
||||||
|
|
||||||
// verificationEmailTemplate returns the HTML template for email verification
|
// verificationEmailTemplate returns the HTML template for email verification
|
||||||
func (s *EmailService) verificationEmailTemplate(name, verificationURL string, langID uint) string {
|
func (s *EmailService) verificationEmailTemplate(name, verificationURL string, langID uint) string {
|
||||||
buf := bytes.Buffer{}
|
buf := bytes.Buffer{}
|
||||||
@@ -137,3 +149,10 @@ func (s *EmailService) newUserAdminNotificationTemplate(userEmail, userName, bas
|
|||||||
emails.EmailAdminNotificationWrapper(view.EmailLayout[view.EmailAdminNotificationData]{LangID: constdata.ADMIN_NOTIFICATION_LANGUAGE, Data: view.EmailAdminNotificationData{UserEmail: userEmail, UserName: userName, BaseURL: baseURL}}).Render(context.Background(), &buf)
|
emails.EmailAdminNotificationWrapper(view.EmailLayout[view.EmailAdminNotificationData]{LangID: constdata.ADMIN_NOTIFICATION_LANGUAGE, Data: view.EmailAdminNotificationData{UserEmail: userEmail, UserName: userName, BaseURL: baseURL}}).Render(context.Background(), &buf)
|
||||||
return buf.String()
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// newUserAdminNotificationTemplate returns the HTML template for admin notification
|
||||||
|
func (s *EmailService) newOrderPlacedTemplate(userID uint) string {
|
||||||
|
buf := bytes.Buffer{}
|
||||||
|
emails.EmailNewOrderPlacedWrapper(view.EmailLayout[view.EmailNewOrderPlacedData]{LangID: constdata.ADMIN_NOTIFICATION_LANGUAGE, Data: view.EmailNewOrderPlacedData{UserID: userID}}).Render(context.Background(), &buf)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
package listService
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.ma-al.com/goc_daniel/b2b/app/model"
|
|
||||||
"git.ma-al.com/goc_daniel/b2b/app/repos/listRepo"
|
|
||||||
"git.ma-al.com/goc_daniel/b2b/app/utils/query/filters"
|
|
||||||
"git.ma-al.com/goc_daniel/b2b/app/utils/query/find"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ListService struct {
|
|
||||||
listRepo listRepo.UIListRepo
|
|
||||||
}
|
|
||||||
|
|
||||||
func New() *ListService {
|
|
||||||
return &ListService{
|
|
||||||
listRepo: listRepo.New(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *ListService) ListProducts(id_lang uint, p find.Paging, filters *filters.FiltersList) (find.Found[model.ProductInList], error) {
|
|
||||||
return s.listRepo.ListProducts(id_lang, p, filters)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *ListService) ListUsers(id_lang uint, p find.Paging, filters *filters.FiltersList) (find.Found[model.UserInList], error) {
|
|
||||||
return s.listRepo.ListUsers(id_lang, p, filters)
|
|
||||||
}
|
|
||||||
@@ -6,7 +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/repos/productDescriptionRepo"
|
searchrepo "git.ma-al.com/goc_daniel/b2b/app/repos/searchRepo"
|
||||||
constdata "git.ma-al.com/goc_daniel/b2b/app/utils/const_data"
|
constdata "git.ma-al.com/goc_daniel/b2b/app/utils/const_data"
|
||||||
"github.com/meilisearch/meilisearch-go"
|
"github.com/meilisearch/meilisearch-go"
|
||||||
)
|
)
|
||||||
@@ -20,7 +20,7 @@ type MeiliIndexSettings struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type MeiliService struct {
|
type MeiliService struct {
|
||||||
productDescriptionRepo productDescriptionRepo.UIProductDescriptionRepo
|
searchRepo searchrepo.UISearchRepo
|
||||||
meiliClient meilisearch.ServiceManager
|
meiliClient meilisearch.ServiceManager
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@ func New() *MeiliService {
|
|||||||
|
|
||||||
return &MeiliService{
|
return &MeiliService{
|
||||||
meiliClient: client,
|
meiliClient: client,
|
||||||
productDescriptionRepo: productDescriptionRepo.New(),
|
searchRepo: searchrepo.New(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ func (s *MeiliService) CreateIndex(id_lang uint) error {
|
|||||||
|
|
||||||
for {
|
for {
|
||||||
// Get batch of products from repo (includes scanning)
|
// Get batch of products from repo (includes scanning)
|
||||||
products, err := s.productDescriptionRepo.GetMeiliProducts(id_lang, offset, batchSize)
|
products, err := s.searchRepo.GetMeiliProducts(id_lang, offset, batchSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get products batch at offset %d: %w", offset, err)
|
return fmt.Errorf("failed to get products batch at offset %d: %w", offset, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,31 +3,45 @@ package menuService
|
|||||||
import (
|
import (
|
||||||
"slices"
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"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/categoriesRepo"
|
categoryrepo "git.ma-al.com/goc_daniel/b2b/app/repos/categoryRepo"
|
||||||
routesRepo "git.ma-al.com/goc_daniel/b2b/app/repos/routesRepo"
|
routesRepo "git.ma-al.com/goc_daniel/b2b/app/repos/routesRepo"
|
||||||
|
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/responseErrors"
|
"git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MenuService struct {
|
type MenuService struct {
|
||||||
categoriesRepo categoriesRepo.UICategoriesRepo
|
categoryRepo categoryrepo.UICategoryRepo
|
||||||
routesRepo routesRepo.UIRoutesRepo
|
routesRepo routesRepo.UIRoutesRepo
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() *MenuService {
|
func New() *MenuService {
|
||||||
return &MenuService{
|
return &MenuService{
|
||||||
categoriesRepo: categoriesRepo.New(),
|
categoryRepo: categoryrepo.New(),
|
||||||
routesRepo: routesRepo.New(),
|
routesRepo: routesRepo.New(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MenuService) GetCategoryTree(root_category_id uint, id_lang uint) (*model.Category, error) {
|
func (s *MenuService) GetCategoryTree(root_category_id uint, id_lang uint) (*model.Category, error) {
|
||||||
all_categories, err := s.categoriesRepo.GetAllCategories(id_lang)
|
all_categories, err := s.categoryRepo.RetrieveMenuCategories(id_lang)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &model.Category{}, err
|
return &model.Category{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remove blacklisted categories
|
||||||
|
// to do so, we detach them from the main tree
|
||||||
|
for i := 0; i < len(all_categories); i++ {
|
||||||
|
if slices.Contains(constdata.CATEGORY_BLACKLIST, all_categories[i].CategoryID) {
|
||||||
|
all_categories[i].ParentID = all_categories[i].CategoryID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
iso_code := all_categories[0].IsoCode
|
||||||
|
s.appendAdditional(&all_categories, id_lang, iso_code)
|
||||||
|
|
||||||
// find the root
|
// find the root
|
||||||
root_index := 0
|
root_index := 0
|
||||||
root_found := false
|
root_found := false
|
||||||
@@ -88,8 +102,8 @@ func (s *MenuService) createTree(index int, all_categories *([]model.ScannedCate
|
|||||||
return node, true
|
return node, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MenuService) GetRoutes(id_lang uint) ([]model.Route, error) {
|
func (s *MenuService) GetRoutes(id_lang, roleId uint) ([]model.Route, error) {
|
||||||
return s.routesRepo.GetRoutes(id_lang)
|
return s.routesRepo.GetRoutes(id_lang, roleId)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MenuService) scannedToNormalCategory(scanned model.ScannedCategory) model.Category {
|
func (s *MenuService) scannedToNormalCategory(scanned model.ScannedCategory) model.Category {
|
||||||
@@ -98,7 +112,7 @@ func (s *MenuService) scannedToNormalCategory(scanned model.ScannedCategory) mod
|
|||||||
normal.CategoryID = scanned.CategoryID
|
normal.CategoryID = scanned.CategoryID
|
||||||
normal.Label = scanned.Name
|
normal.Label = scanned.Name
|
||||||
// normal.Active = scanned.Active == 1
|
// normal.Active = scanned.Active == 1
|
||||||
normal.Params = model.CategoryParams{CategoryID: normal.CategoryID, LinkRewrite: scanned.LinkRewrite, Locale: scanned.IsoCode}
|
normal.Params = model.CategoryParams{CategoryID: normal.CategoryID, LinkRewrite: scanned.LinkRewrite, Locale: scanned.IsoCode, Filter: scanned.Filter}
|
||||||
normal.Children = []model.Category{}
|
normal.Children = []model.Category{}
|
||||||
return normal
|
return normal
|
||||||
}
|
}
|
||||||
@@ -114,11 +128,14 @@ func (a ByPosition) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|||||||
func (a ByPosition) Less(i, j int) bool { return a[i].Position < a[j].Position }
|
func (a ByPosition) Less(i, j int) bool { return a[i].Position < a[j].Position }
|
||||||
|
|
||||||
func (s *MenuService) GetBreadcrumb(root_category_id uint, start_category_id uint, id_lang uint) ([]model.CategoryInBreadcrumb, error) {
|
func (s *MenuService) GetBreadcrumb(root_category_id uint, start_category_id uint, id_lang uint) ([]model.CategoryInBreadcrumb, error) {
|
||||||
all_categories, err := s.categoriesRepo.GetAllCategories(id_lang)
|
all_categories, err := s.categoryRepo.RetrieveMenuCategories(id_lang)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []model.CategoryInBreadcrumb{}, err
|
return []model.CategoryInBreadcrumb{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
iso_code := all_categories[0].IsoCode
|
||||||
|
s.appendAdditional(&all_categories, id_lang, iso_code)
|
||||||
|
|
||||||
breadcrumb := []model.CategoryInBreadcrumb{}
|
breadcrumb := []model.CategoryInBreadcrumb{}
|
||||||
|
|
||||||
start_index := 0
|
start_index := 0
|
||||||
@@ -176,8 +193,8 @@ func (s *MenuService) GetBreadcrumb(root_category_id uint, start_category_id uin
|
|||||||
return breadcrumb, nil
|
return breadcrumb, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MenuService) GetTopMenu(id uint) ([]*model.B2BTopMenu, error) {
|
func (s *MenuService) GetTopMenu(languageId uint, roleId uint) ([]*model.B2BTopMenu, error) {
|
||||||
items, err := s.routesRepo.GetTopMenu(id)
|
items, err := s.routesRepo.GetTopMenu(languageId, roleId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -211,3 +228,57 @@ func (s *MenuService) GetTopMenu(id uint) ([]*model.B2BTopMenu, error) {
|
|||||||
|
|
||||||
return roots, nil
|
return roots, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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_eq=" + strconv.Itoa(int((*all_categories)[i].CategoryID))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
new_products_category.Visited = false
|
||||||
|
new_products_category.Filter = "is_new_eq=true"
|
||||||
|
|
||||||
|
*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)
|
||||||
|
}
|
||||||
|
|||||||
145
app/service/orderService/orderService.go
Normal file
145
app/service/orderService/orderService.go
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
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/service/addressesService"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/service/emailService"
|
||||||
|
"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"
|
||||||
|
)
|
||||||
|
|
||||||
|
type OrderService struct {
|
||||||
|
ordersRepo ordersRepo.UIOrdersRepo
|
||||||
|
cartsRepo cartsRepo.UICartsRepo
|
||||||
|
addressesService *addressesService.AddressesService
|
||||||
|
emailService *emailService.EmailService
|
||||||
|
}
|
||||||
|
|
||||||
|
func New() *OrderService {
|
||||||
|
return &OrderService{
|
||||||
|
ordersRepo: ordersRepo.New(),
|
||||||
|
cartsRepo: cartsRepo.New(),
|
||||||
|
addressesService: addressesService.New(),
|
||||||
|
emailService: emailService.NewEmailService(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *OrderService) Find(user *model.Customer, p find.Paging, filt *filters.FiltersList) (*find.Found[model.CustomerOrder], error) {
|
||||||
|
if !user.HasPermission(perms.OrdersViewAll) {
|
||||||
|
// append filter to view only this user's orders
|
||||||
|
idStr := strconv.FormatUint(uint64(user.ID), 10)
|
||||||
|
filt.Append(filters.Where("b2b_customer_orders.user_id = " + idStr))
|
||||||
|
}
|
||||||
|
|
||||||
|
list, err := s.ordersRepo.Find(user.ID, p, filt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
list.Items[i].AddressUnparsed = &address_unparsed
|
||||||
|
}
|
||||||
|
|
||||||
|
return list, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *OrderService) PlaceNewOrder(user_id uint, cart_id uint, name string, country_id uint, address_info string) error {
|
||||||
|
_, err := s.addressesService.ValidateAddressJson(address_info, country_id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
exists, err := s.cartsRepo.UserHasCart(user_id, cart_id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
return responseErrors.ErrUserHasNoSuchCart
|
||||||
|
}
|
||||||
|
|
||||||
|
cart, err := s.cartsRepo.RetrieveCart(user_id, cart_id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(cart.Products) == 0 {
|
||||||
|
return responseErrors.ErrEmptyCart
|
||||||
|
}
|
||||||
|
|
||||||
|
if name == "" && cart.Name != nil {
|
||||||
|
name = *cart.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// all checks passed
|
||||||
|
err = s.ordersRepo.PlaceNewOrder(cart, name, country_id, address_info)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// from this point onward we do not cancel this order.
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
}(user_id)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *OrderService) ChangeOrderAddress(user *model.Customer, order_id uint, country_id uint, address_info string) error {
|
||||||
|
_, err := s.addressesService.ValidateAddressJson(address_info, country_id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !user.HasPermission(perms.OrdersModifyAll) {
|
||||||
|
exists, err := s.ordersRepo.UserHasOrder(user.ID, order_id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !exists {
|
||||||
|
return responseErrors.ErrUserHasNoSuchOrder
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.ordersRepo.ChangeOrderAddress(order_id, country_id, address_info)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is obiously just an initial version of this function
|
||||||
|
func (s *OrderService) ChangeOrderStatus(user *model.Customer, order_id uint, status string) error {
|
||||||
|
if !user.HasPermission(perms.OrdersModifyAll) {
|
||||||
|
exists, err := s.ordersRepo.UserHasOrder(user.ID, order_id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !exists {
|
||||||
|
return responseErrors.ErrUserHasNoSuchOrder
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.ordersRepo.ChangeOrderStatus(order_id, status)
|
||||||
|
}
|
||||||
168
app/service/productService/productService.go
Normal file
168
app/service/productService/productService.go
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
package productService
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/model"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/repos/productsRepo"
|
||||||
|
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/find"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/view"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ProductService struct {
|
||||||
|
productsRepo productsRepo.UIProductsRepo
|
||||||
|
}
|
||||||
|
|
||||||
|
func New() *ProductService {
|
||||||
|
return &ProductService{
|
||||||
|
productsRepo: productsRepo.New(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ProductService) Get(
|
||||||
|
p_id_product, p_id_lang, p_id_customer, b2b_id_country, p_quantity uint,
|
||||||
|
) (*json.RawMessage, error) {
|
||||||
|
|
||||||
|
product, err := s.productsRepo.GetBase(p_id_product, constdata.SHOP_ID, p_id_lang, p_id_customer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
price, err := s.productsRepo.GetPrice(p_id_product, nil, constdata.SHOP_ID, p_id_customer, b2b_id_country, p_quantity)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
variants, err := s.productsRepo.GetVariants(p_id_product, constdata.SHOP_ID, p_id_lang, p_id_customer, b2b_id_country, p_quantity)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result := view.ProductFull{
|
||||||
|
Product: product,
|
||||||
|
Price: price,
|
||||||
|
Variants: variants,
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(variants) > 0 {
|
||||||
|
result.Variants = variants
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonBytes, err := json.Marshal(result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
raw := json.RawMessage(jsonBytes)
|
||||||
|
return &raw, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ProductService) Find(
|
||||||
|
idLang uint,
|
||||||
|
userID uint,
|
||||||
|
p find.Paging,
|
||||||
|
filters *filters.FiltersList,
|
||||||
|
customer *model.Customer,
|
||||||
|
quantity uint,
|
||||||
|
shopID uint,
|
||||||
|
) (*find.Found[model.ProductInList], error) {
|
||||||
|
|
||||||
|
if customer == nil || customer.Country == nil {
|
||||||
|
return nil, errors.New("customer is nil or missing fields")
|
||||||
|
}
|
||||||
|
|
||||||
|
found, err := s.productsRepo.Find(idLang, userID, p, filters)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. collect simple products (no variants)
|
||||||
|
simpleProductIndexes := make([]int, 0, len(found.Items))
|
||||||
|
|
||||||
|
for i := range found.Items {
|
||||||
|
if found.Items[i].VariantsNumber <= 0 {
|
||||||
|
simpleProductIndexes = append(simpleProductIndexes, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. resolve prices ONLY for simple products
|
||||||
|
for _, i := range simpleProductIndexes {
|
||||||
|
price, err := s.productsRepo.GetPrice(
|
||||||
|
found.Items[i].ProductID,
|
||||||
|
nil,
|
||||||
|
shopID,
|
||||||
|
customer.ID,
|
||||||
|
customer.CountryID,
|
||||||
|
quantity,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
found.Items[i].PriceTaxExcl = price.FinalTaxExcl
|
||||||
|
found.Items[i].PriceTaxIncl = price.FinalTaxIncl
|
||||||
|
}
|
||||||
|
|
||||||
|
return found, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ProductService) GetProductAttributes(
|
||||||
|
langID uint,
|
||||||
|
productID uint,
|
||||||
|
shopID uint,
|
||||||
|
customerID uint,
|
||||||
|
countryID uint,
|
||||||
|
quantity uint,
|
||||||
|
) ([]view.ProductAttribute, error) {
|
||||||
|
variants, err := s.productsRepo.GetVariants(productID, constdata.SHOP_ID, langID, customerID, countryID, quantity)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return variants, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ProductService) AddToFavorites(userID uint, productID uint) error {
|
||||||
|
exists, err := s.productsRepo.ProductInDatabase(productID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
return responseErrors.ErrProductNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
exists, err = s.productsRepo.ExistsInFavorites(userID, productID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if exists {
|
||||||
|
return responseErrors.ErrAlreadyInFavorites
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.productsRepo.AddToFavorites(userID, productID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ProductService) RemoveFromFavorites(userID uint, productID uint) error {
|
||||||
|
exists, err := s.productsRepo.ProductInDatabase(productID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
return responseErrors.ErrProductNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
exists, err = s.productsRepo.ExistsInFavorites(userID, productID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
return responseErrors.ErrNotInFavorites
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.productsRepo.RemoveFromFavorites(userID, productID)
|
||||||
|
}
|
||||||
@@ -89,13 +89,24 @@ func (s *ProductTranslationService) GetProductDescription(userID uint, productID
|
|||||||
// Updates relevant fields with the "updates" map
|
// Updates relevant fields with the "updates" map
|
||||||
func (s *ProductTranslationService) SaveProductDescription(userID uint, productID uint, productLangID uint, updates map[string]string) error {
|
func (s *ProductTranslationService) SaveProductDescription(userID uint, productID uint, productLangID uint, updates map[string]string) error {
|
||||||
// only some fields can be affected
|
// only some fields can be affected
|
||||||
allowedFields := []string{"description", "description_short", "meta_description", "meta_title", "name", "available_now", "available_later", "usage"}
|
allowedFields := []string{"description", "description_short", "link_rewrite", "meta_description", "meta_keywords", "meta_title", "name",
|
||||||
|
"available_now", "available_later", "delivery_in_stock", "delivery_out_stock", "usage"}
|
||||||
for key := range updates {
|
for key := range updates {
|
||||||
if !slices.Contains(allowedFields, key) {
|
if !slices.Contains(allowedFields, key) {
|
||||||
return responseErrors.ErrBadField
|
return responseErrors.ErrBadField
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if text, exists := updates["link_rewrite"]; exists {
|
||||||
|
// sanitize and check that link_rewrite is a valid url slug
|
||||||
|
sanitized := SanitizeSlug(text)
|
||||||
|
if !IsValidSlug(sanitized) {
|
||||||
|
return responseErrors.ErrInvalidURLSlug
|
||||||
|
}
|
||||||
|
|
||||||
|
updates["link_rewrite"] = sanitized
|
||||||
|
}
|
||||||
|
|
||||||
// check that fields description, description_short and usage, if they exist, have a valid html format
|
// check that fields description, description_short and usage, if they exist, have a valid html format
|
||||||
mustBeHTML := []string{"description", "description_short", "usage"}
|
mustBeHTML := []string{"description", "description_short", "usage"}
|
||||||
for i := 0; i < len(mustBeHTML); i++ {
|
for i := 0; i < len(mustBeHTML); i++ {
|
||||||
@@ -136,20 +147,28 @@ func (s *ProductTranslationService) TranslateProductDescription(userID uint, pro
|
|||||||
|
|
||||||
fields := []*string{&productDescription.Description,
|
fields := []*string{&productDescription.Description,
|
||||||
&productDescription.DescriptionShort,
|
&productDescription.DescriptionShort,
|
||||||
|
&productDescription.LinkRewrite,
|
||||||
&productDescription.MetaDescription,
|
&productDescription.MetaDescription,
|
||||||
|
&productDescription.MetaKeywords,
|
||||||
&productDescription.MetaTitle,
|
&productDescription.MetaTitle,
|
||||||
&productDescription.Name,
|
&productDescription.Name,
|
||||||
&productDescription.AvailableNow,
|
&productDescription.AvailableNow,
|
||||||
&productDescription.AvailableLater,
|
&productDescription.AvailableLater,
|
||||||
|
&productDescription.DeliveryInStock,
|
||||||
|
&productDescription.DeliveryOutStock,
|
||||||
&productDescription.Usage,
|
&productDescription.Usage,
|
||||||
}
|
}
|
||||||
keys := []string{"translation_of_product_description",
|
keys := []string{"translation_of_product_description",
|
||||||
"translation_of_product_short_description",
|
"translation_of_product_short_description",
|
||||||
|
"translation_of_product_url_link",
|
||||||
"translation_of_product_meta_description",
|
"translation_of_product_meta_description",
|
||||||
|
"translation_of_product_meta_keywords",
|
||||||
"translation_of_product_meta_title",
|
"translation_of_product_meta_title",
|
||||||
"translation_of_product_name",
|
"translation_of_product_name",
|
||||||
"translation_of_product_available_now",
|
"translation_of_product_available_now_message",
|
||||||
"translation_of_product_available_later",
|
"translation_of_product_available_later_message",
|
||||||
|
"translation_of_product_delivery_in_stock_message",
|
||||||
|
"translation_of_product_delivery_out_stock_message",
|
||||||
"translation_of_product_usage",
|
"translation_of_product_usage",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
68
app/service/productTranslationService/sanitizeURLSlug.go
Normal file
68
app/service/productTranslationService/sanitizeURLSlug.go
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
package productTranslationService
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/db"
|
||||||
|
constdata "git.ma-al.com/goc_daniel/b2b/app/utils/const_data"
|
||||||
|
"github.com/dlclark/regexp2"
|
||||||
|
"golang.org/x/text/runes"
|
||||||
|
"golang.org/x/text/transform"
|
||||||
|
"golang.org/x/text/unicode/norm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func IsValidSlug(s string) bool {
|
||||||
|
var slug_regex2 = regexp2.MustCompile(constdata.SLUG_REGEX, regexp2.None)
|
||||||
|
|
||||||
|
ok, _ := slug_regex2.MatchString(s)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func SanitizeSlug(s string) string {
|
||||||
|
s = strings.TrimSpace(strings.ToLower(s))
|
||||||
|
|
||||||
|
// First apply explicit transliteration for language-specific letters.
|
||||||
|
s = transliterateSlug(s)
|
||||||
|
|
||||||
|
// Then normalize and strip any remaining combining marks.
|
||||||
|
s = removeDiacritics(s)
|
||||||
|
|
||||||
|
// Replace all non-alphanumeric runs with "-"
|
||||||
|
var non_alphanum_regex2 = regexp2.MustCompile(constdata.NON_ALNUM_REGEX, regexp2.None)
|
||||||
|
s, _ = non_alphanum_regex2.Replace(s, "-", -1, -1)
|
||||||
|
|
||||||
|
// Collapse repeated "-" and trim edges
|
||||||
|
var multi_dash_regex2 = regexp2.MustCompile(constdata.MULTI_DASH_REGEX, regexp2.None)
|
||||||
|
s, _ = multi_dash_regex2.Replace(s, "-", -1, -1)
|
||||||
|
|
||||||
|
s = strings.Trim(s, "-")
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func transliterateSlug(s string) string {
|
||||||
|
var cleared string
|
||||||
|
|
||||||
|
err := db.DB.Raw("SELECT slugify_eu(?)", s).Scan(&cleared).Error
|
||||||
|
if err != nil {
|
||||||
|
// log error
|
||||||
|
_ = err
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
return cleared
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeDiacritics(s string) string {
|
||||||
|
t := transform.Chain(
|
||||||
|
norm.NFD,
|
||||||
|
runes.Remove(runes.In(unicode.Mn)),
|
||||||
|
norm.NFC,
|
||||||
|
)
|
||||||
|
out, _, err := transform.String(t, s)
|
||||||
|
if err != nil {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
124
app/service/specificPriceService/specificPriceService.go
Normal file
124
app/service/specificPriceService/specificPriceService.go
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
package specificPriceService
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/model"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/repos/specificPriceRepo"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SpecificPriceService struct {
|
||||||
|
specificPriceRepo specificPriceRepo.UISpecificPriceRepo
|
||||||
|
}
|
||||||
|
|
||||||
|
func New() *SpecificPriceService {
|
||||||
|
return &SpecificPriceService{
|
||||||
|
specificPriceRepo: specificPriceRepo.New(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SpecificPriceService) Create(ctx context.Context, pr *model.SpecificPrice) (*model.SpecificPrice, error) {
|
||||||
|
if err := s.validateRequest(pr); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.specificPriceRepo.Create(ctx, pr); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return pr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SpecificPriceService) Update(ctx context.Context, id uint64, pr *model.SpecificPrice) (*model.SpecificPrice, error) {
|
||||||
|
existing, err := s.specificPriceRepo.GetByID(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if existing == nil {
|
||||||
|
return nil, responseErrors.ErrSpecificPriceNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.validateUpdateRequest(pr); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pr.ID = id
|
||||||
|
|
||||||
|
if err := s.specificPriceRepo.Update(ctx, pr); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return pr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SpecificPriceService) GetByID(ctx context.Context, id uint64) (*model.SpecificPrice, error) {
|
||||||
|
pr, err := s.specificPriceRepo.GetByID(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if pr == nil {
|
||||||
|
return nil, responseErrors.ErrSpecificPriceNotFound
|
||||||
|
}
|
||||||
|
return pr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SpecificPriceService) List(ctx context.Context) ([]*model.SpecificPrice, error) {
|
||||||
|
return s.specificPriceRepo.List(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SpecificPriceService) SetActive(ctx context.Context, id uint64, active bool) error {
|
||||||
|
pr, err := s.specificPriceRepo.GetByID(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if pr == nil {
|
||||||
|
return responseErrors.ErrSpecificPriceNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.specificPriceRepo.SetActive(ctx, id, active)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SpecificPriceService) Delete(ctx context.Context, id uint64) error {
|
||||||
|
pr, err := s.specificPriceRepo.GetByID(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if pr == nil {
|
||||||
|
return responseErrors.ErrSpecificPriceNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.specificPriceRepo.Delete(ctx, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SpecificPriceService) validateRequest(pr *model.SpecificPrice) error {
|
||||||
|
if pr.ReductionType != "amount" && pr.ReductionType != "percentage" {
|
||||||
|
return responseErrors.ErrInvalidReductionType
|
||||||
|
}
|
||||||
|
|
||||||
|
if pr.ReductionType == "percentage" && pr.PercentageReduction == nil {
|
||||||
|
return responseErrors.ErrPercentageRequired
|
||||||
|
}
|
||||||
|
|
||||||
|
if pr.ReductionType == "amount" && pr.Price == nil {
|
||||||
|
return responseErrors.ErrPriceRequired
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SpecificPriceService) validateUpdateRequest(pr *model.SpecificPrice) error {
|
||||||
|
if pr.ReductionType != "" && pr.ReductionType != "amount" && pr.ReductionType != "percentage" {
|
||||||
|
return responseErrors.ErrInvalidReductionType
|
||||||
|
}
|
||||||
|
|
||||||
|
if pr.ReductionType == "percentage" && pr.PercentageReduction == nil {
|
||||||
|
return responseErrors.ErrPercentageRequired
|
||||||
|
}
|
||||||
|
|
||||||
|
if pr.ReductionType == "amount" && pr.Price == nil {
|
||||||
|
return responseErrors.ErrPriceRequired
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
26
app/templ/emails/emailNewOrderPlacedNotification.templ
Normal file
26
app/templ/emails/emailNewOrderPlacedNotification.templ
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package emails
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/templ/layout"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/view"
|
||||||
|
"git.ma-al.com/goc_daniel/b2b/app/utils/i18n"
|
||||||
|
)
|
||||||
|
|
||||||
|
templ EmailNewOrderPlacedWrapper(data view.EmailLayout[view.EmailNewOrderPlacedData]) {
|
||||||
|
@layout.Base( i18n.T___(data.LangID, "email.email_new_order_placed_notification_title")) {
|
||||||
|
<div class="container">
|
||||||
|
<div class="email-wrapper">
|
||||||
|
<div class="email-header">
|
||||||
|
<h1>New Order Placed</h1>
|
||||||
|
</div>
|
||||||
|
<div class="email-body">
|
||||||
|
<p>Hello Administrator,</p>
|
||||||
|
<p>User with id { data.Data.UserID } has placed a new order. </p>
|
||||||
|
</div>
|
||||||
|
<div class="email-footer">
|
||||||
|
<p>© 2024 Gitea Manager. All rights reserved.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,18 +3,36 @@ package constdata
|
|||||||
// PASSWORD_VALIDATION_REGEX is used by the frontend (JavaScript supports lookaheads).
|
// PASSWORD_VALIDATION_REGEX is used by the frontend (JavaScript supports lookaheads).
|
||||||
const PASSWORD_VALIDATION_REGEX = `^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{10,}$`
|
const PASSWORD_VALIDATION_REGEX = `^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{10,}$`
|
||||||
const SHOP_ID = 1
|
const SHOP_ID = 1
|
||||||
|
const DEFAULT_PRODUCT_QUANTITY = 1
|
||||||
const SHOP_DEFAULT_LANGUAGE = 1
|
const SHOP_DEFAULT_LANGUAGE = 1
|
||||||
const ADMIN_NOTIFICATION_LANGUAGE = 2
|
const ADMIN_NOTIFICATION_LANGUAGE = 2
|
||||||
|
|
||||||
// CATEGORY_TREE_ROOT_ID corresponds to id_category in ps_category which has is_root_category=1
|
// CATEGORY_TREE_ROOT_ID corresponds to id_category in ps_category which has is_root_category=1
|
||||||
const CATEGORY_TREE_ROOT_ID = 2
|
const CATEGORY_TREE_ROOT_ID = 2
|
||||||
|
const ADDITIONAL_CATEGORIES_INDEX = 10000
|
||||||
|
|
||||||
|
// since arrays can not be const
|
||||||
|
var CATEGORY_BLACKLIST = []uint{250}
|
||||||
|
|
||||||
const MAX_AMOUNT_OF_CARTS_PER_USER = 10
|
const MAX_AMOUNT_OF_CARTS_PER_USER = 10
|
||||||
const DEFAULT_NEW_CART_NAME = "new cart"
|
const DEFAULT_NEW_CART_NAME = "new cart"
|
||||||
|
const MAX_AMOUNT_OF_PRODUCT_IN_CART = 1024
|
||||||
|
|
||||||
|
const MAX_AMOUNT_OF_ADDRESSES_PER_USER = 10
|
||||||
|
|
||||||
const USER_LOCALE = "user"
|
const USER_LOCALE = "user"
|
||||||
|
|
||||||
|
// ORDERS
|
||||||
|
const NEW_ORDER_STATUS = "PENDING"
|
||||||
|
|
||||||
// WEBDAV
|
// WEBDAV
|
||||||
const NBYTES_IN_WEBDAV_TOKEN = 32
|
const NBYTES_IN_WEBDAV_TOKEN = 32
|
||||||
const WEBDAV_HREF_ROOT = "http://localhost:3000/api/v1/webdav/storage"
|
const WEBDAV_HREF_ROOT = "http://localhost:3000/api/v1/webdav/storage"
|
||||||
const WEBDAV_TRIMMED_ROOT = "localhost:3000/api/v1/webdav/storage"
|
const WEBDAV_TRIMMED_ROOT = "localhost:3000/api/v1/webdav/storage"
|
||||||
|
|
||||||
|
// Slug sanitization
|
||||||
|
const NON_ALNUM_REGEX = `[^a-z0-9]+`
|
||||||
|
const MULTI_DASH_REGEX = `-+`
|
||||||
|
const SLUG_REGEX = `^[a-z0-9]+(?:-[a-z0-9]+)*$`
|
||||||
|
|
||||||
|
const UNLOGGED_USER_ROLE_ID = 4
|
||||||
|
|||||||
@@ -22,10 +22,10 @@ func GetUserID(c fiber.Ctx) (uint, bool) {
|
|||||||
return user_locale.User.ID, true
|
return user_locale.User.ID, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetOriginalUserRole(c fiber.Ctx) (model.CustomerRole, bool) {
|
func GetCustomer(c fiber.Ctx) (*model.Customer, bool) {
|
||||||
user_locale, ok := c.Locals(constdata.USER_LOCALE).(*model.UserLocale)
|
user_locale, ok := c.Locals(constdata.USER_LOCALE).(*model.UserLocale)
|
||||||
if !ok || user_locale.OriginalUser == nil {
|
if !ok || user_locale.User == nil {
|
||||||
return "", false
|
return nil, false
|
||||||
}
|
}
|
||||||
return user_locale.OriginalUser.Role, true
|
return user_locale.User, true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package find
|
package find
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -28,18 +27,13 @@ type Found[T any] struct {
|
|||||||
Spec map[string]interface{} `json:"spec,omitempty"`
|
Spec map[string]interface{} `json:"spec,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wraps given query adding limit, offset clauses and SQL_CALC_FOUND_ROWS to it
|
|
||||||
// and running SELECT FOUND_ROWS() afterwards to fetch the total number
|
|
||||||
// (ignoring LIMIT) of results. The final results are wrapped into the
|
|
||||||
// [find.Found] type.
|
|
||||||
func Paginate[T any](langID uint, paging Paging, stmt *gorm.DB) (Found[T], error) {
|
func Paginate[T any](langID uint, paging Paging, stmt *gorm.DB) (Found[T], error) {
|
||||||
var items []T
|
var items []T
|
||||||
var count uint64
|
var count int64
|
||||||
|
|
||||||
// stmt.Debug()
|
stmt.Count(&count)
|
||||||
|
|
||||||
err := stmt.
|
err := stmt.
|
||||||
Clauses(SqlCalcFound()).
|
|
||||||
Offset(paging.Offset()).
|
Offset(paging.Offset()).
|
||||||
Limit(paging.Limit()).
|
Limit(paging.Limit()).
|
||||||
Find(&items).
|
Find(&items).
|
||||||
@@ -48,22 +42,14 @@ func Paginate[T any](langID uint, paging Paging, stmt *gorm.DB) (Found[T], error
|
|||||||
return Found[T]{}, err
|
return Found[T]{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
countInterface, ok := stmt.Get(FOUND_ROWS_CTX_KEY)
|
// columnsSpec := GetColumnsSpec[T](langID)
|
||||||
if !ok {
|
|
||||||
return Found[T]{}, errors.New(FOUND_ROWS_CTX_KEY + " value was not found in the gorm db context")
|
|
||||||
}
|
|
||||||
if count, ok = countInterface.(uint64); !ok {
|
|
||||||
return Found[T]{}, errors.New("failed to cast value under " + FOUND_ROWS_CTX_KEY + " to uint64")
|
|
||||||
}
|
|
||||||
|
|
||||||
columnsSpec := GetColumnsSpec[T](langID)
|
|
||||||
|
|
||||||
return Found[T]{
|
return Found[T]{
|
||||||
Items: items,
|
Items: items,
|
||||||
Count: uint(count),
|
Count: uint(count),
|
||||||
Spec: map[string]interface{}{
|
// Spec: map[string]interface{}{
|
||||||
"columns": columnsSpec,
|
// "columns": columnsSpec,
|
||||||
},
|
// },
|
||||||
}, err
|
}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
// Typed errors for request validation and authentication
|
// Typed errors for request validation and authentication
|
||||||
|
ErrForbidden = errors.New("forbidden")
|
||||||
ErrInvalidBody = errors.New("invalid request body")
|
ErrInvalidBody = errors.New("invalid request body")
|
||||||
ErrNotAuthenticated = errors.New("not authenticated")
|
ErrNotAuthenticated = errors.New("not authenticated")
|
||||||
ErrUserNotFound = errors.New("user not found")
|
ErrUserNotFound = errors.New("user not found")
|
||||||
@@ -16,7 +17,7 @@ var (
|
|||||||
ErrInvalidToken = errors.New("invalid token")
|
ErrInvalidToken = errors.New("invalid token")
|
||||||
ErrTokenExpired = errors.New("token has expired")
|
ErrTokenExpired = errors.New("token has expired")
|
||||||
ErrTokenRequired = errors.New("token is required")
|
ErrTokenRequired = errors.New("token is required")
|
||||||
ErrAdminAccessRequired = errors.New("admin access is required")
|
ErrAdminAccessRequired = errors.New("admin access required")
|
||||||
|
|
||||||
// Typed errors for logging in and registering
|
// Typed errors for logging in and registering
|
||||||
ErrInvalidCredentials = errors.New("invalid email or password")
|
ErrInvalidCredentials = errors.New("invalid email or password")
|
||||||
@@ -43,12 +44,16 @@ var (
|
|||||||
// Typed errors for product description handler
|
// Typed errors for product description handler
|
||||||
ErrBadAttribute = errors.New("bad or missing attribute value in header")
|
ErrBadAttribute = errors.New("bad or missing attribute value in header")
|
||||||
ErrBadField = errors.New("this field can not be updated")
|
ErrBadField = errors.New("this field can not be updated")
|
||||||
|
ErrInvalidURLSlug = errors.New("URL slug does not obey the industry standard")
|
||||||
ErrInvalidXHTML = errors.New("text is not in xhtml format")
|
ErrInvalidXHTML = errors.New("text is not in xhtml format")
|
||||||
ErrAIResponseFail = errors.New("AI responded with failure")
|
ErrAIResponseFail = errors.New("AI responded with failure")
|
||||||
ErrAIBadOutput = errors.New("AI response does not obey the format")
|
ErrAIBadOutput = errors.New("AI response does not obey the format")
|
||||||
|
|
||||||
// Typed errors for product list handler
|
// Typed errors for product handler
|
||||||
ErrBadPaging = errors.New("bad or missing paging attribute value in header")
|
ErrBadPaging = errors.New("bad or missing paging attribute value in header")
|
||||||
|
ErrProductNotFound = errors.New("product with provided id does not exist")
|
||||||
|
ErrAlreadyInFavorites = errors.New("the product already is in your favorites")
|
||||||
|
ErrNotInFavorites = errors.New("the product already is not in your favorites")
|
||||||
|
|
||||||
// Typed errors for menu handler
|
// Typed errors for menu handler
|
||||||
ErrNoRootFound = errors.New("no root found in categories table")
|
ErrNoRootFound = errors.New("no root found in categories table")
|
||||||
@@ -60,6 +65,18 @@ var (
|
|||||||
ErrMaxAmtOfCartsReached = errors.New("maximal amount of carts reached")
|
ErrMaxAmtOfCartsReached = errors.New("maximal amount of carts reached")
|
||||||
ErrUserHasNoSuchCart = errors.New("user does not have cart with given id")
|
ErrUserHasNoSuchCart = errors.New("user does not have cart with given id")
|
||||||
ErrProductOrItsVariationDoesNotExist = errors.New("product or its variation with given ids does not exist")
|
ErrProductOrItsVariationDoesNotExist = errors.New("product or its variation with given ids does not exist")
|
||||||
|
ErrAmountMustBePositive = errors.New("amount must be positive")
|
||||||
|
ErrAmountMustBeReasonable = errors.New("amount must be reasonable")
|
||||||
|
|
||||||
|
// Typed errors for orders handler
|
||||||
|
ErrEmptyCart = errors.New("the cart is empty")
|
||||||
|
ErrUserHasNoSuchOrder = errors.New("user does not have order with given id")
|
||||||
|
|
||||||
|
// Typed errors for price reduction handler
|
||||||
|
ErrInvalidReductionType = errors.New("invalid reduction type: must be 'amount' or 'percentage'")
|
||||||
|
ErrPercentageRequired = errors.New("percentage_reduction required when reduction_type is percentage")
|
||||||
|
ErrPriceRequired = errors.New("price required when reduction_type is amount")
|
||||||
|
ErrSpecificPriceNotFound = errors.New("price reduction not found")
|
||||||
|
|
||||||
// Typed errors for storage
|
// Typed errors for storage
|
||||||
ErrAccessDenied = errors.New("access denied!")
|
ErrAccessDenied = errors.New("access denied!")
|
||||||
@@ -67,6 +84,15 @@ var (
|
|||||||
ErrFileDoesNotExist = errors.New("file does not exist")
|
ErrFileDoesNotExist = errors.New("file does not exist")
|
||||||
ErrNameTaken = errors.New("name taken")
|
ErrNameTaken = errors.New("name taken")
|
||||||
ErrMissingFileFieldDocument = errors.New("missing file field 'document'")
|
ErrMissingFileFieldDocument = errors.New("missing file field 'document'")
|
||||||
|
|
||||||
|
// Typed errors for data parsing
|
||||||
|
ErrJSONBody = errors.New("invalid JSON body")
|
||||||
|
|
||||||
|
// Typed errors for addresses
|
||||||
|
ErrMaxAmtOfAddressesReached = errors.New("maximal amount of addresses per user reached")
|
||||||
|
ErrUserHasNoSuchAddress = errors.New("user has no such address")
|
||||||
|
ErrInvalidCountryID = errors.New("invalid country id")
|
||||||
|
ErrInvalidAddressJSON = errors.New("invalid address json")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Error represents an error with HTTP status code
|
// Error represents an error with HTTP status code
|
||||||
@@ -91,6 +117,8 @@ func NewError(err error, status int) *Error {
|
|||||||
// GetErrorCode returns the error code string for HTTP response mapping
|
// GetErrorCode returns the error code string for HTTP response mapping
|
||||||
func GetErrorCode(c fiber.Ctx, err error) string {
|
func GetErrorCode(c fiber.Ctx, err error) string {
|
||||||
switch {
|
switch {
|
||||||
|
case errors.Is(err, ErrForbidden):
|
||||||
|
return i18n.T_(c, "error.err_forbidden")
|
||||||
case errors.Is(err, ErrInvalidBody):
|
case errors.Is(err, ErrInvalidBody):
|
||||||
return i18n.T_(c, "error.err_invalid_body")
|
return i18n.T_(c, "error.err_invalid_body")
|
||||||
case errors.Is(err, ErrInvalidCredentials):
|
case errors.Is(err, ErrInvalidCredentials):
|
||||||
@@ -146,6 +174,8 @@ func GetErrorCode(c fiber.Ctx, err error) string {
|
|||||||
return i18n.T_(c, "error.err_bad_attribute")
|
return i18n.T_(c, "error.err_bad_attribute")
|
||||||
case errors.Is(err, ErrBadField):
|
case errors.Is(err, ErrBadField):
|
||||||
return i18n.T_(c, "error.err_bad_field")
|
return i18n.T_(c, "error.err_bad_field")
|
||||||
|
case errors.Is(err, ErrInvalidURLSlug):
|
||||||
|
return i18n.T_(c, "error.err_invalid_url_slug")
|
||||||
case errors.Is(err, ErrInvalidXHTML):
|
case errors.Is(err, ErrInvalidXHTML):
|
||||||
return i18n.T_(c, "error.err_invalid_html")
|
return i18n.T_(c, "error.err_invalid_html")
|
||||||
case errors.Is(err, ErrAIResponseFail):
|
case errors.Is(err, ErrAIResponseFail):
|
||||||
@@ -155,33 +185,69 @@ func GetErrorCode(c fiber.Ctx, err error) string {
|
|||||||
|
|
||||||
case errors.Is(err, ErrBadPaging):
|
case errors.Is(err, ErrBadPaging):
|
||||||
return i18n.T_(c, "error.err_bad_paging")
|
return i18n.T_(c, "error.err_bad_paging")
|
||||||
|
case errors.Is(err, ErrProductNotFound):
|
||||||
|
return i18n.T_(c, "error.err_product_not_found")
|
||||||
|
case errors.Is(err, ErrAlreadyInFavorites):
|
||||||
|
return i18n.T_(c, "error.err_already_in_favorites")
|
||||||
|
case errors.Is(err, ErrNotInFavorites):
|
||||||
|
return i18n.T_(c, "error.err_already_not_in_favorites")
|
||||||
|
|
||||||
case errors.Is(err, ErrNoRootFound):
|
case errors.Is(err, ErrNoRootFound):
|
||||||
return i18n.T_(c, "error.no_root_found")
|
return i18n.T_(c, "error.err_no_root_found")
|
||||||
case errors.Is(err, ErrCircularDependency):
|
case errors.Is(err, ErrCircularDependency):
|
||||||
return i18n.T_(c, "error.circular_dependency")
|
return i18n.T_(c, "error.err_circular_dependency")
|
||||||
case errors.Is(err, ErrStartCategoryNotFound):
|
case errors.Is(err, ErrStartCategoryNotFound):
|
||||||
return i18n.T_(c, "error.start_category_not_found")
|
return i18n.T_(c, "error.err_start_category_not_found")
|
||||||
case errors.Is(err, ErrRootNeverReached):
|
case errors.Is(err, ErrRootNeverReached):
|
||||||
return i18n.T_(c, "error.root_never_reached")
|
return i18n.T_(c, "error.err_root_never_reached")
|
||||||
|
|
||||||
case errors.Is(err, ErrMaxAmtOfCartsReached):
|
case errors.Is(err, ErrMaxAmtOfCartsReached):
|
||||||
return i18n.T_(c, "error.max_amt_of_carts_reached")
|
return i18n.T_(c, "error.err_max_amt_of_carts_reached")
|
||||||
case errors.Is(err, ErrUserHasNoSuchCart):
|
case errors.Is(err, ErrUserHasNoSuchCart):
|
||||||
return i18n.T_(c, "error.user_has_no_such_cart")
|
return i18n.T_(c, "error.err_user_has_no_such_cart")
|
||||||
case errors.Is(err, ErrProductOrItsVariationDoesNotExist):
|
case errors.Is(err, ErrProductOrItsVariationDoesNotExist):
|
||||||
return i18n.T_(c, "error.product_or_its_variation_does_not_exist")
|
return i18n.T_(c, "error.err_product_or_its_variation_does_not_exist")
|
||||||
|
case errors.Is(err, ErrAmountMustBePositive):
|
||||||
|
return i18n.T_(c, "error.err_amount_must_be_positive")
|
||||||
|
case errors.Is(err, ErrAmountMustBeReasonable):
|
||||||
|
return i18n.T_(c, "error.err_amount_must_be_reasonable")
|
||||||
|
|
||||||
|
case errors.Is(err, ErrEmptyCart):
|
||||||
|
return i18n.T_(c, "error.err_cart_is_empty")
|
||||||
|
case errors.Is(err, ErrUserHasNoSuchOrder):
|
||||||
|
return i18n.T_(c, "error.err_user_has_no_such_order")
|
||||||
|
|
||||||
case errors.Is(err, ErrAccessDenied):
|
case errors.Is(err, ErrAccessDenied):
|
||||||
return i18n.T_(c, "error.access_denied")
|
return i18n.T_(c, "error.err_access_denied")
|
||||||
case errors.Is(err, ErrFolderDoesNotExist):
|
case errors.Is(err, ErrFolderDoesNotExist):
|
||||||
return i18n.T_(c, "error.folder_does_not_exist")
|
return i18n.T_(c, "error.err_folder_does_not_exist")
|
||||||
case errors.Is(err, ErrFileDoesNotExist):
|
case errors.Is(err, ErrFileDoesNotExist):
|
||||||
return i18n.T_(c, "error.file_does_not_exist")
|
return i18n.T_(c, "error.err_file_does_not_exist")
|
||||||
case errors.Is(err, ErrNameTaken):
|
case errors.Is(err, ErrNameTaken):
|
||||||
return i18n.T_(c, "error.name_taken")
|
return i18n.T_(c, "error.err_name_taken")
|
||||||
case errors.Is(err, ErrMissingFileFieldDocument):
|
case errors.Is(err, ErrMissingFileFieldDocument):
|
||||||
return i18n.T_(c, "error.missing_file_field_document")
|
return i18n.T_(c, "error.err_missing_file_field_document")
|
||||||
|
|
||||||
|
case errors.Is(err, ErrInvalidReductionType):
|
||||||
|
return i18n.T_(c, "error.invalid_reduction_type")
|
||||||
|
case errors.Is(err, ErrPercentageRequired):
|
||||||
|
return i18n.T_(c, "error.percentage_required")
|
||||||
|
case errors.Is(err, ErrPriceRequired):
|
||||||
|
return i18n.T_(c, "error.price_required")
|
||||||
|
case errors.Is(err, ErrSpecificPriceNotFound):
|
||||||
|
return i18n.T_(c, "error.price_reduction_not_found")
|
||||||
|
|
||||||
|
case errors.Is(err, ErrJSONBody):
|
||||||
|
return i18n.T_(c, "error.err_json_body")
|
||||||
|
|
||||||
|
case errors.Is(err, ErrMaxAmtOfAddressesReached):
|
||||||
|
return i18n.T_(c, "error.err_max_amt_of_addresses_reached")
|
||||||
|
case errors.Is(err, ErrUserHasNoSuchAddress):
|
||||||
|
return i18n.T_(c, "error.err_user_has_no_such_address")
|
||||||
|
case errors.Is(err, ErrInvalidCountryID):
|
||||||
|
return i18n.T_(c, "error.err_invalid_country_id")
|
||||||
|
case errors.Is(err, ErrInvalidAddressJSON):
|
||||||
|
return i18n.T_(c, "error.err_invalid_address_json")
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return i18n.T_(c, "error.err_internal_server_error")
|
return i18n.T_(c, "error.err_internal_server_error")
|
||||||
@@ -191,6 +257,8 @@ func GetErrorCode(c fiber.Ctx, err error) string {
|
|||||||
// GetErrorStatus returns the HTTP status code for the given error
|
// GetErrorStatus returns the HTTP status code for the given error
|
||||||
func GetErrorStatus(err error) int {
|
func GetErrorStatus(err error) int {
|
||||||
switch {
|
switch {
|
||||||
|
case errors.Is(err, ErrForbidden):
|
||||||
|
return fiber.StatusForbidden
|
||||||
case errors.Is(err, ErrInvalidCredentials),
|
case errors.Is(err, ErrInvalidCredentials),
|
||||||
errors.Is(err, ErrNotAuthenticated),
|
errors.Is(err, ErrNotAuthenticated),
|
||||||
errors.Is(err, ErrInvalidToken),
|
errors.Is(err, ErrInvalidToken),
|
||||||
@@ -217,8 +285,12 @@ func GetErrorStatus(err error) int {
|
|||||||
errors.Is(err, ErrInvalidPassword),
|
errors.Is(err, ErrInvalidPassword),
|
||||||
errors.Is(err, ErrBadAttribute),
|
errors.Is(err, ErrBadAttribute),
|
||||||
errors.Is(err, ErrBadField),
|
errors.Is(err, ErrBadField),
|
||||||
|
errors.Is(err, ErrInvalidURLSlug),
|
||||||
errors.Is(err, ErrInvalidXHTML),
|
errors.Is(err, ErrInvalidXHTML),
|
||||||
errors.Is(err, ErrBadPaging),
|
errors.Is(err, ErrBadPaging),
|
||||||
|
errors.Is(err, ErrProductNotFound),
|
||||||
|
errors.Is(err, ErrAlreadyInFavorites),
|
||||||
|
errors.Is(err, ErrNotInFavorites),
|
||||||
errors.Is(err, ErrNoRootFound),
|
errors.Is(err, ErrNoRootFound),
|
||||||
errors.Is(err, ErrCircularDependency),
|
errors.Is(err, ErrCircularDependency),
|
||||||
errors.Is(err, ErrStartCategoryNotFound),
|
errors.Is(err, ErrStartCategoryNotFound),
|
||||||
@@ -226,12 +298,26 @@ func GetErrorStatus(err error) int {
|
|||||||
errors.Is(err, ErrMaxAmtOfCartsReached),
|
errors.Is(err, ErrMaxAmtOfCartsReached),
|
||||||
errors.Is(err, ErrUserHasNoSuchCart),
|
errors.Is(err, ErrUserHasNoSuchCart),
|
||||||
errors.Is(err, ErrProductOrItsVariationDoesNotExist),
|
errors.Is(err, ErrProductOrItsVariationDoesNotExist),
|
||||||
|
errors.Is(err, ErrAmountMustBePositive),
|
||||||
|
errors.Is(err, ErrAmountMustBeReasonable),
|
||||||
|
errors.Is(err, ErrEmptyCart),
|
||||||
|
errors.Is(err, ErrUserHasNoSuchOrder),
|
||||||
|
errors.Is(err, ErrInvalidReductionType),
|
||||||
|
errors.Is(err, ErrPercentageRequired),
|
||||||
|
errors.Is(err, ErrPriceRequired),
|
||||||
errors.Is(err, ErrAccessDenied),
|
errors.Is(err, ErrAccessDenied),
|
||||||
errors.Is(err, ErrFolderDoesNotExist),
|
errors.Is(err, ErrFolderDoesNotExist),
|
||||||
errors.Is(err, ErrFileDoesNotExist),
|
errors.Is(err, ErrFileDoesNotExist),
|
||||||
errors.Is(err, ErrNameTaken),
|
errors.Is(err, ErrNameTaken),
|
||||||
errors.Is(err, ErrMissingFileFieldDocument):
|
errors.Is(err, ErrMissingFileFieldDocument),
|
||||||
|
errors.Is(err, ErrJSONBody),
|
||||||
|
errors.Is(err, ErrMaxAmtOfAddressesReached),
|
||||||
|
errors.Is(err, ErrUserHasNoSuchAddress),
|
||||||
|
errors.Is(err, ErrInvalidCountryID),
|
||||||
|
errors.Is(err, ErrInvalidAddressJSON):
|
||||||
return fiber.StatusBadRequest
|
return fiber.StatusBadRequest
|
||||||
|
case errors.Is(err, ErrSpecificPriceNotFound):
|
||||||
|
return fiber.StatusNotFound
|
||||||
case errors.Is(err, ErrEmailExists):
|
case errors.Is(err, ErrEmailExists):
|
||||||
return fiber.StatusConflict
|
return fiber.StatusConflict
|
||||||
case errors.Is(err, ErrAIResponseFail),
|
case errors.Is(err, ErrAIResponseFail),
|
||||||
|
|||||||
@@ -18,3 +18,7 @@ type EmailAdminNotificationData struct {
|
|||||||
type EmailPasswordResetData struct {
|
type EmailPasswordResetData struct {
|
||||||
ResetURL string
|
ResetURL string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type EmailNewOrderPlacedData struct {
|
||||||
|
UserID uint
|
||||||
|
}
|
||||||
|
|||||||
100
app/view/product.go
Normal file
100
app/view/product.go
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
package view
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ProductAttribute struct {
|
||||||
|
IDProductAttribute uint `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"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Price struct {
|
||||||
|
Base float64 `json:"base"`
|
||||||
|
FinalTaxExcl float64 `json:"final_tax_excl"`
|
||||||
|
FinalTaxIncl float64 `json:"final_tax_incl"`
|
||||||
|
TaxRate float64 `json:"tax_rate"`
|
||||||
|
Priority int `json:"priority"` // or string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Variant struct {
|
||||||
|
ID uint `json:"id_product_attribute"`
|
||||||
|
Reference string `json:"reference"`
|
||||||
|
|
||||||
|
BasePrice float64 `json:"base_price"`
|
||||||
|
FinalExcl float64 `json:"final_tax_excl"`
|
||||||
|
FinalIncl float64 `json:"final_tax_incl"`
|
||||||
|
|
||||||
|
Stock int `json:"stock"`
|
||||||
|
}
|
||||||
|
type ProductFull struct {
|
||||||
|
Product Product `json:"product"`
|
||||||
|
Price Price `json:"price"`
|
||||||
|
Variants []ProductAttribute `json:"variants,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Product struct {
|
||||||
|
ID uint `gorm:"column:id" json:"id"`
|
||||||
|
Reference string `gorm:"column:reference" json:"reference"`
|
||||||
|
SupplierReference string `gorm:"column:supplier_reference" json:"supplier_reference,omitempty"`
|
||||||
|
EAN13 string `gorm:"column:ean13" json:"ean13,omitempty"`
|
||||||
|
UPC string `gorm:"column:upc" json:"upc,omitempty"`
|
||||||
|
ISBN string `gorm:"column:isbn" json:"isbn,omitempty"`
|
||||||
|
|
||||||
|
// Basic Price (from product table)
|
||||||
|
BasePrice float64 `gorm:"column:base_price" json:"base_price"`
|
||||||
|
WholesalePrice float64 `gorm:"column:wholesale_price" json:"wholesale_price,omitempty"`
|
||||||
|
Unity string `gorm:"column:unity" json:"unity,omitempty"`
|
||||||
|
UnitPriceRatio float64 `gorm:"column:unit_price_ratio" json:"unit_price_ratio,omitempty"`
|
||||||
|
|
||||||
|
// Stock & Availability
|
||||||
|
Quantity int `gorm:"column:quantity" json:"quantity"`
|
||||||
|
MinimalQuantity int `gorm:"column:minimal_quantity" json:"minimal_quantity"`
|
||||||
|
AvailableForOrder bool `gorm:"column:available_for_order" json:"available_for_order"`
|
||||||
|
AvailableDate string `gorm:"column:available_date" json:"available_date,omitempty"`
|
||||||
|
OutOfStockBehavior int `gorm:"column:out_of_stock_behavior" json:"out_of_stock_behavior"`
|
||||||
|
|
||||||
|
// Flags
|
||||||
|
OnSale bool `gorm:"column:on_sale" json:"on_sale"`
|
||||||
|
ShowPrice bool `gorm:"column:show_price" json:"show_price"`
|
||||||
|
Condition string `gorm:"column:condition" json:"condition"`
|
||||||
|
IsVirtual bool `gorm:"column:is_virtual" json:"is_virtual"`
|
||||||
|
|
||||||
|
// Physical
|
||||||
|
Weight float64 `gorm:"column:weight" json:"weight"`
|
||||||
|
Width float64 `gorm:"column:width" json:"width"`
|
||||||
|
Height float64 `gorm:"column:height" json:"height"`
|
||||||
|
Depth float64 `gorm:"column:depth" json:"depth"`
|
||||||
|
AdditionalShippingCost float64 `gorm:"column:additional_shipping_cost" json:"additional_shipping_cost,omitempty"`
|
||||||
|
|
||||||
|
// Delivery
|
||||||
|
DeliveryDays int `gorm:"column:delivery_days" json:"delivery_days,omitempty"`
|
||||||
|
|
||||||
|
// Status
|
||||||
|
Active bool `gorm:"column:active" json:"active"`
|
||||||
|
Visibility string `gorm:"column:visibility" json:"visibility"`
|
||||||
|
Indexed bool `gorm:"column:indexed" json:"indexed"`
|
||||||
|
|
||||||
|
// Timestamps
|
||||||
|
DateAdd time.Time `gorm:"column:date_add" json:"date_add"`
|
||||||
|
DateUpd time.Time `gorm:"column:date_upd" json:"date_upd"`
|
||||||
|
|
||||||
|
// Language fields
|
||||||
|
Name string `gorm:"column:name" json:"name"`
|
||||||
|
Description string `gorm:"column:description" json:"description"`
|
||||||
|
DescriptionShort string `gorm:"column:description_short" json:"description_short"`
|
||||||
|
|
||||||
|
// Relations
|
||||||
|
Manufacturer string `gorm:"column:manufacturer" json:"manufacturer"`
|
||||||
|
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"`
|
||||||
|
}
|
||||||
1
bo/components.d.ts
vendored
1
bo/components.d.ts
vendored
@@ -13,7 +13,6 @@ declare module 'vue' {
|
|||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
CartDetails: typeof import('./src/components/customer/CartDetails.vue')['default']
|
CartDetails: typeof import('./src/components/customer/CartDetails.vue')['default']
|
||||||
CategoryMenu: typeof import('./src/components/inner/categoryMenu.vue')['default']
|
CategoryMenu: typeof import('./src/components/inner/categoryMenu.vue')['default']
|
||||||
CategoryMenuListing: typeof import('./src/components/inner/categoryMenuListing.vue')['default']
|
|
||||||
Cs_PrivacyPolicyView: typeof import('./src/components/terms/cs_PrivacyPolicyView.vue')['default']
|
Cs_PrivacyPolicyView: typeof import('./src/components/terms/cs_PrivacyPolicyView.vue')['default']
|
||||||
Cs_TermsAndConditionsView: typeof import('./src/components/terms/cs_TermsAndConditionsView.vue')['default']
|
Cs_TermsAndConditionsView: typeof import('./src/components/terms/cs_TermsAndConditionsView.vue')['default']
|
||||||
En_PrivacyPolicyView: typeof import('./src/components/terms/en_PrivacyPolicyView.vue')['default']
|
En_PrivacyPolicyView: typeof import('./src/components/terms/en_PrivacyPolicyView.vue')['default']
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
info:
|
info:
|
||||||
name: Change Locales
|
name: Change Locales
|
||||||
type: http
|
type: http
|
||||||
seq: 4
|
seq: 5
|
||||||
|
|
||||||
http:
|
http:
|
||||||
method: POST
|
method: POST
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
info:
|
info:
|
||||||
name: Create Search Index
|
name: Create Search Index
|
||||||
type: http
|
type: http
|
||||||
seq: 2
|
seq: 1
|
||||||
|
|
||||||
http:
|
http:
|
||||||
method: GET
|
method: GET
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
info:
|
info:
|
||||||
name: Search Index Settings
|
name: Search Index Settings
|
||||||
type: http
|
type: http
|
||||||
seq: 5
|
seq: 6
|
||||||
|
|
||||||
http:
|
http:
|
||||||
method: POST
|
method: POST
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
info:
|
info:
|
||||||
name: Search Items
|
name: Search Items
|
||||||
type: http
|
type: http
|
||||||
seq: 3
|
seq: 4
|
||||||
|
|
||||||
http:
|
http:
|
||||||
method: POST
|
method: POST
|
||||||
|
|||||||
29
bruno/api_v1/auth/Login.yml
Normal file
29
bruno/api_v1/auth/Login.yml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
info:
|
||||||
|
name: Login
|
||||||
|
type: http
|
||||||
|
seq: 1
|
||||||
|
|
||||||
|
http:
|
||||||
|
method: POST
|
||||||
|
url: "{{bas_url}}/public/auth/login"
|
||||||
|
body:
|
||||||
|
type: json
|
||||||
|
data: |-
|
||||||
|
{
|
||||||
|
"email":"{{email}}",
|
||||||
|
"password":"{{password}}"
|
||||||
|
}
|
||||||
|
auth: inherit
|
||||||
|
|
||||||
|
runtime:
|
||||||
|
variables:
|
||||||
|
- name: email
|
||||||
|
value: admin@ma-al.com
|
||||||
|
- name: password
|
||||||
|
value: Maal12345678
|
||||||
|
|
||||||
|
settings:
|
||||||
|
encodeUrl: true
|
||||||
|
timeout: 0
|
||||||
|
followRedirects: true
|
||||||
|
maxRedirects: 5
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
info:
|
info:
|
||||||
name: list
|
name: auth
|
||||||
type: folder
|
type: folder
|
||||||
seq: 3
|
seq: 2
|
||||||
|
|
||||||
request:
|
request:
|
||||||
auth: inherit
|
auth: inherit
|
||||||
22
bruno/api_v1/currency/currency-rate.yml
Normal file
22
bruno/api_v1/currency/currency-rate.yml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
info:
|
||||||
|
name: currency-rate
|
||||||
|
type: http
|
||||||
|
seq: 2
|
||||||
|
|
||||||
|
http:
|
||||||
|
method: POST
|
||||||
|
url: "{{bas_url}}/restricted/currency-rate"
|
||||||
|
body:
|
||||||
|
type: json
|
||||||
|
data: |-
|
||||||
|
{
|
||||||
|
"b2b_id_currency" : 1,
|
||||||
|
"conversion_rate": 3
|
||||||
|
}
|
||||||
|
auth: inherit
|
||||||
|
|
||||||
|
settings:
|
||||||
|
encodeUrl: true
|
||||||
|
timeout: 0
|
||||||
|
followRedirects: true
|
||||||
|
maxRedirects: 5
|
||||||
20
bruno/api_v1/currency/currency.yml
Normal file
20
bruno/api_v1/currency/currency.yml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
info:
|
||||||
|
name: currency
|
||||||
|
type: http
|
||||||
|
seq: 1
|
||||||
|
|
||||||
|
http:
|
||||||
|
method: GET
|
||||||
|
url: "{{bas_url}}/restricted/currency-rate/{{id}}"
|
||||||
|
auth: inherit
|
||||||
|
|
||||||
|
runtime:
|
||||||
|
variables:
|
||||||
|
- name: id
|
||||||
|
value: "2"
|
||||||
|
|
||||||
|
settings:
|
||||||
|
encodeUrl: true
|
||||||
|
timeout: 0
|
||||||
|
followRedirects: true
|
||||||
|
maxRedirects: 5
|
||||||
7
bruno/api_v1/currency/folder.yml
Normal file
7
bruno/api_v1/currency/folder.yml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
info:
|
||||||
|
name: currency
|
||||||
|
type: folder
|
||||||
|
seq: 9
|
||||||
|
|
||||||
|
request:
|
||||||
|
auth: inherit
|
||||||
15
bruno/api_v1/customer/Customer (me).yml
Normal file
15
bruno/api_v1/customer/Customer (me).yml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
info:
|
||||||
|
name: Customer (me)
|
||||||
|
type: http
|
||||||
|
seq: 2
|
||||||
|
|
||||||
|
http:
|
||||||
|
method: GET
|
||||||
|
url: "{{bas_url}}/restricted/customer"
|
||||||
|
auth: inherit
|
||||||
|
|
||||||
|
settings:
|
||||||
|
encodeUrl: true
|
||||||
|
timeout: 0
|
||||||
|
followRedirects: true
|
||||||
|
maxRedirects: 5
|
||||||
19
bruno/api_v1/customer/Customer (other).yml
Normal file
19
bruno/api_v1/customer/Customer (other).yml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
info:
|
||||||
|
name: Customer (other)
|
||||||
|
type: http
|
||||||
|
seq: 9
|
||||||
|
|
||||||
|
http:
|
||||||
|
method: GET
|
||||||
|
url: "{{bas_url}}/restricted/customer?id=2"
|
||||||
|
params:
|
||||||
|
- name: id
|
||||||
|
value: "2"
|
||||||
|
type: query
|
||||||
|
auth: inherit
|
||||||
|
|
||||||
|
settings:
|
||||||
|
encodeUrl: true
|
||||||
|
timeout: 0
|
||||||
|
followRedirects: true
|
||||||
|
maxRedirects: 5
|
||||||
19
bruno/api_v1/customer/Customer list.yml
Normal file
19
bruno/api_v1/customer/Customer list.yml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
info:
|
||||||
|
name: Customer list
|
||||||
|
type: http
|
||||||
|
seq: 3
|
||||||
|
|
||||||
|
http:
|
||||||
|
method: GET
|
||||||
|
url: "{{bas_url}}/restricted/customer/list?search=marek"
|
||||||
|
params:
|
||||||
|
- name: search
|
||||||
|
value: marek
|
||||||
|
type: query
|
||||||
|
auth: inherit
|
||||||
|
|
||||||
|
settings:
|
||||||
|
encodeUrl: true
|
||||||
|
timeout: 0
|
||||||
|
followRedirects: true
|
||||||
|
maxRedirects: 5
|
||||||
7
bruno/api_v1/customer/folder.yml
Normal file
7
bruno/api_v1/customer/folder.yml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
info:
|
||||||
|
name: customer
|
||||||
|
type: folder
|
||||||
|
seq: 10
|
||||||
|
|
||||||
|
request:
|
||||||
|
auth: inherit
|
||||||
15
bruno/api_v1/product/Add To Favorites.yml
Normal file
15
bruno/api_v1/product/Add To Favorites.yml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
info:
|
||||||
|
name: Add To Favorites
|
||||||
|
type: http
|
||||||
|
seq: 3
|
||||||
|
|
||||||
|
http:
|
||||||
|
method: POST
|
||||||
|
url: "{{bas_url}}/restricted/product/favorite/53"
|
||||||
|
auth: inherit
|
||||||
|
|
||||||
|
settings:
|
||||||
|
encodeUrl: true
|
||||||
|
timeout: 0
|
||||||
|
followRedirects: true
|
||||||
|
maxRedirects: 5
|
||||||
15
bruno/api_v1/product/Get Product.yml
Normal file
15
bruno/api_v1/product/Get Product.yml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
info:
|
||||||
|
name: Get Product
|
||||||
|
type: http
|
||||||
|
seq: 1
|
||||||
|
|
||||||
|
http:
|
||||||
|
method: GET
|
||||||
|
url: "{{bas_url}}/restricted/product/51/1/7"
|
||||||
|
auth: inherit
|
||||||
|
|
||||||
|
settings:
|
||||||
|
encodeUrl: true
|
||||||
|
timeout: 0
|
||||||
|
followRedirects: true
|
||||||
|
maxRedirects: 5
|
||||||
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: "51"
|
||||||
|
|
||||||
|
settings:
|
||||||
|
encodeUrl: true
|
||||||
|
timeout: 0
|
||||||
|
followRedirects: true
|
||||||
|
maxRedirects: 5
|
||||||
@@ -5,7 +5,7 @@ info:
|
|||||||
|
|
||||||
http:
|
http:
|
||||||
method: GET
|
method: GET
|
||||||
url: "{{bas_url}}/restricted/list/list-products?p=1&elems=30&sort=product_id,asc&category_id_in=243&reference=~62"
|
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:
|
params:
|
||||||
- name: p
|
- name: p
|
||||||
value: "1"
|
value: "1"
|
||||||
@@ -16,18 +16,26 @@ http:
|
|||||||
- name: sort
|
- name: sort
|
||||||
value: product_id,asc
|
value: product_id,asc
|
||||||
type: query
|
type: query
|
||||||
|
disabled: true
|
||||||
- name: category_id_in
|
- name: category_id_in
|
||||||
value: "243"
|
value: "23"
|
||||||
type: query
|
type: query
|
||||||
|
disabled: true
|
||||||
- name: reference
|
- name: reference
|
||||||
value: ~62
|
value: ~NC100
|
||||||
|
type: query
|
||||||
|
- name: is_new_eq
|
||||||
|
value: "0"
|
||||||
|
type: query
|
||||||
|
- name: is_favorite_eq
|
||||||
|
value: "false"
|
||||||
|
type: query
|
||||||
|
- name: is_oem_eq
|
||||||
|
value: "FALSE"
|
||||||
type: query
|
type: query
|
||||||
body:
|
body:
|
||||||
type: json
|
type: json
|
||||||
data: ""
|
data: ""
|
||||||
auth:
|
|
||||||
type: bearer
|
|
||||||
token: "{{token}}"
|
|
||||||
|
|
||||||
settings:
|
settings:
|
||||||
encodeUrl: true
|
encodeUrl: true
|
||||||
15
bruno/api_v1/product/Remove Form Favorites.yml
Normal file
15
bruno/api_v1/product/Remove Form Favorites.yml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
info:
|
||||||
|
name: Remove Form Favorites
|
||||||
|
type: http
|
||||||
|
seq: 4
|
||||||
|
|
||||||
|
http:
|
||||||
|
method: DELETE
|
||||||
|
url: "{{bas_url}}/restricted/product/favorite/51"
|
||||||
|
auth: inherit
|
||||||
|
|
||||||
|
settings:
|
||||||
|
encodeUrl: true
|
||||||
|
timeout: 0
|
||||||
|
followRedirects: true
|
||||||
|
maxRedirects: 5
|
||||||
7
bruno/api_v1/product/folder.yml
Normal file
7
bruno/api_v1/product/folder.yml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
info:
|
||||||
|
name: product
|
||||||
|
type: folder
|
||||||
|
seq: 8
|
||||||
|
|
||||||
|
request:
|
||||||
|
auth: inherit
|
||||||
15
bruno/api_v1/routes/Routes.yml
Normal file
15
bruno/api_v1/routes/Routes.yml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
info:
|
||||||
|
name: Routes
|
||||||
|
type: http
|
||||||
|
seq: 1
|
||||||
|
|
||||||
|
http:
|
||||||
|
method: GET
|
||||||
|
url: ""
|
||||||
|
auth: inherit
|
||||||
|
|
||||||
|
settings:
|
||||||
|
encodeUrl: true
|
||||||
|
timeout: 0
|
||||||
|
followRedirects: true
|
||||||
|
maxRedirects: 5
|
||||||
7
bruno/api_v1/routes/folder.yml
Normal file
7
bruno/api_v1/routes/folder.yml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
info:
|
||||||
|
name: routes
|
||||||
|
type: folder
|
||||||
|
seq: 10
|
||||||
|
|
||||||
|
request:
|
||||||
|
auth: inherit
|
||||||
20
bruno/api_v1/specific_price/Activate.yml
Normal file
20
bruno/api_v1/specific_price/Activate.yml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
info:
|
||||||
|
name: Activate
|
||||||
|
type: http
|
||||||
|
seq: 5
|
||||||
|
|
||||||
|
http:
|
||||||
|
method: PATCH
|
||||||
|
url: "{{bas_url}}/restricted/specific-price/{{id}}/activate"
|
||||||
|
auth: inherit
|
||||||
|
|
||||||
|
runtime:
|
||||||
|
variables:
|
||||||
|
- name: id
|
||||||
|
value: "1"
|
||||||
|
|
||||||
|
settings:
|
||||||
|
encodeUrl: true
|
||||||
|
timeout: 0
|
||||||
|
followRedirects: true
|
||||||
|
maxRedirects: 5
|
||||||
27
bruno/api_v1/specific_price/Create.yml
Normal file
27
bruno/api_v1/specific_price/Create.yml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
info:
|
||||||
|
name: Create
|
||||||
|
type: http
|
||||||
|
seq: 3
|
||||||
|
|
||||||
|
http:
|
||||||
|
method: POST
|
||||||
|
url: "{{bas_url}}/restricted/specific-price"
|
||||||
|
body:
|
||||||
|
type: json
|
||||||
|
data: |-
|
||||||
|
{
|
||||||
|
"name": "Summer Sale 3",
|
||||||
|
"scope": "shop",
|
||||||
|
"reduction_type": "amount",
|
||||||
|
"price": 69,
|
||||||
|
"from_quantity": 1,
|
||||||
|
"is_active": true,
|
||||||
|
"currency_id": 2
|
||||||
|
}
|
||||||
|
auth: inherit
|
||||||
|
|
||||||
|
settings:
|
||||||
|
encodeUrl: true
|
||||||
|
timeout: 0
|
||||||
|
followRedirects: true
|
||||||
|
maxRedirects: 5
|
||||||
20
bruno/api_v1/specific_price/Deactivate.yml
Normal file
20
bruno/api_v1/specific_price/Deactivate.yml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
info:
|
||||||
|
name: Deactivate
|
||||||
|
type: http
|
||||||
|
seq: 6
|
||||||
|
|
||||||
|
http:
|
||||||
|
method: PATCH
|
||||||
|
url: "{{bas_url}}/restricted/specific-price/{{id}}/deactivate"
|
||||||
|
auth: inherit
|
||||||
|
|
||||||
|
runtime:
|
||||||
|
variables:
|
||||||
|
- name: id
|
||||||
|
value: "1"
|
||||||
|
|
||||||
|
settings:
|
||||||
|
encodeUrl: true
|
||||||
|
timeout: 0
|
||||||
|
followRedirects: true
|
||||||
|
maxRedirects: 5
|
||||||
20
bruno/api_v1/specific_price/Delete.yml
Normal file
20
bruno/api_v1/specific_price/Delete.yml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
info:
|
||||||
|
name: Delete
|
||||||
|
type: http
|
||||||
|
seq: 7
|
||||||
|
|
||||||
|
http:
|
||||||
|
method: DELETE
|
||||||
|
url: "{{bas_url}}/restricted/price-reductions/{{id}}"
|
||||||
|
auth: inherit
|
||||||
|
|
||||||
|
runtime:
|
||||||
|
variables:
|
||||||
|
- name: id
|
||||||
|
value: "1"
|
||||||
|
|
||||||
|
settings:
|
||||||
|
encodeUrl: true
|
||||||
|
timeout: 0
|
||||||
|
followRedirects: true
|
||||||
|
maxRedirects: 5
|
||||||
20
bruno/api_v1/specific_price/Get.yml
Normal file
20
bruno/api_v1/specific_price/Get.yml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
info:
|
||||||
|
name: Get
|
||||||
|
type: http
|
||||||
|
seq: 2
|
||||||
|
|
||||||
|
http:
|
||||||
|
method: GET
|
||||||
|
url: "{{bas_url}}/restricted/specific-price/{{id}}"
|
||||||
|
auth: inherit
|
||||||
|
|
||||||
|
runtime:
|
||||||
|
variables:
|
||||||
|
- name: id
|
||||||
|
value: "1"
|
||||||
|
|
||||||
|
settings:
|
||||||
|
encodeUrl: true
|
||||||
|
timeout: 0
|
||||||
|
followRedirects: true
|
||||||
|
maxRedirects: 5
|
||||||
15
bruno/api_v1/specific_price/List.yml
Normal file
15
bruno/api_v1/specific_price/List.yml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
info:
|
||||||
|
name: List
|
||||||
|
type: http
|
||||||
|
seq: 1
|
||||||
|
|
||||||
|
http:
|
||||||
|
method: GET
|
||||||
|
url: "{{bas_url}}/restricted/specific-price"
|
||||||
|
auth: inherit
|
||||||
|
|
||||||
|
settings:
|
||||||
|
encodeUrl: true
|
||||||
|
timeout: 0
|
||||||
|
followRedirects: true
|
||||||
|
maxRedirects: 5
|
||||||
38
bruno/api_v1/specific_price/Update.yml
Normal file
38
bruno/api_v1/specific_price/Update.yml
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
info:
|
||||||
|
name: Update
|
||||||
|
type: http
|
||||||
|
seq: 4
|
||||||
|
|
||||||
|
http:
|
||||||
|
method: PUT
|
||||||
|
url: "{{bas_url}}/restricted/specific-price/{{id}}"
|
||||||
|
body:
|
||||||
|
type: json
|
||||||
|
data: |-
|
||||||
|
{
|
||||||
|
"name": "Summer Sale Updated",
|
||||||
|
"reduction_type": "amount",
|
||||||
|
"percentage_reduction": 50.0,
|
||||||
|
"price": 69,
|
||||||
|
"currency_id": 1,
|
||||||
|
"scope": "shop",
|
||||||
|
"is_active": true,
|
||||||
|
"from_quantity":1
|
||||||
|
// "product_ids": [51,53],
|
||||||
|
// "category_ids": [1],
|
||||||
|
// "product_attribute_ids": [1114],
|
||||||
|
// "country_ids": [1],
|
||||||
|
// "customer_ids": [2,1]
|
||||||
|
}
|
||||||
|
auth: inherit
|
||||||
|
|
||||||
|
runtime:
|
||||||
|
variables:
|
||||||
|
- name: id
|
||||||
|
value: "3"
|
||||||
|
|
||||||
|
settings:
|
||||||
|
encodeUrl: true
|
||||||
|
timeout: 0
|
||||||
|
followRedirects: true
|
||||||
|
maxRedirects: 5
|
||||||
73
bruno/api_v1/specific_price/folder.yml
Normal file
73
bruno/api_v1/specific_price/folder.yml
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
info:
|
||||||
|
name: specific_price
|
||||||
|
type: folder
|
||||||
|
seq: 3
|
||||||
|
|
||||||
|
docs:
|
||||||
|
content: |
|
||||||
|
# Specific Price API
|
||||||
|
|
||||||
|
Endpoints for managing specific price rules (price reductions).
|
||||||
|
|
||||||
|
## Scopes
|
||||||
|
|
||||||
|
Specific prices can be **global** or **scoped**:
|
||||||
|
|
||||||
|
- **Global**: If all scope arrays (`product_ids`, `category_ids`, `product_attribute_ids`, `country_ids`, `customer_ids`) are empty, the price reduction applies to everything.
|
||||||
|
|
||||||
|
- **Scoped**: If ANY scope array has values, the price reduction applies only when ANY condition matches (UNION logic).
|
||||||
|
|
||||||
|
### Scope Fields
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `product_ids` | uint[] | Specific products |
|
||||||
|
| `category_ids` | uint[] | Products in categories |
|
||||||
|
| `product_attribute_ids` | uint[] | Product variants (e.g., size, color) |
|
||||||
|
| `country_ids` | uint[] | Customers in countries |
|
||||||
|
| `customer_ids` | uint[] | Specific customers |
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
**Global** (applies to all products):
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "Global Sale",
|
||||||
|
"reduction_type": "percentage",
|
||||||
|
"percentage_reduction": 10,
|
||||||
|
"from_quantity": 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Scoped to specific products**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "Product Sale",
|
||||||
|
"reduction_type": "percentage",
|
||||||
|
"percentage_reduction": 20,
|
||||||
|
"product_ids": [1, 2, 3]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Scoped to category + country**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "Category Country Sale",
|
||||||
|
"reduction_type": "amount",
|
||||||
|
"price": 9.99,
|
||||||
|
"category_ids": [5],
|
||||||
|
"country_ids": [1]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Reduction Types
|
||||||
|
|
||||||
|
- `percentage`: Requires `percentage_reduction` (e.g., 10.5 = 10.5% off)
|
||||||
|
- `amount`: Requires `price` (fixed price after reduction)
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
- `reduction_type` is required and must be "percentage" or "amount"
|
||||||
|
- If `reduction_type` is "percentage", then `percentage_reduction` is required
|
||||||
|
- If `reduction_type` is "amount", then `price` is required
|
||||||
|
type: text/markdown
|
||||||
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user