1
0

beepzone-schema-dump.sql 105 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088
  1. /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
  2. /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
  3. /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
  4. /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
  5. /*!40101 SET @saved_cs_client = @@character_set_client */;
  6. /*!50503 SET character_set_client = utf8mb4 */;
  7. CREATE TABLE `asset_change_log` (
  8. `id` int(11) NOT NULL AUTO_INCREMENT,
  9. `table_name` varchar(50) NOT NULL,
  10. `action` enum('INSERT','UPDATE','DELETE') NOT NULL,
  11. `record_id` int(11) NOT NULL,
  12. `changed_fields` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT 'Only fields that actually changed' CHECK (json_valid(`changed_fields`)),
  13. `old_values` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`old_values`)),
  14. `new_values` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`new_values`)),
  15. `changed_at` timestamp NULL DEFAULT current_timestamp(),
  16. `changed_by_id` int(11) DEFAULT NULL,
  17. `changed_by_username` varchar(100) DEFAULT NULL,
  18. PRIMARY KEY (`id`),
  19. KEY `idx_table_action` (`table_name`,`action`),
  20. KEY `idx_timestamp` (`changed_at`),
  21. KEY `idx_record` (`record_id`),
  22. KEY `idx_user` (`changed_by_id`),
  23. CONSTRAINT `asset_change_log_ibfk_1` FOREIGN KEY (`changed_by_id`) REFERENCES `users` (`id`) ON DELETE SET NULL
  24. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
  25. /*!40101 SET character_set_client = @saved_cs_client */;
  26. /*!40101 SET @saved_cs_client = @@character_set_client */;
  27. /*!50503 SET character_set_client = utf8mb4 */;
  28. CREATE TABLE `assets` (
  29. `id` int(11) NOT NULL AUTO_INCREMENT,
  30. `asset_tag` varchar(200) DEFAULT NULL,
  31. `tag_generation_string` varchar(255) DEFAULT NULL,
  32. `asset_numeric_id` int(11) NOT NULL CHECK (`asset_numeric_id` between 10000000 and 99999999),
  33. `belongs_to_item` int(11) DEFAULT NULL COMMENT 'References asset_numeric_id of parent asset',
  34. `previously_was` int(11) DEFAULT NULL COMMENT 'References asset_numeric_id of the asset this replaced',
  35. `asset_type` enum('N','B','L','C') NOT NULL,
  36. `name` varchar(255) DEFAULT NULL,
  37. `category_id` int(11) DEFAULT NULL,
  38. `manufacturer` varchar(200) DEFAULT NULL,
  39. `model` varchar(200) DEFAULT NULL,
  40. `serial_number` varchar(200) DEFAULT NULL,
  41. `zone_id` int(11) DEFAULT NULL,
  42. `zone_plus` enum('Floating Local','Floating Global','Clarify') DEFAULT NULL,
  43. `zone_note` text DEFAULT NULL,
  44. `status` enum('Good','Attention','Faulty','Missing','Retired','In Repair','In Transit','Expired','Unmanaged') DEFAULT 'Good',
  45. `last_audit` date DEFAULT NULL,
  46. `last_audit_status` varchar(100) DEFAULT NULL,
  47. `price` decimal(12,2) DEFAULT NULL CHECK (`price` is null or `price` >= 0),
  48. `purchase_date` date DEFAULT NULL,
  49. `warranty_until` date DEFAULT NULL,
  50. `expiry_date` date DEFAULT NULL,
  51. `quantity_available` int(11) DEFAULT NULL,
  52. `quantity_total` int(11) DEFAULT NULL,
  53. `quantity_used` int(11) DEFAULT 0,
  54. `supplier_id` int(11) DEFAULT NULL,
  55. `lendable` tinyint(1) DEFAULT 0,
  56. `minimum_role_for_lending` int(11) DEFAULT 1 CHECK (`minimum_role_for_lending` >= 1 and `minimum_role_for_lending` <= 100),
  57. `lending_status` enum('Available','Deployed','Borrowed','Overdue','Illegally Handed Out','Stolen') DEFAULT NULL,
  58. `current_borrower_id` int(11) DEFAULT NULL,
  59. `due_date` date DEFAULT NULL,
  60. `previous_borrower_id` int(11) DEFAULT NULL,
  61. `audit_task_id` int(11) DEFAULT NULL,
  62. `label_template_id` int(11) DEFAULT NULL,
  63. `no_scan` enum('Yes','Ask','No') DEFAULT 'No',
  64. `notes` text DEFAULT NULL,
  65. `additional_fields` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`additional_fields`)),
  66. `file_attachment` mediumblob DEFAULT NULL,
  67. `created_date` timestamp NULL DEFAULT current_timestamp(),
  68. `created_by` int(11) DEFAULT NULL,
  69. `last_modified_date` timestamp NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
  70. `last_modified_by` int(11) DEFAULT NULL,
  71. PRIMARY KEY (`id`),
  72. UNIQUE KEY `asset_numeric_id` (`asset_numeric_id`),
  73. UNIQUE KEY `asset_tag` (`asset_tag`),
  74. KEY `supplier_id` (`supplier_id`),
  75. KEY `current_borrower_id` (`current_borrower_id`),
  76. KEY `previous_borrower_id` (`previous_borrower_id`),
  77. KEY `audit_task_id` (`audit_task_id`),
  78. KEY `created_by` (`created_by`),
  79. KEY `last_modified_by` (`last_modified_by`),
  80. KEY `idx_asset_tag` (`asset_tag`),
  81. KEY `idx_asset_numeric` (`asset_numeric_id`),
  82. KEY `idx_belongs_to` (`belongs_to_item`),
  83. KEY `idx_previously_was` (`previously_was`),
  84. KEY `idx_type` (`asset_type`),
  85. KEY `idx_status` (`status`),
  86. KEY `idx_zone` (`zone_id`),
  87. KEY `idx_category` (`category_id`),
  88. KEY `idx_lendable` (`lendable`),
  89. KEY `idx_lending_status` (`lending_status`),
  90. KEY `idx_label_template` (`label_template_id`),
  91. CONSTRAINT `assets_ibfk_1` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`),
  92. CONSTRAINT `assets_ibfk_2` FOREIGN KEY (`zone_id`) REFERENCES `zones` (`id`),
  93. CONSTRAINT `assets_ibfk_3` FOREIGN KEY (`supplier_id`) REFERENCES `suppliers` (`id`) ON DELETE SET NULL,
  94. CONSTRAINT `assets_ibfk_4` FOREIGN KEY (`current_borrower_id`) REFERENCES `borrowers` (`id`) ON DELETE SET NULL,
  95. CONSTRAINT `assets_ibfk_5` FOREIGN KEY (`previous_borrower_id`) REFERENCES `borrowers` (`id`) ON DELETE SET NULL,
  96. CONSTRAINT `assets_ibfk_6` FOREIGN KEY (`audit_task_id`) REFERENCES `audit_tasks` (`id`) ON DELETE SET NULL,
  97. CONSTRAINT `assets_ibfk_7` FOREIGN KEY (`created_by`) REFERENCES `users` (`id`) ON DELETE SET NULL,
  98. CONSTRAINT `assets_ibfk_8` FOREIGN KEY (`last_modified_by`) REFERENCES `users` (`id`) ON DELETE SET NULL,
  99. CONSTRAINT `fk_asset_label_template` FOREIGN KEY (`label_template_id`) REFERENCES `label_templates` (`id`) ON DELETE SET NULL,
  100. CONSTRAINT `fk_assets_belongs_to` FOREIGN KEY (`belongs_to_item`) REFERENCES `assets` (`asset_numeric_id`) ON DELETE SET NULL,
  101. CONSTRAINT `fk_assets_previously_was` FOREIGN KEY (`previously_was`) REFERENCES `assets` (`asset_numeric_id`) ON DELETE SET NULL
  102. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
  103. /*!40101 SET character_set_client = @saved_cs_client */;
  104. /*!50003 SET @saved_cs_client = @@character_set_client */ ;
  105. /*!50003 SET @saved_cs_results = @@character_set_results */ ;
  106. /*!50003 SET @saved_col_connection = @@collation_connection */ ;
  107. /*!50003 SET character_set_client = utf8mb4 */ ;
  108. /*!50003 SET character_set_results = utf8mb4 */ ;
  109. /*!50003 SET collation_connection = utf8mb4_0900_ai_ci */ ;
  110. /*!50003 SET @saved_sql_mode = @@sql_mode */ ;
  111. /*!50003 SET sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
  112. DELIMITER ;;
  113. /*!50003 CREATE*/ /*!50017 DEFINER=`root`@`%`*/ /*!50003 TRIGGER assets_before_insert_meta
  114. BEFORE INSERT ON assets
  115. FOR EACH ROW
  116. BEGIN
  117. IF NEW.created_by IS NULL AND @current_user_id IS NOT NULL THEN
  118. SET NEW.created_by = @current_user_id;
  119. END IF;
  120. END */;;
  121. DELIMITER ;
  122. /*!50003 SET sql_mode = @saved_sql_mode */ ;
  123. /*!50003 SET character_set_client = @saved_cs_client */ ;
  124. /*!50003 SET character_set_results = @saved_cs_results */ ;
  125. /*!50003 SET collation_connection = @saved_col_connection */ ;
  126. /*!50003 SET @saved_cs_client = @@character_set_client */ ;
  127. /*!50003 SET @saved_cs_results = @@character_set_results */ ;
  128. /*!50003 SET @saved_col_connection = @@collation_connection */ ;
  129. /*!50003 SET character_set_client = utf8mb4 */ ;
  130. /*!50003 SET character_set_results = utf8mb4 */ ;
  131. /*!50003 SET collation_connection = utf8mb4_0900_ai_ci */ ;
  132. /*!50003 SET @saved_sql_mode = @@sql_mode */ ;
  133. /*!50003 SET sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
  134. DELIMITER ;;
  135. /*!50003 CREATE*/ /*!50017 DEFINER=`root`@`%`*/ /*!50003 TRIGGER validate_zone_plus_insert
  136. BEFORE INSERT ON assets
  137. FOR EACH ROW
  138. BEGIN
  139. IF NEW.zone_plus = 'Clarify' AND (NEW.zone_note IS NULL OR NEW.zone_note = '') THEN
  140. SIGNAL SQLSTATE '45000'
  141. SET MESSAGE_TEXT = 'zone_note is required when zone_plus is set to Clarify';
  142. END IF;
  143. END */;;
  144. DELIMITER ;
  145. /*!50003 SET sql_mode = @saved_sql_mode */ ;
  146. /*!50003 SET character_set_client = @saved_cs_client */ ;
  147. /*!50003 SET character_set_results = @saved_cs_results */ ;
  148. /*!50003 SET collation_connection = @saved_col_connection */ ;
  149. /*!50003 SET @saved_cs_client = @@character_set_client */ ;
  150. /*!50003 SET @saved_cs_results = @@character_set_results */ ;
  151. /*!50003 SET @saved_col_connection = @@collation_connection */ ;
  152. /*!50003 SET character_set_client = utf8mb4 */ ;
  153. /*!50003 SET character_set_results = utf8mb4 */ ;
  154. /*!50003 SET collation_connection = utf8mb4_0900_ai_ci */ ;
  155. /*!50003 SET @saved_sql_mode = @@sql_mode */ ;
  156. /*!50003 SET sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
  157. DELIMITER ;;
  158. /*!50003 CREATE*/ /*!50017 DEFINER=`root`@`%`*/ /*!50003 TRIGGER assets_after_insert_log
  159. AFTER INSERT ON assets
  160. FOR EACH ROW
  161. BEGIN
  162. DECLARE username VARCHAR(100);
  163. DECLARE set_fields_array JSON;
  164. DECLARE new_vals JSON;
  165. IF @current_user_id IS NOT NULL THEN
  166. SELECT users.username INTO username FROM users WHERE id = @current_user_id;
  167. END IF;
  168. -- Build JSON objects only with non-NULL fields
  169. SET set_fields_array = JSON_ARRAY();
  170. SET new_vals = JSON_OBJECT();
  171. -- Always log these core fields
  172. IF NEW.asset_tag IS NOT NULL THEN
  173. SET set_fields_array = JSON_ARRAY_APPEND(set_fields_array, '$', 'asset_tag');
  174. SET new_vals = JSON_SET(new_vals, '$.asset_tag', NEW.asset_tag);
  175. END IF;
  176. IF NEW.asset_numeric_id IS NOT NULL THEN
  177. SET set_fields_array = JSON_ARRAY_APPEND(set_fields_array, '$', 'asset_numeric_id');
  178. SET new_vals = JSON_SET(new_vals, '$.asset_numeric_id', NEW.asset_numeric_id);
  179. END IF;
  180. IF NEW.asset_type IS NOT NULL THEN
  181. SET set_fields_array = JSON_ARRAY_APPEND(set_fields_array, '$', 'asset_type');
  182. SET new_vals = JSON_SET(new_vals, '$.asset_type', NEW.asset_type);
  183. END IF;
  184. IF NEW.name IS NOT NULL THEN
  185. SET set_fields_array = JSON_ARRAY_APPEND(set_fields_array, '$', 'name');
  186. SET new_vals = JSON_SET(new_vals, '$.name', NEW.name);
  187. END IF;
  188. IF NEW.category_id IS NOT NULL THEN
  189. SET set_fields_array = JSON_ARRAY_APPEND(set_fields_array, '$', 'category_id');
  190. SET new_vals = JSON_SET(new_vals, '$.category_id', NEW.category_id);
  191. END IF;
  192. IF NEW.manufacturer IS NOT NULL THEN
  193. SET set_fields_array = JSON_ARRAY_APPEND(set_fields_array, '$', 'manufacturer');
  194. SET new_vals = JSON_SET(new_vals, '$.manufacturer', NEW.manufacturer);
  195. END IF;
  196. IF NEW.model IS NOT NULL THEN
  197. SET set_fields_array = JSON_ARRAY_APPEND(set_fields_array, '$', 'model');
  198. SET new_vals = JSON_SET(new_vals, '$.model', NEW.model);
  199. END IF;
  200. IF NEW.serial_number IS NOT NULL THEN
  201. SET set_fields_array = JSON_ARRAY_APPEND(set_fields_array, '$', 'serial_number');
  202. SET new_vals = JSON_SET(new_vals, '$.serial_number', NEW.serial_number);
  203. END IF;
  204. IF NEW.zone_id IS NOT NULL THEN
  205. SET set_fields_array = JSON_ARRAY_APPEND(set_fields_array, '$', 'zone_id');
  206. SET new_vals = JSON_SET(new_vals, '$.zone_id', NEW.zone_id);
  207. END IF;
  208. IF NEW.zone_plus IS NOT NULL THEN
  209. SET set_fields_array = JSON_ARRAY_APPEND(set_fields_array, '$', 'zone_plus');
  210. SET new_vals = JSON_SET(new_vals, '$.zone_plus', NEW.zone_plus);
  211. END IF;
  212. IF NEW.zone_note IS NOT NULL THEN
  213. SET set_fields_array = JSON_ARRAY_APPEND(set_fields_array, '$', 'zone_note');
  214. SET new_vals = JSON_SET(new_vals, '$.zone_note', NEW.zone_note);
  215. END IF;
  216. IF NEW.status IS NOT NULL THEN
  217. SET set_fields_array = JSON_ARRAY_APPEND(set_fields_array, '$', 'status');
  218. SET new_vals = JSON_SET(new_vals, '$.status', NEW.status);
  219. END IF;
  220. IF NEW.last_audit IS NOT NULL THEN
  221. SET set_fields_array = JSON_ARRAY_APPEND(set_fields_array, '$', 'last_audit');
  222. SET new_vals = JSON_SET(new_vals, '$.last_audit', NEW.last_audit);
  223. END IF;
  224. IF NEW.last_audit_status IS NOT NULL THEN
  225. SET set_fields_array = JSON_ARRAY_APPEND(set_fields_array, '$', 'last_audit_status');
  226. SET new_vals = JSON_SET(new_vals, '$.last_audit_status', NEW.last_audit_status);
  227. END IF;
  228. IF NEW.price IS NOT NULL THEN
  229. SET set_fields_array = JSON_ARRAY_APPEND(set_fields_array, '$', 'price');
  230. SET new_vals = JSON_SET(new_vals, '$.price', NEW.price);
  231. END IF;
  232. IF NEW.purchase_date IS NOT NULL THEN
  233. SET set_fields_array = JSON_ARRAY_APPEND(set_fields_array, '$', 'purchase_date');
  234. SET new_vals = JSON_SET(new_vals, '$.purchase_date', NEW.purchase_date);
  235. END IF;
  236. IF NEW.warranty_until IS NOT NULL THEN
  237. SET set_fields_array = JSON_ARRAY_APPEND(set_fields_array, '$', 'warranty_until');
  238. SET new_vals = JSON_SET(new_vals, '$.warranty_until', NEW.warranty_until);
  239. END IF;
  240. IF NEW.expiry_date IS NOT NULL THEN
  241. SET set_fields_array = JSON_ARRAY_APPEND(set_fields_array, '$', 'expiry_date');
  242. SET new_vals = JSON_SET(new_vals, '$.expiry_date', NEW.expiry_date);
  243. END IF;
  244. IF NEW.quantity_available IS NOT NULL THEN
  245. SET set_fields_array = JSON_ARRAY_APPEND(set_fields_array, '$', 'quantity_available');
  246. SET new_vals = JSON_SET(new_vals, '$.quantity_available', NEW.quantity_available);
  247. END IF;
  248. IF NEW.quantity_total IS NOT NULL THEN
  249. SET set_fields_array = JSON_ARRAY_APPEND(set_fields_array, '$', 'quantity_total');
  250. SET new_vals = JSON_SET(new_vals, '$.quantity_total', NEW.quantity_total);
  251. END IF;
  252. IF NEW.quantity_used IS NOT NULL THEN
  253. SET set_fields_array = JSON_ARRAY_APPEND(set_fields_array, '$', 'quantity_used');
  254. SET new_vals = JSON_SET(new_vals, '$.quantity_used', NEW.quantity_used);
  255. END IF;
  256. IF NEW.supplier_id IS NOT NULL THEN
  257. SET set_fields_array = JSON_ARRAY_APPEND(set_fields_array, '$', 'supplier_id');
  258. SET new_vals = JSON_SET(new_vals, '$.supplier_id', NEW.supplier_id);
  259. END IF;
  260. IF NEW.lendable IS NOT NULL THEN
  261. SET set_fields_array = JSON_ARRAY_APPEND(set_fields_array, '$', 'lendable');
  262. SET new_vals = JSON_SET(new_vals, '$.lendable', NEW.lendable);
  263. END IF;
  264. IF NEW.minimum_role_for_lending IS NOT NULL THEN
  265. SET set_fields_array = JSON_ARRAY_APPEND(set_fields_array, '$', 'minimum_role_for_lending');
  266. SET new_vals = JSON_SET(new_vals, '$.minimum_role_for_lending', NEW.minimum_role_for_lending);
  267. END IF;
  268. IF NEW.lending_status IS NOT NULL THEN
  269. SET set_fields_array = JSON_ARRAY_APPEND(set_fields_array, '$', 'lending_status');
  270. SET new_vals = JSON_SET(new_vals, '$.lending_status', NEW.lending_status);
  271. END IF;
  272. IF NEW.current_borrower_id IS NOT NULL THEN
  273. SET set_fields_array = JSON_ARRAY_APPEND(set_fields_array, '$', 'current_borrower_id');
  274. SET new_vals = JSON_SET(new_vals, '$.current_borrower_id', NEW.current_borrower_id);
  275. END IF;
  276. IF NEW.due_date IS NOT NULL THEN
  277. SET set_fields_array = JSON_ARRAY_APPEND(set_fields_array, '$', 'due_date');
  278. SET new_vals = JSON_SET(new_vals, '$.due_date', NEW.due_date);
  279. END IF;
  280. IF NEW.previous_borrower_id IS NOT NULL THEN
  281. SET set_fields_array = JSON_ARRAY_APPEND(set_fields_array, '$', 'previous_borrower_id');
  282. SET new_vals = JSON_SET(new_vals, '$.previous_borrower_id', NEW.previous_borrower_id);
  283. END IF;
  284. IF NEW.audit_task_id IS NOT NULL THEN
  285. SET set_fields_array = JSON_ARRAY_APPEND(set_fields_array, '$', 'audit_task_id');
  286. SET new_vals = JSON_SET(new_vals, '$.audit_task_id', NEW.audit_task_id);
  287. END IF;
  288. IF NEW.no_scan IS NOT NULL THEN
  289. SET set_fields_array = JSON_ARRAY_APPEND(set_fields_array, '$', 'no_scan');
  290. SET new_vals = JSON_SET(new_vals, '$.no_scan', NEW.no_scan);
  291. END IF;
  292. IF NEW.notes IS NOT NULL THEN
  293. SET set_fields_array = JSON_ARRAY_APPEND(set_fields_array, '$', 'notes');
  294. SET new_vals = JSON_SET(new_vals, '$.notes', NEW.notes);
  295. END IF;
  296. IF NEW.additional_fields IS NOT NULL THEN
  297. SET set_fields_array = JSON_ARRAY_APPEND(set_fields_array, '$', 'additional_fields');
  298. SET new_vals = JSON_SET(new_vals, '$.additional_fields', NEW.additional_fields);
  299. END IF;
  300. IF NEW.created_by IS NOT NULL THEN
  301. SET set_fields_array = JSON_ARRAY_APPEND(set_fields_array, '$', 'created_by');
  302. SET new_vals = JSON_SET(new_vals, '$.created_by', NEW.created_by);
  303. END IF;
  304. -- Log the INSERT with only the fields that were set
  305. INSERT INTO asset_change_log (
  306. table_name, action, record_id, changed_fields, new_values,
  307. changed_by_id, changed_by_username
  308. )
  309. VALUES (
  310. 'assets',
  311. 'INSERT',
  312. NEW.id,
  313. set_fields_array,
  314. new_vals,
  315. @current_user_id,
  316. username
  317. );
  318. END */;;
  319. DELIMITER ;
  320. /*!50003 SET sql_mode = @saved_sql_mode */ ;
  321. /*!50003 SET character_set_client = @saved_cs_client */ ;
  322. /*!50003 SET character_set_results = @saved_cs_results */ ;
  323. /*!50003 SET collation_connection = @saved_col_connection */ ;
  324. /*!50003 SET @saved_cs_client = @@character_set_client */ ;
  325. /*!50003 SET @saved_cs_results = @@character_set_results */ ;
  326. /*!50003 SET @saved_col_connection = @@collation_connection */ ;
  327. /*!50003 SET character_set_client = utf8mb4 */ ;
  328. /*!50003 SET character_set_results = utf8mb4 */ ;
  329. /*!50003 SET collation_connection = utf8mb4_0900_ai_ci */ ;
  330. /*!50003 SET @saved_sql_mode = @@sql_mode */ ;
  331. /*!50003 SET sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
  332. DELIMITER ;;
  333. /*!50003 CREATE*/ /*!50017 DEFINER=`root`@`%`*/ /*!50003 TRIGGER assets_before_update_meta
  334. BEFORE UPDATE ON assets
  335. FOR EACH ROW
  336. BEGIN
  337. SET NEW.last_modified_date = NOW();
  338. IF @current_user_id IS NOT NULL THEN
  339. SET NEW.last_modified_by = @current_user_id;
  340. END IF;
  341. END */;;
  342. DELIMITER ;
  343. /*!50003 SET sql_mode = @saved_sql_mode */ ;
  344. /*!50003 SET character_set_client = @saved_cs_client */ ;
  345. /*!50003 SET character_set_results = @saved_cs_results */ ;
  346. /*!50003 SET collation_connection = @saved_col_connection */ ;
  347. /*!50003 SET @saved_cs_client = @@character_set_client */ ;
  348. /*!50003 SET @saved_cs_results = @@character_set_results */ ;
  349. /*!50003 SET @saved_col_connection = @@collation_connection */ ;
  350. /*!50003 SET character_set_client = utf8mb4 */ ;
  351. /*!50003 SET character_set_results = utf8mb4 */ ;
  352. /*!50003 SET collation_connection = utf8mb4_0900_ai_ci */ ;
  353. /*!50003 SET @saved_sql_mode = @@sql_mode */ ;
  354. /*!50003 SET sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
  355. DELIMITER ;;
  356. /*!50003 CREATE*/ /*!50017 DEFINER=`root`@`%`*/ /*!50003 TRIGGER prevent_lend_non_lendable_assets
  357. BEFORE UPDATE ON assets
  358. FOR EACH ROW
  359. BEGIN
  360. -- Check if trying to set lending_status to any borrowed state on a non-lendable asset
  361. IF (NEW.lendable = FALSE OR NEW.lendable IS NULL) AND
  362. NEW.lending_status IN ('Borrowed', 'Deployed', 'Overdue') AND
  363. (OLD.lending_status NOT IN ('Borrowed', 'Deployed', 'Overdue') OR OLD.lending_status IS NULL) THEN
  364. SIGNAL SQLSTATE '45000'
  365. SET MESSAGE_TEXT = 'Cannot lend asset that is marked as non-lendable. Set lendable=TRUE first.';
  366. END IF;
  367. END */;;
  368. DELIMITER ;
  369. /*!50003 SET sql_mode = @saved_sql_mode */ ;
  370. /*!50003 SET character_set_client = @saved_cs_client */ ;
  371. /*!50003 SET character_set_results = @saved_cs_results */ ;
  372. /*!50003 SET collation_connection = @saved_col_connection */ ;
  373. /*!50003 SET @saved_cs_client = @@character_set_client */ ;
  374. /*!50003 SET @saved_cs_results = @@character_set_results */ ;
  375. /*!50003 SET @saved_col_connection = @@collation_connection */ ;
  376. /*!50003 SET character_set_client = utf8mb4 */ ;
  377. /*!50003 SET character_set_results = utf8mb4 */ ;
  378. /*!50003 SET collation_connection = utf8mb4_0900_ai_ci */ ;
  379. /*!50003 SET @saved_sql_mode = @@sql_mode */ ;
  380. /*!50003 SET sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
  381. DELIMITER ;;
  382. /*!50003 CREATE*/ /*!50017 DEFINER=`root`@`%`*/ /*!50003 TRIGGER validate_zone_plus_update
  383. BEFORE UPDATE ON assets
  384. FOR EACH ROW
  385. BEGIN
  386. IF NEW.zone_plus = 'Clarify' AND (NEW.zone_note IS NULL OR NEW.zone_note = '') THEN
  387. SIGNAL SQLSTATE '45000'
  388. SET MESSAGE_TEXT = 'zone_note is required when zone_plus is set to Clarify';
  389. END IF;
  390. END */;;
  391. DELIMITER ;
  392. /*!50003 SET sql_mode = @saved_sql_mode */ ;
  393. /*!50003 SET character_set_client = @saved_cs_client */ ;
  394. /*!50003 SET character_set_results = @saved_cs_results */ ;
  395. /*!50003 SET collation_connection = @saved_col_connection */ ;
  396. /*!50003 SET @saved_cs_client = @@character_set_client */ ;
  397. /*!50003 SET @saved_cs_results = @@character_set_results */ ;
  398. /*!50003 SET @saved_col_connection = @@collation_connection */ ;
  399. /*!50003 SET character_set_client = utf8mb4 */ ;
  400. /*!50003 SET character_set_results = utf8mb4 */ ;
  401. /*!50003 SET collation_connection = utf8mb4_0900_ai_ci */ ;
  402. /*!50003 SET @saved_sql_mode = @@sql_mode */ ;
  403. /*!50003 SET sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
  404. DELIMITER ;;
  405. /*!50003 CREATE*/ /*!50017 DEFINER=`root`@`%`*/ /*!50003 TRIGGER assets_after_update_log
  406. AFTER UPDATE ON assets
  407. FOR EACH ROW
  408. BEGIN
  409. DECLARE username VARCHAR(100);
  410. DECLARE changed_fields_array JSON;
  411. DECLARE old_vals JSON;
  412. DECLARE new_vals JSON;
  413. IF @current_user_id IS NOT NULL THEN
  414. SELECT users.username INTO username FROM users WHERE id = @current_user_id;
  415. END IF;
  416. -- Build JSON objects only with changed fields
  417. SET changed_fields_array = JSON_ARRAY();
  418. SET old_vals = JSON_OBJECT();
  419. SET new_vals = JSON_OBJECT();
  420. IF OLD.asset_tag <=> NEW.asset_tag IS FALSE THEN
  421. SET changed_fields_array = JSON_ARRAY_APPEND(changed_fields_array, '$', 'asset_tag');
  422. SET old_vals = JSON_SET(old_vals, '$.asset_tag', OLD.asset_tag);
  423. SET new_vals = JSON_SET(new_vals, '$.asset_tag', NEW.asset_tag);
  424. END IF;
  425. IF OLD.asset_numeric_id <=> NEW.asset_numeric_id IS FALSE THEN
  426. SET changed_fields_array = JSON_ARRAY_APPEND(changed_fields_array, '$', 'asset_numeric_id');
  427. SET old_vals = JSON_SET(old_vals, '$.asset_numeric_id', OLD.asset_numeric_id);
  428. SET new_vals = JSON_SET(new_vals, '$.asset_numeric_id', NEW.asset_numeric_id);
  429. END IF;
  430. IF OLD.asset_type <=> NEW.asset_type IS FALSE THEN
  431. SET changed_fields_array = JSON_ARRAY_APPEND(changed_fields_array, '$', 'asset_type');
  432. SET old_vals = JSON_SET(old_vals, '$.asset_type', OLD.asset_type);
  433. SET new_vals = JSON_SET(new_vals, '$.asset_type', NEW.asset_type);
  434. END IF;
  435. IF OLD.name <=> NEW.name IS FALSE THEN
  436. SET changed_fields_array = JSON_ARRAY_APPEND(changed_fields_array, '$', 'name');
  437. SET old_vals = JSON_SET(old_vals, '$.name', OLD.name);
  438. SET new_vals = JSON_SET(new_vals, '$.name', NEW.name);
  439. END IF;
  440. IF OLD.category_id <=> NEW.category_id IS FALSE THEN
  441. SET changed_fields_array = JSON_ARRAY_APPEND(changed_fields_array, '$', 'category_id');
  442. SET old_vals = JSON_SET(old_vals, '$.category_id', OLD.category_id);
  443. SET new_vals = JSON_SET(new_vals, '$.category_id', NEW.category_id);
  444. END IF;
  445. IF OLD.manufacturer <=> NEW.manufacturer IS FALSE THEN
  446. SET changed_fields_array = JSON_ARRAY_APPEND(changed_fields_array, '$', 'manufacturer');
  447. SET old_vals = JSON_SET(old_vals, '$.manufacturer', OLD.manufacturer);
  448. SET new_vals = JSON_SET(new_vals, '$.manufacturer', NEW.manufacturer);
  449. END IF;
  450. IF OLD.model <=> NEW.model IS FALSE THEN
  451. SET changed_fields_array = JSON_ARRAY_APPEND(changed_fields_array, '$', 'model');
  452. SET old_vals = JSON_SET(old_vals, '$.model', OLD.model);
  453. SET new_vals = JSON_SET(new_vals, '$.model', NEW.model);
  454. END IF;
  455. IF OLD.serial_number <=> NEW.serial_number IS FALSE THEN
  456. SET changed_fields_array = JSON_ARRAY_APPEND(changed_fields_array, '$', 'serial_number');
  457. SET old_vals = JSON_SET(old_vals, '$.serial_number', OLD.serial_number);
  458. SET new_vals = JSON_SET(new_vals, '$.serial_number', NEW.serial_number);
  459. END IF;
  460. IF OLD.zone_id <=> NEW.zone_id IS FALSE THEN
  461. SET changed_fields_array = JSON_ARRAY_APPEND(changed_fields_array, '$', 'zone_id');
  462. SET old_vals = JSON_SET(old_vals, '$.zone_id', OLD.zone_id);
  463. SET new_vals = JSON_SET(new_vals, '$.zone_id', NEW.zone_id);
  464. END IF;
  465. IF OLD.zone_plus <=> NEW.zone_plus IS FALSE THEN
  466. SET changed_fields_array = JSON_ARRAY_APPEND(changed_fields_array, '$', 'zone_plus');
  467. SET old_vals = JSON_SET(old_vals, '$.zone_plus', OLD.zone_plus);
  468. SET new_vals = JSON_SET(new_vals, '$.zone_plus', NEW.zone_plus);
  469. END IF;
  470. IF OLD.zone_note <=> NEW.zone_note IS FALSE THEN
  471. SET changed_fields_array = JSON_ARRAY_APPEND(changed_fields_array, '$', 'zone_note');
  472. SET old_vals = JSON_SET(old_vals, '$.zone_note', OLD.zone_note);
  473. SET new_vals = JSON_SET(new_vals, '$.zone_note', NEW.zone_note);
  474. END IF;
  475. IF OLD.status <=> NEW.status IS FALSE THEN
  476. SET changed_fields_array = JSON_ARRAY_APPEND(changed_fields_array, '$', 'status');
  477. SET old_vals = JSON_SET(old_vals, '$.status', OLD.status);
  478. SET new_vals = JSON_SET(new_vals, '$.status', NEW.status);
  479. END IF;
  480. IF OLD.last_audit <=> NEW.last_audit IS FALSE THEN
  481. SET changed_fields_array = JSON_ARRAY_APPEND(changed_fields_array, '$', 'last_audit');
  482. SET old_vals = JSON_SET(old_vals, '$.last_audit', OLD.last_audit);
  483. SET new_vals = JSON_SET(new_vals, '$.last_audit', NEW.last_audit);
  484. END IF;
  485. IF OLD.last_audit_status <=> NEW.last_audit_status IS FALSE THEN
  486. SET changed_fields_array = JSON_ARRAY_APPEND(changed_fields_array, '$', 'last_audit_status');
  487. SET old_vals = JSON_SET(old_vals, '$.last_audit_status', OLD.last_audit_status);
  488. SET new_vals = JSON_SET(new_vals, '$.last_audit_status', NEW.last_audit_status);
  489. END IF;
  490. IF OLD.price <=> NEW.price IS FALSE THEN
  491. SET changed_fields_array = JSON_ARRAY_APPEND(changed_fields_array, '$', 'price');
  492. SET old_vals = JSON_SET(old_vals, '$.price', OLD.price);
  493. SET new_vals = JSON_SET(new_vals, '$.price', NEW.price);
  494. END IF;
  495. IF OLD.purchase_date <=> NEW.purchase_date IS FALSE THEN
  496. SET changed_fields_array = JSON_ARRAY_APPEND(changed_fields_array, '$', 'purchase_date');
  497. SET old_vals = JSON_SET(old_vals, '$.purchase_date', OLD.purchase_date);
  498. SET new_vals = JSON_SET(new_vals, '$.purchase_date', NEW.purchase_date);
  499. END IF;
  500. IF OLD.warranty_until <=> NEW.warranty_until IS FALSE THEN
  501. SET changed_fields_array = JSON_ARRAY_APPEND(changed_fields_array, '$', 'warranty_until');
  502. SET old_vals = JSON_SET(old_vals, '$.warranty_until', OLD.warranty_until);
  503. SET new_vals = JSON_SET(new_vals, '$.warranty_until', NEW.warranty_until);
  504. END IF;
  505. IF OLD.expiry_date <=> NEW.expiry_date IS FALSE THEN
  506. SET changed_fields_array = JSON_ARRAY_APPEND(changed_fields_array, '$', 'expiry_date');
  507. SET old_vals = JSON_SET(old_vals, '$.expiry_date', OLD.expiry_date);
  508. SET new_vals = JSON_SET(new_vals, '$.expiry_date', NEW.expiry_date);
  509. END IF;
  510. IF OLD.quantity_available <=> NEW.quantity_available IS FALSE THEN
  511. SET changed_fields_array = JSON_ARRAY_APPEND(changed_fields_array, '$', 'quantity_available');
  512. SET old_vals = JSON_SET(old_vals, '$.quantity_available', OLD.quantity_available);
  513. SET new_vals = JSON_SET(new_vals, '$.quantity_available', NEW.quantity_available);
  514. END IF;
  515. IF OLD.quantity_total <=> NEW.quantity_total IS FALSE THEN
  516. SET changed_fields_array = JSON_ARRAY_APPEND(changed_fields_array, '$', 'quantity_total');
  517. SET old_vals = JSON_SET(old_vals, '$.quantity_total', OLD.quantity_total);
  518. SET new_vals = JSON_SET(new_vals, '$.quantity_total', NEW.quantity_total);
  519. END IF;
  520. IF OLD.quantity_used <=> NEW.quantity_used IS FALSE THEN
  521. SET changed_fields_array = JSON_ARRAY_APPEND(changed_fields_array, '$', 'quantity_used');
  522. SET old_vals = JSON_SET(old_vals, '$.quantity_used', OLD.quantity_used);
  523. SET new_vals = JSON_SET(new_vals, '$.quantity_used', NEW.quantity_used);
  524. END IF;
  525. IF OLD.supplier_id <=> NEW.supplier_id IS FALSE THEN
  526. SET changed_fields_array = JSON_ARRAY_APPEND(changed_fields_array, '$', 'supplier_id');
  527. SET old_vals = JSON_SET(old_vals, '$.supplier_id', OLD.supplier_id);
  528. SET new_vals = JSON_SET(new_vals, '$.supplier_id', NEW.supplier_id);
  529. END IF;
  530. IF OLD.lendable <=> NEW.lendable IS FALSE THEN
  531. SET changed_fields_array = JSON_ARRAY_APPEND(changed_fields_array, '$', 'lendable');
  532. SET old_vals = JSON_SET(old_vals, '$.lendable', OLD.lendable);
  533. SET new_vals = JSON_SET(new_vals, '$.lendable', NEW.lendable);
  534. END IF;
  535. IF OLD.minimum_role_for_lending <=> NEW.minimum_role_for_lending IS FALSE THEN
  536. SET changed_fields_array = JSON_ARRAY_APPEND(changed_fields_array, '$', 'minimum_role_for_lending');
  537. SET old_vals = JSON_SET(old_vals, '$.minimum_role_for_lending', OLD.minimum_role_for_lending);
  538. SET new_vals = JSON_SET(new_vals, '$.minimum_role_for_lending', NEW.minimum_role_for_lending);
  539. END IF;
  540. IF OLD.lending_status <=> NEW.lending_status IS FALSE THEN
  541. SET changed_fields_array = JSON_ARRAY_APPEND(changed_fields_array, '$', 'lending_status');
  542. SET old_vals = JSON_SET(old_vals, '$.lending_status', OLD.lending_status);
  543. SET new_vals = JSON_SET(new_vals, '$.lending_status', NEW.lending_status);
  544. END IF;
  545. IF OLD.current_borrower_id <=> NEW.current_borrower_id IS FALSE THEN
  546. SET changed_fields_array = JSON_ARRAY_APPEND(changed_fields_array, '$', 'current_borrower_id');
  547. SET old_vals = JSON_SET(old_vals, '$.current_borrower_id', OLD.current_borrower_id);
  548. SET new_vals = JSON_SET(new_vals, '$.current_borrower_id', NEW.current_borrower_id);
  549. END IF;
  550. IF OLD.due_date <=> NEW.due_date IS FALSE THEN
  551. SET changed_fields_array = JSON_ARRAY_APPEND(changed_fields_array, '$', 'due_date');
  552. SET old_vals = JSON_SET(old_vals, '$.due_date', OLD.due_date);
  553. SET new_vals = JSON_SET(new_vals, '$.due_date', NEW.due_date);
  554. END IF;
  555. IF OLD.previous_borrower_id <=> NEW.previous_borrower_id IS FALSE THEN
  556. SET changed_fields_array = JSON_ARRAY_APPEND(changed_fields_array, '$', 'previous_borrower_id');
  557. SET old_vals = JSON_SET(old_vals, '$.previous_borrower_id', OLD.previous_borrower_id);
  558. SET new_vals = JSON_SET(new_vals, '$.previous_borrower_id', NEW.previous_borrower_id);
  559. END IF;
  560. IF OLD.audit_task_id <=> NEW.audit_task_id IS FALSE THEN
  561. SET changed_fields_array = JSON_ARRAY_APPEND(changed_fields_array, '$', 'audit_task_id');
  562. SET old_vals = JSON_SET(old_vals, '$.audit_task_id', OLD.audit_task_id);
  563. SET new_vals = JSON_SET(new_vals, '$.audit_task_id', NEW.audit_task_id);
  564. END IF;
  565. IF OLD.no_scan <=> NEW.no_scan IS FALSE THEN
  566. SET changed_fields_array = JSON_ARRAY_APPEND(changed_fields_array, '$', 'no_scan');
  567. SET old_vals = JSON_SET(old_vals, '$.no_scan', OLD.no_scan);
  568. SET new_vals = JSON_SET(new_vals, '$.no_scan', NEW.no_scan);
  569. END IF;
  570. IF OLD.notes <=> NEW.notes IS FALSE THEN
  571. SET changed_fields_array = JSON_ARRAY_APPEND(changed_fields_array, '$', 'notes');
  572. SET old_vals = JSON_SET(old_vals, '$.notes', OLD.notes);
  573. SET new_vals = JSON_SET(new_vals, '$.notes', NEW.notes);
  574. END IF;
  575. IF OLD.additional_fields <=> NEW.additional_fields IS FALSE THEN
  576. SET changed_fields_array = JSON_ARRAY_APPEND(changed_fields_array, '$', 'additional_fields');
  577. SET old_vals = JSON_SET(old_vals, '$.additional_fields', OLD.additional_fields);
  578. SET new_vals = JSON_SET(new_vals, '$.additional_fields', NEW.additional_fields);
  579. END IF;
  580. -- Only log if there were actual changes (excluding auto-updated fields)
  581. IF JSON_LENGTH(changed_fields_array) > 0 THEN
  582. INSERT INTO asset_change_log (
  583. table_name, action, record_id, changed_fields, old_values, new_values,
  584. changed_by_id, changed_by_username
  585. )
  586. VALUES (
  587. 'assets',
  588. 'UPDATE',
  589. NEW.id,
  590. changed_fields_array,
  591. old_vals,
  592. new_vals,
  593. @current_user_id,
  594. username
  595. );
  596. END IF;
  597. END */;;
  598. DELIMITER ;
  599. /*!50003 SET sql_mode = @saved_sql_mode */ ;
  600. /*!50003 SET character_set_client = @saved_cs_client */ ;
  601. /*!50003 SET character_set_results = @saved_cs_results */ ;
  602. /*!50003 SET collation_connection = @saved_col_connection */ ;
  603. /*!50003 SET @saved_cs_client = @@character_set_client */ ;
  604. /*!50003 SET @saved_cs_results = @@character_set_results */ ;
  605. /*!50003 SET @saved_col_connection = @@collation_connection */ ;
  606. /*!50003 SET character_set_client = utf8mb4 */ ;
  607. /*!50003 SET character_set_results = utf8mb4 */ ;
  608. /*!50003 SET collation_connection = utf8mb4_0900_ai_ci */ ;
  609. /*!50003 SET @saved_sql_mode = @@sql_mode */ ;
  610. /*!50003 SET sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
  611. DELIMITER ;;
  612. /*!50003 CREATE*/ /*!50017 DEFINER=`root`@`%`*/ /*!50003 TRIGGER auto_detect_asset_issues
  613. AFTER UPDATE ON assets
  614. FOR EACH ROW
  615. BEGIN
  616. DECLARE issue_title VARCHAR(255);
  617. DECLARE issue_description TEXT;
  618. DECLARE issue_severity ENUM('Critical', 'High', 'Medium', 'Low');
  619. DECLARE detection_trigger_name VARCHAR(100);
  620. -- Check for lending_status changes to problematic states
  621. IF (OLD.lending_status IS NULL OR OLD.lending_status != NEW.lending_status)
  622. AND NEW.lending_status IN ('Overdue', 'Illegally Handed Out', 'Stolen') THEN
  623. -- Determine issue details based on lending_status
  624. CASE NEW.lending_status
  625. WHEN 'Overdue' THEN
  626. SET issue_title = CONCAT('Asset Overdue: ', COALESCE(NEW.name, NEW.asset_tag, CAST(NEW.asset_numeric_id AS CHAR)));
  627. SET issue_description = CONCAT('Asset lending status changed to Overdue. Asset: ', NEW.asset_tag,
  628. CASE WHEN NEW.name IS NOT NULL THEN CONCAT(' (', NEW.name, ')') ELSE '' END);
  629. SET issue_severity = 'High';
  630. SET detection_trigger_name = 'LENDING_OVERDUE';
  631. WHEN 'Illegally Handed Out' THEN
  632. SET issue_title = CONCAT('Asset Illegally Handed Out: ', COALESCE(NEW.name, NEW.asset_tag, CAST(NEW.asset_numeric_id AS CHAR)));
  633. SET issue_description = CONCAT('Asset lending status changed to Illegally Handed Out. Asset: ', NEW.asset_tag,
  634. CASE WHEN NEW.name IS NOT NULL THEN CONCAT(' (', NEW.name, ')') ELSE '' END);
  635. SET issue_severity = 'Critical';
  636. SET detection_trigger_name = 'LENDING_ILLEGAL';
  637. WHEN 'Stolen' THEN
  638. SET issue_title = CONCAT('Asset Stolen: ', COALESCE(NEW.name, NEW.asset_tag, CAST(NEW.asset_numeric_id AS CHAR)));
  639. SET issue_description = CONCAT('Asset lending status changed to Stolen (14+ days overdue). Asset: ', NEW.asset_tag,
  640. CASE WHEN NEW.name IS NOT NULL THEN CONCAT(' (', NEW.name, ')') ELSE '' END);
  641. SET issue_severity = 'Critical';
  642. SET detection_trigger_name = 'LENDING_STOLEN';
  643. END CASE;
  644. -- Insert the auto-detected issue
  645. INSERT INTO issue_tracker (
  646. issue_type, asset_id, title, description, severity, priority, status,
  647. reported_by, auto_detected, detection_trigger, created_date
  648. )
  649. VALUES (
  650. 'Asset Issue', NEW.id, issue_title, issue_description, issue_severity, 'Urgent', 'Open',
  651. COALESCE(@current_user_id, 1), TRUE, detection_trigger_name, NOW()
  652. );
  653. END IF;
  654. -- Check for status changes to problematic states
  655. IF OLD.status != NEW.status AND NEW.status IN ('Attention', 'Faulty', 'Missing', 'Retired', 'In Repair', 'Expired') THEN
  656. -- Determine issue details based on status
  657. CASE NEW.status
  658. WHEN 'Attention' THEN
  659. SET issue_title = CONCAT('Asset Needs Attention: ', COALESCE(NEW.name, NEW.asset_tag, NEW.asset_numeric_id));
  660. SET issue_description = CONCAT('Asset status changed to Attention. Asset: ', NEW.asset_numeric_id, CASE WHEN NEW.name IS NOT NULL THEN CONCAT(' (', NEW.name, ')') ELSE '' END);
  661. SET issue_severity = 'Medium';
  662. SET detection_trigger_name = 'STATUS_ATTENTION';
  663. WHEN 'Faulty' THEN
  664. SET issue_title = CONCAT('Asset Faulty: ', COALESCE(NEW.name, NEW.asset_tag, NEW.asset_numeric_id));
  665. SET issue_description = CONCAT('Asset status changed to Faulty. Asset: ', NEW.asset_numeric_id, CASE WHEN NEW.name IS NOT NULL THEN CONCAT(' (', NEW.name, ')') ELSE '' END);
  666. SET issue_severity = 'High';
  667. SET detection_trigger_name = 'STATUS_FAULTY';
  668. WHEN 'Missing' THEN
  669. SET issue_title = CONCAT('Asset Missing: ', COALESCE(NEW.name, NEW.asset_tag, NEW.asset_numeric_id));
  670. SET issue_description = CONCAT('Asset status changed to Missing. Asset: ', NEW.asset_numeric_id, CASE WHEN NEW.name IS NOT NULL THEN CONCAT(' (', NEW.name, ')') ELSE '' END);
  671. SET issue_severity = 'Critical';
  672. SET detection_trigger_name = 'STATUS_MISSING';
  673. WHEN 'Retired' THEN
  674. SET issue_title = CONCAT('Asset Retired: ', COALESCE(NEW.name, NEW.asset_tag, NEW.asset_numeric_id));
  675. SET issue_description = CONCAT('Asset status changed to Retired. Asset: ', NEW.asset_numeric_id, CASE WHEN NEW.name IS NOT NULL THEN CONCAT(' (', NEW.name, ')') ELSE '' END);
  676. SET issue_severity = 'Low';
  677. SET detection_trigger_name = 'STATUS_RETIRED';
  678. WHEN 'In Repair' THEN
  679. SET issue_title = CONCAT('Asset In Repair: ', COALESCE(NEW.name, NEW.asset_tag, NEW.asset_numeric_id));
  680. SET issue_description = CONCAT('Asset status changed to In Repair. Asset: ', NEW.asset_numeric_id, CASE WHEN NEW.name IS NOT NULL THEN CONCAT(' (', NEW.name, ')') ELSE '' END);
  681. SET issue_severity = 'Medium';
  682. SET detection_trigger_name = 'STATUS_IN_REPAIR';
  683. WHEN 'Expired' THEN
  684. SET issue_title = CONCAT('Asset Expired: ', COALESCE(NEW.name, NEW.asset_tag, NEW.asset_numeric_id));
  685. SET issue_description = CONCAT('Asset status changed to Expired. Asset: ', NEW.asset_numeric_id, CASE WHEN NEW.name IS NOT NULL THEN CONCAT(' (', NEW.name, ')') ELSE '' END);
  686. SET issue_severity = 'Medium';
  687. SET detection_trigger_name = 'STATUS_EXPIRED';
  688. END CASE;
  689. -- Insert the auto-detected issue
  690. INSERT INTO issue_tracker (
  691. issue_type, asset_id, title, description, severity, priority, status,
  692. reported_by, auto_detected, detection_trigger, created_date
  693. )
  694. VALUES (
  695. 'Asset Issue', NEW.id, issue_title, issue_description, issue_severity, 'Normal', 'Open',
  696. COALESCE(@current_user_id, 1), TRUE, detection_trigger_name, NOW()
  697. );
  698. END IF;
  699. -- Auto-resolve issues when status becomes Good again
  700. IF OLD.status != NEW.status AND NEW.status = 'Good' AND OLD.status IN ('Faulty', 'Missing', 'In Repair', 'Expired') THEN
  701. UPDATE issue_tracker
  702. SET status = 'Resolved',
  703. solution = 'Automatically Fixed',
  704. solution_plus = CONCAT('Asset status automatically changed from ', OLD.status, ' to Good'),
  705. resolved_date = NOW(),
  706. resolved_by = COALESCE(@current_user_id, 1)
  707. WHERE asset_id = NEW.id
  708. AND status IN ('Open', 'In Progress')
  709. AND auto_detected = TRUE
  710. AND detection_trigger IN ('STATUS_FAULTY', 'STATUS_MISSING', 'STATUS_IN_REPAIR', 'STATUS_EXPIRED');
  711. END IF;
  712. -- Auto-resolve overdue/stolen/illegal issues when item is returned (lending_status becomes Available)
  713. IF (OLD.lending_status IS NULL OR OLD.lending_status != NEW.lending_status)
  714. AND NEW.lending_status = 'Available'
  715. AND OLD.lending_status IN ('Overdue', 'Illegally Handed Out', 'Stolen') THEN
  716. UPDATE issue_tracker
  717. SET status = 'Resolved',
  718. solution = 'Items Returned',
  719. solution_plus = CONCAT('Asset was returned - lending status changed from ', OLD.lending_status, ' to Available'),
  720. resolved_date = NOW(),
  721. resolved_by = COALESCE(@current_user_id, 1)
  722. WHERE asset_id = NEW.id
  723. AND status IN ('Open', 'In Progress')
  724. AND auto_detected = TRUE
  725. AND detection_trigger IN ('LENDING_OVERDUE', 'LENDING_ILLEGAL', 'LENDING_STOLEN');
  726. END IF;
  727. END */;;
  728. DELIMITER ;
  729. /*!50003 SET sql_mode = @saved_sql_mode */ ;
  730. /*!50003 SET character_set_client = @saved_cs_client */ ;
  731. /*!50003 SET character_set_results = @saved_cs_results */ ;
  732. /*!50003 SET collation_connection = @saved_col_connection */ ;
  733. /*!50003 SET @saved_cs_client = @@character_set_client */ ;
  734. /*!50003 SET @saved_cs_results = @@character_set_results */ ;
  735. /*!50003 SET @saved_col_connection = @@collation_connection */ ;
  736. /*!50003 SET character_set_client = utf8mb4 */ ;
  737. /*!50003 SET character_set_results = utf8mb4 */ ;
  738. /*!50003 SET collation_connection = utf8mb4_0900_ai_ci */ ;
  739. /*!50003 SET @saved_sql_mode = @@sql_mode */ ;
  740. /*!50003 SET sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
  741. DELIMITER ;;
  742. /*!50003 CREATE*/ /*!50017 DEFINER=`root`@`%`*/ /*!50003 TRIGGER prevent_delete_borrowed_assets
  743. BEFORE DELETE ON assets
  744. FOR EACH ROW
  745. BEGIN
  746. IF OLD.lending_status IN ('Borrowed', 'Deployed', 'Overdue') THEN
  747. SIGNAL SQLSTATE '45000'
  748. SET MESSAGE_TEXT = 'Cannot delete asset that is currently borrowed or deployed, maybe update to retired or unmanaged before';
  749. END IF;
  750. END */;;
  751. DELIMITER ;
  752. /*!50003 SET sql_mode = @saved_sql_mode */ ;
  753. /*!50003 SET character_set_client = @saved_cs_client */ ;
  754. /*!50003 SET character_set_results = @saved_cs_results */ ;
  755. /*!50003 SET collation_connection = @saved_col_connection */ ;
  756. /*!50003 SET @saved_cs_client = @@character_set_client */ ;
  757. /*!50003 SET @saved_cs_results = @@character_set_results */ ;
  758. /*!50003 SET @saved_col_connection = @@collation_connection */ ;
  759. /*!50003 SET character_set_client = utf8mb4 */ ;
  760. /*!50003 SET character_set_results = utf8mb4 */ ;
  761. /*!50003 SET collation_connection = utf8mb4_0900_ai_ci */ ;
  762. /*!50003 SET @saved_sql_mode = @@sql_mode */ ;
  763. /*!50003 SET sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
  764. DELIMITER ;;
  765. /*!50003 CREATE*/ /*!50017 DEFINER=`root`@`%`*/ /*!50003 TRIGGER assets_after_delete_log
  766. AFTER DELETE ON assets
  767. FOR EACH ROW
  768. BEGIN
  769. DECLARE username VARCHAR(100);
  770. DECLARE deleted_fields_array JSON;
  771. DECLARE old_vals JSON;
  772. IF @current_user_id IS NOT NULL THEN
  773. SELECT users.username INTO username FROM users WHERE id = @current_user_id;
  774. END IF;
  775. -- Build JSON objects only with non-NULL fields (for restore capability)
  776. SET deleted_fields_array = JSON_ARRAY();
  777. SET old_vals = JSON_OBJECT();
  778. IF OLD.asset_tag IS NOT NULL THEN
  779. SET deleted_fields_array = JSON_ARRAY_APPEND(deleted_fields_array, '$', 'asset_tag');
  780. SET old_vals = JSON_SET(old_vals, '$.asset_tag', OLD.asset_tag);
  781. END IF;
  782. IF OLD.asset_numeric_id IS NOT NULL THEN
  783. SET deleted_fields_array = JSON_ARRAY_APPEND(deleted_fields_array, '$', 'asset_numeric_id');
  784. SET old_vals = JSON_SET(old_vals, '$.asset_numeric_id', OLD.asset_numeric_id);
  785. END IF;
  786. IF OLD.asset_type IS NOT NULL THEN
  787. SET deleted_fields_array = JSON_ARRAY_APPEND(deleted_fields_array, '$', 'asset_type');
  788. SET old_vals = JSON_SET(old_vals, '$.asset_type', OLD.asset_type);
  789. END IF;
  790. IF OLD.name IS NOT NULL THEN
  791. SET deleted_fields_array = JSON_ARRAY_APPEND(deleted_fields_array, '$', 'name');
  792. SET old_vals = JSON_SET(old_vals, '$.name', OLD.name);
  793. END IF;
  794. IF OLD.category_id IS NOT NULL THEN
  795. SET deleted_fields_array = JSON_ARRAY_APPEND(deleted_fields_array, '$', 'category_id');
  796. SET old_vals = JSON_SET(old_vals, '$.category_id', OLD.category_id);
  797. END IF;
  798. IF OLD.manufacturer IS NOT NULL THEN
  799. SET deleted_fields_array = JSON_ARRAY_APPEND(deleted_fields_array, '$', 'manufacturer');
  800. SET old_vals = JSON_SET(old_vals, '$.manufacturer', OLD.manufacturer);
  801. END IF;
  802. IF OLD.model IS NOT NULL THEN
  803. SET deleted_fields_array = JSON_ARRAY_APPEND(deleted_fields_array, '$', 'model');
  804. SET old_vals = JSON_SET(old_vals, '$.model', OLD.model);
  805. END IF;
  806. IF OLD.serial_number IS NOT NULL THEN
  807. SET deleted_fields_array = JSON_ARRAY_APPEND(deleted_fields_array, '$', 'serial_number');
  808. SET old_vals = JSON_SET(old_vals, '$.serial_number', OLD.serial_number);
  809. END IF;
  810. IF OLD.zone_id IS NOT NULL THEN
  811. SET deleted_fields_array = JSON_ARRAY_APPEND(deleted_fields_array, '$', 'zone_id');
  812. SET old_vals = JSON_SET(old_vals, '$.zone_id', OLD.zone_id);
  813. END IF;
  814. IF OLD.zone_plus IS NOT NULL THEN
  815. SET deleted_fields_array = JSON_ARRAY_APPEND(deleted_fields_array, '$', 'zone_plus');
  816. SET old_vals = JSON_SET(old_vals, '$.zone_plus', OLD.zone_plus);
  817. END IF;
  818. IF OLD.zone_note IS NOT NULL THEN
  819. SET deleted_fields_array = JSON_ARRAY_APPEND(deleted_fields_array, '$', 'zone_note');
  820. SET old_vals = JSON_SET(old_vals, '$.zone_note', OLD.zone_note);
  821. END IF;
  822. IF OLD.status IS NOT NULL THEN
  823. SET deleted_fields_array = JSON_ARRAY_APPEND(deleted_fields_array, '$', 'status');
  824. SET old_vals = JSON_SET(old_vals, '$.status', OLD.status);
  825. END IF;
  826. IF OLD.last_audit IS NOT NULL THEN
  827. SET deleted_fields_array = JSON_ARRAY_APPEND(deleted_fields_array, '$', 'last_audit');
  828. SET old_vals = JSON_SET(old_vals, '$.last_audit', OLD.last_audit);
  829. END IF;
  830. IF OLD.last_audit_status IS NOT NULL THEN
  831. SET deleted_fields_array = JSON_ARRAY_APPEND(deleted_fields_array, '$', 'last_audit_status');
  832. SET old_vals = JSON_SET(old_vals, '$.last_audit_status', OLD.last_audit_status);
  833. END IF;
  834. IF OLD.price IS NOT NULL THEN
  835. SET deleted_fields_array = JSON_ARRAY_APPEND(deleted_fields_array, '$', 'price');
  836. SET old_vals = JSON_SET(old_vals, '$.price', OLD.price);
  837. END IF;
  838. IF OLD.purchase_date IS NOT NULL THEN
  839. SET deleted_fields_array = JSON_ARRAY_APPEND(deleted_fields_array, '$', 'purchase_date');
  840. SET old_vals = JSON_SET(old_vals, '$.purchase_date', OLD.purchase_date);
  841. END IF;
  842. IF OLD.warranty_until IS NOT NULL THEN
  843. SET deleted_fields_array = JSON_ARRAY_APPEND(deleted_fields_array, '$', 'warranty_until');
  844. SET old_vals = JSON_SET(old_vals, '$.warranty_until', OLD.warranty_until);
  845. END IF;
  846. IF OLD.expiry_date IS NOT NULL THEN
  847. SET deleted_fields_array = JSON_ARRAY_APPEND(deleted_fields_array, '$', 'expiry_date');
  848. SET old_vals = JSON_SET(old_vals, '$.expiry_date', OLD.expiry_date);
  849. END IF;
  850. IF OLD.quantity_available IS NOT NULL THEN
  851. SET deleted_fields_array = JSON_ARRAY_APPEND(deleted_fields_array, '$', 'quantity_available');
  852. SET old_vals = JSON_SET(old_vals, '$.quantity_available', OLD.quantity_available);
  853. END IF;
  854. IF OLD.quantity_total IS NOT NULL THEN
  855. SET deleted_fields_array = JSON_ARRAY_APPEND(deleted_fields_array, '$', 'quantity_total');
  856. SET old_vals = JSON_SET(old_vals, '$.quantity_total', OLD.quantity_total);
  857. END IF;
  858. IF OLD.quantity_used IS NOT NULL THEN
  859. SET deleted_fields_array = JSON_ARRAY_APPEND(deleted_fields_array, '$', 'quantity_used');
  860. SET old_vals = JSON_SET(old_vals, '$.quantity_used', OLD.quantity_used);
  861. END IF;
  862. IF OLD.supplier_id IS NOT NULL THEN
  863. SET deleted_fields_array = JSON_ARRAY_APPEND(deleted_fields_array, '$', 'supplier_id');
  864. SET old_vals = JSON_SET(old_vals, '$.supplier_id', OLD.supplier_id);
  865. END IF;
  866. IF OLD.lendable IS NOT NULL THEN
  867. SET deleted_fields_array = JSON_ARRAY_APPEND(deleted_fields_array, '$', 'lendable');
  868. SET old_vals = JSON_SET(old_vals, '$.lendable', OLD.lendable);
  869. END IF;
  870. IF OLD.minimum_role_for_lending IS NOT NULL THEN
  871. SET deleted_fields_array = JSON_ARRAY_APPEND(deleted_fields_array, '$', 'minimum_role_for_lending');
  872. SET old_vals = JSON_SET(old_vals, '$.minimum_role_for_lending', OLD.minimum_role_for_lending);
  873. END IF;
  874. IF OLD.lending_status IS NOT NULL THEN
  875. SET deleted_fields_array = JSON_ARRAY_APPEND(deleted_fields_array, '$', 'lending_status');
  876. SET old_vals = JSON_SET(old_vals, '$.lending_status', OLD.lending_status);
  877. END IF;
  878. IF OLD.current_borrower_id IS NOT NULL THEN
  879. SET deleted_fields_array = JSON_ARRAY_APPEND(deleted_fields_array, '$', 'current_borrower_id');
  880. SET old_vals = JSON_SET(old_vals, '$.current_borrower_id', OLD.current_borrower_id);
  881. END IF;
  882. IF OLD.due_date IS NOT NULL THEN
  883. SET deleted_fields_array = JSON_ARRAY_APPEND(deleted_fields_array, '$', 'due_date');
  884. SET old_vals = JSON_SET(old_vals, '$.due_date', OLD.due_date);
  885. END IF;
  886. IF OLD.previous_borrower_id IS NOT NULL THEN
  887. SET deleted_fields_array = JSON_ARRAY_APPEND(deleted_fields_array, '$', 'previous_borrower_id');
  888. SET old_vals = JSON_SET(old_vals, '$.previous_borrower_id', OLD.previous_borrower_id);
  889. END IF;
  890. IF OLD.audit_task_id IS NOT NULL THEN
  891. SET deleted_fields_array = JSON_ARRAY_APPEND(deleted_fields_array, '$', 'audit_task_id');
  892. SET old_vals = JSON_SET(old_vals, '$.audit_task_id', OLD.audit_task_id);
  893. END IF;
  894. IF OLD.no_scan IS NOT NULL THEN
  895. SET deleted_fields_array = JSON_ARRAY_APPEND(deleted_fields_array, '$', 'no_scan');
  896. SET old_vals = JSON_SET(old_vals, '$.no_scan', OLD.no_scan);
  897. END IF;
  898. IF OLD.notes IS NOT NULL THEN
  899. SET deleted_fields_array = JSON_ARRAY_APPEND(deleted_fields_array, '$', 'notes');
  900. SET old_vals = JSON_SET(old_vals, '$.notes', OLD.notes);
  901. END IF;
  902. IF OLD.additional_fields IS NOT NULL THEN
  903. SET deleted_fields_array = JSON_ARRAY_APPEND(deleted_fields_array, '$', 'additional_fields');
  904. SET old_vals = JSON_SET(old_vals, '$.additional_fields', OLD.additional_fields);
  905. END IF;
  906. -- Always capture metadata fields for restore
  907. IF OLD.created_date IS NOT NULL THEN
  908. SET deleted_fields_array = JSON_ARRAY_APPEND(deleted_fields_array, '$', 'created_date');
  909. SET old_vals = JSON_SET(old_vals, '$.created_date', OLD.created_date);
  910. END IF;
  911. IF OLD.created_by IS NOT NULL THEN
  912. SET deleted_fields_array = JSON_ARRAY_APPEND(deleted_fields_array, '$', 'created_by');
  913. SET old_vals = JSON_SET(old_vals, '$.created_by', OLD.created_by);
  914. END IF;
  915. IF OLD.last_modified_date IS NOT NULL THEN
  916. SET deleted_fields_array = JSON_ARRAY_APPEND(deleted_fields_array, '$', 'last_modified_date');
  917. SET old_vals = JSON_SET(old_vals, '$.last_modified_date', OLD.last_modified_date);
  918. END IF;
  919. IF OLD.last_modified_by IS NOT NULL THEN
  920. SET deleted_fields_array = JSON_ARRAY_APPEND(deleted_fields_array, '$', 'last_modified_by');
  921. SET old_vals = JSON_SET(old_vals, '$.last_modified_by', OLD.last_modified_by);
  922. END IF;
  923. -- Log the DELETE with only non-NULL fields
  924. INSERT INTO asset_change_log (
  925. table_name, action, record_id, changed_fields, old_values,
  926. changed_by_id, changed_by_username
  927. )
  928. VALUES (
  929. 'assets',
  930. 'DELETE',
  931. OLD.id,
  932. deleted_fields_array,
  933. old_vals,
  934. @current_user_id,
  935. username
  936. );
  937. END */;;
  938. DELIMITER ;
  939. /*!50003 SET sql_mode = @saved_sql_mode */ ;
  940. /*!50003 SET character_set_client = @saved_cs_client */ ;
  941. /*!50003 SET character_set_results = @saved_cs_results */ ;
  942. /*!50003 SET collation_connection = @saved_col_connection */ ;
  943. /*!40101 SET @saved_cs_client = @@character_set_client */;
  944. /*!50503 SET character_set_client = utf8mb4 */;
  945. CREATE TABLE `audit_tasks` (
  946. `id` int(11) NOT NULL AUTO_INCREMENT,
  947. `task_name` varchar(200) NOT NULL,
  948. `json_sequence` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL CHECK (json_valid(`json_sequence`)),
  949. `created_at` timestamp NULL DEFAULT current_timestamp(),
  950. `updated_at` timestamp NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
  951. PRIMARY KEY (`id`)
  952. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
  953. /*!40101 SET character_set_client = @saved_cs_client */;
  954. /*!40101 SET @saved_cs_client = @@character_set_client */;
  955. /*!50503 SET character_set_client = utf8mb4 */;
  956. CREATE TABLE `borrowers` (
  957. `id` int(11) NOT NULL AUTO_INCREMENT,
  958. `name` varchar(200) NOT NULL,
  959. `email` varchar(255) DEFAULT NULL,
  960. `phone_number` varchar(50) DEFAULT NULL,
  961. `class_name` varchar(100) DEFAULT NULL,
  962. `role` varchar(100) DEFAULT NULL,
  963. `notes` text DEFAULT NULL,
  964. `added_by` int(11) NOT NULL,
  965. `added_date` timestamp NULL DEFAULT current_timestamp(),
  966. `banned` tinyint(1) DEFAULT 0,
  967. `unban_fine` decimal(10,2) DEFAULT 0.00,
  968. `last_unban_by` int(11) DEFAULT NULL,
  969. `last_unban_date` date DEFAULT NULL,
  970. PRIMARY KEY (`id`),
  971. KEY `added_by` (`added_by`),
  972. KEY `last_unban_by` (`last_unban_by`),
  973. KEY `idx_name` (`name`),
  974. KEY `idx_banned` (`banned`),
  975. CONSTRAINT `borrowers_ibfk_1` FOREIGN KEY (`added_by`) REFERENCES `users` (`id`),
  976. CONSTRAINT `borrowers_ibfk_2` FOREIGN KEY (`last_unban_by`) REFERENCES `users` (`id`) ON DELETE SET NULL
  977. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
  978. /*!40101 SET character_set_client = @saved_cs_client */;
  979. /*!50003 SET @saved_cs_client = @@character_set_client */ ;
  980. /*!50003 SET @saved_cs_results = @@character_set_results */ ;
  981. /*!50003 SET @saved_col_connection = @@collation_connection */ ;
  982. /*!50003 SET character_set_client = utf8mb4 */ ;
  983. /*!50003 SET character_set_results = utf8mb4 */ ;
  984. /*!50003 SET collation_connection = utf8mb4_0900_ai_ci */ ;
  985. /*!50003 SET @saved_sql_mode = @@sql_mode */ ;
  986. /*!50003 SET sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
  987. DELIMITER ;;
  988. /*!50003 CREATE*/ /*!50017 DEFINER=`root`@`%`*/ /*!50003 TRIGGER borrowers_before_insert_meta
  989. BEFORE INSERT ON borrowers
  990. FOR EACH ROW
  991. BEGIN
  992. IF NEW.added_by IS NULL AND @current_user_id IS NOT NULL THEN
  993. SET NEW.added_by = @current_user_id;
  994. END IF;
  995. END */;;
  996. DELIMITER ;
  997. /*!50003 SET sql_mode = @saved_sql_mode */ ;
  998. /*!50003 SET character_set_client = @saved_cs_client */ ;
  999. /*!50003 SET character_set_results = @saved_cs_results */ ;
  1000. /*!50003 SET collation_connection = @saved_col_connection */ ;
  1001. /*!50003 SET @saved_cs_client = @@character_set_client */ ;
  1002. /*!50003 SET @saved_cs_results = @@character_set_results */ ;
  1003. /*!50003 SET @saved_col_connection = @@collation_connection */ ;
  1004. /*!50003 SET character_set_client = utf8mb4 */ ;
  1005. /*!50003 SET character_set_results = utf8mb4 */ ;
  1006. /*!50003 SET collation_connection = utf8mb4_0900_ai_ci */ ;
  1007. /*!50003 SET @saved_sql_mode = @@sql_mode */ ;
  1008. /*!50003 SET sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
  1009. DELIMITER ;;
  1010. /*!50003 CREATE*/ /*!50017 DEFINER=`root`@`%`*/ /*!50003 TRIGGER borrowers_before_update_meta
  1011. BEFORE UPDATE ON borrowers
  1012. FOR EACH ROW
  1013. BEGIN
  1014. IF OLD.banned = TRUE AND NEW.banned = FALSE THEN
  1015. IF NEW.last_unban_by IS NULL AND @current_user_id IS NOT NULL THEN
  1016. SET NEW.last_unban_by = @current_user_id;
  1017. END IF;
  1018. IF NEW.last_unban_date IS NULL THEN
  1019. SET NEW.last_unban_date = CURDATE();
  1020. END IF;
  1021. END IF;
  1022. END */;;
  1023. DELIMITER ;
  1024. /*!50003 SET sql_mode = @saved_sql_mode */ ;
  1025. /*!50003 SET character_set_client = @saved_cs_client */ ;
  1026. /*!50003 SET character_set_results = @saved_cs_results */ ;
  1027. /*!50003 SET collation_connection = @saved_col_connection */ ;
  1028. /*!50003 SET @saved_cs_client = @@character_set_client */ ;
  1029. /*!50003 SET @saved_cs_results = @@character_set_results */ ;
  1030. /*!50003 SET @saved_col_connection = @@collation_connection */ ;
  1031. /*!50003 SET character_set_client = utf8mb4 */ ;
  1032. /*!50003 SET character_set_results = utf8mb4 */ ;
  1033. /*!50003 SET collation_connection = utf8mb4_0900_ai_ci */ ;
  1034. /*!50003 SET @saved_sql_mode = @@sql_mode */ ;
  1035. /*!50003 SET sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
  1036. DELIMITER ;;
  1037. /*!50003 CREATE*/ /*!50017 DEFINER=`root`@`%`*/ /*!50003 TRIGGER auto_detect_borrower_issues
  1038. AFTER UPDATE ON borrowers
  1039. FOR EACH ROW
  1040. BEGIN
  1041. DECLARE issue_title VARCHAR(255);
  1042. DECLARE issue_description TEXT;
  1043. -- Auto-detect when borrower gets banned
  1044. IF OLD.banned = FALSE AND NEW.banned = TRUE THEN
  1045. SET issue_title = CONCAT('Borrower Banned: ', NEW.name);
  1046. SET issue_description = CONCAT('Borrower has been banned. Name: ', NEW.name, CASE WHEN NEW.unban_fine > 0 THEN CONCAT(', Unban Fine: $', NEW.unban_fine) ELSE '' END);
  1047. INSERT INTO issue_tracker (
  1048. issue_type, borrower_id, title, description, severity, priority, status,
  1049. reported_by, auto_detected, detection_trigger, created_date
  1050. )
  1051. VALUES (
  1052. 'Borrower Issue', NEW.id, issue_title, issue_description, 'High', 'Normal', 'Open',
  1053. COALESCE(@current_user_id, 1), TRUE, 'BORROWER_BANNED', NOW()
  1054. );
  1055. END IF;
  1056. -- Auto-resolve when borrower gets unbanned
  1057. IF OLD.banned = TRUE AND NEW.banned = FALSE THEN
  1058. UPDATE issue_tracker
  1059. SET status = 'Resolved',
  1060. solution = 'Items Returned',
  1061. solution_plus = CONCAT('Borrower unbanned on ', COALESCE(NEW.last_unban_date, CURDATE()), CASE WHEN NEW.last_unban_by IS NOT NULL THEN CONCAT(' by user ID ', NEW.last_unban_by) ELSE '' END),
  1062. resolved_date = NOW(),
  1063. resolved_by = COALESCE(@current_user_id, NEW.last_unban_by, 1)
  1064. WHERE borrower_id = NEW.id
  1065. AND status IN ('Open', 'In Progress')
  1066. AND auto_detected = TRUE
  1067. AND detection_trigger = 'BORROWER_BANNED';
  1068. END IF;
  1069. END */;;
  1070. DELIMITER ;
  1071. /*!50003 SET sql_mode = @saved_sql_mode */ ;
  1072. /*!50003 SET character_set_client = @saved_cs_client */ ;
  1073. /*!50003 SET character_set_results = @saved_cs_results */ ;
  1074. /*!50003 SET collation_connection = @saved_col_connection */ ;
  1075. /*!40101 SET @saved_cs_client = @@character_set_client */;
  1076. /*!50503 SET character_set_client = utf8mb4 */;
  1077. CREATE TABLE `categories` (
  1078. `id` int(11) NOT NULL AUTO_INCREMENT,
  1079. `category_name` varchar(200) NOT NULL,
  1080. `category_description` text DEFAULT NULL,
  1081. `parent_id` int(11) DEFAULT NULL,
  1082. `category_code` varchar(50) DEFAULT NULL,
  1083. PRIMARY KEY (`id`),
  1084. KEY `idx_parent` (`parent_id`),
  1085. KEY `idx_code` (`category_code`),
  1086. CONSTRAINT `categories_ibfk_1` FOREIGN KEY (`parent_id`) REFERENCES `categories` (`id`)
  1087. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
  1088. /*!40101 SET character_set_client = @saved_cs_client */;
  1089. /*!40101 SET @saved_cs_client = @@character_set_client */;
  1090. /*!50503 SET character_set_client = utf8mb4 */;
  1091. CREATE TABLE `issue_tracker` (
  1092. `id` int(11) NOT NULL AUTO_INCREMENT,
  1093. `issue_type` enum('Asset Issue','Borrower Issue','System Issue','Maintenance','Other') NOT NULL,
  1094. `asset_id` int(11) DEFAULT NULL,
  1095. `borrower_id` int(11) DEFAULT NULL,
  1096. `title` varchar(255) NOT NULL,
  1097. `description` text NOT NULL,
  1098. `severity` enum('Critical','High','Medium','Low') DEFAULT NULL,
  1099. `priority` enum('Urgent','High','Normal','Low') DEFAULT 'Normal',
  1100. `status` enum('Open','In Progress','Resolved','Closed','On Hold') DEFAULT 'Open',
  1101. `solution` enum('Fixed','Replaced','Clarify','No Action Needed','Deferred','Items Returned','Automatically Fixed') DEFAULT NULL,
  1102. `solution_plus` text DEFAULT NULL,
  1103. `replacement_asset_id` int(11) DEFAULT NULL,
  1104. `reported_by` int(11) NOT NULL,
  1105. `assigned_to` int(11) DEFAULT NULL,
  1106. `resolved_by` int(11) DEFAULT NULL,
  1107. `cost` decimal(10,2) DEFAULT NULL,
  1108. `created_date` datetime NOT NULL DEFAULT current_timestamp(),
  1109. `updated_date` datetime DEFAULT current_timestamp() ON UPDATE current_timestamp(),
  1110. `resolved_date` datetime DEFAULT NULL,
  1111. `notes` text DEFAULT NULL,
  1112. `auto_detected` tinyint(1) DEFAULT 0,
  1113. `detection_trigger` varchar(100) DEFAULT NULL,
  1114. PRIMARY KEY (`id`),
  1115. KEY `replacement_asset_id` (`replacement_asset_id`),
  1116. KEY `reported_by` (`reported_by`),
  1117. KEY `assigned_to` (`assigned_to`),
  1118. KEY `resolved_by` (`resolved_by`),
  1119. KEY `idx_issue_type` (`issue_type`),
  1120. KEY `idx_asset` (`asset_id`),
  1121. KEY `idx_borrower` (`borrower_id`),
  1122. KEY `idx_severity` (`severity`),
  1123. KEY `idx_status` (`status`),
  1124. KEY `idx_created_date` (`created_date`),
  1125. KEY `idx_auto_detected` (`auto_detected`),
  1126. CONSTRAINT `issue_tracker_ibfk_1` FOREIGN KEY (`asset_id`) REFERENCES `assets` (`id`) ON DELETE CASCADE,
  1127. CONSTRAINT `issue_tracker_ibfk_2` FOREIGN KEY (`borrower_id`) REFERENCES `borrowers` (`id`) ON DELETE CASCADE,
  1128. CONSTRAINT `issue_tracker_ibfk_3` FOREIGN KEY (`replacement_asset_id`) REFERENCES `assets` (`id`) ON DELETE SET NULL,
  1129. CONSTRAINT `issue_tracker_ibfk_4` FOREIGN KEY (`reported_by`) REFERENCES `users` (`id`),
  1130. CONSTRAINT `issue_tracker_ibfk_5` FOREIGN KEY (`assigned_to`) REFERENCES `users` (`id`) ON DELETE SET NULL,
  1131. CONSTRAINT `issue_tracker_ibfk_6` FOREIGN KEY (`resolved_by`) REFERENCES `users` (`id`) ON DELETE SET NULL
  1132. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
  1133. /*!40101 SET character_set_client = @saved_cs_client */;
  1134. /*!50003 SET @saved_cs_client = @@character_set_client */ ;
  1135. /*!50003 SET @saved_cs_results = @@character_set_results */ ;
  1136. /*!50003 SET @saved_col_connection = @@collation_connection */ ;
  1137. /*!50003 SET character_set_client = utf8mb4 */ ;
  1138. /*!50003 SET character_set_results = utf8mb4 */ ;
  1139. /*!50003 SET collation_connection = utf8mb4_0900_ai_ci */ ;
  1140. /*!50003 SET @saved_sql_mode = @@sql_mode */ ;
  1141. /*!50003 SET sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
  1142. DELIMITER ;;
  1143. /*!50003 CREATE*/ /*!50017 DEFINER=`root`@`%`*/ /*!50003 TRIGGER issue_tracker_before_insert_meta
  1144. BEFORE INSERT ON issue_tracker
  1145. FOR EACH ROW
  1146. BEGIN
  1147. IF NEW.reported_by IS NULL AND @current_user_id IS NOT NULL THEN
  1148. SET NEW.reported_by = @current_user_id;
  1149. END IF;
  1150. END */;;
  1151. DELIMITER ;
  1152. /*!50003 SET sql_mode = @saved_sql_mode */ ;
  1153. /*!50003 SET character_set_client = @saved_cs_client */ ;
  1154. /*!50003 SET character_set_results = @saved_cs_results */ ;
  1155. /*!50003 SET collation_connection = @saved_col_connection */ ;
  1156. /*!50003 SET @saved_cs_client = @@character_set_client */ ;
  1157. /*!50003 SET @saved_cs_results = @@character_set_results */ ;
  1158. /*!50003 SET @saved_col_connection = @@collation_connection */ ;
  1159. /*!50003 SET character_set_client = utf8mb4 */ ;
  1160. /*!50003 SET character_set_results = utf8mb4 */ ;
  1161. /*!50003 SET collation_connection = utf8mb4_0900_ai_ci */ ;
  1162. /*!50003 SET @saved_sql_mode = @@sql_mode */ ;
  1163. /*!50003 SET sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
  1164. DELIMITER ;;
  1165. /*!50003 CREATE*/ /*!50017 DEFINER=`root`@`%`*/ /*!50003 TRIGGER validate_issue_tracker_insert
  1166. BEFORE INSERT ON issue_tracker
  1167. FOR EACH ROW
  1168. BEGIN
  1169. -- Clarify solution requires solution_plus
  1170. IF NEW.solution = 'Clarify' AND (NEW.solution_plus IS NULL OR NEW.solution_plus = '') THEN
  1171. SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'solution_plus is required when solution is set to Clarify';
  1172. END IF;
  1173. -- Replacement solution requires replacement_asset_id
  1174. IF NEW.solution = 'Replaced' AND NEW.replacement_asset_id IS NULL THEN
  1175. SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'replacement_asset_id is required when solution is set to Replaced';
  1176. END IF;
  1177. -- Asset Issue requires asset_id
  1178. IF NEW.issue_type = 'Asset Issue' AND NEW.asset_id IS NULL THEN
  1179. SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'asset_id is required for Asset Issue type';
  1180. END IF;
  1181. -- Borrower Issue requires borrower_id
  1182. IF NEW.issue_type = 'Borrower Issue' AND NEW.borrower_id IS NULL THEN
  1183. SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'borrower_id is required for Borrower Issue type';
  1184. END IF;
  1185. -- Auto-set resolved_date when status becomes Resolved or Closed
  1186. IF NEW.status IN ('Resolved', 'Closed') AND NEW.resolved_date IS NULL THEN
  1187. SET NEW.resolved_date = NOW();
  1188. END IF;
  1189. -- Auto-set resolved_by when status becomes Resolved or Closed
  1190. IF NEW.status IN ('Resolved', 'Closed') AND NEW.resolved_by IS NULL AND @current_user_id IS NOT NULL THEN
  1191. SET NEW.resolved_by = @current_user_id;
  1192. END IF;
  1193. END */;;
  1194. DELIMITER ;
  1195. /*!50003 SET sql_mode = @saved_sql_mode */ ;
  1196. /*!50003 SET character_set_client = @saved_cs_client */ ;
  1197. /*!50003 SET character_set_results = @saved_cs_results */ ;
  1198. /*!50003 SET collation_connection = @saved_col_connection */ ;
  1199. /*!50003 SET @saved_cs_client = @@character_set_client */ ;
  1200. /*!50003 SET @saved_cs_results = @@character_set_results */ ;
  1201. /*!50003 SET @saved_col_connection = @@collation_connection */ ;
  1202. /*!50003 SET character_set_client = utf8mb4 */ ;
  1203. /*!50003 SET character_set_results = utf8mb4 */ ;
  1204. /*!50003 SET collation_connection = utf8mb4_0900_ai_ci */ ;
  1205. /*!50003 SET @saved_sql_mode = @@sql_mode */ ;
  1206. /*!50003 SET sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
  1207. DELIMITER ;;
  1208. /*!50003 CREATE*/ /*!50017 DEFINER=`root`@`%`*/ /*!50003 TRIGGER issue_tracker_after_insert_log
  1209. AFTER INSERT ON issue_tracker
  1210. FOR EACH ROW
  1211. BEGIN
  1212. DECLARE set_fields JSON DEFAULT JSON_ARRAY();
  1213. DECLARE new_vals JSON DEFAULT JSON_OBJECT();
  1214. -- Build JSON of non-NULL inserted fields
  1215. IF NEW.issue_type IS NOT NULL THEN
  1216. SET set_fields = JSON_ARRAY_APPEND(set_fields, '$', 'issue_type');
  1217. SET new_vals = JSON_SET(new_vals, '$.issue_type', NEW.issue_type);
  1218. END IF;
  1219. IF NEW.asset_id IS NOT NULL THEN
  1220. SET set_fields = JSON_ARRAY_APPEND(set_fields, '$', 'asset_id');
  1221. SET new_vals = JSON_SET(new_vals, '$.asset_id', NEW.asset_id);
  1222. END IF;
  1223. IF NEW.borrower_id IS NOT NULL THEN
  1224. SET set_fields = JSON_ARRAY_APPEND(set_fields, '$', 'borrower_id');
  1225. SET new_vals = JSON_SET(new_vals, '$.borrower_id', NEW.borrower_id);
  1226. END IF;
  1227. IF NEW.title IS NOT NULL THEN
  1228. SET set_fields = JSON_ARRAY_APPEND(set_fields, '$', 'title');
  1229. SET new_vals = JSON_SET(new_vals, '$.title', NEW.title);
  1230. END IF;
  1231. IF NEW.severity IS NOT NULL THEN
  1232. SET set_fields = JSON_ARRAY_APPEND(set_fields, '$', 'severity');
  1233. SET new_vals = JSON_SET(new_vals, '$.severity', NEW.severity);
  1234. END IF;
  1235. IF NEW.status IS NOT NULL THEN
  1236. SET set_fields = JSON_ARRAY_APPEND(set_fields, '$', 'status');
  1237. SET new_vals = JSON_SET(new_vals, '$.status', NEW.status);
  1238. END IF;
  1239. INSERT INTO issue_tracker_change_log (issue_id, change_type, changed_fields, new_values, changed_by)
  1240. VALUES (NEW.id, 'INSERT', set_fields, new_vals, COALESCE(@current_user_id, NEW.reported_by));
  1241. END */;;
  1242. DELIMITER ;
  1243. /*!50003 SET sql_mode = @saved_sql_mode */ ;
  1244. /*!50003 SET character_set_client = @saved_cs_client */ ;
  1245. /*!50003 SET character_set_results = @saved_cs_results */ ;
  1246. /*!50003 SET collation_connection = @saved_col_connection */ ;
  1247. /*!50003 SET @saved_cs_client = @@character_set_client */ ;
  1248. /*!50003 SET @saved_cs_results = @@character_set_results */ ;
  1249. /*!50003 SET @saved_col_connection = @@collation_connection */ ;
  1250. /*!50003 SET character_set_client = utf8mb4 */ ;
  1251. /*!50003 SET character_set_results = utf8mb4 */ ;
  1252. /*!50003 SET collation_connection = utf8mb4_0900_ai_ci */ ;
  1253. /*!50003 SET @saved_sql_mode = @@sql_mode */ ;
  1254. /*!50003 SET sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
  1255. DELIMITER ;;
  1256. /*!50003 CREATE*/ /*!50017 DEFINER=`root`@`%`*/ /*!50003 TRIGGER validate_issue_tracker_update
  1257. BEFORE UPDATE ON issue_tracker
  1258. FOR EACH ROW
  1259. BEGIN
  1260. -- Clarify solution requires solution_plus
  1261. IF NEW.solution = 'Clarify' AND (NEW.solution_plus IS NULL OR NEW.solution_plus = '') THEN
  1262. SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'solution_plus is required when solution is set to Clarify';
  1263. END IF;
  1264. -- Replacement solution requires replacement_asset_id
  1265. IF NEW.solution = 'Replaced' AND NEW.replacement_asset_id IS NULL THEN
  1266. SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'replacement_asset_id is required when solution is set to Replaced';
  1267. END IF;
  1268. -- Auto-set resolved_date when status changes to Resolved or Closed
  1269. IF OLD.status NOT IN ('Resolved', 'Closed') AND NEW.status IN ('Resolved', 'Closed') THEN
  1270. SET NEW.resolved_date = NOW();
  1271. IF @current_user_id IS NOT NULL THEN
  1272. SET NEW.resolved_by = @current_user_id;
  1273. END IF;
  1274. END IF;
  1275. -- Clear resolved_date when status changes away from Resolved/Closed
  1276. IF OLD.status IN ('Resolved', 'Closed') AND NEW.status NOT IN ('Resolved', 'Closed') THEN
  1277. SET NEW.resolved_date = NULL;
  1278. SET NEW.resolved_by = NULL;
  1279. END IF;
  1280. END */;;
  1281. DELIMITER ;
  1282. /*!50003 SET sql_mode = @saved_sql_mode */ ;
  1283. /*!50003 SET character_set_client = @saved_cs_client */ ;
  1284. /*!50003 SET character_set_results = @saved_cs_results */ ;
  1285. /*!50003 SET collation_connection = @saved_col_connection */ ;
  1286. /*!50003 SET @saved_cs_client = @@character_set_client */ ;
  1287. /*!50003 SET @saved_cs_results = @@character_set_results */ ;
  1288. /*!50003 SET @saved_col_connection = @@collation_connection */ ;
  1289. /*!50003 SET character_set_client = utf8mb4 */ ;
  1290. /*!50003 SET character_set_results = utf8mb4 */ ;
  1291. /*!50003 SET collation_connection = utf8mb4_0900_ai_ci */ ;
  1292. /*!50003 SET @saved_sql_mode = @@sql_mode */ ;
  1293. /*!50003 SET sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
  1294. DELIMITER ;;
  1295. /*!50003 CREATE*/ /*!50017 DEFINER=`root`@`%`*/ /*!50003 TRIGGER issue_tracker_after_update_log
  1296. AFTER UPDATE ON issue_tracker
  1297. FOR EACH ROW
  1298. BEGIN
  1299. DECLARE changed_fields JSON DEFAULT JSON_ARRAY();
  1300. DECLARE old_vals JSON DEFAULT JSON_OBJECT();
  1301. DECLARE new_vals JSON DEFAULT JSON_OBJECT();
  1302. -- Track all changed fields
  1303. IF OLD.status <=> NEW.status IS FALSE THEN
  1304. SET changed_fields = JSON_ARRAY_APPEND(changed_fields, '$', 'status');
  1305. SET old_vals = JSON_SET(old_vals, '$.status', OLD.status);
  1306. SET new_vals = JSON_SET(new_vals, '$.status', NEW.status);
  1307. END IF;
  1308. IF OLD.severity <=> NEW.severity IS FALSE THEN
  1309. SET changed_fields = JSON_ARRAY_APPEND(changed_fields, '$', 'severity');
  1310. SET old_vals = JSON_SET(old_vals, '$.severity', OLD.severity);
  1311. SET new_vals = JSON_SET(new_vals, '$.severity', NEW.severity);
  1312. END IF;
  1313. IF OLD.priority <=> NEW.priority IS FALSE THEN
  1314. SET changed_fields = JSON_ARRAY_APPEND(changed_fields, '$', 'priority');
  1315. SET old_vals = JSON_SET(old_vals, '$.priority', OLD.priority);
  1316. SET new_vals = JSON_SET(new_vals, '$.priority', NEW.priority);
  1317. END IF;
  1318. IF OLD.solution <=> NEW.solution IS FALSE THEN
  1319. SET changed_fields = JSON_ARRAY_APPEND(changed_fields, '$', 'solution');
  1320. SET old_vals = JSON_SET(old_vals, '$.solution', OLD.solution);
  1321. SET new_vals = JSON_SET(new_vals, '$.solution', NEW.solution);
  1322. END IF;
  1323. IF OLD.assigned_to <=> NEW.assigned_to IS FALSE THEN
  1324. SET changed_fields = JSON_ARRAY_APPEND(changed_fields, '$', 'assigned_to');
  1325. SET old_vals = JSON_SET(old_vals, '$.assigned_to', OLD.assigned_to);
  1326. SET new_vals = JSON_SET(new_vals, '$.assigned_to', NEW.assigned_to);
  1327. END IF;
  1328. IF OLD.resolved_by <=> NEW.resolved_by IS FALSE THEN
  1329. SET changed_fields = JSON_ARRAY_APPEND(changed_fields, '$', 'resolved_by');
  1330. SET old_vals = JSON_SET(old_vals, '$.resolved_by', OLD.resolved_by);
  1331. SET new_vals = JSON_SET(new_vals, '$.resolved_by', NEW.resolved_by);
  1332. END IF;
  1333. -- Only log if something actually changed
  1334. IF JSON_LENGTH(changed_fields) > 0 THEN
  1335. INSERT INTO issue_tracker_change_log (issue_id, change_type, changed_fields, old_values, new_values, changed_by)
  1336. VALUES (NEW.id, 'UPDATE', changed_fields, old_vals, new_vals, COALESCE(@current_user_id, OLD.reported_by));
  1337. END IF;
  1338. END */;;
  1339. DELIMITER ;
  1340. /*!50003 SET sql_mode = @saved_sql_mode */ ;
  1341. /*!50003 SET character_set_client = @saved_cs_client */ ;
  1342. /*!50003 SET character_set_results = @saved_cs_results */ ;
  1343. /*!50003 SET collation_connection = @saved_col_connection */ ;
  1344. /*!50003 SET @saved_cs_client = @@character_set_client */ ;
  1345. /*!50003 SET @saved_cs_results = @@character_set_results */ ;
  1346. /*!50003 SET @saved_col_connection = @@collation_connection */ ;
  1347. /*!50003 SET character_set_client = utf8mb4 */ ;
  1348. /*!50003 SET character_set_results = utf8mb4 */ ;
  1349. /*!50003 SET collation_connection = utf8mb4_0900_ai_ci */ ;
  1350. /*!50003 SET @saved_sql_mode = @@sql_mode */ ;
  1351. /*!50003 SET sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
  1352. DELIMITER ;;
  1353. /*!50003 CREATE*/ /*!50017 DEFINER=`root`@`%`*/ /*!50003 TRIGGER issue_tracker_before_delete
  1354. BEFORE DELETE ON issue_tracker
  1355. FOR EACH ROW
  1356. BEGIN
  1357. -- If issue is not already resolved/closed, update it before deletion
  1358. IF OLD.status NOT IN ('Resolved', 'Closed') THEN
  1359. -- Can't UPDATE in a BEFORE DELETE trigger, so we just ensure it was marked resolved
  1360. -- This will prevent accidental deletion of open issues
  1361. SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Cannot delete open issues. Please close or resolve the issue first.';
  1362. END IF;
  1363. END */;;
  1364. DELIMITER ;
  1365. /*!50003 SET sql_mode = @saved_sql_mode */ ;
  1366. /*!50003 SET character_set_client = @saved_cs_client */ ;
  1367. /*!50003 SET character_set_results = @saved_cs_results */ ;
  1368. /*!50003 SET collation_connection = @saved_col_connection */ ;
  1369. /*!50003 SET @saved_cs_client = @@character_set_client */ ;
  1370. /*!50003 SET @saved_cs_results = @@character_set_results */ ;
  1371. /*!50003 SET @saved_col_connection = @@collation_connection */ ;
  1372. /*!50003 SET character_set_client = utf8mb4 */ ;
  1373. /*!50003 SET character_set_results = utf8mb4 */ ;
  1374. /*!50003 SET collation_connection = utf8mb4_0900_ai_ci */ ;
  1375. /*!50003 SET @saved_sql_mode = @@sql_mode */ ;
  1376. /*!50003 SET sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
  1377. DELIMITER ;;
  1378. /*!50003 CREATE*/ /*!50017 DEFINER=`root`@`%`*/ /*!50003 TRIGGER issue_tracker_after_delete_log
  1379. AFTER DELETE ON issue_tracker
  1380. FOR EACH ROW
  1381. BEGIN
  1382. DECLARE deleted_fields JSON DEFAULT JSON_ARRAY();
  1383. DECLARE old_vals JSON DEFAULT JSON_OBJECT();
  1384. -- Log all fields from deleted issue
  1385. IF OLD.issue_type IS NOT NULL THEN
  1386. SET deleted_fields = JSON_ARRAY_APPEND(deleted_fields, '$', 'issue_type');
  1387. SET old_vals = JSON_SET(old_vals, '$.issue_type', OLD.issue_type);
  1388. END IF;
  1389. IF OLD.asset_id IS NOT NULL THEN
  1390. SET deleted_fields = JSON_ARRAY_APPEND(deleted_fields, '$', 'asset_id');
  1391. SET old_vals = JSON_SET(old_vals, '$.asset_id', OLD.asset_id);
  1392. END IF;
  1393. IF OLD.title IS NOT NULL THEN
  1394. SET deleted_fields = JSON_ARRAY_APPEND(deleted_fields, '$', 'title');
  1395. SET old_vals = JSON_SET(old_vals, '$.title', OLD.title);
  1396. END IF;
  1397. IF OLD.status IS NOT NULL THEN
  1398. SET deleted_fields = JSON_ARRAY_APPEND(deleted_fields, '$', 'status');
  1399. SET old_vals = JSON_SET(old_vals, '$.status', OLD.status);
  1400. END IF;
  1401. IF OLD.solution IS NOT NULL THEN
  1402. SET deleted_fields = JSON_ARRAY_APPEND(deleted_fields, '$', 'solution');
  1403. SET old_vals = JSON_SET(old_vals, '$.solution', OLD.solution);
  1404. END IF;
  1405. INSERT INTO issue_tracker_change_log (issue_id, change_type, changed_fields, old_values, changed_by)
  1406. VALUES (OLD.id, 'DELETE', deleted_fields, old_vals, COALESCE(@current_user_id, OLD.reported_by));
  1407. END */;;
  1408. DELIMITER ;
  1409. /*!50003 SET sql_mode = @saved_sql_mode */ ;
  1410. /*!50003 SET character_set_client = @saved_cs_client */ ;
  1411. /*!50003 SET character_set_results = @saved_cs_results */ ;
  1412. /*!50003 SET collation_connection = @saved_col_connection */ ;
  1413. /*!40101 SET @saved_cs_client = @@character_set_client */;
  1414. /*!50503 SET character_set_client = utf8mb4 */;
  1415. CREATE TABLE `issue_tracker_change_log` (
  1416. `id` int(11) NOT NULL AUTO_INCREMENT,
  1417. `issue_id` int(11) NOT NULL,
  1418. `change_type` enum('INSERT','UPDATE','DELETE') NOT NULL,
  1419. `changed_fields` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`changed_fields`)),
  1420. `old_values` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`old_values`)),
  1421. `new_values` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`new_values`)),
  1422. `changed_by` int(11) DEFAULT NULL,
  1423. `change_date` timestamp NULL DEFAULT current_timestamp(),
  1424. PRIMARY KEY (`id`),
  1425. KEY `changed_by` (`changed_by`),
  1426. KEY `idx_issue` (`issue_id`),
  1427. KEY `idx_change_type` (`change_type`),
  1428. KEY `idx_change_date` (`change_date`),
  1429. CONSTRAINT `issue_tracker_change_log_ibfk_1` FOREIGN KEY (`issue_id`) REFERENCES `issue_tracker` (`id`) ON DELETE CASCADE,
  1430. CONSTRAINT `issue_tracker_change_log_ibfk_2` FOREIGN KEY (`changed_by`) REFERENCES `users` (`id`) ON DELETE SET NULL
  1431. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
  1432. /*!40101 SET character_set_client = @saved_cs_client */;
  1433. /*!40101 SET @saved_cs_client = @@character_set_client */;
  1434. /*!50503 SET character_set_client = utf8mb4 */;
  1435. CREATE TABLE `label_templates` (
  1436. `id` int(11) NOT NULL AUTO_INCREMENT,
  1437. `template_code` varchar(100) NOT NULL COMMENT 'Unique code like "CABLE"',
  1438. `template_name` varchar(200) NOT NULL COMMENT 'Human readable name',
  1439. `layout_json` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT 'Universal label design: SVG graphics, auto-populated field placeholders, styling' CHECK (json_valid(`layout_json`)),
  1440. `created_at` timestamp NULL DEFAULT current_timestamp(),
  1441. `created_by` int(11) DEFAULT NULL,
  1442. `last_modified_at` timestamp NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
  1443. `last_modified_by` int(11) DEFAULT NULL,
  1444. PRIMARY KEY (`id`),
  1445. UNIQUE KEY `template_code` (`template_code`),
  1446. KEY `created_by` (`created_by`),
  1447. KEY `last_modified_by` (`last_modified_by`),
  1448. KEY `idx_template_code` (`template_code`),
  1449. KEY `idx_template_name` (`template_name`),
  1450. CONSTRAINT `label_templates_ibfk_1` FOREIGN KEY (`created_by`) REFERENCES `users` (`id`) ON DELETE SET NULL,
  1451. CONSTRAINT `label_templates_ibfk_2` FOREIGN KEY (`last_modified_by`) REFERENCES `users` (`id`) ON DELETE SET NULL
  1452. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
  1453. /*!40101 SET character_set_client = @saved_cs_client */;
  1454. /*!40101 SET @saved_cs_client = @@character_set_client */;
  1455. /*!50503 SET character_set_client = utf8mb4 */;
  1456. CREATE TABLE `lending_history` (
  1457. `id` int(11) NOT NULL AUTO_INCREMENT,
  1458. `asset_id` int(11) NOT NULL,
  1459. `borrower_id` int(11) NOT NULL,
  1460. `checkout_date` datetime NOT NULL DEFAULT current_timestamp(),
  1461. `due_date` date DEFAULT NULL,
  1462. `return_date` datetime DEFAULT NULL,
  1463. `checked_out_by` int(11) DEFAULT NULL,
  1464. `checked_in_by` int(11) DEFAULT NULL,
  1465. `notes` text DEFAULT NULL,
  1466. PRIMARY KEY (`id`),
  1467. KEY `checked_out_by` (`checked_out_by`),
  1468. KEY `checked_in_by` (`checked_in_by`),
  1469. KEY `idx_asset` (`asset_id`),
  1470. KEY `idx_borrower` (`borrower_id`),
  1471. KEY `idx_checkout_date` (`checkout_date`),
  1472. KEY `idx_return_date` (`return_date`),
  1473. CONSTRAINT `lending_history_ibfk_1` FOREIGN KEY (`asset_id`) REFERENCES `assets` (`id`) ON DELETE CASCADE,
  1474. CONSTRAINT `lending_history_ibfk_2` FOREIGN KEY (`borrower_id`) REFERENCES `borrowers` (`id`),
  1475. CONSTRAINT `lending_history_ibfk_3` FOREIGN KEY (`checked_out_by`) REFERENCES `users` (`id`),
  1476. CONSTRAINT `lending_history_ibfk_4` FOREIGN KEY (`checked_in_by`) REFERENCES `users` (`id`) ON DELETE SET NULL
  1477. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
  1478. /*!40101 SET character_set_client = @saved_cs_client */;
  1479. /*!50003 SET @saved_cs_client = @@character_set_client */ ;
  1480. /*!50003 SET @saved_cs_results = @@character_set_results */ ;
  1481. /*!50003 SET @saved_col_connection = @@collation_connection */ ;
  1482. /*!50003 SET character_set_client = utf8mb4 */ ;
  1483. /*!50003 SET character_set_results = utf8mb4 */ ;
  1484. /*!50003 SET collation_connection = utf8mb4_0900_ai_ci */ ;
  1485. /*!50003 SET @saved_sql_mode = @@sql_mode */ ;
  1486. /*!50003 SET sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
  1487. DELIMITER ;;
  1488. /*!50003 CREATE*/ /*!50017 DEFINER=`root`@`%`*/ /*!50003 TRIGGER lending_history_before_insert_meta
  1489. BEFORE INSERT ON lending_history
  1490. FOR EACH ROW
  1491. BEGIN
  1492. IF NEW.checked_out_by IS NULL AND @current_user_id IS NOT NULL THEN
  1493. SET NEW.checked_out_by = @current_user_id;
  1494. END IF;
  1495. END */;;
  1496. DELIMITER ;
  1497. /*!50003 SET sql_mode = @saved_sql_mode */ ;
  1498. /*!50003 SET character_set_client = @saved_cs_client */ ;
  1499. /*!50003 SET character_set_results = @saved_cs_results */ ;
  1500. /*!50003 SET collation_connection = @saved_col_connection */ ;
  1501. /*!50003 SET @saved_cs_client = @@character_set_client */ ;
  1502. /*!50003 SET @saved_cs_results = @@character_set_results */ ;
  1503. /*!50003 SET @saved_col_connection = @@collation_connection */ ;
  1504. /*!50003 SET character_set_client = utf8mb4 */ ;
  1505. /*!50003 SET character_set_results = utf8mb4 */ ;
  1506. /*!50003 SET collation_connection = utf8mb4_0900_ai_ci */ ;
  1507. /*!50003 SET @saved_sql_mode = @@sql_mode */ ;
  1508. /*!50003 SET sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
  1509. DELIMITER ;;
  1510. /*!50003 CREATE*/ /*!50017 DEFINER=`root`@`%`*/ /*!50003 TRIGGER lending_history_before_update_meta
  1511. BEFORE UPDATE ON lending_history
  1512. FOR EACH ROW
  1513. BEGIN
  1514. IF OLD.return_date IS NULL AND NEW.return_date IS NOT NULL THEN
  1515. IF NEW.checked_in_by IS NULL AND @current_user_id IS NOT NULL THEN
  1516. SET NEW.checked_in_by = @current_user_id;
  1517. END IF;
  1518. END IF;
  1519. END */;;
  1520. DELIMITER ;
  1521. /*!50003 SET sql_mode = @saved_sql_mode */ ;
  1522. /*!50003 SET character_set_client = @saved_cs_client */ ;
  1523. /*!50003 SET character_set_results = @saved_cs_results */ ;
  1524. /*!50003 SET collation_connection = @saved_col_connection */ ;
  1525. /*!40101 SET @saved_cs_client = @@character_set_client */;
  1526. /*!50503 SET character_set_client = utf8mb4 */;
  1527. CREATE TABLE `physical_audit_logs` (
  1528. `id` int(11) NOT NULL AUTO_INCREMENT,
  1529. `physical_audit_id` int(11) NOT NULL COMMENT 'Reference to the audit session',
  1530. `asset_id` int(11) NOT NULL,
  1531. `audit_date` datetime NOT NULL DEFAULT current_timestamp(),
  1532. `audited_by` int(11) NOT NULL,
  1533. `status_found` enum('Good','Attention','Faulty','Missing','Retired','In Repair','In Transit','Expired','Unmanaged') DEFAULT 'Good',
  1534. `audit_task_id` int(11) DEFAULT NULL COMMENT 'Which audit task was run on this asset',
  1535. `audit_task_responses` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT 'User responses to the JSON sequence questions' CHECK (json_valid(`audit_task_responses`)),
  1536. `exception_type` enum('wrong-zone','unexpected-asset','damaged','missing-label','other') DEFAULT NULL,
  1537. `exception_details` text DEFAULT NULL COMMENT 'Details about the exception found',
  1538. `found_in_zone_id` int(11) DEFAULT NULL COMMENT 'Which zone the asset was actually found in (if different from expected)',
  1539. `auditor_action` enum('physical-move','virtual-update','no-action') DEFAULT NULL COMMENT 'What the auditor chose to do about wrong-zone assets',
  1540. `notes` text DEFAULT NULL,
  1541. PRIMARY KEY (`id`),
  1542. KEY `audit_task_id` (`audit_task_id`),
  1543. KEY `found_in_zone_id` (`found_in_zone_id`),
  1544. KEY `idx_physical_audit` (`physical_audit_id`),
  1545. KEY `idx_asset` (`asset_id`),
  1546. KEY `idx_audit_date` (`audit_date`),
  1547. KEY `idx_audited_by` (`audited_by`),
  1548. KEY `idx_status_found` (`status_found`),
  1549. KEY `idx_exception_type` (`exception_type`),
  1550. CONSTRAINT `physical_audit_logs_ibfk_1` FOREIGN KEY (`physical_audit_id`) REFERENCES `physical_audits` (`id`) ON DELETE CASCADE,
  1551. CONSTRAINT `physical_audit_logs_ibfk_2` FOREIGN KEY (`asset_id`) REFERENCES `assets` (`id`) ON DELETE CASCADE,
  1552. CONSTRAINT `physical_audit_logs_ibfk_3` FOREIGN KEY (`audited_by`) REFERENCES `users` (`id`),
  1553. CONSTRAINT `physical_audit_logs_ibfk_4` FOREIGN KEY (`audit_task_id`) REFERENCES `audit_tasks` (`id`) ON DELETE SET NULL,
  1554. CONSTRAINT `physical_audit_logs_ibfk_5` FOREIGN KEY (`found_in_zone_id`) REFERENCES `zones` (`id`) ON DELETE SET NULL
  1555. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
  1556. /*!40101 SET character_set_client = @saved_cs_client */;
  1557. /*!50003 SET @saved_cs_client = @@character_set_client */ ;
  1558. /*!50003 SET @saved_cs_results = @@character_set_results */ ;
  1559. /*!50003 SET @saved_col_connection = @@collation_connection */ ;
  1560. /*!50003 SET character_set_client = utf8mb4 */ ;
  1561. /*!50003 SET character_set_results = utf8mb4 */ ;
  1562. /*!50003 SET collation_connection = utf8mb4_0900_ai_ci */ ;
  1563. /*!50003 SET @saved_sql_mode = @@sql_mode */ ;
  1564. /*!50003 SET sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
  1565. DELIMITER ;;
  1566. /*!50003 CREATE*/ /*!50017 DEFINER=`root`@`%`*/ /*!50003 TRIGGER physical_audit_logs_before_insert_meta
  1567. BEFORE INSERT ON physical_audit_logs
  1568. FOR EACH ROW
  1569. BEGIN
  1570. -- Auto-populate audited_by from session variable if not provided
  1571. IF NEW.audited_by IS NULL OR NEW.audited_by = 0 THEN
  1572. SET NEW.audited_by = COALESCE(@current_user_id, 1);
  1573. END IF;
  1574. END */;;
  1575. DELIMITER ;
  1576. /*!50003 SET sql_mode = @saved_sql_mode */ ;
  1577. /*!50003 SET character_set_client = @saved_cs_client */ ;
  1578. /*!50003 SET character_set_results = @saved_cs_results */ ;
  1579. /*!50003 SET collation_connection = @saved_col_connection */ ;
  1580. /*!50003 SET @saved_cs_client = @@character_set_client */ ;
  1581. /*!50003 SET @saved_cs_results = @@character_set_results */ ;
  1582. /*!50003 SET @saved_col_connection = @@collation_connection */ ;
  1583. /*!50003 SET character_set_client = utf8mb4 */ ;
  1584. /*!50003 SET character_set_results = utf8mb4 */ ;
  1585. /*!50003 SET collation_connection = utf8mb4_0900_ai_ci */ ;
  1586. /*!50003 SET @saved_sql_mode = @@sql_mode */ ;
  1587. /*!50003 SET sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
  1588. DELIMITER ;;
  1589. /*!50003 CREATE*/ /*!50017 DEFINER=`root`@`%`*/ /*!50003 TRIGGER update_assets_found
  1590. AFTER INSERT ON physical_audit_logs
  1591. FOR EACH ROW
  1592. BEGIN
  1593. UPDATE physical_audits
  1594. SET assets_found = assets_found + 1
  1595. WHERE id = NEW.physical_audit_id;
  1596. END */;;
  1597. DELIMITER ;
  1598. /*!50003 SET sql_mode = @saved_sql_mode */ ;
  1599. /*!50003 SET character_set_client = @saved_cs_client */ ;
  1600. /*!50003 SET character_set_results = @saved_cs_results */ ;
  1601. /*!50003 SET collation_connection = @saved_col_connection */ ;
  1602. /*!50003 SET @saved_cs_client = @@character_set_client */ ;
  1603. /*!50003 SET @saved_cs_results = @@character_set_results */ ;
  1604. /*!50003 SET @saved_col_connection = @@collation_connection */ ;
  1605. /*!50003 SET character_set_client = utf8mb4 */ ;
  1606. /*!50003 SET character_set_results = utf8mb4 */ ;
  1607. /*!50003 SET collation_connection = utf8mb4_0900_ai_ci */ ;
  1608. /*!50003 SET @saved_sql_mode = @@sql_mode */ ;
  1609. /*!50003 SET sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
  1610. DELIMITER ;;
  1611. /*!50003 CREATE*/ /*!50017 DEFINER=`root`@`%`*/ /*!50003 TRIGGER update_asset_from_audit
  1612. AFTER INSERT ON physical_audit_logs
  1613. FOR EACH ROW
  1614. BEGIN
  1615. DECLARE current_status VARCHAR(100);
  1616. -- Update asset's last_audit date
  1617. UPDATE assets
  1618. SET last_audit = DATE(NEW.audit_date),
  1619. last_audit_status = NEW.status_found
  1620. WHERE id = NEW.asset_id;
  1621. -- Compare found status with current asset status
  1622. SELECT status INTO current_status FROM assets WHERE id = NEW.asset_id LIMIT 1;
  1623. IF NEW.status_found != current_status THEN
  1624. UPDATE assets
  1625. SET status = NEW.status_found
  1626. WHERE id = NEW.asset_id;
  1627. END IF;
  1628. END */;;
  1629. DELIMITER ;
  1630. /*!50003 SET sql_mode = @saved_sql_mode */ ;
  1631. /*!50003 SET character_set_client = @saved_cs_client */ ;
  1632. /*!50003 SET character_set_results = @saved_cs_results */ ;
  1633. /*!50003 SET collation_connection = @saved_col_connection */ ;
  1634. /*!40101 SET @saved_cs_client = @@character_set_client */;
  1635. /*!50503 SET character_set_client = utf8mb4 */;
  1636. CREATE TABLE `physical_audits` (
  1637. `id` int(11) NOT NULL AUTO_INCREMENT,
  1638. `audit_type` enum('full-zone','spot-check') NOT NULL,
  1639. `zone_id` int(11) DEFAULT NULL COMMENT 'Zone being audited (NULL for spot-check audits)',
  1640. `audit_name` varchar(255) DEFAULT NULL COMMENT 'Custom name for the audit session',
  1641. `started_by` int(11) NOT NULL,
  1642. `started_at` datetime NOT NULL DEFAULT current_timestamp(),
  1643. `completed_at` datetime DEFAULT NULL,
  1644. `status` enum('in-progress','all-good','timeout','attention','cancelled') DEFAULT 'in-progress',
  1645. `timeout_minutes` int(11) DEFAULT NULL COMMENT 'Timeout setting used for this audit',
  1646. `issues_found` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT 'Array of issues: missing_assets, moved_assets, damaged_assets, etc.' CHECK (json_valid(`issues_found`)),
  1647. `assets_expected` int(11) DEFAULT NULL COMMENT 'Total assets expected to be found in zone',
  1648. `assets_found` int(11) DEFAULT 0 COMMENT 'Total assets actually found and scanned',
  1649. `notes` text DEFAULT NULL,
  1650. `cancelled_reason` text DEFAULT NULL,
  1651. PRIMARY KEY (`id`),
  1652. KEY `idx_audit_type` (`audit_type`),
  1653. KEY `idx_zone` (`zone_id`),
  1654. KEY `idx_status` (`status`),
  1655. KEY `idx_started_at` (`started_at`),
  1656. KEY `idx_started_by` (`started_by`),
  1657. CONSTRAINT `physical_audits_ibfk_1` FOREIGN KEY (`zone_id`) REFERENCES `zones` (`id`),
  1658. CONSTRAINT `physical_audits_ibfk_2` FOREIGN KEY (`started_by`) REFERENCES `users` (`id`)
  1659. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
  1660. /*!40101 SET character_set_client = @saved_cs_client */;
  1661. /*!50003 SET @saved_cs_client = @@character_set_client */ ;
  1662. /*!50003 SET @saved_cs_results = @@character_set_results */ ;
  1663. /*!50003 SET @saved_col_connection = @@collation_connection */ ;
  1664. /*!50003 SET character_set_client = utf8mb4 */ ;
  1665. /*!50003 SET character_set_results = utf8mb4 */ ;
  1666. /*!50003 SET collation_connection = utf8mb4_0900_ai_ci */ ;
  1667. /*!50003 SET @saved_sql_mode = @@sql_mode */ ;
  1668. /*!50003 SET sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
  1669. DELIMITER ;;
  1670. /*!50003 CREATE*/ /*!50017 DEFINER=`root`@`%`*/ /*!50003 TRIGGER calculate_assets_expected
  1671. BEFORE INSERT ON physical_audits
  1672. FOR EACH ROW
  1673. BEGIN
  1674. DECLARE expected_count INT DEFAULT 0;
  1675. DECLARE v_timeout INT;
  1676. -- For full-zone audits, calculate expected assets in the zone
  1677. IF NEW.audit_type = 'full-zone' AND NEW.zone_id IS NOT NULL THEN
  1678. SELECT COUNT(*) INTO expected_count
  1679. FROM assets
  1680. WHERE zone_id = NEW.zone_id
  1681. AND status NOT IN ('Missing', 'Retired');
  1682. SET NEW.assets_expected = expected_count;
  1683. END IF;
  1684. -- Set timeout from zone settings if not specified
  1685. IF NEW.timeout_minutes IS NULL AND NEW.zone_id IS NOT NULL THEN
  1686. SELECT audit_timeout_minutes INTO v_timeout
  1687. FROM zones
  1688. WHERE id = NEW.zone_id
  1689. LIMIT 1;
  1690. IF v_timeout IS NOT NULL THEN
  1691. SET NEW.timeout_minutes = v_timeout;
  1692. END IF;
  1693. END IF;
  1694. END */;;
  1695. DELIMITER ;
  1696. /*!50003 SET sql_mode = @saved_sql_mode */ ;
  1697. /*!50003 SET character_set_client = @saved_cs_client */ ;
  1698. /*!50003 SET character_set_results = @saved_cs_results */ ;
  1699. /*!50003 SET collation_connection = @saved_col_connection */ ;
  1700. /*!50003 SET @saved_cs_client = @@character_set_client */ ;
  1701. /*!50003 SET @saved_cs_results = @@character_set_results */ ;
  1702. /*!50003 SET @saved_col_connection = @@collation_connection */ ;
  1703. /*!50003 SET character_set_client = utf8mb4 */ ;
  1704. /*!50003 SET character_set_results = utf8mb4 */ ;
  1705. /*!50003 SET collation_connection = utf8mb4_0900_ai_ci */ ;
  1706. /*!50003 SET @saved_sql_mode = @@sql_mode */ ;
  1707. /*!50003 SET sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' */ ;
  1708. DELIMITER ;;
  1709. /*!50003 CREATE*/ /*!50017 DEFINER=`root`@`%`*/ /*!50003 TRIGGER auto_detect_audit_issues
  1710. AFTER UPDATE ON physical_audits
  1711. FOR EACH ROW
  1712. BEGIN
  1713. DECLARE missing_count INT DEFAULT 0;
  1714. DECLARE zone_name VARCHAR(200);
  1715. -- Only process when audit status changes to completed states
  1716. IF OLD.status = 'in-progress' AND NEW.status IN ('all-good', 'attention', 'timeout') THEN
  1717. -- Get zone name for reporting
  1718. IF NEW.zone_id IS NOT NULL THEN
  1719. SELECT zone_name INTO zone_name FROM zones WHERE id = NEW.zone_id;
  1720. END IF;
  1721. -- For full-zone audits, check for missing assets
  1722. IF NEW.audit_type = 'full-zone' AND NEW.assets_expected IS NOT NULL THEN
  1723. SET missing_count = GREATEST(0, NEW.assets_expected - NEW.assets_found);
  1724. END IF;
  1725. -- Create issue for missing assets
  1726. IF missing_count > 0 THEN
  1727. INSERT INTO issue_tracker (
  1728. issue_type, title, description, severity, priority, status,
  1729. reported_by, auto_detected, detection_trigger, created_date, notes
  1730. )
  1731. VALUES (
  1732. 'System Issue',
  1733. CONCAT('Audit: Missing Assets in ', COALESCE(zone_name, 'Unknown Zone')),
  1734. CONCAT('Full zone audit completed with ', missing_count, ' missing assets. Expected: ', NEW.assets_expected, ', Found: ', NEW.assets_found, '. Audit ID: ', NEW.id),
  1735. CASE WHEN missing_count >= 5 THEN 'Critical' WHEN missing_count >= 2 THEN 'High' ELSE 'Medium' END,
  1736. 'High', 'Open',
  1737. NEW.started_by, TRUE, 'AUDIT_MISSING_ASSETS', NOW(),
  1738. CONCAT('Physical Audit ID: ', NEW.id, ' in zone: ', COALESCE(zone_name, NEW.zone_id))
  1739. );
  1740. END IF;
  1741. END IF;
  1742. END */;;
  1743. DELIMITER ;
  1744. /*!50003 SET sql_mode = @saved_sql_mode */ ;
  1745. /*!50003 SET character_set_client = @saved_cs_client */ ;
  1746. /*!50003 SET character_set_results = @saved_cs_results */ ;
  1747. /*!50003 SET collation_connection = @saved_col_connection */ ;
  1748. /*!40101 SET @saved_cs_client = @@character_set_client */;
  1749. /*!50503 SET character_set_client = utf8mb4 */;
  1750. CREATE TABLE `print_history` (
  1751. `id` int(11) NOT NULL AUTO_INCREMENT,
  1752. `entity_type` enum('Asset','Template','Borrower','Zone','Report','Custom') NOT NULL,
  1753. `entity_id` int(11) DEFAULT NULL COMMENT 'ID of the asset/template/borrower/zone (NULL for reports)',
  1754. `label_template_id` int(11) DEFAULT NULL,
  1755. `printer_id` int(11) DEFAULT NULL,
  1756. `quantity` int(11) DEFAULT 1,
  1757. `print_status` enum('Success','Failed','Cancelled','Queued') NOT NULL,
  1758. `error_message` text DEFAULT NULL,
  1759. `rendered_data` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT 'The actual data that was sent to printer (for debugging)' CHECK (json_valid(`rendered_data`)),
  1760. `printed_at` timestamp NULL DEFAULT current_timestamp(),
  1761. `printed_by` int(11) DEFAULT NULL,
  1762. PRIMARY KEY (`id`),
  1763. KEY `label_template_id` (`label_template_id`),
  1764. KEY `idx_entity` (`entity_type`,`entity_id`),
  1765. KEY `idx_printed_at` (`printed_at`),
  1766. KEY `idx_printed_by` (`printed_by`),
  1767. KEY `idx_printer` (`printer_id`),
  1768. KEY `idx_status` (`print_status`),
  1769. CONSTRAINT `print_history_ibfk_1` FOREIGN KEY (`label_template_id`) REFERENCES `label_templates` (`id`) ON DELETE SET NULL,
  1770. CONSTRAINT `print_history_ibfk_2` FOREIGN KEY (`printer_id`) REFERENCES `printer_settings` (`id`) ON DELETE SET NULL,
  1771. CONSTRAINT `print_history_ibfk_3` FOREIGN KEY (`printed_by`) REFERENCES `users` (`id`) ON DELETE SET NULL
  1772. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
  1773. /*!40101 SET character_set_client = @saved_cs_client */;
  1774. /*!40101 SET @saved_cs_client = @@character_set_client */;
  1775. /*!50503 SET character_set_client = utf8mb4 */;
  1776. CREATE TABLE `printer_settings` (
  1777. `id` int(11) NOT NULL AUTO_INCREMENT,
  1778. `printer_name` varchar(200) NOT NULL,
  1779. `description` text DEFAULT NULL,
  1780. `log` tinyint(1) DEFAULT 1 COMMENT 'Log all print jobs to this printer',
  1781. `can_be_used_for_reports` tinyint(1) DEFAULT 0 COMMENT 'Can this printer be used for printing reports',
  1782. `min_powerlevel_to_use` int(11) NOT NULL DEFAULT 75 COMMENT 'Minimum role power level required to use this printer',
  1783. `printer_plugin` enum('Ptouch','Brother','Zebra','System','PDF','Network','Custom') NOT NULL COMMENT 'Which printer plugin the client should send printer_settings to',
  1784. `printer_settings` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT 'Printer-specific settings: connection, paper size, DPI, margins, etc.' CHECK (json_valid(`printer_settings`)),
  1785. `created_at` timestamp NULL DEFAULT current_timestamp(),
  1786. `created_by` int(11) DEFAULT NULL,
  1787. `last_modified_at` timestamp NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
  1788. `last_modified_by` int(11) DEFAULT NULL,
  1789. PRIMARY KEY (`id`),
  1790. KEY `created_by` (`created_by`),
  1791. KEY `last_modified_by` (`last_modified_by`),
  1792. KEY `idx_printer_name` (`printer_name`),
  1793. KEY `idx_printer_plugin` (`printer_plugin`),
  1794. KEY `idx_min_powerlevel` (`min_powerlevel_to_use`),
  1795. KEY `idx_can_reports` (`can_be_used_for_reports`),
  1796. CONSTRAINT `printer_settings_ibfk_1` FOREIGN KEY (`created_by`) REFERENCES `users` (`id`) ON DELETE SET NULL,
  1797. CONSTRAINT `printer_settings_ibfk_2` FOREIGN KEY (`last_modified_by`) REFERENCES `users` (`id`) ON DELETE SET NULL,
  1798. CONSTRAINT `CONSTRAINT_1` CHECK (`min_powerlevel_to_use` >= 1 and `min_powerlevel_to_use` <= 100)
  1799. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
  1800. /*!40101 SET character_set_client = @saved_cs_client */;
  1801. /*!40101 SET @saved_cs_client = @@character_set_client */;
  1802. /*!50503 SET character_set_client = utf8mb4 */;
  1803. CREATE TABLE `roles` (
  1804. `id` int(11) NOT NULL AUTO_INCREMENT,
  1805. `name` varchar(100) NOT NULL,
  1806. `power` int(11) NOT NULL CHECK (`power` >= 1 and `power` <= 100),
  1807. `created_at` timestamp NULL DEFAULT current_timestamp(),
  1808. PRIMARY KEY (`id`),
  1809. UNIQUE KEY `name` (`name`)
  1810. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
  1811. /*!40101 SET character_set_client = @saved_cs_client */;
  1812. /*!40101 SET @saved_cs_client = @@character_set_client */;
  1813. /*!50503 SET character_set_client = utf8mb4 */;
  1814. CREATE TABLE `suppliers` (
  1815. `id` int(11) NOT NULL AUTO_INCREMENT,
  1816. `name` varchar(200) NOT NULL,
  1817. `contact` varchar(200) DEFAULT NULL,
  1818. `email` varchar(255) DEFAULT NULL,
  1819. `phone` varchar(50) DEFAULT NULL,
  1820. `website` varchar(255) DEFAULT NULL,
  1821. `notes` text DEFAULT NULL,
  1822. `created_at` timestamp NULL DEFAULT current_timestamp(),
  1823. PRIMARY KEY (`id`)
  1824. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
  1825. /*!40101 SET character_set_client = @saved_cs_client */;
  1826. /*!40101 SET @saved_cs_client = @@character_set_client */;
  1827. /*!50503 SET character_set_client = utf8mb4 */;
  1828. CREATE TABLE `templates` (
  1829. `id` int(11) NOT NULL AUTO_INCREMENT,
  1830. `template_code` varchar(50) DEFAULT NULL,
  1831. `asset_tag_generation_string` varchar(500) DEFAULT NULL,
  1832. `description` text DEFAULT NULL,
  1833. `active` tinyint(1) DEFAULT 1,
  1834. `asset_type` enum('N','B','L','C') DEFAULT NULL,
  1835. `name` varchar(255) DEFAULT NULL,
  1836. `category_id` int(11) DEFAULT NULL,
  1837. `manufacturer` varchar(200) DEFAULT NULL,
  1838. `model` varchar(200) DEFAULT NULL,
  1839. `zone_id` int(11) DEFAULT NULL,
  1840. `zone_plus` enum('Floating Local','Floating Global','Clarify') DEFAULT NULL,
  1841. `zone_note` text DEFAULT NULL,
  1842. `status` enum('Good','Attention','Faulty','Missing','Retired','In Repair','In Transit','Expired','Unmanaged') DEFAULT NULL,
  1843. `price` decimal(12,2) DEFAULT NULL CHECK (`price` is null or `price` >= 0),
  1844. `purchase_date` date DEFAULT NULL COMMENT 'Default purchase date for assets created from this template',
  1845. `purchase_date_now` tinyint(1) DEFAULT 0 COMMENT 'Auto-set purchase date to current date when creating assets',
  1846. `warranty_until` date DEFAULT NULL,
  1847. `warranty_auto` tinyint(1) DEFAULT 0 COMMENT 'Auto-calculate warranty_until from purchase_date',
  1848. `warranty_auto_amount` int(11) DEFAULT NULL COMMENT 'Number of days/years for warranty calculation',
  1849. `warranty_auto_unit` enum('days','years') DEFAULT 'years' COMMENT 'Unit for warranty auto-calculation',
  1850. `expiry_date` date DEFAULT NULL,
  1851. `expiry_auto` tinyint(1) DEFAULT 0 COMMENT 'Auto-calculate expiry_date from purchase_date',
  1852. `expiry_auto_amount` int(11) DEFAULT NULL COMMENT 'Number of days/years for expiry calculation',
  1853. `expiry_auto_unit` enum('days','years') DEFAULT 'years' COMMENT 'Unit for expiry auto-calculation',
  1854. `quantity_total` int(11) DEFAULT NULL,
  1855. `quantity_used` int(11) DEFAULT NULL,
  1856. `supplier_id` int(11) DEFAULT NULL,
  1857. `lendable` tinyint(1) DEFAULT NULL,
  1858. `lending_status` enum('Available','Borrowed','Overdue','Deployed','Illegally Handed Out','Stolen') DEFAULT 'Available' COMMENT 'Default lending status for assets created from this template',
  1859. `minimum_role_for_lending` int(11) DEFAULT NULL,
  1860. `audit_task_id` int(11) DEFAULT NULL,
  1861. `label_template_id` int(11) DEFAULT NULL,
  1862. `no_scan` enum('Yes','Ask','No') DEFAULT NULL,
  1863. `notes` text DEFAULT NULL,
  1864. `additional_fields` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`additional_fields`)),
  1865. `created_at` timestamp NULL DEFAULT current_timestamp(),
  1866. PRIMARY KEY (`id`),
  1867. UNIQUE KEY `template_code` (`template_code`),
  1868. KEY `category_id` (`category_id`),
  1869. KEY `zone_id` (`zone_id`),
  1870. KEY `supplier_id` (`supplier_id`),
  1871. KEY `audit_task_id` (`audit_task_id`),
  1872. KEY `idx_template_code` (`template_code`),
  1873. KEY `idx_label_template` (`label_template_id`),
  1874. KEY `idx_asset_tag_generation` (`asset_tag_generation_string`),
  1875. CONSTRAINT `fk_template_label_template` FOREIGN KEY (`label_template_id`) REFERENCES `label_templates` (`id`) ON DELETE SET NULL,
  1876. CONSTRAINT `templates_ibfk_1` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE SET NULL,
  1877. CONSTRAINT `templates_ibfk_2` FOREIGN KEY (`zone_id`) REFERENCES `zones` (`id`) ON DELETE SET NULL,
  1878. CONSTRAINT `templates_ibfk_3` FOREIGN KEY (`supplier_id`) REFERENCES `suppliers` (`id`) ON DELETE SET NULL,
  1879. CONSTRAINT `templates_ibfk_4` FOREIGN KEY (`audit_task_id`) REFERENCES `audit_tasks` (`id`) ON DELETE SET NULL
  1880. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
  1881. /*!40101 SET character_set_client = @saved_cs_client */;
  1882. /*!40101 SET @saved_cs_client = @@character_set_client */;
  1883. /*!50503 SET character_set_client = utf8mb4 */;
  1884. CREATE TABLE `users` (
  1885. `id` int(11) NOT NULL AUTO_INCREMENT,
  1886. `name` varchar(200) NOT NULL,
  1887. `username` varchar(100) NOT NULL,
  1888. `password` varchar(255) NOT NULL,
  1889. `pin_code` varchar(8) DEFAULT NULL,
  1890. `login_string` varchar(255) DEFAULT NULL,
  1891. `role_id` int(11) NOT NULL,
  1892. `email` varchar(255) DEFAULT NULL,
  1893. `phone` varchar(50) DEFAULT NULL,
  1894. `notes` text DEFAULT NULL,
  1895. `active` tinyint(1) DEFAULT 1,
  1896. `last_login_date` datetime DEFAULT NULL,
  1897. `created_date` timestamp NULL DEFAULT current_timestamp(),
  1898. `password_reset_token` varchar(255) DEFAULT NULL,
  1899. `password_reset_expiry` datetime DEFAULT NULL,
  1900. `preferences` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT 'User personalization settings: common (all clients) + client-specific (web, mobile, desktop)' CHECK (json_valid(`preferences`)),
  1901. PRIMARY KEY (`id`),
  1902. UNIQUE KEY `username` (`username`),
  1903. KEY `role_id` (`role_id`),
  1904. KEY `idx_username` (`username`),
  1905. KEY `idx_login_string` (`login_string`),
  1906. CONSTRAINT `users_ibfk_1` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`)
  1907. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
  1908. /*!40101 SET character_set_client = @saved_cs_client */;
  1909. /*!40101 SET @saved_cs_client = @@character_set_client */;
  1910. /*!50503 SET character_set_client = utf8mb4 */;
  1911. CREATE TABLE `zones` (
  1912. `id` int(11) NOT NULL AUTO_INCREMENT,
  1913. `zone_name` varchar(200) NOT NULL,
  1914. `zone_notes` text DEFAULT NULL,
  1915. `zone_type` enum('Building','Floor','Room','Storage Area') NOT NULL,
  1916. `zone_code` varchar(50) DEFAULT NULL,
  1917. `mini_code` varchar(50) DEFAULT NULL,
  1918. `parent_id` int(11) DEFAULT NULL,
  1919. `include_in_parent` tinyint(1) DEFAULT 1,
  1920. `audit_timeout_minutes` int(11) DEFAULT 60 COMMENT 'Audit timeout in minutes for this zone',
  1921. PRIMARY KEY (`id`),
  1922. KEY `idx_parent` (`parent_id`),
  1923. KEY `idx_type` (`zone_type`),
  1924. CONSTRAINT `zones_ibfk_1` FOREIGN KEY (`parent_id`) REFERENCES `zones` (`id`)
  1925. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
  1926. /*!40101 SET character_set_client = @saved_cs_client */;
  1927. --
  1928. -- WARNING: can't read the INFORMATION_SCHEMA.libraries table. It's most probably an old server 12.0.2-MariaDB-ubu2404.
  1929. --
  1930. /*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
  1931. /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
  1932. /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
  1933. /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;