@hed-hog/operations 0.0.321 → 0.0.325

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 (701) hide show
  1. package/dist/controllers/operations-collaborators.controller.d.ts +9 -0
  2. package/dist/controllers/operations-collaborators.controller.d.ts.map +1 -1
  3. package/dist/controllers/operations-collaborators.controller.js +25 -0
  4. package/dist/controllers/operations-collaborators.controller.js.map +1 -1
  5. package/dist/controllers/operations-contracts.controller.d.ts +9 -9
  6. package/dist/controllers/operations-project-costs.controller.d.ts +422 -0
  7. package/dist/controllers/operations-project-costs.controller.d.ts.map +1 -0
  8. package/dist/controllers/operations-project-costs.controller.js +250 -0
  9. package/dist/controllers/operations-project-costs.controller.js.map +1 -0
  10. package/dist/controllers/operations-reports.controller.d.ts +9 -0
  11. package/dist/controllers/operations-reports.controller.d.ts.map +1 -1
  12. package/dist/controllers/operations-tasks.controller.d.ts +64 -0
  13. package/dist/controllers/operations-tasks.controller.d.ts.map +1 -1
  14. package/dist/controllers/operations-tasks.controller.js +85 -0
  15. package/dist/controllers/operations-tasks.controller.js.map +1 -1
  16. package/dist/controllers/operations-timesheets.controller.d.ts +1 -0
  17. package/dist/controllers/operations-timesheets.controller.d.ts.map +1 -1
  18. package/dist/dto/create-collaborator-project-assignment.dto.d.ts +5 -0
  19. package/dist/dto/create-collaborator-project-assignment.dto.d.ts.map +1 -0
  20. package/dist/dto/create-collaborator-project-assignment.dto.js +30 -0
  21. package/dist/dto/create-collaborator-project-assignment.dto.js.map +1 -0
  22. package/dist/dto/create-project-cost-category.dto.d.ts +10 -0
  23. package/dist/dto/create-project-cost-category.dto.d.ts.map +1 -0
  24. package/dist/dto/create-project-cost-category.dto.js +59 -0
  25. package/dist/dto/create-project-cost-category.dto.js.map +1 -0
  26. package/dist/dto/create-project-cost-type.dto.d.ts +14 -0
  27. package/dist/dto/create-project-cost-type.dto.d.ts.map +1 -0
  28. package/dist/dto/create-project-cost-type.dto.js +87 -0
  29. package/dist/dto/create-project-cost-type.dto.js.map +1 -0
  30. package/dist/dto/create-project-cost.dto.d.ts +22 -0
  31. package/dist/dto/create-project-cost.dto.d.ts.map +1 -0
  32. package/dist/dto/create-project-cost.dto.js +135 -0
  33. package/dist/dto/create-project-cost.dto.js.map +1 -0
  34. package/dist/dto/create-task.dto.d.ts.map +1 -1
  35. package/dist/dto/create-task.dto.js +0 -1
  36. package/dist/dto/create-task.dto.js.map +1 -1
  37. package/dist/dto/get-project-cost-report.dto.d.ts +10 -0
  38. package/dist/dto/get-project-cost-report.dto.d.ts.map +1 -0
  39. package/dist/dto/get-project-cost-report.dto.js +65 -0
  40. package/dist/dto/get-project-cost-report.dto.js.map +1 -0
  41. package/dist/dto/list-project-cost-categories.dto.d.ts +6 -0
  42. package/dist/dto/list-project-cost-categories.dto.d.ts.map +1 -0
  43. package/dist/dto/list-project-cost-categories.dto.js +34 -0
  44. package/dist/dto/list-project-cost-categories.dto.js.map +1 -0
  45. package/dist/dto/list-project-cost-types.dto.d.ts +8 -0
  46. package/dist/dto/list-project-cost-types.dto.d.ts.map +1 -0
  47. package/dist/dto/list-project-cost-types.dto.js +45 -0
  48. package/dist/dto/list-project-cost-types.dto.js.map +1 -0
  49. package/dist/dto/list-project-costs.dto.d.ts +14 -0
  50. package/dist/dto/list-project-costs.dto.d.ts.map +1 -0
  51. package/dist/dto/list-project-costs.dto.js +81 -0
  52. package/dist/dto/list-project-costs.dto.js.map +1 -0
  53. package/dist/dto/list-tasks.dto.d.ts +1 -0
  54. package/dist/dto/list-tasks.dto.d.ts.map +1 -1
  55. package/dist/dto/list-tasks.dto.js +6 -0
  56. package/dist/dto/list-tasks.dto.js.map +1 -1
  57. package/dist/dto/list-timesheets.dto.d.ts +1 -0
  58. package/dist/dto/list-timesheets.dto.d.ts.map +1 -1
  59. package/dist/dto/list-timesheets.dto.js +7 -0
  60. package/dist/dto/list-timesheets.dto.js.map +1 -1
  61. package/dist/dto/update-collaborator-project-assignment.dto.d.ts +11 -0
  62. package/dist/dto/update-collaborator-project-assignment.dto.d.ts.map +1 -0
  63. package/dist/dto/update-collaborator-project-assignment.dto.js +65 -0
  64. package/dist/dto/update-collaborator-project-assignment.dto.js.map +1 -0
  65. package/dist/dto/update-project-cost-category.dto.d.ts +6 -0
  66. package/dist/dto/update-project-cost-category.dto.d.ts.map +1 -0
  67. package/dist/dto/update-project-cost-category.dto.js +9 -0
  68. package/dist/dto/update-project-cost-category.dto.js.map +1 -0
  69. package/dist/dto/update-project-cost-type.dto.d.ts +6 -0
  70. package/dist/dto/update-project-cost-type.dto.d.ts.map +1 -0
  71. package/dist/dto/update-project-cost-type.dto.js +9 -0
  72. package/dist/dto/update-project-cost-type.dto.js.map +1 -0
  73. package/dist/dto/update-project-cost.dto.d.ts +6 -0
  74. package/dist/dto/update-project-cost.dto.d.ts.map +1 -0
  75. package/dist/dto/update-project-cost.dto.js +9 -0
  76. package/dist/dto/update-project-cost.dto.js.map +1 -0
  77. package/dist/dto/update-task.dto.d.ts.map +1 -1
  78. package/dist/dto/update-task.dto.js +0 -1
  79. package/dist/dto/update-task.dto.js.map +1 -1
  80. package/dist/operations.module.d.ts.map +1 -1
  81. package/dist/operations.module.js +2 -0
  82. package/dist/operations.module.js.map +1 -1
  83. package/dist/operations.service.d.ts +584 -0
  84. package/dist/operations.service.d.ts.map +1 -1
  85. package/dist/operations.service.js +1734 -69
  86. package/dist/operations.service.js.map +1 -1
  87. package/hedhog/data/menu.yaml +52 -0
  88. package/hedhog/data/operations_project_cost_category.yaml +80 -0
  89. package/hedhog/data/operations_project_cost_type.yaml +503 -0
  90. package/hedhog/data/route.yaml +313 -0
  91. package/hedhog/frontend/app/_components/collaborator-costs-section.tsx.ejs +2 -18
  92. package/hedhog/frontend/app/_components/collaborator-form-screen.tsx.ejs +185 -276
  93. package/hedhog/frontend/app/_components/collaborator-tasks-tab.tsx.ejs +358 -0
  94. package/hedhog/frontend/app/_components/collaborator-timesheets-tab.tsx.ejs +242 -0
  95. package/hedhog/frontend/app/_components/my-project-summary-screen.tsx.ejs +167 -59
  96. package/hedhog/frontend/app/_components/person-select-with-create.tsx.ejs +1 -826
  97. package/hedhog/frontend/app/_components/project-assignments-tab.tsx.ejs +450 -0
  98. package/hedhog/frontend/app/_components/project-cost-report-screen.tsx.ejs +602 -0
  99. package/hedhog/frontend/app/_components/project-costs-section.tsx.ejs +1401 -0
  100. package/hedhog/frontend/app/_components/project-details-screen.tsx.ejs +3390 -889
  101. package/hedhog/frontend/app/_components/project-form-screen.tsx.ejs +118 -79
  102. package/hedhog/frontend/app/_components/task-detail-sheet.tsx.ejs +297 -2
  103. package/hedhog/frontend/app/_components/task-file-attachments.tsx.ejs +388 -0
  104. package/hedhog/frontend/app/_components/task-form-sheet.tsx.ejs +530 -0
  105. package/hedhog/frontend/app/_lib/api.ts.ejs +247 -0
  106. package/hedhog/frontend/app/_lib/types.ts.ejs +197 -7
  107. package/hedhog/frontend/app/_lib/utils/format.ts.ejs +9 -3
  108. package/hedhog/frontend/app/collaborators/page.tsx.ejs +18 -7
  109. package/hedhog/frontend/app/my-tasks/page.tsx.ejs +340 -133
  110. package/hedhog/frontend/app/project-cost-categories/page.tsx.ejs +674 -0
  111. package/hedhog/frontend/app/project-cost-types/page.tsx.ejs +845 -0
  112. package/hedhog/frontend/app/projects/[id]/costs-report/page.tsx.ejs +10 -0
  113. package/hedhog/frontend/app/projects/page.tsx.ejs +105 -22
  114. package/hedhog/frontend/app/reports/collaborators/page.tsx.ejs +20 -349
  115. package/hedhog/frontend/app/reports/projects/page.tsx.ejs +192 -484
  116. package/hedhog/frontend/messages/en.json +421 -11
  117. package/hedhog/frontend/messages/en.json.ejs +2043 -0
  118. package/hedhog/frontend/messages/operations/en.json +2068 -0
  119. package/hedhog/frontend/messages/operations/operations/en.json +2102 -0
  120. package/hedhog/frontend/messages/operations/operations/pt.json +2111 -0
  121. package/hedhog/frontend/messages/operations/pt.json +2072 -0
  122. package/hedhog/frontend/messages/pt.json +426 -14
  123. package/hedhog/frontend/messages/pt.json.ejs +2056 -0
  124. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/async-options-combobox.d.ts +29 -0
  125. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/async-options-combobox.d.ts.map +1 -0
  126. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/async-options-combobox.js +95 -0
  127. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/async-options-combobox.js.map +1 -0
  128. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/async-options-combobox.tsx +233 -0
  129. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-costs-section.d.ts +10 -0
  130. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-costs-section.d.ts.map +1 -0
  131. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-costs-section.js +577 -0
  132. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-costs-section.js.map +1 -0
  133. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-costs-section.tsx +868 -0
  134. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-details-screen.d.ts +4 -0
  135. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-details-screen.d.ts.map +1 -0
  136. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-details-screen.js +337 -0
  137. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-details-screen.js.map +1 -0
  138. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-details-screen.tsx +476 -0
  139. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-form-screen.d.ts +9 -0
  140. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-form-screen.d.ts.map +1 -0
  141. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-form-screen.js +1348 -0
  142. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-form-screen.js.map +1 -0
  143. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-form-screen.tsx +2233 -0
  144. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-select-with-create.d.ts +12 -0
  145. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-select-with-create.d.ts.map +1 -0
  146. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-select-with-create.js +162 -0
  147. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-select-with-create.js.map +1 -0
  148. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-select-with-create.tsx +261 -0
  149. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-content-editor.d.ts +18 -0
  150. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-content-editor.d.ts.map +1 -0
  151. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-content-editor.js +145 -0
  152. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-content-editor.js.map +1 -0
  153. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-content-editor.tsx +258 -0
  154. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-details-screen.d.ts +4 -0
  155. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-details-screen.d.ts.map +1 -0
  156. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-details-screen.js +223 -0
  157. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-details-screen.js.map +1 -0
  158. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-details-screen.tsx +342 -0
  159. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-form-screen.d.ts +58 -0
  160. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-form-screen.d.ts.map +1 -0
  161. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-form-screen.js +438 -0
  162. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-form-screen.js.map +1 -0
  163. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-form-screen.tsx +698 -0
  164. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/department-select-with-create.d.ts +20 -0
  165. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/department-select-with-create.d.ts.map +1 -0
  166. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/department-select-with-create.js +233 -0
  167. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/department-select-with-create.js.map +1 -0
  168. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/department-select-with-create.tsx +392 -0
  169. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/my-project-summary-screen.d.ts +4 -0
  170. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/my-project-summary-screen.d.ts.map +1 -0
  171. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/my-project-summary-screen.js +814 -0
  172. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/my-project-summary-screen.js.map +1 -0
  173. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/my-project-summary-screen.tsx +1288 -0
  174. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/operations-calendar-view.d.ts +21 -0
  175. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/operations-calendar-view.d.ts.map +1 -0
  176. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/operations-calendar-view.js +174 -0
  177. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/operations-calendar-view.js.map +1 -0
  178. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/operations-calendar-view.tsx +306 -0
  179. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/operations-header.d.ts +10 -0
  180. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/operations-header.d.ts.map +1 -0
  181. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/operations-header.js +12 -0
  182. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/operations-header.js.map +1 -0
  183. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/operations-header.tsx +29 -0
  184. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/person-select-with-create.d.ts +15 -0
  185. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/person-select-with-create.d.ts.map +1 -0
  186. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/person-select-with-create.js +501 -0
  187. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/person-select-with-create.js.map +1 -0
  188. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/person-select-with-create.tsx +853 -0
  189. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-costs-section.d.ts +6 -0
  190. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-costs-section.d.ts.map +1 -0
  191. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-costs-section.js +847 -0
  192. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-costs-section.js.map +1 -0
  193. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-costs-section.tsx +1340 -0
  194. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-details-screen.d.ts +4 -0
  195. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-details-screen.d.ts.map +1 -0
  196. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-details-screen.js +2930 -0
  197. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-details-screen.js.map +1 -0
  198. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-details-screen.tsx +4378 -0
  199. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-form-screen.d.ts +9 -0
  200. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-form-screen.d.ts.map +1 -0
  201. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-form-screen.js +1013 -0
  202. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-form-screen.js.map +1 -0
  203. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-form-screen.tsx +1745 -0
  204. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/section-card.d.ts +13 -0
  205. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/section-card.d.ts.map +1 -0
  206. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/section-card.js +38 -0
  207. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/section-card.js.map +1 -0
  208. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/section-card.tsx +74 -0
  209. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/status-badge.d.ts +7 -0
  210. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/status-badge.d.ts.map +1 -0
  211. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/status-badge.js +11 -0
  212. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/status-badge.js.map +1 -0
  213. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/status-badge.tsx +15 -0
  214. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/system-user-select-with-create.d.ts +18 -0
  215. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/system-user-select-with-create.d.ts.map +1 -0
  216. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/system-user-select-with-create.js +406 -0
  217. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/system-user-select-with-create.js.map +1 -0
  218. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/system-user-select-with-create.tsx +660 -0
  219. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/task-detail-sheet.d.ts +26 -0
  220. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/task-detail-sheet.d.ts.map +1 -0
  221. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/task-detail-sheet.js +332 -0
  222. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/task-detail-sheet.js.map +1 -0
  223. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/task-detail-sheet.tsx +518 -0
  224. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/task-file-attachments.d.ts +6 -0
  225. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/task-file-attachments.d.ts.map +1 -0
  226. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/task-file-attachments.js +255 -0
  227. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/task-file-attachments.js.map +1 -0
  228. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/task-file-attachments.tsx +388 -0
  229. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/timesheet-task-create-sheet.d.ts +10 -0
  230. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/timesheet-task-create-sheet.d.ts.map +1 -0
  231. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/timesheet-task-create-sheet.js +131 -0
  232. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/timesheet-task-create-sheet.js.map +1 -0
  233. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/timesheet-task-create-sheet.tsx +214 -0
  234. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/api.d.ts +108 -0
  235. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/api.d.ts.map +1 -0
  236. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/api.js +162 -0
  237. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/api.js.map +1 -0
  238. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/api.ts +428 -0
  239. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/hooks/use-operations-access.d.ts +8 -0
  240. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/hooks/use-operations-access.d.ts.map +1 -0
  241. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/hooks/use-operations-access.js +36 -0
  242. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/hooks/use-operations-access.js.map +1 -0
  243. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/hooks/use-operations-access.ts +44 -0
  244. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/types.d.ts +836 -0
  245. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/types.d.ts.map +1 -0
  246. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/types.js +3 -0
  247. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/types.js.map +1 -0
  248. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/types.ts +860 -0
  249. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/utils/format.d.ts +16 -0
  250. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/utils/format.d.ts.map +1 -0
  251. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/utils/format.js +182 -0
  252. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/utils/format.js.map +1 -0
  253. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/utils/format.ts +250 -0
  254. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/utils/forms.d.ts +4 -0
  255. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/utils/forms.d.ts.map +1 -0
  256. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/utils/forms.js +51 -0
  257. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/utils/forms.js.map +1 -0
  258. package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/utils/forms.ts +61 -0
  259. package/hedhog/frontend/src/app/(app)/(libraries)/operations/approvals/page.d.ts +2 -0
  260. package/hedhog/frontend/src/app/(app)/(libraries)/operations/approvals/page.d.ts.map +1 -0
  261. package/hedhog/frontend/src/app/(app)/(libraries)/operations/approvals/page.js +954 -0
  262. package/hedhog/frontend/src/app/(app)/(libraries)/operations/approvals/page.js.map +1 -0
  263. package/hedhog/frontend/src/app/(app)/(libraries)/operations/approvals/page.tsx +1277 -0
  264. package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborator-types/page.d.ts +2 -0
  265. package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborator-types/page.d.ts.map +1 -0
  266. package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborator-types/page.js +488 -0
  267. package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborator-types/page.js.map +1 -0
  268. package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborator-types/page.tsx +805 -0
  269. package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/[id]/edit/page.d.ts +6 -0
  270. package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/[id]/edit/page.d.ts.map +1 -0
  271. package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/[id]/edit/page.js +9 -0
  272. package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/[id]/edit/page.js.map +1 -0
  273. package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/[id]/edit/page.tsx +11 -0
  274. package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/[id]/page.d.ts +6 -0
  275. package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/[id]/page.d.ts.map +1 -0
  276. package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/[id]/page.js +9 -0
  277. package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/[id]/page.js.map +1 -0
  278. package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/[id]/page.tsx +11 -0
  279. package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/new/page.d.ts +2 -0
  280. package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/new/page.d.ts.map +1 -0
  281. package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/new/page.js +8 -0
  282. package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/new/page.js.map +1 -0
  283. package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/new/page.tsx +5 -0
  284. package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/page.d.ts +2 -0
  285. package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/page.d.ts.map +1 -0
  286. package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/page.js +612 -0
  287. package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/page.js.map +1 -0
  288. package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/page.tsx +939 -0
  289. package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/[id]/edit/page.d.ts +6 -0
  290. package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/[id]/edit/page.d.ts.map +1 -0
  291. package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/[id]/edit/page.js +9 -0
  292. package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/[id]/edit/page.js.map +1 -0
  293. package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/[id]/edit/page.tsx +11 -0
  294. package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/[id]/page.d.ts +6 -0
  295. package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/[id]/page.d.ts.map +1 -0
  296. package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/[id]/page.js +9 -0
  297. package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/[id]/page.js.map +1 -0
  298. package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/[id]/page.tsx +11 -0
  299. package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/new/page.d.ts +6 -0
  300. package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/new/page.d.ts.map +1 -0
  301. package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/new/page.js +9 -0
  302. package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/new/page.js.map +1 -0
  303. package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/new/page.tsx +17 -0
  304. package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/page.d.ts +2 -0
  305. package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/page.d.ts.map +1 -0
  306. package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/page.js +348 -0
  307. package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/page.js.map +1 -0
  308. package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/page.tsx +536 -0
  309. package/hedhog/frontend/src/app/(app)/(libraries)/operations/departments/page.d.ts +2 -0
  310. package/hedhog/frontend/src/app/(app)/(libraries)/operations/departments/page.d.ts.map +1 -0
  311. package/hedhog/frontend/src/app/(app)/(libraries)/operations/departments/page.js +401 -0
  312. package/hedhog/frontend/src/app/(app)/(libraries)/operations/departments/page.js.map +1 -0
  313. package/hedhog/frontend/src/app/(app)/(libraries)/operations/departments/page.tsx +607 -0
  314. package/hedhog/frontend/src/app/(app)/(libraries)/operations/layout.d.ts +5 -0
  315. package/hedhog/frontend/src/app/(app)/(libraries)/operations/layout.d.ts.map +1 -0
  316. package/hedhog/frontend/src/app/(app)/(libraries)/operations/layout.js +7 -0
  317. package/hedhog/frontend/src/app/(app)/(libraries)/operations/layout.js.map +1 -0
  318. package/hedhog/frontend/src/app/(app)/(libraries)/operations/layout.tsx +9 -0
  319. package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-projects/[id]/page.d.ts +6 -0
  320. package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-projects/[id]/page.d.ts.map +1 -0
  321. package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-projects/[id]/page.js +9 -0
  322. package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-projects/[id]/page.js.map +1 -0
  323. package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-projects/[id]/page.tsx +11 -0
  324. package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-projects/page.d.ts +2 -0
  325. package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-projects/page.d.ts.map +1 -0
  326. package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-projects/page.js +321 -0
  327. package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-projects/page.js.map +1 -0
  328. package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-projects/page.tsx +440 -0
  329. package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-tasks/page.d.ts +2 -0
  330. package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-tasks/page.d.ts.map +1 -0
  331. package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-tasks/page.js +939 -0
  332. package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-tasks/page.js.map +1 -0
  333. package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-tasks/page.tsx +1499 -0
  334. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/async-options-combobox.d.ts +29 -0
  335. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/async-options-combobox.d.ts.map +1 -0
  336. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/async-options-combobox.js +95 -0
  337. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/async-options-combobox.js.map +1 -0
  338. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/async-options-combobox.tsx +233 -0
  339. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-costs-section.d.ts +10 -0
  340. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-costs-section.d.ts.map +1 -0
  341. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-costs-section.js +577 -0
  342. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-costs-section.js.map +1 -0
  343. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-costs-section.tsx +868 -0
  344. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-details-screen.d.ts +4 -0
  345. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-details-screen.d.ts.map +1 -0
  346. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-details-screen.js +337 -0
  347. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-details-screen.js.map +1 -0
  348. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-details-screen.tsx +476 -0
  349. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-form-screen.d.ts +9 -0
  350. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-form-screen.d.ts.map +1 -0
  351. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-form-screen.js +1348 -0
  352. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-form-screen.js.map +1 -0
  353. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-form-screen.tsx +2233 -0
  354. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-select-with-create.d.ts +12 -0
  355. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-select-with-create.d.ts.map +1 -0
  356. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-select-with-create.js +162 -0
  357. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-select-with-create.js.map +1 -0
  358. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-select-with-create.tsx +261 -0
  359. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-content-editor.d.ts +18 -0
  360. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-content-editor.d.ts.map +1 -0
  361. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-content-editor.js +145 -0
  362. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-content-editor.js.map +1 -0
  363. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-content-editor.tsx +258 -0
  364. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-details-screen.d.ts +4 -0
  365. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-details-screen.d.ts.map +1 -0
  366. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-details-screen.js +223 -0
  367. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-details-screen.js.map +1 -0
  368. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-details-screen.tsx +342 -0
  369. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-form-screen.d.ts +58 -0
  370. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-form-screen.d.ts.map +1 -0
  371. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-form-screen.js +438 -0
  372. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-form-screen.js.map +1 -0
  373. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-form-screen.tsx +698 -0
  374. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/department-select-with-create.d.ts +20 -0
  375. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/department-select-with-create.d.ts.map +1 -0
  376. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/department-select-with-create.js +233 -0
  377. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/department-select-with-create.js.map +1 -0
  378. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/department-select-with-create.tsx +392 -0
  379. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/my-project-summary-screen.d.ts +4 -0
  380. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/my-project-summary-screen.d.ts.map +1 -0
  381. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/my-project-summary-screen.js +814 -0
  382. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/my-project-summary-screen.js.map +1 -0
  383. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/my-project-summary-screen.tsx +1288 -0
  384. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/operations-calendar-view.d.ts +21 -0
  385. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/operations-calendar-view.d.ts.map +1 -0
  386. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/operations-calendar-view.js +174 -0
  387. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/operations-calendar-view.js.map +1 -0
  388. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/operations-calendar-view.tsx +306 -0
  389. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/operations-header.d.ts +10 -0
  390. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/operations-header.d.ts.map +1 -0
  391. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/operations-header.js +12 -0
  392. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/operations-header.js.map +1 -0
  393. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/operations-header.tsx +29 -0
  394. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/person-select-with-create.d.ts +15 -0
  395. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/person-select-with-create.d.ts.map +1 -0
  396. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/person-select-with-create.js +501 -0
  397. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/person-select-with-create.js.map +1 -0
  398. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/person-select-with-create.tsx +853 -0
  399. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-cost-report-screen.d.ts +6 -0
  400. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-cost-report-screen.d.ts.map +1 -0
  401. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-cost-report-screen.js +459 -0
  402. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-cost-report-screen.js.map +1 -0
  403. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-cost-report-screen.tsx +598 -0
  404. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-costs-section.d.ts +6 -0
  405. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-costs-section.d.ts.map +1 -0
  406. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-costs-section.js +876 -0
  407. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-costs-section.js.map +1 -0
  408. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-costs-section.tsx +1368 -0
  409. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-details-screen.d.ts +4 -0
  410. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-details-screen.d.ts.map +1 -0
  411. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-details-screen.js +2930 -0
  412. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-details-screen.js.map +1 -0
  413. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-details-screen.tsx +4378 -0
  414. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-form-screen.d.ts +9 -0
  415. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-form-screen.d.ts.map +1 -0
  416. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-form-screen.js +1013 -0
  417. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-form-screen.js.map +1 -0
  418. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-form-screen.tsx +1745 -0
  419. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/section-card.d.ts +13 -0
  420. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/section-card.d.ts.map +1 -0
  421. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/section-card.js +38 -0
  422. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/section-card.js.map +1 -0
  423. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/section-card.tsx +74 -0
  424. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/status-badge.d.ts +7 -0
  425. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/status-badge.d.ts.map +1 -0
  426. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/status-badge.js +11 -0
  427. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/status-badge.js.map +1 -0
  428. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/status-badge.tsx +15 -0
  429. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/system-user-select-with-create.d.ts +18 -0
  430. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/system-user-select-with-create.d.ts.map +1 -0
  431. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/system-user-select-with-create.js +406 -0
  432. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/system-user-select-with-create.js.map +1 -0
  433. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/system-user-select-with-create.tsx +660 -0
  434. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/task-detail-sheet.d.ts +26 -0
  435. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/task-detail-sheet.d.ts.map +1 -0
  436. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/task-detail-sheet.js +332 -0
  437. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/task-detail-sheet.js.map +1 -0
  438. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/task-detail-sheet.tsx +518 -0
  439. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/task-file-attachments.d.ts +6 -0
  440. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/task-file-attachments.d.ts.map +1 -0
  441. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/task-file-attachments.js +255 -0
  442. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/task-file-attachments.js.map +1 -0
  443. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/task-file-attachments.tsx +388 -0
  444. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/timesheet-task-create-sheet.d.ts +10 -0
  445. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/timesheet-task-create-sheet.d.ts.map +1 -0
  446. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/timesheet-task-create-sheet.js +131 -0
  447. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/timesheet-task-create-sheet.js.map +1 -0
  448. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/timesheet-task-create-sheet.tsx +214 -0
  449. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/api.d.ts +108 -0
  450. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/api.d.ts.map +1 -0
  451. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/api.js +162 -0
  452. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/api.js.map +1 -0
  453. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/api.ts +428 -0
  454. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/hooks/use-operations-access.d.ts +8 -0
  455. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/hooks/use-operations-access.d.ts.map +1 -0
  456. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/hooks/use-operations-access.js +36 -0
  457. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/hooks/use-operations-access.js.map +1 -0
  458. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/hooks/use-operations-access.ts +44 -0
  459. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/types.d.ts +836 -0
  460. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/types.d.ts.map +1 -0
  461. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/types.js +3 -0
  462. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/types.js.map +1 -0
  463. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/types.ts +860 -0
  464. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/utils/format.d.ts +16 -0
  465. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/utils/format.d.ts.map +1 -0
  466. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/utils/format.js +182 -0
  467. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/utils/format.js.map +1 -0
  468. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/utils/format.ts +250 -0
  469. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/utils/forms.d.ts +4 -0
  470. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/utils/forms.d.ts.map +1 -0
  471. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/utils/forms.js +51 -0
  472. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/utils/forms.js.map +1 -0
  473. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/utils/forms.ts +61 -0
  474. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/approvals/page.d.ts +2 -0
  475. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/approvals/page.d.ts.map +1 -0
  476. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/approvals/page.js +954 -0
  477. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/approvals/page.js.map +1 -0
  478. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/approvals/page.tsx +1277 -0
  479. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborator-types/page.d.ts +2 -0
  480. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborator-types/page.d.ts.map +1 -0
  481. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborator-types/page.js +488 -0
  482. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborator-types/page.js.map +1 -0
  483. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborator-types/page.tsx +805 -0
  484. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/[id]/edit/page.d.ts +6 -0
  485. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/[id]/edit/page.d.ts.map +1 -0
  486. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/[id]/edit/page.js +9 -0
  487. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/[id]/edit/page.js.map +1 -0
  488. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/[id]/edit/page.tsx +11 -0
  489. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/[id]/page.d.ts +6 -0
  490. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/[id]/page.d.ts.map +1 -0
  491. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/[id]/page.js +9 -0
  492. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/[id]/page.js.map +1 -0
  493. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/[id]/page.tsx +11 -0
  494. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/new/page.d.ts +2 -0
  495. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/new/page.d.ts.map +1 -0
  496. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/new/page.js +8 -0
  497. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/new/page.js.map +1 -0
  498. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/new/page.tsx +5 -0
  499. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/page.d.ts +2 -0
  500. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/page.d.ts.map +1 -0
  501. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/page.js +612 -0
  502. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/page.js.map +1 -0
  503. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/page.tsx +939 -0
  504. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/[id]/edit/page.d.ts +6 -0
  505. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/[id]/edit/page.d.ts.map +1 -0
  506. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/[id]/edit/page.js +9 -0
  507. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/[id]/edit/page.js.map +1 -0
  508. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/[id]/edit/page.tsx +11 -0
  509. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/[id]/page.d.ts +6 -0
  510. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/[id]/page.d.ts.map +1 -0
  511. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/[id]/page.js +9 -0
  512. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/[id]/page.js.map +1 -0
  513. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/[id]/page.tsx +11 -0
  514. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/new/page.d.ts +6 -0
  515. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/new/page.d.ts.map +1 -0
  516. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/new/page.js +9 -0
  517. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/new/page.js.map +1 -0
  518. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/new/page.tsx +17 -0
  519. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/page.d.ts +2 -0
  520. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/page.d.ts.map +1 -0
  521. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/page.js +348 -0
  522. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/page.js.map +1 -0
  523. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/page.tsx +536 -0
  524. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/departments/page.d.ts +2 -0
  525. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/departments/page.d.ts.map +1 -0
  526. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/departments/page.js +401 -0
  527. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/departments/page.js.map +1 -0
  528. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/departments/page.tsx +607 -0
  529. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/layout.d.ts +5 -0
  530. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/layout.d.ts.map +1 -0
  531. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/layout.js +7 -0
  532. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/layout.js.map +1 -0
  533. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/layout.tsx +9 -0
  534. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-projects/[id]/page.d.ts +6 -0
  535. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-projects/[id]/page.d.ts.map +1 -0
  536. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-projects/[id]/page.js +9 -0
  537. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-projects/[id]/page.js.map +1 -0
  538. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-projects/[id]/page.tsx +11 -0
  539. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-projects/page.d.ts +2 -0
  540. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-projects/page.d.ts.map +1 -0
  541. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-projects/page.js +321 -0
  542. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-projects/page.js.map +1 -0
  543. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-projects/page.tsx +440 -0
  544. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-tasks/page.d.ts +2 -0
  545. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-tasks/page.d.ts.map +1 -0
  546. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-tasks/page.js +939 -0
  547. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-tasks/page.js.map +1 -0
  548. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-tasks/page.tsx +1499 -0
  549. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/page.d.ts +2 -0
  550. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/page.d.ts.map +1 -0
  551. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/page.js +8 -0
  552. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/page.js.map +1 -0
  553. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/page.tsx +5 -0
  554. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/project-cost-categories/page.d.ts +2 -0
  555. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/project-cost-categories/page.d.ts.map +1 -0
  556. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/project-cost-categories/page.js +436 -0
  557. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/project-cost-categories/page.js.map +1 -0
  558. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/project-cost-categories/page.tsx +675 -0
  559. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/project-cost-types/page.d.ts +2 -0
  560. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/project-cost-types/page.d.ts.map +1 -0
  561. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/project-cost-types/page.js +563 -0
  562. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/project-cost-types/page.js.map +1 -0
  563. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/project-cost-types/page.tsx +846 -0
  564. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/costs-report/page.d.ts +6 -0
  565. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/costs-report/page.d.ts.map +1 -0
  566. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/costs-report/page.js +9 -0
  567. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/costs-report/page.js.map +1 -0
  568. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/costs-report/page.tsx +10 -0
  569. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/edit/page.d.ts +6 -0
  570. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/edit/page.d.ts.map +1 -0
  571. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/edit/page.js +9 -0
  572. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/edit/page.js.map +1 -0
  573. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/edit/page.tsx +11 -0
  574. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/page.d.ts +6 -0
  575. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/page.d.ts.map +1 -0
  576. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/page.js +9 -0
  577. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/page.js.map +1 -0
  578. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/page.tsx +11 -0
  579. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/new/page.d.ts +2 -0
  580. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/new/page.d.ts.map +1 -0
  581. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/new/page.js +8 -0
  582. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/new/page.js.map +1 -0
  583. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/new/page.tsx +5 -0
  584. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/page.d.ts +2 -0
  585. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/page.d.ts.map +1 -0
  586. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/page.js +492 -0
  587. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/page.js.map +1 -0
  588. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/page.tsx +757 -0
  589. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/reports/collaborators/page.d.ts +2 -0
  590. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/reports/collaborators/page.d.ts.map +1 -0
  591. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/reports/collaborators/page.js +342 -0
  592. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/reports/collaborators/page.js.map +1 -0
  593. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/reports/collaborators/page.tsx +430 -0
  594. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/reports/projects/page.d.ts +2 -0
  595. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/reports/projects/page.d.ts.map +1 -0
  596. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/reports/projects/page.js +338 -0
  597. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/reports/projects/page.js.map +1 -0
  598. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/reports/projects/page.tsx +428 -0
  599. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/schedule-adjustments/page.d.ts +2 -0
  600. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/schedule-adjustments/page.d.ts.map +1 -0
  601. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/schedule-adjustments/page.js +660 -0
  602. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/schedule-adjustments/page.js.map +1 -0
  603. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/schedule-adjustments/page.tsx +992 -0
  604. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/time-off/page.d.ts +2 -0
  605. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/time-off/page.d.ts.map +1 -0
  606. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/time-off/page.js +515 -0
  607. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/time-off/page.js.map +1 -0
  608. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/time-off/page.tsx +707 -0
  609. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/timesheets/page.d.ts +2 -0
  610. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/timesheets/page.d.ts.map +1 -0
  611. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/timesheets/page.js +1141 -0
  612. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/timesheets/page.js.map +1 -0
  613. package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/timesheets/page.tsx +1705 -0
  614. package/hedhog/frontend/src/app/(app)/(libraries)/operations/page.d.ts +2 -0
  615. package/hedhog/frontend/src/app/(app)/(libraries)/operations/page.d.ts.map +1 -0
  616. package/hedhog/frontend/src/app/(app)/(libraries)/operations/page.js +8 -0
  617. package/hedhog/frontend/src/app/(app)/(libraries)/operations/page.js.map +1 -0
  618. package/hedhog/frontend/src/app/(app)/(libraries)/operations/page.tsx +5 -0
  619. package/hedhog/frontend/src/app/(app)/(libraries)/operations/project-cost-categories/page.d.ts +2 -0
  620. package/hedhog/frontend/src/app/(app)/(libraries)/operations/project-cost-categories/page.d.ts.map +1 -0
  621. package/hedhog/frontend/src/app/(app)/(libraries)/operations/project-cost-categories/page.js +436 -0
  622. package/hedhog/frontend/src/app/(app)/(libraries)/operations/project-cost-categories/page.js.map +1 -0
  623. package/hedhog/frontend/src/app/(app)/(libraries)/operations/project-cost-categories/page.tsx +675 -0
  624. package/hedhog/frontend/src/app/(app)/(libraries)/operations/project-cost-types/page.d.ts +2 -0
  625. package/hedhog/frontend/src/app/(app)/(libraries)/operations/project-cost-types/page.d.ts.map +1 -0
  626. package/hedhog/frontend/src/app/(app)/(libraries)/operations/project-cost-types/page.js +563 -0
  627. package/hedhog/frontend/src/app/(app)/(libraries)/operations/project-cost-types/page.js.map +1 -0
  628. package/hedhog/frontend/src/app/(app)/(libraries)/operations/project-cost-types/page.tsx +846 -0
  629. package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/[id]/edit/page.d.ts +6 -0
  630. package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/[id]/edit/page.d.ts.map +1 -0
  631. package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/[id]/edit/page.js +9 -0
  632. package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/[id]/edit/page.js.map +1 -0
  633. package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/[id]/edit/page.tsx +11 -0
  634. package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/[id]/page.d.ts +6 -0
  635. package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/[id]/page.d.ts.map +1 -0
  636. package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/[id]/page.js +9 -0
  637. package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/[id]/page.js.map +1 -0
  638. package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/[id]/page.tsx +11 -0
  639. package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/new/page.d.ts +2 -0
  640. package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/new/page.d.ts.map +1 -0
  641. package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/new/page.js +8 -0
  642. package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/new/page.js.map +1 -0
  643. package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/new/page.tsx +5 -0
  644. package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/page.d.ts +2 -0
  645. package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/page.d.ts.map +1 -0
  646. package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/page.js +492 -0
  647. package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/page.js.map +1 -0
  648. package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/page.tsx +757 -0
  649. package/hedhog/frontend/src/app/(app)/(libraries)/operations/reports/collaborators/page.d.ts +2 -0
  650. package/hedhog/frontend/src/app/(app)/(libraries)/operations/reports/collaborators/page.d.ts.map +1 -0
  651. package/hedhog/frontend/src/app/(app)/(libraries)/operations/reports/collaborators/page.js +342 -0
  652. package/hedhog/frontend/src/app/(app)/(libraries)/operations/reports/collaborators/page.js.map +1 -0
  653. package/hedhog/frontend/src/app/(app)/(libraries)/operations/reports/collaborators/page.tsx +430 -0
  654. package/hedhog/frontend/src/app/(app)/(libraries)/operations/reports/projects/page.d.ts +2 -0
  655. package/hedhog/frontend/src/app/(app)/(libraries)/operations/reports/projects/page.d.ts.map +1 -0
  656. package/hedhog/frontend/src/app/(app)/(libraries)/operations/reports/projects/page.js +338 -0
  657. package/hedhog/frontend/src/app/(app)/(libraries)/operations/reports/projects/page.js.map +1 -0
  658. package/hedhog/frontend/src/app/(app)/(libraries)/operations/reports/projects/page.tsx +428 -0
  659. package/hedhog/frontend/src/app/(app)/(libraries)/operations/schedule-adjustments/page.d.ts +2 -0
  660. package/hedhog/frontend/src/app/(app)/(libraries)/operations/schedule-adjustments/page.d.ts.map +1 -0
  661. package/hedhog/frontend/src/app/(app)/(libraries)/operations/schedule-adjustments/page.js +660 -0
  662. package/hedhog/frontend/src/app/(app)/(libraries)/operations/schedule-adjustments/page.js.map +1 -0
  663. package/hedhog/frontend/src/app/(app)/(libraries)/operations/schedule-adjustments/page.tsx +992 -0
  664. package/hedhog/frontend/src/app/(app)/(libraries)/operations/time-off/page.d.ts +2 -0
  665. package/hedhog/frontend/src/app/(app)/(libraries)/operations/time-off/page.d.ts.map +1 -0
  666. package/hedhog/frontend/src/app/(app)/(libraries)/operations/time-off/page.js +515 -0
  667. package/hedhog/frontend/src/app/(app)/(libraries)/operations/time-off/page.js.map +1 -0
  668. package/hedhog/frontend/src/app/(app)/(libraries)/operations/time-off/page.tsx +707 -0
  669. package/hedhog/frontend/src/app/(app)/(libraries)/operations/timesheets/page.d.ts +2 -0
  670. package/hedhog/frontend/src/app/(app)/(libraries)/operations/timesheets/page.d.ts.map +1 -0
  671. package/hedhog/frontend/src/app/(app)/(libraries)/operations/timesheets/page.js +1141 -0
  672. package/hedhog/frontend/src/app/(app)/(libraries)/operations/timesheets/page.js.map +1 -0
  673. package/hedhog/frontend/src/app/(app)/(libraries)/operations/timesheets/page.tsx +1705 -0
  674. package/hedhog/table/operations_project_assignment.yaml +1 -0
  675. package/hedhog/table/operations_project_cost.yaml +93 -0
  676. package/hedhog/table/operations_project_cost_category.yaml +37 -0
  677. package/hedhog/table/operations_project_cost_type.yaml +55 -0
  678. package/hedhog/table/operations_task_comment.yaml +26 -0
  679. package/hedhog/table/operations_task_file.yaml +23 -0
  680. package/package.json +5 -5
  681. package/src/controllers/operations-collaborators.controller.ts +26 -0
  682. package/src/controllers/operations-project-costs.controller.ts +249 -0
  683. package/src/controllers/operations-tasks.controller.ts +92 -9
  684. package/src/dto/create-collaborator-project-assignment.dto.ts +14 -0
  685. package/src/dto/create-project-cost-category.dto.ts +37 -0
  686. package/src/dto/create-project-cost-type.dto.ts +64 -0
  687. package/src/dto/create-project-cost.dto.ts +126 -0
  688. package/src/dto/create-task.dto.ts +0 -1
  689. package/src/dto/get-project-cost-report.dto.ts +46 -0
  690. package/src/dto/list-project-cost-categories.dto.ts +17 -0
  691. package/src/dto/list-project-cost-types.dto.ts +28 -0
  692. package/src/dto/list-project-costs.dto.ts +59 -0
  693. package/src/dto/list-tasks.dto.ts +7 -0
  694. package/src/dto/list-timesheets.dto.ts +7 -1
  695. package/src/dto/update-collaborator-project-assignment.dto.ts +58 -0
  696. package/src/dto/update-project-cost-category.dto.ts +4 -0
  697. package/src/dto/update-project-cost-type.dto.ts +4 -0
  698. package/src/dto/update-project-cost.dto.ts +4 -0
  699. package/src/dto/update-task.dto.ts +0 -1
  700. package/src/operations.module.ts +2 -0
  701. package/src/operations.service.ts +2421 -64
@@ -1143,6 +1143,62 @@ let OperationsService = OperationsService_1 = class OperationsService {
1143
1143
  });
1144
1144
  return this.getCollaboratorByIdForUser(userId, collaboratorId);
1145
1145
  }
1146
+ async updateCollaboratorProjectAssignment(collaboratorId, projectId, data) {
1147
+ var _a, _b, _c, _d, _e, _f;
1148
+ const sets = [];
1149
+ const params = [collaboratorId, projectId];
1150
+ let idx = 3;
1151
+ if ('projectRoleId' in data) {
1152
+ sets.push(`project_role_id = $${idx++}`);
1153
+ params.push((_a = data.projectRoleId) !== null && _a !== void 0 ? _a : null);
1154
+ }
1155
+ if ('roleLabel' in data) {
1156
+ sets.push(`role_label = $${idx++}`);
1157
+ params.push((_b = data.roleLabel) !== null && _b !== void 0 ? _b : null);
1158
+ }
1159
+ if ('allocationPercent' in data) {
1160
+ sets.push(`allocation_percent = $${idx++}`);
1161
+ params.push((_c = data.allocationPercent) !== null && _c !== void 0 ? _c : null);
1162
+ }
1163
+ if ('weeklyHours' in data) {
1164
+ sets.push(`weekly_hours = $${idx++}`);
1165
+ params.push((_d = data.weeklyHours) !== null && _d !== void 0 ? _d : null);
1166
+ }
1167
+ if ('startDate' in data) {
1168
+ sets.push(`start_date = $${idx++}::date`);
1169
+ params.push((_e = data.startDate) !== null && _e !== void 0 ? _e : null);
1170
+ }
1171
+ if ('endDate' in data) {
1172
+ sets.push(`end_date = $${idx++}::date`);
1173
+ params.push((_f = data.endDate) !== null && _f !== void 0 ? _f : null);
1174
+ }
1175
+ if ('status' in data) {
1176
+ sets.push(`status = $${idx++}::operations_project_assignment_status_155b459bbf_enum`);
1177
+ params.push(data.status);
1178
+ }
1179
+ if (!sets.length)
1180
+ return { updated: false };
1181
+ sets.push(`updated_at = NOW()`);
1182
+ await this.prisma.$executeRawUnsafe(`UPDATE operations_project_assignment
1183
+ SET ${sets.join(', ')}
1184
+ WHERE collaborator_id = $1
1185
+ AND project_id = $2
1186
+ AND deleted_at IS NULL`, ...params);
1187
+ return { updated: true };
1188
+ }
1189
+ async addCollaboratorProjectAssignment(collaboratorId, data) {
1190
+ var _a;
1191
+ const existing = await this.querySingle(`SELECT id FROM operations_project_assignment
1192
+ WHERE collaborator_id = $1 AND project_id = $2 AND deleted_at IS NULL`, [collaboratorId, data.projectId]);
1193
+ if (existing) {
1194
+ return { id: existing.id, created: false };
1195
+ }
1196
+ const row = await this.querySingle(`INSERT INTO operations_project_assignment
1197
+ (collaborator_id, project_id, role_label, status)
1198
+ VALUES ($1, $2, $3, 'active')
1199
+ RETURNING id`, [collaboratorId, data.projectId, (_a = data.roleLabel) !== null && _a !== void 0 ? _a : '']);
1200
+ return { id: row.id, created: true };
1201
+ }
1146
1202
  async getCollaboratorCompensationHistory(userId, collaboratorId) {
1147
1203
  const actor = await this.getActorContext(userId);
1148
1204
  this.ensureDirector(actor);
@@ -1425,6 +1481,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
1425
1481
  p.code,
1426
1482
  p.name,
1427
1483
  p.client_name AS "clientName",
1484
+ cp.avatar_id AS "clientAvatarId",
1428
1485
  p.summary,
1429
1486
  p.status,
1430
1487
  p.progress_percent AS "progressPercent",
@@ -1435,17 +1492,20 @@ let OperationsService = OperationsService_1 = class OperationsService {
1435
1492
  c.name AS "contractName",
1436
1493
  c.status AS "contractStatus",
1437
1494
  m.display_name AS "managerName",
1495
+ mp.avatar_id AS "managerAvatarId",
1438
1496
  ${ownAssignmentSelect}
1439
1497
  COUNT(DISTINCT pa.id)::int AS "teamSize"
1440
1498
  FROM operations_project p
1441
1499
  LEFT JOIN operations_contract c ON c.id = p.contract_id
1442
1500
  LEFT JOIN operations_collaborator m ON m.id = p.manager_collaborator_id
1501
+ LEFT JOIN person cp ON cp.id = p.client_person_id
1502
+ LEFT JOIN person mp ON mp.id = m.person_id
1443
1503
  LEFT JOIN operations_project_assignment pa
1444
1504
  ON pa.project_id = p.id
1445
1505
  AND pa.deleted_at IS NULL
1446
1506
  AND pa.status IN ('planned', 'active')
1447
1507
  WHERE ${whereClause}
1448
- GROUP BY p.id, c.id, m.id`;
1508
+ GROUP BY p.id, c.id, m.id, cp.id, mp.id`;
1449
1509
  if (!pagination) {
1450
1510
  return this.queryRows(`${baseQuery} ORDER BY p.name ASC`, params);
1451
1511
  }
@@ -1585,6 +1645,10 @@ let OperationsService = OperationsService_1 = class OperationsService {
1585
1645
  if (paginationParams.status) {
1586
1646
  filters.push(`t.status::text = ${this.param(params, paginationParams.status)}`);
1587
1647
  }
1648
+ if (paginationParams.collaboratorId) {
1649
+ const colId = paginationParams.collaboratorId;
1650
+ filters.push(`(pa.collaborator_id = ${this.param(params, colId)} OR t.assignee_collaborator_id = ${this.param(params, colId)})`);
1651
+ }
1588
1652
  const whereClause = filters.join(' AND ');
1589
1653
  const totalRow = await this.querySingle(`SELECT COUNT(*)::text AS total
1590
1654
  FROM operations_task t
@@ -1617,6 +1681,8 @@ let OperationsService = OperationsService_1 = class OperationsService {
1617
1681
  ac.display_name AS "assigneeName",
1618
1682
  au.photo_id AS "assigneeUserPhotoId",
1619
1683
  ap.avatar_id AS "assigneePersonAvatarId",
1684
+ COALESCE(task_comments.count, 0)::int AS "commentCount",
1685
+ COALESCE(task_files.count, 0)::int AS "fileCount",
1620
1686
  t.created_at AS "createdAt",
1621
1687
  t.deleted_at AS "deletedAt"
1622
1688
  FROM operations_task t
@@ -1629,6 +1695,16 @@ let OperationsService = OperationsService_1 = class OperationsService {
1629
1695
  ON au.id = ac.user_id
1630
1696
  LEFT JOIN person ap
1631
1697
  ON ap.id = ac.person_id
1698
+ LEFT JOIN LATERAL (
1699
+ SELECT COUNT(*) AS count
1700
+ FROM operations_task_comment tc
1701
+ WHERE tc.task_id = t.id
1702
+ ) task_comments ON TRUE
1703
+ LEFT JOIN LATERAL (
1704
+ SELECT COUNT(*) AS count
1705
+ FROM operations_task_file tf
1706
+ WHERE tf.operations_task_id = t.id
1707
+ ) task_files ON TRUE
1632
1708
  JOIN operations_project p
1633
1709
  ON p.id = COALESCE(t.project_id, pa.project_id)
1634
1710
  WHERE ${whereClause}
@@ -1645,25 +1721,31 @@ let OperationsService = OperationsService_1 = class OperationsService {
1645
1721
  }
1646
1722
  this.requireFields(data, ['name']);
1647
1723
  let assignmentId = null;
1648
- let projectId = null;
1724
+ let projectId = (_a = data.projectId) !== null && _a !== void 0 ? _a : null;
1649
1725
  if (data.projectId || data.projectAssignmentId) {
1650
- const assignment = await this.resolveProjectAssignmentForActor(this.prisma, actor, {
1651
- projectId: (_a = data.projectId) !== null && _a !== void 0 ? _a : null,
1652
- projectAssignmentId: (_b = data.projectAssignmentId) !== null && _b !== void 0 ? _b : null,
1653
- });
1654
- await this.assertProjectAccess(actor, assignment.projectId);
1655
- assignmentId = assignment.id;
1656
- projectId = assignment.projectId;
1657
- }
1658
- else if (data.projectId) {
1659
- projectId = data.projectId;
1660
- await this.assertProjectAccess(actor, projectId);
1661
- }
1662
- else {
1663
- throw new common_1.BadRequestException('Either projectId or projectAssignmentId is required.');
1664
- }
1665
- if (!projectId) {
1666
- projectId = (_c = data.projectId) !== null && _c !== void 0 ? _c : null;
1726
+ if (actor.isCollaborator && !actor.isDirector && !actor.isSupervisor) {
1727
+ const assignment = await this.resolveProjectAssignmentForActor(this.prisma, actor, {
1728
+ projectId: (_b = data.projectId) !== null && _b !== void 0 ? _b : null,
1729
+ projectAssignmentId: (_c = data.projectAssignmentId) !== null && _c !== void 0 ? _c : null,
1730
+ });
1731
+ await this.assertProjectAccess(actor, assignment.projectId);
1732
+ assignmentId = assignment.id;
1733
+ projectId = assignment.projectId;
1734
+ }
1735
+ else {
1736
+ if (data.projectId) {
1737
+ await this.assertProjectAccess(actor, data.projectId);
1738
+ projectId = data.projectId;
1739
+ }
1740
+ if (data.projectAssignmentId) {
1741
+ const assignment = await this.resolveProjectAssignmentForActor(this.prisma, actor, {
1742
+ projectId: (_d = data.projectId) !== null && _d !== void 0 ? _d : null,
1743
+ projectAssignmentId: data.projectAssignmentId,
1744
+ });
1745
+ assignmentId = assignment.id;
1746
+ projectId = assignment.projectId;
1747
+ }
1748
+ }
1667
1749
  }
1668
1750
  const name = this.normalizeOptionalText(data.name);
1669
1751
  if (!name) {
@@ -1673,8 +1755,8 @@ let OperationsService = OperationsService_1 = class OperationsService {
1673
1755
  FROM operations_task
1674
1756
  WHERE project_id = $1
1675
1757
  AND status = $2::operations_task_status_574c143dbe_enum
1676
- AND deleted_at IS NULL`, [projectId, (_d = data.status) !== null && _d !== void 0 ? _d : 'todo']);
1677
- const nextPosition = ((_e = maxPositionRow === null || maxPositionRow === void 0 ? void 0 : maxPositionRow.max_pos) !== null && _e !== void 0 ? _e : -1) + 1;
1758
+ AND deleted_at IS NULL`, [projectId, (_e = data.status) !== null && _e !== void 0 ? _e : 'todo']);
1759
+ const nextPosition = ((_f = maxPositionRow === null || maxPositionRow === void 0 ? void 0 : maxPositionRow.max_pos) !== null && _f !== void 0 ? _f : -1) + 1;
1678
1760
  const created = await this.querySingle(`INSERT INTO operations_task (
1679
1761
  project_id,
1680
1762
  project_assignment_id,
@@ -1698,7 +1780,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
1698
1780
  RETURNING id`, [
1699
1781
  projectId,
1700
1782
  assignmentId,
1701
- (_g = (_f = data.assigneeCollaboratorId) !== null && _f !== void 0 ? _f : actor.collaboratorId) !== null && _g !== void 0 ? _g : null,
1783
+ (_g = data.assigneeCollaboratorId) !== null && _g !== void 0 ? _g : null,
1702
1784
  name,
1703
1785
  this.normalizeOptionalText(data.description),
1704
1786
  (_h = data.priority) !== null && _h !== void 0 ? _h : 'medium',
@@ -1789,6 +1871,154 @@ let OperationsService = OperationsService_1 = class OperationsService {
1789
1871
  });
1790
1872
  return { success: true };
1791
1873
  }
1874
+ async listTaskFiles(userId, taskId) {
1875
+ const actor = await this.getActorContext(userId);
1876
+ if (!actor.isCollaborator && !actor.isDirector && !actor.isSupervisor) {
1877
+ throw new common_1.ForbiddenException('Operations collaborator access is required.');
1878
+ }
1879
+ const current = await this.getTaskRecordForActor(this.prisma, actor, taskId);
1880
+ await this.assertProjectAccess(actor, current.projectId);
1881
+ const rows = (await this.prisma.$queryRawUnsafe(`SELECT tf.id, tf.file_id, f.filename, f.size, m.name AS mimetype, tf.created_at
1882
+ FROM operations_task_file tf
1883
+ JOIN file f ON f.id = tf.file_id
1884
+ JOIN file_mimetype m ON m.id = f.mimetype_id
1885
+ WHERE tf.operations_task_id = $1
1886
+ ORDER BY tf.created_at ASC`, taskId));
1887
+ return rows;
1888
+ }
1889
+ async addTaskFile(userId, taskId, file) {
1890
+ const actor = await this.getActorContext(userId);
1891
+ if (!actor.isCollaborator && !actor.isDirector && !actor.isSupervisor) {
1892
+ throw new common_1.ForbiddenException('Operations collaborator access is required.');
1893
+ }
1894
+ const current = await this.getTaskRecordForActor(this.prisma, actor, taskId);
1895
+ await this.assertProjectAccess(actor, current.projectId);
1896
+ const uploaded = await this.fileService.upload(`operations/tasks/${taskId}`, file);
1897
+ await this.prisma.$executeRawUnsafe(`INSERT INTO operations_task_file (operations_task_id, file_id, created_at, updated_at)
1898
+ VALUES ($1, $2, NOW(), NOW())`, taskId, uploaded.id);
1899
+ return uploaded;
1900
+ }
1901
+ async removeTaskFile(userId, taskId, fileRelationId) {
1902
+ const actor = await this.getActorContext(userId);
1903
+ if (!actor.isCollaborator && !actor.isDirector && !actor.isSupervisor) {
1904
+ throw new common_1.ForbiddenException('Operations collaborator access is required.');
1905
+ }
1906
+ const current = await this.getTaskRecordForActor(this.prisma, actor, taskId);
1907
+ await this.assertProjectAccess(actor, current.projectId);
1908
+ const rows = (await this.prisma.$queryRawUnsafe(`SELECT file_id FROM operations_task_file WHERE id = $1 AND operations_task_id = $2`, fileRelationId, taskId));
1909
+ if (!rows.length) {
1910
+ throw new common_1.NotFoundException('Task file attachment not found.');
1911
+ }
1912
+ const fileId = rows[0].file_id;
1913
+ await this.prisma.$executeRawUnsafe(`DELETE FROM operations_task_file WHERE id = $1`, fileRelationId);
1914
+ if (fileId) {
1915
+ await this.fileService.delete('en', { ids: [fileId] });
1916
+ }
1917
+ return { success: true };
1918
+ }
1919
+ async listTaskComments(userId, taskId) {
1920
+ const actor = await this.getActorContext(userId);
1921
+ if (!actor.isCollaborator && !actor.isDirector && !actor.isSupervisor) {
1922
+ throw new common_1.ForbiddenException('Operations collaborator access is required.');
1923
+ }
1924
+ const current = await this.getTaskRecordForActor(this.prisma, actor, taskId);
1925
+ await this.assertProjectAccess(actor, current.projectId);
1926
+ return this.queryRows(`SELECT tc.id,
1927
+ tc.task_id AS "taskId",
1928
+ tc.content,
1929
+ tc.actor_collaborator_id AS "actorCollaboratorId",
1930
+ actor.display_name AS "actorName",
1931
+ actor_user.photo_id AS "actorUserPhotoId",
1932
+ actor_person.avatar_id AS "actorPersonAvatarId",
1933
+ tc.created_at AS "createdAt",
1934
+ tc.updated_at AS "updatedAt"
1935
+ FROM operations_task_comment tc
1936
+ LEFT JOIN operations_collaborator actor
1937
+ ON actor.id = tc.actor_collaborator_id
1938
+ AND actor.deleted_at IS NULL
1939
+ LEFT JOIN "user" actor_user
1940
+ ON actor_user.id = actor.user_id
1941
+ LEFT JOIN person actor_person
1942
+ ON actor_person.id = actor.person_id
1943
+ WHERE tc.task_id = $1
1944
+ ORDER BY tc.created_at ASC, tc.id ASC`, [taskId]);
1945
+ }
1946
+ async addTaskComment(userId, taskId, content) {
1947
+ var _a, _b;
1948
+ const actor = await this.getActorContext(userId);
1949
+ if (!actor.isCollaborator && !actor.isDirector && !actor.isSupervisor) {
1950
+ throw new common_1.ForbiddenException('Operations collaborator access is required.');
1951
+ }
1952
+ const current = await this.getTaskRecordForActor(this.prisma, actor, taskId);
1953
+ await this.assertProjectAccess(actor, current.projectId);
1954
+ const normalizedContent = this.normalizeOptionalText(content);
1955
+ if (!normalizedContent) {
1956
+ throw new common_1.BadRequestException('Comment content is required.');
1957
+ }
1958
+ const inserted = await this.queryRows(`INSERT INTO operations_task_comment (
1959
+ task_id,
1960
+ actor_collaborator_id,
1961
+ content,
1962
+ created_at,
1963
+ updated_at
1964
+ ) VALUES ($1, $2, $3, NOW(), NOW())
1965
+ RETURNING id`, [taskId, (_a = actor.collaboratorId) !== null && _a !== void 0 ? _a : null, normalizedContent]);
1966
+ const commentId = (_b = inserted[0]) === null || _b === void 0 ? void 0 : _b.id;
1967
+ const comments = await this.listTaskComments(userId, taskId);
1968
+ const createdComment = comments.find((comment) => comment.id === commentId);
1969
+ if (!createdComment) {
1970
+ throw new common_1.NotFoundException('Task comment could not be loaded.');
1971
+ }
1972
+ return createdComment;
1973
+ }
1974
+ async updateTaskComment(userId, taskId, commentId, content) {
1975
+ var _a;
1976
+ const actor = await this.getActorContext(userId);
1977
+ if (!actor.isCollaborator && !actor.isDirector && !actor.isSupervisor) {
1978
+ throw new common_1.ForbiddenException('Operations collaborator access is required.');
1979
+ }
1980
+ const current = await this.getTaskRecordForActor(this.prisma, actor, taskId);
1981
+ await this.assertProjectAccess(actor, current.projectId);
1982
+ const normalizedContent = this.normalizeOptionalText(content);
1983
+ if (!normalizedContent) {
1984
+ throw new common_1.BadRequestException('Comment content is required.');
1985
+ }
1986
+ const rows = await this.queryRows(`SELECT id, actor_collaborator_id AS "actorCollaboratorId"
1987
+ FROM operations_task_comment
1988
+ WHERE id = $1 AND task_id = $2`, [commentId, taskId]);
1989
+ const row = rows[0];
1990
+ if (!row) {
1991
+ throw new common_1.NotFoundException('Comment not found.');
1992
+ }
1993
+ if (row.actorCollaboratorId !== actor.collaboratorId) {
1994
+ throw new common_1.ForbiddenException('You can only edit your own comments.');
1995
+ }
1996
+ await this.queryRows(`UPDATE operations_task_comment
1997
+ SET content = $1, updated_at = NOW()
1998
+ WHERE id = $2`, [normalizedContent, commentId]);
1999
+ const comments = await this.listTaskComments(userId, taskId);
2000
+ return (_a = comments.find((c) => c.id === commentId)) !== null && _a !== void 0 ? _a : null;
2001
+ }
2002
+ async removeTaskComment(userId, taskId, commentId) {
2003
+ const actor = await this.getActorContext(userId);
2004
+ if (!actor.isCollaborator && !actor.isDirector && !actor.isSupervisor) {
2005
+ throw new common_1.ForbiddenException('Operations collaborator access is required.');
2006
+ }
2007
+ const current = await this.getTaskRecordForActor(this.prisma, actor, taskId);
2008
+ await this.assertProjectAccess(actor, current.projectId);
2009
+ const rows = await this.queryRows(`SELECT id, actor_collaborator_id AS "actorCollaboratorId"
2010
+ FROM operations_task_comment
2011
+ WHERE id = $1 AND task_id = $2`, [commentId, taskId]);
2012
+ const row = rows[0];
2013
+ if (!row) {
2014
+ throw new common_1.NotFoundException('Comment not found.');
2015
+ }
2016
+ if (row.actorCollaboratorId !== actor.collaboratorId) {
2017
+ throw new common_1.ForbiddenException('You can only delete your own comments.');
2018
+ }
2019
+ await this.queryRows(`DELETE FROM operations_task_comment WHERE id = $1`, [commentId]);
2020
+ return { success: true };
2021
+ }
1792
2022
  async listTimesheetEntries(userId, paginationParams) {
1793
2023
  var _a, _b;
1794
2024
  const actor = await this.getActorContext(userId);
@@ -3000,6 +3230,9 @@ let OperationsService = OperationsService_1 = class OperationsService {
3000
3230
  if (filters.dateTo) {
3001
3231
  where.push(`t.week_start_date <= ${this.param(params, filters.dateTo)}::date`);
3002
3232
  }
3233
+ if (filters.collaboratorId) {
3234
+ where.push(`t.collaborator_id = ${this.param(params, filters.collaboratorId)}`);
3235
+ }
3003
3236
  if (pagination === null || pagination === void 0 ? void 0 : pagination.search) {
3004
3237
  const searchPlaceholder = this.param(params, `%${pagination.search}%`);
3005
3238
  where.push(`(
@@ -3033,7 +3266,8 @@ let OperationsService = OperationsService_1 = class OperationsService {
3033
3266
  t.submitted_at AS "submittedAt",
3034
3267
  t.reviewed_at AS "reviewedAt",
3035
3268
  t.notes,
3036
- approval.decision_note AS "decisionNote"
3269
+ approval.decision_note AS "decisionNote",
3270
+ approval.id AS "approvalId"
3037
3271
  FROM operations_timesheet t
3038
3272
  JOIN operations_collaborator c ON c.id = t.collaborator_id
3039
3273
  LEFT JOIN operations_collaborator a ON a.id = t.approver_collaborator_id
@@ -4827,6 +5061,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
4827
5061
  au.photo_id AS "assigneeUserPhotoId",
4828
5062
  ap.avatar_id AS "assigneePersonAvatarId",
4829
5063
  t.project_assignment_id AS "projectAssignmentId",
5064
+ COALESCE(task_comments.count, 0)::int AS "commentCount",
4830
5065
  t.created_at AS "createdAt"
4831
5066
  FROM operations_task t
4832
5067
  LEFT JOIN operations_collaborator ac
@@ -4835,6 +5070,11 @@ let OperationsService = OperationsService_1 = class OperationsService {
4835
5070
  ON au.id = ac.user_id
4836
5071
  LEFT JOIN person ap
4837
5072
  ON ap.id = ac.person_id
5073
+ LEFT JOIN LATERAL (
5074
+ SELECT COUNT(*) AS count
5075
+ FROM operations_task_comment tc
5076
+ WHERE tc.task_id = t.id
5077
+ ) task_comments ON TRUE
4838
5078
  WHERE COALESCE(t.project_id, (
4839
5079
  SELECT pa.project_id FROM operations_project_assignment pa
4840
5080
  WHERE pa.id = t.project_assignment_id AND pa.deleted_at IS NULL
@@ -4861,6 +5101,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
4861
5101
  ap.avatar_id AS "assigneePersonAvatarId",
4862
5102
  t.project_assignment_id AS "projectAssignmentId",
4863
5103
  COALESCE(t.project_id, pa.project_id) AS "projectId",
5104
+ COALESCE(task_comments.count, 0)::int AS "commentCount",
4864
5105
  t.created_at AS "createdAt",
4865
5106
  t.deleted_at AS "deletedAt"
4866
5107
  FROM operations_task t
@@ -4870,6 +5111,11 @@ let OperationsService = OperationsService_1 = class OperationsService {
4870
5111
  LEFT JOIN person ap ON ap.id = ac.person_id
4871
5112
  LEFT JOIN operations_project_assignment pa
4872
5113
  ON pa.id = t.project_assignment_id AND pa.deleted_at IS NULL
5114
+ LEFT JOIN LATERAL (
5115
+ SELECT COUNT(*) AS count
5116
+ FROM operations_task_comment tc
5117
+ WHERE tc.task_id = t.id
5118
+ ) task_comments ON TRUE
4873
5119
  WHERE t.id = $1`, [taskId]);
4874
5120
  return (_a = rows[0]) !== null && _a !== void 0 ? _a : null;
4875
5121
  }
@@ -6864,12 +7110,18 @@ let OperationsService = OperationsService_1 = class OperationsService {
6864
7110
  au.photo_id AS "assigneeUserPhotoId",
6865
7111
  ap.avatar_id AS "assigneePersonAvatarId",
6866
7112
  t.project_assignment_id AS "projectAssignmentId",
7113
+ COALESCE(task_comments.count, 0)::int AS "commentCount",
6867
7114
  t.created_at AS "createdAt"
6868
7115
  FROM operations_task t
6869
7116
  LEFT JOIN operations_collaborator ac
6870
7117
  ON ac.id = t.assignee_collaborator_id AND ac.deleted_at IS NULL
6871
7118
  LEFT JOIN "user" au ON au.id = ac.user_id
6872
7119
  LEFT JOIN person ap ON ap.id = ac.person_id
7120
+ LEFT JOIN LATERAL (
7121
+ SELECT COUNT(*) AS count
7122
+ FROM operations_task_comment tc
7123
+ WHERE tc.task_id = t.id
7124
+ ) task_comments ON TRUE
6873
7125
  WHERE COALESCE(t.project_id, (
6874
7126
  SELECT pa.project_id FROM operations_project_assignment pa
6875
7127
  WHERE pa.id = t.project_assignment_id AND pa.deleted_at IS NULL
@@ -6909,7 +7161,9 @@ let OperationsService = OperationsService_1 = class OperationsService {
6909
7161
  )`,
6910
7162
  ];
6911
7163
  if (actor.collaboratorId) {
6912
- filters.push(`pa.collaborator_id = ${this.param(params, actor.collaboratorId)}`);
7164
+ const p1 = this.param(params, actor.collaboratorId);
7165
+ const p2 = this.param(params, actor.collaboratorId);
7166
+ filters.push(`(pa.collaborator_id = ${p1} OR t.assignee_collaborator_id = ${p2})`);
6913
7167
  }
6914
7168
  if (pagination.search) {
6915
7169
  const searchPlaceholder = this.param(params, `%${pagination.search}%`);
@@ -6955,6 +7209,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
6955
7209
  ac.display_name AS "assigneeName",
6956
7210
  au.photo_id AS "assigneeUserPhotoId",
6957
7211
  ap.avatar_id AS "assigneePersonAvatarId",
7212
+ COALESCE(task_comments.count, 0)::int AS "commentCount",
6958
7213
  t.created_at AS "createdAt",
6959
7214
  t.deleted_at AS "deletedAt"
6960
7215
  FROM operations_task t
@@ -6967,6 +7222,11 @@ let OperationsService = OperationsService_1 = class OperationsService {
6967
7222
  ON au.id = ac.user_id
6968
7223
  LEFT JOIN person ap
6969
7224
  ON ap.id = ac.person_id
7225
+ LEFT JOIN LATERAL (
7226
+ SELECT COUNT(*) AS count
7227
+ FROM operations_task_comment tc
7228
+ WHERE tc.task_id = t.id
7229
+ ) task_comments ON TRUE
6970
7230
  JOIN operations_project p
6971
7231
  ON p.id = COALESCE(t.project_id, pa.project_id)
6972
7232
  WHERE ${whereClause}
@@ -7118,7 +7378,12 @@ let OperationsService = OperationsService_1 = class OperationsService {
7118
7378
  : scenario === 'conservative'
7119
7379
  ? { revenue: 0.9, cost: 0.96, backlog: 0.82 }
7120
7380
  : { revenue: 1, cost: 1, backlog: 1 };
7121
- const params = [from, to];
7381
+ const fromDate = new Date(`${from}T00:00:00`);
7382
+ const toDate = new Date(`${to}T00:00:00`);
7383
+ const periodDays = Math.max(1, Math.floor((toDate.getTime() - fromDate.getTime()) / 86400000) + 1);
7384
+ const periodWeeks = Math.max(1, Math.ceil(periodDays / 7));
7385
+ const periodMonths = periodDays / 30.4375;
7386
+ const params = [from, to, periodMonths, periodWeeks];
7122
7387
  const where = [
7123
7388
  'p.deleted_at IS NULL',
7124
7389
  '(p.end_date IS NULL OR p.end_date >= $1::date)',
@@ -7141,6 +7406,8 @@ let OperationsService = OperationsService_1 = class OperationsService {
7141
7406
  COALESCE(assignment_stats.weekly_hours, 0)::text AS "weeklyHours",
7142
7407
  COALESCE(time_stats.actual_hours, 0)::text AS "actualHours",
7143
7408
  COALESCE(time_stats.billable_hours, 0)::text AS "billableHours",
7409
+ COALESCE(cost_stats.realized_cost, 0)::text AS "realizedCost",
7410
+ COALESCE(alloc_cost_stats.allocated_cost, 0)::text AS "allocatedCost",
7144
7411
  COALESCE(task_stats.open_tasks, 0)::text AS "openTasks",
7145
7412
  COALESCE(task_stats.backlog_hours, 0)::text AS "backlogHours",
7146
7413
  COALESCE(task_stats.future_deliveries, 0)::text AS "futureDeliveries"
@@ -7169,6 +7436,146 @@ let OperationsService = OperationsService_1 = class OperationsService {
7169
7436
  AND entry.deleted_at IS NULL
7170
7437
  AND entry.work_date BETWEEN $1::date AND $2::date
7171
7438
  ) time_stats ON TRUE
7439
+ LEFT JOIN LATERAL (
7440
+ SELECT COALESCE(
7441
+ SUM(
7442
+ entry.hours
7443
+ * (
7444
+ (
7445
+ COALESCE(collaborator_costs.salary_cost, 0)
7446
+ + COALESCE(collaborator_costs.benefits_cost, 0)
7447
+ + COALESCE(collaborator_costs.taxes_cost, 0)
7448
+ + COALESCE(collaborator_costs.tools_cost, 0)
7449
+ )
7450
+ * $3::numeric
7451
+ / GREATEST(
7452
+ COALESCE(collaborator_record.weekly_capacity_hours, 40)::numeric * $4::numeric,
7453
+ COALESCE(collaborator_hours.total_hours, 0),
7454
+ 1
7455
+ )
7456
+ )
7457
+ ),
7458
+ 0
7459
+ ) AS realized_cost
7460
+ FROM operations_timesheet_entry entry
7461
+ JOIN operations_project_assignment pa
7462
+ ON pa.id = entry.project_assignment_id
7463
+ AND pa.deleted_at IS NULL
7464
+ JOIN operations_collaborator collaborator_record
7465
+ ON collaborator_record.id = pa.collaborator_id
7466
+ AND collaborator_record.deleted_at IS NULL
7467
+ LEFT JOIN LATERAL (
7468
+ SELECT COALESCE(NULLIF(cost_totals.salary_cost, 0), compensation_history.amount, hiring_contract.budget_amount, 0) AS salary_cost,
7469
+ cost_totals.benefits_cost,
7470
+ cost_totals.taxes_cost,
7471
+ cost_totals.tools_cost
7472
+ FROM (
7473
+ SELECT COALESCE(SUM(cost.amount) FILTER (WHERE cost.recurrence::text = 'monthly' AND cost_type.slug IN ('salario-base', 'pro-labore')), 0) AS salary_cost,
7474
+ COALESCE(SUM(cost.amount) FILTER (WHERE cost.recurrence::text = 'monthly' AND cost_type.slug IN ('vale-refeicao', 'vale-alimentacao', 'vale-transporte', 'plano-saude', 'plano-odontologico', 'seguro-vida')), 0) AS benefits_cost,
7475
+ COALESCE(SUM(cost.amount) FILTER (WHERE cost.recurrence::text = 'monthly' AND cost_type.slug IN ('inss-patronal', 'fgts', 'rat-fap', 'terceiros-sistema-s', 'provisao-decimo-terceiro', 'provisao-ferias')), 0) AS taxes_cost,
7476
+ COALESCE(SUM(cost.amount) FILTER (WHERE cost.recurrence::text = 'monthly' AND cost_type.slug IN ('software-licenca', 'equipamento')), 0) AS tools_cost
7477
+ FROM operations_collaborator_cost cost
7478
+ LEFT JOIN operations_cost_type cost_type
7479
+ ON cost_type.id = cost.cost_type_id
7480
+ WHERE cost.collaborator_id = collaborator_record.id
7481
+ AND (cost.start_date IS NULL OR cost.start_date <= $2::date)
7482
+ AND (cost.end_date IS NULL OR cost.end_date >= $1::date)
7483
+ ) cost_totals
7484
+ LEFT JOIN LATERAL (
7485
+ SELECT h.amount
7486
+ FROM operations_collaborator_compensation_history h
7487
+ WHERE h.collaborator_id = collaborator_record.id
7488
+ AND (h.effective_date IS NULL OR h.effective_date <= $2::date)
7489
+ ORDER BY h.effective_date DESC NULLS LAST, h.created_at DESC
7490
+ LIMIT 1
7491
+ ) compensation_history ON TRUE
7492
+ LEFT JOIN LATERAL (
7493
+ SELECT oc.budget_amount
7494
+ FROM operations_contract oc
7495
+ WHERE oc.related_collaborator_id = collaborator_record.id
7496
+ AND oc.deleted_at IS NULL
7497
+ ORDER BY CASE WHEN oc.origin_type = 'employee_hiring' THEN 0 ELSE 1 END,
7498
+ oc.created_at DESC
7499
+ LIMIT 1
7500
+ ) hiring_contract ON TRUE
7501
+ ) collaborator_costs ON TRUE
7502
+ LEFT JOIN LATERAL (
7503
+ SELECT COALESCE(SUM(entry2.hours), 0) AS total_hours
7504
+ FROM operations_timesheet_entry entry2
7505
+ JOIN operations_project_assignment pa2
7506
+ ON pa2.id = entry2.project_assignment_id
7507
+ AND pa2.deleted_at IS NULL
7508
+ WHERE pa2.collaborator_id = collaborator_record.id
7509
+ AND entry2.deleted_at IS NULL
7510
+ AND entry2.work_date BETWEEN $1::date AND $2::date
7511
+ ) collaborator_hours ON TRUE
7512
+ WHERE pa.project_id = p.id
7513
+ AND entry.deleted_at IS NULL
7514
+ AND entry.work_date BETWEEN $1::date AND $2::date
7515
+ ) cost_stats ON TRUE
7516
+ LEFT JOIN LATERAL (
7517
+ SELECT COALESCE(
7518
+ SUM(
7519
+ pa.weekly_hours
7520
+ * (
7521
+ (
7522
+ COALESCE(alloc_costs.salary_cost, 0)
7523
+ + COALESCE(alloc_costs.benefits_cost, 0)
7524
+ + COALESCE(alloc_costs.taxes_cost, 0)
7525
+ + COALESCE(alloc_costs.tools_cost, 0)
7526
+ )
7527
+ * $3::numeric
7528
+ / GREATEST(
7529
+ COALESCE(alloc_col.weekly_capacity_hours, 40)::numeric,
7530
+ 1
7531
+ )
7532
+ )
7533
+ ),
7534
+ 0
7535
+ ) AS allocated_cost
7536
+ FROM operations_project_assignment pa
7537
+ JOIN operations_collaborator alloc_col
7538
+ ON alloc_col.id = pa.collaborator_id
7539
+ AND alloc_col.deleted_at IS NULL
7540
+ LEFT JOIN LATERAL (
7541
+ SELECT COALESCE(NULLIF(ct.salary_cost, 0), ch.amount, hc.budget_amount, 0) AS salary_cost,
7542
+ ct.benefits_cost,
7543
+ ct.taxes_cost,
7544
+ ct.tools_cost
7545
+ FROM (
7546
+ SELECT COALESCE(SUM(c.amount) FILTER (WHERE c.recurrence::text = 'monthly' AND ct2.slug IN ('salario-base', 'pro-labore')), 0) AS salary_cost,
7547
+ COALESCE(SUM(c.amount) FILTER (WHERE c.recurrence::text = 'monthly' AND ct2.slug IN ('vale-refeicao', 'vale-alimentacao', 'vale-transporte', 'plano-saude', 'plano-odontologico', 'seguro-vida')), 0) AS benefits_cost,
7548
+ COALESCE(SUM(c.amount) FILTER (WHERE c.recurrence::text = 'monthly' AND ct2.slug IN ('inss-patronal', 'fgts', 'rat-fap', 'terceiros-sistema-s', 'provisao-decimo-terceiro', 'provisao-ferias')), 0) AS taxes_cost,
7549
+ COALESCE(SUM(c.amount) FILTER (WHERE c.recurrence::text = 'monthly' AND ct2.slug IN ('software-licenca', 'equipamento')), 0) AS tools_cost
7550
+ FROM operations_collaborator_cost c
7551
+ LEFT JOIN operations_cost_type ct2
7552
+ ON ct2.id = c.cost_type_id
7553
+ WHERE c.collaborator_id = alloc_col.id
7554
+ AND (c.start_date IS NULL OR c.start_date <= $2::date)
7555
+ AND (c.end_date IS NULL OR c.end_date >= $1::date)
7556
+ ) ct
7557
+ LEFT JOIN LATERAL (
7558
+ SELECT h.amount
7559
+ FROM operations_collaborator_compensation_history h
7560
+ WHERE h.collaborator_id = alloc_col.id
7561
+ AND (h.effective_date IS NULL OR h.effective_date <= $2::date)
7562
+ ORDER BY h.effective_date DESC NULLS LAST, h.created_at DESC
7563
+ LIMIT 1
7564
+ ) ch ON TRUE
7565
+ LEFT JOIN LATERAL (
7566
+ SELECT oc.budget_amount
7567
+ FROM operations_contract oc
7568
+ WHERE oc.related_collaborator_id = alloc_col.id
7569
+ AND oc.deleted_at IS NULL
7570
+ ORDER BY CASE WHEN oc.origin_type = 'employee_hiring' THEN 0 ELSE 1 END,
7571
+ oc.created_at DESC
7572
+ LIMIT 1
7573
+ ) hc ON TRUE
7574
+ ) alloc_costs ON TRUE
7575
+ WHERE pa.project_id = p.id
7576
+ AND pa.deleted_at IS NULL
7577
+ AND pa.status IN ('planned', 'active')
7578
+ ) alloc_cost_stats ON TRUE
7172
7579
  LEFT JOIN LATERAL (
7173
7580
  SELECT COUNT(*) FILTER (WHERE task.status IN ('todo', 'doing', 'review')) AS open_tasks,
7174
7581
  COALESCE(SUM(task.estimate_hours) FILTER (WHERE task.status IN ('todo', 'doing', 'review')), 0) AS backlog_hours,
@@ -7179,18 +7586,20 @@ let OperationsService = OperationsService_1 = class OperationsService {
7179
7586
  ) task_stats ON TRUE
7180
7587
  WHERE ${where.join(' AND ')}
7181
7588
  ORDER BY p.name ASC`, params);
7182
- const fromDate = new Date(`${from}T00:00:00`);
7183
- const toDate = new Date(`${to}T00:00:00`);
7184
- const periodWeeks = Math.max(1, Math.ceil((toDate.getTime() - fromDate.getTime()) / 604800000));
7185
7589
  const rows = dbRows
7186
7590
  .map((row) => {
7187
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
7591
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
7188
7592
  const progress = Number((_a = row.progressPercent) !== null && _a !== void 0 ? _a : 0);
7189
7593
  const contractedRevenue = Number((_b = row.contractedRevenue) !== null && _b !== void 0 ? _b : 0);
7190
7594
  const recognizedRevenue = contractedRevenue * (progress / 100);
7191
7595
  const actualHours = Number((_c = row.actualHours) !== null && _c !== void 0 ? _c : 0);
7192
7596
  const plannedHours = Math.max(Number((_d = row.weeklyHours) !== null && _d !== void 0 ? _d : 0) * periodWeeks, actualHours);
7193
- const realizedCost = 0;
7597
+ const realizedCost = Number((_e = row.realizedCost) !== null && _e !== void 0 ? _e : 0);
7598
+ const allocatedCost = Number((_f = row.allocatedCost) !== null && _f !== void 0 ? _f : 0);
7599
+ const consumedHoursCost = realizedCost;
7600
+ const idlenessHours = Math.max(plannedHours - actualHours, 0);
7601
+ const idlenessRate = plannedHours > 0 ? (idlenessHours / plannedHours) * 100 : 0;
7602
+ const idlenessCost = Math.max(allocatedCost - consumedHoursCost, 0);
7194
7603
  const reportStatus = row.status === 'paused'
7195
7604
  ? 'paused'
7196
7605
  : row.status === 'at_risk'
@@ -7206,9 +7615,9 @@ let OperationsService = OperationsService_1 = class OperationsService {
7206
7615
  return {
7207
7616
  id: Number(row.id),
7208
7617
  name: row.name,
7209
- client: (_e = row.client) !== null && _e !== void 0 ? _e : '-',
7210
- manager: (_f = row.manager) !== null && _f !== void 0 ? _f : '-',
7211
- squad: String((_g = row.squad) !== null && _g !== void 0 ? _g : '-').replace(/_/g, ' '),
7618
+ client: (_g = row.client) !== null && _g !== void 0 ? _g : '-',
7619
+ manager: (_h = row.manager) !== null && _h !== void 0 ? _h : '-',
7620
+ squad: String((_j = row.squad) !== null && _j !== void 0 ? _j : '-').replace(/_/g, ' '),
7212
7621
  status: reportStatus,
7213
7622
  contractType: row.contractType === 'monthly_retainer'
7214
7623
  ? 'retainer'
@@ -7221,7 +7630,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
7221
7630
  contractedRevenue,
7222
7631
  recognizedRevenue,
7223
7632
  realizedCost,
7224
- forecastCost: realizedCost,
7633
+ forecastCost: realizedCost * multiplier.cost,
7225
7634
  teamCost: realizedCost,
7226
7635
  infraCost: 0,
7227
7636
  licenseCost: 0,
@@ -7229,14 +7638,18 @@ let OperationsService = OperationsService_1 = class OperationsService {
7229
7638
  reworkCost: 0,
7230
7639
  plannedHours,
7231
7640
  actualHours,
7232
- billableHours: Number((_h = row.billableHours) !== null && _h !== void 0 ? _h : 0),
7641
+ billableHours: Number((_k = row.billableHours) !== null && _k !== void 0 ? _k : 0),
7233
7642
  reworkHours: 0,
7234
- internalHours: Math.max(actualHours - Number((_j = row.billableHours) !== null && _j !== void 0 ? _j : 0), 0),
7643
+ internalHours: Math.max(actualHours - Number((_l = row.billableHours) !== null && _l !== void 0 ? _l : 0), 0),
7235
7644
  allocatedCapacity: plannedHours ? (actualHours / plannedHours) * 100 : 0,
7236
7645
  physicalProgress: progress,
7237
7646
  financialProgress: contractedRevenue ? (recognizedRevenue / contractedRevenue) * 100 : 0,
7238
7647
  backlogValue: Math.max(contractedRevenue - recognizedRevenue, 0),
7239
- futureDeliveries: Number((_k = row.futureDeliveries) !== null && _k !== void 0 ? _k : 0),
7648
+ futureDeliveries: Number((_m = row.futureDeliveries) !== null && _m !== void 0 ? _m : 0),
7649
+ allocatedCost,
7650
+ consumedHoursCost,
7651
+ idlenessRate,
7652
+ idlenessCost,
7240
7653
  risk,
7241
7654
  recommendation: risk === 'alto'
7242
7655
  ? 'Revisar escopo, prazo ou capacidade alocada.'
@@ -7259,6 +7672,9 @@ let OperationsService = OperationsService_1 = class OperationsService {
7259
7672
  acc.avgDeadline += row.physicalProgress;
7260
7673
  acc.avgAllocation += row.allocatedCapacity;
7261
7674
  acc.atRisk += row.risk === 'alto' ? 1 : 0;
7675
+ acc.allocatedCost += row.allocatedCost;
7676
+ acc.consumedHoursCost += row.consumedHoursCost;
7677
+ acc.idlenessCost += row.idlenessCost;
7262
7678
  return acc;
7263
7679
  }, {
7264
7680
  contractedRevenue: 0,
@@ -7276,12 +7692,21 @@ let OperationsService = OperationsService_1 = class OperationsService {
7276
7692
  avgAllocation: 0,
7277
7693
  atRisk: 0,
7278
7694
  burnRate: 0,
7695
+ allocatedCost: 0,
7696
+ consumedHoursCost: 0,
7697
+ idlenessCost: 0,
7698
+ idlenessRate: 0,
7699
+ plannedProfit: 0,
7279
7700
  });
7280
7701
  summary.profit = summary.recognizedRevenue - summary.realizedCost;
7281
7702
  summary.margin = summary.recognizedRevenue ? (summary.profit / summary.recognizedRevenue) * 100 : 0;
7282
7703
  summary.avgDeadline = rows.length ? summary.avgDeadline / rows.length : 0;
7283
7704
  summary.avgAllocation = rows.length ? summary.avgAllocation / rows.length : 0;
7284
7705
  summary.burnRate = summary.plannedHours ? (summary.actualHours / summary.plannedHours) * 100 : 0;
7706
+ summary.plannedProfit = summary.contractedRevenue - summary.allocatedCost;
7707
+ summary.idlenessRate = summary.plannedHours > 0
7708
+ ? Math.max(0, (summary.plannedHours - summary.actualHours) / summary.plannedHours * 100)
7709
+ : 0;
7285
7710
  const forecast = Array.from({ length: 12 }, (_, index) => {
7286
7711
  const monthDate = new Date(fromDate);
7287
7712
  monthDate.setMonth(fromDate.getMonth() + index);
@@ -7370,6 +7795,11 @@ let OperationsService = OperationsService_1 = class OperationsService {
7370
7795
  : scenario === 'conservative'
7371
7796
  ? { revenue: 0.9, cost: 0.96, capacity: 0.94 }
7372
7797
  : { revenue: 1, cost: 1, capacity: 1 };
7798
+ const fromDate = new Date(`${from}T00:00:00`);
7799
+ const toDate = new Date(`${to}T00:00:00`);
7800
+ const periodDays = Math.max(1, Math.floor((toDate.getTime() - fromDate.getTime()) / 86400000) + 1);
7801
+ const periodWeeks = Math.max(1, Math.ceil(periodDays / 7));
7802
+ const periodMonths = periodDays / 30.4375;
7373
7803
  const params = [from, to];
7374
7804
  const where = [
7375
7805
  'c.deleted_at IS NULL',
@@ -7396,9 +7826,14 @@ let OperationsService = OperationsService_1 = class OperationsService {
7396
7826
  COALESCE(cost_stats.taxes_cost, 0)::text AS "taxesCost",
7397
7827
  COALESCE(cost_stats.tools_cost, 0)::text AS "toolsCost",
7398
7828
  COALESCE(value_stats.billable_value, 0)::text AS "billableValue",
7829
+ COALESCE(assignment_stats.planned_allocated_hours, 0)::text AS "plannedAllocatedHours",
7830
+ COALESCE(assignment_stats.planned_billable_hours, 0)::text AS "plannedBillableHours",
7831
+ COALESCE(task_stats.open_task_hours, 0)::text AS "openTaskHours",
7832
+ COALESCE(task_stats.open_task_billable_hours, 0)::text AS "openTaskBillableHours",
7833
+ COALESCE(task_stats.open_tasks, 0)::text AS "openTasks",
7399
7834
  COALESCE(value_stats.allocated_hours, 0)::text AS "allocatedHours",
7400
7835
  COALESCE(value_stats.billable_hours, 0)::text AS "billableHours",
7401
- COALESCE(project_stats.projects, 0)::text AS projects
7836
+ COALESCE(assignment_stats.projects, 0)::text AS projects
7402
7837
  FROM operations_collaborator c
7403
7838
  LEFT JOIN person person_record ON person_record.id = c.person_id
7404
7839
  LEFT JOIN operations_department department_record
@@ -7411,16 +7846,85 @@ let OperationsService = OperationsService_1 = class OperationsService {
7411
7846
  ON collaborator_type.id = c.collaborator_type_id
7412
7847
  AND collaborator_type.deleted_at IS NULL
7413
7848
  LEFT JOIN LATERAL (
7414
- SELECT COALESCE(SUM(cost.amount) FILTER (WHERE cost.recurrence::text = 'monthly' AND cost_type.slug IN ('salario-base', 'pro-labore')), 0) AS salary_cost,
7415
- COALESCE(SUM(cost.amount) FILTER (WHERE cost.recurrence::text = 'monthly' AND cost_type.slug IN ('vale-refeicao', 'vale-alimentacao', 'vale-transporte', 'plano-saude', 'plano-odontologico', 'seguro-vida')), 0) AS benefits_cost,
7416
- COALESCE(SUM(cost.amount) FILTER (WHERE cost.recurrence::text = 'monthly' AND cost_type.slug IN ('inss-patronal', 'fgts', 'rat-fap', 'terceiros-sistema-s', 'provisao-decimo-terceiro', 'provisao-ferias')), 0) AS taxes_cost,
7417
- COALESCE(SUM(cost.amount) FILTER (WHERE cost.recurrence::text = 'monthly' AND cost_type.slug IN ('software-licenca', 'equipamento')), 0) AS tools_cost
7418
- FROM operations_collaborator_cost cost
7419
- LEFT JOIN operations_cost_type cost_type ON cost_type.id = cost.cost_type_id
7420
- WHERE cost.collaborator_id = c.id
7421
- AND (cost.start_date IS NULL OR cost.start_date <= $2::date)
7422
- AND (cost.end_date IS NULL OR cost.end_date >= $1::date)
7849
+ SELECT COALESCE(NULLIF(cost_totals.salary_cost, 0), compensation_history.amount, hiring_contract.budget_amount, 0) AS salary_cost,
7850
+ cost_totals.benefits_cost,
7851
+ cost_totals.taxes_cost,
7852
+ cost_totals.tools_cost
7853
+ FROM (
7854
+ SELECT COALESCE(SUM(cost.amount) FILTER (WHERE cost.recurrence::text = 'monthly' AND cost_type.slug IN ('salario-base', 'pro-labore')), 0) AS salary_cost,
7855
+ COALESCE(SUM(cost.amount) FILTER (WHERE cost.recurrence::text = 'monthly' AND cost_type.slug IN ('vale-refeicao', 'vale-alimentacao', 'vale-transporte', 'plano-saude', 'plano-odontologico', 'seguro-vida')), 0) AS benefits_cost,
7856
+ COALESCE(SUM(cost.amount) FILTER (WHERE cost.recurrence::text = 'monthly' AND cost_type.slug IN ('inss-patronal', 'fgts', 'rat-fap', 'terceiros-sistema-s', 'provisao-decimo-terceiro', 'provisao-ferias')), 0) AS taxes_cost,
7857
+ COALESCE(SUM(cost.amount) FILTER (WHERE cost.recurrence::text = 'monthly' AND cost_type.slug IN ('software-licenca', 'equipamento')), 0) AS tools_cost
7858
+ FROM operations_collaborator_cost cost
7859
+ LEFT JOIN operations_cost_type cost_type
7860
+ ON cost_type.id = cost.cost_type_id
7861
+ WHERE cost.collaborator_id = c.id
7862
+ AND (cost.start_date IS NULL OR cost.start_date <= $2::date)
7863
+ AND (cost.end_date IS NULL OR cost.end_date >= $1::date)
7864
+ ) cost_totals
7865
+ LEFT JOIN LATERAL (
7866
+ SELECT h.amount
7867
+ FROM operations_collaborator_compensation_history h
7868
+ WHERE h.collaborator_id = c.id
7869
+ AND (h.effective_date IS NULL OR h.effective_date <= $2::date)
7870
+ ORDER BY h.effective_date DESC NULLS LAST, h.created_at DESC
7871
+ LIMIT 1
7872
+ ) compensation_history ON TRUE
7873
+ LEFT JOIN LATERAL (
7874
+ SELECT oc.budget_amount
7875
+ FROM operations_contract oc
7876
+ WHERE oc.related_collaborator_id = c.id
7877
+ AND oc.deleted_at IS NULL
7878
+ ORDER BY CASE WHEN oc.origin_type = 'employee_hiring' THEN 0 ELSE 1 END,
7879
+ oc.created_at DESC
7880
+ LIMIT 1
7881
+ ) hiring_contract ON TRUE
7423
7882
  ) cost_stats ON TRUE
7883
+ LEFT JOIN LATERAL (
7884
+ SELECT COALESCE(
7885
+ SUM(
7886
+ COALESCE(
7887
+ pa.weekly_hours,
7888
+ COALESCE(c.weekly_capacity_hours, 40) * COALESCE(pa.allocation_percent, 0) / 100
7889
+ ) * GREATEST(
7890
+ CEIL(
7891
+ (
7892
+ LEAST(COALESCE(pa.end_date, $2::date), $2::date)
7893
+ - GREATEST(COALESCE(pa.start_date, $1::date), $1::date)
7894
+ + 1
7895
+ ) / 7.0
7896
+ ),
7897
+ 0
7898
+ )
7899
+ ),
7900
+ 0
7901
+ ) AS planned_allocated_hours,
7902
+ COALESCE(
7903
+ SUM(
7904
+ COALESCE(
7905
+ pa.weekly_hours,
7906
+ COALESCE(c.weekly_capacity_hours, 40) * COALESCE(pa.allocation_percent, 0) / 100
7907
+ ) * GREATEST(
7908
+ CEIL(
7909
+ (
7910
+ LEAST(COALESCE(pa.end_date, $2::date), $2::date)
7911
+ - GREATEST(COALESCE(pa.start_date, $1::date), $1::date)
7912
+ + 1
7913
+ ) / 7.0
7914
+ ),
7915
+ 0
7916
+ )
7917
+ ) FILTER (WHERE pa.is_billable = true),
7918
+ 0
7919
+ ) AS planned_billable_hours,
7920
+ COUNT(DISTINCT pa.project_id) AS projects
7921
+ FROM operations_project_assignment pa
7922
+ WHERE pa.collaborator_id = c.id
7923
+ AND pa.deleted_at IS NULL
7924
+ AND pa.status IN ('planned', 'active')
7925
+ AND (pa.start_date IS NULL OR pa.start_date <= $2::date)
7926
+ AND (pa.end_date IS NULL OR pa.end_date >= $1::date)
7927
+ ) assignment_stats ON TRUE
7424
7928
  LEFT JOIN LATERAL (
7425
7929
  SELECT COALESCE(SUM(entry.hours), 0) AS allocated_hours,
7426
7930
  COALESCE(SUM(entry.hours) FILTER (WHERE pa.is_billable = true), 0) AS billable_hours,
@@ -7434,48 +7938,62 @@ let OperationsService = OperationsService_1 = class OperationsService {
7434
7938
  AND entry.work_date BETWEEN $1::date AND $2::date
7435
7939
  ) value_stats ON TRUE
7436
7940
  LEFT JOIN LATERAL (
7437
- SELECT COUNT(DISTINCT pa.project_id) AS projects
7438
- FROM operations_project_assignment pa
7439
- WHERE pa.collaborator_id = c.id
7941
+ SELECT COUNT(*) AS open_tasks,
7942
+ COALESCE(SUM(COALESCE(task.estimate_hours, 0)), 0) AS open_task_hours,
7943
+ COALESCE(
7944
+ SUM(COALESCE(task.estimate_hours, 0)) FILTER (WHERE pa.is_billable = true),
7945
+ 0
7946
+ ) AS open_task_billable_hours
7947
+ FROM operations_task task
7948
+ LEFT JOIN operations_project_assignment pa
7949
+ ON pa.id = task.project_assignment_id
7440
7950
  AND pa.deleted_at IS NULL
7441
- AND pa.status IN ('planned', 'active')
7442
- ) project_stats ON TRUE
7951
+ WHERE task.deleted_at IS NULL
7952
+ AND task.status IN ('todo', 'doing', 'review')
7953
+ AND (
7954
+ task.assignee_collaborator_id = c.id
7955
+ OR pa.collaborator_id = c.id
7956
+ )
7957
+ ) task_stats ON TRUE
7443
7958
  WHERE ${where.join(' AND ')}
7444
7959
  ORDER BY name ASC`, params);
7445
- const fromDate = new Date(`${from}T00:00:00`);
7446
- const toDate = new Date(`${to}T00:00:00`);
7447
- const periodWeeks = Math.max(1, Math.ceil((toDate.getTime() - fromDate.getTime()) / 604800000));
7448
7960
  const rows = dbRows.map((row) => {
7449
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
7450
- const salaryCost = Number((_a = row.salaryCost) !== null && _a !== void 0 ? _a : 0);
7451
- const benefitsCost = Number((_b = row.benefitsCost) !== null && _b !== void 0 ? _b : 0);
7452
- const taxesCost = Number((_c = row.taxesCost) !== null && _c !== void 0 ? _c : 0);
7453
- const toolsCost = Number((_d = row.toolsCost) !== null && _d !== void 0 ? _d : 0);
7961
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
7962
+ const salaryCost = Number((_a = row.salaryCost) !== null && _a !== void 0 ? _a : 0) * periodMonths;
7963
+ const benefitsCost = Number((_b = row.benefitsCost) !== null && _b !== void 0 ? _b : 0) * periodMonths;
7964
+ const taxesCost = Number((_c = row.taxesCost) !== null && _c !== void 0 ? _c : 0) * periodMonths;
7965
+ const toolsCost = Number((_d = row.toolsCost) !== null && _d !== void 0 ? _d : 0) * periodMonths;
7454
7966
  const availableHours = Number((_e = row.weeklyCapacityHours) !== null && _e !== void 0 ? _e : 40) * periodWeeks;
7455
- const allocatedHours = Number((_f = row.allocatedHours) !== null && _f !== void 0 ? _f : 0);
7456
- const billableHours = Number((_g = row.billableHours) !== null && _g !== void 0 ? _g : 0);
7967
+ const plannedAllocatedHours = Number((_f = row.plannedAllocatedHours) !== null && _f !== void 0 ? _f : 0);
7968
+ const plannedBillableHours = Number((_g = row.plannedBillableHours) !== null && _g !== void 0 ? _g : 0);
7969
+ const openTaskHours = Number((_h = row.openTaskHours) !== null && _h !== void 0 ? _h : 0);
7970
+ const openTaskBillableHours = Number((_j = row.openTaskBillableHours) !== null && _j !== void 0 ? _j : 0);
7971
+ const actualAllocatedHours = Number((_k = row.allocatedHours) !== null && _k !== void 0 ? _k : 0);
7972
+ const actualBillableHours = Number((_l = row.billableHours) !== null && _l !== void 0 ? _l : 0);
7973
+ const allocatedHours = Math.max(actualAllocatedHours, plannedAllocatedHours, openTaskHours);
7974
+ const billableHours = Math.max(actualBillableHours, plannedBillableHours, openTaskBillableHours);
7457
7975
  const allocation = availableHours ? (allocatedHours / availableHours) * 100 : 0;
7458
7976
  const risk = allocation >= 98 ? 'alto' : allocation < 75 ? 'médio' : 'baixo';
7459
7977
  return {
7460
7978
  id: Number(row.id),
7461
7979
  name: row.name,
7462
- role: (_h = row.role) !== null && _h !== void 0 ? _h : '-',
7463
- seniority: (_j = row.seniority) !== null && _j !== void 0 ? _j : '-',
7464
- department: (_k = row.department) !== null && _k !== void 0 ? _k : '-',
7465
- contractType: (_l = row.contractType) !== null && _l !== void 0 ? _l : '-',
7980
+ role: (_m = row.role) !== null && _m !== void 0 ? _m : '-',
7981
+ seniority: (_o = row.seniority) !== null && _o !== void 0 ? _o : '-',
7982
+ department: (_p = row.department) !== null && _p !== void 0 ? _p : '-',
7983
+ contractType: (_q = row.contractType) !== null && _q !== void 0 ? _q : '-',
7466
7984
  startDate: row.startDate,
7467
7985
  endDate: row.endDate,
7468
7986
  salaryCost,
7469
7987
  benefitsCost,
7470
7988
  taxesCost,
7471
7989
  toolsCost,
7472
- billableValue: Number((_m = row.billableValue) !== null && _m !== void 0 ? _m : 0),
7990
+ billableValue: Number((_r = row.billableValue) !== null && _r !== void 0 ? _r : 0),
7473
7991
  availableHours,
7474
7992
  allocatedHours,
7475
7993
  billableHours,
7476
7994
  internalHours: Math.max(allocatedHours - billableHours, 0),
7477
7995
  overtimeHours: Math.max(allocatedHours - availableHours, 0),
7478
- projects: Number((_o = row.projects) !== null && _o !== void 0 ? _o : 0),
7996
+ projects: Number((_s = row.projects) !== null && _s !== void 0 ? _s : 0),
7479
7997
  risk,
7480
7998
  recommendation: risk === 'alto'
7481
7999
  ? 'Reduzir sobrecarga ou redistribuir entregas.'
@@ -7523,7 +8041,11 @@ let OperationsService = OperationsService_1 = class OperationsService {
7523
8041
  summary.freeHours = Math.max(summary.availableHours - summary.allocatedHours, 0);
7524
8042
  summary.allocation = summary.availableHours ? (summary.allocatedHours / summary.availableHours) * 100 : 0;
7525
8043
  summary.utilization = summary.availableHours ? (summary.billableHours / summary.availableHours) * 100 : 0;
7526
- summary.hourlyCost = summary.allocatedHours ? summary.cost / summary.allocatedHours : 0;
8044
+ summary.hourlyCost = summary.allocatedHours
8045
+ ? summary.cost / summary.allocatedHours
8046
+ : summary.availableHours
8047
+ ? summary.cost / summary.availableHours
8048
+ : 0;
7527
8049
  const forecast = Array.from({ length: 12 }, (_, index) => {
7528
8050
  const monthDate = new Date(fromDate);
7529
8051
  monthDate.setMonth(fromDate.getMonth() + index);
@@ -7709,6 +8231,1149 @@ let OperationsService = OperationsService_1 = class OperationsService {
7709
8231
  await this.prisma.$queryRawUnsafe(`DELETE FROM operations_collaborator_cost WHERE id = $1`, costId);
7710
8232
  return { success: true };
7711
8233
  }
8234
+ // ──────────────────────────────────────────────────────────────────────────
8235
+ // Project Cost Categories
8236
+ // ──────────────────────────────────────────────────────────────────────────
8237
+ async listProjectCostCategories(userId, filters = {}) {
8238
+ var _a;
8239
+ await this.getActorContext(userId);
8240
+ const localeId = await this.resolvePreferredLocaleId();
8241
+ const params = [localeId];
8242
+ const where = ['pcc.deleted_at IS NULL'];
8243
+ if (filters.is_active === true) {
8244
+ where.push('pcc.is_active = true');
8245
+ }
8246
+ if ((_a = filters.search) === null || _a === void 0 ? void 0 : _a.trim()) {
8247
+ const p = this.param(params, `%${filters.search.trim()}%`);
8248
+ where.push(`(COALESCE(pccl.name, pcc.slug) ILIKE ${p} OR COALESCE(pcc.slug, '') ILIKE ${p})`);
8249
+ }
8250
+ const whereClause = `WHERE ${where.join(' AND ')}`;
8251
+ return this.queryRows(`SELECT pcc.id,
8252
+ pcc.slug,
8253
+ COALESCE(pccl.name, pcc.slug) AS name,
8254
+ pccl.description,
8255
+ pcc.icon,
8256
+ pcc.color,
8257
+ pcc.is_active AS "isActive",
8258
+ pcc.sort_order AS "sortOrder",
8259
+ pcc.created_at AS "createdAt"
8260
+ FROM operations_project_cost_category pcc
8261
+ LEFT JOIN LATERAL (
8262
+ SELECT l.name, l.description
8263
+ FROM operations_project_cost_category_locale l
8264
+ WHERE l.operations_project_cost_category_id = pcc.id
8265
+ ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC,
8266
+ l.id ASC
8267
+ LIMIT 1
8268
+ ) pccl ON TRUE
8269
+ ${whereClause}
8270
+ ORDER BY pcc.sort_order ASC, COALESCE(pccl.name, pcc.slug) ASC`, params);
8271
+ }
8272
+ async createProjectCostCategory(userId, data) {
8273
+ var _a;
8274
+ const actor = await this.getActorContext(userId);
8275
+ this.ensureDirector(actor);
8276
+ const slug = (_a = data.slug) === null || _a === void 0 ? void 0 : _a.trim();
8277
+ if (!slug) {
8278
+ throw new common_1.BadRequestException('Cost category slug is required.');
8279
+ }
8280
+ return this.prisma.$transaction(async (tx) => {
8281
+ var _a, _b, _c, _d, _e, _f;
8282
+ const localeId = await this.resolvePreferredLocaleId(tx);
8283
+ const created = (await tx.$queryRawUnsafe(`INSERT INTO operations_project_cost_category (slug, icon, color, is_active, sort_order, created_at, updated_at)
8284
+ VALUES ($1, $2, $3, $4, $5, NOW(), NOW())
8285
+ RETURNING id`, slug, (_a = data.icon) !== null && _a !== void 0 ? _a : null, (_b = data.color) !== null && _b !== void 0 ? _b : null, (_c = data.is_active) !== null && _c !== void 0 ? _c : true, (_d = data.sort_order) !== null && _d !== void 0 ? _d : 0));
8286
+ const createdId = (_e = created[0]) === null || _e === void 0 ? void 0 : _e.id;
8287
+ if (!createdId) {
8288
+ throw new common_1.BadRequestException('Unable to create project cost category.');
8289
+ }
8290
+ const name = typeof data.name === 'string' ? data.name : (data.name ? JSON.stringify(data.name) : slug);
8291
+ const description = typeof data.description === 'string' ? data.description : (data.description ? JSON.stringify(data.description) : null);
8292
+ if (localeId && name) {
8293
+ await tx.$executeRawUnsafe(`INSERT INTO operations_project_cost_category_locale (operations_project_cost_category_id, locale_id, name, description)
8294
+ VALUES ($1, $2, $3, $4)`, createdId, localeId, name, description !== null && description !== void 0 ? description : null);
8295
+ }
8296
+ const rows = (await tx.$queryRawUnsafe(`SELECT pcc.id,
8297
+ pcc.slug,
8298
+ COALESCE(pccl.name, pcc.slug) AS name,
8299
+ pccl.description,
8300
+ pcc.icon,
8301
+ pcc.color,
8302
+ pcc.is_active AS "isActive",
8303
+ pcc.sort_order AS "sortOrder",
8304
+ pcc.created_at AS "createdAt"
8305
+ FROM operations_project_cost_category pcc
8306
+ LEFT JOIN operations_project_cost_category_locale pccl
8307
+ ON pccl.operations_project_cost_category_id = pcc.id AND pccl.locale_id = $2
8308
+ WHERE pcc.id = $1`, createdId, localeId));
8309
+ return (_f = rows[0]) !== null && _f !== void 0 ? _f : null;
8310
+ });
8311
+ }
8312
+ async updateProjectCostCategory(userId, id, data) {
8313
+ const actor = await this.getActorContext(userId);
8314
+ this.ensureDirector(actor);
8315
+ const category = await this.querySingle(`SELECT id FROM operations_project_cost_category WHERE id = $1 AND deleted_at IS NULL LIMIT 1`, [id]);
8316
+ if (!category) {
8317
+ throw new common_1.NotFoundException('Project cost category not found.');
8318
+ }
8319
+ const sets = [];
8320
+ const params = [];
8321
+ if (data.slug !== undefined)
8322
+ sets.push(`slug = ${this.param(params, data.slug)}`);
8323
+ if (data.icon !== undefined)
8324
+ sets.push(`icon = ${this.param(params, data.icon)}`);
8325
+ if (data.color !== undefined)
8326
+ sets.push(`color = ${this.param(params, data.color)}`);
8327
+ if (data.is_active !== undefined)
8328
+ sets.push(`is_active = ${this.param(params, data.is_active)}`);
8329
+ if (data.sort_order !== undefined)
8330
+ sets.push(`sort_order = ${this.param(params, data.sort_order)}`);
8331
+ if (sets.length > 0) {
8332
+ sets.push(`updated_at = NOW()`);
8333
+ await this.prisma.$queryRawUnsafe(`UPDATE operations_project_cost_category SET ${sets.join(', ')} WHERE id = ${this.param(params, id)}`, ...params);
8334
+ }
8335
+ if (data.name !== undefined || data.description !== undefined) {
8336
+ const localeId = await this.resolvePreferredLocaleId();
8337
+ if (localeId) {
8338
+ const name = typeof data.name === 'string' ? data.name : (data.name ? JSON.stringify(data.name) : undefined);
8339
+ const description = typeof data.description === 'string' ? data.description : (data.description ? JSON.stringify(data.description) : null);
8340
+ const existing = await this.querySingle(`SELECT id FROM operations_project_cost_category_locale WHERE operations_project_cost_category_id = $1 AND locale_id = $2 LIMIT 1`, [id, localeId]);
8341
+ if (existing) {
8342
+ const localeSets = [];
8343
+ const localeParams = [];
8344
+ if (name !== undefined)
8345
+ localeSets.push(`name = ${this.param(localeParams, name)}`);
8346
+ if (description !== undefined)
8347
+ localeSets.push(`description = ${this.param(localeParams, description)}`);
8348
+ if (localeSets.length > 0) {
8349
+ await this.prisma.$queryRawUnsafe(`UPDATE operations_project_cost_category_locale SET ${localeSets.join(', ')} WHERE operations_project_cost_category_id = ${this.param(localeParams, id)} AND locale_id = ${this.param(localeParams, localeId)}`, ...localeParams);
8350
+ }
8351
+ }
8352
+ else if (name) {
8353
+ await this.prisma.$queryRawUnsafe(`INSERT INTO operations_project_cost_category_locale (operations_project_cost_category_id, locale_id, name, description) VALUES ($1, $2, $3, $4)`, id, localeId, name, description !== null && description !== void 0 ? description : null);
8354
+ }
8355
+ }
8356
+ }
8357
+ return this.querySingle(`SELECT id, slug FROM operations_project_cost_category WHERE id = $1`, [id]);
8358
+ }
8359
+ async deleteProjectCostCategory(userId, id) {
8360
+ const actor = await this.getActorContext(userId);
8361
+ this.ensureDirector(actor);
8362
+ const category = await this.querySingle(`SELECT id FROM operations_project_cost_category WHERE id = $1 AND deleted_at IS NULL LIMIT 1`, [id]);
8363
+ if (!category) {
8364
+ throw new common_1.NotFoundException('Project cost category not found.');
8365
+ }
8366
+ await this.prisma.$queryRawUnsafe(`UPDATE operations_project_cost_category SET deleted_at = NOW() WHERE id = $1`, id);
8367
+ return { success: true };
8368
+ }
8369
+ async getProjectCostCategory(userId, id) {
8370
+ await this.getActorContext(userId);
8371
+ const localeId = await this.resolvePreferredLocaleId();
8372
+ const row = await this.querySingle(`SELECT pcc.id,
8373
+ pcc.slug,
8374
+ COALESCE(pccl.name, pcc.slug) AS name,
8375
+ pccl.description,
8376
+ pcc.icon,
8377
+ pcc.color,
8378
+ pcc.is_active AS "isActive",
8379
+ pcc.sort_order AS "sortOrder",
8380
+ pcc.created_at AS "createdAt"
8381
+ FROM operations_project_cost_category pcc
8382
+ LEFT JOIN LATERAL (
8383
+ SELECT l.name, l.description
8384
+ FROM operations_project_cost_category_locale l
8385
+ WHERE l.operations_project_cost_category_id = pcc.id
8386
+ ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC,
8387
+ l.id ASC
8388
+ LIMIT 1
8389
+ ) pccl ON TRUE
8390
+ WHERE pcc.id = $2 AND pcc.deleted_at IS NULL`, [localeId, id]);
8391
+ if (!row) {
8392
+ throw new common_1.NotFoundException('Project cost category not found.');
8393
+ }
8394
+ return row;
8395
+ }
8396
+ // ──────────────────────────────────────────────────────────────────────────
8397
+ // Project Cost Types
8398
+ // ──────────────────────────────────────────────────────────────────────────
8399
+ async listProjectCostTypes(userId, filters = {}) {
8400
+ var _a;
8401
+ await this.getActorContext(userId);
8402
+ const localeId = await this.resolvePreferredLocaleId();
8403
+ const params = [localeId];
8404
+ const where = ['pct.deleted_at IS NULL'];
8405
+ if (filters.is_active === true) {
8406
+ where.push('pct.is_active = true');
8407
+ }
8408
+ if (filters.category_id) {
8409
+ where.push(`pct.category_id = ${this.param(params, filters.category_id)}`);
8410
+ }
8411
+ if (filters.default_calculation_type) {
8412
+ where.push(`pct.default_calculation_type = ${this.param(params, filters.default_calculation_type)}`);
8413
+ }
8414
+ if ((_a = filters.search) === null || _a === void 0 ? void 0 : _a.trim()) {
8415
+ const p = this.param(params, `%${filters.search.trim()}%`);
8416
+ where.push(`(COALESCE(pctl.name, pct.slug) ILIKE ${p} OR COALESCE(pct.code, '') ILIKE ${p} OR COALESCE(pct.slug, '') ILIKE ${p})`);
8417
+ }
8418
+ const whereClause = `WHERE ${where.join(' AND ')}`;
8419
+ return this.queryRows(`SELECT pct.id,
8420
+ pct.slug,
8421
+ pct.code,
8422
+ COALESCE(pctl.name, pct.slug) AS name,
8423
+ pctl.description,
8424
+ pct.category_id AS "categoryId",
8425
+ pcc.slug AS "categorySlug",
8426
+ COALESCE(pccl.name, pcc.slug) AS "categoryName",
8427
+ pct.default_unit AS "defaultUnit",
8428
+ pct.default_calculation_type AS "defaultCalculationType",
8429
+ pct.is_recurring_allowed AS "isRecurringAllowed",
8430
+ pct.is_active AS "isActive",
8431
+ pct.sort_order AS "sortOrder",
8432
+ pct.created_at AS "createdAt"
8433
+ FROM operations_project_cost_type pct
8434
+ LEFT JOIN operations_project_cost_category pcc
8435
+ ON pcc.id = pct.category_id AND pcc.deleted_at IS NULL
8436
+ LEFT JOIN LATERAL (
8437
+ SELECT l.name, l.description
8438
+ FROM operations_project_cost_category_locale l
8439
+ WHERE l.operations_project_cost_category_id = pcc.id
8440
+ ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC,
8441
+ l.id ASC
8442
+ LIMIT 1
8443
+ ) pccl ON TRUE
8444
+ LEFT JOIN LATERAL (
8445
+ SELECT l.name, l.description
8446
+ FROM operations_project_cost_type_locale l
8447
+ WHERE l.operations_project_cost_type_id = pct.id
8448
+ ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC,
8449
+ l.id ASC
8450
+ LIMIT 1
8451
+ ) pctl ON TRUE
8452
+ ${whereClause}
8453
+ ORDER BY pct.sort_order ASC, COALESCE(pctl.name, pct.slug) ASC`, params);
8454
+ }
8455
+ async createProjectCostType(userId, data) {
8456
+ var _a, _b, _c;
8457
+ const actor = await this.getActorContext(userId);
8458
+ this.ensureDirector(actor);
8459
+ const slug = (_a = data.slug) === null || _a === void 0 ? void 0 : _a.trim();
8460
+ if (!slug) {
8461
+ throw new common_1.BadRequestException('Cost type slug is required.');
8462
+ }
8463
+ if (data.category_id) {
8464
+ const category = await this.querySingle(`SELECT id FROM operations_project_cost_category WHERE id = $1 AND deleted_at IS NULL LIMIT 1`, [data.category_id]);
8465
+ if (!category) {
8466
+ throw new common_1.BadRequestException(`Category with id ${data.category_id} not found.`);
8467
+ }
8468
+ }
8469
+ const existingSlug = await this.querySingle(`SELECT id FROM operations_project_cost_type WHERE slug = $1 AND deleted_at IS NULL LIMIT 1`, [slug]);
8470
+ if (existingSlug) {
8471
+ throw new common_1.ConflictException(`A cost type with slug '${slug}' already exists.`);
8472
+ }
8473
+ const code = (_c = (_b = data.code) === null || _b === void 0 ? void 0 : _b.trim()) !== null && _c !== void 0 ? _c : slug;
8474
+ const existingCode = await this.querySingle(`SELECT id FROM operations_project_cost_type WHERE code = $1 AND deleted_at IS NULL LIMIT 1`, [code]);
8475
+ if (existingCode) {
8476
+ throw new common_1.ConflictException(`A cost type with code '${code}' already exists.`);
8477
+ }
8478
+ return this.prisma.$transaction(async (tx) => {
8479
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
8480
+ const localeId = await this.resolvePreferredLocaleId(tx);
8481
+ const created = (await tx.$queryRawUnsafe(`INSERT INTO operations_project_cost_type (category_id, slug, code, default_unit, default_calculation_type, is_recurring_allowed, is_active, sort_order, created_at, updated_at)
8482
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, NOW(), NOW())
8483
+ RETURNING id`, (_a = data.category_id) !== null && _a !== void 0 ? _a : null, slug, (_c = (_b = data.code) === null || _b === void 0 ? void 0 : _b.trim()) !== null && _c !== void 0 ? _c : slug, (_d = data.default_unit) !== null && _d !== void 0 ? _d : null, (_e = data.default_calculation_type) !== null && _e !== void 0 ? _e : 'fixed', (_f = data.is_recurring_allowed) !== null && _f !== void 0 ? _f : true, (_g = data.is_active) !== null && _g !== void 0 ? _g : true, (_h = data.sort_order) !== null && _h !== void 0 ? _h : 0));
8484
+ const createdId = (_j = created[0]) === null || _j === void 0 ? void 0 : _j.id;
8485
+ if (!createdId) {
8486
+ throw new common_1.BadRequestException('Unable to create project cost type.');
8487
+ }
8488
+ const name = typeof data.name === 'string' ? data.name : (data.name ? JSON.stringify(data.name) : slug);
8489
+ const description = typeof data.description === 'string' ? data.description : (data.description ? JSON.stringify(data.description) : null);
8490
+ if (localeId && name) {
8491
+ await tx.$executeRawUnsafe(`INSERT INTO operations_project_cost_type_locale (operations_project_cost_type_id, locale_id, name, description)
8492
+ VALUES ($1, $2, $3, $4)`, createdId, localeId, name, description !== null && description !== void 0 ? description : null);
8493
+ }
8494
+ const rows = (await tx.$queryRawUnsafe(`SELECT pct.id,
8495
+ pct.slug,
8496
+ pct.code,
8497
+ COALESCE(pctl.name, pct.slug) AS name,
8498
+ pctl.description,
8499
+ pct.category_id AS "categoryId",
8500
+ pct.default_unit AS "defaultUnit",
8501
+ pct.default_calculation_type AS "defaultCalculationType",
8502
+ pct.is_recurring_allowed AS "isRecurringAllowed",
8503
+ pct.is_active AS "isActive",
8504
+ pct.sort_order AS "sortOrder",
8505
+ pct.created_at AS "createdAt"
8506
+ FROM operations_project_cost_type pct
8507
+ LEFT JOIN operations_project_cost_type_locale pctl
8508
+ ON pctl.operations_project_cost_type_id = pct.id AND pctl.locale_id = $2
8509
+ WHERE pct.id = $1`, createdId, localeId));
8510
+ return (_k = rows[0]) !== null && _k !== void 0 ? _k : null;
8511
+ });
8512
+ }
8513
+ async updateProjectCostType(userId, id, data) {
8514
+ const actor = await this.getActorContext(userId);
8515
+ this.ensureDirector(actor);
8516
+ const costType = await this.querySingle(`SELECT id FROM operations_project_cost_type WHERE id = $1 AND deleted_at IS NULL LIMIT 1`, [id]);
8517
+ if (!costType) {
8518
+ throw new common_1.NotFoundException('Project cost type not found.');
8519
+ }
8520
+ if (data.category_id !== undefined && data.category_id !== null) {
8521
+ const category = await this.querySingle(`SELECT id FROM operations_project_cost_category WHERE id = $1 AND deleted_at IS NULL LIMIT 1`, [data.category_id]);
8522
+ if (!category) {
8523
+ throw new common_1.BadRequestException(`Category with id ${data.category_id} not found.`);
8524
+ }
8525
+ }
8526
+ if (data.slug !== undefined) {
8527
+ const existingSlug = await this.querySingle(`SELECT id FROM operations_project_cost_type WHERE slug = $1 AND id != $2 AND deleted_at IS NULL LIMIT 1`, [data.slug, id]);
8528
+ if (existingSlug) {
8529
+ throw new common_1.ConflictException(`A cost type with slug '${data.slug}' already exists.`);
8530
+ }
8531
+ }
8532
+ if (data.code !== undefined) {
8533
+ const existingCode = await this.querySingle(`SELECT id FROM operations_project_cost_type WHERE code = $1 AND id != $2 AND deleted_at IS NULL LIMIT 1`, [data.code, id]);
8534
+ if (existingCode) {
8535
+ throw new common_1.ConflictException(`A cost type with code '${data.code}' already exists.`);
8536
+ }
8537
+ }
8538
+ const sets = [];
8539
+ const params = [];
8540
+ if (data.category_id !== undefined)
8541
+ sets.push(`category_id = ${this.param(params, data.category_id)}`);
8542
+ if (data.slug !== undefined)
8543
+ sets.push(`slug = ${this.param(params, data.slug)}`);
8544
+ if (data.code !== undefined)
8545
+ sets.push(`code = ${this.param(params, data.code)}`);
8546
+ if (data.default_unit !== undefined)
8547
+ sets.push(`default_unit = ${this.param(params, data.default_unit)}`);
8548
+ if (data.default_calculation_type !== undefined)
8549
+ sets.push(`default_calculation_type = ${this.param(params, data.default_calculation_type)}`);
8550
+ if (data.is_recurring_allowed !== undefined)
8551
+ sets.push(`is_recurring_allowed = ${this.param(params, data.is_recurring_allowed)}`);
8552
+ if (data.is_active !== undefined)
8553
+ sets.push(`is_active = ${this.param(params, data.is_active)}`);
8554
+ if (data.sort_order !== undefined)
8555
+ sets.push(`sort_order = ${this.param(params, data.sort_order)}`);
8556
+ if (sets.length > 0) {
8557
+ sets.push(`updated_at = NOW()`);
8558
+ await this.prisma.$queryRawUnsafe(`UPDATE operations_project_cost_type SET ${sets.join(', ')} WHERE id = ${this.param(params, id)}`, ...params);
8559
+ }
8560
+ if (data.name !== undefined || data.description !== undefined) {
8561
+ const localeId = await this.resolvePreferredLocaleId();
8562
+ if (localeId) {
8563
+ const name = typeof data.name === 'string' ? data.name : (data.name ? JSON.stringify(data.name) : undefined);
8564
+ const description = typeof data.description === 'string' ? data.description : (data.description ? JSON.stringify(data.description) : null);
8565
+ const existing = await this.querySingle(`SELECT id FROM operations_project_cost_type_locale WHERE operations_project_cost_type_id = $1 AND locale_id = $2 LIMIT 1`, [id, localeId]);
8566
+ if (existing) {
8567
+ const localeSets = [];
8568
+ const localeParams = [];
8569
+ if (name !== undefined)
8570
+ localeSets.push(`name = ${this.param(localeParams, name)}`);
8571
+ if (description !== undefined)
8572
+ localeSets.push(`description = ${this.param(localeParams, description)}`);
8573
+ if (localeSets.length > 0) {
8574
+ await this.prisma.$queryRawUnsafe(`UPDATE operations_project_cost_type_locale SET ${localeSets.join(', ')} WHERE operations_project_cost_type_id = ${this.param(localeParams, id)} AND locale_id = ${this.param(localeParams, localeId)}`, ...localeParams);
8575
+ }
8576
+ }
8577
+ else if (name) {
8578
+ await this.prisma.$queryRawUnsafe(`INSERT INTO operations_project_cost_type_locale (operations_project_cost_type_id, locale_id, name, description) VALUES ($1, $2, $3, $4)`, id, localeId, name, description !== null && description !== void 0 ? description : null);
8579
+ }
8580
+ }
8581
+ }
8582
+ return this.querySingle(`SELECT id, slug FROM operations_project_cost_type WHERE id = $1`, [id]);
8583
+ }
8584
+ async deleteProjectCostType(userId, id) {
8585
+ const actor = await this.getActorContext(userId);
8586
+ this.ensureDirector(actor);
8587
+ const costType = await this.querySingle(`SELECT id FROM operations_project_cost_type WHERE id = $1 AND deleted_at IS NULL LIMIT 1`, [id]);
8588
+ if (!costType) {
8589
+ throw new common_1.NotFoundException('Project cost type not found.');
8590
+ }
8591
+ await this.prisma.$queryRawUnsafe(`UPDATE operations_project_cost_type SET deleted_at = NOW() WHERE id = $1`, id);
8592
+ return { success: true };
8593
+ }
8594
+ async getProjectCostType(userId, id) {
8595
+ await this.getActorContext(userId);
8596
+ const localeId = await this.resolvePreferredLocaleId();
8597
+ const row = await this.querySingle(`SELECT pct.id,
8598
+ pct.slug,
8599
+ pct.code,
8600
+ COALESCE(pctl.name, pct.slug) AS name,
8601
+ pctl.description,
8602
+ pct.default_unit,
8603
+ pct.default_calculation_type,
8604
+ pct.is_recurring_allowed,
8605
+ pct.is_active,
8606
+ pct.sort_order,
8607
+ pct.category_id,
8608
+ CASE WHEN pcc.id IS NOT NULL THEN
8609
+ jsonb_build_object(
8610
+ 'id', pcc.id,
8611
+ 'slug', pcc.slug,
8612
+ 'name', COALESCE(pccl.name, pcc.slug),
8613
+ 'color', pcc.color,
8614
+ 'icon', pcc.icon
8615
+ )
8616
+ ELSE NULL END AS category
8617
+ FROM operations_project_cost_type pct
8618
+ LEFT JOIN operations_project_cost_category pcc
8619
+ ON pcc.id = pct.category_id AND pcc.deleted_at IS NULL
8620
+ LEFT JOIN LATERAL (
8621
+ SELECT l.name, l.description
8622
+ FROM operations_project_cost_category_locale l
8623
+ WHERE l.operations_project_cost_category_id = pcc.id
8624
+ ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC,
8625
+ l.id ASC
8626
+ LIMIT 1
8627
+ ) pccl ON TRUE
8628
+ LEFT JOIN LATERAL (
8629
+ SELECT l.name, l.description
8630
+ FROM operations_project_cost_type_locale l
8631
+ WHERE l.operations_project_cost_type_id = pct.id
8632
+ ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC,
8633
+ l.id ASC
8634
+ LIMIT 1
8635
+ ) pctl ON TRUE
8636
+ WHERE pct.id = $2 AND pct.deleted_at IS NULL`, [localeId, id]);
8637
+ if (!row) {
8638
+ throw new common_1.NotFoundException('Project cost type not found.');
8639
+ }
8640
+ return row;
8641
+ }
8642
+ // ──────────────────────────────────────────────────────────────────────────
8643
+ // Project Costs
8644
+ // ──────────────────────────────────────────────────────────────────────────
8645
+ async listProjectCosts(userId, projectId, filters = {}) {
8646
+ var _a;
8647
+ await this.getActorContext(userId);
8648
+ const localeId = await this.resolvePreferredLocaleId();
8649
+ const params = [localeId, projectId];
8650
+ const where = ['pc.deleted_at IS NULL', 'pc.project_id = $2'];
8651
+ if (filters.cost_type_id) {
8652
+ where.push(`pc.cost_type_id = ${this.param(params, filters.cost_type_id)}`);
8653
+ }
8654
+ if (filters.category_id) {
8655
+ where.push(`COALESCE(pc.category_id, pct.category_id) = ${this.param(params, filters.category_id)}`);
8656
+ }
8657
+ if (filters.recurrence_type) {
8658
+ where.push(`pc.recurrence_type = ${this.param(params, filters.recurrence_type)}`);
8659
+ }
8660
+ if (filters.calculation_type) {
8661
+ where.push(`pc.calculation_type = ${this.param(params, filters.calculation_type)}`);
8662
+ }
8663
+ if (filters.status) {
8664
+ where.push(`pc.status = ${this.param(params, filters.status)}`);
8665
+ }
8666
+ if (filters.is_billable !== undefined) {
8667
+ where.push(`pc.is_billable = ${this.param(params, filters.is_billable)}`);
8668
+ }
8669
+ if (filters.is_reimbursable !== undefined) {
8670
+ where.push(`pc.is_reimbursable = ${this.param(params, filters.is_reimbursable)}`);
8671
+ }
8672
+ if (filters.date_from) {
8673
+ where.push(`pc.cost_date >= ${this.param(params, filters.date_from)}::date`);
8674
+ }
8675
+ if (filters.date_to) {
8676
+ where.push(`pc.cost_date <= ${this.param(params, filters.date_to)}::date`);
8677
+ }
8678
+ if ((_a = filters.search) === null || _a === void 0 ? void 0 : _a.trim()) {
8679
+ const p = this.param(params, `%${filters.search.trim()}%`);
8680
+ where.push(`(COALESCE(pc.description, '') ILIKE ${p} OR COALESCE(pc.notes, '') ILIKE ${p})`);
8681
+ }
8682
+ const whereClause = `WHERE ${where.join(' AND ')}`;
8683
+ const rows = await this.queryRows(`SELECT pc.id,
8684
+ pc.project_id AS "projectId",
8685
+ pc.cost_type_id AS "costTypeId",
8686
+ pct.slug AS "costTypeSlug",
8687
+ pct.code AS "costTypeCode",
8688
+ COALESCE(pctl.name, pct.slug) AS "costTypeName",
8689
+ pc.category_id AS "categoryId",
8690
+ COALESCE(pc.category_id, pct.category_id) AS "resolvedCategoryId",
8691
+ pcc.slug AS "categorySlug",
8692
+ COALESCE(pccl.name, pcc.slug) AS "categoryName",
8693
+ pcc.color AS "categoryColor",
8694
+ pcc.icon AS "categoryIcon",
8695
+ pc.description,
8696
+ pc.amount::text AS amount,
8697
+ pc.quantity::text AS quantity,
8698
+ pc.unit_amount::text AS "unitAmount",
8699
+ pc.currency,
8700
+ TO_CHAR(pc.cost_date, 'YYYY-MM-DD') AS "costDate",
8701
+ TO_CHAR(pc.period_start, 'YYYY-MM-DD') AS "periodStart",
8702
+ TO_CHAR(pc.period_end, 'YYYY-MM-DD') AS "periodEnd",
8703
+ pc.calculation_type AS "calculationType",
8704
+ pc.recurrence_type AS "recurrenceType",
8705
+ pc.is_billable AS "isBillable",
8706
+ pc.is_reimbursable AS "isReimbursable",
8707
+ pc.notes,
8708
+ pc.status,
8709
+ pc.created_at AS "createdAt"
8710
+ FROM operations_project_cost pc
8711
+ LEFT JOIN operations_project_cost_type pct
8712
+ ON pct.id = pc.cost_type_id AND pct.deleted_at IS NULL
8713
+ LEFT JOIN operations_project_cost_category pcc
8714
+ ON pcc.id = COALESCE(pc.category_id, pct.category_id) AND pcc.deleted_at IS NULL
8715
+ LEFT JOIN LATERAL (
8716
+ SELECT l.name
8717
+ FROM operations_project_cost_type_locale l
8718
+ WHERE l.operations_project_cost_type_id = pct.id
8719
+ ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC,
8720
+ l.id ASC
8721
+ LIMIT 1
8722
+ ) pctl ON TRUE
8723
+ LEFT JOIN LATERAL (
8724
+ SELECT l.name
8725
+ FROM operations_project_cost_category_locale l
8726
+ WHERE l.operations_project_cost_category_id = pcc.id
8727
+ ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC,
8728
+ l.id ASC
8729
+ LIMIT 1
8730
+ ) pccl ON TRUE
8731
+ ${whereClause}
8732
+ ORDER BY pc.created_at DESC`, params);
8733
+ return rows.map((row) => ({
8734
+ id: row.id,
8735
+ project_id: row.projectId,
8736
+ cost_type_id: row.costTypeId,
8737
+ category_id: row.categoryId,
8738
+ description: row.description,
8739
+ amount: row.amount,
8740
+ quantity: row.quantity,
8741
+ unit_amount: row.unitAmount,
8742
+ currency: row.currency,
8743
+ cost_date: row.costDate,
8744
+ period_start: row.periodStart,
8745
+ period_end: row.periodEnd,
8746
+ calculation_type: row.calculationType,
8747
+ recurrence_type: row.recurrenceType,
8748
+ is_billable: row.isBillable,
8749
+ is_reimbursable: row.isReimbursable,
8750
+ notes: row.notes,
8751
+ status: row.status,
8752
+ created_at: row.createdAt,
8753
+ cost_type: row.costTypeId
8754
+ ? { id: row.costTypeId, slug: row.costTypeSlug, name: row.costTypeName, code: row.costTypeCode }
8755
+ : null,
8756
+ category: row.resolvedCategoryId
8757
+ ? { id: row.resolvedCategoryId, slug: row.categorySlug, name: row.categoryName, color: row.categoryColor, icon: row.categoryIcon }
8758
+ : null,
8759
+ }));
8760
+ }
8761
+ async getProjectCostsSummaryGrouped(userId, projectId) {
8762
+ var _a, _b;
8763
+ const items = await this.listProjectCosts(userId, projectId, {});
8764
+ // Group by resolved category
8765
+ const categoryMap = new Map();
8766
+ for (const cost of items) {
8767
+ const cat = (_a = cost.category) !== null && _a !== void 0 ? _a : null;
8768
+ const key = (_b = cat === null || cat === void 0 ? void 0 : cat.id) !== null && _b !== void 0 ? _b : null;
8769
+ if (!categoryMap.has(key)) {
8770
+ categoryMap.set(key, { category: cat, items: [], total_amount: 0 });
8771
+ }
8772
+ const group = categoryMap.get(key);
8773
+ group.items.push(cost);
8774
+ group.total_amount += (parseFloat(String(cost.amount)) || 0) * (parseFloat(String(cost.quantity)) || 1);
8775
+ }
8776
+ const grand_total = Array.from(categoryMap.values()).reduce((sum, g) => sum + g.total_amount, 0);
8777
+ return {
8778
+ categories: Array.from(categoryMap.values()).map((g) => ({
8779
+ category: g.category,
8780
+ items: g.items,
8781
+ total_amount: Math.round(g.total_amount * 100) / 100,
8782
+ count: g.items.length,
8783
+ })),
8784
+ grand_total: Math.round(grand_total * 100) / 100,
8785
+ };
8786
+ }
8787
+ async getProjectCost(userId, projectId, id) {
8788
+ const rows = await this.listProjectCosts(userId, projectId, {});
8789
+ const cost = rows.find((r) => r.id === id);
8790
+ if (!cost) {
8791
+ throw new common_1.NotFoundException('Project cost not found.');
8792
+ }
8793
+ return cost;
8794
+ }
8795
+ async getProjectCostsSummary(userId, projectId) {
8796
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
8797
+ await this.getActorContext(userId);
8798
+ const localeId = await this.resolvePreferredLocaleId();
8799
+ // ── 1. Verify project exists and fetch budget_amount ──────────────────
8800
+ const project = await this.querySingle(`SELECT id, budget_amount::text AS "budgetAmount"
8801
+ FROM operations_project
8802
+ WHERE id = $1 AND deleted_at IS NULL
8803
+ LIMIT 1`, [projectId]);
8804
+ if (!project) {
8805
+ throw new common_1.NotFoundException('Project not found.');
8806
+ }
8807
+ const budgetAmount = parseFloat((_a = project.budgetAmount) !== null && _a !== void 0 ? _a : '0') || 0;
8808
+ // ── 2. Aggregated cost totals ─────────────────────────────────────────
8809
+ const totals = await this.querySingle(`SELECT
8810
+ COALESCE(SUM(CASE WHEN status != 'cancelled' THEN amount * quantity ELSE 0 END), 0)::text AS "extraCostTotal",
8811
+ COALESCE(SUM(CASE WHEN status = 'planned' THEN amount * quantity ELSE 0 END), 0)::text AS "plannedTotal",
8812
+ COALESCE(SUM(CASE WHEN status = 'approved' THEN amount * quantity ELSE 0 END), 0)::text AS "approvedTotal",
8813
+ COALESCE(SUM(CASE WHEN status = 'realized' THEN amount * quantity ELSE 0 END), 0)::text AS "realizedTotal",
8814
+ COALESCE(SUM(CASE WHEN status = 'cancelled' THEN amount * quantity ELSE 0 END), 0)::text AS "cancelledTotal",
8815
+ COALESCE(SUM(CASE WHEN is_billable = true AND status != 'cancelled' THEN amount * quantity ELSE 0 END), 0)::text AS "billableTotal",
8816
+ COALESCE(SUM(CASE WHEN is_billable = false AND status != 'cancelled' THEN amount * quantity ELSE 0 END), 0)::text AS "nonBillableTotal",
8817
+ COALESCE(SUM(CASE WHEN is_reimbursable = true AND status != 'cancelled' THEN amount * quantity ELSE 0 END), 0)::text AS "reimbursableTotal"
8818
+ FROM operations_project_cost
8819
+ WHERE deleted_at IS NULL
8820
+ AND project_id = $1`, [projectId]);
8821
+ const extraCostTotal = Math.round((parseFloat((_b = totals === null || totals === void 0 ? void 0 : totals.extraCostTotal) !== null && _b !== void 0 ? _b : '0') || 0) * 100) / 100;
8822
+ const plannedTotal = Math.round((parseFloat((_c = totals === null || totals === void 0 ? void 0 : totals.plannedTotal) !== null && _c !== void 0 ? _c : '0') || 0) * 100) / 100;
8823
+ const approvedTotal = Math.round((parseFloat((_d = totals === null || totals === void 0 ? void 0 : totals.approvedTotal) !== null && _d !== void 0 ? _d : '0') || 0) * 100) / 100;
8824
+ const realizedTotal = Math.round((parseFloat((_e = totals === null || totals === void 0 ? void 0 : totals.realizedTotal) !== null && _e !== void 0 ? _e : '0') || 0) * 100) / 100;
8825
+ const cancelledTotal = Math.round((parseFloat((_f = totals === null || totals === void 0 ? void 0 : totals.cancelledTotal) !== null && _f !== void 0 ? _f : '0') || 0) * 100) / 100;
8826
+ const billableTotal = Math.round((parseFloat((_g = totals === null || totals === void 0 ? void 0 : totals.billableTotal) !== null && _g !== void 0 ? _g : '0') || 0) * 100) / 100;
8827
+ const nonBillableTotal = Math.round((parseFloat((_h = totals === null || totals === void 0 ? void 0 : totals.nonBillableTotal) !== null && _h !== void 0 ? _h : '0') || 0) * 100) / 100;
8828
+ const reimbursableTotal = Math.round((parseFloat((_j = totals === null || totals === void 0 ? void 0 : totals.reimbursableTotal) !== null && _j !== void 0 ? _j : '0') || 0) * 100) / 100;
8829
+ const teamCostTotal = 0;
8830
+ const totalProjectCost = Math.round((teamCostTotal + extraCostTotal) * 100) / 100;
8831
+ const remainingBudget = Math.round((budgetAmount - totalProjectCost) * 100) / 100;
8832
+ const budgetUsagePercent = budgetAmount > 0
8833
+ ? Math.round((totalProjectCost / budgetAmount) * 10000) / 100
8834
+ : 0;
8835
+ // ── 3. cost_by_category ───────────────────────────────────────────────
8836
+ const costByCategory = await this.queryRows(`SELECT
8837
+ COALESCE(pc.category_id, pct.category_id) AS "categoryId",
8838
+ pcc.slug AS "categorySlug",
8839
+ COALESCE(pccl.name, pcc.slug) AS "categoryName",
8840
+ pcc.color AS "categoryColor",
8841
+ pcc.icon AS "categoryIcon",
8842
+ SUM(pc.amount * pc.quantity)::text AS total,
8843
+ COUNT(*)::int AS count
8844
+ FROM operations_project_cost pc
8845
+ LEFT JOIN operations_project_cost_type pct
8846
+ ON pct.id = pc.cost_type_id AND pct.deleted_at IS NULL
8847
+ LEFT JOIN operations_project_cost_category pcc
8848
+ ON pcc.id = COALESCE(pc.category_id, pct.category_id) AND pcc.deleted_at IS NULL
8849
+ LEFT JOIN LATERAL (
8850
+ SELECT l.name
8851
+ FROM operations_project_cost_category_locale l
8852
+ WHERE l.operations_project_cost_category_id = pcc.id
8853
+ ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC,
8854
+ l.id ASC
8855
+ LIMIT 1
8856
+ ) pccl ON TRUE
8857
+ WHERE pc.deleted_at IS NULL
8858
+ AND pc.project_id = $2
8859
+ AND pc.status != 'cancelled'
8860
+ GROUP BY COALESCE(pc.category_id, pct.category_id), pcc.slug, pcc.color, pcc.icon, pccl.name
8861
+ ORDER BY SUM(pc.amount * pc.quantity) DESC`, [localeId, projectId]);
8862
+ // ── 4. cost_by_type ───────────────────────────────────────────────────
8863
+ const costByType = await this.queryRows(`SELECT
8864
+ pc.cost_type_id AS "costTypeId",
8865
+ pct.slug AS "costTypeSlug",
8866
+ COALESCE(pctl.name, pct.slug) AS "costTypeName",
8867
+ pct.code AS "costTypeCode",
8868
+ SUM(pc.amount * pc.quantity)::text AS total,
8869
+ COUNT(*)::int AS count
8870
+ FROM operations_project_cost pc
8871
+ LEFT JOIN operations_project_cost_type pct
8872
+ ON pct.id = pc.cost_type_id AND pct.deleted_at IS NULL
8873
+ LEFT JOIN LATERAL (
8874
+ SELECT l.name
8875
+ FROM operations_project_cost_type_locale l
8876
+ WHERE l.operations_project_cost_type_id = pct.id
8877
+ ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC,
8878
+ l.id ASC
8879
+ LIMIT 1
8880
+ ) pctl ON TRUE
8881
+ WHERE pc.deleted_at IS NULL
8882
+ AND pc.project_id = $2
8883
+ AND pc.status != 'cancelled'
8884
+ GROUP BY pc.cost_type_id, pct.slug, pct.code, pctl.name
8885
+ ORDER BY SUM(pc.amount * pc.quantity) DESC`, [localeId, projectId]);
8886
+ // ── 5. cost_by_month ──────────────────────────────────────────────────
8887
+ const costByMonth = await this.queryRows(`SELECT
8888
+ TO_CHAR(COALESCE(pc.cost_date, pc.created_at), 'YYYY-MM') AS month,
8889
+ SUM(pc.amount * pc.quantity)::text AS total,
8890
+ COUNT(*)::int AS count
8891
+ FROM operations_project_cost pc
8892
+ WHERE pc.deleted_at IS NULL
8893
+ AND pc.project_id = $1
8894
+ AND pc.status != 'cancelled'
8895
+ GROUP BY TO_CHAR(COALESCE(pc.cost_date, pc.created_at), 'YYYY-MM')
8896
+ ORDER BY month ASC`, [projectId]);
8897
+ // ── 6. top_cost_types (top 5) ─────────────────────────────────────────
8898
+ const topCostTypes = costByType.slice(0, 5).map((ct) => {
8899
+ const typeTotal = Math.round((parseFloat(ct.total) || 0) * 100) / 100;
8900
+ const percentage = extraCostTotal > 0
8901
+ ? Math.round((typeTotal / extraCostTotal) * 10000) / 100
8902
+ : 0;
8903
+ return {
8904
+ cost_type_id: ct.costTypeId,
8905
+ cost_type_slug: ct.costTypeSlug,
8906
+ cost_type_name: ct.costTypeName,
8907
+ cost_type_code: ct.costTypeCode,
8908
+ total: typeTotal,
8909
+ percentage,
8910
+ };
8911
+ });
8912
+ return {
8913
+ project_id: projectId,
8914
+ budget_amount: budgetAmount,
8915
+ team_cost_total: teamCostTotal,
8916
+ extra_cost_total: extraCostTotal,
8917
+ total_project_cost: totalProjectCost,
8918
+ remaining_budget: remainingBudget,
8919
+ budget_usage_percent: budgetUsagePercent,
8920
+ planned_total: plannedTotal,
8921
+ approved_total: approvedTotal,
8922
+ realized_total: realizedTotal,
8923
+ cancelled_total: cancelledTotal,
8924
+ billable_total: billableTotal,
8925
+ non_billable_total: nonBillableTotal,
8926
+ reimbursable_total: reimbursableTotal,
8927
+ cost_by_category: costByCategory.map((c) => ({
8928
+ category_id: c.categoryId,
8929
+ category_slug: c.categorySlug,
8930
+ category_name: c.categoryName,
8931
+ category_color: c.categoryColor,
8932
+ category_icon: c.categoryIcon,
8933
+ total: Math.round((parseFloat(c.total) || 0) * 100) / 100,
8934
+ count: Number(c.count),
8935
+ })),
8936
+ cost_by_type: costByType.map((t) => ({
8937
+ cost_type_id: t.costTypeId,
8938
+ cost_type_slug: t.costTypeSlug,
8939
+ cost_type_name: t.costTypeName,
8940
+ cost_type_code: t.costTypeCode,
8941
+ total: Math.round((parseFloat(t.total) || 0) * 100) / 100,
8942
+ count: Number(t.count),
8943
+ })),
8944
+ cost_by_month: costByMonth.map((m) => ({
8945
+ month: m.month,
8946
+ total: Math.round((parseFloat(m.total) || 0) * 100) / 100,
8947
+ count: Number(m.count),
8948
+ })),
8949
+ top_cost_types: topCostTypes,
8950
+ };
8951
+ }
8952
+ async getProjectCostReport(userId, projectId, filters) {
8953
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
8954
+ await this.getActorContext(userId);
8955
+ const localeId = await this.resolvePreferredLocaleId();
8956
+ // ── Verify project ───────────────────────────────────────────────────
8957
+ const project = await this.querySingle(`SELECT id, budget_amount::text AS "budgetAmount"
8958
+ FROM operations_project
8959
+ WHERE id = $1 AND deleted_at IS NULL
8960
+ LIMIT 1`, [projectId]);
8961
+ if (!project) {
8962
+ throw new common_1.NotFoundException('Project not found.');
8963
+ }
8964
+ const budgetAmount = parseFloat((_a = project.budgetAmount) !== null && _a !== void 0 ? _a : '0') || 0;
8965
+ // ── Build dynamic WHERE clause ────────────────────────────────────────
8966
+ const conditions = [
8967
+ 'pc.deleted_at IS NULL',
8968
+ 'pc.project_id = $1',
8969
+ ];
8970
+ const params = [projectId];
8971
+ if (filters.date_from) {
8972
+ params.push(filters.date_from);
8973
+ conditions.push(`COALESCE(pc.cost_date, pc.created_at::date) >= $${params.length}::date`);
8974
+ }
8975
+ if (filters.date_to) {
8976
+ params.push(filters.date_to);
8977
+ conditions.push(`COALESCE(pc.cost_date, pc.created_at::date) <= $${params.length}::date`);
8978
+ }
8979
+ if (filters.category_id !== undefined) {
8980
+ params.push(filters.category_id);
8981
+ conditions.push(`(pc.category_id = $${params.length} OR (pc.category_id IS NULL AND EXISTS (
8982
+ SELECT 1 FROM operations_project_cost_type pct2
8983
+ WHERE pct2.id = pc.cost_type_id AND pct2.category_id = $${params.length} AND pct2.deleted_at IS NULL
8984
+ )))`);
8985
+ }
8986
+ if (filters.cost_type_id !== undefined) {
8987
+ params.push(filters.cost_type_id);
8988
+ conditions.push(`pc.cost_type_id = $${params.length}`);
8989
+ }
8990
+ if (filters.status !== undefined) {
8991
+ params.push(filters.status);
8992
+ conditions.push(`pc.status = $${params.length}`);
8993
+ }
8994
+ if (filters.is_billable !== undefined) {
8995
+ params.push(filters.is_billable);
8996
+ conditions.push(`pc.is_billable = $${params.length}`);
8997
+ }
8998
+ if (filters.is_reimbursable !== undefined) {
8999
+ params.push(filters.is_reimbursable);
9000
+ conditions.push(`pc.is_reimbursable = $${params.length}`);
9001
+ }
9002
+ const whereClause = conditions.join(' AND ');
9003
+ // ── Totals ────────────────────────────────────────────────────────────
9004
+ const totals = await this.querySingle(`SELECT
9005
+ COALESCE(SUM(pc.amount * pc.quantity), 0)::text AS "grandTotal",
9006
+ COALESCE(SUM(CASE WHEN pc.status = 'planned' THEN pc.amount * pc.quantity ELSE 0 END), 0)::text AS "plannedTotal",
9007
+ COALESCE(SUM(CASE WHEN pc.status = 'approved' THEN pc.amount * pc.quantity ELSE 0 END), 0)::text AS "approvedTotal",
9008
+ COALESCE(SUM(CASE WHEN pc.status = 'realized' THEN pc.amount * pc.quantity ELSE 0 END), 0)::text AS "realizedTotal",
9009
+ COALESCE(SUM(CASE WHEN pc.status = 'cancelled' THEN pc.amount * pc.quantity ELSE 0 END), 0)::text AS "cancelledTotal",
9010
+ COALESCE(SUM(CASE WHEN pc.is_billable = true THEN pc.amount * pc.quantity ELSE 0 END), 0)::text AS "billableTotal",
9011
+ COALESCE(SUM(CASE WHEN pc.is_billable = false THEN pc.amount * pc.quantity ELSE 0 END), 0)::text AS "nonBillableTotal",
9012
+ COALESCE(SUM(CASE WHEN pc.is_reimbursable = true THEN pc.amount * pc.quantity ELSE 0 END), 0)::text AS "reimbursableTotal",
9013
+ COUNT(*)::int AS "totalCount"
9014
+ FROM operations_project_cost pc
9015
+ WHERE ${whereClause}`, params);
9016
+ const round2 = (v) => Math.round((parseFloat(v !== null && v !== void 0 ? v : '0') || 0) * 100) / 100;
9017
+ const grandTotal = round2(totals === null || totals === void 0 ? void 0 : totals.grandTotal);
9018
+ const plannedTotal = round2(totals === null || totals === void 0 ? void 0 : totals.plannedTotal);
9019
+ const approvedTotal = round2(totals === null || totals === void 0 ? void 0 : totals.approvedTotal);
9020
+ const realizedTotal = round2(totals === null || totals === void 0 ? void 0 : totals.realizedTotal);
9021
+ const cancelledTotal = round2(totals === null || totals === void 0 ? void 0 : totals.cancelledTotal);
9022
+ const billableTotal = round2(totals === null || totals === void 0 ? void 0 : totals.billableTotal);
9023
+ const nonBillableTotal = round2(totals === null || totals === void 0 ? void 0 : totals.nonBillableTotal);
9024
+ const reimbursableTotal = round2(totals === null || totals === void 0 ? void 0 : totals.reimbursableTotal);
9025
+ // ── By category ───────────────────────────────────────────────────────
9026
+ const costByCategory = await this.queryRows(`SELECT
9027
+ COALESCE(pc.category_id, pct.category_id) AS "categoryId",
9028
+ pcc.slug AS "categorySlug",
9029
+ COALESCE(pccl.name, pcc.slug) AS "categoryName",
9030
+ pcc.color AS "categoryColor",
9031
+ pcc.icon AS "categoryIcon",
9032
+ SUM(pc.amount * pc.quantity)::text AS total,
9033
+ COUNT(*)::int AS count,
9034
+ COALESCE(SUM(CASE WHEN pc.status='planned' THEN pc.amount*pc.quantity ELSE 0 END),0)::text AS "plannedSubtotal",
9035
+ COALESCE(SUM(CASE WHEN pc.status='realized' THEN pc.amount*pc.quantity ELSE 0 END),0)::text AS "realizedSubtotal"
9036
+ FROM operations_project_cost pc
9037
+ LEFT JOIN operations_project_cost_type pct
9038
+ ON pct.id = pc.cost_type_id AND pct.deleted_at IS NULL
9039
+ LEFT JOIN operations_project_cost_category pcc
9040
+ ON pcc.id = COALESCE(pc.category_id, pct.category_id) AND pcc.deleted_at IS NULL
9041
+ LEFT JOIN LATERAL (
9042
+ SELECT l.name
9043
+ FROM operations_project_cost_category_locale l
9044
+ WHERE l.operations_project_cost_category_id = pcc.id
9045
+ ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC, l.id ASC
9046
+ LIMIT 1
9047
+ ) pccl ON TRUE
9048
+ WHERE ${whereClause.replace(/\$(\d+)/g, (m, n) => '$' + (Number(n) + 1))}
9049
+ GROUP BY COALESCE(pc.category_id, pct.category_id), pcc.slug, pcc.color, pcc.icon, pccl.name
9050
+ ORDER BY SUM(pc.amount * pc.quantity) DESC`, [localeId, ...params]);
9051
+ // ── By type ───────────────────────────────────────────────────────────
9052
+ const costByType = await this.queryRows(`SELECT
9053
+ pc.cost_type_id AS "costTypeId",
9054
+ pct.slug AS "costTypeSlug",
9055
+ COALESCE(pctl.name, pct.slug) AS "costTypeName",
9056
+ pct.code AS "costTypeCode",
9057
+ SUM(pc.amount * pc.quantity)::text AS total,
9058
+ COUNT(*)::int AS count,
9059
+ COALESCE(SUM(CASE WHEN pc.status='planned' THEN pc.amount*pc.quantity ELSE 0 END),0)::text AS "plannedSubtotal",
9060
+ COALESCE(SUM(CASE WHEN pc.status='realized' THEN pc.amount*pc.quantity ELSE 0 END),0)::text AS "realizedSubtotal"
9061
+ FROM operations_project_cost pc
9062
+ LEFT JOIN operations_project_cost_type pct
9063
+ ON pct.id = pc.cost_type_id AND pct.deleted_at IS NULL
9064
+ LEFT JOIN LATERAL (
9065
+ SELECT l.name
9066
+ FROM operations_project_cost_type_locale l
9067
+ WHERE l.operations_project_cost_type_id = pct.id
9068
+ ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC, l.id ASC
9069
+ LIMIT 1
9070
+ ) pctl ON TRUE
9071
+ WHERE ${whereClause.replace(/\$(\d+)/g, (m, n) => '$' + (Number(n) + 1))}
9072
+ GROUP BY pc.cost_type_id, pct.slug, pct.code, pctl.name
9073
+ ORDER BY SUM(pc.amount * pc.quantity) DESC`, [localeId, ...params]);
9074
+ // ── By month ──────────────────────────────────────────────────────────
9075
+ const costByMonth = await this.queryRows(`SELECT
9076
+ TO_CHAR(COALESCE(pc.cost_date, pc.created_at::date), 'YYYY-MM') AS month,
9077
+ SUM(pc.amount * pc.quantity)::text AS total,
9078
+ COALESCE(SUM(CASE WHEN pc.status='planned' THEN pc.amount*pc.quantity ELSE 0 END),0)::text AS "plannedSubtotal",
9079
+ COALESCE(SUM(CASE WHEN pc.status='realized' THEN pc.amount*pc.quantity ELSE 0 END),0)::text AS "realizedSubtotal",
9080
+ COUNT(*)::int AS count
9081
+ FROM operations_project_cost pc
9082
+ WHERE ${whereClause}
9083
+ GROUP BY TO_CHAR(COALESCE(pc.cost_date, pc.created_at::date), 'YYYY-MM')
9084
+ ORDER BY month ASC`, params);
9085
+ // ── Top 5 individual costs ────────────────────────────────────────────
9086
+ const top5Costs = await this.queryRows(`SELECT
9087
+ pc.id,
9088
+ pc.description,
9089
+ pc.amount::text,
9090
+ pc.quantity::text,
9091
+ pc.status,
9092
+ pc.cost_date AS "costDate",
9093
+ COALESCE(pctl.name, pct.slug) AS "costTypeName",
9094
+ COALESCE(pccl.name, pcc.slug) AS "categoryName",
9095
+ pcc.color AS "categoryColor"
9096
+ FROM operations_project_cost pc
9097
+ LEFT JOIN operations_project_cost_type pct
9098
+ ON pct.id = pc.cost_type_id AND pct.deleted_at IS NULL
9099
+ LEFT JOIN LATERAL (
9100
+ SELECT l.name
9101
+ FROM operations_project_cost_type_locale l
9102
+ WHERE l.operations_project_cost_type_id = pct.id
9103
+ ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC, l.id ASC
9104
+ LIMIT 1
9105
+ ) pctl ON TRUE
9106
+ LEFT JOIN operations_project_cost_category pcc
9107
+ ON pcc.id = COALESCE(pc.category_id, pct.category_id) AND pcc.deleted_at IS NULL
9108
+ LEFT JOIN LATERAL (
9109
+ SELECT l.name
9110
+ FROM operations_project_cost_category_locale l
9111
+ WHERE l.operations_project_cost_category_id = pcc.id
9112
+ ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC, l.id ASC
9113
+ LIMIT 1
9114
+ ) pccl ON TRUE
9115
+ WHERE ${whereClause.replace(/\$(\d+)/g, (m, n) => '$' + (Number(n) + 1))}
9116
+ ORDER BY (pc.amount * pc.quantity) DESC
9117
+ LIMIT 5`, [localeId, ...params]);
9118
+ // ── Detailed list ─────────────────────────────────────────────────────
9119
+ const detailedList = await this.queryRows(`SELECT
9120
+ pc.id,
9121
+ pc.description,
9122
+ pc.amount::text,
9123
+ pc.quantity::text,
9124
+ pc.unit_amount::text AS "unitAmount",
9125
+ pc.currency,
9126
+ pc.calculation_type AS "calculationType",
9127
+ pc.recurrence_type AS "recurrenceType",
9128
+ pc.status,
9129
+ pc.is_billable AS "isBillable",
9130
+ pc.is_reimbursable AS "isReimbursable",
9131
+ pc.cost_date AS "costDate",
9132
+ pc.period_start AS "periodStart",
9133
+ pc.period_end AS "periodEnd",
9134
+ pc.notes,
9135
+ pc.cost_type_id AS "costTypeId",
9136
+ COALESCE(pctl.name, pct.slug) AS "costTypeName",
9137
+ pct.code AS "costTypeCode",
9138
+ COALESCE(pc.category_id, pct.category_id) AS "categoryId",
9139
+ COALESCE(pccl.name, pcc.slug) AS "categoryName",
9140
+ pcc.color AS "categoryColor",
9141
+ pc.created_at::text AS "createdAt"
9142
+ FROM operations_project_cost pc
9143
+ LEFT JOIN operations_project_cost_type pct
9144
+ ON pct.id = pc.cost_type_id AND pct.deleted_at IS NULL
9145
+ LEFT JOIN LATERAL (
9146
+ SELECT l.name
9147
+ FROM operations_project_cost_type_locale l
9148
+ WHERE l.operations_project_cost_type_id = pct.id
9149
+ ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC, l.id ASC
9150
+ LIMIT 1
9151
+ ) pctl ON TRUE
9152
+ LEFT JOIN operations_project_cost_category pcc
9153
+ ON pcc.id = COALESCE(pc.category_id, pct.category_id) AND pcc.deleted_at IS NULL
9154
+ LEFT JOIN LATERAL (
9155
+ SELECT l.name
9156
+ FROM operations_project_cost_category_locale l
9157
+ WHERE l.operations_project_cost_category_id = pcc.id
9158
+ ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC, l.id ASC
9159
+ LIMIT 1
9160
+ ) pccl ON TRUE
9161
+ WHERE ${whereClause.replace(/\$(\d+)/g, (m, n) => '$' + (Number(n) + 1))}
9162
+ ORDER BY (pc.amount * pc.quantity) DESC, pc.cost_date DESC NULLS LAST`, [localeId, ...params]);
9163
+ return {
9164
+ project_id: projectId,
9165
+ budget_amount: budgetAmount,
9166
+ filters_applied: {
9167
+ date_from: (_b = filters.date_from) !== null && _b !== void 0 ? _b : null,
9168
+ date_to: (_c = filters.date_to) !== null && _c !== void 0 ? _c : null,
9169
+ category_id: (_d = filters.category_id) !== null && _d !== void 0 ? _d : null,
9170
+ cost_type_id: (_e = filters.cost_type_id) !== null && _e !== void 0 ? _e : null,
9171
+ status: (_f = filters.status) !== null && _f !== void 0 ? _f : null,
9172
+ is_billable: (_g = filters.is_billable) !== null && _g !== void 0 ? _g : null,
9173
+ is_reimbursable: (_h = filters.is_reimbursable) !== null && _h !== void 0 ? _h : null,
9174
+ },
9175
+ totals: {
9176
+ grand_total: grandTotal,
9177
+ planned_total: plannedTotal,
9178
+ approved_total: approvedTotal,
9179
+ realized_total: realizedTotal,
9180
+ cancelled_total: cancelledTotal,
9181
+ billable_total: billableTotal,
9182
+ non_billable_total: nonBillableTotal,
9183
+ reimbursable_total: reimbursableTotal,
9184
+ total_count: Number((_j = totals === null || totals === void 0 ? void 0 : totals.totalCount) !== null && _j !== void 0 ? _j : 0),
9185
+ },
9186
+ cost_by_category: costByCategory.map((c) => ({
9187
+ category_id: c.categoryId,
9188
+ category_slug: c.categorySlug,
9189
+ category_name: c.categoryName,
9190
+ category_color: c.categoryColor,
9191
+ category_icon: c.categoryIcon,
9192
+ total: round2(c.total),
9193
+ count: Number(c.count),
9194
+ planned_subtotal: round2(c.plannedSubtotal),
9195
+ realized_subtotal: round2(c.realizedSubtotal),
9196
+ })),
9197
+ cost_by_type: costByType.map((t) => ({
9198
+ cost_type_id: t.costTypeId,
9199
+ cost_type_slug: t.costTypeSlug,
9200
+ cost_type_name: t.costTypeName,
9201
+ cost_type_code: t.costTypeCode,
9202
+ total: round2(t.total),
9203
+ count: Number(t.count),
9204
+ planned_subtotal: round2(t.plannedSubtotal),
9205
+ realized_subtotal: round2(t.realizedSubtotal),
9206
+ })),
9207
+ cost_by_month: costByMonth.map((m) => ({
9208
+ month: m.month,
9209
+ total: round2(m.total),
9210
+ planned_subtotal: round2(m.plannedSubtotal),
9211
+ realized_subtotal: round2(m.realizedSubtotal),
9212
+ count: Number(m.count),
9213
+ })),
9214
+ top_5_costs: top5Costs.map((c) => ({
9215
+ id: c.id,
9216
+ description: c.description,
9217
+ total: round2(String(parseFloat(c.amount) * parseFloat(c.quantity))),
9218
+ amount: round2(c.amount),
9219
+ quantity: parseFloat(c.quantity),
9220
+ status: c.status,
9221
+ cost_type_name: c.costTypeName,
9222
+ category_name: c.categoryName,
9223
+ category_color: c.categoryColor,
9224
+ cost_date: c.costDate,
9225
+ })),
9226
+ detailed_list: detailedList.map((c) => ({
9227
+ id: c.id,
9228
+ description: c.description,
9229
+ amount: round2(c.amount),
9230
+ quantity: parseFloat(c.quantity),
9231
+ unit_amount: c.unitAmount ? round2(c.unitAmount) : null,
9232
+ total: round2(String(parseFloat(c.amount) * parseFloat(c.quantity))),
9233
+ currency: c.currency,
9234
+ calculation_type: c.calculationType,
9235
+ recurrence_type: c.recurrenceType,
9236
+ status: c.status,
9237
+ is_billable: c.isBillable,
9238
+ is_reimbursable: c.isReimbursable,
9239
+ cost_date: c.costDate,
9240
+ period_start: c.periodStart,
9241
+ period_end: c.periodEnd,
9242
+ notes: c.notes,
9243
+ cost_type_id: c.costTypeId,
9244
+ cost_type_name: c.costTypeName,
9245
+ cost_type_code: c.costTypeCode,
9246
+ category_id: c.categoryId,
9247
+ category_name: c.categoryName,
9248
+ category_color: c.categoryColor,
9249
+ created_at: c.createdAt,
9250
+ })),
9251
+ };
9252
+ }
9253
+ async createProjectCost(userId, projectId, data) {
9254
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
9255
+ const actor = await this.getActorContext(userId);
9256
+ this.ensureSupervisor(actor);
9257
+ const project = await this.querySingle(`SELECT id FROM operations_project WHERE id = $1 AND deleted_at IS NULL LIMIT 1`, [projectId]);
9258
+ if (!project) {
9259
+ throw new common_1.NotFoundException('Project not found.');
9260
+ }
9261
+ if (data.cost_type_id) {
9262
+ const costType = await this.querySingle(`SELECT id FROM operations_project_cost_type WHERE id = $1 AND deleted_at IS NULL LIMIT 1`, [data.cost_type_id]);
9263
+ if (!costType) {
9264
+ throw new common_1.BadRequestException(`Cost type with id ${data.cost_type_id} not found.`);
9265
+ }
9266
+ }
9267
+ if (data.category_id) {
9268
+ const category = await this.querySingle(`SELECT id FROM operations_project_cost_category WHERE id = $1 AND deleted_at IS NULL LIMIT 1`, [data.category_id]);
9269
+ if (!category) {
9270
+ throw new common_1.BadRequestException(`Cost category with id ${data.category_id} not found.`);
9271
+ }
9272
+ }
9273
+ const calcType = (_a = data.calculation_type) !== null && _a !== void 0 ? _a : 'fixed';
9274
+ let effectiveAmount = data.amount;
9275
+ if (['unit', 'hourly', 'monthly'].includes(calcType) && data.unit_amount !== undefined && data.unit_amount !== null) {
9276
+ const qty = (_b = data.quantity) !== null && _b !== void 0 ? _b : 1;
9277
+ effectiveAmount = Math.round(qty * data.unit_amount * 100) / 100;
9278
+ }
9279
+ const created = await this.querySingle(`INSERT INTO operations_project_cost
9280
+ (project_id, cost_type_id, category_id, description, amount, quantity, unit_amount, currency, cost_date, period_start, period_end, calculation_type, recurrence_type, is_billable, is_reimbursable, notes, status, created_at, updated_at)
9281
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::date, $10::date, $11::date, $12::operations_project_cost_calculation_type_134cdfb49c_enum, $13::operations_project_cost_recurrence_type_09baf0f043_enum, $14, $15, $16, $17::operations_project_cost_status_153e8592ce_enum, NOW(), NOW())
9282
+ RETURNING id`, [
9283
+ projectId,
9284
+ (_c = data.cost_type_id) !== null && _c !== void 0 ? _c : null,
9285
+ (_d = data.category_id) !== null && _d !== void 0 ? _d : null,
9286
+ (_e = data.description) !== null && _e !== void 0 ? _e : null,
9287
+ effectiveAmount,
9288
+ (_f = data.quantity) !== null && _f !== void 0 ? _f : 1,
9289
+ (_g = data.unit_amount) !== null && _g !== void 0 ? _g : null,
9290
+ (_h = data.currency) !== null && _h !== void 0 ? _h : 'BRL',
9291
+ (_j = data.cost_date) !== null && _j !== void 0 ? _j : null,
9292
+ (_k = data.period_start) !== null && _k !== void 0 ? _k : null,
9293
+ (_l = data.period_end) !== null && _l !== void 0 ? _l : null,
9294
+ calcType,
9295
+ (_m = data.recurrence_type) !== null && _m !== void 0 ? _m : 'none',
9296
+ (_o = data.is_billable) !== null && _o !== void 0 ? _o : false,
9297
+ (_p = data.is_reimbursable) !== null && _p !== void 0 ? _p : false,
9298
+ (_q = data.notes) !== null && _q !== void 0 ? _q : null,
9299
+ (_r = data.status) !== null && _r !== void 0 ? _r : 'planned',
9300
+ ]);
9301
+ if (!(created === null || created === void 0 ? void 0 : created.id)) {
9302
+ throw new common_1.BadRequestException('Unable to create project cost.');
9303
+ }
9304
+ const rows = await this.listProjectCosts(userId, projectId, {});
9305
+ return (_s = rows.find((r) => r.id === created.id)) !== null && _s !== void 0 ? _s : null;
9306
+ }
9307
+ async updateProjectCost(userId, id, data) {
9308
+ var _a, _b, _c;
9309
+ const actor = await this.getActorContext(userId);
9310
+ this.ensureSupervisor(actor);
9311
+ const cost = await this.querySingle(`SELECT id, project_id AS "projectId", calculation_type AS "calculationType", unit_amount::text AS "unitAmount", quantity::text AS quantity FROM operations_project_cost WHERE id = $1 AND deleted_at IS NULL LIMIT 1`, [id]);
9312
+ if (!cost) {
9313
+ throw new common_1.NotFoundException('Project cost not found.');
9314
+ }
9315
+ // Auto-calculate amount when applicable
9316
+ const effectiveCalcType = (_a = data.calculation_type) !== null && _a !== void 0 ? _a : cost.calculationType;
9317
+ if (['unit', 'hourly', 'monthly'].includes(effectiveCalcType)) {
9318
+ const ua = data.unit_amount !== undefined ? data.unit_amount : (cost.unitAmount !== null ? parseFloat(cost.unitAmount) : null);
9319
+ const qty = data.quantity !== undefined ? data.quantity : parseFloat(cost.quantity);
9320
+ if (ua !== null && ua !== undefined) {
9321
+ data = Object.assign(Object.assign({}, data), { amount: Math.round(qty * ua * 100) / 100 });
9322
+ }
9323
+ }
9324
+ const sets = [];
9325
+ const params = [];
9326
+ if (data.cost_type_id !== undefined)
9327
+ sets.push(`cost_type_id = ${this.param(params, data.cost_type_id)}`);
9328
+ if (data.category_id !== undefined)
9329
+ sets.push(`category_id = ${this.param(params, data.category_id)}`);
9330
+ if (data.description !== undefined)
9331
+ sets.push(`description = ${this.param(params, data.description)}`);
9332
+ if (data.amount !== undefined)
9333
+ sets.push(`amount = ${this.param(params, data.amount)}`);
9334
+ if (data.currency !== undefined)
9335
+ sets.push(`currency = ${this.param(params, data.currency)}`);
9336
+ if (data.quantity !== undefined)
9337
+ sets.push(`quantity = ${this.param(params, data.quantity)}`);
9338
+ if (data.unit_amount !== undefined)
9339
+ sets.push(`unit_amount = ${this.param(params, data.unit_amount)}`);
9340
+ if (data.calculation_type !== undefined)
9341
+ sets.push(`calculation_type = ${this.param(params, data.calculation_type)}::operations_project_cost_calculation_type_134cdfb49c_enum`);
9342
+ if (data.recurrence_type !== undefined)
9343
+ sets.push(`recurrence_type = ${this.param(params, data.recurrence_type)}::operations_project_cost_recurrence_type_09baf0f043_enum`);
9344
+ if (data.is_billable !== undefined)
9345
+ sets.push(`is_billable = ${this.param(params, data.is_billable)}`);
9346
+ if (data.is_reimbursable !== undefined)
9347
+ sets.push(`is_reimbursable = ${this.param(params, data.is_reimbursable)}`);
9348
+ if (data.cost_date !== undefined)
9349
+ sets.push(`cost_date = ${this.param(params, data.cost_date)}::date`);
9350
+ if (data.period_start !== undefined)
9351
+ sets.push(`period_start = ${this.param(params, data.period_start)}::date`);
9352
+ if (data.period_end !== undefined)
9353
+ sets.push(`period_end = ${this.param(params, data.period_end)}::date`);
9354
+ if (data.notes !== undefined)
9355
+ sets.push(`notes = ${this.param(params, data.notes)}`);
9356
+ if (data.status !== undefined)
9357
+ sets.push(`status = ${this.param(params, data.status)}::operations_project_cost_status_153e8592ce_enum`);
9358
+ if (sets.length === 0) {
9359
+ const rows = await this.listProjectCosts(userId, cost.projectId, {});
9360
+ return (_b = rows.find((r) => r.id === id)) !== null && _b !== void 0 ? _b : null;
9361
+ }
9362
+ sets.push(`updated_at = NOW()`);
9363
+ await this.prisma.$queryRawUnsafe(`UPDATE operations_project_cost SET ${sets.join(', ')} WHERE id = ${this.param(params, id)}`, ...params);
9364
+ const rows = await this.listProjectCosts(userId, cost.projectId, {});
9365
+ return (_c = rows.find((r) => r.id === id)) !== null && _c !== void 0 ? _c : null;
9366
+ }
9367
+ async deleteProjectCost(userId, id) {
9368
+ const actor = await this.getActorContext(userId);
9369
+ this.ensureSupervisor(actor);
9370
+ const cost = await this.querySingle(`SELECT id FROM operations_project_cost WHERE id = $1 AND deleted_at IS NULL LIMIT 1`, [id]);
9371
+ if (!cost) {
9372
+ throw new common_1.NotFoundException('Project cost not found.');
9373
+ }
9374
+ await this.prisma.$queryRawUnsafe(`UPDATE operations_project_cost SET deleted_at = NOW() WHERE id = $1`, id);
9375
+ return { success: true };
9376
+ }
7712
9377
  };
7713
9378
  exports.OperationsService = OperationsService;
7714
9379
  exports.OperationsService = OperationsService = OperationsService_1 = __decorate([