@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.
- package/dist/controllers/operations-collaborators.controller.d.ts +9 -0
- package/dist/controllers/operations-collaborators.controller.d.ts.map +1 -1
- package/dist/controllers/operations-collaborators.controller.js +25 -0
- package/dist/controllers/operations-collaborators.controller.js.map +1 -1
- package/dist/controllers/operations-contracts.controller.d.ts +9 -9
- package/dist/controllers/operations-project-costs.controller.d.ts +422 -0
- package/dist/controllers/operations-project-costs.controller.d.ts.map +1 -0
- package/dist/controllers/operations-project-costs.controller.js +250 -0
- package/dist/controllers/operations-project-costs.controller.js.map +1 -0
- package/dist/controllers/operations-reports.controller.d.ts +9 -0
- package/dist/controllers/operations-reports.controller.d.ts.map +1 -1
- package/dist/controllers/operations-tasks.controller.d.ts +64 -0
- package/dist/controllers/operations-tasks.controller.d.ts.map +1 -1
- package/dist/controllers/operations-tasks.controller.js +85 -0
- package/dist/controllers/operations-tasks.controller.js.map +1 -1
- package/dist/controllers/operations-timesheets.controller.d.ts +1 -0
- package/dist/controllers/operations-timesheets.controller.d.ts.map +1 -1
- package/dist/dto/create-collaborator-project-assignment.dto.d.ts +5 -0
- package/dist/dto/create-collaborator-project-assignment.dto.d.ts.map +1 -0
- package/dist/dto/create-collaborator-project-assignment.dto.js +30 -0
- package/dist/dto/create-collaborator-project-assignment.dto.js.map +1 -0
- package/dist/dto/create-project-cost-category.dto.d.ts +10 -0
- package/dist/dto/create-project-cost-category.dto.d.ts.map +1 -0
- package/dist/dto/create-project-cost-category.dto.js +59 -0
- package/dist/dto/create-project-cost-category.dto.js.map +1 -0
- package/dist/dto/create-project-cost-type.dto.d.ts +14 -0
- package/dist/dto/create-project-cost-type.dto.d.ts.map +1 -0
- package/dist/dto/create-project-cost-type.dto.js +87 -0
- package/dist/dto/create-project-cost-type.dto.js.map +1 -0
- package/dist/dto/create-project-cost.dto.d.ts +22 -0
- package/dist/dto/create-project-cost.dto.d.ts.map +1 -0
- package/dist/dto/create-project-cost.dto.js +135 -0
- package/dist/dto/create-project-cost.dto.js.map +1 -0
- package/dist/dto/create-task.dto.d.ts.map +1 -1
- package/dist/dto/create-task.dto.js +0 -1
- package/dist/dto/create-task.dto.js.map +1 -1
- package/dist/dto/get-project-cost-report.dto.d.ts +10 -0
- package/dist/dto/get-project-cost-report.dto.d.ts.map +1 -0
- package/dist/dto/get-project-cost-report.dto.js +65 -0
- package/dist/dto/get-project-cost-report.dto.js.map +1 -0
- package/dist/dto/list-project-cost-categories.dto.d.ts +6 -0
- package/dist/dto/list-project-cost-categories.dto.d.ts.map +1 -0
- package/dist/dto/list-project-cost-categories.dto.js +34 -0
- package/dist/dto/list-project-cost-categories.dto.js.map +1 -0
- package/dist/dto/list-project-cost-types.dto.d.ts +8 -0
- package/dist/dto/list-project-cost-types.dto.d.ts.map +1 -0
- package/dist/dto/list-project-cost-types.dto.js +45 -0
- package/dist/dto/list-project-cost-types.dto.js.map +1 -0
- package/dist/dto/list-project-costs.dto.d.ts +14 -0
- package/dist/dto/list-project-costs.dto.d.ts.map +1 -0
- package/dist/dto/list-project-costs.dto.js +81 -0
- package/dist/dto/list-project-costs.dto.js.map +1 -0
- package/dist/dto/list-tasks.dto.d.ts +1 -0
- package/dist/dto/list-tasks.dto.d.ts.map +1 -1
- package/dist/dto/list-tasks.dto.js +6 -0
- package/dist/dto/list-tasks.dto.js.map +1 -1
- package/dist/dto/list-timesheets.dto.d.ts +1 -0
- package/dist/dto/list-timesheets.dto.d.ts.map +1 -1
- package/dist/dto/list-timesheets.dto.js +7 -0
- package/dist/dto/list-timesheets.dto.js.map +1 -1
- package/dist/dto/update-collaborator-project-assignment.dto.d.ts +11 -0
- package/dist/dto/update-collaborator-project-assignment.dto.d.ts.map +1 -0
- package/dist/dto/update-collaborator-project-assignment.dto.js +65 -0
- package/dist/dto/update-collaborator-project-assignment.dto.js.map +1 -0
- package/dist/dto/update-project-cost-category.dto.d.ts +6 -0
- package/dist/dto/update-project-cost-category.dto.d.ts.map +1 -0
- package/dist/dto/update-project-cost-category.dto.js +9 -0
- package/dist/dto/update-project-cost-category.dto.js.map +1 -0
- package/dist/dto/update-project-cost-type.dto.d.ts +6 -0
- package/dist/dto/update-project-cost-type.dto.d.ts.map +1 -0
- package/dist/dto/update-project-cost-type.dto.js +9 -0
- package/dist/dto/update-project-cost-type.dto.js.map +1 -0
- package/dist/dto/update-project-cost.dto.d.ts +6 -0
- package/dist/dto/update-project-cost.dto.d.ts.map +1 -0
- package/dist/dto/update-project-cost.dto.js +9 -0
- package/dist/dto/update-project-cost.dto.js.map +1 -0
- package/dist/dto/update-task.dto.d.ts.map +1 -1
- package/dist/dto/update-task.dto.js +0 -1
- package/dist/dto/update-task.dto.js.map +1 -1
- package/dist/operations.module.d.ts.map +1 -1
- package/dist/operations.module.js +2 -0
- package/dist/operations.module.js.map +1 -1
- package/dist/operations.service.d.ts +584 -0
- package/dist/operations.service.d.ts.map +1 -1
- package/dist/operations.service.js +1734 -69
- package/dist/operations.service.js.map +1 -1
- package/hedhog/data/menu.yaml +52 -0
- package/hedhog/data/operations_project_cost_category.yaml +80 -0
- package/hedhog/data/operations_project_cost_type.yaml +503 -0
- package/hedhog/data/route.yaml +313 -0
- package/hedhog/frontend/app/_components/collaborator-costs-section.tsx.ejs +2 -18
- package/hedhog/frontend/app/_components/collaborator-form-screen.tsx.ejs +185 -276
- package/hedhog/frontend/app/_components/collaborator-tasks-tab.tsx.ejs +358 -0
- package/hedhog/frontend/app/_components/collaborator-timesheets-tab.tsx.ejs +242 -0
- package/hedhog/frontend/app/_components/my-project-summary-screen.tsx.ejs +167 -59
- package/hedhog/frontend/app/_components/person-select-with-create.tsx.ejs +1 -826
- package/hedhog/frontend/app/_components/project-assignments-tab.tsx.ejs +450 -0
- package/hedhog/frontend/app/_components/project-cost-report-screen.tsx.ejs +602 -0
- package/hedhog/frontend/app/_components/project-costs-section.tsx.ejs +1401 -0
- package/hedhog/frontend/app/_components/project-details-screen.tsx.ejs +3390 -889
- package/hedhog/frontend/app/_components/project-form-screen.tsx.ejs +118 -79
- package/hedhog/frontend/app/_components/task-detail-sheet.tsx.ejs +297 -2
- package/hedhog/frontend/app/_components/task-file-attachments.tsx.ejs +388 -0
- package/hedhog/frontend/app/_components/task-form-sheet.tsx.ejs +530 -0
- package/hedhog/frontend/app/_lib/api.ts.ejs +247 -0
- package/hedhog/frontend/app/_lib/types.ts.ejs +197 -7
- package/hedhog/frontend/app/_lib/utils/format.ts.ejs +9 -3
- package/hedhog/frontend/app/collaborators/page.tsx.ejs +18 -7
- package/hedhog/frontend/app/my-tasks/page.tsx.ejs +340 -133
- package/hedhog/frontend/app/project-cost-categories/page.tsx.ejs +674 -0
- package/hedhog/frontend/app/project-cost-types/page.tsx.ejs +845 -0
- package/hedhog/frontend/app/projects/[id]/costs-report/page.tsx.ejs +10 -0
- package/hedhog/frontend/app/projects/page.tsx.ejs +105 -22
- package/hedhog/frontend/app/reports/collaborators/page.tsx.ejs +20 -349
- package/hedhog/frontend/app/reports/projects/page.tsx.ejs +192 -484
- package/hedhog/frontend/messages/en.json +421 -11
- package/hedhog/frontend/messages/en.json.ejs +2043 -0
- package/hedhog/frontend/messages/operations/en.json +2068 -0
- package/hedhog/frontend/messages/operations/operations/en.json +2102 -0
- package/hedhog/frontend/messages/operations/operations/pt.json +2111 -0
- package/hedhog/frontend/messages/operations/pt.json +2072 -0
- package/hedhog/frontend/messages/pt.json +426 -14
- package/hedhog/frontend/messages/pt.json.ejs +2056 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/async-options-combobox.d.ts +29 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/async-options-combobox.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/async-options-combobox.js +95 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/async-options-combobox.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/async-options-combobox.tsx +233 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-costs-section.d.ts +10 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-costs-section.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-costs-section.js +577 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-costs-section.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-costs-section.tsx +868 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-details-screen.d.ts +4 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-details-screen.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-details-screen.js +337 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-details-screen.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-details-screen.tsx +476 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-form-screen.d.ts +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-form-screen.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-form-screen.js +1348 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-form-screen.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-form-screen.tsx +2233 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-select-with-create.d.ts +12 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-select-with-create.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-select-with-create.js +162 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-select-with-create.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-select-with-create.tsx +261 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-content-editor.d.ts +18 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-content-editor.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-content-editor.js +145 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-content-editor.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-content-editor.tsx +258 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-details-screen.d.ts +4 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-details-screen.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-details-screen.js +223 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-details-screen.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-details-screen.tsx +342 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-form-screen.d.ts +58 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-form-screen.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-form-screen.js +438 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-form-screen.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-form-screen.tsx +698 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/department-select-with-create.d.ts +20 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/department-select-with-create.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/department-select-with-create.js +233 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/department-select-with-create.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/department-select-with-create.tsx +392 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/my-project-summary-screen.d.ts +4 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/my-project-summary-screen.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/my-project-summary-screen.js +814 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/my-project-summary-screen.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/my-project-summary-screen.tsx +1288 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/operations-calendar-view.d.ts +21 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/operations-calendar-view.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/operations-calendar-view.js +174 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/operations-calendar-view.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/operations-calendar-view.tsx +306 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/operations-header.d.ts +10 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/operations-header.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/operations-header.js +12 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/operations-header.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/operations-header.tsx +29 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/person-select-with-create.d.ts +15 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/person-select-with-create.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/person-select-with-create.js +501 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/person-select-with-create.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/person-select-with-create.tsx +853 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-costs-section.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-costs-section.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-costs-section.js +847 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-costs-section.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-costs-section.tsx +1340 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-details-screen.d.ts +4 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-details-screen.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-details-screen.js +2930 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-details-screen.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-details-screen.tsx +4378 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-form-screen.d.ts +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-form-screen.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-form-screen.js +1013 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-form-screen.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-form-screen.tsx +1745 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/section-card.d.ts +13 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/section-card.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/section-card.js +38 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/section-card.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/section-card.tsx +74 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/status-badge.d.ts +7 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/status-badge.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/status-badge.js +11 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/status-badge.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/status-badge.tsx +15 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/system-user-select-with-create.d.ts +18 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/system-user-select-with-create.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/system-user-select-with-create.js +406 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/system-user-select-with-create.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/system-user-select-with-create.tsx +660 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/task-detail-sheet.d.ts +26 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/task-detail-sheet.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/task-detail-sheet.js +332 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/task-detail-sheet.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/task-detail-sheet.tsx +518 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/task-file-attachments.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/task-file-attachments.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/task-file-attachments.js +255 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/task-file-attachments.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/task-file-attachments.tsx +388 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/timesheet-task-create-sheet.d.ts +10 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/timesheet-task-create-sheet.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/timesheet-task-create-sheet.js +131 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/timesheet-task-create-sheet.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/timesheet-task-create-sheet.tsx +214 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/api.d.ts +108 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/api.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/api.js +162 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/api.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/api.ts +428 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/hooks/use-operations-access.d.ts +8 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/hooks/use-operations-access.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/hooks/use-operations-access.js +36 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/hooks/use-operations-access.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/hooks/use-operations-access.ts +44 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/types.d.ts +836 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/types.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/types.js +3 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/types.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/types.ts +860 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/utils/format.d.ts +16 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/utils/format.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/utils/format.js +182 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/utils/format.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/utils/format.ts +250 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/utils/forms.d.ts +4 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/utils/forms.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/utils/forms.js +51 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/utils/forms.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/utils/forms.ts +61 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/approvals/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/approvals/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/approvals/page.js +954 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/approvals/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/approvals/page.tsx +1277 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborator-types/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborator-types/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborator-types/page.js +488 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborator-types/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborator-types/page.tsx +805 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/[id]/edit/page.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/[id]/edit/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/[id]/edit/page.js +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/[id]/edit/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/[id]/edit/page.tsx +11 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/[id]/page.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/[id]/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/[id]/page.js +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/[id]/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/[id]/page.tsx +11 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/new/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/new/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/new/page.js +8 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/new/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/new/page.tsx +5 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/page.js +612 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/page.tsx +939 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/[id]/edit/page.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/[id]/edit/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/[id]/edit/page.js +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/[id]/edit/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/[id]/edit/page.tsx +11 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/[id]/page.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/[id]/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/[id]/page.js +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/[id]/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/[id]/page.tsx +11 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/new/page.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/new/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/new/page.js +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/new/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/new/page.tsx +17 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/page.js +348 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/page.tsx +536 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/departments/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/departments/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/departments/page.js +401 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/departments/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/departments/page.tsx +607 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/layout.d.ts +5 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/layout.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/layout.js +7 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/layout.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/layout.tsx +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-projects/[id]/page.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-projects/[id]/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-projects/[id]/page.js +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-projects/[id]/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-projects/[id]/page.tsx +11 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-projects/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-projects/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-projects/page.js +321 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-projects/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-projects/page.tsx +440 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-tasks/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-tasks/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-tasks/page.js +939 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-tasks/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-tasks/page.tsx +1499 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/async-options-combobox.d.ts +29 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/async-options-combobox.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/async-options-combobox.js +95 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/async-options-combobox.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/async-options-combobox.tsx +233 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-costs-section.d.ts +10 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-costs-section.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-costs-section.js +577 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-costs-section.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-costs-section.tsx +868 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-details-screen.d.ts +4 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-details-screen.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-details-screen.js +337 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-details-screen.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-details-screen.tsx +476 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-form-screen.d.ts +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-form-screen.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-form-screen.js +1348 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-form-screen.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-form-screen.tsx +2233 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-select-with-create.d.ts +12 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-select-with-create.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-select-with-create.js +162 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-select-with-create.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-select-with-create.tsx +261 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-content-editor.d.ts +18 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-content-editor.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-content-editor.js +145 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-content-editor.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-content-editor.tsx +258 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-details-screen.d.ts +4 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-details-screen.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-details-screen.js +223 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-details-screen.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-details-screen.tsx +342 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-form-screen.d.ts +58 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-form-screen.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-form-screen.js +438 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-form-screen.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-form-screen.tsx +698 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/department-select-with-create.d.ts +20 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/department-select-with-create.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/department-select-with-create.js +233 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/department-select-with-create.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/department-select-with-create.tsx +392 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/my-project-summary-screen.d.ts +4 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/my-project-summary-screen.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/my-project-summary-screen.js +814 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/my-project-summary-screen.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/my-project-summary-screen.tsx +1288 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/operations-calendar-view.d.ts +21 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/operations-calendar-view.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/operations-calendar-view.js +174 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/operations-calendar-view.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/operations-calendar-view.tsx +306 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/operations-header.d.ts +10 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/operations-header.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/operations-header.js +12 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/operations-header.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/operations-header.tsx +29 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/person-select-with-create.d.ts +15 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/person-select-with-create.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/person-select-with-create.js +501 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/person-select-with-create.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/person-select-with-create.tsx +853 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-cost-report-screen.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-cost-report-screen.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-cost-report-screen.js +459 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-cost-report-screen.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-cost-report-screen.tsx +598 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-costs-section.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-costs-section.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-costs-section.js +876 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-costs-section.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-costs-section.tsx +1368 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-details-screen.d.ts +4 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-details-screen.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-details-screen.js +2930 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-details-screen.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-details-screen.tsx +4378 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-form-screen.d.ts +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-form-screen.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-form-screen.js +1013 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-form-screen.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-form-screen.tsx +1745 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/section-card.d.ts +13 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/section-card.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/section-card.js +38 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/section-card.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/section-card.tsx +74 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/status-badge.d.ts +7 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/status-badge.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/status-badge.js +11 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/status-badge.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/status-badge.tsx +15 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/system-user-select-with-create.d.ts +18 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/system-user-select-with-create.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/system-user-select-with-create.js +406 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/system-user-select-with-create.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/system-user-select-with-create.tsx +660 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/task-detail-sheet.d.ts +26 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/task-detail-sheet.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/task-detail-sheet.js +332 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/task-detail-sheet.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/task-detail-sheet.tsx +518 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/task-file-attachments.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/task-file-attachments.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/task-file-attachments.js +255 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/task-file-attachments.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/task-file-attachments.tsx +388 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/timesheet-task-create-sheet.d.ts +10 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/timesheet-task-create-sheet.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/timesheet-task-create-sheet.js +131 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/timesheet-task-create-sheet.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/timesheet-task-create-sheet.tsx +214 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/api.d.ts +108 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/api.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/api.js +162 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/api.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/api.ts +428 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/hooks/use-operations-access.d.ts +8 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/hooks/use-operations-access.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/hooks/use-operations-access.js +36 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/hooks/use-operations-access.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/hooks/use-operations-access.ts +44 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/types.d.ts +836 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/types.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/types.js +3 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/types.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/types.ts +860 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/utils/format.d.ts +16 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/utils/format.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/utils/format.js +182 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/utils/format.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/utils/format.ts +250 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/utils/forms.d.ts +4 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/utils/forms.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/utils/forms.js +51 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/utils/forms.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/utils/forms.ts +61 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/approvals/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/approvals/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/approvals/page.js +954 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/approvals/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/approvals/page.tsx +1277 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborator-types/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborator-types/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborator-types/page.js +488 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborator-types/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborator-types/page.tsx +805 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/[id]/edit/page.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/[id]/edit/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/[id]/edit/page.js +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/[id]/edit/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/[id]/edit/page.tsx +11 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/[id]/page.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/[id]/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/[id]/page.js +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/[id]/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/[id]/page.tsx +11 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/new/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/new/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/new/page.js +8 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/new/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/new/page.tsx +5 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/page.js +612 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/page.tsx +939 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/[id]/edit/page.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/[id]/edit/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/[id]/edit/page.js +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/[id]/edit/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/[id]/edit/page.tsx +11 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/[id]/page.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/[id]/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/[id]/page.js +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/[id]/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/[id]/page.tsx +11 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/new/page.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/new/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/new/page.js +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/new/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/new/page.tsx +17 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/page.js +348 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/page.tsx +536 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/departments/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/departments/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/departments/page.js +401 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/departments/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/departments/page.tsx +607 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/layout.d.ts +5 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/layout.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/layout.js +7 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/layout.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/layout.tsx +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-projects/[id]/page.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-projects/[id]/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-projects/[id]/page.js +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-projects/[id]/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-projects/[id]/page.tsx +11 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-projects/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-projects/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-projects/page.js +321 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-projects/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-projects/page.tsx +440 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-tasks/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-tasks/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-tasks/page.js +939 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-tasks/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-tasks/page.tsx +1499 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/page.js +8 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/page.tsx +5 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/project-cost-categories/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/project-cost-categories/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/project-cost-categories/page.js +436 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/project-cost-categories/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/project-cost-categories/page.tsx +675 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/project-cost-types/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/project-cost-types/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/project-cost-types/page.js +563 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/project-cost-types/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/project-cost-types/page.tsx +846 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/costs-report/page.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/costs-report/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/costs-report/page.js +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/costs-report/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/costs-report/page.tsx +10 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/edit/page.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/edit/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/edit/page.js +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/edit/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/edit/page.tsx +11 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/page.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/page.js +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/page.tsx +11 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/new/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/new/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/new/page.js +8 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/new/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/new/page.tsx +5 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/page.js +492 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/page.tsx +757 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/reports/collaborators/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/reports/collaborators/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/reports/collaborators/page.js +342 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/reports/collaborators/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/reports/collaborators/page.tsx +430 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/reports/projects/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/reports/projects/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/reports/projects/page.js +338 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/reports/projects/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/reports/projects/page.tsx +428 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/schedule-adjustments/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/schedule-adjustments/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/schedule-adjustments/page.js +660 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/schedule-adjustments/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/schedule-adjustments/page.tsx +992 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/time-off/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/time-off/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/time-off/page.js +515 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/time-off/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/time-off/page.tsx +707 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/timesheets/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/timesheets/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/timesheets/page.js +1141 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/timesheets/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/timesheets/page.tsx +1705 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/page.js +8 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/page.tsx +5 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/project-cost-categories/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/project-cost-categories/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/project-cost-categories/page.js +436 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/project-cost-categories/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/project-cost-categories/page.tsx +675 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/project-cost-types/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/project-cost-types/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/project-cost-types/page.js +563 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/project-cost-types/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/project-cost-types/page.tsx +846 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/[id]/edit/page.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/[id]/edit/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/[id]/edit/page.js +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/[id]/edit/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/[id]/edit/page.tsx +11 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/[id]/page.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/[id]/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/[id]/page.js +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/[id]/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/[id]/page.tsx +11 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/new/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/new/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/new/page.js +8 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/new/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/new/page.tsx +5 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/page.js +492 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/page.tsx +757 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/reports/collaborators/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/reports/collaborators/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/reports/collaborators/page.js +342 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/reports/collaborators/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/reports/collaborators/page.tsx +430 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/reports/projects/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/reports/projects/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/reports/projects/page.js +338 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/reports/projects/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/reports/projects/page.tsx +428 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/schedule-adjustments/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/schedule-adjustments/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/schedule-adjustments/page.js +660 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/schedule-adjustments/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/schedule-adjustments/page.tsx +992 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/time-off/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/time-off/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/time-off/page.js +515 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/time-off/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/time-off/page.tsx +707 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/timesheets/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/timesheets/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/timesheets/page.js +1141 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/timesheets/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/timesheets/page.tsx +1705 -0
- package/hedhog/table/operations_project_assignment.yaml +1 -0
- package/hedhog/table/operations_project_cost.yaml +93 -0
- package/hedhog/table/operations_project_cost_category.yaml +37 -0
- package/hedhog/table/operations_project_cost_type.yaml +55 -0
- package/hedhog/table/operations_task_comment.yaml +26 -0
- package/hedhog/table/operations_task_file.yaml +23 -0
- package/package.json +5 -5
- package/src/controllers/operations-collaborators.controller.ts +26 -0
- package/src/controllers/operations-project-costs.controller.ts +249 -0
- package/src/controllers/operations-tasks.controller.ts +92 -9
- package/src/dto/create-collaborator-project-assignment.dto.ts +14 -0
- package/src/dto/create-project-cost-category.dto.ts +37 -0
- package/src/dto/create-project-cost-type.dto.ts +64 -0
- package/src/dto/create-project-cost.dto.ts +126 -0
- package/src/dto/create-task.dto.ts +0 -1
- package/src/dto/get-project-cost-report.dto.ts +46 -0
- package/src/dto/list-project-cost-categories.dto.ts +17 -0
- package/src/dto/list-project-cost-types.dto.ts +28 -0
- package/src/dto/list-project-costs.dto.ts +59 -0
- package/src/dto/list-tasks.dto.ts +7 -0
- package/src/dto/list-timesheets.dto.ts +7 -1
- package/src/dto/update-collaborator-project-assignment.dto.ts +58 -0
- package/src/dto/update-project-cost-category.dto.ts +4 -0
- package/src/dto/update-project-cost-type.dto.ts +4 -0
- package/src/dto/update-project-cost.dto.ts +4 -0
- package/src/dto/update-task.dto.ts +0 -1
- package/src/operations.module.ts +2 -0
- 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
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
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, (
|
|
1677
|
-
const nextPosition = ((
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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: (
|
|
7210
|
-
manager: (
|
|
7211
|
-
squad: String((
|
|
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((
|
|
7641
|
+
billableHours: Number((_k = row.billableHours) !== null && _k !== void 0 ? _k : 0),
|
|
7233
7642
|
reworkHours: 0,
|
|
7234
|
-
internalHours: Math.max(actualHours - Number((
|
|
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((
|
|
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(
|
|
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(
|
|
7415
|
-
|
|
7416
|
-
|
|
7417
|
-
|
|
7418
|
-
FROM
|
|
7419
|
-
|
|
7420
|
-
|
|
7421
|
-
|
|
7422
|
-
|
|
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(
|
|
7438
|
-
|
|
7439
|
-
|
|
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
|
-
|
|
7442
|
-
|
|
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
|
|
7456
|
-
const
|
|
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: (
|
|
7463
|
-
seniority: (
|
|
7464
|
-
department: (
|
|
7465
|
-
contractType: (
|
|
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((
|
|
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((
|
|
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
|
|
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([
|