1
0

beepzone-schema-dump.sql 105 KB

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