@salesforce/ui-bundle-template-feature-property-management-metadata 1.117.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (206) hide show
  1. package/LICENSE.txt +82 -0
  2. package/package.json +25 -0
  3. package/src/force-app/main/default/classes/MaintenanceRequestTriggerHandler.cls +69 -0
  4. package/src/force-app/main/default/classes/MaintenanceRequestTriggerHandler.cls-meta.xml +5 -0
  5. package/src/force-app/main/default/classes/MaintenanceRequestTriggerHandler_Test.cls +308 -0
  6. package/src/force-app/main/default/classes/MaintenanceRequestTriggerHandler_Test.cls-meta.xml +5 -0
  7. package/src/force-app/main/default/classes/TenantTriggerHandler.cls +77 -0
  8. package/src/force-app/main/default/classes/TenantTriggerHandler.cls-meta.xml +5 -0
  9. package/src/force-app/main/default/classes/TenantTriggerHandler_Test.cls +100 -0
  10. package/src/force-app/main/default/classes/TenantTriggerHandler_Test.cls-meta.xml +5 -0
  11. package/src/force-app/main/default/cspTrustedSites/GitHub_Avatars.cspTrustedSite-meta.xml +15 -0
  12. package/src/force-app/main/default/cspTrustedSites/Google_Fonts.cspTrustedSite-meta.xml +15 -0
  13. package/src/force-app/main/default/cspTrustedSites/Google_Fonts_Static.cspTrustedSite-meta.xml +15 -0
  14. package/src/force-app/main/default/cspTrustedSites/OpenStreetMap_Nominatim.cspTrustedSite-meta.xml +15 -0
  15. package/src/force-app/main/default/cspTrustedSites/OpenStreetMap_Tiles.cspTrustedSite-meta.xml +15 -0
  16. package/src/force-app/main/default/cspTrustedSites/Open_Meteo_API.cspTrustedSite-meta.xml +15 -0
  17. package/src/force-app/main/default/cspTrustedSites/Pexels_Images.cspTrustedSite-meta.xml +15 -0
  18. package/src/force-app/main/default/cspTrustedSites/Pexels_Videos.cspTrustedSite-meta.xml +15 -0
  19. package/src/force-app/main/default/cspTrustedSites/Unsplash_Images.cspTrustedSite-meta.xml +15 -0
  20. package/src/force-app/main/default/data/Agent__c.json +79 -0
  21. package/src/force-app/main/default/data/Application__c.json +124 -0
  22. package/src/force-app/main/default/data/Contact.json +44 -0
  23. package/src/force-app/main/default/data/KPI_Snapshot__c.json +160 -0
  24. package/src/force-app/main/default/data/Lease__c.json +9442 -0
  25. package/src/force-app/main/default/data/Maintenance_Request__c.json +514 -0
  26. package/src/force-app/main/default/data/Maintenance_Worker__c.json +304 -0
  27. package/src/force-app/main/default/data/Notification__c.json +214 -0
  28. package/src/force-app/main/default/data/Payment__c.json +1024 -0
  29. package/src/force-app/main/default/data/Property_Cost__c.json +484 -0
  30. package/src/force-app/main/default/data/Property_Feature__c.json +169 -0
  31. package/src/force-app/main/default/data/Property_Image__c.json +148 -0
  32. package/src/force-app/main/default/data/Property_Listing__c.json +130 -0
  33. package/src/force-app/main/default/data/Property_Management_Company__c.json +70 -0
  34. package/src/force-app/main/default/data/Property_Owner__c.json +184 -0
  35. package/src/force-app/main/default/data/Property_Sale__c.json +246 -0
  36. package/src/force-app/main/default/data/Property__c.json +704 -0
  37. package/src/force-app/main/default/data/Tenant__c.json +184 -0
  38. package/src/force-app/main/default/data/data-plan.json +110 -0
  39. package/src/force-app/main/default/data/prepare-import-unique-fields.js +85 -0
  40. package/src/force-app/main/default/layouts/Application__c-Application Layout.layout-meta.xml +58 -0
  41. package/src/force-app/main/default/layouts/KPI_Snapshot__c-KPI Snapshot Layout.layout-meta.xml +87 -0
  42. package/src/force-app/main/default/layouts/Lease__c-Lease Layout.layout-meta.xml +83 -0
  43. package/src/force-app/main/default/layouts/Maintenance_Request__c-Maintenance Request Layout.layout-meta.xml +89 -0
  44. package/src/force-app/main/default/layouts/Maintenance_Worker__c-Maintenance Worker Layout.layout-meta.xml +66 -0
  45. package/src/force-app/main/default/layouts/Payment__c-Payment Layout.layout-meta.xml +88 -0
  46. package/src/force-app/main/default/layouts/Property_Cost__c-Property Cost Layout.layout-meta.xml +88 -0
  47. package/src/force-app/main/default/layouts/Property_Feature__c-Property Feature Layout.layout-meta.xml +80 -0
  48. package/src/force-app/main/default/layouts/Property_Image__c-Property Image Layout.layout-meta.xml +75 -0
  49. package/src/force-app/main/default/layouts/Property_Listing__c-Property Listing Layout.layout-meta.xml +92 -0
  50. package/src/force-app/main/default/layouts/Property_Management_Company__c-Property Management Company Layout.layout-meta.xml +75 -0
  51. package/src/force-app/main/default/layouts/Property_Owner__c-Property Owner Layout.layout-meta.xml +67 -0
  52. package/src/force-app/main/default/layouts/Property_Sale__c-Property Sale Layout.layout-meta.xml +87 -0
  53. package/src/force-app/main/default/layouts/Property__c-Property Layout.layout-meta.xml +130 -0
  54. package/src/force-app/main/default/layouts/Tenant__c-Tenant Layout.layout-meta.xml +58 -0
  55. package/src/force-app/main/default/objects/Agent__c/Agent__c.object-meta.xml +66 -0
  56. package/src/force-app/main/default/objects/Agent__c/fields/Agent_Type__c.field-meta.xml +37 -0
  57. package/src/force-app/main/default/objects/Agent__c/fields/Availability__c.field-meta.xml +37 -0
  58. package/src/force-app/main/default/objects/Agent__c/fields/Emergency_Alt__c.field-meta.xml +11 -0
  59. package/src/force-app/main/default/objects/Agent__c/fields/Language__c.field-meta.xml +42 -0
  60. package/src/force-app/main/default/objects/Agent__c/fields/License_Expiry__c.field-meta.xml +11 -0
  61. package/src/force-app/main/default/objects/Agent__c/fields/License_Number__c.field-meta.xml +14 -0
  62. package/src/force-app/main/default/objects/Agent__c/fields/Office_Location__c.field-meta.xml +42 -0
  63. package/src/force-app/main/default/objects/Agent__c/fields/Territory__c.field-meta.xml +42 -0
  64. package/src/force-app/main/default/objects/Application__c/Application__c.object-meta.xml +67 -0
  65. package/src/force-app/main/default/objects/Application__c/fields/Employment__c.field-meta.xml +10 -0
  66. package/src/force-app/main/default/objects/Application__c/fields/Property__c.field-meta.xml +15 -0
  67. package/src/force-app/main/default/objects/Application__c/fields/References__c.field-meta.xml +10 -0
  68. package/src/force-app/main/default/objects/Application__c/fields/Start_Date__c.field-meta.xml +9 -0
  69. package/src/force-app/main/default/objects/Application__c/fields/Status__c.field-meta.xml +47 -0
  70. package/src/force-app/main/default/objects/Application__c/fields/User__c.field-meta.xml +15 -0
  71. package/src/force-app/main/default/objects/KPI_Snapshot__c/KPI_Snapshot__c.object-meta.xml +65 -0
  72. package/src/force-app/main/default/objects/KPI_Snapshot__c/fields/Previous_Month_Sales__c.field-meta.xml +12 -0
  73. package/src/force-app/main/default/objects/KPI_Snapshot__c/fields/Sales_MoM_Change__c.field-meta.xml +18 -0
  74. package/src/force-app/main/default/objects/KPI_Snapshot__c/fields/Snapshot_Date__c.field-meta.xml +10 -0
  75. package/src/force-app/main/default/objects/KPI_Snapshot__c/fields/Total_Properties__c.field-meta.xml +13 -0
  76. package/src/force-app/main/default/objects/KPI_Snapshot__c/fields/Total_Sales_Amount__c.field-meta.xml +12 -0
  77. package/src/force-app/main/default/objects/KPI_Snapshot__c/fields/Total_Sales_Count__c.field-meta.xml +13 -0
  78. package/src/force-app/main/default/objects/KPI_Snapshot__c/fields/Units_Available__c.field-meta.xml +13 -0
  79. package/src/force-app/main/default/objects/KPI_Snapshot__c/fields/Units_Occupied__c.field-meta.xml +13 -0
  80. package/src/force-app/main/default/objects/Lease__c/Lease__c.object-meta.xml +67 -0
  81. package/src/force-app/main/default/objects/Lease__c/fields/End_Date__c.field-meta.xml +10 -0
  82. package/src/force-app/main/default/objects/Lease__c/fields/Lease_Status__c.field-meta.xml +36 -0
  83. package/src/force-app/main/default/objects/Lease__c/fields/Monthly_Rent__c.field-meta.xml +12 -0
  84. package/src/force-app/main/default/objects/Lease__c/fields/Property__c.field-meta.xml +15 -0
  85. package/src/force-app/main/default/objects/Lease__c/fields/Security_Deposit__c.field-meta.xml +12 -0
  86. package/src/force-app/main/default/objects/Lease__c/fields/Start_Date__c.field-meta.xml +10 -0
  87. package/src/force-app/main/default/objects/Lease__c/fields/Tenant__c.field-meta.xml +14 -0
  88. package/src/force-app/main/default/objects/Lease__c/validationRules/End_Date_After_Start_Date.validationRule-meta.xml +10 -0
  89. package/src/force-app/main/default/objects/Maintenance_Request__c/Maintenance_Request__c.object-meta.xml +73 -0
  90. package/src/force-app/main/default/objects/Maintenance_Request__c/fields/Actual_Cost__c.field-meta.xml +13 -0
  91. package/src/force-app/main/default/objects/Maintenance_Request__c/fields/Assigned_Worker__c.field-meta.xml +15 -0
  92. package/src/force-app/main/default/objects/Maintenance_Request__c/fields/Completed__c.field-meta.xml +11 -0
  93. package/src/force-app/main/default/objects/Maintenance_Request__c/fields/Description__c.field-meta.xml +12 -0
  94. package/src/force-app/main/default/objects/Maintenance_Request__c/fields/Est_Cost__c.field-meta.xml +13 -0
  95. package/src/force-app/main/default/objects/Maintenance_Request__c/fields/Priority__c.field-meta.xml +32 -0
  96. package/src/force-app/main/default/objects/Maintenance_Request__c/fields/Property__c.field-meta.xml +15 -0
  97. package/src/force-app/main/default/objects/Maintenance_Request__c/fields/Scheduled__c.field-meta.xml +11 -0
  98. package/src/force-app/main/default/objects/Maintenance_Request__c/fields/Status__c.field-meta.xml +42 -0
  99. package/src/force-app/main/default/objects/Maintenance_Request__c/fields/Tenant_Home__c.field-meta.xml +11 -0
  100. package/src/force-app/main/default/objects/Maintenance_Request__c/fields/Type__c.field-meta.xml +62 -0
  101. package/src/force-app/main/default/objects/Maintenance_Request__c/fields/User__c.field-meta.xml +15 -0
  102. package/src/force-app/main/default/objects/Maintenance_Worker__c/Maintenance_Worker__c.object-meta.xml +71 -0
  103. package/src/force-app/main/default/objects/Maintenance_Worker__c/fields/Certifications__c.field-meta.xml +12 -0
  104. package/src/force-app/main/default/objects/Maintenance_Worker__c/fields/Employment_Type__c.field-meta.xml +32 -0
  105. package/src/force-app/main/default/objects/Maintenance_Worker__c/fields/Hourly_Rate__c.field-meta.xml +13 -0
  106. package/src/force-app/main/default/objects/Maintenance_Worker__c/fields/IsActive__c.field-meta.xml +11 -0
  107. package/src/force-app/main/default/objects/Maintenance_Worker__c/fields/Location__c.field-meta.xml +13 -0
  108. package/src/force-app/main/default/objects/Maintenance_Worker__c/fields/Phone__c.field-meta.xml +11 -0
  109. package/src/force-app/main/default/objects/Maintenance_Worker__c/fields/Rating__c.field-meta.xml +14 -0
  110. package/src/force-app/main/default/objects/Maintenance_Worker__c/fields/Type__c.field-meta.xml +57 -0
  111. package/src/force-app/main/default/objects/Notification__c/Notification__c.object-meta.xml +67 -0
  112. package/src/force-app/main/default/objects/Notification__c/fields/Is_Read__c.field-meta.xml +10 -0
  113. package/src/force-app/main/default/objects/Notification__c/fields/Message__c.field-meta.xml +11 -0
  114. package/src/force-app/main/default/objects/Notification__c/fields/Priority__c.field-meta.xml +36 -0
  115. package/src/force-app/main/default/objects/Notification__c/fields/Related_Object_Type__c.field-meta.xml +12 -0
  116. package/src/force-app/main/default/objects/Notification__c/fields/Related_Record_Id__c.field-meta.xml +12 -0
  117. package/src/force-app/main/default/objects/Notification__c/fields/Title__c.field-meta.xml +12 -0
  118. package/src/force-app/main/default/objects/Notification__c/fields/Type__c.field-meta.xml +36 -0
  119. package/src/force-app/main/default/objects/Notification__c/fields/User__c.field-meta.xml +14 -0
  120. package/src/force-app/main/default/objects/Payment__c/Payment__c.object-meta.xml +67 -0
  121. package/src/force-app/main/default/objects/Payment__c/fields/Amount__c.field-meta.xml +12 -0
  122. package/src/force-app/main/default/objects/Payment__c/fields/Lease__c.field-meta.xml +15 -0
  123. package/src/force-app/main/default/objects/Payment__c/fields/Notes__c.field-meta.xml +11 -0
  124. package/src/force-app/main/default/objects/Payment__c/fields/Payment_Date__c.field-meta.xml +10 -0
  125. package/src/force-app/main/default/objects/Payment__c/fields/Payment_Method__c.field-meta.xml +41 -0
  126. package/src/force-app/main/default/objects/Payment__c/fields/Payment_Status__c.field-meta.xml +36 -0
  127. package/src/force-app/main/default/objects/Property_Cost__c/Property_Cost__c.object-meta.xml +67 -0
  128. package/src/force-app/main/default/objects/Property_Cost__c/fields/Cost_Amount__c.field-meta.xml +12 -0
  129. package/src/force-app/main/default/objects/Property_Cost__c/fields/Cost_Category__c.field-meta.xml +56 -0
  130. package/src/force-app/main/default/objects/Property_Cost__c/fields/Cost_Date__c.field-meta.xml +11 -0
  131. package/src/force-app/main/default/objects/Property_Cost__c/fields/Description__c.field-meta.xml +12 -0
  132. package/src/force-app/main/default/objects/Property_Cost__c/fields/Property__c.field-meta.xml +15 -0
  133. package/src/force-app/main/default/objects/Property_Cost__c/fields/Vendor__c.field-meta.xml +12 -0
  134. package/src/force-app/main/default/objects/Property_Cost__c/validationRules/Cost_Amount_Limit.validationRule-meta.xml +10 -0
  135. package/src/force-app/main/default/objects/Property_Feature__c/Property_Feature__c.object-meta.xml +65 -0
  136. package/src/force-app/main/default/objects/Property_Feature__c/fields/Description__c.field-meta.xml +11 -0
  137. package/src/force-app/main/default/objects/Property_Feature__c/fields/Display_on_Listing__c.field-meta.xml +10 -0
  138. package/src/force-app/main/default/objects/Property_Feature__c/fields/Feature_Category__c.field-meta.xml +51 -0
  139. package/src/force-app/main/default/objects/Property_Feature__c/fields/Property__c.field-meta.xml +15 -0
  140. package/src/force-app/main/default/objects/Property_Image__c/Property_Image__c.object-meta.xml +65 -0
  141. package/src/force-app/main/default/objects/Property_Image__c/fields/Alt_Text__c.field-meta.xml +12 -0
  142. package/src/force-app/main/default/objects/Property_Image__c/fields/Display_Order__c.field-meta.xml +13 -0
  143. package/src/force-app/main/default/objects/Property_Image__c/fields/Image_Type__c.field-meta.xml +41 -0
  144. package/src/force-app/main/default/objects/Property_Image__c/fields/Image_URL__c.field-meta.xml +10 -0
  145. package/src/force-app/main/default/objects/Property_Image__c/fields/Property__c.field-meta.xml +15 -0
  146. package/src/force-app/main/default/objects/Property_Listing__c/Property_Listing__c.object-meta.xml +65 -0
  147. package/src/force-app/main/default/objects/Property_Listing__c/fields/Display_Order__c.field-meta.xml +13 -0
  148. package/src/force-app/main/default/objects/Property_Listing__c/fields/Featured__c.field-meta.xml +10 -0
  149. package/src/force-app/main/default/objects/Property_Listing__c/fields/Listing_Price__c.field-meta.xml +12 -0
  150. package/src/force-app/main/default/objects/Property_Listing__c/fields/Listing_Status__c.field-meta.xml +41 -0
  151. package/src/force-app/main/default/objects/Property_Listing__c/fields/Marketing_Description__c.field-meta.xml +11 -0
  152. package/src/force-app/main/default/objects/Property_Listing__c/fields/Property__c.field-meta.xml +15 -0
  153. package/src/force-app/main/default/objects/Property_Listing__c/fields/Short_Description__c.field-meta.xml +12 -0
  154. package/src/force-app/main/default/objects/Property_Management_Company__c/Property_Management_Company__c.object-meta.xml +65 -0
  155. package/src/force-app/main/default/objects/Property_Management_Company__c/fields/Active__c.field-meta.xml +10 -0
  156. package/src/force-app/main/default/objects/Property_Management_Company__c/fields/Company_Code__c.field-meta.xml +13 -0
  157. package/src/force-app/main/default/objects/Property_Management_Company__c/fields/Email__c.field-meta.xml +10 -0
  158. package/src/force-app/main/default/objects/Property_Management_Company__c/fields/Phone__c.field-meta.xml +10 -0
  159. package/src/force-app/main/default/objects/Property_Management_Company__c/fields/Primary_Contact__c.field-meta.xml +14 -0
  160. package/src/force-app/main/default/objects/Property_Owner__c/Property_Owner__c.object-meta.xml +65 -0
  161. package/src/force-app/main/default/objects/Property_Owner__c/fields/Address__c.field-meta.xml +10 -0
  162. package/src/force-app/main/default/objects/Property_Owner__c/fields/Email__c.field-meta.xml +11 -0
  163. package/src/force-app/main/default/objects/Property_Owner__c/fields/Phone__c.field-meta.xml +10 -0
  164. package/src/force-app/main/default/objects/Property_Sale__c/Property_Sale__c.object-meta.xml +67 -0
  165. package/src/force-app/main/default/objects/Property_Sale__c/fields/Buyer_Tenant__c.field-meta.xml +13 -0
  166. package/src/force-app/main/default/objects/Property_Sale__c/fields/Payment_Method__c.field-meta.xml +41 -0
  167. package/src/force-app/main/default/objects/Property_Sale__c/fields/Property__c.field-meta.xml +15 -0
  168. package/src/force-app/main/default/objects/Property_Sale__c/fields/Reference_Number__c.field-meta.xml +12 -0
  169. package/src/force-app/main/default/objects/Property_Sale__c/fields/Sale_Amount__c.field-meta.xml +12 -0
  170. package/src/force-app/main/default/objects/Property_Sale__c/fields/Sale_Date__c.field-meta.xml +10 -0
  171. package/src/force-app/main/default/objects/Property_Sale__c/fields/Sale_Status__c.field-meta.xml +36 -0
  172. package/src/force-app/main/default/objects/Property_Sale__c/fields/Sale_Type__c.field-meta.xml +51 -0
  173. package/src/force-app/main/default/objects/Property__c/Property__c.object-meta.xml +71 -0
  174. package/src/force-app/main/default/objects/Property__c/fields/Address__c.field-meta.xml +13 -0
  175. package/src/force-app/main/default/objects/Property__c/fields/Agent__c.field-meta.xml +15 -0
  176. package/src/force-app/main/default/objects/Property__c/fields/Available_Date__c.field-meta.xml +11 -0
  177. package/src/force-app/main/default/objects/Property__c/fields/Bathrooms__c.field-meta.xml +14 -0
  178. package/src/force-app/main/default/objects/Property__c/fields/Bedrooms__c.field-meta.xml +14 -0
  179. package/src/force-app/main/default/objects/Property__c/fields/Coordinates__c.field-meta.xml +12 -0
  180. package/src/force-app/main/default/objects/Property__c/fields/Deposit__c.field-meta.xml +13 -0
  181. package/src/force-app/main/default/objects/Property__c/fields/Description__c.field-meta.xml +12 -0
  182. package/src/force-app/main/default/objects/Property__c/fields/Features__c.field-meta.xml +38 -0
  183. package/src/force-app/main/default/objects/Property__c/fields/Hero_Image__c.field-meta.xml +11 -0
  184. package/src/force-app/main/default/objects/Property__c/fields/Lease_Term__c.field-meta.xml +14 -0
  185. package/src/force-app/main/default/objects/Property__c/fields/Monthly_Rent__c.field-meta.xml +13 -0
  186. package/src/force-app/main/default/objects/Property__c/fields/Parking__c.field-meta.xml +14 -0
  187. package/src/force-app/main/default/objects/Property__c/fields/Pet_Friendly__c.field-meta.xml +11 -0
  188. package/src/force-app/main/default/objects/Property__c/fields/Sq_Ft__c.field-meta.xml +14 -0
  189. package/src/force-app/main/default/objects/Property__c/fields/Status__c.field-meta.xml +37 -0
  190. package/src/force-app/main/default/objects/Property__c/fields/Tour_URL__c.field-meta.xml +11 -0
  191. package/src/force-app/main/default/objects/Property__c/fields/Type__c.field-meta.xml +37 -0
  192. package/src/force-app/main/default/objects/Property__c/fields/Utilities__c.field-meta.xml +38 -0
  193. package/src/force-app/main/default/objects/Property__c/fields/Year_Built__c.field-meta.xml +14 -0
  194. package/src/force-app/main/default/objects/Tenant__c/Tenant__c.object-meta.xml +67 -0
  195. package/src/force-app/main/default/objects/Tenant__c/fields/End_Date__c.field-meta.xml +11 -0
  196. package/src/force-app/main/default/objects/Tenant__c/fields/Property__c.field-meta.xml +15 -0
  197. package/src/force-app/main/default/objects/Tenant__c/fields/Start_Date__c.field-meta.xml +11 -0
  198. package/src/force-app/main/default/objects/Tenant__c/fields/Status__c.field-meta.xml +37 -0
  199. package/src/force-app/main/default/objects/Tenant__c/fields/User_Status__c.field-meta.xml +42 -0
  200. package/src/force-app/main/default/objects/Tenant__c/fields/User__c.field-meta.xml +15 -0
  201. package/src/force-app/main/default/permissionsets/Property_Management_Access.permissionset-meta.xml +633 -0
  202. package/src/force-app/main/default/permissionsets/Tenant_Maintenance_Access.permissionset-meta.xml +137 -0
  203. package/src/force-app/main/default/triggers/MaintenanceRequestTrigger.trigger +5 -0
  204. package/src/force-app/main/default/triggers/MaintenanceRequestTrigger.trigger-meta.xml +5 -0
  205. package/src/force-app/main/default/triggers/TenantTrigger.trigger +8 -0
  206. package/src/force-app/main/default/triggers/TenantTrigger.trigger-meta.xml +5 -0
package/LICENSE.txt ADDED
@@ -0,0 +1,82 @@
1
+ Terms of Use
2
+
3
+ Copyright 2026 Salesforce, Inc. All rights reserved.
4
+
5
+ These Terms of Use govern the download, installation, and/or use of this
6
+ software provided by Salesforce, Inc. ("Salesforce") (the "Software"), were
7
+ last updated on April 15, 2025, and constitute a legally binding
8
+ agreement between you and Salesforce. If you do not agree to these Terms of
9
+ Use, do not install or use the Software.
10
+
11
+ Salesforce grants you a worldwide, non-exclusive, no-charge, royalty-free
12
+ copyright license to reproduce, prepare derivative works of, publicly
13
+ display, publicly perform, sublicense, and distribute the Software and
14
+ derivative works subject to these Terms. These Terms shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ Subject to the limited rights expressly granted hereunder, Salesforce
18
+ reserves all rights, title, and interest in and to all intellectual
19
+ property subsisting in the Software. No rights are granted to you hereunder
20
+ other than as expressly set forth herein. Users residing in countries on
21
+ the United States Office of Foreign Assets Control sanction list, or which
22
+ are otherwise subject to a US export embargo, may not use the Software.
23
+
24
+ Implementation of the Software may require development work, for which you
25
+ are responsible. The Software may contain bugs, errors and
26
+ incompatibilities and is made available on an AS IS basis without support,
27
+ updates, or service level commitments.
28
+
29
+ Salesforce reserves the right at any time to modify, suspend, or
30
+ discontinue, the Software (or any part thereof) with or without notice. You
31
+ agree that Salesforce shall not be liable to you or to any third party for
32
+ any modification, suspension, or discontinuance.
33
+
34
+ You agree to defend Salesforce against any claim, demand, suit or
35
+ proceeding made or brought against Salesforce by a third party arising out
36
+ of or accruing from (a) your use of the Software, and (b) any application
37
+ you develop with the Software that infringes any copyright, trademark,
38
+ trade secret, trade dress, patent, or other intellectual property right of
39
+ any person or defames any person or violates their rights of publicity or
40
+ privacy (each a "Claim Against Salesforce"), and will indemnify Salesforce
41
+ from any damages, attorney fees, and costs finally awarded against
42
+ Salesforce as a result of, or for any amounts paid by Salesforce under a
43
+ settlement approved by you in writing of, a Claim Against Salesforce,
44
+ provided Salesforce (x) promptly gives you written notice of the Claim
45
+ Against Salesforce, (y) gives you sole control of the defense and
46
+ settlement of the Claim Against Salesforce (except that you may not settle
47
+ any Claim Against Salesforce unless it unconditionally releases Salesforce
48
+ of all liability), and (z) gives you all reasonable assistance, at your
49
+ expense.
50
+
51
+ WITHOUT LIMITING THE GENERALITY OF THE FOREGOING, THE SOFTWARE IS NOT
52
+ SUPPORTED AND IS PROVIDED "AS IS," WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
53
+ IMPLIED. IN NO EVENT SHALL SALESFORCE HAVE ANY LIABILITY FOR ANY DAMAGES,
54
+ INCLUDING, BUT NOT LIMITED TO, DIRECT, INDIRECT, SPECIAL, INCIDENTAL,
55
+ PUNITIVE, OR CONSEQUENTIAL DAMAGES, OR DAMAGES BASED ON LOST PROFITS, DATA,
56
+ OR USE, IN CONNECTION WITH THE SOFTWARE, HOWEVER CAUSED AND WHETHER IN
57
+ CONTRACT, TORT, OR UNDER ANY OTHER THEORY OF LIABILITY, WHETHER OR NOT YOU
58
+ HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
59
+
60
+ These Terms of Use shall be governed exclusively by the internal laws of
61
+ the State of California, without regard to its conflicts of laws
62
+ rules. Each party hereby consents to the exclusive jurisdiction of the
63
+ state and federal courts located in San Francisco County, California to
64
+ adjudicate any dispute arising out of or relating to these Terms of Use and
65
+ the download, installation, and/or use of the Software. Except as expressly
66
+ stated herein, these Terms of Use constitute the entire agreement between
67
+ the parties, and supersede all prior and contemporaneous agreements,
68
+ proposals, or representations, written or oral, concerning their subject
69
+ matter. No modification, amendment, or waiver of any provision of these
70
+ Terms of Use shall be effective unless it is by an update to these Terms of
71
+ Use that Salesforce makes available, or is in writing and signed by the
72
+ party against whom the modification, amendment, or waiver is to be
73
+ asserted.
74
+
75
+ Data Privacy: Salesforce may collect, process, and store device,
76
+ system, and other information related to your use of the Software. This
77
+ information includes, but is not limited to, IP address, user metrics, and
78
+ other data ("Usage Data"). Salesforce may use Usage Data for analytics,
79
+ product development, and marketing purposes. You acknowledge that files
80
+ generated in conjunction with the Software may contain sensitive or
81
+ confidential data, and you are solely responsible for anonymizing and
82
+ protecting such data.
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@salesforce/ui-bundle-template-feature-property-management-metadata",
3
+ "version": "1.117.2",
4
+ "description": "Shared Property Management TDX object metadata and data for B2E/B2X sample apps",
5
+ "license": "SEE LICENSE IN LICENSE.txt",
6
+ "author": "",
7
+ "type": "module",
8
+ "main": "index.js",
9
+ "files": [
10
+ "src"
11
+ ],
12
+ "publishConfig": {
13
+ "access": "public"
14
+ },
15
+ "scripts": {
16
+ "clean": "rm -rf dist"
17
+ },
18
+ "nx": {
19
+ "targets": {
20
+ "watch": {
21
+ "executor": "@salesforce/ui-bundle-template-cli:watch-patches"
22
+ }
23
+ }
24
+ }
25
+ }
@@ -0,0 +1,69 @@
1
+ public without sharing class MaintenanceRequestTriggerHandler {
2
+
3
+ /**
4
+ * Handles before insert logic for Maintenance Request records
5
+ * Automatically assigns workers based on request type and updates status to Assigned
6
+ */
7
+ public static void handleBeforeInsert(List<Maintenance_Request__c> newRequests) {
8
+ // Map to store request type to worker type mappings
9
+ Map<String, String> requestTypeToWorkerType = new Map<String, String>{
10
+ 'Plumbing' => 'Plumbing',
11
+ 'Electrical' => 'Electrical',
12
+ 'HVAC' => 'HVAC (Heating & Cooling)',
13
+ 'Appliance' => 'Appliance Repair',
14
+ 'Carpentry' => 'General Carpentry',
15
+ 'Landscaping' => 'Landscaping / Grounds',
16
+ 'Cleaning' => 'Janitorial / Cleaning',
17
+ 'Pest' => 'Pest Control'
18
+ };
19
+
20
+ // Collect unique worker types needed
21
+ Set<String> workerTypesNeeded = new Set<String>();
22
+ for (Maintenance_Request__c request : newRequests) {
23
+ if (request.Type__c != null && requestTypeToWorkerType.containsKey(request.Type__c)) {
24
+ workerTypesNeeded.add(requestTypeToWorkerType.get(request.Type__c));
25
+ }
26
+ }
27
+
28
+ // Query for available workers by type
29
+ Map<String, List<Maintenance_Worker__c>> workersByType = new Map<String, List<Maintenance_Worker__c>>();
30
+ if (!workerTypesNeeded.isEmpty()) {
31
+ for (Maintenance_Worker__c worker : [
32
+ SELECT Id, Name, Type__c, Rating__c
33
+ FROM Maintenance_Worker__c
34
+ WHERE Type__c IN :workerTypesNeeded
35
+ AND IsActive__c = true
36
+ ORDER BY Rating__c DESC NULLS LAST, Name ASC
37
+ ]) {
38
+ if (!workersByType.containsKey(worker.Type__c)) {
39
+ workersByType.put(worker.Type__c, new List<Maintenance_Worker__c>());
40
+ }
41
+ workersByType.get(worker.Type__c).add(worker);
42
+ }
43
+ }
44
+
45
+ // Assign workers to requests
46
+ for (Maintenance_Request__c request : newRequests) {
47
+ // Only process requests with 'New' status (or null, since 'New' is the default)
48
+ if ((request.Status__c == 'New' || request.Status__c == null) &&
49
+ request.Type__c != null && requestTypeToWorkerType.containsKey(request.Type__c)) {
50
+ String workerType = requestTypeToWorkerType.get(request.Type__c);
51
+
52
+ // Check if we have available workers for this type
53
+ if (workersByType.containsKey(workerType) && !workersByType.get(workerType).isEmpty()) {
54
+ // Assign the first available worker (highest rated)
55
+ Maintenance_Worker__c assignedWorker = workersByType.get(workerType).get(0);
56
+ request.Assigned_Worker__c = assignedWorker.Id;
57
+ request.Status__c = 'Assigned';
58
+
59
+ // Set scheduled date to 3 days from now
60
+ request.Scheduled__c = DateTime.now().addDays(3);
61
+
62
+ // Rotate worker to end of list for round-robin assignment
63
+ workersByType.get(workerType).remove(0);
64
+ workersByType.get(workerType).add(assignedWorker);
65
+ }
66
+ }
67
+ }
68
+ }
69
+ }
@@ -0,0 +1,5 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
3
+ <apiVersion>66.0</apiVersion>
4
+ <status>Active</status>
5
+ </ApexClass>
@@ -0,0 +1,308 @@
1
+ /**
2
+ * Test class for MaintenanceRequestTriggerHandler
3
+ * Validates automatic worker assignment, status updates to Assigned, and scheduled date setting
4
+ */
5
+ @isTest
6
+ private class MaintenanceRequestTriggerHandler_Test {
7
+
8
+ /**
9
+ * Setup test data for all test methods
10
+ */
11
+ @testSetup
12
+ static void setupTestData() {
13
+ // Create test maintenance workers with different specialties
14
+ List<Maintenance_Worker__c> workers = new List<Maintenance_Worker__c>{
15
+ new Maintenance_Worker__c(
16
+ Name = 'John Plumber',
17
+ Type__c = 'Plumbing',
18
+ IsActive__c = true,
19
+ Rating__c = 4.5,
20
+ Phone__c = '555-0001'
21
+ ),
22
+ new Maintenance_Worker__c(
23
+ Name = 'Jane Electrician',
24
+ Type__c = 'Electrical',
25
+ IsActive__c = true,
26
+ Rating__c = 4.8,
27
+ Phone__c = '555-0002'
28
+ ),
29
+ new Maintenance_Worker__c(
30
+ Name = 'Bob HVAC',
31
+ Type__c = 'HVAC (Heating & Cooling)',
32
+ IsActive__c = true,
33
+ Rating__c = 4.2,
34
+ Phone__c = '555-0003'
35
+ ),
36
+ new Maintenance_Worker__c(
37
+ Name = 'Alice Appliance',
38
+ Type__c = 'Appliance Repair',
39
+ IsActive__c = true,
40
+ Rating__c = 4.7,
41
+ Phone__c = '555-0004'
42
+ ),
43
+ new Maintenance_Worker__c(
44
+ Name = 'Charlie Pest',
45
+ Type__c = 'Pest Control',
46
+ IsActive__c = true,
47
+ Rating__c = 4.3,
48
+ Phone__c = '555-0005'
49
+ ),
50
+ new Maintenance_Worker__c(
51
+ Name = 'Inactive Worker',
52
+ Type__c = 'Plumbing',
53
+ IsActive__c = false,
54
+ Rating__c = 5.0,
55
+ Phone__c = '555-0006'
56
+ )
57
+ };
58
+ insert workers;
59
+ }
60
+
61
+ /**
62
+ * Test that plumbing requests are assigned to plumbing workers
63
+ */
64
+ @isTest
65
+ static void testPlumbingRequestAssignment() {
66
+ // Query for plumbing worker
67
+ Maintenance_Worker__c plumber = [SELECT Id FROM Maintenance_Worker__c WHERE Type__c = 'Plumbing' AND IsActive__c = true LIMIT 1];
68
+
69
+ Test.startTest();
70
+ Maintenance_Request__c request = new Maintenance_Request__c(
71
+ Type__c = 'Plumbing',
72
+ Description__c = 'Leaky faucet in bathroom',
73
+ Priority__c = 'Standard'
74
+ );
75
+ insert request;
76
+ Test.stopTest();
77
+
78
+ // Verify assignment
79
+ Maintenance_Request__c insertedRequest = [
80
+ SELECT Id, Assigned_Worker__c, Status__c, Scheduled__c, Type__c
81
+ FROM Maintenance_Request__c
82
+ WHERE Id = :request.Id
83
+ ];
84
+
85
+ System.assertEquals(plumber.Id, insertedRequest.Assigned_Worker__c, 'Worker should be assigned');
86
+ System.assertEquals('Assigned', insertedRequest.Status__c, 'Status should be Assigned');
87
+ System.assertNotEquals(null, insertedRequest.Scheduled__c, 'Scheduled date should be set');
88
+
89
+ // Verify scheduled date is approximately 3 days from now (within 1 minute tolerance)
90
+ DateTime expectedDate = DateTime.now().addDays(3);
91
+ Long timeDiff = Math.abs(insertedRequest.Scheduled__c.getTime() - expectedDate.getTime());
92
+ System.assert(timeDiff < 60000, 'Scheduled date should be 3 days from now');
93
+ }
94
+
95
+ /**
96
+ * Test that electrical requests are assigned to electrical workers
97
+ */
98
+ @isTest
99
+ static void testElectricalRequestAssignment() {
100
+ Maintenance_Worker__c electrician = [SELECT Id FROM Maintenance_Worker__c WHERE Type__c = 'Electrical' AND IsActive__c = true LIMIT 1];
101
+
102
+ Test.startTest();
103
+ Maintenance_Request__c request = new Maintenance_Request__c(
104
+ Type__c = 'Electrical',
105
+ Description__c = 'Outlet not working',
106
+ Priority__c = 'High (Same Day)'
107
+ );
108
+ insert request;
109
+ Test.stopTest();
110
+
111
+ Maintenance_Request__c insertedRequest = [
112
+ SELECT Id, Assigned_Worker__c, Status__c
113
+ FROM Maintenance_Request__c
114
+ WHERE Id = :request.Id
115
+ ];
116
+
117
+ System.assertEquals(electrician.Id, insertedRequest.Assigned_Worker__c, 'Electrician should be assigned');
118
+ System.assertEquals('Assigned', insertedRequest.Status__c, 'Status should be Assigned');
119
+ }
120
+
121
+ /**
122
+ * Test that HVAC requests are assigned to HVAC workers
123
+ */
124
+ @isTest
125
+ static void testHVACRequestAssignment() {
126
+ Maintenance_Worker__c hvacWorker = [SELECT Id FROM Maintenance_Worker__c WHERE Type__c = 'HVAC (Heating & Cooling)' AND IsActive__c = true LIMIT 1];
127
+
128
+ Test.startTest();
129
+ Maintenance_Request__c request = new Maintenance_Request__c(
130
+ Type__c = 'HVAC',
131
+ Description__c = 'AC not cooling',
132
+ Priority__c = 'High (Same Day)'
133
+ );
134
+ insert request;
135
+ Test.stopTest();
136
+
137
+ Maintenance_Request__c insertedRequest = [
138
+ SELECT Id, Assigned_Worker__c, Status__c
139
+ FROM Maintenance_Request__c
140
+ WHERE Id = :request.Id
141
+ ];
142
+
143
+ System.assertEquals(hvacWorker.Id, insertedRequest.Assigned_Worker__c, 'HVAC worker should be assigned');
144
+ System.assertEquals('Assigned', insertedRequest.Status__c, 'Status should be Assigned');
145
+ }
146
+
147
+ /**
148
+ * Test that appliance requests are assigned to appliance repair workers
149
+ */
150
+ @isTest
151
+ static void testApplianceRequestAssignment() {
152
+ Maintenance_Worker__c applianceWorker = [SELECT Id FROM Maintenance_Worker__c WHERE Type__c = 'Appliance Repair' AND IsActive__c = true LIMIT 1];
153
+
154
+ Test.startTest();
155
+ Maintenance_Request__c request = new Maintenance_Request__c(
156
+ Type__c = 'Appliance',
157
+ Description__c = 'Dishwasher not draining',
158
+ Priority__c = 'Standard'
159
+ );
160
+ insert request;
161
+ Test.stopTest();
162
+
163
+ Maintenance_Request__c insertedRequest = [
164
+ SELECT Id, Assigned_Worker__c, Status__c
165
+ FROM Maintenance_Request__c
166
+ WHERE Id = :request.Id
167
+ ];
168
+
169
+ System.assertEquals(applianceWorker.Id, insertedRequest.Assigned_Worker__c, 'Appliance worker should be assigned');
170
+ System.assertEquals('Assigned', insertedRequest.Status__c, 'Status should be Assigned');
171
+ }
172
+
173
+ /**
174
+ * Test that pest requests are assigned to pest control workers
175
+ */
176
+ @isTest
177
+ static void testPestRequestAssignment() {
178
+ Maintenance_Worker__c pestWorker = [SELECT Id FROM Maintenance_Worker__c WHERE Type__c = 'Pest Control' AND IsActive__c = true LIMIT 1];
179
+
180
+ Test.startTest();
181
+ Maintenance_Request__c request = new Maintenance_Request__c(
182
+ Type__c = 'Pest',
183
+ Description__c = 'Ants in kitchen',
184
+ Priority__c = 'Standard'
185
+ );
186
+ insert request;
187
+ Test.stopTest();
188
+
189
+ Maintenance_Request__c insertedRequest = [
190
+ SELECT Id, Assigned_Worker__c, Status__c
191
+ FROM Maintenance_Request__c
192
+ WHERE Id = :request.Id
193
+ ];
194
+
195
+ System.assertEquals(pestWorker.Id, insertedRequest.Assigned_Worker__c, 'Pest control worker should be assigned');
196
+ System.assertEquals('Assigned', insertedRequest.Status__c, 'Status should be Assigned');
197
+ }
198
+
199
+ /**
200
+ * Test bulk insert with multiple request types
201
+ */
202
+ @isTest
203
+ static void testBulkRequestAssignment() {
204
+ Test.startTest();
205
+ List<Maintenance_Request__c> requests = new List<Maintenance_Request__c>{
206
+ new Maintenance_Request__c(Type__c = 'Plumbing', Description__c = 'Leak 1', Priority__c = 'High (Same Day)'),
207
+ new Maintenance_Request__c(Type__c = 'Electrical', Description__c = 'Issue 1', Priority__c = 'Standard'),
208
+ new Maintenance_Request__c(Type__c = 'Plumbing', Description__c = 'Leak 2', Priority__c = 'Standard'),
209
+ new Maintenance_Request__c(Type__c = 'HVAC', Description__c = 'AC Issue', Priority__c = 'High (Same Day)'),
210
+ new Maintenance_Request__c(Type__c = 'Appliance', Description__c = 'Fridge Issue', Priority__c = 'Standard')
211
+ };
212
+ insert requests;
213
+ Test.stopTest();
214
+
215
+ List<Maintenance_Request__c> insertedRequests = [
216
+ SELECT Id, Assigned_Worker__c, Status__c, Type__c
217
+ FROM Maintenance_Request__c
218
+ WHERE Id IN :requests
219
+ ];
220
+
221
+ // Verify all requests were assigned
222
+ for (Maintenance_Request__c req : insertedRequests) {
223
+ System.assertNotEquals(null, req.Assigned_Worker__c, 'All requests should have assigned workers');
224
+ System.assertEquals('Assigned', req.Status__c, 'All requests should be Assigned');
225
+ }
226
+
227
+ System.assertEquals(5, insertedRequests.size(), 'All 5 requests should be inserted');
228
+ }
229
+
230
+ /**
231
+ * Test that requests without type are not assigned
232
+ */
233
+ @isTest
234
+ static void testRequestWithoutType() {
235
+ Test.startTest();
236
+ Maintenance_Request__c request = new Maintenance_Request__c(
237
+ Description__c = 'General maintenance',
238
+ Priority__c = 'Standard'
239
+ );
240
+ insert request;
241
+ Test.stopTest();
242
+
243
+ Maintenance_Request__c insertedRequest = [
244
+ SELECT Id, Assigned_Worker__c, Status__c
245
+ FROM Maintenance_Request__c
246
+ WHERE Id = :request.Id
247
+ ];
248
+
249
+ System.assertEquals(null, insertedRequest.Assigned_Worker__c, 'Worker should not be assigned without type');
250
+ }
251
+
252
+ /**
253
+ * Test that inactive workers are not assigned
254
+ */
255
+ @isTest
256
+ static void testInactiveWorkerNotAssigned() {
257
+ // Delete all active plumbing workers
258
+ delete [SELECT Id FROM Maintenance_Worker__c WHERE Type__c = 'Plumbing' AND IsActive__c = true];
259
+
260
+ Test.startTest();
261
+ Maintenance_Request__c request = new Maintenance_Request__c(
262
+ Type__c = 'Plumbing',
263
+ Description__c = 'Emergency leak',
264
+ Priority__c = 'High (Same Day)'
265
+ );
266
+ insert request;
267
+ Test.stopTest();
268
+
269
+ Maintenance_Request__c insertedRequest = [
270
+ SELECT Id, Assigned_Worker__c, Status__c
271
+ FROM Maintenance_Request__c
272
+ WHERE Id = :request.Id
273
+ ];
274
+
275
+ System.assertEquals(null, insertedRequest.Assigned_Worker__c, 'Inactive worker should not be assigned');
276
+ }
277
+
278
+ /**
279
+ * Test that requests with non-New status are not automatically processed
280
+ */
281
+ @isTest
282
+ static void testNonNewStatusNotProcessed() {
283
+ // Query for plumbing worker
284
+ Maintenance_Worker__c plumber = [SELECT Id FROM Maintenance_Worker__c WHERE Type__c = 'Plumbing' AND IsActive__c = true LIMIT 1];
285
+
286
+ Test.startTest();
287
+ // Create a request with status explicitly set to 'In Progress'
288
+ Maintenance_Request__c request = new Maintenance_Request__c(
289
+ Type__c = 'Plumbing',
290
+ Description__c = 'Manually assigned request',
291
+ Priority__c = 'Standard',
292
+ Status__c = 'In Progress'
293
+ );
294
+ insert request;
295
+ Test.stopTest();
296
+
297
+ // Verify the request was not processed by trigger (no worker assigned)
298
+ Maintenance_Request__c insertedRequest = [
299
+ SELECT Id, Assigned_Worker__c, Status__c, Scheduled__c
300
+ FROM Maintenance_Request__c
301
+ WHERE Id = :request.Id
302
+ ];
303
+
304
+ System.assertEquals(null, insertedRequest.Assigned_Worker__c, 'Worker should not be auto-assigned for non-New status');
305
+ System.assertEquals('In Progress', insertedRequest.Status__c, 'Status should remain as originally set');
306
+ System.assertEquals(null, insertedRequest.Scheduled__c, 'Scheduled date should not be set by trigger');
307
+ }
308
+ }
@@ -0,0 +1,5 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
3
+ <apiVersion>66.0</apiVersion>
4
+ <status>Active</status>
5
+ </ApexClass>
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Assigns Tenant_Maintenance_Access permission set to users linked to Tenant__c records.
3
+ * When a Tenant is created or updated with User__c set, the linked user gets the permission set.
4
+ */
5
+ public with sharing class TenantTriggerHandler {
6
+
7
+ private static final String PERMISSION_SET_NAME = 'Tenant_Maintenance_Access';
8
+
9
+ /**
10
+ * Assign Tenant_Maintenance_Access to each user linked via Tenant__c.User__c.
11
+ * Insert: all records with User__c set.
12
+ * Update: records where User__c was set or changed to a non-null value.
13
+ */
14
+ public static void assignTenantMaintenanceAccess(List<Tenant__c> newList, Map<Id, Tenant__c> oldMap) {
15
+ Set<Id> userIds = new Set<Id>();
16
+ for (Tenant__c t : newList) {
17
+ if (t.User__c == null) {
18
+ continue;
19
+ }
20
+ if (oldMap == null) {
21
+ userIds.add(t.User__c);
22
+ } else {
23
+ Tenant__c oldRec = oldMap.get(t.Id);
24
+ if (oldRec == null || oldRec.User__c != t.User__c) {
25
+ userIds.add(t.User__c);
26
+ }
27
+ }
28
+ }
29
+ if (userIds.isEmpty()) {
30
+ return;
31
+ }
32
+ assignTenantMaintenanceAccessAsync(new List<Id>(userIds));
33
+ }
34
+
35
+ @future
36
+ private static void assignTenantMaintenanceAccessAsync(List<Id> userIdsList) {
37
+ Set<Id> userIds = new Set<Id>(userIdsList);
38
+ if (userIds.isEmpty()) {
39
+ return;
40
+ }
41
+ List<PermissionSet> permSets = [
42
+ SELECT Id
43
+ FROM PermissionSet
44
+ WHERE Name = :PERMISSION_SET_NAME
45
+ AND IsOwnedByProfile = false
46
+ LIMIT 1
47
+ ];
48
+ if (permSets.isEmpty()) {
49
+ return;
50
+ }
51
+ Id permSetId = permSets[0].Id;
52
+
53
+ Set<Id> alreadyAssigned = new Set<Id>();
54
+ for (PermissionSetAssignment psa : [
55
+ SELECT AssigneeId
56
+ FROM PermissionSetAssignment
57
+ WHERE PermissionSetId = :permSetId
58
+ AND AssigneeId IN :userIds
59
+ ]) {
60
+ alreadyAssigned.add(psa.AssigneeId);
61
+ }
62
+
63
+ List<PermissionSetAssignment> toInsert = new List<PermissionSetAssignment>();
64
+ for (Id uid : userIds) {
65
+ if (alreadyAssigned.contains(uid)) {
66
+ continue;
67
+ }
68
+ toInsert.add(new PermissionSetAssignment(
69
+ PermissionSetId = permSetId,
70
+ AssigneeId = uid
71
+ ));
72
+ }
73
+ if (!toInsert.isEmpty()) {
74
+ insert toInsert;
75
+ }
76
+ }
77
+ }
@@ -0,0 +1,5 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
3
+ <apiVersion>66.0</apiVersion>
4
+ <status>Active</status>
5
+ </ApexClass>
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Test class for TenantTriggerHandler.
3
+ * Verifies that Tenant_Maintenance_Access permission set is assigned to the user
4
+ * when a Tenant__c record is created or updated with User__c set.
5
+ */
6
+ @isTest
7
+ private class TenantTriggerHandler_Test {
8
+
9
+ @isTest
10
+ static void testAssignOnInsert() {
11
+ Id runAsUserId = UserInfo.getUserId();
12
+ Tenant__c tenant = new Tenant__c(User__c = runAsUserId);
13
+
14
+ Test.startTest();
15
+ insert tenant;
16
+ Test.stopTest();
17
+
18
+ List<PermissionSet> permSets = [
19
+ SELECT Id FROM PermissionSet
20
+ WHERE Name = 'Tenant_Maintenance_Access'
21
+ AND IsOwnedByProfile = false
22
+ LIMIT 1
23
+ ];
24
+ if (!permSets.isEmpty()) {
25
+ List<PermissionSetAssignment> assignments = [
26
+ SELECT Id, AssigneeId, PermissionSetId
27
+ FROM PermissionSetAssignment
28
+ WHERE PermissionSetId = :permSets[0].Id
29
+ AND AssigneeId = :runAsUserId
30
+ ];
31
+ System.assert(assignments.size() == 1, 'Permission set should be assigned to user when Tenant is created with User__c');
32
+ }
33
+ }
34
+
35
+ @isTest
36
+ static void testAssignOnUpdateWhenUserSet() {
37
+ Tenant__c tenant = new Tenant__c();
38
+ insert tenant;
39
+
40
+ Id runAsUserId = UserInfo.getUserId();
41
+
42
+ Test.startTest();
43
+ tenant.User__c = runAsUserId;
44
+ update tenant;
45
+ Test.stopTest();
46
+
47
+ List<PermissionSet> permSets = [
48
+ SELECT Id FROM PermissionSet
49
+ WHERE Name = 'Tenant_Maintenance_Access'
50
+ AND IsOwnedByProfile = false
51
+ LIMIT 1
52
+ ];
53
+ if (!permSets.isEmpty()) {
54
+ List<PermissionSetAssignment> assignments = [
55
+ SELECT Id FROM PermissionSetAssignment
56
+ WHERE PermissionSetId = :permSets[0].Id
57
+ AND AssigneeId = :runAsUserId
58
+ ];
59
+ System.assert(assignments.size() == 1, 'Permission set should be assigned when User__c is set on update');
60
+ }
61
+ }
62
+
63
+ @isTest
64
+ static void testNoAssignWhenUserNull() {
65
+ Tenant__c tenant = new Tenant__c();
66
+ Test.startTest();
67
+ insert tenant;
68
+ Test.stopTest();
69
+ // Handler does not assign when User__c is null; trigger runs without error
70
+ }
71
+
72
+ @isTest
73
+ static void testNoDuplicateAssign() {
74
+ Id runAsUserId = UserInfo.getUserId();
75
+ Tenant__c tenant = new Tenant__c(User__c = runAsUserId);
76
+ Test.startTest();
77
+ insert tenant;
78
+ Test.stopTest();
79
+
80
+ Integer countBefore = [
81
+ SELECT COUNT()
82
+ FROM PermissionSetAssignment
83
+ WHERE AssigneeId = :runAsUserId
84
+ AND PermissionSet.Name = 'Tenant_Maintenance_Access'
85
+ ];
86
+
87
+ Test.startTest();
88
+ tenant.Status__c = 'Active';
89
+ update tenant;
90
+ Test.stopTest();
91
+
92
+ Integer countAfter = [
93
+ SELECT COUNT()
94
+ FROM PermissionSetAssignment
95
+ WHERE AssigneeId = :runAsUserId
96
+ AND PermissionSet.Name = 'Tenant_Maintenance_Access'
97
+ ];
98
+ System.assertEquals(countBefore, countAfter, 'Update without User__c change should not create duplicate assignment');
99
+ }
100
+ }