230 lines
6.2 KiB
JavaScript
230 lines
6.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(DISTINCT u.name)
|
|
FROM (
|
|
SELECT DISTINCT gh.added_by
|
|
FROM grocery_history gh
|
|
WHERE gh.list_item_id = gl.id
|
|
ORDER BY gh.added_by
|
|
) gh
|
|
JOIN users u ON gh.added_by = u.id
|
|
) 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
|
|
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(DISTINCT u.name)
|
|
FROM (
|
|
SELECT DISTINCT gh.added_by
|
|
FROM grocery_history gh
|
|
WHERE gh.list_item_id = gl.id
|
|
ORDER BY gh.added_by
|
|
) gh
|
|
JOIN users u ON gh.added_by = u.id
|
|
) 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.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, quantityBought) => {
|
|
// Get current item
|
|
const item = await pool.query(
|
|
"SELECT quantity FROM grocery_list WHERE id = $1",
|
|
[id]
|
|
);
|
|
|
|
if (!item.rows[0]) return;
|
|
|
|
const currentQuantity = item.rows[0].quantity;
|
|
const remainingQuantity = currentQuantity - quantityBought;
|
|
|
|
if (remainingQuantity <= 0) {
|
|
// Mark as bought if all quantity is purchased
|
|
await pool.query(
|
|
"UPDATE grocery_list SET bought = TRUE, modified_on = NOW() WHERE id = $1",
|
|
[id]
|
|
);
|
|
} else {
|
|
// Reduce quantity if partial purchase
|
|
await pool.query(
|
|
"UPDATE grocery_list SET quantity = $1, modified_on = NOW() WHERE id = $2",
|
|
[remainingQuantity, 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 LOWER(item_name) as 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(DISTINCT u.name)
|
|
FROM (
|
|
SELECT DISTINCT gh.added_by
|
|
FROM grocery_history gh
|
|
WHERE gh.list_item_id = gl.id
|
|
ORDER BY gh.added_by
|
|
) gh
|
|
JOIN users u ON gh.added_by = u.id
|
|
) 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];
|
|
};
|
|
|