fork download
  1. public with sharing class TimesheetController {
  2.  
  3. // ------------------
  4. // CONFIGEDIT ME
  5. // ------------------
  6. private static final String TIMELOG_SOBJ = 'Time__c';
  7. private static final String F_PROJECT = 'Project__c';
  8. private static final String F_MILESTONE = 'Milestone__c';
  9. private static final String F_TASK = 'Project_Task__c';
  10. private static final String F_WORKDATE = 'Request_Date__c';
  11. private static final String F_MINUTES = 'Hours__c';
  12. private static final String F_DESC = 'Description__c';
  13. private static final String F_BILLING = 'Billing_Type__c';
  14. private static final String F_User = 'User__c';
  15.  
  16. private static final String TASK_SOBJ = 'Project_Task__c';
  17. private static final String TASK_F_PROJECT = 'Project__c';
  18. private static final String TASK_F_MILESTONE = 'Milestone__c';
  19.  
  20. // ------------------
  21. // DTOs
  22. // ------------------
  23. public class InitResponse {
  24. @AuraEnabled public String weekStartIso; // yyyy-MM-dd (Sunday)
  25. @AuraEnabled public List<DateLabel> dates; // 7 days of the week
  26. @AuraEnabled public Map<String,Object> prefillRow; // optional row prefill
  27. }
  28. public class DateLabel {
  29. @AuraEnabled public String iso; // yyyy-MM-dd
  30. @AuraEnabled public String label; // e.g., "Sep 21 Sun"
  31. }
  32.  
  33. // ------------------
  34. // INIT
  35. // ------------------
  36. @AuraEnabled(cacheable=true)
  37. public static InitResponse initData(Id taskId, String anchorISO) {
  38. InitResponse out = new InitResponse();
  39.  
  40. Date anchor = (String.isBlank(anchorISO)) ? Date.today() : Date.valueOf(anchorISO);
  41.  
  42. // Compute Sunday-of-week for the given anchor date
  43. Integer dow = dayOfWeek1to7(anchor); // 1=Sun .. 7=Sat
  44. Integer back = dow - 1; // how many days to step back to Sunday
  45. Date weekStart = anchor.addDays(-back);
  46.  
  47. out.weekStartIso = String.valueOf(weekStart);
  48.  
  49. // Build 7 day labels
  50. out.dates = new List<DateLabel>();
  51. for (Integer i = 0; i < 7; i++) {
  52. Date di = weekStart.addDays(i);
  53. DateTime dti = DateTime.newInstance(di.year(), di.month(), di.day(), 0, 0, 0);
  54.  
  55. DateLabel dl = new DateLabel();
  56. dl.iso = String.valueOf(di); // yyyy-MM-dd
  57. dl.label = dti.format('MMM dd') + ' ' + dti.format('EEE'); // "Sep 21 Sun"
  58. out.dates.add(dl);
  59. }
  60.  
  61. // Prefill from clicked Project Task
  62. if (taskId != null) {
  63. SObject sob = Database.query(
  64. 'SELECT Id,' + TASK_F_PROJECT + ',' + TASK_F_MILESTONE +
  65. ' FROM ' + TASK_SOBJ + ' WHERE Id = :taskId LIMIT 1'
  66. );
  67.  
  68. Map<String,Object> row = new Map<String,Object>();
  69. row.put('projectId', (Id) sob.get(TASK_F_PROJECT));
  70. row.put('milestoneId', (Id) sob.get(TASK_F_MILESTONE));
  71. row.put('taskId', taskId);
  72. row.put('billable', 'Billable');
  73. row.put('projectOptions', new List<Object>());
  74. row.put('milestoneOptions', new List<Object>());
  75. row.put('taskOptions', new List<Object>());
  76. row.put('hours', new Map<String,String>{
  77. 'd0'=>'00:00','d1'=>'00:00','d2'=>'00:00','d3'=>'00:00',
  78. 'd4'=>'00:00','d5'=>'00:00','d6'=>'00:00'
  79. });
  80. row.put('desc', new Map<String,String>());
  81. row.put('descButtonLabel', 'Add Description');
  82. row.put('total', '00:00');
  83. out.prefillRow = row;
  84. }
  85. return out;
  86. }
  87.  
  88. // ------------------
  89. // LOOKUP SEARCH
  90. // ------------------
  91. @AuraEnabled(cacheable=true)
  92. public static List<SObject> searchProjects(String q) {
  93. String likeQ = String.isBlank(q) ? '%' : '%' + escapeLike(q) + '%';
  94. return Database.query(
  95. 'SELECT Id, Name FROM Project__c WHERE Name LIKE :likeQ ORDER BY Name LIMIT 50'
  96. );
  97. }
  98.  
  99.  
  100. @AuraEnabled(cacheable=true)
  101. public static Integer getEditableDaysCount() {
  102. try {
  103. String labelValue = System.Label.Weekly_Timesheet_Days;
  104. return String.isNotBlank(labelValue) ? Integer.valueOf(labelValue) : 0;
  105. } catch (Exception e) {
  106. System.debug('Error retrieving Weekly_Timesheet_Days label: ' + e.getMessage());
  107. return 0;
  108. }
  109. }
  110.  
  111. @AuraEnabled(cacheable=true)
  112. public static List<SObject> searchMilestones(Id projectId, String q) {
  113. if (projectId == null) return new List<SObject>();
  114. String likeQ = String.isBlank(q) ? '%' : '%' + escapeLike(q) + '%';
  115. return Database.query(
  116. 'SELECT Id, Name FROM Milestone__c ' +
  117. 'WHERE Project__c = :projectId AND Name LIKE :likeQ ' +
  118. 'ORDER BY Name LIMIT 50'
  119. );
  120. }
  121.  
  122. @AuraEnabled(cacheable=true)
  123. public static List<SObject> searchTasks(Id projectId, Id milestoneId, String q) {
  124. if (projectId == null) return new List<SObject>();
  125. String likeQ = String.isBlank(q) ? '%' : '%' + escapeLike(q) + '%';
  126. String soql = 'SELECT Id, Name,Billing_Type__c FROM ' + TASK_SOBJ +
  127. ' WHERE ' + TASK_F_PROJECT + ' = :projectId';
  128. if (milestoneId != null) {
  129. soql += ' AND ' + TASK_F_MILESTONE + ' = :milestoneId';
  130. }
  131. soql += ' AND Name LIKE :likeQ ORDER BY Name LIMIT 50';
  132. return Database.query(soql);
  133. }
  134.  
  135. private static String escapeLike(String s) {
  136. return s.replace('\\','\\\\').replace('%','\\%').replace('_','\\_');
  137. }
  138.  
  139. // ------------------
  140. // SAVE
  141. // ------------------
  142. public class UIRow {
  143. public Id projectId;
  144. public Id milestoneId;
  145. public Id taskId;
  146. public String billable; // 'Billable' | 'Non-Billable'
  147. public List<Entry> entries;
  148. }
  149. public class Entry {
  150. public String iso; // yyyy-MM-dd
  151. public String hhmm; // "HH:mm"
  152. public String description;
  153. }
  154.  
  155. @AuraEnabled
  156. public static void saveWeek(String weekStartISO, String rowsJSON) {
  157. if (String.isBlank(rowsJSON)) return;
  158.  
  159. List<UIRow> rows = (List<UIRow>) JSON.deserialize(rowsJSON, List<UIRow>.class);
  160. List<SObject> inserts = new List<SObject>();
  161.  
  162. for (UIRow r : rows) {
  163. if (r == null || r.entries == null) continue;
  164.  
  165. for (Entry e : r.entries) {
  166. if (e == null) continue;
  167. Decimal mins = Decimal.valueOf(e.hhmm.replace(':', '.'));
  168. if (mins == 0) continue;
  169.  
  170. SObject tl = Schema.getGlobalDescribe().get(TIMELOG_SOBJ).newSObject();
  171. // if (r.projectId != null) tl.put(F_PROJECT, r.projectId);
  172. // if (r.milestoneId != null) tl.put(F_MILESTONE, r.milestoneId);
  173. if (r.taskId != null) tl.put(F_TASK,r.taskId);
  174.  
  175. tl.put(F_WORKDATE, Date.valueOf(e.iso));
  176. tl.put(F_MINUTES, mins);
  177. if (!String.isBlank(e.description)) tl.put(F_DESC, e.description);
  178. if (!String.isBlank(r.billable)) {
  179. tl.put(F_BILLING, r.billable);
  180. tl.put(F_User, UserInfo.getUserId());
  181. }
  182.  
  183. inserts.add(tl);
  184. }
  185. }
  186. if (!inserts.isEmpty()) insert inserts;
  187. }
  188.  
  189. private static Integer hhmmToMinutes(String hhmm) {
  190. if (String.isBlank(hhmm) || !hhmm.contains(':')) return 0;
  191. List<String> parts = hhmm.split(':');
  192. return Integer.valueOf(parts[0]) * 60 + Integer.valueOf(parts[1]);
  193. }
  194.  
  195. // ------------------
  196. // Helpers
  197. // ------------------
  198. // Returns 1..7 for Sun..Sat using DateTime.format('E') to derive weekday safely
  199. private static Integer dayOfWeek1to7(Date d) {
  200. DateTime dt = DateTime.newInstance(d.year(), d.month(), d.day(), 0, 0, 0);
  201. String name = dt.format('EEE'); // Sun, Mon, Tue, ...
  202. if (name == 'Sun') return 1;
  203. else if (name == 'Mon') return 2;
  204. else if (name == 'Tue') return 3;
  205. else if (name == 'Wed') return 4;
  206. else if (name == 'Thu') return 5;
  207. else if (name == 'Fri') return 6;
  208. else return 7; // Sat
  209. }
  210. }
Success #stdin #stdout #stderr 0.02s 12376KB
stdin
Standard input is empty
stdout
Object: nil error: did not understand #with
MessageNotUnderstood(Exception)>>signal (ExcHandling.st:254)
UndefinedObject(Object)>>doesNotUnderstand: #with (SysExcept.st:1448)
UndefinedObject>>executeStatements (prog:1)
Object: nil error: did not understand #associationAt:ifAbsent:
MessageNotUnderstood(Exception)>>signal (ExcHandling.st:254)
UndefinedObject(Object)>>doesNotUnderstand: #associationAt:ifAbsent: (SysExcept.st:1448)
DeferredVariableBinding>>resolvePathFrom: (DeferBinding.st:115)
DeferredVariableBinding>>value (DeferBinding.st:69)
UndefinedObject>>executeStatements (prog:177)
Object: String error: did not understand #associationAt:ifAbsent:
MessageNotUnderstood(Exception)>>signal (ExcHandling.st:254)
String class(Object)>>doesNotUnderstand: #associationAt:ifAbsent: (SysExcept.st:1448)
DeferredVariableBinding>>resolvePathFrom: (DeferBinding.st:115)
DeferredVariableBinding>>value (DeferBinding.st:69)
UndefinedObject>>executeStatements (prog:178)
Object: nil error: did not understand #associationAt:ifAbsent:
MessageNotUnderstood(Exception)>>signal (ExcHandling.st:254)
UndefinedObject(Object)>>doesNotUnderstand: #associationAt:ifAbsent: (SysExcept.st:1448)
DeferredVariableBinding>>resolvePathFrom: (DeferBinding.st:115)
DeferredVariableBinding>>value (DeferBinding.st:69)
UndefinedObject>>executeStatements (prog:178)
Object: nil error: did not understand #associationAt:ifAbsent:
MessageNotUnderstood(Exception)>>signal (ExcHandling.st:254)
UndefinedObject(Object)>>doesNotUnderstand: #associationAt:ifAbsent: (SysExcept.st:1448)
DeferredVariableBinding>>resolvePathFrom: (DeferBinding.st:115)
DeferredVariableBinding>>value (DeferBinding.st:69)
UndefinedObject>>executeStatements (prog:186)
Object: nil error: did not understand #associationAt:ifAbsent:
MessageNotUnderstood(Exception)>>signal (ExcHandling.st:254)
UndefinedObject(Object)>>doesNotUnderstand: #associationAt:ifAbsent: (SysExcept.st:1448)
DeferredVariableBinding>>resolvePathFrom: (DeferBinding.st:115)
DeferredVariableBinding>>value (DeferBinding.st:69)
UndefinedObject>>executeStatements (prog:190)
stderr
./prog:3: parse error, expected '}'
./prog:4: Invalid character 0xe2
./prog:4: Invalid character 0x80
./prog:4: Invalid character 0x93
./prog:62: expected expression
./prog:128: expected expression
./prog:171: expected expression
./prog:172: expected expression
./prog:173: expected expression
./prog:177: expected expression
./prog:178: expected expression
./prog:186: expected expression
./prog:190: expected expression