diff --git a/migrations/0103.sql b/migrations/0103.sql index babfc48f..127f9da7 100644 --- a/migrations/0103.sql +++ b/migrations/0103.sql @@ -141,20 +141,3 @@ WHERE pr.parent_product_id != pr.sub_product_id GROUP BY pr.sub_product_id HAVING SUM(s.amount) > 0; -DROP VIEW products_resolved; -CREATE VIEW products_resolved AS -SELECT - p.parent_product_id parent_product_id, - p.id as sub_product_id -FROM products p - WHERE p.parent_product_id IS NOT NULL - AND p.active = 1 - -UNION - -SELECT - p.id parent_product_id, - p.id as sub_product_id -FROM products p - WHERE p.parent_product_id IS NULL - AND p.active = 1; diff --git a/migrations/0106.sql b/migrations/0106.sql index 753cece3..f7eab4dd 100644 --- a/migrations/0106.sql +++ b/migrations/0106.sql @@ -1,29 +1,61 @@ -CREATE INDEX ix_products_performance1 ON products ( - parent_product_id -); - -CREATE INDEX ix_products_performance2 ON products ( - CASE WHEN parent_product_id IS NULL THEN id ELSE parent_product_id END, - active -); - -CREATE INDEX ix_stock_performance1 ON stock ( - product_id, - open, - best_before_date, - amount -); - -DROP VIEW products_resolved; -CREATE VIEW products_resolved +DROP VIEW stock_missing_products_including_opened; +CREATE VIEW stock_missing_products_including_opened AS + +/* This is basically the same view as stock_missing_products, but the column "amount_missing" includes opened amounts */ + +-- Products WITHOUT sub products where the amount of the sub products SHOULD NOT be cumulated SELECT - CASE - WHEN p.parent_product_id IS NULL THEN - p.id - ELSE - p.parent_product_id - END AS parent_product_id, - p.id as sub_product_id + p.id, + MAX(p.name) AS name, + p.min_stock_amount - (IFNULL(SUM(s.amount), 0) - IFNULL(SUM(s.amount_opened), 0)) AS amount_missing, + CASE WHEN IFNULL(SUM(s.amount), 0) > 0 THEN 1 ELSE 0 END AS is_partly_in_stock +FROM products_view p +LEFT JOIN stock_current s + ON p.id = s.product_id +WHERE p.min_stock_amount != 0 + AND p.cumulate_min_stock_amount_of_sub_products = 0 + AND p.has_sub_products = 0 + AND p.parent_product_id IS NULL +GROUP BY p.id +HAVING IFNULL(SUM(s.amount), 0) - IFNULL(SUM(s.amount_opened), 0) < p.min_stock_amount + +UNION + +-- Parent products WITH sub products where the amount of the sub products SHOULD be cumulated +SELECT + p.id, + MAX(p.name) AS name, + SUM(sub_p.min_stock_amount) - (IFNULL(SUM(s.amount_aggregated), 0) - IFNULL(SUM(s.amount_opened_aggregated), 0)) AS amount_missing, + CASE WHEN IFNULL(SUM(s.amount), 0) > 0 THEN 1 ELSE 0 END AS is_partly_in_stock +FROM products_view p +JOIN products_resolved pr + ON p.id = pr.parent_product_id +JOIN products sub_p + ON pr.sub_product_id = sub_p.id +LEFT JOIN stock_current s + ON pr.sub_product_id = s.product_id +WHERE sub_p.min_stock_amount != 0 + AND p.cumulate_min_stock_amount_of_sub_products = 1 +GROUP BY p.id +HAVING IFNULL(SUM(s.amount_aggregated), 0) - IFNULL(SUM(s.amount_opened_aggregated), 0) < SUM(sub_p.min_stock_amount) + +UNION + +-- Sub products where the amount SHOULD NOT be cumulated into the parent product +SELECT + sub_p.id, + MAX(sub_p.name) AS name, + SUM(sub_p.min_stock_amount) - (IFNULL(SUM(s.amount), 0) - IFNULL(SUM(s.amount_opened), 0)) AS amount_missing, + CASE WHEN IFNULL(SUM(s.amount), 0) > 0 THEN 1 ELSE 0 END AS is_partly_in_stock FROM products p -WHERE p.active = 1; +JOIN products_resolved pr + ON p.id = pr.parent_product_id +JOIN products sub_p + ON pr.sub_product_id = sub_p.id +LEFT JOIN stock_current s + ON pr.sub_product_id = s.product_id +WHERE sub_p.min_stock_amount != 0 + AND p.cumulate_min_stock_amount_of_sub_products = 0 +GROUP BY sub_p.id +HAVING IFNULL(SUM(s.amount), 0) - IFNULL(SUM(s.amount_opened), 0) < sub_p.min_stock_amount; diff --git a/migrations/0107.sql b/migrations/0107.sql index f7eab4dd..c0956ed4 100644 --- a/migrations/0107.sql +++ b/migrations/0107.sql @@ -1,61 +1,27 @@ -DROP VIEW stock_missing_products_including_opened; -CREATE VIEW stock_missing_products_including_opened -AS +DELETE FROM shopping_list +WHERE shopping_list_id NOT IN (SELECT id FROM shopping_lists); -/* This is basically the same view as stock_missing_products, but the column "amount_missing" includes opened amounts */ +CREATE TRIGGER remove_items_from_deleted_shopping_list AFTER DELETE ON shopping_lists +BEGIN + DELETE FROM shopping_list WHERE shopping_list_id = OLD.id; +END; --- Products WITHOUT sub products where the amount of the sub products SHOULD NOT be cumulated -SELECT - p.id, - MAX(p.name) AS name, - p.min_stock_amount - (IFNULL(SUM(s.amount), 0) - IFNULL(SUM(s.amount_opened), 0)) AS amount_missing, - CASE WHEN IFNULL(SUM(s.amount), 0) > 0 THEN 1 ELSE 0 END AS is_partly_in_stock -FROM products_view p -LEFT JOIN stock_current s - ON p.id = s.product_id -WHERE p.min_stock_amount != 0 - AND p.cumulate_min_stock_amount_of_sub_products = 0 - AND p.has_sub_products = 0 - AND p.parent_product_id IS NULL -GROUP BY p.id -HAVING IFNULL(SUM(s.amount), 0) - IFNULL(SUM(s.amount_opened), 0) < p.min_stock_amount +CREATE TRIGGER prevent_infinite_nested_recipes_INS BEFORE INSERT ON recipes_nestings +BEGIN + SELECT CASE WHEN(( + SELECT 1 + FROM recipes_nestings_resolved rnr + WHERE NEW.recipe_id = rnr.includes_recipe_id + AND NEW.includes_recipe_id = rnr.recipe_id + ) NOTNULL) THEN RAISE(ABORT, "Recursive nested recipe detected") END; +END; -UNION - --- Parent products WITH sub products where the amount of the sub products SHOULD be cumulated -SELECT - p.id, - MAX(p.name) AS name, - SUM(sub_p.min_stock_amount) - (IFNULL(SUM(s.amount_aggregated), 0) - IFNULL(SUM(s.amount_opened_aggregated), 0)) AS amount_missing, - CASE WHEN IFNULL(SUM(s.amount), 0) > 0 THEN 1 ELSE 0 END AS is_partly_in_stock -FROM products_view p -JOIN products_resolved pr - ON p.id = pr.parent_product_id -JOIN products sub_p - ON pr.sub_product_id = sub_p.id -LEFT JOIN stock_current s - ON pr.sub_product_id = s.product_id -WHERE sub_p.min_stock_amount != 0 - AND p.cumulate_min_stock_amount_of_sub_products = 1 -GROUP BY p.id -HAVING IFNULL(SUM(s.amount_aggregated), 0) - IFNULL(SUM(s.amount_opened_aggregated), 0) < SUM(sub_p.min_stock_amount) - -UNION - --- Sub products where the amount SHOULD NOT be cumulated into the parent product -SELECT - sub_p.id, - MAX(sub_p.name) AS name, - SUM(sub_p.min_stock_amount) - (IFNULL(SUM(s.amount), 0) - IFNULL(SUM(s.amount_opened), 0)) AS amount_missing, - CASE WHEN IFNULL(SUM(s.amount), 0) > 0 THEN 1 ELSE 0 END AS is_partly_in_stock -FROM products p -JOIN products_resolved pr - ON p.id = pr.parent_product_id -JOIN products sub_p - ON pr.sub_product_id = sub_p.id -LEFT JOIN stock_current s - ON pr.sub_product_id = s.product_id -WHERE sub_p.min_stock_amount != 0 - AND p.cumulate_min_stock_amount_of_sub_products = 0 -GROUP BY sub_p.id -HAVING IFNULL(SUM(s.amount), 0) - IFNULL(SUM(s.amount_opened), 0) < sub_p.min_stock_amount; +CREATE TRIGGER prevent_infinite_nested_recipes_UPD BEFORE UPDATE ON recipes_nestings +BEGIN + SELECT CASE WHEN(( + SELECT 1 + FROM recipes_nestings_resolved rnr + WHERE NEW.recipe_id = rnr.includes_recipe_id + AND NEW.includes_recipe_id = rnr.recipe_id + ) NOTNULL) THEN RAISE(ABORT, "Recursive nested recipe detected") END; +END; diff --git a/migrations/0108.sql b/migrations/0108.sql index c0956ed4..4b4e511a 100644 --- a/migrations/0108.sql +++ b/migrations/0108.sql @@ -1,27 +1,46 @@ -DELETE FROM shopping_list -WHERE shopping_list_id NOT IN (SELECT id FROM shopping_lists); +DROP VIEW stock_current; +CREATE VIEW stock_current +AS +SELECT + pr.parent_product_id AS product_id, + IFNULL((SELECT SUM(amount) FROM stock WHERE product_id = pr.parent_product_id), 0) AS amount, + SUM(s.amount * IFNULL(qucr.factor, 1.0)) AS amount_aggregated, + IFNULL(ROUND((SELECT SUM(IFNULL(price,0) * amount) FROM stock WHERE product_id = pr.parent_product_id), 2), 0) AS value, + MIN(s.best_before_date) AS best_before_date, + IFNULL((SELECT SUM(amount) FROM stock WHERE product_id = pr.parent_product_id AND open = 1), 0) AS amount_opened, + IFNULL((SELECT SUM(amount) FROM stock WHERE product_id IN (SELECT sub_product_id FROM products_resolved WHERE parent_product_id = pr.parent_product_id) AND open = 1), 0) * IFNULL(qucr.factor, 1) AS amount_opened_aggregated, + CASE WHEN p_sub.parent_product_id IS NOT NULL THEN 1 ELSE 0 END AS is_aggregated_amount +FROM products_resolved pr +JOIN stock s + ON pr.sub_product_id = s.product_id +JOIN products p_parent + ON pr.parent_product_id = p_parent.id + AND p_parent.active = 1 +JOIN products p_sub + ON pr.sub_product_id = p_sub.id + AND p_sub.active = 1 +LEFT JOIN quantity_unit_conversions_resolved qucr + ON pr.sub_product_id = qucr.product_id + AND p_sub.qu_id_stock = qucr.from_qu_id + AND p_parent.qu_id_stock = qucr.to_qu_id +GROUP BY pr.parent_product_id +HAVING SUM(s.amount) > 0 -CREATE TRIGGER remove_items_from_deleted_shopping_list AFTER DELETE ON shopping_lists -BEGIN - DELETE FROM shopping_list WHERE shopping_list_id = OLD.id; -END; +UNION -CREATE TRIGGER prevent_infinite_nested_recipes_INS BEFORE INSERT ON recipes_nestings -BEGIN - SELECT CASE WHEN(( - SELECT 1 - FROM recipes_nestings_resolved rnr - WHERE NEW.recipe_id = rnr.includes_recipe_id - AND NEW.includes_recipe_id = rnr.recipe_id - ) NOTNULL) THEN RAISE(ABORT, "Recursive nested recipe detected") END; -END; - -CREATE TRIGGER prevent_infinite_nested_recipes_UPD BEFORE UPDATE ON recipes_nestings -BEGIN - SELECT CASE WHEN(( - SELECT 1 - FROM recipes_nestings_resolved rnr - WHERE NEW.recipe_id = rnr.includes_recipe_id - AND NEW.includes_recipe_id = rnr.recipe_id - ) NOTNULL) THEN RAISE(ABORT, "Recursive nested recipe detected") END; -END; +-- This is the same as above but sub products not rolled up (no QU conversion and column is_aggregated_amount = 0 here) +SELECT + pr.sub_product_id AS product_id, + SUM(s.amount) AS amount, + SUM(s.amount) AS amount_aggregated, + ROUND(SUM(IFNULL(s.price, 0) * s.amount), 2) AS value, + MIN(s.best_before_date) AS best_before_date, + IFNULL((SELECT SUM(amount) FROM stock WHERE product_id = s.product_id AND open = 1), 0) AS amount_opened, + IFNULL((SELECT SUM(amount) FROM stock WHERE product_id = s.product_id AND open = 1), 0) AS amount_opened_aggregated, + 0 AS is_aggregated_amount +FROM products_resolved pr +JOIN stock s + ON pr.sub_product_id = s.product_id +WHERE pr.parent_product_id != pr.sub_product_id +GROUP BY pr.sub_product_id +HAVING SUM(s.amount) > 0; diff --git a/migrations/0109.sql b/migrations/0109.sql index 4b4e511a..5f0c7600 100644 --- a/migrations/0109.sql +++ b/migrations/0109.sql @@ -1,46 +1,22 @@ -DROP VIEW stock_current; -CREATE VIEW stock_current +CREATE VIEW product_price_history AS SELECT - pr.parent_product_id AS product_id, - IFNULL((SELECT SUM(amount) FROM stock WHERE product_id = pr.parent_product_id), 0) AS amount, - SUM(s.amount * IFNULL(qucr.factor, 1.0)) AS amount_aggregated, - IFNULL(ROUND((SELECT SUM(IFNULL(price,0) * amount) FROM stock WHERE product_id = pr.parent_product_id), 2), 0) AS value, - MIN(s.best_before_date) AS best_before_date, - IFNULL((SELECT SUM(amount) FROM stock WHERE product_id = pr.parent_product_id AND open = 1), 0) AS amount_opened, - IFNULL((SELECT SUM(amount) FROM stock WHERE product_id IN (SELECT sub_product_id FROM products_resolved WHERE parent_product_id = pr.parent_product_id) AND open = 1), 0) * IFNULL(qucr.factor, 1) AS amount_opened_aggregated, - CASE WHEN p_sub.parent_product_id IS NOT NULL THEN 1 ELSE 0 END AS is_aggregated_amount -FROM products_resolved pr -JOIN stock s - ON pr.sub_product_id = s.product_id -JOIN products p_parent - ON pr.parent_product_id = p_parent.id - AND p_parent.active = 1 -JOIN products p_sub - ON pr.sub_product_id = p_sub.id - AND p_sub.active = 1 -LEFT JOIN quantity_unit_conversions_resolved qucr - ON pr.sub_product_id = qucr.product_id - AND p_sub.qu_id_stock = qucr.from_qu_id - AND p_parent.qu_id_stock = qucr.to_qu_id -GROUP BY pr.parent_product_id -HAVING SUM(s.amount) > 0 - -UNION - --- This is the same as above but sub products not rolled up (no QU conversion and column is_aggregated_amount = 0 here) -SELECT - pr.sub_product_id AS product_id, - SUM(s.amount) AS amount, - SUM(s.amount) AS amount_aggregated, - ROUND(SUM(IFNULL(s.price, 0) * s.amount), 2) AS value, - MIN(s.best_before_date) AS best_before_date, - IFNULL((SELECT SUM(amount) FROM stock WHERE product_id = s.product_id AND open = 1), 0) AS amount_opened, - IFNULL((SELECT SUM(amount) FROM stock WHERE product_id = s.product_id AND open = 1), 0) AS amount_opened_aggregated, - 0 AS is_aggregated_amount -FROM products_resolved pr -JOIN stock s - ON pr.sub_product_id = s.product_id -WHERE pr.parent_product_id != pr.sub_product_id -GROUP BY pr.sub_product_id -HAVING SUM(s.amount) > 0; + sl.product_id AS id, -- Dummy, LessQL needs an id column + sl.product_id, + sl.price, + sl.purchased_date, + sl.shopping_location_id +FROM stock_log sl +WHERE sl.transaction_type IN ('purchase', 'inventory-correction', 'stock-edit-new') + AND sl.undone = 0 + AND sl.price IS NOT NULL + AND sl.id NOT IN ( + -- These are edited purchase and inventory-correction rows + SELECT sl_origin.id + FROM stock_log sl_origin + JOIN stock_log sl_edit + ON sl_origin.stock_id = sl_edit.stock_id + AND sl_edit.transaction_type = 'stock-edit-new' + AND sl_edit.id > sl_origin.id + WHERE sl_origin.transaction_type IN ('purchase', 'inventory-correction') + ); diff --git a/migrations/0110.sql b/migrations/0110.sql index 5f0c7600..8fd4fd8e 100644 --- a/migrations/0110.sql +++ b/migrations/0110.sql @@ -1,22 +1,134 @@ -CREATE VIEW product_price_history +CREATE TABLE user_permissions +( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, + permission_id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + + UNIQUE (user_id, permission_id) +); + +CREATE TABLE permission_hierarchy +( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, + name TEXT NOT NULL UNIQUE, + parent INTEGER NULL -- If the user has the parent permission, the user also has the child permission +); + +-- The root/ADMIN permission +INSERT INTO permission_hierarchy + (name, parent) +VALUES + ('ADMIN', NULL); + +-- User add/edit/read permissions +INSERT INTO permission_hierarchy + (name, parent) +VALUES + ('USERS', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')); + +INSERT INTO permission_hierarchy + (name, parent) +VALUES + ('USERS_CREATE', (SELECT id FROM permission_hierarchy WHERE name = 'USERS')); + +INSERT INTO permission_hierarchy + (name, parent) +VALUES + ('USERS_EDIT', last_insert_rowid()); + +INSERT INTO permission_hierarchy + (name, parent) +VALUES + ('USERS_READ', last_insert_rowid()), + ('USERS_EDIT_SELF', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')); + +-- Base permissions per major feature +INSERT INTO permission_hierarchy + (name, parent) +VALUES + ('STOCK', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')), + ('SHOPPINGLIST', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')), + ('RECIPES', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')), + ('CHORES', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')), + ('BATTERIES', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')), + ('TASKS', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')), + ('EQUIPMENT', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')), + ('CALENDAR', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')); + +-- Sub feature permissions +INSERT INTO permission_hierarchy + (name, parent) +VALUES + -- Stock + ('STOCK_PURCHASE', (SELECT id FROM permission_hierarchy WHERE name = 'STOCK')), + ('STOCK_CONSUME', (SELECT id FROM permission_hierarchy WHERE name = 'STOCK')), + ('STOCK_INVENTORY', (SELECT id FROM permission_hierarchy WHERE name = 'STOCK')), + ('STOCK_TRANSFER', (SELECT id FROM permission_hierarchy WHERE name = 'STOCK')), + ('STOCK_OPEN', (SELECT id FROM permission_hierarchy WHERE name = 'STOCK')), + ('STOCK_EDIT', (SELECT id FROM permission_hierarchy WHERE name = 'STOCK')), + + -- Shopping list + ('SHOPPINGLIST_ITEMS_ADD', (SELECT id FROM permission_hierarchy WHERE name = 'SHOPPINGLIST')), + ('SHOPPINGLIST_ITEMS_DELETE', (SELECT id FROM permission_hierarchy WHERE name = 'SHOPPINGLIST')), + + -- Recipes + ('RECIPES_MEALPLAN', (SELECT id FROM permission_hierarchy WHERE name = 'RECIPES')), + + -- Chores + ('CHORE_TRACK_EXECUTION', (SELECT id FROM permission_hierarchy WHERE name = 'CHORES')), + ('CHORE_UNDO_EXECUTION', (SELECT id FROM permission_hierarchy WHERE name = 'CHORES')), + + -- Batteries + ('BATTERIES_TRACK_CHARGE_CYCLE', (SELECT id FROM permission_hierarchy WHERE name = 'BATTERIES')), + ('BATTERIES_UNDO_CHARGE_CYCLE', (SELECT id FROM permission_hierarchy WHERE name = 'BATTERIES')), + + -- Tasks + ('TASKS_UNDO_EXECUTION', (SELECT id FROM permission_hierarchy WHERE name = 'TASKS')), + ('TASKS_MARK_COMPLETED', (SELECT id FROM permission_hierarchy WHERE name = 'TASKS')), + + -- Others + ('MASTER_DATA_EDIT', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')); + +-- All existing users get the ADMIN permission +INSERT INTO user_permissions + (permission_id, user_id) +SELECT (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN'), id +FROM users; + +CREATE VIEW permission_tree +AS +WITH RECURSIVE perm AS ( + SELECT id AS root, id AS child, name, parent + FROM permission_hierarchy + UNION + SELECT perm.root, ph.id, ph.name, ph.id + FROM permission_hierarchy ph, perm + WHERE ph.parent = perm.child +) +SELECT root AS id, name AS name +FROM perm; + +CREATE VIEW user_permissions_resolved AS SELECT - sl.product_id AS id, -- Dummy, LessQL needs an id column - sl.product_id, - sl.price, - sl.purchased_date, - sl.shopping_location_id -FROM stock_log sl -WHERE sl.transaction_type IN ('purchase', 'inventory-correction', 'stock-edit-new') - AND sl.undone = 0 - AND sl.price IS NOT NULL - AND sl.id NOT IN ( - -- These are edited purchase and inventory-correction rows - SELECT sl_origin.id - FROM stock_log sl_origin - JOIN stock_log sl_edit - ON sl_origin.stock_id = sl_edit.stock_id - AND sl_edit.transaction_type = 'stock-edit-new' - AND sl_edit.id > sl_origin.id - WHERE sl_origin.transaction_type IN ('purchase', 'inventory-correction') - ); + u.id AS id, -- Dummy for LessQL + u.id AS user_id, + pt.name AS permission_name +FROM permission_tree pt, users u +WHERE pt.id IN (SELECT permission_id FROM user_permissions sub_up WHERE sub_up.user_id = u.id); + +CREATE VIEW uihelper_user_permissions +AS +SELECT + ph.id AS id, + u.id AS user_id, + ph.name AS permission_name, + ph.id AS permission_id, + (ph.name IN ( + SELECT pc.permission_name + FROM user_permissions_resolved pc + WHERE pc.user_id = u.id + ) + ) AS has_permission, + ph.parent AS parent +FROM users u, permission_hierarchy ph; diff --git a/migrations/0111.sql b/migrations/0111.sql index 8fd4fd8e..b0b464ce 100644 --- a/migrations/0111.sql +++ b/migrations/0111.sql @@ -1,134 +1,16 @@ -CREATE TABLE user_permissions -( - id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, - permission_id INTEGER NOT NULL, - user_id INTEGER NOT NULL, +DELETE FROM userfield_values +WHERE IFNULL(value, '') = ''; - UNIQUE (user_id, permission_id) -); +CREATE TRIGGER prevent_empty_userfields_INS AFTER INSERT ON userfield_values +BEGIN + DELETE FROM userfield_values + WHERE id = NEW.id + AND IFNULL(value, '') = ''; +END; -CREATE TABLE permission_hierarchy -( - id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, - name TEXT NOT NULL UNIQUE, - parent INTEGER NULL -- If the user has the parent permission, the user also has the child permission -); - --- The root/ADMIN permission -INSERT INTO permission_hierarchy - (name, parent) -VALUES - ('ADMIN', NULL); - --- User add/edit/read permissions -INSERT INTO permission_hierarchy - (name, parent) -VALUES - ('USERS', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')); - -INSERT INTO permission_hierarchy - (name, parent) -VALUES - ('USERS_CREATE', (SELECT id FROM permission_hierarchy WHERE name = 'USERS')); - -INSERT INTO permission_hierarchy - (name, parent) -VALUES - ('USERS_EDIT', last_insert_rowid()); - -INSERT INTO permission_hierarchy - (name, parent) -VALUES - ('USERS_READ', last_insert_rowid()), - ('USERS_EDIT_SELF', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')); - --- Base permissions per major feature -INSERT INTO permission_hierarchy - (name, parent) -VALUES - ('STOCK', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')), - ('SHOPPINGLIST', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')), - ('RECIPES', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')), - ('CHORES', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')), - ('BATTERIES', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')), - ('TASKS', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')), - ('EQUIPMENT', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')), - ('CALENDAR', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')); - --- Sub feature permissions -INSERT INTO permission_hierarchy - (name, parent) -VALUES - -- Stock - ('STOCK_PURCHASE', (SELECT id FROM permission_hierarchy WHERE name = 'STOCK')), - ('STOCK_CONSUME', (SELECT id FROM permission_hierarchy WHERE name = 'STOCK')), - ('STOCK_INVENTORY', (SELECT id FROM permission_hierarchy WHERE name = 'STOCK')), - ('STOCK_TRANSFER', (SELECT id FROM permission_hierarchy WHERE name = 'STOCK')), - ('STOCK_OPEN', (SELECT id FROM permission_hierarchy WHERE name = 'STOCK')), - ('STOCK_EDIT', (SELECT id FROM permission_hierarchy WHERE name = 'STOCK')), - - -- Shopping list - ('SHOPPINGLIST_ITEMS_ADD', (SELECT id FROM permission_hierarchy WHERE name = 'SHOPPINGLIST')), - ('SHOPPINGLIST_ITEMS_DELETE', (SELECT id FROM permission_hierarchy WHERE name = 'SHOPPINGLIST')), - - -- Recipes - ('RECIPES_MEALPLAN', (SELECT id FROM permission_hierarchy WHERE name = 'RECIPES')), - - -- Chores - ('CHORE_TRACK_EXECUTION', (SELECT id FROM permission_hierarchy WHERE name = 'CHORES')), - ('CHORE_UNDO_EXECUTION', (SELECT id FROM permission_hierarchy WHERE name = 'CHORES')), - - -- Batteries - ('BATTERIES_TRACK_CHARGE_CYCLE', (SELECT id FROM permission_hierarchy WHERE name = 'BATTERIES')), - ('BATTERIES_UNDO_CHARGE_CYCLE', (SELECT id FROM permission_hierarchy WHERE name = 'BATTERIES')), - - -- Tasks - ('TASKS_UNDO_EXECUTION', (SELECT id FROM permission_hierarchy WHERE name = 'TASKS')), - ('TASKS_MARK_COMPLETED', (SELECT id FROM permission_hierarchy WHERE name = 'TASKS')), - - -- Others - ('MASTER_DATA_EDIT', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')); - --- All existing users get the ADMIN permission -INSERT INTO user_permissions - (permission_id, user_id) -SELECT (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN'), id -FROM users; - -CREATE VIEW permission_tree -AS -WITH RECURSIVE perm AS ( - SELECT id AS root, id AS child, name, parent - FROM permission_hierarchy - UNION - SELECT perm.root, ph.id, ph.name, ph.id - FROM permission_hierarchy ph, perm - WHERE ph.parent = perm.child -) -SELECT root AS id, name AS name -FROM perm; - -CREATE VIEW user_permissions_resolved -AS -SELECT - u.id AS id, -- Dummy for LessQL - u.id AS user_id, - pt.name AS permission_name -FROM permission_tree pt, users u -WHERE pt.id IN (SELECT permission_id FROM user_permissions sub_up WHERE sub_up.user_id = u.id); - -CREATE VIEW uihelper_user_permissions -AS -SELECT - ph.id AS id, - u.id AS user_id, - ph.name AS permission_name, - ph.id AS permission_id, - (ph.name IN ( - SELECT pc.permission_name - FROM user_permissions_resolved pc - WHERE pc.user_id = u.id - ) - ) AS has_permission, - ph.parent AS parent -FROM users u, permission_hierarchy ph; +CREATE TRIGGER prevent_empty_userfields_UPD AFTER UPDATE ON userfield_values +BEGIN + DELETE FROM userfield_values + WHERE id = NEW.id + AND IFNULL(value, '') = ''; +END; diff --git a/migrations/0112.sql b/migrations/0112.sql index b0b464ce..c48371c5 100644 --- a/migrations/0112.sql +++ b/migrations/0112.sql @@ -1,16 +1,36 @@ -DELETE FROM userfield_values -WHERE IFNULL(value, '') = ''; +DROP VIEW userfield_values_resolved; +CREATE VIEW userfield_values_resolved +AS +SELECT + u.id, -- Dummy, LessQL needs an id column + u.entity, + u.name, + u.caption, + u.type, + u.show_as_column_in_tables, + u.row_created_timestamp, + u.config, + uv.object_id, + uv.value +FROM userfields u +JOIN userfield_values uv + ON u.id = uv.field_id -CREATE TRIGGER prevent_empty_userfields_INS AFTER INSERT ON userfield_values -BEGIN - DELETE FROM userfield_values - WHERE id = NEW.id - AND IFNULL(value, '') = ''; -END; +UNION -CREATE TRIGGER prevent_empty_userfields_UPD AFTER UPDATE ON userfield_values -BEGIN - DELETE FROM userfield_values - WHERE id = NEW.id - AND IFNULL(value, '') = ''; -END; +-- Kind of a hack, include userentity userfields also for the table userobjects +SELECT + u.id, -- Dummy, LessQL needs an id column, + 'userobjects', + u.name, + u.caption, + u.type, + u.show_as_column_in_tables, + u.row_created_timestamp, + u.config, + uv.object_id, + uv.value +FROM userfields u +JOIN userfield_values uv + ON u.id = uv.field_id +WHERE u.entity like 'userentity-%'; diff --git a/migrations/0113.sql b/migrations/0113.sql index c48371c5..66493d87 100644 --- a/migrations/0113.sql +++ b/migrations/0113.sql @@ -1,36 +1,92 @@ -DROP VIEW userfield_values_resolved; -CREATE VIEW userfield_values_resolved +CREATE VIEW users_dto AS SELECT - u.id, -- Dummy, LessQL needs an id column - u.entity, - u.name, - u.caption, - u.type, - u.show_as_column_in_tables, - u.row_created_timestamp, - u.config, - uv.object_id, - uv.value -FROM userfields u -JOIN userfield_values uv - ON u.id = uv.field_id + id, + username, + first_name, + last_name, + row_created_timestamp, + (CASE + WHEN IFNULL(first_name, '') = '' AND IFNULL(last_name, '') != '' THEN last_name + WHEN IFNULL(last_name, '') = '' AND IFNULL(first_name, '') != '' THEN first_name + WHEN IFNULL(last_name, '') != '' AND IFNULL(first_name, '') != '' THEN first_name || ' ' || last_name + ELSE username + END + ) AS display_name +FROM users; -UNION - --- Kind of a hack, include userentity userfields also for the table userobjects +DROP VIEW chores_current; +CREATE VIEW chores_current +AS SELECT - u.id, -- Dummy, LessQL needs an id column, - 'userobjects', - u.name, - u.caption, - u.type, - u.show_as_column_in_tables, - u.row_created_timestamp, - u.config, - uv.object_id, - uv.value -FROM userfields u -JOIN userfield_values uv - ON u.id = uv.field_id -WHERE u.entity like 'userentity-%'; + x.chore_id AS id, -- Dummy, LessQL needs an id column + x.chore_id, + x.chore_name, + x.last_tracked_time, + CASE WHEN x.rollover = 1 AND DATETIME('now', 'localtime') > x.next_estimated_execution_time THEN + DATETIME(STRFTIME('%Y-%m-%d', DATETIME('now', 'localtime')) || ' ' || STRFTIME('%H:%M:%S', x.next_estimated_execution_time)) + ELSE + x.next_estimated_execution_time + END AS next_estimated_execution_time, + x.track_date_only, + x.next_execution_assigned_to_user_id +FROM ( + +SELECT + h.id AS chore_id, + h.name AS chore_name, + MAX(l.tracked_time) AS last_tracked_time, + CASE h.period_type + WHEN 'manually' THEN '2999-12-31 23:59:59' + WHEN 'dynamic-regular' THEN DATETIME(MAX(l.tracked_time), '+' || CAST(h.period_days AS TEXT) || ' day') + WHEN 'daily' THEN DATETIME(IFNULL(MAX(l.tracked_time), DATETIME('now', 'localtime')), '+' || CAST(h.period_interval AS TEXT) || ' day') + WHEN 'weekly' THEN ( + SELECT next + FROM ( + SELECT 'sunday' AS day, DATETIME(COALESCE((SELECT tracked_time FROM chores_log WHERE chore_id = h.id ORDER BY tracked_time DESC LIMIT 1), DATETIME('now', 'localtime')), '1 days', '+' || CAST((h.period_interval - 1) * 7 AS TEXT) || ' days', 'weekday 0') AS next + UNION + SELECT 'monday' AS day, DATETIME(COALESCE((SELECT tracked_time FROM chores_log WHERE chore_id = h.id ORDER BY tracked_time DESC LIMIT 1), DATETIME('now', 'localtime')), '1 days', '+' || CAST((h.period_interval - 1) * 7 AS TEXT) || ' days', 'weekday 1') AS next + UNION + SELECT 'tuesday' AS day, DATETIME(COALESCE((SELECT tracked_time FROM chores_log WHERE chore_id = h.id ORDER BY tracked_time DESC LIMIT 1), DATETIME('now', 'localtime')), '1 days', '+' || CAST((h.period_interval - 1) * 7 AS TEXT) || ' days', 'weekday 2') AS next + UNION + SELECT 'wednesday' AS day, DATETIME(COALESCE((SELECT tracked_time FROM chores_log WHERE chore_id = h.id ORDER BY tracked_time DESC LIMIT 1), DATETIME('now', 'localtime')), '1 days', '+' || CAST((h.period_interval - 1) * 7 AS TEXT) || ' days', 'weekday 3') AS next + UNION + SELECT 'thursday' AS day, DATETIME(COALESCE((SELECT tracked_time FROM chores_log WHERE chore_id = h.id ORDER BY tracked_time DESC LIMIT 1), DATETIME('now', 'localtime')), '1 days', '+' || CAST((h.period_interval - 1) * 7 AS TEXT) || ' days', 'weekday 4') AS next + UNION + SELECT 'friday' AS day, DATETIME(COALESCE((SELECT tracked_time FROM chores_log WHERE chore_id = h.id ORDER BY tracked_time DESC LIMIT 1), DATETIME('now', 'localtime')), '1 days', '+' || CAST((h.period_interval - 1) * 7 AS TEXT) || ' days', 'weekday 5') AS next + UNION + SELECT 'saturday' AS day, DATETIME(COALESCE((SELECT tracked_time FROM chores_log WHERE chore_id = h.id ORDER BY tracked_time DESC LIMIT 1), DATETIME('now', 'localtime')), '1 days', '+' || CAST((h.period_interval - 1) * 7 AS TEXT) || ' days', 'weekday 6') AS next + ) + WHERE INSTR(period_config, day) > 0 + ORDER BY next + LIMIT 1 + ) + WHEN 'monthly' THEN DATETIME(IFNULL(MAX(l.tracked_time), DATETIME('now', 'localtime')), '+' || CAST(h.period_interval AS TEXT) || ' month', 'start of month', '+' || CAST(h.period_days - 1 AS TEXT) || ' day') + WHEN 'yearly' THEN DATETIME(IFNULL(MAX(l.tracked_time), DATETIME('now', 'localtime')), '+' || CAST(h.period_interval AS TEXT) || ' years') + END AS next_estimated_execution_time, + h.track_date_only, + h.rollover, + h.next_execution_assigned_to_user_id +FROM chores h +LEFT JOIN chores_log l + ON h.id = l.chore_id + AND l.undone = 0 +GROUP BY h.id, h.name, h.period_days + +) x; + +DROP VIEW batteries_current; +CREATE VIEW batteries_current +AS +SELECT + b.id, -- Dummy, LessQL needs an id column + b.id AS battery_id, + MAX(l.tracked_time) AS last_tracked_time, + CASE WHEN b.charge_interval_days = 0 + THEN '2999-12-31 23:59:59' + ELSE datetime(MAX(l.tracked_time), '+' || CAST(b.charge_interval_days AS TEXT) || ' day') + END AS next_estimated_charge_time +FROM batteries b +LEFT JOIN battery_charge_cycles l + ON b.id = l.battery_id +GROUP BY b.id, b.charge_interval_days; diff --git a/migrations/0114.sql b/migrations/0114.sql index 66493d87..65b7840e 100644 --- a/migrations/0114.sql +++ b/migrations/0114.sql @@ -1,92 +1,51 @@ -CREATE VIEW users_dto +ALTER TABLE stock_log +ADD user_id INTEGER NOT NULL DEFAULT 1; + +CREATE VIEW uihelper_stock_journal AS SELECT - id, - username, - first_name, - last_name, - row_created_timestamp, - (CASE - WHEN IFNULL(first_name, '') = '' AND IFNULL(last_name, '') != '' THEN last_name - WHEN IFNULL(last_name, '') = '' AND IFNULL(first_name, '') != '' THEN first_name - WHEN IFNULL(last_name, '') != '' AND IFNULL(first_name, '') != '' THEN first_name || ' ' || last_name - ELSE username - END - ) AS display_name -FROM users; + sl.id, + sl.row_created_timestamp, + sl.correlation_id, + sl.undone, + sl.undone_timestamp, + sl.row_created_timestamp, + sl.transaction_type, + sl.spoiled, + sl.amount, + sl.location_id, + l.name AS location_name, + p.name AS product_name, + qu.name AS qu_name, + qu.name_plural AS qu_name_plural, + u.display_name AS user_display_name +FROM stock_log sl +JOIN users_dto u + ON sl.user_id = u.id +JOIN products p + ON sl.product_id = p.id +JOIN locations l + ON p.location_id = l.id +JOIN quantity_units qu + ON p.qu_id_stock = qu.id; -DROP VIEW chores_current; -CREATE VIEW chores_current +CREATE VIEW uihelper_stock_journal_summary AS SELECT - x.chore_id AS id, -- Dummy, LessQL needs an id column - x.chore_id, - x.chore_name, - x.last_tracked_time, - CASE WHEN x.rollover = 1 AND DATETIME('now', 'localtime') > x.next_estimated_execution_time THEN - DATETIME(STRFTIME('%Y-%m-%d', DATETIME('now', 'localtime')) || ' ' || STRFTIME('%H:%M:%S', x.next_estimated_execution_time)) - ELSE - x.next_estimated_execution_time - END AS next_estimated_execution_time, - x.track_date_only, - x.next_execution_assigned_to_user_id -FROM ( - -SELECT - h.id AS chore_id, - h.name AS chore_name, - MAX(l.tracked_time) AS last_tracked_time, - CASE h.period_type - WHEN 'manually' THEN '2999-12-31 23:59:59' - WHEN 'dynamic-regular' THEN DATETIME(MAX(l.tracked_time), '+' || CAST(h.period_days AS TEXT) || ' day') - WHEN 'daily' THEN DATETIME(IFNULL(MAX(l.tracked_time), DATETIME('now', 'localtime')), '+' || CAST(h.period_interval AS TEXT) || ' day') - WHEN 'weekly' THEN ( - SELECT next - FROM ( - SELECT 'sunday' AS day, DATETIME(COALESCE((SELECT tracked_time FROM chores_log WHERE chore_id = h.id ORDER BY tracked_time DESC LIMIT 1), DATETIME('now', 'localtime')), '1 days', '+' || CAST((h.period_interval - 1) * 7 AS TEXT) || ' days', 'weekday 0') AS next - UNION - SELECT 'monday' AS day, DATETIME(COALESCE((SELECT tracked_time FROM chores_log WHERE chore_id = h.id ORDER BY tracked_time DESC LIMIT 1), DATETIME('now', 'localtime')), '1 days', '+' || CAST((h.period_interval - 1) * 7 AS TEXT) || ' days', 'weekday 1') AS next - UNION - SELECT 'tuesday' AS day, DATETIME(COALESCE((SELECT tracked_time FROM chores_log WHERE chore_id = h.id ORDER BY tracked_time DESC LIMIT 1), DATETIME('now', 'localtime')), '1 days', '+' || CAST((h.period_interval - 1) * 7 AS TEXT) || ' days', 'weekday 2') AS next - UNION - SELECT 'wednesday' AS day, DATETIME(COALESCE((SELECT tracked_time FROM chores_log WHERE chore_id = h.id ORDER BY tracked_time DESC LIMIT 1), DATETIME('now', 'localtime')), '1 days', '+' || CAST((h.period_interval - 1) * 7 AS TEXT) || ' days', 'weekday 3') AS next - UNION - SELECT 'thursday' AS day, DATETIME(COALESCE((SELECT tracked_time FROM chores_log WHERE chore_id = h.id ORDER BY tracked_time DESC LIMIT 1), DATETIME('now', 'localtime')), '1 days', '+' || CAST((h.period_interval - 1) * 7 AS TEXT) || ' days', 'weekday 4') AS next - UNION - SELECT 'friday' AS day, DATETIME(COALESCE((SELECT tracked_time FROM chores_log WHERE chore_id = h.id ORDER BY tracked_time DESC LIMIT 1), DATETIME('now', 'localtime')), '1 days', '+' || CAST((h.period_interval - 1) * 7 AS TEXT) || ' days', 'weekday 5') AS next - UNION - SELECT 'saturday' AS day, DATETIME(COALESCE((SELECT tracked_time FROM chores_log WHERE chore_id = h.id ORDER BY tracked_time DESC LIMIT 1), DATETIME('now', 'localtime')), '1 days', '+' || CAST((h.period_interval - 1) * 7 AS TEXT) || ' days', 'weekday 6') AS next - ) - WHERE INSTR(period_config, day) > 0 - ORDER BY next - LIMIT 1 - ) - WHEN 'monthly' THEN DATETIME(IFNULL(MAX(l.tracked_time), DATETIME('now', 'localtime')), '+' || CAST(h.period_interval AS TEXT) || ' month', 'start of month', '+' || CAST(h.period_days - 1 AS TEXT) || ' day') - WHEN 'yearly' THEN DATETIME(IFNULL(MAX(l.tracked_time), DATETIME('now', 'localtime')), '+' || CAST(h.period_interval AS TEXT) || ' years') - END AS next_estimated_execution_time, - h.track_date_only, - h.rollover, - h.next_execution_assigned_to_user_id -FROM chores h -LEFT JOIN chores_log l - ON h.id = l.chore_id - AND l.undone = 0 -GROUP BY h.id, h.name, h.period_days - -) x; - -DROP VIEW batteries_current; -CREATE VIEW batteries_current -AS -SELECT - b.id, -- Dummy, LessQL needs an id column - b.id AS battery_id, - MAX(l.tracked_time) AS last_tracked_time, - CASE WHEN b.charge_interval_days = 0 - THEN '2999-12-31 23:59:59' - ELSE datetime(MAX(l.tracked_time), '+' || CAST(b.charge_interval_days AS TEXT) || ' day') - END AS next_estimated_charge_time -FROM batteries b -LEFT JOIN battery_charge_cycles l - ON b.id = l.battery_id -GROUP BY b.id, b.charge_interval_days; + user_id AS id, -- Dummy, LessQL needs an id column + user_id, u.display_name AS user_display_name, + p.name AS product_name, + product_id, + transaction_type, + qu.name AS qu_name, + qu.name_plural AS qu_name_plural, + SUM(amount) AS amount +FROM stock_log sl +JOIN users_dto u + on sl.user_id = u.id +JOIN products p + ON sl.product_id = p.id +JOIN quantity_units qu + ON p.qu_id_stock = qu.id +WHERE undone = 0 +GROUP BY user_id, product_id, transaction_type; diff --git a/migrations/0115.sql b/migrations/0115.sql index 65b7840e..b92a2f52 100644 --- a/migrations/0115.sql +++ b/migrations/0115.sql @@ -1,51 +1 @@ -ALTER TABLE stock_log -ADD user_id INTEGER NOT NULL DEFAULT 1; - -CREATE VIEW uihelper_stock_journal -AS -SELECT - sl.id, - sl.row_created_timestamp, - sl.correlation_id, - sl.undone, - sl.undone_timestamp, - sl.row_created_timestamp, - sl.transaction_type, - sl.spoiled, - sl.amount, - sl.location_id, - l.name AS location_name, - p.name AS product_name, - qu.name AS qu_name, - qu.name_plural AS qu_name_plural, - u.display_name AS user_display_name -FROM stock_log sl -JOIN users_dto u - ON sl.user_id = u.id -JOIN products p - ON sl.product_id = p.id -JOIN locations l - ON p.location_id = l.id -JOIN quantity_units qu - ON p.qu_id_stock = qu.id; - -CREATE VIEW uihelper_stock_journal_summary -AS -SELECT - user_id AS id, -- Dummy, LessQL needs an id column - user_id, u.display_name AS user_display_name, - p.name AS product_name, - product_id, - transaction_type, - qu.name AS qu_name, - qu.name_plural AS qu_name_plural, - SUM(amount) AS amount -FROM stock_log sl -JOIN users_dto u - on sl.user_id = u.id -JOIN products p - ON sl.product_id = p.id -JOIN quantity_units qu - ON p.qu_id_stock = qu.id -WHERE undone = 0 -GROUP BY user_id, product_id, transaction_type; +update user_settings set key = "stock_expiring_soon_days" where key = "stock_expring_soon_days"; diff --git a/migrations/0116.sql b/migrations/0116.sql index b92a2f52..2dfb848c 100644 --- a/migrations/0116.sql +++ b/migrations/0116.sql @@ -1 +1,71 @@ -update user_settings set key = "stock_expiring_soon_days" where key = "stock_expring_soon_days"; +DROP VIEW quantity_unit_conversions_resolved; +CREATE VIEW quantity_unit_conversions_resolved +AS + +-- First: Product "purchase to stock" conversion factor +SELECT + p.id AS id, -- Dummy, LessQL needs an id column + p.id AS product_id, + p.qu_id_purchase AS from_qu_id, + qu_from.name AS from_qu_name, + p.qu_id_stock AS to_qu_id, + qu_to.name AS to_qu_name, + p.qu_factor_purchase_to_stock AS factor +FROM products p +JOIN quantity_units qu_from + ON p.qu_id_purchase = qu_from.id +JOIN quantity_units qu_to + ON p.qu_id_stock = qu_to.id +UNION -- Inversed +SELECT + p.id AS id, -- Dummy, LessQL needs an id column + p.id AS product_id, + p.qu_id_stock AS from_qu_id, + qu_to.name AS from_qu_name, + p.qu_id_purchase AS to_qu_id, + qu_from.name AS to_qu_name, + 1 / p.qu_factor_purchase_to_stock AS factor +FROM products p +JOIN quantity_units qu_from + ON p.qu_id_purchase = qu_from.id +JOIN quantity_units qu_to + ON p.qu_id_stock = qu_to.id + +UNION + +-- Second: Product specific overrides +SELECT + p.id AS id, -- Dummy, LessQL needs an id column + p.id AS product_id, + quc.from_qu_id AS from_qu_id, + qu_from.name AS from_qu_name, + quc.to_qu_id AS to_qu_id, + qu_to.name AS to_qu_name, + quc.factor AS factor +FROM products p +JOIN quantity_unit_conversions quc + ON p.id = quc.product_id +JOIN quantity_units qu_from + ON quc.from_qu_id = qu_from.id +JOIN quantity_units qu_to + ON quc.to_qu_id = qu_to.id + +UNION + +-- Third: Default quantity unit conversion factors +SELECT + p.id AS id, -- Dummy, LessQL needs an id column + p.id AS product_id, + p.qu_id_stock AS from_qu_id, + qu_from.name AS from_qu_name, + quc.to_qu_id AS to_qu_id, + qu_to.name AS to_qu_name, + quc.factor AS factor +FROM products p +JOIN quantity_unit_conversions quc + ON p.qu_id_stock = quc.from_qu_id + AND quc.product_id IS NULL +JOIN quantity_units qu_from + ON quc.from_qu_id = qu_from.id +JOIN quantity_units qu_to + ON quc.to_qu_id = qu_to.id; diff --git a/migrations/0117.sql b/migrations/0117.sql deleted file mode 100644 index 2dfb848c..00000000 --- a/migrations/0117.sql +++ /dev/null @@ -1,71 +0,0 @@ -DROP VIEW quantity_unit_conversions_resolved; -CREATE VIEW quantity_unit_conversions_resolved -AS - --- First: Product "purchase to stock" conversion factor -SELECT - p.id AS id, -- Dummy, LessQL needs an id column - p.id AS product_id, - p.qu_id_purchase AS from_qu_id, - qu_from.name AS from_qu_name, - p.qu_id_stock AS to_qu_id, - qu_to.name AS to_qu_name, - p.qu_factor_purchase_to_stock AS factor -FROM products p -JOIN quantity_units qu_from - ON p.qu_id_purchase = qu_from.id -JOIN quantity_units qu_to - ON p.qu_id_stock = qu_to.id -UNION -- Inversed -SELECT - p.id AS id, -- Dummy, LessQL needs an id column - p.id AS product_id, - p.qu_id_stock AS from_qu_id, - qu_to.name AS from_qu_name, - p.qu_id_purchase AS to_qu_id, - qu_from.name AS to_qu_name, - 1 / p.qu_factor_purchase_to_stock AS factor -FROM products p -JOIN quantity_units qu_from - ON p.qu_id_purchase = qu_from.id -JOIN quantity_units qu_to - ON p.qu_id_stock = qu_to.id - -UNION - --- Second: Product specific overrides -SELECT - p.id AS id, -- Dummy, LessQL needs an id column - p.id AS product_id, - quc.from_qu_id AS from_qu_id, - qu_from.name AS from_qu_name, - quc.to_qu_id AS to_qu_id, - qu_to.name AS to_qu_name, - quc.factor AS factor -FROM products p -JOIN quantity_unit_conversions quc - ON p.id = quc.product_id -JOIN quantity_units qu_from - ON quc.from_qu_id = qu_from.id -JOIN quantity_units qu_to - ON quc.to_qu_id = qu_to.id - -UNION - --- Third: Default quantity unit conversion factors -SELECT - p.id AS id, -- Dummy, LessQL needs an id column - p.id AS product_id, - p.qu_id_stock AS from_qu_id, - qu_from.name AS from_qu_name, - quc.to_qu_id AS to_qu_id, - qu_to.name AS to_qu_name, - quc.factor AS factor -FROM products p -JOIN quantity_unit_conversions quc - ON p.qu_id_stock = quc.from_qu_id - AND quc.product_id IS NULL -JOIN quantity_units qu_from - ON quc.from_qu_id = qu_from.id -JOIN quantity_units qu_to - ON quc.to_qu_id = qu_to.id;