diff --git a/controllers/brand.controller.js b/controllers/brand.controller.js index c6028c42404d810d7c4e8d98bf5f2e06a8078f3b..7af17a33c8b6d57a85e9c2f14434d2a3973a9c21 100644 --- a/controllers/brand.controller.js +++ b/controllers/brand.controller.js @@ -132,6 +132,18 @@ module.exports.getAllBrands = async (req, res) => { }; } + // Filter out brands created by users from deleted stores + whereClause.createdBy = { + [Op.notIn]: Sequelize.literal(`( + SELECT u.ID FROM users u + WHERE u.storeId IN ( + SELECT s.ID FROM stores s + WHERE s.isDeleted = true + ) + AND u.storeId IS NOT NULL + )`) + }; + // Get total count for pagination const totalCount = await getBrandsCountService({ where: whereClause, @@ -189,6 +201,18 @@ module.exports.getBrandsList = async (req, res) => { }; } + // Filter out brands created by users from deleted stores + whereClause.createdBy = { + [Op.notIn]: Sequelize.literal(`( + SELECT u.ID FROM users u + WHERE u.storeId IN ( + SELECT s.ID FROM stores s + WHERE s.isDeleted = true + ) + AND u.storeId IS NOT NULL + )`) + }; + const brands = await getAllBrandsService({ where: whereClause, attributes: ["ID", "brandName"], diff --git a/controllers/category.controller.js b/controllers/category.controller.js index 5477da982e4b257db30b5ca992d303b8981b569d..9262c9240f6ae36e3373514e0ccaef795ba73488 100644 --- a/controllers/category.controller.js +++ b/controllers/category.controller.js @@ -216,6 +216,19 @@ module.exports.getUniqueCategories = async (req, res) => { [Op.like]: `%${search.trim()}%`, // Case-insensitive search for MySQL }; } + + // Filter out categories created by users from deleted stores + whereClause.createdBy = { + [Op.notIn]: Sequelize.literal(`( + SELECT u.ID FROM users u + WHERE u.storeId IN ( + SELECT s.ID FROM stores s + WHERE s.isDeleted = true + ) + AND u.storeId IS NOT NULL + )`), + }; + // Get all unique categories (no grouping needed since categories are unique by default) const categories = await getAllCategoriesService({ where: whereClause, @@ -303,6 +316,18 @@ module.exports.getCategoriesNamesList = async (req, res) => { }; } + // Filter out categories created by users from deleted stores + whereClause.createdBy = { + [Op.notIn]: Sequelize.literal(`( + SELECT u.ID FROM users u + WHERE u.storeId IN ( + SELECT s.ID FROM stores s + WHERE s.isDeleted = true + ) + AND u.storeId IS NOT NULL + )`) + }; + const categories = await getAllCategoriesService({ where: whereClause, attributes: ["ID", "categoryName"], @@ -377,6 +402,18 @@ module.exports.getAllUniqueCategoriesWithCount = async (req, res) => { }; } + // Filter out categories created by users from deleted stores + categoryWhereClause.createdBy = { + [Op.notIn]: Sequelize.literal(`( + SELECT u.ID FROM users u + WHERE u.storeId IN ( + SELECT s.ID FROM stores s + WHERE s.isDeleted = true + ) + AND u.storeId IS NOT NULL + )`), + }; + // Overall total products (active, not deleted) - separate where clause for products const productWhereClause = { isDeleted: false, @@ -1085,6 +1122,7 @@ module.exports.getApprovedCategoriesForStoreAdmin = async (req, res) => { "icon", "categoryImage", "isProtected", + "isDeleted", "parentCategoryId", "status", "isCategoryApproved", @@ -1122,6 +1160,7 @@ module.exports.getApprovedCategoriesForStoreAdmin = async (req, res) => { categoryImage: category.categoryImage, categoryVisibility: category.isProtected ? "protected" : "public", isCategoryApproved: category.isCategoryApproved, + isDeleted: category.isDeleted, parentCategoryId: category.parentCategoryId, parentCategoryName: category.parentCategory ? category.parentCategory.categoryName diff --git a/controllers/mainCategory.controller.js b/controllers/mainCategory.controller.js index 8caf03b05b196f3c42cf2dc83aafdef9e0b1aeac..3da2b47c23de2017a2c08d2bcd0935dc85618c05 100644 --- a/controllers/mainCategory.controller.js +++ b/controllers/mainCategory.controller.js @@ -8,7 +8,7 @@ const { } = require("../services/category.service"); const ERROR_RESPONSE = require("../utils/handleError"); const { getPagination } = require("../utils/pagination"); -const { Op } = require("sequelize"); +const { Op, Sequelize } = require("sequelize"); const emitter = require("../config/event"); // Helper function to generate slug from category name @@ -186,6 +186,18 @@ module.exports.getAllMainCategories = async (req, res) => { } } + // Filter out categories created by users from deleted stores + whereClause.createdBy = { + [Op.notIn]: Sequelize.literal(`( + SELECT u.ID FROM users u + WHERE u.storeId IN ( + SELECT s.ID FROM stores s + WHERE s.isDeleted = true + ) + AND u.storeId IS NOT NULL + )`) + }; + // Include store-category mappings; apply store filter when provided const storeCategoryInclude = { model: require("../models/storeCategory.model"), diff --git a/controllers/product.controller.js b/controllers/product.controller.js index c06d75c42ce2625f28514defa60bc7055c08ddcd..a2c2f597385eca1f5eb371ea065e7305efed2fdf 100644 --- a/controllers/product.controller.js +++ b/controllers/product.controller.js @@ -3546,6 +3546,26 @@ module.exports.getAllProductsSuperadmin = async (req, res) => { const offset = (pageNum - 1) * limitNum; const whereClause = { isDeleted: false, // Exclude soft-deleted products + [Op.or]: [ + // Always include super admin products + { isBySuperAdmin: true }, + // Only include store admin products from non-deleted stores + { + [Op.and]: [ + { isBySuperAdmin: false }, + Sequelize.literal(`( + EXISTS ( + SELECT 1 FROM stores s + INNER JOIN product_store ps ON ps.storeId = s.ID + WHERE ps.productId = products.ID + AND ps.isActive = true + AND s.isDeleted = false + AND s.status = 'active' + ) + )`) + ] + } + ] }; // Handle status filtering @@ -3704,6 +3724,7 @@ module.exports.getAllProductsSuperadmin = async (req, res) => { model: STORE_MODEL, as: "stores", attributes: ["ID", "storeName", "storeSlug", "storeImage"], + required: false, }, { model: BRAND_MODEL, @@ -5504,6 +5525,11 @@ module.exports.globalProductSearch = async (req, res) => { model: STORE_MODEL, as: "stores", attributes: ["ID", "storeName", "storeImage"], + where: { + isDeleted: false, + status: "active" + }, + required: false, }, { model: VARIANT_MODEL, diff --git a/controllers/store.controller.js b/controllers/store.controller.js index 2c2b7ec75613530cd6e0716e707518384482cbdf..2466c185b2a895cb007d2c41d19e16bac98b8746 100644 --- a/controllers/store.controller.js +++ b/controllers/store.controller.js @@ -710,46 +710,20 @@ module.exports.getAllStores = async (req, res) => { limit = parseInt(limit); const offset = (page - 1) * limit; - const searchConditions = []; + // Base conditions - always filter for non-deleted and active stores + const where = { + isDeleted: false, + status: "active" + }; + // Add search conditions if provided if (search && search.trim()) { const searchTerm = search.trim(); - searchConditions.push( + where[Op.or] = [ { storeName: { [Op.like]: `%${searchTerm}%` } }, - // { city: { [Op.like]: `%${searchTerm}%` } }, - // { state: { [Op.like]: `%${searchTerm}%` } }, { businessName: { [Op.like]: `%${searchTerm}%` } }, { addressLine1: { [Op.like]: `%${searchTerm}%` } } - ); - } - - if (status && status !== "all") { - searchConditions.push({ status }); - } - - let where = {}; - - if (searchConditions.length > 0) { - // If we have search conditions, we need to handle them differently - if (search && search.trim()) { - // Search conditions should use Op.or - where[Op.or] = searchConditions.filter( - (condition) => - condition.storeName || - condition.city || - condition.state || - condition.businessName || - condition.addressLine1 - ); - - // Status condition should be combined with Op.and - if (status && status !== "all") { - where = { [Op.and]: [where, { status }] }; - } - } else { - // Only status condition - where = { [Op.and]: searchConditions }; - } + ]; } const query = { @@ -1384,6 +1358,21 @@ module.exports.getStoreWithCategoriesAndProducts = async (req, res) => { }); } + // Validate store is not deleted and is active + if (store.isDeleted === true) { + return res.status(404).json({ + status: false, + message: "Store not found", + }); + } + + if (store.status !== "active") { + return res.status(404).json({ + status: false, + message: "Store not found", + }); + } + // Parse pagination parameters page = parseInt(page); limit = parseInt(limit); @@ -1630,6 +1619,30 @@ module.exports.getStoreCategoryCounts = async (req, res) => { try { const { storeId } = req.params; + // Validate store exists and is not deleted + const store = await getStoreByIdService(parseInt(storeId)); + if (!store) { + return res.status(404).json({ + status: false, + message: "Store not found", + }); + } + + // Validate store is not deleted and is active + if (store.isDeleted === true) { + return res.status(404).json({ + status: false, + message: "Store not found", + }); + } + + if (store.status !== "active") { + return res.status(404).json({ + status: false, + message: "Store not found", + }); + } + // Get all categories and count products for this store using product_store relationship const categoryCounts = await sequelize.query( ` @@ -2449,7 +2462,10 @@ module.exports.getStoresListForSuperAdmin = async (req, res) => { try { const { search, status } = req.query; - const whereClause = {}; + const whereClause = { + isDeleted: false, + status: "active" + }; // Add search condition if provided if (search && search.trim() !== "") { @@ -2459,10 +2475,7 @@ module.exports.getStoresListForSuperAdmin = async (req, res) => { ]; } - // Add status filter if provided - if (status) { - whereClause.status = status; - } + // Note: Always show only active stores, status parameter is ignored for security const stores = await getAllStoresAndCountsService({ where: whereClause,