diff --git a/lib/services/storage/listingsStorage.js b/lib/services/storage/listingsStorage.js index 715543f..e3e6b1b 100755 --- a/lib/services/storage/listingsStorage.js +++ b/lib/services/storage/listingsStorage.js @@ -48,7 +48,8 @@ export const getListingsKpisForJobIds = (jobIds = []) => { SUM(CASE WHEN is_active = 1 THEN 1 ELSE 0 END) AS activeCount, AVG(price) AS avgPrice FROM listings - WHERE job_id IN (${placeholders})`, + WHERE job_id IN (${placeholders}) + AND manually_deleted = 0`, jobIds, )[0] || {}; @@ -80,6 +81,7 @@ export const getProviderDistributionForJobIds = (jobIds = []) => { `SELECT provider, COUNT(*) AS cnt FROM listings WHERE job_id IN (${placeholders}) + AND manually_deleted = 0 GROUP BY provider ORDER BY cnt DESC`, jobIds, @@ -118,8 +120,8 @@ export const getActiveOrUnknownListings = () => { return SqliteConnection.query( `SELECT * FROM listings - WHERE is_active is null - OR is_active = 1 + WHERE (is_active is null OR is_active = 1) + AND manually_deleted = 0 ORDER BY provider`, ); }; @@ -306,6 +308,9 @@ export const queryListings = ({ whereParts.push('(wl.id IS NULL)'); } + // Build whereSql (filtering by manually_deleted = 0) + whereParts.push('(l.manually_deleted = 0)'); + const whereSql = whereParts.length ? `WHERE ${whereParts.join(' AND ')}` : ''; const whereSqlWithAlias = whereSql .replace(/\btitle\b/g, 'l.title') @@ -370,8 +375,8 @@ export const queryListings = ({ export const deleteListingsByJobId = (jobId) => { if (!jobId) return; return SqliteConnection.execute( - `DELETE - FROM listings + `UPDATE listings + SET manually_deleted = 1 WHERE job_id = @jobId`, { jobId }, ); @@ -387,9 +392,9 @@ export const deleteListingsById = (ids) => { if (!Array.isArray(ids) || ids.length === 0) return; const placeholders = ids.map(() => '?').join(','); return SqliteConnection.execute( - `DELETE - FROM listings - WHERE id IN (${placeholders})`, + `UPDATE listings + SET manually_deleted = 1 + WHERE id IN (${placeholders})`, ids, ); }; @@ -404,6 +409,7 @@ export const getListingsToGeocode = () => { `SELECT id, address FROM listings WHERE is_active = 1 + AND manually_deleted = 0 AND address IS NOT NULL AND (latitude IS NULL OR longitude IS NULL)`, ); @@ -443,6 +449,7 @@ export const getListingsForMap = ({ jobId, userId = null, isAdmin = false } = {} 'l.latitude != -1', 'l.longitude != -1', 'l.is_active = 1', + 'l.manually_deleted = 0', ]; const params = { userId: userId || '__NO_USER__' }; @@ -479,7 +486,7 @@ export const getListingsForMap = ({ jobId, userId = null, isAdmin = false } = {} * @returns {{title: string|null, address: string|null, price: number|null}[]} */ export const getAllEntriesFromListings = () => { - return SqliteConnection.query(`SELECT title, address, price FROM listings`); + return SqliteConnection.query(`SELECT title, address, price FROM listings WHERE manually_deleted = 0`); }; /** @@ -493,6 +500,7 @@ export const getGeocoordinatesByAddress = (address) => { `SELECT latitude, longitude FROM listings WHERE address = @address + AND manually_deleted = 0 AND latitude IS NOT NULL AND longitude IS NOT NULL AND latitude != -1 @@ -515,6 +523,7 @@ export const getListingsToCalculateDistance = (jobId) => { FROM listings WHERE job_id = @jobId AND is_active = 1 + AND manually_deleted = 0 AND latitude IS NOT NULL AND longitude IS NOT NULL AND distance_to_destination IS NULL`, @@ -535,6 +544,7 @@ export const getListingsForUserToCalculateDistance = (userId) => { JOIN jobs j ON l.job_id = j.id WHERE j.user_id = @userId AND l.is_active = 1 + AND l.manually_deleted = 0 AND l.latitude IS NOT NULL AND l.longitude IS NOT NULL`, { userId }, diff --git a/lib/services/storage/migrations/sql/10.soft-delete-listings.js b/lib/services/storage/migrations/sql/10.soft-delete-listings.js new file mode 100644 index 0000000..0222769 --- /dev/null +++ b/lib/services/storage/migrations/sql/10.soft-delete-listings.js @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2026 by Christian Kellner. + * Licensed under Apache-2.0 with Commons Clause and Attribution/Naming Clause + */ + +export function up(db) { + // 1. Add manually_deleted column + db.exec(`ALTER TABLE listings ADD COLUMN manually_deleted INTEGER NOT NULL DEFAULT 0;`); + + // 2. Remove change_set column + try { + db.exec(`ALTER TABLE listings DROP COLUMN change_set;`); + } catch { + // if column does not exists for whatever reason + } +}