Changeset 2d1ec46
- Timestamp:
- 09/21/26 00:54:07 (9 days ago)
- Branches:
- main
- Parents:
- 6c7cfa6
- Files:
-
- 35 deleted
- 1 edited
-
database/Advanced Database Developement/Triggers/Automatic calculation of store rating from order reviews.txt (deleted)
-
database/Advanced Database Developement/Triggers/Automatic deletion of product changes when the product is deleted.txt (deleted)
-
database/Advanced Database Developement/Triggers/Automatic setting of the last modified date for orders.txt (deleted)
-
database/Advanced Database Developement/Triggers/Automatic update of employee-store statistics after an employee is hired.txt (deleted)
-
database/Advanced Database Developement/Triggers/Automatic update of product availability after an order.txt (deleted)
-
database/Advanced Database Developement/Triggers/Prevention of deleting stores with existing orders or reports.txt (deleted)
-
database/Advanced Database Developement/Triggers/Prevention of ordering unavailable products.txt (deleted)
-
database/Advanced Database Developement/Triggers/Validation of employee authorization before making a product change.txt (deleted)
-
database/Advanced Database Developement/Views/Complete overview of customer orders.txt (deleted)
-
database/Advanced Database Developement/Views/Complete overview of products and stores.txt (deleted)
-
database/Advanced Database Developement/Views/Customer order history.txt (deleted)
-
database/Advanced Database Developement/Views/Customer request and response overview.txt (deleted)
-
database/Advanced Database Developement/Views/Employee workload and salary report.txt (deleted)
-
database/Advanced Database Developement/Views/Monthly sales and profit per store.txt (deleted)
-
database/Advanced Database Developement/Views/Store inventory overview.txt (deleted)
-
database/Advanced Database Developement/Views/Store performance overview.txt (deleted)
-
database/Advanced Reports for database/Approximate number of orders per client (deleted)
-
database/Advanced Reports for database/Clients ordered by number of orders.txt (deleted)
-
database/Advanced Reports for database/Each product's monthly sales.txt (deleted)
-
database/Advanced Reports for database/Each store's average pay.txt (deleted)
-
database/Advanced Reports for database/Each store's number of request, including how many have been solved, and how many are still in progress.txt (deleted)
-
database/Advanced Reports for database/Employees ordered by total hours worked and total pay.txt (deleted)
-
database/Advanced Reports for database/List of clients who haven't made an order yet.txt (deleted)
-
database/Advanced Reports for database/List of most popular products with total number of sales.txt (deleted)
-
database/Advanced Reports for database/List of products that are low on stock and high in demand.txt (deleted)
-
database/Advanced Reports for database/List of products who have not been ordered.txt (deleted)
-
database/Advanced Reports for database/List of reports which haven't been approved.txt (deleted)
-
database/Advanced Reports for database/Number of product changes each employee has made in the last month.txt (deleted)
-
database/Advanced Reports for database/Orders ordered by order total from highest to lowest.txt (deleted)
-
database/Advanced Reports for database/Products ordered by number of orders from highest to lowest.txt (deleted)
-
database/Advanced Reports for database/Store with highest revenue growth in the last calendar year.txt (deleted)
-
database/Advanced Reports for database/Stores ordered by highest approximate product review.txt (deleted)
-
database/Advanced Reports for database/Stores ordered by monthly profit including monthly revenue growth.txt (deleted)
-
database/Advanced Reports for database/Stores ordered by total revenue in the last calendar year from highest to lowest.txt (deleted)
-
database/Advanced Reports for database/Top 10 employees who have answered the most amount of request in the last month.txt (deleted)
-
server.js (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
server.js
r6c7cfa6 r2d1ec46 911 911 `; 912 912 913 914 const TRIGGERS_SQL = String.raw` 915 -- ============================================================ 916 -- HANDCRAFT MARKETPLACE TRIGGERS 917 -- Compatible with the supplied project schema. 918 -- ============================================================ 919 920 CREATE OR REPLACE FUNCTION update_store_rating() 921 RETURNS TRIGGER 922 LANGUAGE plpgsql 923 AS $$ 924 DECLARE 925 v_order_num VARCHAR(11); 926 v_store_id VARCHAR(3); 927 BEGIN 928 IF TG_OP = 'DELETE' THEN 929 v_order_num := OLD.order_num; 930 ELSE 931 v_order_num := NEW.order_num; 932 END IF; 933 934 v_store_id := LEFT(v_order_num, 3); 935 936 UPDATE store s 937 SET rating = COALESCE( 938 ( 939 SELECT ROUND(AVG(r.rating), 1) 940 FROM review r 941 JOIN includes i 942 ON i.order_num = r.order_num 943 JOIN sells sl 944 ON sl.product_code = i.product_code 945 AND sl.store_ID = v_store_id 946 JOIN "order" o 947 ON o.order_num = r.order_num 948 WHERE LEFT(o.order_num, 3) = v_store_id 949 ), 950 0 951 ) 952 WHERE s.store_ID = v_store_id; 953 954 RETURN NULL; 955 END; 956 $$; 957 958 DROP TRIGGER IF EXISTS trg_update_store_rating ON review; 959 960 CREATE TRIGGER trg_update_store_rating 961 AFTER INSERT OR UPDATE OR DELETE 962 ON review 963 FOR EACH ROW 964 EXECUTE FUNCTION update_store_rating(); 965 966 967 CREATE OR REPLACE FUNCTION check_product_availability() 968 RETURNS TRIGGER 969 LANGUAGE plpgsql 970 AS $$ 971 DECLARE 972 v_available_quantity INTEGER; 973 BEGIN 974 SELECT p.availability 975 INTO v_available_quantity 976 FROM product p 977 WHERE p.code = NEW.product_code 978 FOR UPDATE; 979 980 IF v_available_quantity IS NULL THEN 981 RAISE EXCEPTION 982 'Product % does not exist or is not available.', 983 NEW.product_code; 984 END IF; 985 986 IF TG_OP = 'INSERT' THEN 987 IF NEW.quantity > v_available_quantity THEN 988 RAISE EXCEPTION 989 'Insufficient stock for product %. Available: %, requested: %.', 990 NEW.product_code, 991 v_available_quantity, 992 NEW.quantity; 993 END IF; 994 995 ELSIF TG_OP = 'UPDATE' THEN 996 IF NEW.product_code = OLD.product_code THEN 997 IF NEW.quantity > OLD.quantity 998 AND (NEW.quantity - OLD.quantity) > v_available_quantity THEN 999 RAISE EXCEPTION 1000 'Insufficient stock for product %. Available: %, additional requested: %.', 1001 NEW.product_code, 1002 v_available_quantity, 1003 NEW.quantity - OLD.quantity; 1004 END IF; 1005 ELSE 1006 IF NEW.quantity > v_available_quantity THEN 1007 RAISE EXCEPTION 1008 'Insufficient stock for product %. Available: %, requested: %.', 1009 NEW.product_code, 1010 v_available_quantity, 1011 NEW.quantity; 1012 END IF; 1013 END IF; 1014 END IF; 1015 1016 RETURN NEW; 1017 END; 1018 $$; 1019 1020 DROP TRIGGER IF EXISTS trg_check_product_availability ON includes; 1021 1022 CREATE TRIGGER trg_check_product_availability 1023 BEFORE INSERT OR UPDATE 1024 ON includes 1025 FOR EACH ROW 1026 EXECUTE FUNCTION check_product_availability(); 1027 1028 1029 CREATE OR REPLACE FUNCTION update_product_availability() 1030 RETURNS TRIGGER 1031 LANGUAGE plpgsql 1032 AS $$ 1033 BEGIN 1034 IF TG_OP = 'INSERT' THEN 1035 1036 UPDATE product 1037 SET availability = availability - NEW.quantity 1038 WHERE code = NEW.product_code; 1039 1040 ELSIF TG_OP = 'UPDATE' THEN 1041 1042 IF NEW.product_code = OLD.product_code THEN 1043 1044 UPDATE product 1045 SET availability = availability - (NEW.quantity - OLD.quantity) 1046 WHERE code = NEW.product_code; 1047 1048 ELSE 1049 1050 UPDATE product 1051 SET availability = availability + OLD.quantity 1052 WHERE code = OLD.product_code; 1053 1054 UPDATE product 1055 SET availability = availability - NEW.quantity 1056 WHERE code = NEW.product_code; 1057 1058 END IF; 1059 1060 ELSIF TG_OP = 'DELETE' THEN 1061 1062 UPDATE product 1063 SET availability = availability + OLD.quantity 1064 WHERE code = OLD.product_code; 1065 1066 END IF; 1067 1068 RETURN NULL; 1069 END; 1070 $$; 1071 1072 DROP TRIGGER IF EXISTS trg_update_product_availability ON includes; 1073 1074 CREATE TRIGGER trg_update_product_availability 1075 AFTER INSERT OR UPDATE OR DELETE 1076 ON includes 1077 FOR EACH ROW 1078 EXECUTE FUNCTION update_product_availability(); 1079 1080 1081 CREATE OR REPLACE FUNCTION delete_product_changes() 1082 RETURNS TRIGGER 1083 LANGUAGE plpgsql 1084 AS $$ 1085 BEGIN 1086 DELETE FROM "change" 1087 WHERE product_code = OLD.code; 1088 1089 RETURN OLD; 1090 END; 1091 $$; 1092 1093 DROP TRIGGER IF EXISTS trg_delete_product_changes ON product; 1094 1095 CREATE TRIGGER trg_delete_product_changes 1096 BEFORE DELETE 1097 ON product 1098 FOR EACH ROW 1099 EXECUTE FUNCTION delete_product_changes(); 1100 1101 1102 CREATE OR REPLACE FUNCTION prevent_store_deletion() 1103 RETURNS TRIGGER 1104 LANGUAGE plpgsql 1105 AS $$ 1106 BEGIN 1107 IF EXISTS ( 1108 SELECT 1 1109 FROM report 1110 WHERE store_ID = OLD.store_ID 1111 ) THEN 1112 RAISE EXCEPTION 1113 'Store % cannot be deleted because it has existing reports.', 1114 OLD.store_ID; 1115 END IF; 1116 1117 IF EXISTS ( 1118 SELECT 1 1119 FROM sells 1120 WHERE store_ID = OLD.store_ID 1121 ) THEN 1122 RAISE EXCEPTION 1123 'Store % cannot be deleted because it has existing product records.', 1124 OLD.store_ID; 1125 END IF; 1126 1127 IF EXISTS ( 1128 SELECT 1 1129 FROM "order" 1130 WHERE LEFT(order_num, 3) = OLD.store_ID 1131 ) THEN 1132 RAISE EXCEPTION 1133 'Store % cannot be deleted because it has existing orders.', 1134 OLD.store_ID; 1135 END IF; 1136 1137 RETURN OLD; 1138 END; 1139 $$; 1140 1141 DROP TRIGGER IF EXISTS trg_prevent_store_deletion ON store; 1142 1143 CREATE TRIGGER trg_prevent_store_deletion 1144 BEFORE DELETE 1145 ON store 1146 FOR EACH ROW 1147 EXECUTE FUNCTION prevent_store_deletion(); 1148 1149 1150 CREATE OR REPLACE FUNCTION update_order_modified_date() 1151 RETURNS TRIGGER 1152 LANGUAGE plpgsql 1153 AS $$ 1154 BEGIN 1155 NEW.last_date_mod := CURRENT_TIMESTAMP; 1156 RETURN NEW; 1157 END; 1158 $$; 1159 1160 DROP TRIGGER IF EXISTS trg_update_order_modified_date ON "order"; 1161 1162 CREATE TRIGGER trg_update_order_modified_date 1163 BEFORE UPDATE 1164 ON "order" 1165 FOR EACH ROW 1166 EXECUTE FUNCTION update_order_modified_date(); 1167 1168 1169 CREATE OR REPLACE FUNCTION validate_employee_authorization() 1170 RETURNS TRIGGER 1171 LANGUAGE plpgsql 1172 AS $$ 1173 DECLARE 1174 v_authorisation TEXT; 1175 BEGIN 1176 SELECT p.authorisation 1177 INTO v_authorisation 1178 FROM permissions p 1179 WHERE p.personal_id = NEW.personal_id; 1180 1181 IF v_authorisation IS NULL THEN 1182 RAISE EXCEPTION 1183 'Employee % does not have valid authorization.', 1184 NEW.personal_id; 1185 END IF; 1186 1187 -- makes_change has no authorisation column in the supplied schema. 1188 -- The employee's permission row is therefore the source of truth. 1189 RETURN NEW; 1190 END; 1191 $$; 1192 1193 DROP TRIGGER IF EXISTS trg_validate_employee_authorization ON makes_change; 1194 1195 CREATE TRIGGER trg_validate_employee_authorization 1196 BEFORE INSERT OR UPDATE 1197 ON makes_change 1198 FOR EACH ROW 1199 EXECUTE FUNCTION validate_employee_authorization(); 1200 1201 1202 CREATE OR REPLACE FUNCTION initialize_employee_statistics() 1203 RETURNS TRIGGER 1204 LANGUAGE plpgsql 1205 AS $$ 1206 BEGIN 1207 /* 1208 * The schema creates an employee before store/report assignment in the 1209 * normal application flow. If an assignment and a report already exist, 1210 * create a zero-hours starting record; otherwise there is nothing to 1211 * initialize yet. 1212 */ 1213 INSERT INTO worked ( 1214 personal_id, 1215 report_date, 1216 store_ID, 1217 wage, 1218 pay_method, 1219 total_hours, 1220 week 1221 ) 1222 SELECT 1223 NEW.employee_id, 1224 r.date, 1225 wis.store_ID, 1226 0, 1227 'full_time', 1228 0, 1229 TO_CHAR(CURRENT_DATE - INTERVAL '6 days', 'DD.MM.YYYY') 1230 || ' - ' || 1231 TO_CHAR(CURRENT_DATE, 'DD.MM.YYYY') 1232 FROM works_in_store wis 1233 JOIN LATERAL ( 1234 SELECT r2.date 1235 FROM report r2 1236 WHERE r2.store_ID = wis.store_ID 1237 ORDER BY r2.date DESC 1238 LIMIT 1 1239 ) r ON TRUE 1240 WHERE wis.personal_id = NEW.employee_id 1241 ON CONFLICT (personal_id, report_date, store_ID) DO NOTHING; 1242 1243 RETURN NEW; 1244 END; 1245 $$; 1246 1247 DROP TRIGGER IF EXISTS trg_initialize_employee_statistics ON employees; 1248 1249 CREATE TRIGGER trg_initialize_employee_statistics 1250 AFTER INSERT 1251 ON employees 1252 FOR EACH ROW 1253 EXECUTE FUNCTION initialize_employee_statistics(); 1254 `; 1255 1256 1257 const VIEWS_SQL = String.raw` 1258 -- ============================================================ 1259 -- HANDCRAFT MARKETPLACE VIEWS 1260 -- Compatible with the supplied project schema. 1261 -- ============================================================ 1262 1263 DROP VIEW IF EXISTS vw_product_store_overview; 1264 CREATE VIEW vw_product_store_overview AS 1265 SELECT 1266 p.code AS product_code, 1267 p.description, 1268 p.price, 1269 p.availability, 1270 p.weight, 1271 p.width_x_length_x_depth, 1272 p.aprox_production_time, 1273 s.store_ID, 1274 s.name AS store_name, 1275 s.physical_address, 1276 s.rating, 1277 sl.discount 1278 FROM product p 1279 JOIN sells sl 1280 ON sl.product_code = p.code 1281 JOIN store s 1282 ON s.store_ID = sl.store_ID; 1283 1284 1285 DROP VIEW IF EXISTS vw_customer_order_overview; 1286 CREATE VIEW vw_customer_order_overview AS 1287 SELECT 1288 o.order_num, 1289 i.quantity, 1290 o.status, 1291 o.last_date_mod, 1292 o.payment_method, 1293 o.discount, 1294 c.client_ID, 1295 c.first_name, 1296 c.last_name, 1297 c.email, 1298 p.code AS product_code, 1299 p.description AS product_description, 1300 p.price 1301 FROM "order" o 1302 JOIN makes_request mr 1303 ON mr.order_num = o.order_num 1304 JOIN client c 1305 ON c.client_ID = mr.client_ID 1306 JOIN includes i 1307 ON i.order_num = o.order_num 1308 JOIN product p 1309 ON p.code = i.product_code; 1310 1311 1312 DROP VIEW IF EXISTS vw_monthly_sales_profit; 1313 CREATE VIEW vw_monthly_sales_profit AS 1314 SELECT 1315 s.store_ID, 1316 s.name AS store_name, 1317 r.date AS report_date, 1318 mp.month_and_year, 1319 mp.profit, 1320 r.overall_profit, 1321 ed.monthly_profit, 1322 ed.sales, 1323 ed.damages 1324 FROM store s 1325 JOIN report r 1326 ON r.store_ID = s.store_ID 1327 LEFT JOIN monthly_profit mp 1328 ON mp.report_date = r.date 1329 AND mp.store_ID = r.store_ID 1330 LEFT JOIN exchanges_data ed 1331 ON ed.report_date = r.date 1332 AND ed.store_ID = r.store_ID; 1333 1334 1335 DROP VIEW IF EXISTS vw_employee_workload_salary; 1336 CREATE VIEW vw_employee_workload_salary AS 1337 SELECT 1338 e.employee_id, 1339 p.first_name, 1340 p.last_name, 1341 p.email, 1342 e.date_of_hire, 1343 s.store_ID, 1344 s.name AS store_name, 1345 w.week, 1346 w.total_hours, 1347 w.wage, 1348 w.pay_method, 1349 COALESCE(w.wage * w.total_hours, 0) AS total_pay 1350 FROM employees e 1351 JOIN personal p 1352 ON p.id = e.employee_id 1353 JOIN works_in_store wis 1354 ON wis.personal_id = e.employee_id 1355 JOIN store s 1356 ON s.store_ID = wis.store_ID 1357 LEFT JOIN worked w 1358 ON w.personal_id = e.employee_id 1359 AND w.store_ID = wis.store_ID; 1360 1361 1362 DROP VIEW IF EXISTS vw_customer_request_response; 1363 CREATE VIEW vw_customer_request_response AS 1364 SELECT 1365 r.request_num, 1366 r.date_and_time, 1367 r.problem, 1368 r.notes_of_communication, 1369 r.customer_satisfaction, 1370 c.client_ID, 1371 c.first_name AS client_first_name, 1372 c.last_name AS client_last_name, 1373 c.email AS client_email, 1374 p.id AS employee_id, 1375 p.first_name AS employee_first_name, 1376 p.last_name AS employee_last_name 1377 FROM request r 1378 LEFT JOIN client c 1379 ON c.client_ID = CASE 1380 WHEN SUBSTRING(r.request_num FROM 9 FOR 4) ~ '^[0-9]{4}$' 1381 THEN SUBSTRING(r.request_num FROM 9 FOR 4)::INTEGER 1382 ELSE NULL 1383 END 1384 LEFT JOIN answers a 1385 ON a.request_num = r.request_num 1386 LEFT JOIN personal p 1387 ON p.id = a.personal_id; 1388 1389 1390 DROP VIEW IF EXISTS vw_store_inventory; 1391 CREATE VIEW vw_store_inventory AS 1392 SELECT 1393 s.store_ID, 1394 s.name AS store_name, 1395 p.code AS product_code, 1396 p.description, 1397 p.price, 1398 p.availability, 1399 p.aprox_production_time, 1400 sl.discount 1401 FROM store s 1402 JOIN sells sl 1403 ON sl.store_ID = s.store_ID 1404 JOIN product p 1405 ON p.code = sl.product_code; 1406 1407 1408 DROP VIEW IF EXISTS vw_customer_order_history; 1409 CREATE VIEW vw_customer_order_history AS 1410 SELECT 1411 c.client_ID, 1412 c.first_name, 1413 c.last_name, 1414 c.email, 1415 o.order_num, 1416 i.quantity, 1417 o.status, 1418 o.last_date_mod, 1419 o.payment_method, 1420 o.discount, 1421 p.code AS product_code, 1422 p.description AS product_description, 1423 p.price 1424 FROM client c 1425 JOIN makes_request mr 1426 ON mr.client_ID = c.client_ID 1427 JOIN "order" o 1428 ON o.order_num = mr.order_num 1429 JOIN includes i 1430 ON i.order_num = o.order_num 1431 JOIN product p 1432 ON p.code = i.product_code; 1433 1434 1435 DROP VIEW IF EXISTS vw_store_performance; 1436 CREATE VIEW vw_store_performance AS 1437 SELECT 1438 s.store_ID, 1439 s.name AS store_name, 1440 s.date_of_founding, 1441 s.rating, 1442 COUNT(DISTINCT r.date) AS number_of_reports, 1443 COALESCE(SUM(mp.profit), 0) AS total_reported_profit, 1444 COALESCE(MAX(r.overall_profit), 0) AS overall_profit, 1445 COALESCE(SUM(ed.sales), 0) AS total_sales, 1446 COALESCE(SUM(ed.damages), 0) AS total_damages, 1447 COALESCE(SUM(ed.monthly_profit), 0) AS total_monthly_profit 1448 FROM store s 1449 LEFT JOIN report r 1450 ON r.store_ID = s.store_ID 1451 LEFT JOIN monthly_profit mp 1452 ON mp.report_date = r.date 1453 AND mp.store_ID = r.store_ID 1454 LEFT JOIN exchanges_data ed 1455 ON ed.report_date = r.date 1456 AND ed.store_ID = r.store_ID 1457 GROUP BY 1458 s.store_ID, 1459 s.name, 1460 s.date_of_founding, 1461 s.rating; 1462 `; 1463 1464 913 1465 const database = { 914 1466 database: { … … 1033 1585 await pool.query(REPORT_FUNCTIONS_SQL); 1034 1586 console.log('✅ PostgreSQL report functions installed'); 1587 }, 1588 1589 async installTriggersAndViews() { 1590 await pool.query(TRIGGERS_SQL); 1591 console.log('✅ PostgreSQL triggers installed'); 1592 1593 await pool.query(VIEWS_SQL); 1594 console.log('✅ PostgreSQL views installed'); 1035 1595 }, 1036 1596 … … 2162 2722 await database.initializeDatabase(); 2163 2723 await database.installReportFunctions(); 2724 await database.installTriggersAndViews(); 2164 2725 console.log('✅ Database initialization completed'); 2165 2726 } catch (err) {
Note:
See TracChangeset
for help on using the changeset viewer.
