grocery-app/backend/controllers/stores.controller.js

413 lines
12 KiB
JavaScript

const storeModel = require("../models/store.model");
const { sendError } = require("../utils/http");
const { logError } = require("../utils/logger");
function parsePositiveInteger(value) {
const parsed = Number.parseInt(String(value), 10);
return Number.isInteger(parsed) && parsed > 0 ? parsed : null;
}
function getHouseholdId(req) {
return req.params.householdId || req.household?.id;
}
function getLocationId(req) {
return req.params.locationId || req.params.storeId;
}
// Legacy global store catalog. Kept for system-admin compatibility.
exports.getAllStores = async (req, res) => {
try {
const stores = await storeModel.getAllStores();
res.json(stores);
} catch (error) {
logError(req, "stores.getAllStores", error);
sendError(res, 500, "Failed to fetch stores");
}
};
exports.createStore = async (req, res) => {
try {
const { name, default_zones } = req.body;
if (!name || name.trim().length === 0) {
return sendError(res, 400, "Store name is required");
}
const store = await storeModel.createStore(name.trim(), default_zones || null);
res.status(201).json({
message: "Store created successfully",
store,
});
} catch (error) {
logError(req, "stores.createStore", error);
if (error.code === "23505") {
return sendError(res, 400, "Store with this name already exists");
}
sendError(res, 500, "Failed to create store");
}
};
exports.updateStore = async (req, res) => {
try {
const { name, default_zones } = req.body;
const store = await storeModel.updateStore(req.params.storeId, {
name: name?.trim(),
default_zones,
});
if (!store) {
return sendError(res, 404, "Store not found");
}
res.json({
message: "Store updated successfully",
store,
});
} catch (error) {
logError(req, "stores.updateStore", error);
sendError(res, 500, "Failed to update store");
}
};
exports.deleteStore = async (req, res) => {
try {
await storeModel.deleteStore(req.params.storeId);
res.json({ message: "Store deleted successfully" });
} catch (error) {
logError(req, "stores.deleteStore", error);
if (error.message.includes("in use")) {
return sendError(res, 400, error.message);
}
sendError(res, 500, "Failed to delete store");
}
};
// Household-owned store/location management.
exports.getHouseholdStores = async (req, res) => {
try {
const stores = await storeModel.getHouseholdStores(getHouseholdId(req));
res.json(stores);
} catch (error) {
logError(req, "stores.getHouseholdStores", error);
sendError(res, 500, "Failed to fetch household stores");
}
};
exports.createHouseholdStore = async (req, res) => {
try {
const householdId = getHouseholdId(req);
const { name, location_name, address } = req.body;
if (!name || name.trim().length === 0) {
return sendError(res, 400, "Store name is required");
}
const store = await storeModel.createHouseholdStore(
householdId,
name,
location_name || "Default Location",
address || null,
req.user.id
);
res.status(201).json({
message: "Store location created successfully",
store,
});
} catch (error) {
logError(req, "stores.createHouseholdStore", error);
if (error.code === "23505") {
return sendError(res, 400, "Store or location already exists for this household");
}
sendError(res, 500, "Failed to create store location");
}
};
exports.updateHouseholdStore = async (req, res) => {
try {
const { name } = req.body;
const householdStoreId = parsePositiveInteger(req.params.householdStoreId);
if (!householdStoreId) {
return sendError(res, 400, "Store ID must be a positive integer");
}
if (!name || name.trim().length === 0) {
return sendError(res, 400, "Store name is required");
}
const store = await storeModel.updateHouseholdStore(getHouseholdId(req), householdStoreId, {
name,
});
if (!store) {
return sendError(res, 404, "Store not found");
}
res.json({ message: "Store updated successfully", store });
} catch (error) {
logError(req, "stores.updateHouseholdStore", error);
sendError(res, 500, "Failed to update store");
}
};
exports.deleteHouseholdStore = async (req, res) => {
try {
const householdStoreId = parsePositiveInteger(req.params.householdStoreId);
if (!householdStoreId) {
return sendError(res, 400, "Store ID must be a positive integer");
}
const deleted = await storeModel.deleteHouseholdStore(getHouseholdId(req), householdStoreId);
if (!deleted) {
return sendError(res, 404, "Store not found");
}
res.json({ message: "Store deleted successfully" });
} catch (error) {
logError(req, "stores.deleteHouseholdStore", error);
if (error.message.includes("last store location")) {
return sendError(res, 400, error.message);
}
sendError(res, 500, "Failed to delete store");
}
};
exports.addLocationToStore = async (req, res) => {
try {
const householdStoreId = parsePositiveInteger(req.params.householdStoreId);
const { name, address } = req.body;
if (!householdStoreId) {
return sendError(res, 400, "Store ID must be a positive integer");
}
if (!name || name.trim().length === 0) {
return sendError(res, 400, "Location name is required");
}
const location = await storeModel.addLocationToStore(
getHouseholdId(req),
householdStoreId,
name,
address || null,
req.user.id
);
if (!location) {
return sendError(res, 404, "Store not found");
}
res.status(201).json({
message: "Location added successfully",
store: location,
});
} catch (error) {
logError(req, "stores.addLocationToStore", error);
if (error.code === "23505") {
return sendError(res, 400, "Location already exists for this store");
}
sendError(res, 500, "Failed to add location");
}
};
exports.updateLocation = async (req, res) => {
try {
const locationId = parsePositiveInteger(getLocationId(req));
const { name, address, map_data } = req.body;
if (!locationId) {
return sendError(res, 400, "Location ID must be a positive integer");
}
const location = await storeModel.updateLocation(getHouseholdId(req), locationId, {
name,
address,
map_data,
});
if (!location) {
return sendError(res, 404, "Location not found");
}
res.json({ message: "Location updated successfully", store: location });
} catch (error) {
logError(req, "stores.updateLocation", error);
sendError(res, 500, "Failed to update location");
}
};
exports.deleteLocation = async (req, res) => {
try {
const locationId = parsePositiveInteger(getLocationId(req));
if (!locationId) {
return sendError(res, 400, "Location ID must be a positive integer");
}
const deleted = await storeModel.deleteLocation(getHouseholdId(req), locationId);
if (!deleted) {
return sendError(res, 404, "Location not found");
}
res.json({ message: "Location removed successfully" });
} catch (error) {
logError(req, "stores.deleteLocation", error);
if (error.message.includes("last store location")) {
return sendError(res, 400, error.message);
}
sendError(res, 500, "Failed to remove location");
}
};
exports.setDefaultLocation = async (req, res) => {
try {
const locationId = parsePositiveInteger(getLocationId(req));
if (!locationId) {
return sendError(res, 400, "Location ID must be a positive integer");
}
await storeModel.setDefaultLocation(getHouseholdId(req), locationId);
res.json({ message: "Default location updated successfully" });
} catch (error) {
logError(req, "stores.setDefaultLocation", error);
sendError(res, 500, "Failed to set default location");
}
};
exports.getLocationZones = async (req, res) => {
try {
const locationId = parsePositiveInteger(getLocationId(req));
if (!locationId) {
return sendError(res, 400, "Location ID must be a positive integer");
}
const zones = await storeModel.listLocationZones(getHouseholdId(req), locationId);
res.json({ zones });
} catch (error) {
logError(req, "stores.getLocationZones", error);
sendError(res, 500, "Failed to load zones");
}
};
exports.createZone = async (req, res) => {
try {
const locationId = parsePositiveInteger(getLocationId(req));
const { name, sort_order, color, map_metadata } = req.body;
if (!locationId) {
return sendError(res, 400, "Location ID must be a positive integer");
}
if (!name || name.trim().length === 0) {
return sendError(res, 400, "Zone name is required");
}
const zone = await storeModel.createZone(getHouseholdId(req), locationId, {
name,
sort_order: Number.isInteger(sort_order) ? sort_order : Number.parseInt(sort_order, 10),
color,
map_metadata,
});
res.status(201).json({ message: "Zone created successfully", zone });
} catch (error) {
logError(req, "stores.createZone", error);
if (error.code === "23505") {
return sendError(res, 400, "Zone already exists for this location");
}
sendError(res, 500, "Failed to create zone");
}
};
exports.updateZone = async (req, res) => {
try {
const locationId = parsePositiveInteger(getLocationId(req));
const zoneId = parsePositiveInteger(req.params.zoneId);
if (!locationId || !zoneId) {
return sendError(res, 400, "Location ID and zone ID must be positive integers");
}
const sortOrder = req.body.sort_order;
const zone = await storeModel.updateZone(getHouseholdId(req), locationId, zoneId, {
name: req.body.name,
sort_order:
sortOrder === undefined
? undefined
: Number.isInteger(sortOrder)
? sortOrder
: Number.parseInt(sortOrder, 10),
color: req.body.color,
map_metadata: req.body.map_metadata,
is_active: req.body.is_active,
});
if (!zone) {
return sendError(res, 404, "Zone not found");
}
res.json({ message: "Zone updated successfully", zone });
} catch (error) {
logError(req, "stores.updateZone", error);
sendError(res, 500, "Failed to update zone");
}
};
exports.deleteZone = async (req, res) => {
try {
const locationId = parsePositiveInteger(getLocationId(req));
const zoneId = parsePositiveInteger(req.params.zoneId);
if (!locationId || !zoneId) {
return sendError(res, 400, "Location ID and zone ID must be positive integers");
}
const deleted = await storeModel.deleteZone(getHouseholdId(req), locationId, zoneId);
if (!deleted) {
return sendError(res, 404, "Zone not found");
}
res.json({ message: "Zone removed successfully" });
} catch (error) {
logError(req, "stores.deleteZone", error);
sendError(res, 500, "Failed to remove zone");
}
};
// Backward-compatible handlers for the old /stores/household routes.
exports.addStoreToHousehold = async (req, res) => {
try {
const { storeId } = req.body;
if (!storeId) {
return sendError(res, 400, "Store ID is required");
}
const legacyStore = await storeModel.getStoreById(storeId);
if (!legacyStore) {
return sendError(res, 404, "Store not found");
}
const store = await storeModel.createHouseholdStore(
getHouseholdId(req),
legacyStore.name,
"Default Location",
null,
req.user.id
);
res.status(201).json({
message: "Store added to household successfully",
store,
});
} catch (error) {
logError(req, "stores.addStoreToHousehold", error);
sendError(res, 500, "Failed to add store to household");
}
};
exports.removeStoreFromHousehold = exports.deleteLocation;
exports.setDefaultStore = exports.setDefaultLocation;