costco-grocery-list/backend/models/list.model.js
Nico 8d5b2d3ea3
All checks were successful
Build & Deploy Costco Grocery List / build (push) Successful in 45s
Build & Deploy Costco Grocery List / deploy (push) Successful in 14s
Build & Deploy Costco Grocery List / notify (push) Successful in 1s
change backend to fix added_by data
2026-01-04 20:14:54 -08:00

189 lines
5.2 KiB
JavaScript

const pool = require("../db/pool");
exports.getUnboughtItems = async () => {
const result = await pool.query(
`SELECT
gl.id,
LOWER(gl.item_name) AS item_name,
gl.quantity,
gl.bought,
ENCODE(gl.item_image, 'base64') as item_image,
gl.image_mime_type,
(
SELECT ARRAY_AGG(u.name ORDER BY gh.added_on DESC)
FROM (
SELECT gh.added_by, gh.added_on,
ROW_NUMBER() OVER (PARTITION BY gh.list_item_id ORDER BY gh.added_on DESC) as rn
FROM grocery_history gh
WHERE gh.list_item_id = gl.id
) gh
JOIN users u ON gh.added_by = u.id
WHERE gh.rn <= gl.quantity
) as added_by_users,
gl.modified_on as last_added_on,
ic.item_type,
ic.item_group,
ic.zone
FROM grocery_list gl
LEFT JOIN item_classification ic ON gl.id = ic.id
WHERE gl.bought = FALSE
ORDER BY gl.id ASC`
);
return result.rows;
};
exports.getItemByName = async (itemName) => {
const result = await pool.query(
"SELECT * FROM grocery_list WHERE item_name ILIKE $1",
[itemName]
);
return result.rows[0] || null;
};
exports.addOrUpdateItem = async (itemName, quantity, userId, imageBuffer = null, mimeType = null) => {
const lowerItemName = itemName.toLowerCase();
const result = await pool.query(
"SELECT id, bought FROM grocery_list WHERE item_name ILIKE $1",
[lowerItemName]
);
if (result.rowCount > 0) {
// Update existing item
if (imageBuffer && mimeType) {
await pool.query(
`UPDATE grocery_list
SET quantity = $1,
bought = FALSE,
item_image = $3,
image_mime_type = $4,
modified_on = NOW()
WHERE id = $2`,
[quantity, result.rows[0].id, imageBuffer, mimeType]
);
} else {
await pool.query(
`UPDATE grocery_list
SET quantity = $1,
bought = FALSE,
modified_on = NOW()
WHERE id = $2`,
[quantity, result.rows[0].id]
);
}
return result.rows[0].id;
} else {
// Insert new item
const insert = await pool.query(
`INSERT INTO grocery_list
(item_name, quantity, added_by, item_image, image_mime_type)
VALUES ($1, $2, $3, $4, $5) RETURNING id`,
[lowerItemName, quantity, userId, imageBuffer, mimeType]
);
return insert.rows[0].id;
}
};
exports.setBought = async (id, userId) => {
await pool.query(
"UPDATE grocery_list SET bought = TRUE, modified_on = NOW() WHERE id = $1",
[id]
);
};
exports.addHistoryRecord = async (itemId, quantity, userId) => {
await pool.query(
`INSERT INTO grocery_history (list_item_id, quantity, added_by, added_on)
VALUES ($1, $2, $3, NOW())`,
[itemId, quantity, userId]
);
};
exports.getSuggestions = async (query) => {
const result = await pool.query(
`SELECT DISTINCT item_name
FROM grocery_list
WHERE item_name ILIKE $1
LIMIT 10`,
[`%${query}%`]
);
return result.rows;
};
exports.getRecentlyBoughtItems = async () => {
const result = await pool.query(
`SELECT
gl.id,
LOWER(gl.item_name) AS item_name,
gl.quantity,
gl.bought,
ENCODE(gl.item_image, 'base64') as item_image,
gl.image_mime_type,
(
SELECT ARRAY_AGG(u.name ORDER BY gh.added_on DESC)
FROM (
SELECT gh.added_by, gh.added_on,
ROW_NUMBER() OVER (PARTITION BY gh.list_item_id ORDER BY gh.added_on DESC) as rn
FROM grocery_history gh
WHERE gh.list_item_id = gl.id
) gh
JOIN users u ON gh.added_by = u.id
WHERE gh.rn <= gl.quantity
) as added_by_users,
gl.modified_on as last_added_on
FROM grocery_list gl
WHERE gl.bought = TRUE
AND gl.modified_on >= NOW() - INTERVAL '24 hours'
ORDER BY gl.modified_on DESC`
);
return result.rows;
};
// Classification methods
exports.getClassification = async (itemId) => {
const result = await pool.query(
`SELECT item_type, item_group, zone, confidence, source
FROM item_classification
WHERE id = $1`,
[itemId]
);
return result.rows[0] || null;
};
exports.upsertClassification = async (itemId, classification) => {
const { item_type, item_group, zone, confidence, source } = classification;
const result = await pool.query(
`INSERT INTO item_classification (id, item_type, item_group, zone, confidence, source)
VALUES ($1, $2, $3, $4, $5, $6)
ON CONFLICT (id)
DO UPDATE SET
item_type = EXCLUDED.item_type,
item_group = EXCLUDED.item_group,
zone = EXCLUDED.zone,
confidence = EXCLUDED.confidence,
source = EXCLUDED.source
RETURNING *`,
[itemId, item_type, item_group, zone, confidence, source]
);
return result.rows[0];
};
exports.updateItem = async (id, itemName, quantity) => {
const result = await pool.query(
`UPDATE grocery_list
SET item_name = $2, quantity = $3, modified_on = NOW()
WHERE id = $1
RETURNING *`,
[id, itemName, quantity]
);
return result.rows[0];
};