@hed-hog/operations 0.0.322 → 0.0.326
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 +14 -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-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 +42 -0
- package/dist/controllers/operations-tasks.controller.d.ts.map +1 -1
- package/dist/controllers/operations-tasks.controller.js +48 -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/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/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 +571 -1
- package/dist/operations.service.d.ts.map +1 -1
- package/dist/operations.service.js +1793 -69
- package/dist/operations.service.js.map +1 -1
- package/hedhog/data/integration_event_catalog.yaml +313 -0
- 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 +274 -0
- package/hedhog/data/setting_group.yaml +21 -0
- package/hedhog/frontend/app/_components/collaborator-costs-section.tsx.ejs +2 -18
- package/hedhog/frontend/app/_components/collaborator-form-screen.tsx.ejs +593 -297
- 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 +533 -296
- package/hedhog/frontend/app/_components/person-select-with-create.tsx.ejs +1 -853
- 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 +2248 -2063
- package/hedhog/frontend/app/_components/project-form-screen.tsx.ejs +56 -11
- package/hedhog/frontend/app/_components/task-detail-sheet.tsx.ejs +454 -96
- package/hedhog/frontend/app/_components/task-form-sheet.tsx.ejs +784 -0
- package/hedhog/frontend/app/_lib/api.ts.ejs +256 -0
- package/hedhog/frontend/app/_lib/hooks/use-mention-items.ts.ejs +28 -0
- package/hedhog/frontend/app/_lib/types.ts.ejs +190 -0
- 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 +536 -328
- 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/reports/collaborators/page.tsx.ejs +20 -349
- package/hedhog/frontend/app/reports/projects/page.tsx.ejs +217 -485
- package/hedhog/frontend/messages/en.json +257 -5
- package/hedhog/frontend/messages/en.json.ejs +2060 -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 +256 -4
- package/hedhog/frontend/messages/pt.json.ejs +2067 -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 +837 -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 +861 -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 +837 -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 +861 -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_collaborator.yaml +5 -0
- package/hedhog/table/operations_collaborator_compensation_history.yaml +4 -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/package.json +6 -6
- 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 +49 -0
- 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/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/operations.module.ts +2 -0
- package/src/operations.service.ts +2472 -61
|
@@ -997,7 +997,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
997
997
|
throw new common_1.BadRequestException('Field "personId" is required.');
|
|
998
998
|
}
|
|
999
999
|
const collaboratorId = await this.prisma.$transaction(async (tx) => {
|
|
1000
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2;
|
|
1000
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6;
|
|
1001
1001
|
const normalizedCode = String((_a = data.code) !== null && _a !== void 0 ? _a : '').trim() ||
|
|
1002
1002
|
(await this.generateCollaboratorCode(tx));
|
|
1003
1003
|
const resolvedDepartment = await this.resolveDepartmentReference(tx, {
|
|
@@ -1053,11 +1053,23 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
1053
1053
|
});
|
|
1054
1054
|
}
|
|
1055
1055
|
if (data.compensationAmount != null) {
|
|
1056
|
-
await this.insertCollaboratorCompensationHistory(tx, createdCollaboratorId, Number(data.compensationAmount), actor.userId, null);
|
|
1056
|
+
await this.insertCollaboratorCompensationHistory(tx, createdCollaboratorId, Number(data.compensationAmount), actor.userId, (_3 = data.compensationNotes) !== null && _3 !== void 0 ? _3 : null, (_4 = data.compensationEffectiveDate) !== null && _4 !== void 0 ? _4 : null);
|
|
1057
|
+
}
|
|
1058
|
+
if (data.hourlyRate != null) {
|
|
1059
|
+
await tx.$executeRawUnsafe(`UPDATE operations_collaborator SET hourly_rate = $1 WHERE id = $2`, Number(data.hourlyRate), createdCollaboratorId);
|
|
1060
|
+
await this.insertCollaboratorCompensationHistory(tx, createdCollaboratorId, Number(data.hourlyRate), actor.userId, (_5 = data.compensationNotes) !== null && _5 !== void 0 ? _5 : null, (_6 = data.compensationEffectiveDate) !== null && _6 !== void 0 ? _6 : null, 'hourly_rate');
|
|
1057
1061
|
}
|
|
1058
1062
|
return createdCollaboratorId;
|
|
1059
1063
|
});
|
|
1060
|
-
|
|
1064
|
+
const result = await this.getCollaboratorByIdForUser(userId, collaboratorId);
|
|
1065
|
+
await this.integrationApi.publishEvent({
|
|
1066
|
+
eventName: 'operations.collaborator.created',
|
|
1067
|
+
sourceModule: 'operations',
|
|
1068
|
+
aggregateType: 'collaborator',
|
|
1069
|
+
aggregateId: String(collaboratorId),
|
|
1070
|
+
payload: { id: collaboratorId, displayName: resolvedDisplayName, status: normalizedStatus },
|
|
1071
|
+
}).catch(() => null);
|
|
1072
|
+
return result;
|
|
1061
1073
|
}
|
|
1062
1074
|
async updateCollaborator(userId, collaboratorId, data) {
|
|
1063
1075
|
var _a, _b, _c, _d, _e;
|
|
@@ -1080,34 +1092,41 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
1080
1092
|
}
|
|
1081
1093
|
this.pushUpdate(updates, params, 'level_label', data.levelLabel);
|
|
1082
1094
|
this.pushUpdate(updates, params, 'weekly_capacity_hours', data.weeklyCapacityHours);
|
|
1095
|
+
this.pushUpdate(updates, params, 'hourly_rate', data.hourlyRate);
|
|
1083
1096
|
this.pushUpdate(updates, params, 'status', normalizedStatus, 'operations_collaborator_status_ef779877d4_enum');
|
|
1084
1097
|
this.pushUpdate(updates, params, 'joined_at', data.joinedAt, 'date');
|
|
1085
1098
|
this.pushUpdate(updates, params, 'left_at', data.leftAt, 'date');
|
|
1086
1099
|
this.pushUpdate(updates, params, 'notes', data.notes);
|
|
1100
|
+
let currentHourlyRate = null;
|
|
1087
1101
|
await this.prisma.$transaction(async (tx) => {
|
|
1088
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
1102
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
|
|
1103
|
+
if (data.hourlyRate !== undefined && data.hourlyRate !== null) {
|
|
1104
|
+
const curr = (await tx.$queryRawUnsafe(`SELECT hourly_rate AS "hourlyRate" FROM operations_collaborator WHERE id = $1`, collaboratorId));
|
|
1105
|
+
currentHourlyRate =
|
|
1106
|
+
((_a = curr[0]) === null || _a === void 0 ? void 0 : _a.hourlyRate) != null ? Number(curr[0].hourlyRate) : null;
|
|
1107
|
+
}
|
|
1089
1108
|
if (data.collaboratorType !== undefined ||
|
|
1090
1109
|
data.collaboratorTypeId !== undefined ||
|
|
1091
1110
|
data.collaboratorTypeSlug !== undefined) {
|
|
1092
1111
|
const resolvedCollaboratorType = await this.resolveCollaboratorTypeReference(tx, {
|
|
1093
|
-
collaboratorTypeId: (
|
|
1094
|
-
collaboratorTypeSlug: (
|
|
1112
|
+
collaboratorTypeId: (_b = data.collaboratorTypeId) !== null && _b !== void 0 ? _b : null,
|
|
1113
|
+
collaboratorTypeSlug: (_d = (_c = data.collaboratorTypeSlug) !== null && _c !== void 0 ? _c : data.collaboratorType) !== null && _d !== void 0 ? _d : null,
|
|
1095
1114
|
});
|
|
1096
|
-
this.pushUpdate(updates, params, 'collaborator_type_id', (
|
|
1115
|
+
this.pushUpdate(updates, params, 'collaborator_type_id', (_e = resolvedCollaboratorType === null || resolvedCollaboratorType === void 0 ? void 0 : resolvedCollaboratorType.id) !== null && _e !== void 0 ? _e : null);
|
|
1097
1116
|
}
|
|
1098
1117
|
if (data.departmentId !== undefined) {
|
|
1099
1118
|
const resolvedDepartment = await this.resolveDepartmentReference(tx, {
|
|
1100
|
-
departmentId: (
|
|
1119
|
+
departmentId: (_f = data.departmentId) !== null && _f !== void 0 ? _f : null,
|
|
1101
1120
|
});
|
|
1102
|
-
this.pushUpdate(updates, params, 'department_id', (
|
|
1121
|
+
this.pushUpdate(updates, params, 'department_id', (_g = resolvedDepartment === null || resolvedDepartment === void 0 ? void 0 : resolvedDepartment.id) !== null && _g !== void 0 ? _g : null);
|
|
1103
1122
|
}
|
|
1104
1123
|
if (data.title !== undefined || data.jobTitleId !== undefined) {
|
|
1105
1124
|
const resolvedJobTitle = await this.resolveJobTitleReference(tx, {
|
|
1106
|
-
jobTitleId: (
|
|
1125
|
+
jobTitleId: (_h = data.jobTitleId) !== null && _h !== void 0 ? _h : null,
|
|
1107
1126
|
jobTitleName: data.title,
|
|
1108
1127
|
});
|
|
1109
|
-
this.pushUpdate(updates, params, 'job_title_id', (
|
|
1110
|
-
this.pushUpdate(updates, params, 'title', (
|
|
1128
|
+
this.pushUpdate(updates, params, 'job_title_id', (_j = resolvedJobTitle === null || resolvedJobTitle === void 0 ? void 0 : resolvedJobTitle.id) !== null && _j !== void 0 ? _j : null);
|
|
1129
|
+
this.pushUpdate(updates, params, 'title', (_k = resolvedJobTitle === null || resolvedJobTitle === void 0 ? void 0 : resolvedJobTitle.name) !== null && _k !== void 0 ? _k : this.normalizeOptionalText(data.title));
|
|
1111
1130
|
}
|
|
1112
1131
|
if (updates.length) {
|
|
1113
1132
|
params.push(collaboratorId);
|
|
@@ -1125,6 +1144,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
1125
1144
|
if (data.compensationAmount !== undefined ||
|
|
1126
1145
|
data.contractDescription !== undefined ||
|
|
1127
1146
|
data.autoGenerateContractDraft !== undefined ||
|
|
1147
|
+
data.hourlyRate !== undefined ||
|
|
1128
1148
|
data.joinedAt !== undefined ||
|
|
1129
1149
|
data.weeklyCapacityHours !== undefined ||
|
|
1130
1150
|
data.supervisorCollaboratorId !== undefined ||
|
|
@@ -1134,14 +1154,103 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
1134
1154
|
data.code !== undefined ||
|
|
1135
1155
|
data.personId !== undefined ||
|
|
1136
1156
|
data.displayName !== undefined) {
|
|
1157
|
+
let currentBudgetAmount = null;
|
|
1158
|
+
if (data.compensationAmount !== undefined &&
|
|
1159
|
+
data.compensationAmount !== null) {
|
|
1160
|
+
const hiringContracts = (await tx.$queryRawUnsafe(`SELECT budget_amount AS "budgetAmount"
|
|
1161
|
+
FROM operations_contract
|
|
1162
|
+
WHERE related_collaborator_id = $1
|
|
1163
|
+
AND origin_type = 'employee_hiring'
|
|
1164
|
+
AND deleted_at IS NULL
|
|
1165
|
+
ORDER BY created_at DESC
|
|
1166
|
+
LIMIT 1`, collaboratorId));
|
|
1167
|
+
currentBudgetAmount =
|
|
1168
|
+
((_l = hiringContracts[0]) === null || _l === void 0 ? void 0 : _l.budgetAmount) != null
|
|
1169
|
+
? Number(hiringContracts[0].budgetAmount)
|
|
1170
|
+
: null;
|
|
1171
|
+
}
|
|
1137
1172
|
await this.syncHiringContractDraft(tx, actor.userId, collaboratorId, data);
|
|
1138
1173
|
if (data.compensationAmount !== undefined &&
|
|
1139
1174
|
data.compensationAmount !== null) {
|
|
1140
|
-
|
|
1175
|
+
const newAmount = Number(data.compensationAmount);
|
|
1176
|
+
if (currentBudgetAmount === null ||
|
|
1177
|
+
newAmount !== currentBudgetAmount) {
|
|
1178
|
+
await this.insertCollaboratorCompensationHistory(tx, collaboratorId, newAmount, actor.userId, (_m = data.compensationNotes) !== null && _m !== void 0 ? _m : null, (_o = data.compensationEffectiveDate) !== null && _o !== void 0 ? _o : null);
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
if (data.hourlyRate !== undefined && data.hourlyRate !== null) {
|
|
1182
|
+
const newRate = Number(data.hourlyRate);
|
|
1183
|
+
if (currentHourlyRate === null || newRate !== currentHourlyRate) {
|
|
1184
|
+
await this.insertCollaboratorCompensationHistory(tx, collaboratorId, newRate, actor.userId, (_p = data.compensationNotes) !== null && _p !== void 0 ? _p : null, (_q = data.compensationEffectiveDate) !== null && _q !== void 0 ? _q : null, 'hourly_rate');
|
|
1185
|
+
}
|
|
1141
1186
|
}
|
|
1142
1187
|
}
|
|
1143
1188
|
});
|
|
1144
|
-
|
|
1189
|
+
const collaboratorResult = await this.getCollaboratorByIdForUser(userId, collaboratorId);
|
|
1190
|
+
await this.integrationApi.publishEvent({
|
|
1191
|
+
eventName: 'operations.collaborator.updated',
|
|
1192
|
+
sourceModule: 'operations',
|
|
1193
|
+
aggregateType: 'collaborator',
|
|
1194
|
+
aggregateId: String(collaboratorId),
|
|
1195
|
+
payload: { id: collaboratorId, displayName: data.displayName, status: data.status },
|
|
1196
|
+
}).catch(() => null);
|
|
1197
|
+
return collaboratorResult;
|
|
1198
|
+
}
|
|
1199
|
+
async updateCollaboratorProjectAssignment(collaboratorId, projectId, data) {
|
|
1200
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1201
|
+
const sets = [];
|
|
1202
|
+
const params = [collaboratorId, projectId];
|
|
1203
|
+
let idx = 3;
|
|
1204
|
+
if ('projectRoleId' in data) {
|
|
1205
|
+
sets.push(`project_role_id = $${idx++}`);
|
|
1206
|
+
params.push((_a = data.projectRoleId) !== null && _a !== void 0 ? _a : null);
|
|
1207
|
+
}
|
|
1208
|
+
if ('roleLabel' in data) {
|
|
1209
|
+
sets.push(`role_label = $${idx++}`);
|
|
1210
|
+
params.push((_b = data.roleLabel) !== null && _b !== void 0 ? _b : null);
|
|
1211
|
+
}
|
|
1212
|
+
if ('allocationPercent' in data) {
|
|
1213
|
+
sets.push(`allocation_percent = $${idx++}`);
|
|
1214
|
+
params.push((_c = data.allocationPercent) !== null && _c !== void 0 ? _c : null);
|
|
1215
|
+
}
|
|
1216
|
+
if ('weeklyHours' in data) {
|
|
1217
|
+
sets.push(`weekly_hours = $${idx++}`);
|
|
1218
|
+
params.push((_d = data.weeklyHours) !== null && _d !== void 0 ? _d : null);
|
|
1219
|
+
}
|
|
1220
|
+
if ('startDate' in data) {
|
|
1221
|
+
sets.push(`start_date = $${idx++}::date`);
|
|
1222
|
+
params.push((_e = data.startDate) !== null && _e !== void 0 ? _e : null);
|
|
1223
|
+
}
|
|
1224
|
+
if ('endDate' in data) {
|
|
1225
|
+
sets.push(`end_date = $${idx++}::date`);
|
|
1226
|
+
params.push((_f = data.endDate) !== null && _f !== void 0 ? _f : null);
|
|
1227
|
+
}
|
|
1228
|
+
if ('status' in data) {
|
|
1229
|
+
sets.push(`status = $${idx++}::operations_project_assignment_status_155b459bbf_enum`);
|
|
1230
|
+
params.push(data.status);
|
|
1231
|
+
}
|
|
1232
|
+
if (!sets.length)
|
|
1233
|
+
return { updated: false };
|
|
1234
|
+
sets.push(`updated_at = NOW()`);
|
|
1235
|
+
await this.prisma.$executeRawUnsafe(`UPDATE operations_project_assignment
|
|
1236
|
+
SET ${sets.join(', ')}
|
|
1237
|
+
WHERE collaborator_id = $1
|
|
1238
|
+
AND project_id = $2
|
|
1239
|
+
AND deleted_at IS NULL`, ...params);
|
|
1240
|
+
return { updated: true };
|
|
1241
|
+
}
|
|
1242
|
+
async addCollaboratorProjectAssignment(collaboratorId, data) {
|
|
1243
|
+
var _a;
|
|
1244
|
+
const existing = await this.querySingle(`SELECT id FROM operations_project_assignment
|
|
1245
|
+
WHERE collaborator_id = $1 AND project_id = $2 AND deleted_at IS NULL`, [collaboratorId, data.projectId]);
|
|
1246
|
+
if (existing) {
|
|
1247
|
+
return { id: existing.id, created: false };
|
|
1248
|
+
}
|
|
1249
|
+
const row = await this.querySingle(`INSERT INTO operations_project_assignment
|
|
1250
|
+
(collaborator_id, project_id, role_label, status)
|
|
1251
|
+
VALUES ($1, $2, $3, 'active')
|
|
1252
|
+
RETURNING id`, [collaboratorId, data.projectId, (_a = data.roleLabel) !== null && _a !== void 0 ? _a : '']);
|
|
1253
|
+
return { id: row.id, created: true };
|
|
1145
1254
|
}
|
|
1146
1255
|
async getCollaboratorCompensationHistory(userId, collaboratorId) {
|
|
1147
1256
|
const actor = await this.getActorContext(userId);
|
|
@@ -1154,6 +1263,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
1154
1263
|
h.actor_user_id AS "actorUserId",
|
|
1155
1264
|
u.name AS "actorName",
|
|
1156
1265
|
h.notes,
|
|
1266
|
+
h.amount_type AS "amountType",
|
|
1157
1267
|
h.created_at AS "createdAt"
|
|
1158
1268
|
FROM operations_collaborator_compensation_history h
|
|
1159
1269
|
LEFT JOIN "user" u ON u.id = h.actor_user_id
|
|
@@ -1589,6 +1699,10 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
1589
1699
|
if (paginationParams.status) {
|
|
1590
1700
|
filters.push(`t.status::text = ${this.param(params, paginationParams.status)}`);
|
|
1591
1701
|
}
|
|
1702
|
+
if (paginationParams.collaboratorId) {
|
|
1703
|
+
const colId = paginationParams.collaboratorId;
|
|
1704
|
+
filters.push(`(pa.collaborator_id = ${this.param(params, colId)} OR t.assignee_collaborator_id = ${this.param(params, colId)})`);
|
|
1705
|
+
}
|
|
1592
1706
|
const whereClause = filters.join(' AND ');
|
|
1593
1707
|
const totalRow = await this.querySingle(`SELECT COUNT(*)::text AS total
|
|
1594
1708
|
FROM operations_task t
|
|
@@ -1621,6 +1735,8 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
1621
1735
|
ac.display_name AS "assigneeName",
|
|
1622
1736
|
au.photo_id AS "assigneeUserPhotoId",
|
|
1623
1737
|
ap.avatar_id AS "assigneePersonAvatarId",
|
|
1738
|
+
COALESCE(task_comments.count, 0)::int AS "commentCount",
|
|
1739
|
+
COALESCE(task_files.count, 0)::int AS "fileCount",
|
|
1624
1740
|
t.created_at AS "createdAt",
|
|
1625
1741
|
t.deleted_at AS "deletedAt"
|
|
1626
1742
|
FROM operations_task t
|
|
@@ -1633,6 +1749,16 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
1633
1749
|
ON au.id = ac.user_id
|
|
1634
1750
|
LEFT JOIN person ap
|
|
1635
1751
|
ON ap.id = ac.person_id
|
|
1752
|
+
LEFT JOIN LATERAL (
|
|
1753
|
+
SELECT COUNT(*) AS count
|
|
1754
|
+
FROM operations_task_comment tc
|
|
1755
|
+
WHERE tc.task_id = t.id
|
|
1756
|
+
) task_comments ON TRUE
|
|
1757
|
+
LEFT JOIN LATERAL (
|
|
1758
|
+
SELECT COUNT(*) AS count
|
|
1759
|
+
FROM operations_task_file tf
|
|
1760
|
+
WHERE tf.operations_task_id = t.id
|
|
1761
|
+
) task_files ON TRUE
|
|
1636
1762
|
JOIN operations_project p
|
|
1637
1763
|
ON p.id = COALESCE(t.project_id, pa.project_id)
|
|
1638
1764
|
WHERE ${whereClause}
|
|
@@ -1642,7 +1768,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
1642
1768
|
return this.buildPaginationResult(rows.map((row) => (Object.assign(Object.assign({}, row), { label: [row.name, row.projectName].filter(Boolean).join(' • ') }))), Number((_b = totalRow === null || totalRow === void 0 ? void 0 : totalRow.total) !== null && _b !== void 0 ? _b : 0), pagination.page, pagination.pageSize);
|
|
1643
1769
|
}
|
|
1644
1770
|
async createTask(userId, data) {
|
|
1645
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
|
|
1771
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
|
|
1646
1772
|
const actor = await this.getActorContext(userId);
|
|
1647
1773
|
if (!actor.isCollaborator && !actor.isDirector && !actor.isSupervisor) {
|
|
1648
1774
|
throw new common_1.ForbiddenException('Operations collaborator access is required.');
|
|
@@ -1718,7 +1844,15 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
1718
1844
|
(_m = data.position) !== null && _m !== void 0 ? _m : nextPosition,
|
|
1719
1845
|
(_o = data.tags) !== null && _o !== void 0 ? _o : null,
|
|
1720
1846
|
]);
|
|
1721
|
-
|
|
1847
|
+
const task = await this.getProjectBoardTask((_p = created === null || created === void 0 ? void 0 : created.id) !== null && _p !== void 0 ? _p : 0);
|
|
1848
|
+
await this.integrationApi.publishEvent({
|
|
1849
|
+
eventName: 'operations.task.created',
|
|
1850
|
+
sourceModule: 'operations',
|
|
1851
|
+
aggregateType: 'task',
|
|
1852
|
+
aggregateId: String((_q = created === null || created === void 0 ? void 0 : created.id) !== null && _q !== void 0 ? _q : 0),
|
|
1853
|
+
payload: { id: created === null || created === void 0 ? void 0 : created.id, projectId, name, status: (_r = data.status) !== null && _r !== void 0 ? _r : 'todo', priority: (_s = data.priority) !== null && _s !== void 0 ? _s : 'medium' },
|
|
1854
|
+
}).catch(() => null);
|
|
1855
|
+
return task;
|
|
1722
1856
|
}
|
|
1723
1857
|
async updateTask(userId, taskId, data) {
|
|
1724
1858
|
const actor = await this.getActorContext(userId);
|
|
@@ -1776,7 +1910,15 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
1776
1910
|
? this.normalizeOptionalText(data.description)
|
|
1777
1911
|
: ((_d = current.description) !== null && _d !== void 0 ? _d : null), (_e = data.priority) !== null && _e !== void 0 ? _e : current.priority, nextStatus, data.dueDate !== undefined ? ((_f = data.dueDate) !== null && _f !== void 0 ? _f : null) : current.dueDate, data.estimateHours !== undefined ? ((_g = data.estimateHours) !== null && _g !== void 0 ? _g : null) : current.estimateHours, data.position !== undefined ? data.position : current.position, data.tags !== undefined ? ((_h = data.tags) !== null && _h !== void 0 ? _h : null) : current.tags, nextArchived, taskId, Boolean(current.deletedAt));
|
|
1778
1912
|
});
|
|
1779
|
-
|
|
1913
|
+
const taskResult = await this.getProjectBoardTask(taskId);
|
|
1914
|
+
await this.integrationApi.publishEvent({
|
|
1915
|
+
eventName: 'operations.task.updated',
|
|
1916
|
+
sourceModule: 'operations',
|
|
1917
|
+
aggregateType: 'task',
|
|
1918
|
+
aggregateId: String(taskId),
|
|
1919
|
+
payload: { id: taskId, name: data.name, status: data.status },
|
|
1920
|
+
}).catch(() => null);
|
|
1921
|
+
return taskResult;
|
|
1780
1922
|
}
|
|
1781
1923
|
async removeTask(userId, taskId, permanent = false) {
|
|
1782
1924
|
const actor = await this.getActorContext(userId);
|
|
@@ -1797,6 +1939,13 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
1797
1939
|
WHERE id = $1
|
|
1798
1940
|
AND deleted_at IS NULL`, taskId);
|
|
1799
1941
|
});
|
|
1942
|
+
await this.integrationApi.publishEvent({
|
|
1943
|
+
eventName: 'operations.task.deleted',
|
|
1944
|
+
sourceModule: 'operations',
|
|
1945
|
+
aggregateType: 'task',
|
|
1946
|
+
aggregateId: String(taskId),
|
|
1947
|
+
payload: { id: taskId, projectId: current.projectId, permanent },
|
|
1948
|
+
}).catch(() => null);
|
|
1800
1949
|
return { success: true };
|
|
1801
1950
|
}
|
|
1802
1951
|
async listTaskFiles(userId, taskId) {
|
|
@@ -1844,6 +1993,126 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
1844
1993
|
}
|
|
1845
1994
|
return { success: true };
|
|
1846
1995
|
}
|
|
1996
|
+
async listTaskComments(userId, taskId) {
|
|
1997
|
+
const actor = await this.getActorContext(userId);
|
|
1998
|
+
if (!actor.isCollaborator && !actor.isDirector && !actor.isSupervisor) {
|
|
1999
|
+
throw new common_1.ForbiddenException('Operations collaborator access is required.');
|
|
2000
|
+
}
|
|
2001
|
+
const current = await this.getTaskRecordForActor(this.prisma, actor, taskId);
|
|
2002
|
+
await this.assertProjectAccess(actor, current.projectId);
|
|
2003
|
+
return this.queryRows(`SELECT tc.id,
|
|
2004
|
+
tc.task_id AS "taskId",
|
|
2005
|
+
tc.content,
|
|
2006
|
+
tc.actor_collaborator_id AS "actorCollaboratorId",
|
|
2007
|
+
actor.display_name AS "actorName",
|
|
2008
|
+
actor_user.photo_id AS "actorUserPhotoId",
|
|
2009
|
+
actor_person.avatar_id AS "actorPersonAvatarId",
|
|
2010
|
+
tc.created_at AS "createdAt",
|
|
2011
|
+
tc.updated_at AS "updatedAt"
|
|
2012
|
+
FROM operations_task_comment tc
|
|
2013
|
+
LEFT JOIN operations_collaborator actor
|
|
2014
|
+
ON actor.id = tc.actor_collaborator_id
|
|
2015
|
+
AND actor.deleted_at IS NULL
|
|
2016
|
+
LEFT JOIN "user" actor_user
|
|
2017
|
+
ON actor_user.id = actor.user_id
|
|
2018
|
+
LEFT JOIN person actor_person
|
|
2019
|
+
ON actor_person.id = actor.person_id
|
|
2020
|
+
WHERE tc.task_id = $1
|
|
2021
|
+
ORDER BY tc.created_at ASC, tc.id ASC`, [taskId]);
|
|
2022
|
+
}
|
|
2023
|
+
async addTaskComment(userId, taskId, content) {
|
|
2024
|
+
var _a, _b;
|
|
2025
|
+
const actor = await this.getActorContext(userId);
|
|
2026
|
+
if (!actor.isCollaborator && !actor.isDirector && !actor.isSupervisor) {
|
|
2027
|
+
throw new common_1.ForbiddenException('Operations collaborator access is required.');
|
|
2028
|
+
}
|
|
2029
|
+
const current = await this.getTaskRecordForActor(this.prisma, actor, taskId);
|
|
2030
|
+
await this.assertProjectAccess(actor, current.projectId);
|
|
2031
|
+
const normalizedContent = this.normalizeOptionalText(content);
|
|
2032
|
+
if (!normalizedContent) {
|
|
2033
|
+
throw new common_1.BadRequestException('Comment content is required.');
|
|
2034
|
+
}
|
|
2035
|
+
const inserted = await this.queryRows(`INSERT INTO operations_task_comment (
|
|
2036
|
+
task_id,
|
|
2037
|
+
actor_collaborator_id,
|
|
2038
|
+
content,
|
|
2039
|
+
created_at,
|
|
2040
|
+
updated_at
|
|
2041
|
+
) VALUES ($1, $2, $3, NOW(), NOW())
|
|
2042
|
+
RETURNING id`, [taskId, (_a = actor.collaboratorId) !== null && _a !== void 0 ? _a : null, normalizedContent]);
|
|
2043
|
+
const commentId = (_b = inserted[0]) === null || _b === void 0 ? void 0 : _b.id;
|
|
2044
|
+
const comments = await this.listTaskComments(userId, taskId);
|
|
2045
|
+
const createdComment = comments.find((comment) => comment.id === commentId);
|
|
2046
|
+
if (!createdComment) {
|
|
2047
|
+
throw new common_1.NotFoundException('Task comment could not be loaded.');
|
|
2048
|
+
}
|
|
2049
|
+
return createdComment;
|
|
2050
|
+
}
|
|
2051
|
+
async updateTaskComment(userId, taskId, commentId, content) {
|
|
2052
|
+
var _a, _b;
|
|
2053
|
+
const actor = await this.getActorContext(userId);
|
|
2054
|
+
if (!actor.isCollaborator && !actor.isDirector && !actor.isSupervisor) {
|
|
2055
|
+
throw new common_1.ForbiddenException('Operations collaborator access is required.');
|
|
2056
|
+
}
|
|
2057
|
+
const current = await this.getTaskRecordForActor(this.prisma, actor, taskId);
|
|
2058
|
+
await this.assertProjectAccess(actor, current.projectId);
|
|
2059
|
+
const normalizedContent = this.normalizeOptionalText(content);
|
|
2060
|
+
if (!normalizedContent) {
|
|
2061
|
+
throw new common_1.BadRequestException('Comment content is required.');
|
|
2062
|
+
}
|
|
2063
|
+
const rows = await this.queryRows(`SELECT id, actor_collaborator_id AS "actorCollaboratorId", created_at AS "createdAt"
|
|
2064
|
+
FROM operations_task_comment
|
|
2065
|
+
WHERE id = $1 AND task_id = $2`, [commentId, taskId]);
|
|
2066
|
+
const row = rows[0];
|
|
2067
|
+
if (!row) {
|
|
2068
|
+
throw new common_1.NotFoundException('Comment not found.');
|
|
2069
|
+
}
|
|
2070
|
+
if (row.actorCollaboratorId !== actor.collaboratorId) {
|
|
2071
|
+
throw new common_1.ForbiddenException('You can only edit your own comments.');
|
|
2072
|
+
}
|
|
2073
|
+
const editSettings = await this.settingService.getSettingValues(['operations.comment-edit-window']);
|
|
2074
|
+
const editWindowMinutes = Number((_a = editSettings['operations.comment-edit-window']) !== null && _a !== void 0 ? _a : 5);
|
|
2075
|
+
if (editWindowMinutes > 0) {
|
|
2076
|
+
const diffMinutes = (Date.now() - new Date(row.createdAt).getTime()) / 60000;
|
|
2077
|
+
if (diffMinutes > editWindowMinutes) {
|
|
2078
|
+
throw new common_1.ForbiddenException(`Comments can only be edited within ${editWindowMinutes} minute(s) of posting.`);
|
|
2079
|
+
}
|
|
2080
|
+
}
|
|
2081
|
+
await this.queryRows(`UPDATE operations_task_comment
|
|
2082
|
+
SET content = $1, updated_at = NOW()
|
|
2083
|
+
WHERE id = $2`, [normalizedContent, commentId]);
|
|
2084
|
+
const comments = await this.listTaskComments(userId, taskId);
|
|
2085
|
+
return (_b = comments.find((c) => c.id === commentId)) !== null && _b !== void 0 ? _b : null;
|
|
2086
|
+
}
|
|
2087
|
+
async removeTaskComment(userId, taskId, commentId) {
|
|
2088
|
+
var _a;
|
|
2089
|
+
const actor = await this.getActorContext(userId);
|
|
2090
|
+
if (!actor.isCollaborator && !actor.isDirector && !actor.isSupervisor) {
|
|
2091
|
+
throw new common_1.ForbiddenException('Operations collaborator access is required.');
|
|
2092
|
+
}
|
|
2093
|
+
const current = await this.getTaskRecordForActor(this.prisma, actor, taskId);
|
|
2094
|
+
await this.assertProjectAccess(actor, current.projectId);
|
|
2095
|
+
const rows = await this.queryRows(`SELECT id, actor_collaborator_id AS "actorCollaboratorId", created_at AS "createdAt"
|
|
2096
|
+
FROM operations_task_comment
|
|
2097
|
+
WHERE id = $1 AND task_id = $2`, [commentId, taskId]);
|
|
2098
|
+
const row = rows[0];
|
|
2099
|
+
if (!row) {
|
|
2100
|
+
throw new common_1.NotFoundException('Comment not found.');
|
|
2101
|
+
}
|
|
2102
|
+
if (row.actorCollaboratorId !== actor.collaboratorId) {
|
|
2103
|
+
throw new common_1.ForbiddenException('You can only delete your own comments.');
|
|
2104
|
+
}
|
|
2105
|
+
const deleteSettings = await this.settingService.getSettingValues(['operations.comment-edit-window']);
|
|
2106
|
+
const deleteWindowMinutes = Number((_a = deleteSettings['operations.comment-edit-window']) !== null && _a !== void 0 ? _a : 5);
|
|
2107
|
+
if (deleteWindowMinutes > 0) {
|
|
2108
|
+
const diffMinutes = (Date.now() - new Date(row.createdAt).getTime()) / 60000;
|
|
2109
|
+
if (diffMinutes > deleteWindowMinutes) {
|
|
2110
|
+
throw new common_1.ForbiddenException(`Comments can only be deleted within ${deleteWindowMinutes} minute(s) of posting.`);
|
|
2111
|
+
}
|
|
2112
|
+
}
|
|
2113
|
+
await this.queryRows(`DELETE FROM operations_task_comment WHERE id = $1`, [commentId]);
|
|
2114
|
+
return { success: true };
|
|
2115
|
+
}
|
|
1847
2116
|
async listTimesheetEntries(userId, paginationParams) {
|
|
1848
2117
|
var _a, _b;
|
|
1849
2118
|
const actor = await this.getActorContext(userId);
|
|
@@ -2110,6 +2379,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
2110
2379
|
return this.getProjectDetails(projectId, actor.collaboratorId);
|
|
2111
2380
|
}
|
|
2112
2381
|
async createProject(userId, data) {
|
|
2382
|
+
var _a;
|
|
2113
2383
|
const actor = await this.getActorContext(userId);
|
|
2114
2384
|
this.ensureDirector(actor);
|
|
2115
2385
|
this.requireFields(data, ['code', 'name']);
|
|
@@ -2145,7 +2415,15 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
2145
2415
|
}
|
|
2146
2416
|
return projectId;
|
|
2147
2417
|
});
|
|
2148
|
-
|
|
2418
|
+
const result = await this.getProjectById(userId, createdProjectId);
|
|
2419
|
+
await this.integrationApi.publishEvent({
|
|
2420
|
+
eventName: 'operations.project.created',
|
|
2421
|
+
sourceModule: 'operations',
|
|
2422
|
+
aggregateType: 'project',
|
|
2423
|
+
aggregateId: String(createdProjectId),
|
|
2424
|
+
payload: { id: createdProjectId, code: data.code, name: data.name, status: (_a = data.status) !== null && _a !== void 0 ? _a : 'planning' },
|
|
2425
|
+
}).catch(() => null);
|
|
2426
|
+
return result;
|
|
2149
2427
|
}
|
|
2150
2428
|
async updateProject(userId, projectId, data) {
|
|
2151
2429
|
const actor = await this.getActorContext(userId);
|
|
@@ -2198,7 +2476,15 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
2198
2476
|
}
|
|
2199
2477
|
}
|
|
2200
2478
|
});
|
|
2201
|
-
|
|
2479
|
+
const projectResult = await this.getProjectById(userId, projectId);
|
|
2480
|
+
await this.integrationApi.publishEvent({
|
|
2481
|
+
eventName: 'operations.project.updated',
|
|
2482
|
+
sourceModule: 'operations',
|
|
2483
|
+
aggregateType: 'project',
|
|
2484
|
+
aggregateId: String(projectId),
|
|
2485
|
+
payload: { id: projectId, name: data.name, status: data.status },
|
|
2486
|
+
}).catch(() => null);
|
|
2487
|
+
return projectResult;
|
|
2202
2488
|
}
|
|
2203
2489
|
async listContracts(userId, filters = {}) {
|
|
2204
2490
|
var _a, _b;
|
|
@@ -3055,6 +3341,9 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
3055
3341
|
if (filters.dateTo) {
|
|
3056
3342
|
where.push(`t.week_start_date <= ${this.param(params, filters.dateTo)}::date`);
|
|
3057
3343
|
}
|
|
3344
|
+
if (filters.collaboratorId) {
|
|
3345
|
+
where.push(`t.collaborator_id = ${this.param(params, filters.collaboratorId)}`);
|
|
3346
|
+
}
|
|
3058
3347
|
if (pagination === null || pagination === void 0 ? void 0 : pagination.search) {
|
|
3059
3348
|
const searchPlaceholder = this.param(params, `%${pagination.search}%`);
|
|
3060
3349
|
where.push(`(
|
|
@@ -3088,7 +3377,8 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
3088
3377
|
t.submitted_at AS "submittedAt",
|
|
3089
3378
|
t.reviewed_at AS "reviewedAt",
|
|
3090
3379
|
t.notes,
|
|
3091
|
-
approval.decision_note AS "decisionNote"
|
|
3380
|
+
approval.decision_note AS "decisionNote",
|
|
3381
|
+
approval.id AS "approvalId"
|
|
3092
3382
|
FROM operations_timesheet t
|
|
3093
3383
|
JOIN operations_collaborator c ON c.id = t.collaborator_id
|
|
3094
3384
|
LEFT JOIN operations_collaborator a ON a.id = t.approver_collaborator_id
|
|
@@ -4587,6 +4877,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
4587
4877
|
COALESCE(NULLIF(job_title_record.name, ''), NULLIF(c.title, '')) AS "title",
|
|
4588
4878
|
c.level_label AS "levelLabel",
|
|
4589
4879
|
c.weekly_capacity_hours AS "weeklyCapacityHours",
|
|
4880
|
+
c.hourly_rate AS "hourlyRate",
|
|
4590
4881
|
c.status,
|
|
4591
4882
|
c.joined_at AS "joinedAt",
|
|
4592
4883
|
c.left_at AS "leftAt",
|
|
@@ -4882,6 +5173,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
4882
5173
|
au.photo_id AS "assigneeUserPhotoId",
|
|
4883
5174
|
ap.avatar_id AS "assigneePersonAvatarId",
|
|
4884
5175
|
t.project_assignment_id AS "projectAssignmentId",
|
|
5176
|
+
COALESCE(task_comments.count, 0)::int AS "commentCount",
|
|
4885
5177
|
t.created_at AS "createdAt"
|
|
4886
5178
|
FROM operations_task t
|
|
4887
5179
|
LEFT JOIN operations_collaborator ac
|
|
@@ -4890,6 +5182,11 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
4890
5182
|
ON au.id = ac.user_id
|
|
4891
5183
|
LEFT JOIN person ap
|
|
4892
5184
|
ON ap.id = ac.person_id
|
|
5185
|
+
LEFT JOIN LATERAL (
|
|
5186
|
+
SELECT COUNT(*) AS count
|
|
5187
|
+
FROM operations_task_comment tc
|
|
5188
|
+
WHERE tc.task_id = t.id
|
|
5189
|
+
) task_comments ON TRUE
|
|
4893
5190
|
WHERE COALESCE(t.project_id, (
|
|
4894
5191
|
SELECT pa.project_id FROM operations_project_assignment pa
|
|
4895
5192
|
WHERE pa.id = t.project_assignment_id AND pa.deleted_at IS NULL
|
|
@@ -4916,6 +5213,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
4916
5213
|
ap.avatar_id AS "assigneePersonAvatarId",
|
|
4917
5214
|
t.project_assignment_id AS "projectAssignmentId",
|
|
4918
5215
|
COALESCE(t.project_id, pa.project_id) AS "projectId",
|
|
5216
|
+
COALESCE(task_comments.count, 0)::int AS "commentCount",
|
|
4919
5217
|
t.created_at AS "createdAt",
|
|
4920
5218
|
t.deleted_at AS "deletedAt"
|
|
4921
5219
|
FROM operations_task t
|
|
@@ -4925,6 +5223,11 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
4925
5223
|
LEFT JOIN person ap ON ap.id = ac.person_id
|
|
4926
5224
|
LEFT JOIN operations_project_assignment pa
|
|
4927
5225
|
ON pa.id = t.project_assignment_id AND pa.deleted_at IS NULL
|
|
5226
|
+
LEFT JOIN LATERAL (
|
|
5227
|
+
SELECT COUNT(*) AS count
|
|
5228
|
+
FROM operations_task_comment tc
|
|
5229
|
+
WHERE tc.task_id = t.id
|
|
5230
|
+
) task_comments ON TRUE
|
|
4928
5231
|
WHERE t.id = $1`, [taskId]);
|
|
4929
5232
|
return (_a = rows[0]) !== null && _a !== void 0 ? _a : null;
|
|
4930
5233
|
}
|
|
@@ -6031,16 +6334,18 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
6031
6334
|
$1, $2, $3, $4, $5, NOW()
|
|
6032
6335
|
)`, contractId, actorUserId, action, note, metadataJson !== null && metadataJson !== void 0 ? metadataJson : null);
|
|
6033
6336
|
}
|
|
6034
|
-
async insertCollaboratorCompensationHistory(client, collaboratorId, amount, actorUserId, notes) {
|
|
6337
|
+
async insertCollaboratorCompensationHistory(client, collaboratorId, amount, actorUserId, notes, effectiveDate, amountType = 'salary') {
|
|
6035
6338
|
await client.$executeRawUnsafe(`INSERT INTO operations_collaborator_compensation_history (
|
|
6036
6339
|
collaborator_id,
|
|
6037
6340
|
amount,
|
|
6038
6341
|
actor_user_id,
|
|
6039
6342
|
notes,
|
|
6343
|
+
effective_date,
|
|
6344
|
+
amount_type,
|
|
6040
6345
|
created_at
|
|
6041
6346
|
) VALUES (
|
|
6042
|
-
$1, $2, $3, $4, NOW()
|
|
6043
|
-
)`, collaboratorId, amount, actorUserId, notes !== null && notes !== void 0 ? notes : null);
|
|
6347
|
+
$1, $2, $3, $4, $5::date, $6::operations_collaborator_compensation_history_am_f803c4196e_enum, NOW()
|
|
6348
|
+
)`, collaboratorId, amount, actorUserId, notes !== null && notes !== void 0 ? notes : null, effectiveDate !== null && effectiveDate !== void 0 ? effectiveDate : null, amountType);
|
|
6044
6349
|
}
|
|
6045
6350
|
async generateCollaboratorCode(client) {
|
|
6046
6351
|
for (let attempt = 0; attempt < 5; attempt += 1) {
|
|
@@ -6919,12 +7224,18 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
6919
7224
|
au.photo_id AS "assigneeUserPhotoId",
|
|
6920
7225
|
ap.avatar_id AS "assigneePersonAvatarId",
|
|
6921
7226
|
t.project_assignment_id AS "projectAssignmentId",
|
|
7227
|
+
COALESCE(task_comments.count, 0)::int AS "commentCount",
|
|
6922
7228
|
t.created_at AS "createdAt"
|
|
6923
7229
|
FROM operations_task t
|
|
6924
7230
|
LEFT JOIN operations_collaborator ac
|
|
6925
7231
|
ON ac.id = t.assignee_collaborator_id AND ac.deleted_at IS NULL
|
|
6926
7232
|
LEFT JOIN "user" au ON au.id = ac.user_id
|
|
6927
7233
|
LEFT JOIN person ap ON ap.id = ac.person_id
|
|
7234
|
+
LEFT JOIN LATERAL (
|
|
7235
|
+
SELECT COUNT(*) AS count
|
|
7236
|
+
FROM operations_task_comment tc
|
|
7237
|
+
WHERE tc.task_id = t.id
|
|
7238
|
+
) task_comments ON TRUE
|
|
6928
7239
|
WHERE COALESCE(t.project_id, (
|
|
6929
7240
|
SELECT pa.project_id FROM operations_project_assignment pa
|
|
6930
7241
|
WHERE pa.id = t.project_assignment_id AND pa.deleted_at IS NULL
|
|
@@ -6964,7 +7275,9 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
6964
7275
|
)`,
|
|
6965
7276
|
];
|
|
6966
7277
|
if (actor.collaboratorId) {
|
|
6967
|
-
|
|
7278
|
+
const p1 = this.param(params, actor.collaboratorId);
|
|
7279
|
+
const p2 = this.param(params, actor.collaboratorId);
|
|
7280
|
+
filters.push(`(pa.collaborator_id = ${p1} OR t.assignee_collaborator_id = ${p2})`);
|
|
6968
7281
|
}
|
|
6969
7282
|
if (pagination.search) {
|
|
6970
7283
|
const searchPlaceholder = this.param(params, `%${pagination.search}%`);
|
|
@@ -7010,6 +7323,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
7010
7323
|
ac.display_name AS "assigneeName",
|
|
7011
7324
|
au.photo_id AS "assigneeUserPhotoId",
|
|
7012
7325
|
ap.avatar_id AS "assigneePersonAvatarId",
|
|
7326
|
+
COALESCE(task_comments.count, 0)::int AS "commentCount",
|
|
7013
7327
|
t.created_at AS "createdAt",
|
|
7014
7328
|
t.deleted_at AS "deletedAt"
|
|
7015
7329
|
FROM operations_task t
|
|
@@ -7022,6 +7336,11 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
7022
7336
|
ON au.id = ac.user_id
|
|
7023
7337
|
LEFT JOIN person ap
|
|
7024
7338
|
ON ap.id = ac.person_id
|
|
7339
|
+
LEFT JOIN LATERAL (
|
|
7340
|
+
SELECT COUNT(*) AS count
|
|
7341
|
+
FROM operations_task_comment tc
|
|
7342
|
+
WHERE tc.task_id = t.id
|
|
7343
|
+
) task_comments ON TRUE
|
|
7025
7344
|
JOIN operations_project p
|
|
7026
7345
|
ON p.id = COALESCE(t.project_id, pa.project_id)
|
|
7027
7346
|
WHERE ${whereClause}
|
|
@@ -7173,7 +7492,12 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
7173
7492
|
: scenario === 'conservative'
|
|
7174
7493
|
? { revenue: 0.9, cost: 0.96, backlog: 0.82 }
|
|
7175
7494
|
: { revenue: 1, cost: 1, backlog: 1 };
|
|
7176
|
-
const
|
|
7495
|
+
const fromDate = new Date(`${from}T00:00:00`);
|
|
7496
|
+
const toDate = new Date(`${to}T00:00:00`);
|
|
7497
|
+
const periodDays = Math.max(1, Math.floor((toDate.getTime() - fromDate.getTime()) / 86400000) + 1);
|
|
7498
|
+
const periodWeeks = Math.max(1, Math.ceil(periodDays / 7));
|
|
7499
|
+
const periodMonths = periodDays / 30.4375;
|
|
7500
|
+
const params = [from, to, periodMonths, periodWeeks];
|
|
7177
7501
|
const where = [
|
|
7178
7502
|
'p.deleted_at IS NULL',
|
|
7179
7503
|
'(p.end_date IS NULL OR p.end_date >= $1::date)',
|
|
@@ -7196,6 +7520,8 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
7196
7520
|
COALESCE(assignment_stats.weekly_hours, 0)::text AS "weeklyHours",
|
|
7197
7521
|
COALESCE(time_stats.actual_hours, 0)::text AS "actualHours",
|
|
7198
7522
|
COALESCE(time_stats.billable_hours, 0)::text AS "billableHours",
|
|
7523
|
+
COALESCE(cost_stats.realized_cost, 0)::text AS "realizedCost",
|
|
7524
|
+
COALESCE(alloc_cost_stats.allocated_cost, 0)::text AS "allocatedCost",
|
|
7199
7525
|
COALESCE(task_stats.open_tasks, 0)::text AS "openTasks",
|
|
7200
7526
|
COALESCE(task_stats.backlog_hours, 0)::text AS "backlogHours",
|
|
7201
7527
|
COALESCE(task_stats.future_deliveries, 0)::text AS "futureDeliveries"
|
|
@@ -7224,6 +7550,146 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
7224
7550
|
AND entry.deleted_at IS NULL
|
|
7225
7551
|
AND entry.work_date BETWEEN $1::date AND $2::date
|
|
7226
7552
|
) time_stats ON TRUE
|
|
7553
|
+
LEFT JOIN LATERAL (
|
|
7554
|
+
SELECT COALESCE(
|
|
7555
|
+
SUM(
|
|
7556
|
+
entry.hours
|
|
7557
|
+
* (
|
|
7558
|
+
(
|
|
7559
|
+
COALESCE(collaborator_costs.salary_cost, 0)
|
|
7560
|
+
+ COALESCE(collaborator_costs.benefits_cost, 0)
|
|
7561
|
+
+ COALESCE(collaborator_costs.taxes_cost, 0)
|
|
7562
|
+
+ COALESCE(collaborator_costs.tools_cost, 0)
|
|
7563
|
+
)
|
|
7564
|
+
* $3::numeric
|
|
7565
|
+
/ GREATEST(
|
|
7566
|
+
COALESCE(collaborator_record.weekly_capacity_hours, 40)::numeric * $4::numeric,
|
|
7567
|
+
COALESCE(collaborator_hours.total_hours, 0),
|
|
7568
|
+
1
|
|
7569
|
+
)
|
|
7570
|
+
)
|
|
7571
|
+
),
|
|
7572
|
+
0
|
|
7573
|
+
) AS realized_cost
|
|
7574
|
+
FROM operations_timesheet_entry entry
|
|
7575
|
+
JOIN operations_project_assignment pa
|
|
7576
|
+
ON pa.id = entry.project_assignment_id
|
|
7577
|
+
AND pa.deleted_at IS NULL
|
|
7578
|
+
JOIN operations_collaborator collaborator_record
|
|
7579
|
+
ON collaborator_record.id = pa.collaborator_id
|
|
7580
|
+
AND collaborator_record.deleted_at IS NULL
|
|
7581
|
+
LEFT JOIN LATERAL (
|
|
7582
|
+
SELECT COALESCE(NULLIF(cost_totals.salary_cost, 0), compensation_history.amount, hiring_contract.budget_amount, 0) AS salary_cost,
|
|
7583
|
+
cost_totals.benefits_cost,
|
|
7584
|
+
cost_totals.taxes_cost,
|
|
7585
|
+
cost_totals.tools_cost
|
|
7586
|
+
FROM (
|
|
7587
|
+
SELECT COALESCE(SUM(cost.amount) FILTER (WHERE cost.recurrence::text = 'monthly' AND cost_type.slug IN ('salario-base', 'pro-labore')), 0) AS salary_cost,
|
|
7588
|
+
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,
|
|
7589
|
+
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,
|
|
7590
|
+
COALESCE(SUM(cost.amount) FILTER (WHERE cost.recurrence::text = 'monthly' AND cost_type.slug IN ('software-licenca', 'equipamento')), 0) AS tools_cost
|
|
7591
|
+
FROM operations_collaborator_cost cost
|
|
7592
|
+
LEFT JOIN operations_cost_type cost_type
|
|
7593
|
+
ON cost_type.id = cost.cost_type_id
|
|
7594
|
+
WHERE cost.collaborator_id = collaborator_record.id
|
|
7595
|
+
AND (cost.start_date IS NULL OR cost.start_date <= $2::date)
|
|
7596
|
+
AND (cost.end_date IS NULL OR cost.end_date >= $1::date)
|
|
7597
|
+
) cost_totals
|
|
7598
|
+
LEFT JOIN LATERAL (
|
|
7599
|
+
SELECT h.amount
|
|
7600
|
+
FROM operations_collaborator_compensation_history h
|
|
7601
|
+
WHERE h.collaborator_id = collaborator_record.id
|
|
7602
|
+
AND (h.effective_date IS NULL OR h.effective_date <= $2::date)
|
|
7603
|
+
ORDER BY h.effective_date DESC NULLS LAST, h.created_at DESC
|
|
7604
|
+
LIMIT 1
|
|
7605
|
+
) compensation_history ON TRUE
|
|
7606
|
+
LEFT JOIN LATERAL (
|
|
7607
|
+
SELECT oc.budget_amount
|
|
7608
|
+
FROM operations_contract oc
|
|
7609
|
+
WHERE oc.related_collaborator_id = collaborator_record.id
|
|
7610
|
+
AND oc.deleted_at IS NULL
|
|
7611
|
+
ORDER BY CASE WHEN oc.origin_type = 'employee_hiring' THEN 0 ELSE 1 END,
|
|
7612
|
+
oc.created_at DESC
|
|
7613
|
+
LIMIT 1
|
|
7614
|
+
) hiring_contract ON TRUE
|
|
7615
|
+
) collaborator_costs ON TRUE
|
|
7616
|
+
LEFT JOIN LATERAL (
|
|
7617
|
+
SELECT COALESCE(SUM(entry2.hours), 0) AS total_hours
|
|
7618
|
+
FROM operations_timesheet_entry entry2
|
|
7619
|
+
JOIN operations_project_assignment pa2
|
|
7620
|
+
ON pa2.id = entry2.project_assignment_id
|
|
7621
|
+
AND pa2.deleted_at IS NULL
|
|
7622
|
+
WHERE pa2.collaborator_id = collaborator_record.id
|
|
7623
|
+
AND entry2.deleted_at IS NULL
|
|
7624
|
+
AND entry2.work_date BETWEEN $1::date AND $2::date
|
|
7625
|
+
) collaborator_hours ON TRUE
|
|
7626
|
+
WHERE pa.project_id = p.id
|
|
7627
|
+
AND entry.deleted_at IS NULL
|
|
7628
|
+
AND entry.work_date BETWEEN $1::date AND $2::date
|
|
7629
|
+
) cost_stats ON TRUE
|
|
7630
|
+
LEFT JOIN LATERAL (
|
|
7631
|
+
SELECT COALESCE(
|
|
7632
|
+
SUM(
|
|
7633
|
+
pa.weekly_hours
|
|
7634
|
+
* (
|
|
7635
|
+
(
|
|
7636
|
+
COALESCE(alloc_costs.salary_cost, 0)
|
|
7637
|
+
+ COALESCE(alloc_costs.benefits_cost, 0)
|
|
7638
|
+
+ COALESCE(alloc_costs.taxes_cost, 0)
|
|
7639
|
+
+ COALESCE(alloc_costs.tools_cost, 0)
|
|
7640
|
+
)
|
|
7641
|
+
* $3::numeric
|
|
7642
|
+
/ GREATEST(
|
|
7643
|
+
COALESCE(alloc_col.weekly_capacity_hours, 40)::numeric,
|
|
7644
|
+
1
|
|
7645
|
+
)
|
|
7646
|
+
)
|
|
7647
|
+
),
|
|
7648
|
+
0
|
|
7649
|
+
) AS allocated_cost
|
|
7650
|
+
FROM operations_project_assignment pa
|
|
7651
|
+
JOIN operations_collaborator alloc_col
|
|
7652
|
+
ON alloc_col.id = pa.collaborator_id
|
|
7653
|
+
AND alloc_col.deleted_at IS NULL
|
|
7654
|
+
LEFT JOIN LATERAL (
|
|
7655
|
+
SELECT COALESCE(NULLIF(ct.salary_cost, 0), ch.amount, hc.budget_amount, 0) AS salary_cost,
|
|
7656
|
+
ct.benefits_cost,
|
|
7657
|
+
ct.taxes_cost,
|
|
7658
|
+
ct.tools_cost
|
|
7659
|
+
FROM (
|
|
7660
|
+
SELECT COALESCE(SUM(c.amount) FILTER (WHERE c.recurrence::text = 'monthly' AND ct2.slug IN ('salario-base', 'pro-labore')), 0) AS salary_cost,
|
|
7661
|
+
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,
|
|
7662
|
+
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,
|
|
7663
|
+
COALESCE(SUM(c.amount) FILTER (WHERE c.recurrence::text = 'monthly' AND ct2.slug IN ('software-licenca', 'equipamento')), 0) AS tools_cost
|
|
7664
|
+
FROM operations_collaborator_cost c
|
|
7665
|
+
LEFT JOIN operations_cost_type ct2
|
|
7666
|
+
ON ct2.id = c.cost_type_id
|
|
7667
|
+
WHERE c.collaborator_id = alloc_col.id
|
|
7668
|
+
AND (c.start_date IS NULL OR c.start_date <= $2::date)
|
|
7669
|
+
AND (c.end_date IS NULL OR c.end_date >= $1::date)
|
|
7670
|
+
) ct
|
|
7671
|
+
LEFT JOIN LATERAL (
|
|
7672
|
+
SELECT h.amount
|
|
7673
|
+
FROM operations_collaborator_compensation_history h
|
|
7674
|
+
WHERE h.collaborator_id = alloc_col.id
|
|
7675
|
+
AND (h.effective_date IS NULL OR h.effective_date <= $2::date)
|
|
7676
|
+
ORDER BY h.effective_date DESC NULLS LAST, h.created_at DESC
|
|
7677
|
+
LIMIT 1
|
|
7678
|
+
) ch ON TRUE
|
|
7679
|
+
LEFT JOIN LATERAL (
|
|
7680
|
+
SELECT oc.budget_amount
|
|
7681
|
+
FROM operations_contract oc
|
|
7682
|
+
WHERE oc.related_collaborator_id = alloc_col.id
|
|
7683
|
+
AND oc.deleted_at IS NULL
|
|
7684
|
+
ORDER BY CASE WHEN oc.origin_type = 'employee_hiring' THEN 0 ELSE 1 END,
|
|
7685
|
+
oc.created_at DESC
|
|
7686
|
+
LIMIT 1
|
|
7687
|
+
) hc ON TRUE
|
|
7688
|
+
) alloc_costs ON TRUE
|
|
7689
|
+
WHERE pa.project_id = p.id
|
|
7690
|
+
AND pa.deleted_at IS NULL
|
|
7691
|
+
AND pa.status IN ('planned', 'active')
|
|
7692
|
+
) alloc_cost_stats ON TRUE
|
|
7227
7693
|
LEFT JOIN LATERAL (
|
|
7228
7694
|
SELECT COUNT(*) FILTER (WHERE task.status IN ('todo', 'doing', 'review')) AS open_tasks,
|
|
7229
7695
|
COALESCE(SUM(task.estimate_hours) FILTER (WHERE task.status IN ('todo', 'doing', 'review')), 0) AS backlog_hours,
|
|
@@ -7234,18 +7700,20 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
7234
7700
|
) task_stats ON TRUE
|
|
7235
7701
|
WHERE ${where.join(' AND ')}
|
|
7236
7702
|
ORDER BY p.name ASC`, params);
|
|
7237
|
-
const fromDate = new Date(`${from}T00:00:00`);
|
|
7238
|
-
const toDate = new Date(`${to}T00:00:00`);
|
|
7239
|
-
const periodWeeks = Math.max(1, Math.ceil((toDate.getTime() - fromDate.getTime()) / 604800000));
|
|
7240
7703
|
const rows = dbRows
|
|
7241
7704
|
.map((row) => {
|
|
7242
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
7705
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
7243
7706
|
const progress = Number((_a = row.progressPercent) !== null && _a !== void 0 ? _a : 0);
|
|
7244
7707
|
const contractedRevenue = Number((_b = row.contractedRevenue) !== null && _b !== void 0 ? _b : 0);
|
|
7245
7708
|
const recognizedRevenue = contractedRevenue * (progress / 100);
|
|
7246
7709
|
const actualHours = Number((_c = row.actualHours) !== null && _c !== void 0 ? _c : 0);
|
|
7247
7710
|
const plannedHours = Math.max(Number((_d = row.weeklyHours) !== null && _d !== void 0 ? _d : 0) * periodWeeks, actualHours);
|
|
7248
|
-
const realizedCost = 0;
|
|
7711
|
+
const realizedCost = Number((_e = row.realizedCost) !== null && _e !== void 0 ? _e : 0);
|
|
7712
|
+
const allocatedCost = Number((_f = row.allocatedCost) !== null && _f !== void 0 ? _f : 0);
|
|
7713
|
+
const consumedHoursCost = realizedCost;
|
|
7714
|
+
const idlenessHours = Math.max(plannedHours - actualHours, 0);
|
|
7715
|
+
const idlenessRate = plannedHours > 0 ? (idlenessHours / plannedHours) * 100 : 0;
|
|
7716
|
+
const idlenessCost = Math.max(allocatedCost - consumedHoursCost, 0);
|
|
7249
7717
|
const reportStatus = row.status === 'paused'
|
|
7250
7718
|
? 'paused'
|
|
7251
7719
|
: row.status === 'at_risk'
|
|
@@ -7261,9 +7729,9 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
7261
7729
|
return {
|
|
7262
7730
|
id: Number(row.id),
|
|
7263
7731
|
name: row.name,
|
|
7264
|
-
client: (
|
|
7265
|
-
manager: (
|
|
7266
|
-
squad: String((
|
|
7732
|
+
client: (_g = row.client) !== null && _g !== void 0 ? _g : '-',
|
|
7733
|
+
manager: (_h = row.manager) !== null && _h !== void 0 ? _h : '-',
|
|
7734
|
+
squad: String((_j = row.squad) !== null && _j !== void 0 ? _j : '-').replace(/_/g, ' '),
|
|
7267
7735
|
status: reportStatus,
|
|
7268
7736
|
contractType: row.contractType === 'monthly_retainer'
|
|
7269
7737
|
? 'retainer'
|
|
@@ -7276,7 +7744,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
7276
7744
|
contractedRevenue,
|
|
7277
7745
|
recognizedRevenue,
|
|
7278
7746
|
realizedCost,
|
|
7279
|
-
forecastCost: realizedCost,
|
|
7747
|
+
forecastCost: realizedCost * multiplier.cost,
|
|
7280
7748
|
teamCost: realizedCost,
|
|
7281
7749
|
infraCost: 0,
|
|
7282
7750
|
licenseCost: 0,
|
|
@@ -7284,14 +7752,18 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
7284
7752
|
reworkCost: 0,
|
|
7285
7753
|
plannedHours,
|
|
7286
7754
|
actualHours,
|
|
7287
|
-
billableHours: Number((
|
|
7755
|
+
billableHours: Number((_k = row.billableHours) !== null && _k !== void 0 ? _k : 0),
|
|
7288
7756
|
reworkHours: 0,
|
|
7289
|
-
internalHours: Math.max(actualHours - Number((
|
|
7757
|
+
internalHours: Math.max(actualHours - Number((_l = row.billableHours) !== null && _l !== void 0 ? _l : 0), 0),
|
|
7290
7758
|
allocatedCapacity: plannedHours ? (actualHours / plannedHours) * 100 : 0,
|
|
7291
7759
|
physicalProgress: progress,
|
|
7292
7760
|
financialProgress: contractedRevenue ? (recognizedRevenue / contractedRevenue) * 100 : 0,
|
|
7293
7761
|
backlogValue: Math.max(contractedRevenue - recognizedRevenue, 0),
|
|
7294
|
-
futureDeliveries: Number((
|
|
7762
|
+
futureDeliveries: Number((_m = row.futureDeliveries) !== null && _m !== void 0 ? _m : 0),
|
|
7763
|
+
allocatedCost,
|
|
7764
|
+
consumedHoursCost,
|
|
7765
|
+
idlenessRate,
|
|
7766
|
+
idlenessCost,
|
|
7295
7767
|
risk,
|
|
7296
7768
|
recommendation: risk === 'alto'
|
|
7297
7769
|
? 'Revisar escopo, prazo ou capacidade alocada.'
|
|
@@ -7314,6 +7786,9 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
7314
7786
|
acc.avgDeadline += row.physicalProgress;
|
|
7315
7787
|
acc.avgAllocation += row.allocatedCapacity;
|
|
7316
7788
|
acc.atRisk += row.risk === 'alto' ? 1 : 0;
|
|
7789
|
+
acc.allocatedCost += row.allocatedCost;
|
|
7790
|
+
acc.consumedHoursCost += row.consumedHoursCost;
|
|
7791
|
+
acc.idlenessCost += row.idlenessCost;
|
|
7317
7792
|
return acc;
|
|
7318
7793
|
}, {
|
|
7319
7794
|
contractedRevenue: 0,
|
|
@@ -7331,12 +7806,21 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
7331
7806
|
avgAllocation: 0,
|
|
7332
7807
|
atRisk: 0,
|
|
7333
7808
|
burnRate: 0,
|
|
7809
|
+
allocatedCost: 0,
|
|
7810
|
+
consumedHoursCost: 0,
|
|
7811
|
+
idlenessCost: 0,
|
|
7812
|
+
idlenessRate: 0,
|
|
7813
|
+
plannedProfit: 0,
|
|
7334
7814
|
});
|
|
7335
7815
|
summary.profit = summary.recognizedRevenue - summary.realizedCost;
|
|
7336
7816
|
summary.margin = summary.recognizedRevenue ? (summary.profit / summary.recognizedRevenue) * 100 : 0;
|
|
7337
7817
|
summary.avgDeadline = rows.length ? summary.avgDeadline / rows.length : 0;
|
|
7338
7818
|
summary.avgAllocation = rows.length ? summary.avgAllocation / rows.length : 0;
|
|
7339
7819
|
summary.burnRate = summary.plannedHours ? (summary.actualHours / summary.plannedHours) * 100 : 0;
|
|
7820
|
+
summary.plannedProfit = summary.contractedRevenue - summary.allocatedCost;
|
|
7821
|
+
summary.idlenessRate = summary.plannedHours > 0
|
|
7822
|
+
? Math.max(0, (summary.plannedHours - summary.actualHours) / summary.plannedHours * 100)
|
|
7823
|
+
: 0;
|
|
7340
7824
|
const forecast = Array.from({ length: 12 }, (_, index) => {
|
|
7341
7825
|
const monthDate = new Date(fromDate);
|
|
7342
7826
|
monthDate.setMonth(fromDate.getMonth() + index);
|
|
@@ -7425,6 +7909,11 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
7425
7909
|
: scenario === 'conservative'
|
|
7426
7910
|
? { revenue: 0.9, cost: 0.96, capacity: 0.94 }
|
|
7427
7911
|
: { revenue: 1, cost: 1, capacity: 1 };
|
|
7912
|
+
const fromDate = new Date(`${from}T00:00:00`);
|
|
7913
|
+
const toDate = new Date(`${to}T00:00:00`);
|
|
7914
|
+
const periodDays = Math.max(1, Math.floor((toDate.getTime() - fromDate.getTime()) / 86400000) + 1);
|
|
7915
|
+
const periodWeeks = Math.max(1, Math.ceil(periodDays / 7));
|
|
7916
|
+
const periodMonths = periodDays / 30.4375;
|
|
7428
7917
|
const params = [from, to];
|
|
7429
7918
|
const where = [
|
|
7430
7919
|
'c.deleted_at IS NULL',
|
|
@@ -7451,9 +7940,14 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
7451
7940
|
COALESCE(cost_stats.taxes_cost, 0)::text AS "taxesCost",
|
|
7452
7941
|
COALESCE(cost_stats.tools_cost, 0)::text AS "toolsCost",
|
|
7453
7942
|
COALESCE(value_stats.billable_value, 0)::text AS "billableValue",
|
|
7943
|
+
COALESCE(assignment_stats.planned_allocated_hours, 0)::text AS "plannedAllocatedHours",
|
|
7944
|
+
COALESCE(assignment_stats.planned_billable_hours, 0)::text AS "plannedBillableHours",
|
|
7945
|
+
COALESCE(task_stats.open_task_hours, 0)::text AS "openTaskHours",
|
|
7946
|
+
COALESCE(task_stats.open_task_billable_hours, 0)::text AS "openTaskBillableHours",
|
|
7947
|
+
COALESCE(task_stats.open_tasks, 0)::text AS "openTasks",
|
|
7454
7948
|
COALESCE(value_stats.allocated_hours, 0)::text AS "allocatedHours",
|
|
7455
7949
|
COALESCE(value_stats.billable_hours, 0)::text AS "billableHours",
|
|
7456
|
-
COALESCE(
|
|
7950
|
+
COALESCE(assignment_stats.projects, 0)::text AS projects
|
|
7457
7951
|
FROM operations_collaborator c
|
|
7458
7952
|
LEFT JOIN person person_record ON person_record.id = c.person_id
|
|
7459
7953
|
LEFT JOIN operations_department department_record
|
|
@@ -7466,16 +7960,85 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
7466
7960
|
ON collaborator_type.id = c.collaborator_type_id
|
|
7467
7961
|
AND collaborator_type.deleted_at IS NULL
|
|
7468
7962
|
LEFT JOIN LATERAL (
|
|
7469
|
-
SELECT COALESCE(
|
|
7470
|
-
|
|
7471
|
-
|
|
7472
|
-
|
|
7473
|
-
FROM
|
|
7474
|
-
|
|
7475
|
-
|
|
7476
|
-
|
|
7477
|
-
|
|
7963
|
+
SELECT COALESCE(NULLIF(cost_totals.salary_cost, 0), compensation_history.amount, hiring_contract.budget_amount, 0) AS salary_cost,
|
|
7964
|
+
cost_totals.benefits_cost,
|
|
7965
|
+
cost_totals.taxes_cost,
|
|
7966
|
+
cost_totals.tools_cost
|
|
7967
|
+
FROM (
|
|
7968
|
+
SELECT COALESCE(SUM(cost.amount) FILTER (WHERE cost.recurrence::text = 'monthly' AND cost_type.slug IN ('salario-base', 'pro-labore')), 0) AS salary_cost,
|
|
7969
|
+
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,
|
|
7970
|
+
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,
|
|
7971
|
+
COALESCE(SUM(cost.amount) FILTER (WHERE cost.recurrence::text = 'monthly' AND cost_type.slug IN ('software-licenca', 'equipamento')), 0) AS tools_cost
|
|
7972
|
+
FROM operations_collaborator_cost cost
|
|
7973
|
+
LEFT JOIN operations_cost_type cost_type
|
|
7974
|
+
ON cost_type.id = cost.cost_type_id
|
|
7975
|
+
WHERE cost.collaborator_id = c.id
|
|
7976
|
+
AND (cost.start_date IS NULL OR cost.start_date <= $2::date)
|
|
7977
|
+
AND (cost.end_date IS NULL OR cost.end_date >= $1::date)
|
|
7978
|
+
) cost_totals
|
|
7979
|
+
LEFT JOIN LATERAL (
|
|
7980
|
+
SELECT h.amount
|
|
7981
|
+
FROM operations_collaborator_compensation_history h
|
|
7982
|
+
WHERE h.collaborator_id = c.id
|
|
7983
|
+
AND (h.effective_date IS NULL OR h.effective_date <= $2::date)
|
|
7984
|
+
ORDER BY h.effective_date DESC NULLS LAST, h.created_at DESC
|
|
7985
|
+
LIMIT 1
|
|
7986
|
+
) compensation_history ON TRUE
|
|
7987
|
+
LEFT JOIN LATERAL (
|
|
7988
|
+
SELECT oc.budget_amount
|
|
7989
|
+
FROM operations_contract oc
|
|
7990
|
+
WHERE oc.related_collaborator_id = c.id
|
|
7991
|
+
AND oc.deleted_at IS NULL
|
|
7992
|
+
ORDER BY CASE WHEN oc.origin_type = 'employee_hiring' THEN 0 ELSE 1 END,
|
|
7993
|
+
oc.created_at DESC
|
|
7994
|
+
LIMIT 1
|
|
7995
|
+
) hiring_contract ON TRUE
|
|
7478
7996
|
) cost_stats ON TRUE
|
|
7997
|
+
LEFT JOIN LATERAL (
|
|
7998
|
+
SELECT COALESCE(
|
|
7999
|
+
SUM(
|
|
8000
|
+
COALESCE(
|
|
8001
|
+
pa.weekly_hours,
|
|
8002
|
+
COALESCE(c.weekly_capacity_hours, 40) * COALESCE(pa.allocation_percent, 0) / 100
|
|
8003
|
+
) * GREATEST(
|
|
8004
|
+
CEIL(
|
|
8005
|
+
(
|
|
8006
|
+
LEAST(COALESCE(pa.end_date, $2::date), $2::date)
|
|
8007
|
+
- GREATEST(COALESCE(pa.start_date, $1::date), $1::date)
|
|
8008
|
+
+ 1
|
|
8009
|
+
) / 7.0
|
|
8010
|
+
),
|
|
8011
|
+
0
|
|
8012
|
+
)
|
|
8013
|
+
),
|
|
8014
|
+
0
|
|
8015
|
+
) AS planned_allocated_hours,
|
|
8016
|
+
COALESCE(
|
|
8017
|
+
SUM(
|
|
8018
|
+
COALESCE(
|
|
8019
|
+
pa.weekly_hours,
|
|
8020
|
+
COALESCE(c.weekly_capacity_hours, 40) * COALESCE(pa.allocation_percent, 0) / 100
|
|
8021
|
+
) * GREATEST(
|
|
8022
|
+
CEIL(
|
|
8023
|
+
(
|
|
8024
|
+
LEAST(COALESCE(pa.end_date, $2::date), $2::date)
|
|
8025
|
+
- GREATEST(COALESCE(pa.start_date, $1::date), $1::date)
|
|
8026
|
+
+ 1
|
|
8027
|
+
) / 7.0
|
|
8028
|
+
),
|
|
8029
|
+
0
|
|
8030
|
+
)
|
|
8031
|
+
) FILTER (WHERE pa.is_billable = true),
|
|
8032
|
+
0
|
|
8033
|
+
) AS planned_billable_hours,
|
|
8034
|
+
COUNT(DISTINCT pa.project_id) AS projects
|
|
8035
|
+
FROM operations_project_assignment pa
|
|
8036
|
+
WHERE pa.collaborator_id = c.id
|
|
8037
|
+
AND pa.deleted_at IS NULL
|
|
8038
|
+
AND pa.status IN ('planned', 'active')
|
|
8039
|
+
AND (pa.start_date IS NULL OR pa.start_date <= $2::date)
|
|
8040
|
+
AND (pa.end_date IS NULL OR pa.end_date >= $1::date)
|
|
8041
|
+
) assignment_stats ON TRUE
|
|
7479
8042
|
LEFT JOIN LATERAL (
|
|
7480
8043
|
SELECT COALESCE(SUM(entry.hours), 0) AS allocated_hours,
|
|
7481
8044
|
COALESCE(SUM(entry.hours) FILTER (WHERE pa.is_billable = true), 0) AS billable_hours,
|
|
@@ -7489,48 +8052,62 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
7489
8052
|
AND entry.work_date BETWEEN $1::date AND $2::date
|
|
7490
8053
|
) value_stats ON TRUE
|
|
7491
8054
|
LEFT JOIN LATERAL (
|
|
7492
|
-
SELECT COUNT(
|
|
7493
|
-
|
|
7494
|
-
|
|
8055
|
+
SELECT COUNT(*) AS open_tasks,
|
|
8056
|
+
COALESCE(SUM(COALESCE(task.estimate_hours, 0)), 0) AS open_task_hours,
|
|
8057
|
+
COALESCE(
|
|
8058
|
+
SUM(COALESCE(task.estimate_hours, 0)) FILTER (WHERE pa.is_billable = true),
|
|
8059
|
+
0
|
|
8060
|
+
) AS open_task_billable_hours
|
|
8061
|
+
FROM operations_task task
|
|
8062
|
+
LEFT JOIN operations_project_assignment pa
|
|
8063
|
+
ON pa.id = task.project_assignment_id
|
|
7495
8064
|
AND pa.deleted_at IS NULL
|
|
7496
|
-
|
|
7497
|
-
|
|
8065
|
+
WHERE task.deleted_at IS NULL
|
|
8066
|
+
AND task.status IN ('todo', 'doing', 'review')
|
|
8067
|
+
AND (
|
|
8068
|
+
task.assignee_collaborator_id = c.id
|
|
8069
|
+
OR pa.collaborator_id = c.id
|
|
8070
|
+
)
|
|
8071
|
+
) task_stats ON TRUE
|
|
7498
8072
|
WHERE ${where.join(' AND ')}
|
|
7499
8073
|
ORDER BY name ASC`, params);
|
|
7500
|
-
const fromDate = new Date(`${from}T00:00:00`);
|
|
7501
|
-
const toDate = new Date(`${to}T00:00:00`);
|
|
7502
|
-
const periodWeeks = Math.max(1, Math.ceil((toDate.getTime() - fromDate.getTime()) / 604800000));
|
|
7503
8074
|
const rows = dbRows.map((row) => {
|
|
7504
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
7505
|
-
const salaryCost = Number((_a = row.salaryCost) !== null && _a !== void 0 ? _a : 0);
|
|
7506
|
-
const benefitsCost = Number((_b = row.benefitsCost) !== null && _b !== void 0 ? _b : 0);
|
|
7507
|
-
const taxesCost = Number((_c = row.taxesCost) !== null && _c !== void 0 ? _c : 0);
|
|
7508
|
-
const toolsCost = Number((_d = row.toolsCost) !== null && _d !== void 0 ? _d : 0);
|
|
8075
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
|
|
8076
|
+
const salaryCost = Number((_a = row.salaryCost) !== null && _a !== void 0 ? _a : 0) * periodMonths;
|
|
8077
|
+
const benefitsCost = Number((_b = row.benefitsCost) !== null && _b !== void 0 ? _b : 0) * periodMonths;
|
|
8078
|
+
const taxesCost = Number((_c = row.taxesCost) !== null && _c !== void 0 ? _c : 0) * periodMonths;
|
|
8079
|
+
const toolsCost = Number((_d = row.toolsCost) !== null && _d !== void 0 ? _d : 0) * periodMonths;
|
|
7509
8080
|
const availableHours = Number((_e = row.weeklyCapacityHours) !== null && _e !== void 0 ? _e : 40) * periodWeeks;
|
|
7510
|
-
const
|
|
7511
|
-
const
|
|
8081
|
+
const plannedAllocatedHours = Number((_f = row.plannedAllocatedHours) !== null && _f !== void 0 ? _f : 0);
|
|
8082
|
+
const plannedBillableHours = Number((_g = row.plannedBillableHours) !== null && _g !== void 0 ? _g : 0);
|
|
8083
|
+
const openTaskHours = Number((_h = row.openTaskHours) !== null && _h !== void 0 ? _h : 0);
|
|
8084
|
+
const openTaskBillableHours = Number((_j = row.openTaskBillableHours) !== null && _j !== void 0 ? _j : 0);
|
|
8085
|
+
const actualAllocatedHours = Number((_k = row.allocatedHours) !== null && _k !== void 0 ? _k : 0);
|
|
8086
|
+
const actualBillableHours = Number((_l = row.billableHours) !== null && _l !== void 0 ? _l : 0);
|
|
8087
|
+
const allocatedHours = Math.max(actualAllocatedHours, plannedAllocatedHours, openTaskHours);
|
|
8088
|
+
const billableHours = Math.max(actualBillableHours, plannedBillableHours, openTaskBillableHours);
|
|
7512
8089
|
const allocation = availableHours ? (allocatedHours / availableHours) * 100 : 0;
|
|
7513
8090
|
const risk = allocation >= 98 ? 'alto' : allocation < 75 ? 'médio' : 'baixo';
|
|
7514
8091
|
return {
|
|
7515
8092
|
id: Number(row.id),
|
|
7516
8093
|
name: row.name,
|
|
7517
|
-
role: (
|
|
7518
|
-
seniority: (
|
|
7519
|
-
department: (
|
|
7520
|
-
contractType: (
|
|
8094
|
+
role: (_m = row.role) !== null && _m !== void 0 ? _m : '-',
|
|
8095
|
+
seniority: (_o = row.seniority) !== null && _o !== void 0 ? _o : '-',
|
|
8096
|
+
department: (_p = row.department) !== null && _p !== void 0 ? _p : '-',
|
|
8097
|
+
contractType: (_q = row.contractType) !== null && _q !== void 0 ? _q : '-',
|
|
7521
8098
|
startDate: row.startDate,
|
|
7522
8099
|
endDate: row.endDate,
|
|
7523
8100
|
salaryCost,
|
|
7524
8101
|
benefitsCost,
|
|
7525
8102
|
taxesCost,
|
|
7526
8103
|
toolsCost,
|
|
7527
|
-
billableValue: Number((
|
|
8104
|
+
billableValue: Number((_r = row.billableValue) !== null && _r !== void 0 ? _r : 0),
|
|
7528
8105
|
availableHours,
|
|
7529
8106
|
allocatedHours,
|
|
7530
8107
|
billableHours,
|
|
7531
8108
|
internalHours: Math.max(allocatedHours - billableHours, 0),
|
|
7532
8109
|
overtimeHours: Math.max(allocatedHours - availableHours, 0),
|
|
7533
|
-
projects: Number((
|
|
8110
|
+
projects: Number((_s = row.projects) !== null && _s !== void 0 ? _s : 0),
|
|
7534
8111
|
risk,
|
|
7535
8112
|
recommendation: risk === 'alto'
|
|
7536
8113
|
? 'Reduzir sobrecarga ou redistribuir entregas.'
|
|
@@ -7578,7 +8155,11 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
7578
8155
|
summary.freeHours = Math.max(summary.availableHours - summary.allocatedHours, 0);
|
|
7579
8156
|
summary.allocation = summary.availableHours ? (summary.allocatedHours / summary.availableHours) * 100 : 0;
|
|
7580
8157
|
summary.utilization = summary.availableHours ? (summary.billableHours / summary.availableHours) * 100 : 0;
|
|
7581
|
-
summary.hourlyCost = summary.allocatedHours
|
|
8158
|
+
summary.hourlyCost = summary.allocatedHours
|
|
8159
|
+
? summary.cost / summary.allocatedHours
|
|
8160
|
+
: summary.availableHours
|
|
8161
|
+
? summary.cost / summary.availableHours
|
|
8162
|
+
: 0;
|
|
7582
8163
|
const forecast = Array.from({ length: 12 }, (_, index) => {
|
|
7583
8164
|
const monthDate = new Date(fromDate);
|
|
7584
8165
|
monthDate.setMonth(fromDate.getMonth() + index);
|
|
@@ -7764,6 +8345,1149 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
7764
8345
|
await this.prisma.$queryRawUnsafe(`DELETE FROM operations_collaborator_cost WHERE id = $1`, costId);
|
|
7765
8346
|
return { success: true };
|
|
7766
8347
|
}
|
|
8348
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
8349
|
+
// Project Cost Categories
|
|
8350
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
8351
|
+
async listProjectCostCategories(userId, filters = {}) {
|
|
8352
|
+
var _a;
|
|
8353
|
+
await this.getActorContext(userId);
|
|
8354
|
+
const localeId = await this.resolvePreferredLocaleId();
|
|
8355
|
+
const params = [localeId];
|
|
8356
|
+
const where = ['pcc.deleted_at IS NULL'];
|
|
8357
|
+
if (filters.is_active === true) {
|
|
8358
|
+
where.push('pcc.is_active = true');
|
|
8359
|
+
}
|
|
8360
|
+
if ((_a = filters.search) === null || _a === void 0 ? void 0 : _a.trim()) {
|
|
8361
|
+
const p = this.param(params, `%${filters.search.trim()}%`);
|
|
8362
|
+
where.push(`(COALESCE(pccl.name, pcc.slug) ILIKE ${p} OR COALESCE(pcc.slug, '') ILIKE ${p})`);
|
|
8363
|
+
}
|
|
8364
|
+
const whereClause = `WHERE ${where.join(' AND ')}`;
|
|
8365
|
+
return this.queryRows(`SELECT pcc.id,
|
|
8366
|
+
pcc.slug,
|
|
8367
|
+
COALESCE(pccl.name, pcc.slug) AS name,
|
|
8368
|
+
pccl.description,
|
|
8369
|
+
pcc.icon,
|
|
8370
|
+
pcc.color,
|
|
8371
|
+
pcc.is_active AS "isActive",
|
|
8372
|
+
pcc.sort_order AS "sortOrder",
|
|
8373
|
+
pcc.created_at AS "createdAt"
|
|
8374
|
+
FROM operations_project_cost_category pcc
|
|
8375
|
+
LEFT JOIN LATERAL (
|
|
8376
|
+
SELECT l.name, l.description
|
|
8377
|
+
FROM operations_project_cost_category_locale l
|
|
8378
|
+
WHERE l.operations_project_cost_category_id = pcc.id
|
|
8379
|
+
ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC,
|
|
8380
|
+
l.id ASC
|
|
8381
|
+
LIMIT 1
|
|
8382
|
+
) pccl ON TRUE
|
|
8383
|
+
${whereClause}
|
|
8384
|
+
ORDER BY pcc.sort_order ASC, COALESCE(pccl.name, pcc.slug) ASC`, params);
|
|
8385
|
+
}
|
|
8386
|
+
async createProjectCostCategory(userId, data) {
|
|
8387
|
+
var _a;
|
|
8388
|
+
const actor = await this.getActorContext(userId);
|
|
8389
|
+
this.ensureDirector(actor);
|
|
8390
|
+
const slug = (_a = data.slug) === null || _a === void 0 ? void 0 : _a.trim();
|
|
8391
|
+
if (!slug) {
|
|
8392
|
+
throw new common_1.BadRequestException('Cost category slug is required.');
|
|
8393
|
+
}
|
|
8394
|
+
return this.prisma.$transaction(async (tx) => {
|
|
8395
|
+
var _a, _b, _c, _d, _e, _f;
|
|
8396
|
+
const localeId = await this.resolvePreferredLocaleId(tx);
|
|
8397
|
+
const created = (await tx.$queryRawUnsafe(`INSERT INTO operations_project_cost_category (slug, icon, color, is_active, sort_order, created_at, updated_at)
|
|
8398
|
+
VALUES ($1, $2, $3, $4, $5, NOW(), NOW())
|
|
8399
|
+
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));
|
|
8400
|
+
const createdId = (_e = created[0]) === null || _e === void 0 ? void 0 : _e.id;
|
|
8401
|
+
if (!createdId) {
|
|
8402
|
+
throw new common_1.BadRequestException('Unable to create project cost category.');
|
|
8403
|
+
}
|
|
8404
|
+
const name = typeof data.name === 'string' ? data.name : (data.name ? JSON.stringify(data.name) : slug);
|
|
8405
|
+
const description = typeof data.description === 'string' ? data.description : (data.description ? JSON.stringify(data.description) : null);
|
|
8406
|
+
if (localeId && name) {
|
|
8407
|
+
await tx.$executeRawUnsafe(`INSERT INTO operations_project_cost_category_locale (operations_project_cost_category_id, locale_id, name, description)
|
|
8408
|
+
VALUES ($1, $2, $3, $4)`, createdId, localeId, name, description !== null && description !== void 0 ? description : null);
|
|
8409
|
+
}
|
|
8410
|
+
const rows = (await tx.$queryRawUnsafe(`SELECT pcc.id,
|
|
8411
|
+
pcc.slug,
|
|
8412
|
+
COALESCE(pccl.name, pcc.slug) AS name,
|
|
8413
|
+
pccl.description,
|
|
8414
|
+
pcc.icon,
|
|
8415
|
+
pcc.color,
|
|
8416
|
+
pcc.is_active AS "isActive",
|
|
8417
|
+
pcc.sort_order AS "sortOrder",
|
|
8418
|
+
pcc.created_at AS "createdAt"
|
|
8419
|
+
FROM operations_project_cost_category pcc
|
|
8420
|
+
LEFT JOIN operations_project_cost_category_locale pccl
|
|
8421
|
+
ON pccl.operations_project_cost_category_id = pcc.id AND pccl.locale_id = $2
|
|
8422
|
+
WHERE pcc.id = $1`, createdId, localeId));
|
|
8423
|
+
return (_f = rows[0]) !== null && _f !== void 0 ? _f : null;
|
|
8424
|
+
});
|
|
8425
|
+
}
|
|
8426
|
+
async updateProjectCostCategory(userId, id, data) {
|
|
8427
|
+
const actor = await this.getActorContext(userId);
|
|
8428
|
+
this.ensureDirector(actor);
|
|
8429
|
+
const category = await this.querySingle(`SELECT id FROM operations_project_cost_category WHERE id = $1 AND deleted_at IS NULL LIMIT 1`, [id]);
|
|
8430
|
+
if (!category) {
|
|
8431
|
+
throw new common_1.NotFoundException('Project cost category not found.');
|
|
8432
|
+
}
|
|
8433
|
+
const sets = [];
|
|
8434
|
+
const params = [];
|
|
8435
|
+
if (data.slug !== undefined)
|
|
8436
|
+
sets.push(`slug = ${this.param(params, data.slug)}`);
|
|
8437
|
+
if (data.icon !== undefined)
|
|
8438
|
+
sets.push(`icon = ${this.param(params, data.icon)}`);
|
|
8439
|
+
if (data.color !== undefined)
|
|
8440
|
+
sets.push(`color = ${this.param(params, data.color)}`);
|
|
8441
|
+
if (data.is_active !== undefined)
|
|
8442
|
+
sets.push(`is_active = ${this.param(params, data.is_active)}`);
|
|
8443
|
+
if (data.sort_order !== undefined)
|
|
8444
|
+
sets.push(`sort_order = ${this.param(params, data.sort_order)}`);
|
|
8445
|
+
if (sets.length > 0) {
|
|
8446
|
+
sets.push(`updated_at = NOW()`);
|
|
8447
|
+
await this.prisma.$queryRawUnsafe(`UPDATE operations_project_cost_category SET ${sets.join(', ')} WHERE id = ${this.param(params, id)}`, ...params);
|
|
8448
|
+
}
|
|
8449
|
+
if (data.name !== undefined || data.description !== undefined) {
|
|
8450
|
+
const localeId = await this.resolvePreferredLocaleId();
|
|
8451
|
+
if (localeId) {
|
|
8452
|
+
const name = typeof data.name === 'string' ? data.name : (data.name ? JSON.stringify(data.name) : undefined);
|
|
8453
|
+
const description = typeof data.description === 'string' ? data.description : (data.description ? JSON.stringify(data.description) : null);
|
|
8454
|
+
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]);
|
|
8455
|
+
if (existing) {
|
|
8456
|
+
const localeSets = [];
|
|
8457
|
+
const localeParams = [];
|
|
8458
|
+
if (name !== undefined)
|
|
8459
|
+
localeSets.push(`name = ${this.param(localeParams, name)}`);
|
|
8460
|
+
if (description !== undefined)
|
|
8461
|
+
localeSets.push(`description = ${this.param(localeParams, description)}`);
|
|
8462
|
+
if (localeSets.length > 0) {
|
|
8463
|
+
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);
|
|
8464
|
+
}
|
|
8465
|
+
}
|
|
8466
|
+
else if (name) {
|
|
8467
|
+
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);
|
|
8468
|
+
}
|
|
8469
|
+
}
|
|
8470
|
+
}
|
|
8471
|
+
return this.querySingle(`SELECT id, slug FROM operations_project_cost_category WHERE id = $1`, [id]);
|
|
8472
|
+
}
|
|
8473
|
+
async deleteProjectCostCategory(userId, id) {
|
|
8474
|
+
const actor = await this.getActorContext(userId);
|
|
8475
|
+
this.ensureDirector(actor);
|
|
8476
|
+
const category = await this.querySingle(`SELECT id FROM operations_project_cost_category WHERE id = $1 AND deleted_at IS NULL LIMIT 1`, [id]);
|
|
8477
|
+
if (!category) {
|
|
8478
|
+
throw new common_1.NotFoundException('Project cost category not found.');
|
|
8479
|
+
}
|
|
8480
|
+
await this.prisma.$queryRawUnsafe(`UPDATE operations_project_cost_category SET deleted_at = NOW() WHERE id = $1`, id);
|
|
8481
|
+
return { success: true };
|
|
8482
|
+
}
|
|
8483
|
+
async getProjectCostCategory(userId, id) {
|
|
8484
|
+
await this.getActorContext(userId);
|
|
8485
|
+
const localeId = await this.resolvePreferredLocaleId();
|
|
8486
|
+
const row = await this.querySingle(`SELECT pcc.id,
|
|
8487
|
+
pcc.slug,
|
|
8488
|
+
COALESCE(pccl.name, pcc.slug) AS name,
|
|
8489
|
+
pccl.description,
|
|
8490
|
+
pcc.icon,
|
|
8491
|
+
pcc.color,
|
|
8492
|
+
pcc.is_active AS "isActive",
|
|
8493
|
+
pcc.sort_order AS "sortOrder",
|
|
8494
|
+
pcc.created_at AS "createdAt"
|
|
8495
|
+
FROM operations_project_cost_category pcc
|
|
8496
|
+
LEFT JOIN LATERAL (
|
|
8497
|
+
SELECT l.name, l.description
|
|
8498
|
+
FROM operations_project_cost_category_locale l
|
|
8499
|
+
WHERE l.operations_project_cost_category_id = pcc.id
|
|
8500
|
+
ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC,
|
|
8501
|
+
l.id ASC
|
|
8502
|
+
LIMIT 1
|
|
8503
|
+
) pccl ON TRUE
|
|
8504
|
+
WHERE pcc.id = $2 AND pcc.deleted_at IS NULL`, [localeId, id]);
|
|
8505
|
+
if (!row) {
|
|
8506
|
+
throw new common_1.NotFoundException('Project cost category not found.');
|
|
8507
|
+
}
|
|
8508
|
+
return row;
|
|
8509
|
+
}
|
|
8510
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
8511
|
+
// Project Cost Types
|
|
8512
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
8513
|
+
async listProjectCostTypes(userId, filters = {}) {
|
|
8514
|
+
var _a;
|
|
8515
|
+
await this.getActorContext(userId);
|
|
8516
|
+
const localeId = await this.resolvePreferredLocaleId();
|
|
8517
|
+
const params = [localeId];
|
|
8518
|
+
const where = ['pct.deleted_at IS NULL'];
|
|
8519
|
+
if (filters.is_active === true) {
|
|
8520
|
+
where.push('pct.is_active = true');
|
|
8521
|
+
}
|
|
8522
|
+
if (filters.category_id) {
|
|
8523
|
+
where.push(`pct.category_id = ${this.param(params, filters.category_id)}`);
|
|
8524
|
+
}
|
|
8525
|
+
if (filters.default_calculation_type) {
|
|
8526
|
+
where.push(`pct.default_calculation_type = ${this.param(params, filters.default_calculation_type)}`);
|
|
8527
|
+
}
|
|
8528
|
+
if ((_a = filters.search) === null || _a === void 0 ? void 0 : _a.trim()) {
|
|
8529
|
+
const p = this.param(params, `%${filters.search.trim()}%`);
|
|
8530
|
+
where.push(`(COALESCE(pctl.name, pct.slug) ILIKE ${p} OR COALESCE(pct.code, '') ILIKE ${p} OR COALESCE(pct.slug, '') ILIKE ${p})`);
|
|
8531
|
+
}
|
|
8532
|
+
const whereClause = `WHERE ${where.join(' AND ')}`;
|
|
8533
|
+
return this.queryRows(`SELECT pct.id,
|
|
8534
|
+
pct.slug,
|
|
8535
|
+
pct.code,
|
|
8536
|
+
COALESCE(pctl.name, pct.slug) AS name,
|
|
8537
|
+
pctl.description,
|
|
8538
|
+
pct.category_id AS "categoryId",
|
|
8539
|
+
pcc.slug AS "categorySlug",
|
|
8540
|
+
COALESCE(pccl.name, pcc.slug) AS "categoryName",
|
|
8541
|
+
pct.default_unit AS "defaultUnit",
|
|
8542
|
+
pct.default_calculation_type AS "defaultCalculationType",
|
|
8543
|
+
pct.is_recurring_allowed AS "isRecurringAllowed",
|
|
8544
|
+
pct.is_active AS "isActive",
|
|
8545
|
+
pct.sort_order AS "sortOrder",
|
|
8546
|
+
pct.created_at AS "createdAt"
|
|
8547
|
+
FROM operations_project_cost_type pct
|
|
8548
|
+
LEFT JOIN operations_project_cost_category pcc
|
|
8549
|
+
ON pcc.id = pct.category_id AND pcc.deleted_at IS NULL
|
|
8550
|
+
LEFT JOIN LATERAL (
|
|
8551
|
+
SELECT l.name, l.description
|
|
8552
|
+
FROM operations_project_cost_category_locale l
|
|
8553
|
+
WHERE l.operations_project_cost_category_id = pcc.id
|
|
8554
|
+
ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC,
|
|
8555
|
+
l.id ASC
|
|
8556
|
+
LIMIT 1
|
|
8557
|
+
) pccl ON TRUE
|
|
8558
|
+
LEFT JOIN LATERAL (
|
|
8559
|
+
SELECT l.name, l.description
|
|
8560
|
+
FROM operations_project_cost_type_locale l
|
|
8561
|
+
WHERE l.operations_project_cost_type_id = pct.id
|
|
8562
|
+
ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC,
|
|
8563
|
+
l.id ASC
|
|
8564
|
+
LIMIT 1
|
|
8565
|
+
) pctl ON TRUE
|
|
8566
|
+
${whereClause}
|
|
8567
|
+
ORDER BY pct.sort_order ASC, COALESCE(pctl.name, pct.slug) ASC`, params);
|
|
8568
|
+
}
|
|
8569
|
+
async createProjectCostType(userId, data) {
|
|
8570
|
+
var _a, _b, _c;
|
|
8571
|
+
const actor = await this.getActorContext(userId);
|
|
8572
|
+
this.ensureDirector(actor);
|
|
8573
|
+
const slug = (_a = data.slug) === null || _a === void 0 ? void 0 : _a.trim();
|
|
8574
|
+
if (!slug) {
|
|
8575
|
+
throw new common_1.BadRequestException('Cost type slug is required.');
|
|
8576
|
+
}
|
|
8577
|
+
if (data.category_id) {
|
|
8578
|
+
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]);
|
|
8579
|
+
if (!category) {
|
|
8580
|
+
throw new common_1.BadRequestException(`Category with id ${data.category_id} not found.`);
|
|
8581
|
+
}
|
|
8582
|
+
}
|
|
8583
|
+
const existingSlug = await this.querySingle(`SELECT id FROM operations_project_cost_type WHERE slug = $1 AND deleted_at IS NULL LIMIT 1`, [slug]);
|
|
8584
|
+
if (existingSlug) {
|
|
8585
|
+
throw new common_1.ConflictException(`A cost type with slug '${slug}' already exists.`);
|
|
8586
|
+
}
|
|
8587
|
+
const code = (_c = (_b = data.code) === null || _b === void 0 ? void 0 : _b.trim()) !== null && _c !== void 0 ? _c : slug;
|
|
8588
|
+
const existingCode = await this.querySingle(`SELECT id FROM operations_project_cost_type WHERE code = $1 AND deleted_at IS NULL LIMIT 1`, [code]);
|
|
8589
|
+
if (existingCode) {
|
|
8590
|
+
throw new common_1.ConflictException(`A cost type with code '${code}' already exists.`);
|
|
8591
|
+
}
|
|
8592
|
+
return this.prisma.$transaction(async (tx) => {
|
|
8593
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
8594
|
+
const localeId = await this.resolvePreferredLocaleId(tx);
|
|
8595
|
+
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)
|
|
8596
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, NOW(), NOW())
|
|
8597
|
+
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));
|
|
8598
|
+
const createdId = (_j = created[0]) === null || _j === void 0 ? void 0 : _j.id;
|
|
8599
|
+
if (!createdId) {
|
|
8600
|
+
throw new common_1.BadRequestException('Unable to create project cost type.');
|
|
8601
|
+
}
|
|
8602
|
+
const name = typeof data.name === 'string' ? data.name : (data.name ? JSON.stringify(data.name) : slug);
|
|
8603
|
+
const description = typeof data.description === 'string' ? data.description : (data.description ? JSON.stringify(data.description) : null);
|
|
8604
|
+
if (localeId && name) {
|
|
8605
|
+
await tx.$executeRawUnsafe(`INSERT INTO operations_project_cost_type_locale (operations_project_cost_type_id, locale_id, name, description)
|
|
8606
|
+
VALUES ($1, $2, $3, $4)`, createdId, localeId, name, description !== null && description !== void 0 ? description : null);
|
|
8607
|
+
}
|
|
8608
|
+
const rows = (await tx.$queryRawUnsafe(`SELECT pct.id,
|
|
8609
|
+
pct.slug,
|
|
8610
|
+
pct.code,
|
|
8611
|
+
COALESCE(pctl.name, pct.slug) AS name,
|
|
8612
|
+
pctl.description,
|
|
8613
|
+
pct.category_id AS "categoryId",
|
|
8614
|
+
pct.default_unit AS "defaultUnit",
|
|
8615
|
+
pct.default_calculation_type AS "defaultCalculationType",
|
|
8616
|
+
pct.is_recurring_allowed AS "isRecurringAllowed",
|
|
8617
|
+
pct.is_active AS "isActive",
|
|
8618
|
+
pct.sort_order AS "sortOrder",
|
|
8619
|
+
pct.created_at AS "createdAt"
|
|
8620
|
+
FROM operations_project_cost_type pct
|
|
8621
|
+
LEFT JOIN operations_project_cost_type_locale pctl
|
|
8622
|
+
ON pctl.operations_project_cost_type_id = pct.id AND pctl.locale_id = $2
|
|
8623
|
+
WHERE pct.id = $1`, createdId, localeId));
|
|
8624
|
+
return (_k = rows[0]) !== null && _k !== void 0 ? _k : null;
|
|
8625
|
+
});
|
|
8626
|
+
}
|
|
8627
|
+
async updateProjectCostType(userId, id, data) {
|
|
8628
|
+
const actor = await this.getActorContext(userId);
|
|
8629
|
+
this.ensureDirector(actor);
|
|
8630
|
+
const costType = await this.querySingle(`SELECT id FROM operations_project_cost_type WHERE id = $1 AND deleted_at IS NULL LIMIT 1`, [id]);
|
|
8631
|
+
if (!costType) {
|
|
8632
|
+
throw new common_1.NotFoundException('Project cost type not found.');
|
|
8633
|
+
}
|
|
8634
|
+
if (data.category_id !== undefined && data.category_id !== null) {
|
|
8635
|
+
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]);
|
|
8636
|
+
if (!category) {
|
|
8637
|
+
throw new common_1.BadRequestException(`Category with id ${data.category_id} not found.`);
|
|
8638
|
+
}
|
|
8639
|
+
}
|
|
8640
|
+
if (data.slug !== undefined) {
|
|
8641
|
+
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]);
|
|
8642
|
+
if (existingSlug) {
|
|
8643
|
+
throw new common_1.ConflictException(`A cost type with slug '${data.slug}' already exists.`);
|
|
8644
|
+
}
|
|
8645
|
+
}
|
|
8646
|
+
if (data.code !== undefined) {
|
|
8647
|
+
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]);
|
|
8648
|
+
if (existingCode) {
|
|
8649
|
+
throw new common_1.ConflictException(`A cost type with code '${data.code}' already exists.`);
|
|
8650
|
+
}
|
|
8651
|
+
}
|
|
8652
|
+
const sets = [];
|
|
8653
|
+
const params = [];
|
|
8654
|
+
if (data.category_id !== undefined)
|
|
8655
|
+
sets.push(`category_id = ${this.param(params, data.category_id)}`);
|
|
8656
|
+
if (data.slug !== undefined)
|
|
8657
|
+
sets.push(`slug = ${this.param(params, data.slug)}`);
|
|
8658
|
+
if (data.code !== undefined)
|
|
8659
|
+
sets.push(`code = ${this.param(params, data.code)}`);
|
|
8660
|
+
if (data.default_unit !== undefined)
|
|
8661
|
+
sets.push(`default_unit = ${this.param(params, data.default_unit)}`);
|
|
8662
|
+
if (data.default_calculation_type !== undefined)
|
|
8663
|
+
sets.push(`default_calculation_type = ${this.param(params, data.default_calculation_type)}`);
|
|
8664
|
+
if (data.is_recurring_allowed !== undefined)
|
|
8665
|
+
sets.push(`is_recurring_allowed = ${this.param(params, data.is_recurring_allowed)}`);
|
|
8666
|
+
if (data.is_active !== undefined)
|
|
8667
|
+
sets.push(`is_active = ${this.param(params, data.is_active)}`);
|
|
8668
|
+
if (data.sort_order !== undefined)
|
|
8669
|
+
sets.push(`sort_order = ${this.param(params, data.sort_order)}`);
|
|
8670
|
+
if (sets.length > 0) {
|
|
8671
|
+
sets.push(`updated_at = NOW()`);
|
|
8672
|
+
await this.prisma.$queryRawUnsafe(`UPDATE operations_project_cost_type SET ${sets.join(', ')} WHERE id = ${this.param(params, id)}`, ...params);
|
|
8673
|
+
}
|
|
8674
|
+
if (data.name !== undefined || data.description !== undefined) {
|
|
8675
|
+
const localeId = await this.resolvePreferredLocaleId();
|
|
8676
|
+
if (localeId) {
|
|
8677
|
+
const name = typeof data.name === 'string' ? data.name : (data.name ? JSON.stringify(data.name) : undefined);
|
|
8678
|
+
const description = typeof data.description === 'string' ? data.description : (data.description ? JSON.stringify(data.description) : null);
|
|
8679
|
+
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]);
|
|
8680
|
+
if (existing) {
|
|
8681
|
+
const localeSets = [];
|
|
8682
|
+
const localeParams = [];
|
|
8683
|
+
if (name !== undefined)
|
|
8684
|
+
localeSets.push(`name = ${this.param(localeParams, name)}`);
|
|
8685
|
+
if (description !== undefined)
|
|
8686
|
+
localeSets.push(`description = ${this.param(localeParams, description)}`);
|
|
8687
|
+
if (localeSets.length > 0) {
|
|
8688
|
+
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);
|
|
8689
|
+
}
|
|
8690
|
+
}
|
|
8691
|
+
else if (name) {
|
|
8692
|
+
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);
|
|
8693
|
+
}
|
|
8694
|
+
}
|
|
8695
|
+
}
|
|
8696
|
+
return this.querySingle(`SELECT id, slug FROM operations_project_cost_type WHERE id = $1`, [id]);
|
|
8697
|
+
}
|
|
8698
|
+
async deleteProjectCostType(userId, id) {
|
|
8699
|
+
const actor = await this.getActorContext(userId);
|
|
8700
|
+
this.ensureDirector(actor);
|
|
8701
|
+
const costType = await this.querySingle(`SELECT id FROM operations_project_cost_type WHERE id = $1 AND deleted_at IS NULL LIMIT 1`, [id]);
|
|
8702
|
+
if (!costType) {
|
|
8703
|
+
throw new common_1.NotFoundException('Project cost type not found.');
|
|
8704
|
+
}
|
|
8705
|
+
await this.prisma.$queryRawUnsafe(`UPDATE operations_project_cost_type SET deleted_at = NOW() WHERE id = $1`, id);
|
|
8706
|
+
return { success: true };
|
|
8707
|
+
}
|
|
8708
|
+
async getProjectCostType(userId, id) {
|
|
8709
|
+
await this.getActorContext(userId);
|
|
8710
|
+
const localeId = await this.resolvePreferredLocaleId();
|
|
8711
|
+
const row = await this.querySingle(`SELECT pct.id,
|
|
8712
|
+
pct.slug,
|
|
8713
|
+
pct.code,
|
|
8714
|
+
COALESCE(pctl.name, pct.slug) AS name,
|
|
8715
|
+
pctl.description,
|
|
8716
|
+
pct.default_unit,
|
|
8717
|
+
pct.default_calculation_type,
|
|
8718
|
+
pct.is_recurring_allowed,
|
|
8719
|
+
pct.is_active,
|
|
8720
|
+
pct.sort_order,
|
|
8721
|
+
pct.category_id,
|
|
8722
|
+
CASE WHEN pcc.id IS NOT NULL THEN
|
|
8723
|
+
jsonb_build_object(
|
|
8724
|
+
'id', pcc.id,
|
|
8725
|
+
'slug', pcc.slug,
|
|
8726
|
+
'name', COALESCE(pccl.name, pcc.slug),
|
|
8727
|
+
'color', pcc.color,
|
|
8728
|
+
'icon', pcc.icon
|
|
8729
|
+
)
|
|
8730
|
+
ELSE NULL END AS category
|
|
8731
|
+
FROM operations_project_cost_type pct
|
|
8732
|
+
LEFT JOIN operations_project_cost_category pcc
|
|
8733
|
+
ON pcc.id = pct.category_id AND pcc.deleted_at IS NULL
|
|
8734
|
+
LEFT JOIN LATERAL (
|
|
8735
|
+
SELECT l.name, l.description
|
|
8736
|
+
FROM operations_project_cost_category_locale l
|
|
8737
|
+
WHERE l.operations_project_cost_category_id = pcc.id
|
|
8738
|
+
ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC,
|
|
8739
|
+
l.id ASC
|
|
8740
|
+
LIMIT 1
|
|
8741
|
+
) pccl ON TRUE
|
|
8742
|
+
LEFT JOIN LATERAL (
|
|
8743
|
+
SELECT l.name, l.description
|
|
8744
|
+
FROM operations_project_cost_type_locale l
|
|
8745
|
+
WHERE l.operations_project_cost_type_id = pct.id
|
|
8746
|
+
ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC,
|
|
8747
|
+
l.id ASC
|
|
8748
|
+
LIMIT 1
|
|
8749
|
+
) pctl ON TRUE
|
|
8750
|
+
WHERE pct.id = $2 AND pct.deleted_at IS NULL`, [localeId, id]);
|
|
8751
|
+
if (!row) {
|
|
8752
|
+
throw new common_1.NotFoundException('Project cost type not found.');
|
|
8753
|
+
}
|
|
8754
|
+
return row;
|
|
8755
|
+
}
|
|
8756
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
8757
|
+
// Project Costs
|
|
8758
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
8759
|
+
async listProjectCosts(userId, projectId, filters = {}) {
|
|
8760
|
+
var _a;
|
|
8761
|
+
await this.getActorContext(userId);
|
|
8762
|
+
const localeId = await this.resolvePreferredLocaleId();
|
|
8763
|
+
const params = [localeId, projectId];
|
|
8764
|
+
const where = ['pc.deleted_at IS NULL', 'pc.project_id = $2'];
|
|
8765
|
+
if (filters.cost_type_id) {
|
|
8766
|
+
where.push(`pc.cost_type_id = ${this.param(params, filters.cost_type_id)}`);
|
|
8767
|
+
}
|
|
8768
|
+
if (filters.category_id) {
|
|
8769
|
+
where.push(`COALESCE(pc.category_id, pct.category_id) = ${this.param(params, filters.category_id)}`);
|
|
8770
|
+
}
|
|
8771
|
+
if (filters.recurrence_type) {
|
|
8772
|
+
where.push(`pc.recurrence_type = ${this.param(params, filters.recurrence_type)}`);
|
|
8773
|
+
}
|
|
8774
|
+
if (filters.calculation_type) {
|
|
8775
|
+
where.push(`pc.calculation_type = ${this.param(params, filters.calculation_type)}`);
|
|
8776
|
+
}
|
|
8777
|
+
if (filters.status) {
|
|
8778
|
+
where.push(`pc.status = ${this.param(params, filters.status)}`);
|
|
8779
|
+
}
|
|
8780
|
+
if (filters.is_billable !== undefined) {
|
|
8781
|
+
where.push(`pc.is_billable = ${this.param(params, filters.is_billable)}`);
|
|
8782
|
+
}
|
|
8783
|
+
if (filters.is_reimbursable !== undefined) {
|
|
8784
|
+
where.push(`pc.is_reimbursable = ${this.param(params, filters.is_reimbursable)}`);
|
|
8785
|
+
}
|
|
8786
|
+
if (filters.date_from) {
|
|
8787
|
+
where.push(`pc.cost_date >= ${this.param(params, filters.date_from)}::date`);
|
|
8788
|
+
}
|
|
8789
|
+
if (filters.date_to) {
|
|
8790
|
+
where.push(`pc.cost_date <= ${this.param(params, filters.date_to)}::date`);
|
|
8791
|
+
}
|
|
8792
|
+
if ((_a = filters.search) === null || _a === void 0 ? void 0 : _a.trim()) {
|
|
8793
|
+
const p = this.param(params, `%${filters.search.trim()}%`);
|
|
8794
|
+
where.push(`(COALESCE(pc.description, '') ILIKE ${p} OR COALESCE(pc.notes, '') ILIKE ${p})`);
|
|
8795
|
+
}
|
|
8796
|
+
const whereClause = `WHERE ${where.join(' AND ')}`;
|
|
8797
|
+
const rows = await this.queryRows(`SELECT pc.id,
|
|
8798
|
+
pc.project_id AS "projectId",
|
|
8799
|
+
pc.cost_type_id AS "costTypeId",
|
|
8800
|
+
pct.slug AS "costTypeSlug",
|
|
8801
|
+
pct.code AS "costTypeCode",
|
|
8802
|
+
COALESCE(pctl.name, pct.slug) AS "costTypeName",
|
|
8803
|
+
pc.category_id AS "categoryId",
|
|
8804
|
+
COALESCE(pc.category_id, pct.category_id) AS "resolvedCategoryId",
|
|
8805
|
+
pcc.slug AS "categorySlug",
|
|
8806
|
+
COALESCE(pccl.name, pcc.slug) AS "categoryName",
|
|
8807
|
+
pcc.color AS "categoryColor",
|
|
8808
|
+
pcc.icon AS "categoryIcon",
|
|
8809
|
+
pc.description,
|
|
8810
|
+
pc.amount::text AS amount,
|
|
8811
|
+
pc.quantity::text AS quantity,
|
|
8812
|
+
pc.unit_amount::text AS "unitAmount",
|
|
8813
|
+
pc.currency,
|
|
8814
|
+
TO_CHAR(pc.cost_date, 'YYYY-MM-DD') AS "costDate",
|
|
8815
|
+
TO_CHAR(pc.period_start, 'YYYY-MM-DD') AS "periodStart",
|
|
8816
|
+
TO_CHAR(pc.period_end, 'YYYY-MM-DD') AS "periodEnd",
|
|
8817
|
+
pc.calculation_type AS "calculationType",
|
|
8818
|
+
pc.recurrence_type AS "recurrenceType",
|
|
8819
|
+
pc.is_billable AS "isBillable",
|
|
8820
|
+
pc.is_reimbursable AS "isReimbursable",
|
|
8821
|
+
pc.notes,
|
|
8822
|
+
pc.status,
|
|
8823
|
+
pc.created_at AS "createdAt"
|
|
8824
|
+
FROM operations_project_cost pc
|
|
8825
|
+
LEFT JOIN operations_project_cost_type pct
|
|
8826
|
+
ON pct.id = pc.cost_type_id AND pct.deleted_at IS NULL
|
|
8827
|
+
LEFT JOIN operations_project_cost_category pcc
|
|
8828
|
+
ON pcc.id = COALESCE(pc.category_id, pct.category_id) AND pcc.deleted_at IS NULL
|
|
8829
|
+
LEFT JOIN LATERAL (
|
|
8830
|
+
SELECT l.name
|
|
8831
|
+
FROM operations_project_cost_type_locale l
|
|
8832
|
+
WHERE l.operations_project_cost_type_id = pct.id
|
|
8833
|
+
ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC,
|
|
8834
|
+
l.id ASC
|
|
8835
|
+
LIMIT 1
|
|
8836
|
+
) pctl ON TRUE
|
|
8837
|
+
LEFT JOIN LATERAL (
|
|
8838
|
+
SELECT l.name
|
|
8839
|
+
FROM operations_project_cost_category_locale l
|
|
8840
|
+
WHERE l.operations_project_cost_category_id = pcc.id
|
|
8841
|
+
ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC,
|
|
8842
|
+
l.id ASC
|
|
8843
|
+
LIMIT 1
|
|
8844
|
+
) pccl ON TRUE
|
|
8845
|
+
${whereClause}
|
|
8846
|
+
ORDER BY pc.created_at DESC`, params);
|
|
8847
|
+
return rows.map((row) => ({
|
|
8848
|
+
id: row.id,
|
|
8849
|
+
project_id: row.projectId,
|
|
8850
|
+
cost_type_id: row.costTypeId,
|
|
8851
|
+
category_id: row.categoryId,
|
|
8852
|
+
description: row.description,
|
|
8853
|
+
amount: row.amount,
|
|
8854
|
+
quantity: row.quantity,
|
|
8855
|
+
unit_amount: row.unitAmount,
|
|
8856
|
+
currency: row.currency,
|
|
8857
|
+
cost_date: row.costDate,
|
|
8858
|
+
period_start: row.periodStart,
|
|
8859
|
+
period_end: row.periodEnd,
|
|
8860
|
+
calculation_type: row.calculationType,
|
|
8861
|
+
recurrence_type: row.recurrenceType,
|
|
8862
|
+
is_billable: row.isBillable,
|
|
8863
|
+
is_reimbursable: row.isReimbursable,
|
|
8864
|
+
notes: row.notes,
|
|
8865
|
+
status: row.status,
|
|
8866
|
+
created_at: row.createdAt,
|
|
8867
|
+
cost_type: row.costTypeId
|
|
8868
|
+
? { id: row.costTypeId, slug: row.costTypeSlug, name: row.costTypeName, code: row.costTypeCode }
|
|
8869
|
+
: null,
|
|
8870
|
+
category: row.resolvedCategoryId
|
|
8871
|
+
? { id: row.resolvedCategoryId, slug: row.categorySlug, name: row.categoryName, color: row.categoryColor, icon: row.categoryIcon }
|
|
8872
|
+
: null,
|
|
8873
|
+
}));
|
|
8874
|
+
}
|
|
8875
|
+
async getProjectCostsSummaryGrouped(userId, projectId) {
|
|
8876
|
+
var _a, _b;
|
|
8877
|
+
const items = await this.listProjectCosts(userId, projectId, {});
|
|
8878
|
+
// Group by resolved category
|
|
8879
|
+
const categoryMap = new Map();
|
|
8880
|
+
for (const cost of items) {
|
|
8881
|
+
const cat = (_a = cost.category) !== null && _a !== void 0 ? _a : null;
|
|
8882
|
+
const key = (_b = cat === null || cat === void 0 ? void 0 : cat.id) !== null && _b !== void 0 ? _b : null;
|
|
8883
|
+
if (!categoryMap.has(key)) {
|
|
8884
|
+
categoryMap.set(key, { category: cat, items: [], total_amount: 0 });
|
|
8885
|
+
}
|
|
8886
|
+
const group = categoryMap.get(key);
|
|
8887
|
+
group.items.push(cost);
|
|
8888
|
+
group.total_amount += (parseFloat(String(cost.amount)) || 0) * (parseFloat(String(cost.quantity)) || 1);
|
|
8889
|
+
}
|
|
8890
|
+
const grand_total = Array.from(categoryMap.values()).reduce((sum, g) => sum + g.total_amount, 0);
|
|
8891
|
+
return {
|
|
8892
|
+
categories: Array.from(categoryMap.values()).map((g) => ({
|
|
8893
|
+
category: g.category,
|
|
8894
|
+
items: g.items,
|
|
8895
|
+
total_amount: Math.round(g.total_amount * 100) / 100,
|
|
8896
|
+
count: g.items.length,
|
|
8897
|
+
})),
|
|
8898
|
+
grand_total: Math.round(grand_total * 100) / 100,
|
|
8899
|
+
};
|
|
8900
|
+
}
|
|
8901
|
+
async getProjectCost(userId, projectId, id) {
|
|
8902
|
+
const rows = await this.listProjectCosts(userId, projectId, {});
|
|
8903
|
+
const cost = rows.find((r) => r.id === id);
|
|
8904
|
+
if (!cost) {
|
|
8905
|
+
throw new common_1.NotFoundException('Project cost not found.');
|
|
8906
|
+
}
|
|
8907
|
+
return cost;
|
|
8908
|
+
}
|
|
8909
|
+
async getProjectCostsSummary(userId, projectId) {
|
|
8910
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
8911
|
+
await this.getActorContext(userId);
|
|
8912
|
+
const localeId = await this.resolvePreferredLocaleId();
|
|
8913
|
+
// ── 1. Verify project exists and fetch budget_amount ──────────────────
|
|
8914
|
+
const project = await this.querySingle(`SELECT id, budget_amount::text AS "budgetAmount"
|
|
8915
|
+
FROM operations_project
|
|
8916
|
+
WHERE id = $1 AND deleted_at IS NULL
|
|
8917
|
+
LIMIT 1`, [projectId]);
|
|
8918
|
+
if (!project) {
|
|
8919
|
+
throw new common_1.NotFoundException('Project not found.');
|
|
8920
|
+
}
|
|
8921
|
+
const budgetAmount = parseFloat((_a = project.budgetAmount) !== null && _a !== void 0 ? _a : '0') || 0;
|
|
8922
|
+
// ── 2. Aggregated cost totals ─────────────────────────────────────────
|
|
8923
|
+
const totals = await this.querySingle(`SELECT
|
|
8924
|
+
COALESCE(SUM(CASE WHEN status != 'cancelled' THEN amount * quantity ELSE 0 END), 0)::text AS "extraCostTotal",
|
|
8925
|
+
COALESCE(SUM(CASE WHEN status = 'planned' THEN amount * quantity ELSE 0 END), 0)::text AS "plannedTotal",
|
|
8926
|
+
COALESCE(SUM(CASE WHEN status = 'approved' THEN amount * quantity ELSE 0 END), 0)::text AS "approvedTotal",
|
|
8927
|
+
COALESCE(SUM(CASE WHEN status = 'realized' THEN amount * quantity ELSE 0 END), 0)::text AS "realizedTotal",
|
|
8928
|
+
COALESCE(SUM(CASE WHEN status = 'cancelled' THEN amount * quantity ELSE 0 END), 0)::text AS "cancelledTotal",
|
|
8929
|
+
COALESCE(SUM(CASE WHEN is_billable = true AND status != 'cancelled' THEN amount * quantity ELSE 0 END), 0)::text AS "billableTotal",
|
|
8930
|
+
COALESCE(SUM(CASE WHEN is_billable = false AND status != 'cancelled' THEN amount * quantity ELSE 0 END), 0)::text AS "nonBillableTotal",
|
|
8931
|
+
COALESCE(SUM(CASE WHEN is_reimbursable = true AND status != 'cancelled' THEN amount * quantity ELSE 0 END), 0)::text AS "reimbursableTotal"
|
|
8932
|
+
FROM operations_project_cost
|
|
8933
|
+
WHERE deleted_at IS NULL
|
|
8934
|
+
AND project_id = $1`, [projectId]);
|
|
8935
|
+
const extraCostTotal = Math.round((parseFloat((_b = totals === null || totals === void 0 ? void 0 : totals.extraCostTotal) !== null && _b !== void 0 ? _b : '0') || 0) * 100) / 100;
|
|
8936
|
+
const plannedTotal = Math.round((parseFloat((_c = totals === null || totals === void 0 ? void 0 : totals.plannedTotal) !== null && _c !== void 0 ? _c : '0') || 0) * 100) / 100;
|
|
8937
|
+
const approvedTotal = Math.round((parseFloat((_d = totals === null || totals === void 0 ? void 0 : totals.approvedTotal) !== null && _d !== void 0 ? _d : '0') || 0) * 100) / 100;
|
|
8938
|
+
const realizedTotal = Math.round((parseFloat((_e = totals === null || totals === void 0 ? void 0 : totals.realizedTotal) !== null && _e !== void 0 ? _e : '0') || 0) * 100) / 100;
|
|
8939
|
+
const cancelledTotal = Math.round((parseFloat((_f = totals === null || totals === void 0 ? void 0 : totals.cancelledTotal) !== null && _f !== void 0 ? _f : '0') || 0) * 100) / 100;
|
|
8940
|
+
const billableTotal = Math.round((parseFloat((_g = totals === null || totals === void 0 ? void 0 : totals.billableTotal) !== null && _g !== void 0 ? _g : '0') || 0) * 100) / 100;
|
|
8941
|
+
const nonBillableTotal = Math.round((parseFloat((_h = totals === null || totals === void 0 ? void 0 : totals.nonBillableTotal) !== null && _h !== void 0 ? _h : '0') || 0) * 100) / 100;
|
|
8942
|
+
const reimbursableTotal = Math.round((parseFloat((_j = totals === null || totals === void 0 ? void 0 : totals.reimbursableTotal) !== null && _j !== void 0 ? _j : '0') || 0) * 100) / 100;
|
|
8943
|
+
const teamCostTotal = 0;
|
|
8944
|
+
const totalProjectCost = Math.round((teamCostTotal + extraCostTotal) * 100) / 100;
|
|
8945
|
+
const remainingBudget = Math.round((budgetAmount - totalProjectCost) * 100) / 100;
|
|
8946
|
+
const budgetUsagePercent = budgetAmount > 0
|
|
8947
|
+
? Math.round((totalProjectCost / budgetAmount) * 10000) / 100
|
|
8948
|
+
: 0;
|
|
8949
|
+
// ── 3. cost_by_category ───────────────────────────────────────────────
|
|
8950
|
+
const costByCategory = await this.queryRows(`SELECT
|
|
8951
|
+
COALESCE(pc.category_id, pct.category_id) AS "categoryId",
|
|
8952
|
+
pcc.slug AS "categorySlug",
|
|
8953
|
+
COALESCE(pccl.name, pcc.slug) AS "categoryName",
|
|
8954
|
+
pcc.color AS "categoryColor",
|
|
8955
|
+
pcc.icon AS "categoryIcon",
|
|
8956
|
+
SUM(pc.amount * pc.quantity)::text AS total,
|
|
8957
|
+
COUNT(*)::int AS count
|
|
8958
|
+
FROM operations_project_cost pc
|
|
8959
|
+
LEFT JOIN operations_project_cost_type pct
|
|
8960
|
+
ON pct.id = pc.cost_type_id AND pct.deleted_at IS NULL
|
|
8961
|
+
LEFT JOIN operations_project_cost_category pcc
|
|
8962
|
+
ON pcc.id = COALESCE(pc.category_id, pct.category_id) AND pcc.deleted_at IS NULL
|
|
8963
|
+
LEFT JOIN LATERAL (
|
|
8964
|
+
SELECT l.name
|
|
8965
|
+
FROM operations_project_cost_category_locale l
|
|
8966
|
+
WHERE l.operations_project_cost_category_id = pcc.id
|
|
8967
|
+
ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC,
|
|
8968
|
+
l.id ASC
|
|
8969
|
+
LIMIT 1
|
|
8970
|
+
) pccl ON TRUE
|
|
8971
|
+
WHERE pc.deleted_at IS NULL
|
|
8972
|
+
AND pc.project_id = $2
|
|
8973
|
+
AND pc.status != 'cancelled'
|
|
8974
|
+
GROUP BY COALESCE(pc.category_id, pct.category_id), pcc.slug, pcc.color, pcc.icon, pccl.name
|
|
8975
|
+
ORDER BY SUM(pc.amount * pc.quantity) DESC`, [localeId, projectId]);
|
|
8976
|
+
// ── 4. cost_by_type ───────────────────────────────────────────────────
|
|
8977
|
+
const costByType = await this.queryRows(`SELECT
|
|
8978
|
+
pc.cost_type_id AS "costTypeId",
|
|
8979
|
+
pct.slug AS "costTypeSlug",
|
|
8980
|
+
COALESCE(pctl.name, pct.slug) AS "costTypeName",
|
|
8981
|
+
pct.code AS "costTypeCode",
|
|
8982
|
+
SUM(pc.amount * pc.quantity)::text AS total,
|
|
8983
|
+
COUNT(*)::int AS count
|
|
8984
|
+
FROM operations_project_cost pc
|
|
8985
|
+
LEFT JOIN operations_project_cost_type pct
|
|
8986
|
+
ON pct.id = pc.cost_type_id AND pct.deleted_at IS NULL
|
|
8987
|
+
LEFT JOIN LATERAL (
|
|
8988
|
+
SELECT l.name
|
|
8989
|
+
FROM operations_project_cost_type_locale l
|
|
8990
|
+
WHERE l.operations_project_cost_type_id = pct.id
|
|
8991
|
+
ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC,
|
|
8992
|
+
l.id ASC
|
|
8993
|
+
LIMIT 1
|
|
8994
|
+
) pctl ON TRUE
|
|
8995
|
+
WHERE pc.deleted_at IS NULL
|
|
8996
|
+
AND pc.project_id = $2
|
|
8997
|
+
AND pc.status != 'cancelled'
|
|
8998
|
+
GROUP BY pc.cost_type_id, pct.slug, pct.code, pctl.name
|
|
8999
|
+
ORDER BY SUM(pc.amount * pc.quantity) DESC`, [localeId, projectId]);
|
|
9000
|
+
// ── 5. cost_by_month ──────────────────────────────────────────────────
|
|
9001
|
+
const costByMonth = await this.queryRows(`SELECT
|
|
9002
|
+
TO_CHAR(COALESCE(pc.cost_date, pc.created_at), 'YYYY-MM') AS month,
|
|
9003
|
+
SUM(pc.amount * pc.quantity)::text AS total,
|
|
9004
|
+
COUNT(*)::int AS count
|
|
9005
|
+
FROM operations_project_cost pc
|
|
9006
|
+
WHERE pc.deleted_at IS NULL
|
|
9007
|
+
AND pc.project_id = $1
|
|
9008
|
+
AND pc.status != 'cancelled'
|
|
9009
|
+
GROUP BY TO_CHAR(COALESCE(pc.cost_date, pc.created_at), 'YYYY-MM')
|
|
9010
|
+
ORDER BY month ASC`, [projectId]);
|
|
9011
|
+
// ── 6. top_cost_types (top 5) ─────────────────────────────────────────
|
|
9012
|
+
const topCostTypes = costByType.slice(0, 5).map((ct) => {
|
|
9013
|
+
const typeTotal = Math.round((parseFloat(ct.total) || 0) * 100) / 100;
|
|
9014
|
+
const percentage = extraCostTotal > 0
|
|
9015
|
+
? Math.round((typeTotal / extraCostTotal) * 10000) / 100
|
|
9016
|
+
: 0;
|
|
9017
|
+
return {
|
|
9018
|
+
cost_type_id: ct.costTypeId,
|
|
9019
|
+
cost_type_slug: ct.costTypeSlug,
|
|
9020
|
+
cost_type_name: ct.costTypeName,
|
|
9021
|
+
cost_type_code: ct.costTypeCode,
|
|
9022
|
+
total: typeTotal,
|
|
9023
|
+
percentage,
|
|
9024
|
+
};
|
|
9025
|
+
});
|
|
9026
|
+
return {
|
|
9027
|
+
project_id: projectId,
|
|
9028
|
+
budget_amount: budgetAmount,
|
|
9029
|
+
team_cost_total: teamCostTotal,
|
|
9030
|
+
extra_cost_total: extraCostTotal,
|
|
9031
|
+
total_project_cost: totalProjectCost,
|
|
9032
|
+
remaining_budget: remainingBudget,
|
|
9033
|
+
budget_usage_percent: budgetUsagePercent,
|
|
9034
|
+
planned_total: plannedTotal,
|
|
9035
|
+
approved_total: approvedTotal,
|
|
9036
|
+
realized_total: realizedTotal,
|
|
9037
|
+
cancelled_total: cancelledTotal,
|
|
9038
|
+
billable_total: billableTotal,
|
|
9039
|
+
non_billable_total: nonBillableTotal,
|
|
9040
|
+
reimbursable_total: reimbursableTotal,
|
|
9041
|
+
cost_by_category: costByCategory.map((c) => ({
|
|
9042
|
+
category_id: c.categoryId,
|
|
9043
|
+
category_slug: c.categorySlug,
|
|
9044
|
+
category_name: c.categoryName,
|
|
9045
|
+
category_color: c.categoryColor,
|
|
9046
|
+
category_icon: c.categoryIcon,
|
|
9047
|
+
total: Math.round((parseFloat(c.total) || 0) * 100) / 100,
|
|
9048
|
+
count: Number(c.count),
|
|
9049
|
+
})),
|
|
9050
|
+
cost_by_type: costByType.map((t) => ({
|
|
9051
|
+
cost_type_id: t.costTypeId,
|
|
9052
|
+
cost_type_slug: t.costTypeSlug,
|
|
9053
|
+
cost_type_name: t.costTypeName,
|
|
9054
|
+
cost_type_code: t.costTypeCode,
|
|
9055
|
+
total: Math.round((parseFloat(t.total) || 0) * 100) / 100,
|
|
9056
|
+
count: Number(t.count),
|
|
9057
|
+
})),
|
|
9058
|
+
cost_by_month: costByMonth.map((m) => ({
|
|
9059
|
+
month: m.month,
|
|
9060
|
+
total: Math.round((parseFloat(m.total) || 0) * 100) / 100,
|
|
9061
|
+
count: Number(m.count),
|
|
9062
|
+
})),
|
|
9063
|
+
top_cost_types: topCostTypes,
|
|
9064
|
+
};
|
|
9065
|
+
}
|
|
9066
|
+
async getProjectCostReport(userId, projectId, filters) {
|
|
9067
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
9068
|
+
await this.getActorContext(userId);
|
|
9069
|
+
const localeId = await this.resolvePreferredLocaleId();
|
|
9070
|
+
// ── Verify project ───────────────────────────────────────────────────
|
|
9071
|
+
const project = await this.querySingle(`SELECT id, budget_amount::text AS "budgetAmount"
|
|
9072
|
+
FROM operations_project
|
|
9073
|
+
WHERE id = $1 AND deleted_at IS NULL
|
|
9074
|
+
LIMIT 1`, [projectId]);
|
|
9075
|
+
if (!project) {
|
|
9076
|
+
throw new common_1.NotFoundException('Project not found.');
|
|
9077
|
+
}
|
|
9078
|
+
const budgetAmount = parseFloat((_a = project.budgetAmount) !== null && _a !== void 0 ? _a : '0') || 0;
|
|
9079
|
+
// ── Build dynamic WHERE clause ────────────────────────────────────────
|
|
9080
|
+
const conditions = [
|
|
9081
|
+
'pc.deleted_at IS NULL',
|
|
9082
|
+
'pc.project_id = $1',
|
|
9083
|
+
];
|
|
9084
|
+
const params = [projectId];
|
|
9085
|
+
if (filters.date_from) {
|
|
9086
|
+
params.push(filters.date_from);
|
|
9087
|
+
conditions.push(`COALESCE(pc.cost_date, pc.created_at::date) >= $${params.length}::date`);
|
|
9088
|
+
}
|
|
9089
|
+
if (filters.date_to) {
|
|
9090
|
+
params.push(filters.date_to);
|
|
9091
|
+
conditions.push(`COALESCE(pc.cost_date, pc.created_at::date) <= $${params.length}::date`);
|
|
9092
|
+
}
|
|
9093
|
+
if (filters.category_id !== undefined) {
|
|
9094
|
+
params.push(filters.category_id);
|
|
9095
|
+
conditions.push(`(pc.category_id = $${params.length} OR (pc.category_id IS NULL AND EXISTS (
|
|
9096
|
+
SELECT 1 FROM operations_project_cost_type pct2
|
|
9097
|
+
WHERE pct2.id = pc.cost_type_id AND pct2.category_id = $${params.length} AND pct2.deleted_at IS NULL
|
|
9098
|
+
)))`);
|
|
9099
|
+
}
|
|
9100
|
+
if (filters.cost_type_id !== undefined) {
|
|
9101
|
+
params.push(filters.cost_type_id);
|
|
9102
|
+
conditions.push(`pc.cost_type_id = $${params.length}`);
|
|
9103
|
+
}
|
|
9104
|
+
if (filters.status !== undefined) {
|
|
9105
|
+
params.push(filters.status);
|
|
9106
|
+
conditions.push(`pc.status = $${params.length}`);
|
|
9107
|
+
}
|
|
9108
|
+
if (filters.is_billable !== undefined) {
|
|
9109
|
+
params.push(filters.is_billable);
|
|
9110
|
+
conditions.push(`pc.is_billable = $${params.length}`);
|
|
9111
|
+
}
|
|
9112
|
+
if (filters.is_reimbursable !== undefined) {
|
|
9113
|
+
params.push(filters.is_reimbursable);
|
|
9114
|
+
conditions.push(`pc.is_reimbursable = $${params.length}`);
|
|
9115
|
+
}
|
|
9116
|
+
const whereClause = conditions.join(' AND ');
|
|
9117
|
+
// ── Totals ────────────────────────────────────────────────────────────
|
|
9118
|
+
const totals = await this.querySingle(`SELECT
|
|
9119
|
+
COALESCE(SUM(pc.amount * pc.quantity), 0)::text AS "grandTotal",
|
|
9120
|
+
COALESCE(SUM(CASE WHEN pc.status = 'planned' THEN pc.amount * pc.quantity ELSE 0 END), 0)::text AS "plannedTotal",
|
|
9121
|
+
COALESCE(SUM(CASE WHEN pc.status = 'approved' THEN pc.amount * pc.quantity ELSE 0 END), 0)::text AS "approvedTotal",
|
|
9122
|
+
COALESCE(SUM(CASE WHEN pc.status = 'realized' THEN pc.amount * pc.quantity ELSE 0 END), 0)::text AS "realizedTotal",
|
|
9123
|
+
COALESCE(SUM(CASE WHEN pc.status = 'cancelled' THEN pc.amount * pc.quantity ELSE 0 END), 0)::text AS "cancelledTotal",
|
|
9124
|
+
COALESCE(SUM(CASE WHEN pc.is_billable = true THEN pc.amount * pc.quantity ELSE 0 END), 0)::text AS "billableTotal",
|
|
9125
|
+
COALESCE(SUM(CASE WHEN pc.is_billable = false THEN pc.amount * pc.quantity ELSE 0 END), 0)::text AS "nonBillableTotal",
|
|
9126
|
+
COALESCE(SUM(CASE WHEN pc.is_reimbursable = true THEN pc.amount * pc.quantity ELSE 0 END), 0)::text AS "reimbursableTotal",
|
|
9127
|
+
COUNT(*)::int AS "totalCount"
|
|
9128
|
+
FROM operations_project_cost pc
|
|
9129
|
+
WHERE ${whereClause}`, params);
|
|
9130
|
+
const round2 = (v) => Math.round((parseFloat(v !== null && v !== void 0 ? v : '0') || 0) * 100) / 100;
|
|
9131
|
+
const grandTotal = round2(totals === null || totals === void 0 ? void 0 : totals.grandTotal);
|
|
9132
|
+
const plannedTotal = round2(totals === null || totals === void 0 ? void 0 : totals.plannedTotal);
|
|
9133
|
+
const approvedTotal = round2(totals === null || totals === void 0 ? void 0 : totals.approvedTotal);
|
|
9134
|
+
const realizedTotal = round2(totals === null || totals === void 0 ? void 0 : totals.realizedTotal);
|
|
9135
|
+
const cancelledTotal = round2(totals === null || totals === void 0 ? void 0 : totals.cancelledTotal);
|
|
9136
|
+
const billableTotal = round2(totals === null || totals === void 0 ? void 0 : totals.billableTotal);
|
|
9137
|
+
const nonBillableTotal = round2(totals === null || totals === void 0 ? void 0 : totals.nonBillableTotal);
|
|
9138
|
+
const reimbursableTotal = round2(totals === null || totals === void 0 ? void 0 : totals.reimbursableTotal);
|
|
9139
|
+
// ── By category ───────────────────────────────────────────────────────
|
|
9140
|
+
const costByCategory = await this.queryRows(`SELECT
|
|
9141
|
+
COALESCE(pc.category_id, pct.category_id) AS "categoryId",
|
|
9142
|
+
pcc.slug AS "categorySlug",
|
|
9143
|
+
COALESCE(pccl.name, pcc.slug) AS "categoryName",
|
|
9144
|
+
pcc.color AS "categoryColor",
|
|
9145
|
+
pcc.icon AS "categoryIcon",
|
|
9146
|
+
SUM(pc.amount * pc.quantity)::text AS total,
|
|
9147
|
+
COUNT(*)::int AS count,
|
|
9148
|
+
COALESCE(SUM(CASE WHEN pc.status='planned' THEN pc.amount*pc.quantity ELSE 0 END),0)::text AS "plannedSubtotal",
|
|
9149
|
+
COALESCE(SUM(CASE WHEN pc.status='realized' THEN pc.amount*pc.quantity ELSE 0 END),0)::text AS "realizedSubtotal"
|
|
9150
|
+
FROM operations_project_cost pc
|
|
9151
|
+
LEFT JOIN operations_project_cost_type pct
|
|
9152
|
+
ON pct.id = pc.cost_type_id AND pct.deleted_at IS NULL
|
|
9153
|
+
LEFT JOIN operations_project_cost_category pcc
|
|
9154
|
+
ON pcc.id = COALESCE(pc.category_id, pct.category_id) AND pcc.deleted_at IS NULL
|
|
9155
|
+
LEFT JOIN LATERAL (
|
|
9156
|
+
SELECT l.name
|
|
9157
|
+
FROM operations_project_cost_category_locale l
|
|
9158
|
+
WHERE l.operations_project_cost_category_id = pcc.id
|
|
9159
|
+
ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC, l.id ASC
|
|
9160
|
+
LIMIT 1
|
|
9161
|
+
) pccl ON TRUE
|
|
9162
|
+
WHERE ${whereClause.replace(/\$(\d+)/g, (m, n) => '$' + (Number(n) + 1))}
|
|
9163
|
+
GROUP BY COALESCE(pc.category_id, pct.category_id), pcc.slug, pcc.color, pcc.icon, pccl.name
|
|
9164
|
+
ORDER BY SUM(pc.amount * pc.quantity) DESC`, [localeId, ...params]);
|
|
9165
|
+
// ── By type ───────────────────────────────────────────────────────────
|
|
9166
|
+
const costByType = await this.queryRows(`SELECT
|
|
9167
|
+
pc.cost_type_id AS "costTypeId",
|
|
9168
|
+
pct.slug AS "costTypeSlug",
|
|
9169
|
+
COALESCE(pctl.name, pct.slug) AS "costTypeName",
|
|
9170
|
+
pct.code AS "costTypeCode",
|
|
9171
|
+
SUM(pc.amount * pc.quantity)::text AS total,
|
|
9172
|
+
COUNT(*)::int AS count,
|
|
9173
|
+
COALESCE(SUM(CASE WHEN pc.status='planned' THEN pc.amount*pc.quantity ELSE 0 END),0)::text AS "plannedSubtotal",
|
|
9174
|
+
COALESCE(SUM(CASE WHEN pc.status='realized' THEN pc.amount*pc.quantity ELSE 0 END),0)::text AS "realizedSubtotal"
|
|
9175
|
+
FROM operations_project_cost pc
|
|
9176
|
+
LEFT JOIN operations_project_cost_type pct
|
|
9177
|
+
ON pct.id = pc.cost_type_id AND pct.deleted_at IS NULL
|
|
9178
|
+
LEFT JOIN LATERAL (
|
|
9179
|
+
SELECT l.name
|
|
9180
|
+
FROM operations_project_cost_type_locale l
|
|
9181
|
+
WHERE l.operations_project_cost_type_id = pct.id
|
|
9182
|
+
ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC, l.id ASC
|
|
9183
|
+
LIMIT 1
|
|
9184
|
+
) pctl ON TRUE
|
|
9185
|
+
WHERE ${whereClause.replace(/\$(\d+)/g, (m, n) => '$' + (Number(n) + 1))}
|
|
9186
|
+
GROUP BY pc.cost_type_id, pct.slug, pct.code, pctl.name
|
|
9187
|
+
ORDER BY SUM(pc.amount * pc.quantity) DESC`, [localeId, ...params]);
|
|
9188
|
+
// ── By month ──────────────────────────────────────────────────────────
|
|
9189
|
+
const costByMonth = await this.queryRows(`SELECT
|
|
9190
|
+
TO_CHAR(COALESCE(pc.cost_date, pc.created_at::date), 'YYYY-MM') AS month,
|
|
9191
|
+
SUM(pc.amount * pc.quantity)::text AS total,
|
|
9192
|
+
COALESCE(SUM(CASE WHEN pc.status='planned' THEN pc.amount*pc.quantity ELSE 0 END),0)::text AS "plannedSubtotal",
|
|
9193
|
+
COALESCE(SUM(CASE WHEN pc.status='realized' THEN pc.amount*pc.quantity ELSE 0 END),0)::text AS "realizedSubtotal",
|
|
9194
|
+
COUNT(*)::int AS count
|
|
9195
|
+
FROM operations_project_cost pc
|
|
9196
|
+
WHERE ${whereClause}
|
|
9197
|
+
GROUP BY TO_CHAR(COALESCE(pc.cost_date, pc.created_at::date), 'YYYY-MM')
|
|
9198
|
+
ORDER BY month ASC`, params);
|
|
9199
|
+
// ── Top 5 individual costs ────────────────────────────────────────────
|
|
9200
|
+
const top5Costs = await this.queryRows(`SELECT
|
|
9201
|
+
pc.id,
|
|
9202
|
+
pc.description,
|
|
9203
|
+
pc.amount::text,
|
|
9204
|
+
pc.quantity::text,
|
|
9205
|
+
pc.status,
|
|
9206
|
+
pc.cost_date AS "costDate",
|
|
9207
|
+
COALESCE(pctl.name, pct.slug) AS "costTypeName",
|
|
9208
|
+
COALESCE(pccl.name, pcc.slug) AS "categoryName",
|
|
9209
|
+
pcc.color AS "categoryColor"
|
|
9210
|
+
FROM operations_project_cost pc
|
|
9211
|
+
LEFT JOIN operations_project_cost_type pct
|
|
9212
|
+
ON pct.id = pc.cost_type_id AND pct.deleted_at IS NULL
|
|
9213
|
+
LEFT JOIN LATERAL (
|
|
9214
|
+
SELECT l.name
|
|
9215
|
+
FROM operations_project_cost_type_locale l
|
|
9216
|
+
WHERE l.operations_project_cost_type_id = pct.id
|
|
9217
|
+
ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC, l.id ASC
|
|
9218
|
+
LIMIT 1
|
|
9219
|
+
) pctl ON TRUE
|
|
9220
|
+
LEFT JOIN operations_project_cost_category pcc
|
|
9221
|
+
ON pcc.id = COALESCE(pc.category_id, pct.category_id) AND pcc.deleted_at IS NULL
|
|
9222
|
+
LEFT JOIN LATERAL (
|
|
9223
|
+
SELECT l.name
|
|
9224
|
+
FROM operations_project_cost_category_locale l
|
|
9225
|
+
WHERE l.operations_project_cost_category_id = pcc.id
|
|
9226
|
+
ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC, l.id ASC
|
|
9227
|
+
LIMIT 1
|
|
9228
|
+
) pccl ON TRUE
|
|
9229
|
+
WHERE ${whereClause.replace(/\$(\d+)/g, (m, n) => '$' + (Number(n) + 1))}
|
|
9230
|
+
ORDER BY (pc.amount * pc.quantity) DESC
|
|
9231
|
+
LIMIT 5`, [localeId, ...params]);
|
|
9232
|
+
// ── Detailed list ─────────────────────────────────────────────────────
|
|
9233
|
+
const detailedList = await this.queryRows(`SELECT
|
|
9234
|
+
pc.id,
|
|
9235
|
+
pc.description,
|
|
9236
|
+
pc.amount::text,
|
|
9237
|
+
pc.quantity::text,
|
|
9238
|
+
pc.unit_amount::text AS "unitAmount",
|
|
9239
|
+
pc.currency,
|
|
9240
|
+
pc.calculation_type AS "calculationType",
|
|
9241
|
+
pc.recurrence_type AS "recurrenceType",
|
|
9242
|
+
pc.status,
|
|
9243
|
+
pc.is_billable AS "isBillable",
|
|
9244
|
+
pc.is_reimbursable AS "isReimbursable",
|
|
9245
|
+
pc.cost_date AS "costDate",
|
|
9246
|
+
pc.period_start AS "periodStart",
|
|
9247
|
+
pc.period_end AS "periodEnd",
|
|
9248
|
+
pc.notes,
|
|
9249
|
+
pc.cost_type_id AS "costTypeId",
|
|
9250
|
+
COALESCE(pctl.name, pct.slug) AS "costTypeName",
|
|
9251
|
+
pct.code AS "costTypeCode",
|
|
9252
|
+
COALESCE(pc.category_id, pct.category_id) AS "categoryId",
|
|
9253
|
+
COALESCE(pccl.name, pcc.slug) AS "categoryName",
|
|
9254
|
+
pcc.color AS "categoryColor",
|
|
9255
|
+
pc.created_at::text AS "createdAt"
|
|
9256
|
+
FROM operations_project_cost pc
|
|
9257
|
+
LEFT JOIN operations_project_cost_type pct
|
|
9258
|
+
ON pct.id = pc.cost_type_id AND pct.deleted_at IS NULL
|
|
9259
|
+
LEFT JOIN LATERAL (
|
|
9260
|
+
SELECT l.name
|
|
9261
|
+
FROM operations_project_cost_type_locale l
|
|
9262
|
+
WHERE l.operations_project_cost_type_id = pct.id
|
|
9263
|
+
ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC, l.id ASC
|
|
9264
|
+
LIMIT 1
|
|
9265
|
+
) pctl ON TRUE
|
|
9266
|
+
LEFT JOIN operations_project_cost_category pcc
|
|
9267
|
+
ON pcc.id = COALESCE(pc.category_id, pct.category_id) AND pcc.deleted_at IS NULL
|
|
9268
|
+
LEFT JOIN LATERAL (
|
|
9269
|
+
SELECT l.name
|
|
9270
|
+
FROM operations_project_cost_category_locale l
|
|
9271
|
+
WHERE l.operations_project_cost_category_id = pcc.id
|
|
9272
|
+
ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC, l.id ASC
|
|
9273
|
+
LIMIT 1
|
|
9274
|
+
) pccl ON TRUE
|
|
9275
|
+
WHERE ${whereClause.replace(/\$(\d+)/g, (m, n) => '$' + (Number(n) + 1))}
|
|
9276
|
+
ORDER BY (pc.amount * pc.quantity) DESC, pc.cost_date DESC NULLS LAST`, [localeId, ...params]);
|
|
9277
|
+
return {
|
|
9278
|
+
project_id: projectId,
|
|
9279
|
+
budget_amount: budgetAmount,
|
|
9280
|
+
filters_applied: {
|
|
9281
|
+
date_from: (_b = filters.date_from) !== null && _b !== void 0 ? _b : null,
|
|
9282
|
+
date_to: (_c = filters.date_to) !== null && _c !== void 0 ? _c : null,
|
|
9283
|
+
category_id: (_d = filters.category_id) !== null && _d !== void 0 ? _d : null,
|
|
9284
|
+
cost_type_id: (_e = filters.cost_type_id) !== null && _e !== void 0 ? _e : null,
|
|
9285
|
+
status: (_f = filters.status) !== null && _f !== void 0 ? _f : null,
|
|
9286
|
+
is_billable: (_g = filters.is_billable) !== null && _g !== void 0 ? _g : null,
|
|
9287
|
+
is_reimbursable: (_h = filters.is_reimbursable) !== null && _h !== void 0 ? _h : null,
|
|
9288
|
+
},
|
|
9289
|
+
totals: {
|
|
9290
|
+
grand_total: grandTotal,
|
|
9291
|
+
planned_total: plannedTotal,
|
|
9292
|
+
approved_total: approvedTotal,
|
|
9293
|
+
realized_total: realizedTotal,
|
|
9294
|
+
cancelled_total: cancelledTotal,
|
|
9295
|
+
billable_total: billableTotal,
|
|
9296
|
+
non_billable_total: nonBillableTotal,
|
|
9297
|
+
reimbursable_total: reimbursableTotal,
|
|
9298
|
+
total_count: Number((_j = totals === null || totals === void 0 ? void 0 : totals.totalCount) !== null && _j !== void 0 ? _j : 0),
|
|
9299
|
+
},
|
|
9300
|
+
cost_by_category: costByCategory.map((c) => ({
|
|
9301
|
+
category_id: c.categoryId,
|
|
9302
|
+
category_slug: c.categorySlug,
|
|
9303
|
+
category_name: c.categoryName,
|
|
9304
|
+
category_color: c.categoryColor,
|
|
9305
|
+
category_icon: c.categoryIcon,
|
|
9306
|
+
total: round2(c.total),
|
|
9307
|
+
count: Number(c.count),
|
|
9308
|
+
planned_subtotal: round2(c.plannedSubtotal),
|
|
9309
|
+
realized_subtotal: round2(c.realizedSubtotal),
|
|
9310
|
+
})),
|
|
9311
|
+
cost_by_type: costByType.map((t) => ({
|
|
9312
|
+
cost_type_id: t.costTypeId,
|
|
9313
|
+
cost_type_slug: t.costTypeSlug,
|
|
9314
|
+
cost_type_name: t.costTypeName,
|
|
9315
|
+
cost_type_code: t.costTypeCode,
|
|
9316
|
+
total: round2(t.total),
|
|
9317
|
+
count: Number(t.count),
|
|
9318
|
+
planned_subtotal: round2(t.plannedSubtotal),
|
|
9319
|
+
realized_subtotal: round2(t.realizedSubtotal),
|
|
9320
|
+
})),
|
|
9321
|
+
cost_by_month: costByMonth.map((m) => ({
|
|
9322
|
+
month: m.month,
|
|
9323
|
+
total: round2(m.total),
|
|
9324
|
+
planned_subtotal: round2(m.plannedSubtotal),
|
|
9325
|
+
realized_subtotal: round2(m.realizedSubtotal),
|
|
9326
|
+
count: Number(m.count),
|
|
9327
|
+
})),
|
|
9328
|
+
top_5_costs: top5Costs.map((c) => ({
|
|
9329
|
+
id: c.id,
|
|
9330
|
+
description: c.description,
|
|
9331
|
+
total: round2(String(parseFloat(c.amount) * parseFloat(c.quantity))),
|
|
9332
|
+
amount: round2(c.amount),
|
|
9333
|
+
quantity: parseFloat(c.quantity),
|
|
9334
|
+
status: c.status,
|
|
9335
|
+
cost_type_name: c.costTypeName,
|
|
9336
|
+
category_name: c.categoryName,
|
|
9337
|
+
category_color: c.categoryColor,
|
|
9338
|
+
cost_date: c.costDate,
|
|
9339
|
+
})),
|
|
9340
|
+
detailed_list: detailedList.map((c) => ({
|
|
9341
|
+
id: c.id,
|
|
9342
|
+
description: c.description,
|
|
9343
|
+
amount: round2(c.amount),
|
|
9344
|
+
quantity: parseFloat(c.quantity),
|
|
9345
|
+
unit_amount: c.unitAmount ? round2(c.unitAmount) : null,
|
|
9346
|
+
total: round2(String(parseFloat(c.amount) * parseFloat(c.quantity))),
|
|
9347
|
+
currency: c.currency,
|
|
9348
|
+
calculation_type: c.calculationType,
|
|
9349
|
+
recurrence_type: c.recurrenceType,
|
|
9350
|
+
status: c.status,
|
|
9351
|
+
is_billable: c.isBillable,
|
|
9352
|
+
is_reimbursable: c.isReimbursable,
|
|
9353
|
+
cost_date: c.costDate,
|
|
9354
|
+
period_start: c.periodStart,
|
|
9355
|
+
period_end: c.periodEnd,
|
|
9356
|
+
notes: c.notes,
|
|
9357
|
+
cost_type_id: c.costTypeId,
|
|
9358
|
+
cost_type_name: c.costTypeName,
|
|
9359
|
+
cost_type_code: c.costTypeCode,
|
|
9360
|
+
category_id: c.categoryId,
|
|
9361
|
+
category_name: c.categoryName,
|
|
9362
|
+
category_color: c.categoryColor,
|
|
9363
|
+
created_at: c.createdAt,
|
|
9364
|
+
})),
|
|
9365
|
+
};
|
|
9366
|
+
}
|
|
9367
|
+
async createProjectCost(userId, projectId, data) {
|
|
9368
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
|
|
9369
|
+
const actor = await this.getActorContext(userId);
|
|
9370
|
+
this.ensureSupervisor(actor);
|
|
9371
|
+
const project = await this.querySingle(`SELECT id FROM operations_project WHERE id = $1 AND deleted_at IS NULL LIMIT 1`, [projectId]);
|
|
9372
|
+
if (!project) {
|
|
9373
|
+
throw new common_1.NotFoundException('Project not found.');
|
|
9374
|
+
}
|
|
9375
|
+
if (data.cost_type_id) {
|
|
9376
|
+
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]);
|
|
9377
|
+
if (!costType) {
|
|
9378
|
+
throw new common_1.BadRequestException(`Cost type with id ${data.cost_type_id} not found.`);
|
|
9379
|
+
}
|
|
9380
|
+
}
|
|
9381
|
+
if (data.category_id) {
|
|
9382
|
+
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]);
|
|
9383
|
+
if (!category) {
|
|
9384
|
+
throw new common_1.BadRequestException(`Cost category with id ${data.category_id} not found.`);
|
|
9385
|
+
}
|
|
9386
|
+
}
|
|
9387
|
+
const calcType = (_a = data.calculation_type) !== null && _a !== void 0 ? _a : 'fixed';
|
|
9388
|
+
let effectiveAmount = data.amount;
|
|
9389
|
+
if (['unit', 'hourly', 'monthly'].includes(calcType) && data.unit_amount !== undefined && data.unit_amount !== null) {
|
|
9390
|
+
const qty = (_b = data.quantity) !== null && _b !== void 0 ? _b : 1;
|
|
9391
|
+
effectiveAmount = Math.round(qty * data.unit_amount * 100) / 100;
|
|
9392
|
+
}
|
|
9393
|
+
const created = await this.querySingle(`INSERT INTO operations_project_cost
|
|
9394
|
+
(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)
|
|
9395
|
+
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())
|
|
9396
|
+
RETURNING id`, [
|
|
9397
|
+
projectId,
|
|
9398
|
+
(_c = data.cost_type_id) !== null && _c !== void 0 ? _c : null,
|
|
9399
|
+
(_d = data.category_id) !== null && _d !== void 0 ? _d : null,
|
|
9400
|
+
(_e = data.description) !== null && _e !== void 0 ? _e : null,
|
|
9401
|
+
effectiveAmount,
|
|
9402
|
+
(_f = data.quantity) !== null && _f !== void 0 ? _f : 1,
|
|
9403
|
+
(_g = data.unit_amount) !== null && _g !== void 0 ? _g : null,
|
|
9404
|
+
(_h = data.currency) !== null && _h !== void 0 ? _h : 'BRL',
|
|
9405
|
+
(_j = data.cost_date) !== null && _j !== void 0 ? _j : null,
|
|
9406
|
+
(_k = data.period_start) !== null && _k !== void 0 ? _k : null,
|
|
9407
|
+
(_l = data.period_end) !== null && _l !== void 0 ? _l : null,
|
|
9408
|
+
calcType,
|
|
9409
|
+
(_m = data.recurrence_type) !== null && _m !== void 0 ? _m : 'none',
|
|
9410
|
+
(_o = data.is_billable) !== null && _o !== void 0 ? _o : false,
|
|
9411
|
+
(_p = data.is_reimbursable) !== null && _p !== void 0 ? _p : false,
|
|
9412
|
+
(_q = data.notes) !== null && _q !== void 0 ? _q : null,
|
|
9413
|
+
(_r = data.status) !== null && _r !== void 0 ? _r : 'planned',
|
|
9414
|
+
]);
|
|
9415
|
+
if (!(created === null || created === void 0 ? void 0 : created.id)) {
|
|
9416
|
+
throw new common_1.BadRequestException('Unable to create project cost.');
|
|
9417
|
+
}
|
|
9418
|
+
const rows = await this.listProjectCosts(userId, projectId, {});
|
|
9419
|
+
return (_s = rows.find((r) => r.id === created.id)) !== null && _s !== void 0 ? _s : null;
|
|
9420
|
+
}
|
|
9421
|
+
async updateProjectCost(userId, id, data) {
|
|
9422
|
+
var _a, _b, _c;
|
|
9423
|
+
const actor = await this.getActorContext(userId);
|
|
9424
|
+
this.ensureSupervisor(actor);
|
|
9425
|
+
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]);
|
|
9426
|
+
if (!cost) {
|
|
9427
|
+
throw new common_1.NotFoundException('Project cost not found.');
|
|
9428
|
+
}
|
|
9429
|
+
// Auto-calculate amount when applicable
|
|
9430
|
+
const effectiveCalcType = (_a = data.calculation_type) !== null && _a !== void 0 ? _a : cost.calculationType;
|
|
9431
|
+
if (['unit', 'hourly', 'monthly'].includes(effectiveCalcType)) {
|
|
9432
|
+
const ua = data.unit_amount !== undefined ? data.unit_amount : (cost.unitAmount !== null ? parseFloat(cost.unitAmount) : null);
|
|
9433
|
+
const qty = data.quantity !== undefined ? data.quantity : parseFloat(cost.quantity);
|
|
9434
|
+
if (ua !== null && ua !== undefined) {
|
|
9435
|
+
data = Object.assign(Object.assign({}, data), { amount: Math.round(qty * ua * 100) / 100 });
|
|
9436
|
+
}
|
|
9437
|
+
}
|
|
9438
|
+
const sets = [];
|
|
9439
|
+
const params = [];
|
|
9440
|
+
if (data.cost_type_id !== undefined)
|
|
9441
|
+
sets.push(`cost_type_id = ${this.param(params, data.cost_type_id)}`);
|
|
9442
|
+
if (data.category_id !== undefined)
|
|
9443
|
+
sets.push(`category_id = ${this.param(params, data.category_id)}`);
|
|
9444
|
+
if (data.description !== undefined)
|
|
9445
|
+
sets.push(`description = ${this.param(params, data.description)}`);
|
|
9446
|
+
if (data.amount !== undefined)
|
|
9447
|
+
sets.push(`amount = ${this.param(params, data.amount)}`);
|
|
9448
|
+
if (data.currency !== undefined)
|
|
9449
|
+
sets.push(`currency = ${this.param(params, data.currency)}`);
|
|
9450
|
+
if (data.quantity !== undefined)
|
|
9451
|
+
sets.push(`quantity = ${this.param(params, data.quantity)}`);
|
|
9452
|
+
if (data.unit_amount !== undefined)
|
|
9453
|
+
sets.push(`unit_amount = ${this.param(params, data.unit_amount)}`);
|
|
9454
|
+
if (data.calculation_type !== undefined)
|
|
9455
|
+
sets.push(`calculation_type = ${this.param(params, data.calculation_type)}::operations_project_cost_calculation_type_134cdfb49c_enum`);
|
|
9456
|
+
if (data.recurrence_type !== undefined)
|
|
9457
|
+
sets.push(`recurrence_type = ${this.param(params, data.recurrence_type)}::operations_project_cost_recurrence_type_09baf0f043_enum`);
|
|
9458
|
+
if (data.is_billable !== undefined)
|
|
9459
|
+
sets.push(`is_billable = ${this.param(params, data.is_billable)}`);
|
|
9460
|
+
if (data.is_reimbursable !== undefined)
|
|
9461
|
+
sets.push(`is_reimbursable = ${this.param(params, data.is_reimbursable)}`);
|
|
9462
|
+
if (data.cost_date !== undefined)
|
|
9463
|
+
sets.push(`cost_date = ${this.param(params, data.cost_date)}::date`);
|
|
9464
|
+
if (data.period_start !== undefined)
|
|
9465
|
+
sets.push(`period_start = ${this.param(params, data.period_start)}::date`);
|
|
9466
|
+
if (data.period_end !== undefined)
|
|
9467
|
+
sets.push(`period_end = ${this.param(params, data.period_end)}::date`);
|
|
9468
|
+
if (data.notes !== undefined)
|
|
9469
|
+
sets.push(`notes = ${this.param(params, data.notes)}`);
|
|
9470
|
+
if (data.status !== undefined)
|
|
9471
|
+
sets.push(`status = ${this.param(params, data.status)}::operations_project_cost_status_153e8592ce_enum`);
|
|
9472
|
+
if (sets.length === 0) {
|
|
9473
|
+
const rows = await this.listProjectCosts(userId, cost.projectId, {});
|
|
9474
|
+
return (_b = rows.find((r) => r.id === id)) !== null && _b !== void 0 ? _b : null;
|
|
9475
|
+
}
|
|
9476
|
+
sets.push(`updated_at = NOW()`);
|
|
9477
|
+
await this.prisma.$queryRawUnsafe(`UPDATE operations_project_cost SET ${sets.join(', ')} WHERE id = ${this.param(params, id)}`, ...params);
|
|
9478
|
+
const rows = await this.listProjectCosts(userId, cost.projectId, {});
|
|
9479
|
+
return (_c = rows.find((r) => r.id === id)) !== null && _c !== void 0 ? _c : null;
|
|
9480
|
+
}
|
|
9481
|
+
async deleteProjectCost(userId, id) {
|
|
9482
|
+
const actor = await this.getActorContext(userId);
|
|
9483
|
+
this.ensureSupervisor(actor);
|
|
9484
|
+
const cost = await this.querySingle(`SELECT id FROM operations_project_cost WHERE id = $1 AND deleted_at IS NULL LIMIT 1`, [id]);
|
|
9485
|
+
if (!cost) {
|
|
9486
|
+
throw new common_1.NotFoundException('Project cost not found.');
|
|
9487
|
+
}
|
|
9488
|
+
await this.prisma.$queryRawUnsafe(`UPDATE operations_project_cost SET deleted_at = NOW() WHERE id = $1`, id);
|
|
9489
|
+
return { success: true };
|
|
9490
|
+
}
|
|
7767
9491
|
};
|
|
7768
9492
|
exports.OperationsService = OperationsService;
|
|
7769
9493
|
exports.OperationsService = OperationsService = OperationsService_1 = __decorate([
|