325 lines
9.3 KiB
JavaScript
325 lines
9.3 KiB
JavaScript
const List = require("../models/list.model.v2");
|
|
const { isValidItemType, isValidItemGroup, isValidZone } = require("../constants/classifications");
|
|
|
|
/**
|
|
* Get list items for household and store
|
|
* GET /households/:householdId/stores/:storeId/list
|
|
*/
|
|
exports.getList = async (req, res) => {
|
|
try {
|
|
const { householdId, storeId } = req.params;
|
|
const items = await List.getHouseholdStoreList(householdId, storeId);
|
|
res.json({ items });
|
|
} catch (error) {
|
|
console.error("Error getting list:", error);
|
|
res.status(500).json({ message: "Failed to get list" });
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Get specific item by name
|
|
* GET /households/:householdId/stores/:storeId/list/item
|
|
*/
|
|
exports.getItemByName = async (req, res) => {
|
|
try {
|
|
const { householdId, storeId } = req.params;
|
|
const { item_name } = req.query;
|
|
|
|
if (!item_name) {
|
|
return res.status(400).json({ message: "Item name is required" });
|
|
}
|
|
|
|
const item = await List.getItemByName(householdId, storeId, item_name);
|
|
if (!item) {
|
|
return res.status(404).json({ message: "Item not found" });
|
|
}
|
|
|
|
res.json(item);
|
|
} catch (error) {
|
|
console.error("Error getting item:", error);
|
|
res.status(500).json({ message: "Failed to get item" });
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Add or update item in household store list
|
|
* POST /households/:householdId/stores/:storeId/list/add
|
|
*/
|
|
exports.addItem = async (req, res) => {
|
|
try {
|
|
const { householdId, storeId } = req.params;
|
|
const { item_name, quantity, notes } = req.body;
|
|
const userId = req.user.id;
|
|
|
|
if (!item_name || item_name.trim() === "") {
|
|
return res.status(400).json({ message: "Item name is required" });
|
|
}
|
|
|
|
// Get processed image if uploaded
|
|
const imageBuffer = req.processedImage?.buffer || null;
|
|
const mimeType = req.processedImage?.mimeType || null;
|
|
|
|
const result = await List.addOrUpdateItem(
|
|
householdId,
|
|
storeId,
|
|
item_name,
|
|
quantity || "1",
|
|
userId,
|
|
imageBuffer,
|
|
mimeType,
|
|
notes
|
|
);
|
|
|
|
// Add history record
|
|
await List.addHistoryRecord(result.listId, quantity || "1", userId);
|
|
|
|
res.json({
|
|
message: result.isNew ? "Item added" : "Item updated",
|
|
item: {
|
|
id: result.listId,
|
|
item_name: result.itemName,
|
|
quantity: quantity || "1",
|
|
bought: false
|
|
}
|
|
});
|
|
} catch (error) {
|
|
console.error("Error adding item:", error);
|
|
res.status(500).json({ message: "Failed to add item" });
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Mark item as bought or unbought
|
|
* PATCH /households/:householdId/stores/:storeId/list/item
|
|
*/
|
|
exports.markBought = async (req, res) => {
|
|
try {
|
|
const { householdId, storeId } = req.params;
|
|
const { item_name, bought, quantity_bought } = req.body;
|
|
|
|
if (!item_name) return res.status(400).json({ message: "Item name is required" });
|
|
|
|
const item = await List.getItemByName(householdId, storeId, item_name);
|
|
console.log('requesting mark ', { item, householdId, storeId, item_name, bought, quantity_bought });
|
|
if (!item) return res.status(404).json({ message: "Item not found" });
|
|
|
|
|
|
// Update bought status (with optional partial purchase)
|
|
await List.setBought(item.id, bought, quantity_bought);
|
|
|
|
res.json({ message: bought ? "Item marked as bought" : "Item unmarked" });
|
|
} catch (error) {
|
|
console.error("Error marking bought:", error);
|
|
res.status(500).json({ message: "Failed to update item" });
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Update item details (quantity, notes)
|
|
* PUT /households/:householdId/stores/:storeId/list/item
|
|
*/
|
|
exports.updateItem = async (req, res) => {
|
|
try {
|
|
const { householdId, storeId } = req.params;
|
|
const { item_name, quantity, notes } = req.body;
|
|
|
|
if (!item_name) {
|
|
return res.status(400).json({ message: "Item name is required" });
|
|
}
|
|
|
|
// Get the list item
|
|
const item = await List.getItemByName(householdId, storeId, item_name);
|
|
if (!item) {
|
|
return res.status(404).json({ message: "Item not found" });
|
|
}
|
|
|
|
// Update item
|
|
await List.updateItem(item.id, item_name, quantity, notes);
|
|
|
|
res.json({
|
|
message: "Item updated",
|
|
item: {
|
|
id: item.id,
|
|
item_name,
|
|
quantity,
|
|
notes
|
|
}
|
|
});
|
|
} catch (error) {
|
|
console.error("Error updating item:", error);
|
|
res.status(500).json({ message: "Failed to update item" });
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Delete item from list
|
|
* DELETE /households/:householdId/stores/:storeId/list/item
|
|
*/
|
|
exports.deleteItem = async (req, res) => {
|
|
try {
|
|
const { householdId, storeId } = req.params;
|
|
const { item_name } = req.body;
|
|
|
|
if (!item_name) {
|
|
return res.status(400).json({ message: "Item name is required" });
|
|
}
|
|
|
|
// Get the list item
|
|
const item = await List.getItemByName(householdId, storeId, item_name);
|
|
if (!item) {
|
|
return res.status(404).json({ message: "Item not found" });
|
|
}
|
|
|
|
await List.deleteItem(item.id);
|
|
|
|
res.json({ message: "Item deleted" });
|
|
} catch (error) {
|
|
console.error("Error deleting item:", error);
|
|
res.status(500).json({ message: "Failed to delete item" });
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Get item suggestions based on query
|
|
* GET /households/:householdId/stores/:storeId/list/suggestions
|
|
*/
|
|
exports.getSuggestions = async (req, res) => {
|
|
try {
|
|
const { householdId, storeId } = req.params;
|
|
const { query } = req.query;
|
|
|
|
const suggestions = await List.getSuggestions(query || "", householdId, storeId);
|
|
res.json(suggestions);
|
|
} catch (error) {
|
|
console.error("Error getting suggestions:", error);
|
|
res.status(500).json({ message: "Failed to get suggestions" });
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Get recently bought items
|
|
* GET /households/:householdId/stores/:storeId/list/recent
|
|
*/
|
|
exports.getRecentlyBought = async (req, res) => {
|
|
try {
|
|
const { householdId, storeId } = req.params;
|
|
const items = await List.getRecentlyBoughtItems(householdId, storeId);
|
|
res.json(items);
|
|
} catch (error) {
|
|
console.error("Error getting recent items:", error);
|
|
res.status(500).json({ message: "Failed to get recent items" });
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Get item classification
|
|
* GET /households/:householdId/stores/:storeId/list/classification
|
|
*/
|
|
exports.getClassification = async (req, res) => {
|
|
try {
|
|
const { householdId, storeId } = req.params;
|
|
const { item_name } = req.query;
|
|
|
|
if (!item_name) {
|
|
return res.status(400).json({ message: "Item name is required" });
|
|
}
|
|
|
|
// Get item ID from name
|
|
const item = await List.getItemByName(householdId, storeId, item_name);
|
|
if (!item) {
|
|
return res.json({ classification: null });
|
|
}
|
|
|
|
const classification = await List.getClassification(householdId, item.item_id);
|
|
res.json({ classification });
|
|
} catch (error) {
|
|
console.error("Error getting classification:", error);
|
|
res.status(500).json({ message: "Failed to get classification" });
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Set/update item classification
|
|
* POST /households/:householdId/stores/:storeId/list/classification
|
|
*/
|
|
exports.setClassification = async (req, res) => {
|
|
try {
|
|
const { householdId, storeId } = req.params;
|
|
const { item_name, classification } = req.body;
|
|
|
|
if (!item_name) {
|
|
return res.status(400).json({ message: "Item name is required" });
|
|
}
|
|
|
|
if (!classification) {
|
|
return res.status(400).json({ message: "Classification is required" });
|
|
}
|
|
|
|
// Validate classification
|
|
const validClassifications = ['produce', 'dairy', 'meat', 'bakery', 'frozen', 'pantry', 'snacks', 'beverages', 'household', 'other'];
|
|
if (!validClassifications.includes(classification)) {
|
|
return res.status(400).json({ message: "Invalid classification value" });
|
|
}
|
|
|
|
// Get item - add to master items if not exists
|
|
const item = await List.getItemByName(householdId, storeId, item_name);
|
|
let itemId;
|
|
|
|
if (!item) {
|
|
// Item doesn't exist in list, need to get from items table or create
|
|
const itemResult = await List.addOrUpdateItem(
|
|
householdId,
|
|
storeId,
|
|
item_name,
|
|
"1",
|
|
req.user.id,
|
|
null,
|
|
null
|
|
);
|
|
itemId = itemResult.itemId;
|
|
} else {
|
|
itemId = item.item_id;
|
|
}
|
|
|
|
// Set classification (using item_type field for simplicity)
|
|
await List.upsertClassification(householdId, itemId, {
|
|
item_type: classification,
|
|
item_group: null,
|
|
zone: null
|
|
});
|
|
|
|
res.json({ message: "Classification set", classification });
|
|
} catch (error) {
|
|
console.error("Error setting classification:", error);
|
|
res.status(500).json({ message: "Failed to set classification" });
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Update item image
|
|
* POST /households/:householdId/stores/:storeId/list/update-image
|
|
*/
|
|
exports.updateItemImage = async (req, res) => {
|
|
try {
|
|
const { householdId, storeId } = req.params;
|
|
const { item_name, quantity } = req.body;
|
|
const userId = req.user.id;
|
|
|
|
// Get processed image
|
|
const imageBuffer = req.processedImage?.buffer || null;
|
|
const mimeType = req.processedImage?.mimeType || null;
|
|
|
|
if (!imageBuffer) {
|
|
return res.status(400).json({ message: "No image provided" });
|
|
}
|
|
|
|
// Update the item with new image
|
|
await List.addOrUpdateItem(householdId, storeId, item_name, quantity, userId, imageBuffer, mimeType);
|
|
|
|
res.json({ message: "Image updated successfully" });
|
|
} catch (error) {
|
|
console.error("Error updating image:", error);
|
|
res.status(500).json({ message: "Failed to update image" });
|
|
}
|
|
};
|