@hed-hog/operations 0.0.322 → 0.0.325
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/controllers/operations-collaborators.controller.d.ts +9 -0
- package/dist/controllers/operations-collaborators.controller.d.ts.map +1 -1
- package/dist/controllers/operations-collaborators.controller.js +25 -0
- package/dist/controllers/operations-collaborators.controller.js.map +1 -1
- package/dist/controllers/operations-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 +562 -0
- package/dist/operations.service.d.ts.map +1 -1
- package/dist/operations.service.js +1657 -47
- package/dist/operations.service.js.map +1 -1
- package/hedhog/data/menu.yaml +52 -0
- package/hedhog/data/operations_project_cost_category.yaml +80 -0
- package/hedhog/data/operations_project_cost_type.yaml +503 -0
- package/hedhog/data/route.yaml +274 -0
- package/hedhog/frontend/app/_components/collaborator-costs-section.tsx.ejs +2 -18
- package/hedhog/frontend/app/_components/collaborator-form-screen.tsx.ejs +185 -276
- package/hedhog/frontend/app/_components/collaborator-tasks-tab.tsx.ejs +358 -0
- package/hedhog/frontend/app/_components/collaborator-timesheets-tab.tsx.ejs +242 -0
- package/hedhog/frontend/app/_components/my-project-summary-screen.tsx.ejs +167 -59
- package/hedhog/frontend/app/_components/person-select-with-create.tsx.ejs +1 -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 +2003 -1846
- package/hedhog/frontend/app/_components/project-form-screen.tsx.ejs +56 -11
- package/hedhog/frontend/app/_components/task-detail-sheet.tsx.ejs +297 -2
- package/hedhog/frontend/app/_components/task-form-sheet.tsx.ejs +530 -0
- package/hedhog/frontend/app/_lib/api.ts.ejs +247 -0
- package/hedhog/frontend/app/_lib/types.ts.ejs +196 -7
- package/hedhog/frontend/app/_lib/utils/format.ts.ejs +9 -3
- package/hedhog/frontend/app/collaborators/page.tsx.ejs +18 -7
- package/hedhog/frontend/app/my-tasks/page.tsx.ejs +219 -122
- 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 +192 -484
- package/hedhog/frontend/messages/en.json +279 -10
- package/hedhog/frontend/messages/en.json.ejs +2043 -0
- package/hedhog/frontend/messages/operations/en.json +2068 -0
- package/hedhog/frontend/messages/operations/operations/en.json +2102 -0
- package/hedhog/frontend/messages/operations/operations/pt.json +2111 -0
- package/hedhog/frontend/messages/operations/pt.json +2072 -0
- package/hedhog/frontend/messages/pt.json +284 -13
- package/hedhog/frontend/messages/pt.json.ejs +2056 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/async-options-combobox.d.ts +29 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/async-options-combobox.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/async-options-combobox.js +95 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/async-options-combobox.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/async-options-combobox.tsx +233 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-costs-section.d.ts +10 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-costs-section.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-costs-section.js +577 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-costs-section.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-costs-section.tsx +868 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-details-screen.d.ts +4 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-details-screen.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-details-screen.js +337 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-details-screen.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-details-screen.tsx +476 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-form-screen.d.ts +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-form-screen.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-form-screen.js +1348 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-form-screen.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-form-screen.tsx +2233 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-select-with-create.d.ts +12 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-select-with-create.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-select-with-create.js +162 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-select-with-create.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/collaborator-select-with-create.tsx +261 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-content-editor.d.ts +18 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-content-editor.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-content-editor.js +145 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-content-editor.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-content-editor.tsx +258 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-details-screen.d.ts +4 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-details-screen.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-details-screen.js +223 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-details-screen.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-details-screen.tsx +342 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-form-screen.d.ts +58 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-form-screen.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-form-screen.js +438 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-form-screen.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/contract-form-screen.tsx +698 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/department-select-with-create.d.ts +20 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/department-select-with-create.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/department-select-with-create.js +233 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/department-select-with-create.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/department-select-with-create.tsx +392 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/my-project-summary-screen.d.ts +4 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/my-project-summary-screen.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/my-project-summary-screen.js +814 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/my-project-summary-screen.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/my-project-summary-screen.tsx +1288 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/operations-calendar-view.d.ts +21 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/operations-calendar-view.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/operations-calendar-view.js +174 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/operations-calendar-view.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/operations-calendar-view.tsx +306 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/operations-header.d.ts +10 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/operations-header.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/operations-header.js +12 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/operations-header.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/operations-header.tsx +29 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/person-select-with-create.d.ts +15 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/person-select-with-create.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/person-select-with-create.js +501 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/person-select-with-create.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/person-select-with-create.tsx +853 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-costs-section.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-costs-section.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-costs-section.js +847 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-costs-section.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-costs-section.tsx +1340 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-details-screen.d.ts +4 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-details-screen.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-details-screen.js +2930 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-details-screen.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-details-screen.tsx +4378 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-form-screen.d.ts +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-form-screen.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-form-screen.js +1013 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-form-screen.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/project-form-screen.tsx +1745 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/section-card.d.ts +13 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/section-card.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/section-card.js +38 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/section-card.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/section-card.tsx +74 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/status-badge.d.ts +7 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/status-badge.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/status-badge.js +11 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/status-badge.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/status-badge.tsx +15 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/system-user-select-with-create.d.ts +18 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/system-user-select-with-create.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/system-user-select-with-create.js +406 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/system-user-select-with-create.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/system-user-select-with-create.tsx +660 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/task-detail-sheet.d.ts +26 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/task-detail-sheet.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/task-detail-sheet.js +332 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/task-detail-sheet.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/task-detail-sheet.tsx +518 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/task-file-attachments.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/task-file-attachments.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/task-file-attachments.js +255 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/task-file-attachments.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/task-file-attachments.tsx +388 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/timesheet-task-create-sheet.d.ts +10 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/timesheet-task-create-sheet.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/timesheet-task-create-sheet.js +131 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/timesheet-task-create-sheet.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_components/timesheet-task-create-sheet.tsx +214 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/api.d.ts +108 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/api.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/api.js +162 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/api.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/api.ts +428 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/hooks/use-operations-access.d.ts +8 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/hooks/use-operations-access.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/hooks/use-operations-access.js +36 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/hooks/use-operations-access.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/hooks/use-operations-access.ts +44 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/types.d.ts +836 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/types.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/types.js +3 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/types.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/types.ts +860 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/utils/format.d.ts +16 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/utils/format.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/utils/format.js +182 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/utils/format.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/utils/format.ts +250 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/utils/forms.d.ts +4 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/utils/forms.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/utils/forms.js +51 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/utils/forms.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/_lib/utils/forms.ts +61 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/approvals/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/approvals/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/approvals/page.js +954 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/approvals/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/approvals/page.tsx +1277 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborator-types/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborator-types/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborator-types/page.js +488 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborator-types/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborator-types/page.tsx +805 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/[id]/edit/page.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/[id]/edit/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/[id]/edit/page.js +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/[id]/edit/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/[id]/edit/page.tsx +11 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/[id]/page.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/[id]/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/[id]/page.js +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/[id]/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/[id]/page.tsx +11 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/new/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/new/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/new/page.js +8 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/new/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/new/page.tsx +5 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/page.js +612 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/collaborators/page.tsx +939 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/[id]/edit/page.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/[id]/edit/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/[id]/edit/page.js +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/[id]/edit/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/[id]/edit/page.tsx +11 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/[id]/page.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/[id]/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/[id]/page.js +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/[id]/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/[id]/page.tsx +11 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/new/page.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/new/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/new/page.js +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/new/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/new/page.tsx +17 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/page.js +348 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/contracts/page.tsx +536 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/departments/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/departments/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/departments/page.js +401 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/departments/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/departments/page.tsx +607 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/layout.d.ts +5 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/layout.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/layout.js +7 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/layout.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/layout.tsx +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-projects/[id]/page.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-projects/[id]/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-projects/[id]/page.js +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-projects/[id]/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-projects/[id]/page.tsx +11 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-projects/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-projects/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-projects/page.js +321 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-projects/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-projects/page.tsx +440 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-tasks/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-tasks/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-tasks/page.js +939 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-tasks/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/my-tasks/page.tsx +1499 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/async-options-combobox.d.ts +29 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/async-options-combobox.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/async-options-combobox.js +95 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/async-options-combobox.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/async-options-combobox.tsx +233 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-costs-section.d.ts +10 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-costs-section.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-costs-section.js +577 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-costs-section.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-costs-section.tsx +868 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-details-screen.d.ts +4 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-details-screen.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-details-screen.js +337 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-details-screen.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-details-screen.tsx +476 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-form-screen.d.ts +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-form-screen.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-form-screen.js +1348 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-form-screen.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-form-screen.tsx +2233 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-select-with-create.d.ts +12 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-select-with-create.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-select-with-create.js +162 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-select-with-create.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/collaborator-select-with-create.tsx +261 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-content-editor.d.ts +18 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-content-editor.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-content-editor.js +145 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-content-editor.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-content-editor.tsx +258 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-details-screen.d.ts +4 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-details-screen.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-details-screen.js +223 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-details-screen.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-details-screen.tsx +342 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-form-screen.d.ts +58 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-form-screen.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-form-screen.js +438 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-form-screen.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/contract-form-screen.tsx +698 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/department-select-with-create.d.ts +20 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/department-select-with-create.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/department-select-with-create.js +233 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/department-select-with-create.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/department-select-with-create.tsx +392 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/my-project-summary-screen.d.ts +4 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/my-project-summary-screen.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/my-project-summary-screen.js +814 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/my-project-summary-screen.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/my-project-summary-screen.tsx +1288 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/operations-calendar-view.d.ts +21 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/operations-calendar-view.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/operations-calendar-view.js +174 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/operations-calendar-view.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/operations-calendar-view.tsx +306 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/operations-header.d.ts +10 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/operations-header.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/operations-header.js +12 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/operations-header.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/operations-header.tsx +29 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/person-select-with-create.d.ts +15 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/person-select-with-create.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/person-select-with-create.js +501 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/person-select-with-create.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/person-select-with-create.tsx +853 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-cost-report-screen.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-cost-report-screen.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-cost-report-screen.js +459 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-cost-report-screen.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-cost-report-screen.tsx +598 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-costs-section.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-costs-section.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-costs-section.js +876 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-costs-section.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-costs-section.tsx +1368 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-details-screen.d.ts +4 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-details-screen.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-details-screen.js +2930 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-details-screen.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-details-screen.tsx +4378 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-form-screen.d.ts +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-form-screen.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-form-screen.js +1013 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-form-screen.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/project-form-screen.tsx +1745 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/section-card.d.ts +13 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/section-card.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/section-card.js +38 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/section-card.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/section-card.tsx +74 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/status-badge.d.ts +7 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/status-badge.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/status-badge.js +11 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/status-badge.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/status-badge.tsx +15 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/system-user-select-with-create.d.ts +18 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/system-user-select-with-create.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/system-user-select-with-create.js +406 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/system-user-select-with-create.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/system-user-select-with-create.tsx +660 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/task-detail-sheet.d.ts +26 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/task-detail-sheet.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/task-detail-sheet.js +332 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/task-detail-sheet.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/task-detail-sheet.tsx +518 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/task-file-attachments.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/task-file-attachments.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/task-file-attachments.js +255 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/task-file-attachments.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/task-file-attachments.tsx +388 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/timesheet-task-create-sheet.d.ts +10 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/timesheet-task-create-sheet.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/timesheet-task-create-sheet.js +131 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/timesheet-task-create-sheet.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_components/timesheet-task-create-sheet.tsx +214 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/api.d.ts +108 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/api.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/api.js +162 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/api.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/api.ts +428 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/hooks/use-operations-access.d.ts +8 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/hooks/use-operations-access.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/hooks/use-operations-access.js +36 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/hooks/use-operations-access.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/hooks/use-operations-access.ts +44 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/types.d.ts +836 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/types.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/types.js +3 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/types.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/types.ts +860 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/utils/format.d.ts +16 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/utils/format.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/utils/format.js +182 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/utils/format.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/utils/format.ts +250 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/utils/forms.d.ts +4 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/utils/forms.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/utils/forms.js +51 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/utils/forms.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/_lib/utils/forms.ts +61 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/approvals/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/approvals/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/approvals/page.js +954 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/approvals/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/approvals/page.tsx +1277 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborator-types/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborator-types/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborator-types/page.js +488 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborator-types/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborator-types/page.tsx +805 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/[id]/edit/page.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/[id]/edit/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/[id]/edit/page.js +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/[id]/edit/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/[id]/edit/page.tsx +11 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/[id]/page.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/[id]/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/[id]/page.js +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/[id]/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/[id]/page.tsx +11 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/new/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/new/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/new/page.js +8 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/new/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/new/page.tsx +5 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/page.js +612 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/collaborators/page.tsx +939 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/[id]/edit/page.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/[id]/edit/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/[id]/edit/page.js +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/[id]/edit/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/[id]/edit/page.tsx +11 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/[id]/page.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/[id]/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/[id]/page.js +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/[id]/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/[id]/page.tsx +11 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/new/page.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/new/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/new/page.js +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/new/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/new/page.tsx +17 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/page.js +348 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/contracts/page.tsx +536 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/departments/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/departments/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/departments/page.js +401 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/departments/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/departments/page.tsx +607 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/layout.d.ts +5 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/layout.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/layout.js +7 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/layout.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/layout.tsx +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-projects/[id]/page.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-projects/[id]/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-projects/[id]/page.js +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-projects/[id]/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-projects/[id]/page.tsx +11 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-projects/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-projects/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-projects/page.js +321 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-projects/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-projects/page.tsx +440 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-tasks/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-tasks/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-tasks/page.js +939 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-tasks/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/my-tasks/page.tsx +1499 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/page.js +8 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/page.tsx +5 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/project-cost-categories/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/project-cost-categories/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/project-cost-categories/page.js +436 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/project-cost-categories/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/project-cost-categories/page.tsx +675 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/project-cost-types/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/project-cost-types/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/project-cost-types/page.js +563 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/project-cost-types/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/project-cost-types/page.tsx +846 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/costs-report/page.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/costs-report/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/costs-report/page.js +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/costs-report/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/costs-report/page.tsx +10 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/edit/page.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/edit/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/edit/page.js +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/edit/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/edit/page.tsx +11 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/page.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/page.js +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/[id]/page.tsx +11 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/new/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/new/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/new/page.js +8 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/new/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/new/page.tsx +5 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/page.js +492 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/projects/page.tsx +757 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/reports/collaborators/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/reports/collaborators/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/reports/collaborators/page.js +342 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/reports/collaborators/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/reports/collaborators/page.tsx +430 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/reports/projects/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/reports/projects/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/reports/projects/page.js +338 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/reports/projects/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/reports/projects/page.tsx +428 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/schedule-adjustments/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/schedule-adjustments/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/schedule-adjustments/page.js +660 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/schedule-adjustments/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/schedule-adjustments/page.tsx +992 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/time-off/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/time-off/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/time-off/page.js +515 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/time-off/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/time-off/page.tsx +707 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/timesheets/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/timesheets/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/timesheets/page.js +1141 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/timesheets/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/operations/timesheets/page.tsx +1705 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/page.js +8 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/page.tsx +5 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/project-cost-categories/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/project-cost-categories/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/project-cost-categories/page.js +436 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/project-cost-categories/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/project-cost-categories/page.tsx +675 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/project-cost-types/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/project-cost-types/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/project-cost-types/page.js +563 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/project-cost-types/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/project-cost-types/page.tsx +846 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/[id]/edit/page.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/[id]/edit/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/[id]/edit/page.js +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/[id]/edit/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/[id]/edit/page.tsx +11 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/[id]/page.d.ts +6 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/[id]/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/[id]/page.js +9 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/[id]/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/[id]/page.tsx +11 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/new/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/new/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/new/page.js +8 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/new/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/new/page.tsx +5 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/page.js +492 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/projects/page.tsx +757 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/reports/collaborators/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/reports/collaborators/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/reports/collaborators/page.js +342 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/reports/collaborators/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/reports/collaborators/page.tsx +430 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/reports/projects/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/reports/projects/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/reports/projects/page.js +338 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/reports/projects/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/reports/projects/page.tsx +428 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/schedule-adjustments/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/schedule-adjustments/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/schedule-adjustments/page.js +660 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/schedule-adjustments/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/schedule-adjustments/page.tsx +992 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/time-off/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/time-off/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/time-off/page.js +515 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/time-off/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/time-off/page.tsx +707 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/timesheets/page.d.ts +2 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/timesheets/page.d.ts.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/timesheets/page.js +1141 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/timesheets/page.js.map +1 -0
- package/hedhog/frontend/src/app/(app)/(libraries)/operations/timesheets/page.tsx +1705 -0
- package/hedhog/table/operations_project_assignment.yaml +1 -0
- package/hedhog/table/operations_project_cost.yaml +93 -0
- package/hedhog/table/operations_project_cost_category.yaml +37 -0
- package/hedhog/table/operations_project_cost_type.yaml +55 -0
- package/hedhog/table/operations_task_comment.yaml +26 -0
- package/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 +2274 -39
|
@@ -1143,6 +1143,62 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
1143
1143
|
});
|
|
1144
1144
|
return this.getCollaboratorByIdForUser(userId, collaboratorId);
|
|
1145
1145
|
}
|
|
1146
|
+
async updateCollaboratorProjectAssignment(collaboratorId, projectId, data) {
|
|
1147
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1148
|
+
const sets = [];
|
|
1149
|
+
const params = [collaboratorId, projectId];
|
|
1150
|
+
let idx = 3;
|
|
1151
|
+
if ('projectRoleId' in data) {
|
|
1152
|
+
sets.push(`project_role_id = $${idx++}`);
|
|
1153
|
+
params.push((_a = data.projectRoleId) !== null && _a !== void 0 ? _a : null);
|
|
1154
|
+
}
|
|
1155
|
+
if ('roleLabel' in data) {
|
|
1156
|
+
sets.push(`role_label = $${idx++}`);
|
|
1157
|
+
params.push((_b = data.roleLabel) !== null && _b !== void 0 ? _b : null);
|
|
1158
|
+
}
|
|
1159
|
+
if ('allocationPercent' in data) {
|
|
1160
|
+
sets.push(`allocation_percent = $${idx++}`);
|
|
1161
|
+
params.push((_c = data.allocationPercent) !== null && _c !== void 0 ? _c : null);
|
|
1162
|
+
}
|
|
1163
|
+
if ('weeklyHours' in data) {
|
|
1164
|
+
sets.push(`weekly_hours = $${idx++}`);
|
|
1165
|
+
params.push((_d = data.weeklyHours) !== null && _d !== void 0 ? _d : null);
|
|
1166
|
+
}
|
|
1167
|
+
if ('startDate' in data) {
|
|
1168
|
+
sets.push(`start_date = $${idx++}::date`);
|
|
1169
|
+
params.push((_e = data.startDate) !== null && _e !== void 0 ? _e : null);
|
|
1170
|
+
}
|
|
1171
|
+
if ('endDate' in data) {
|
|
1172
|
+
sets.push(`end_date = $${idx++}::date`);
|
|
1173
|
+
params.push((_f = data.endDate) !== null && _f !== void 0 ? _f : null);
|
|
1174
|
+
}
|
|
1175
|
+
if ('status' in data) {
|
|
1176
|
+
sets.push(`status = $${idx++}::operations_project_assignment_status_155b459bbf_enum`);
|
|
1177
|
+
params.push(data.status);
|
|
1178
|
+
}
|
|
1179
|
+
if (!sets.length)
|
|
1180
|
+
return { updated: false };
|
|
1181
|
+
sets.push(`updated_at = NOW()`);
|
|
1182
|
+
await this.prisma.$executeRawUnsafe(`UPDATE operations_project_assignment
|
|
1183
|
+
SET ${sets.join(', ')}
|
|
1184
|
+
WHERE collaborator_id = $1
|
|
1185
|
+
AND project_id = $2
|
|
1186
|
+
AND deleted_at IS NULL`, ...params);
|
|
1187
|
+
return { updated: true };
|
|
1188
|
+
}
|
|
1189
|
+
async addCollaboratorProjectAssignment(collaboratorId, data) {
|
|
1190
|
+
var _a;
|
|
1191
|
+
const existing = await this.querySingle(`SELECT id FROM operations_project_assignment
|
|
1192
|
+
WHERE collaborator_id = $1 AND project_id = $2 AND deleted_at IS NULL`, [collaboratorId, data.projectId]);
|
|
1193
|
+
if (existing) {
|
|
1194
|
+
return { id: existing.id, created: false };
|
|
1195
|
+
}
|
|
1196
|
+
const row = await this.querySingle(`INSERT INTO operations_project_assignment
|
|
1197
|
+
(collaborator_id, project_id, role_label, status)
|
|
1198
|
+
VALUES ($1, $2, $3, 'active')
|
|
1199
|
+
RETURNING id`, [collaboratorId, data.projectId, (_a = data.roleLabel) !== null && _a !== void 0 ? _a : '']);
|
|
1200
|
+
return { id: row.id, created: true };
|
|
1201
|
+
}
|
|
1146
1202
|
async getCollaboratorCompensationHistory(userId, collaboratorId) {
|
|
1147
1203
|
const actor = await this.getActorContext(userId);
|
|
1148
1204
|
this.ensureDirector(actor);
|
|
@@ -1589,6 +1645,10 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
1589
1645
|
if (paginationParams.status) {
|
|
1590
1646
|
filters.push(`t.status::text = ${this.param(params, paginationParams.status)}`);
|
|
1591
1647
|
}
|
|
1648
|
+
if (paginationParams.collaboratorId) {
|
|
1649
|
+
const colId = paginationParams.collaboratorId;
|
|
1650
|
+
filters.push(`(pa.collaborator_id = ${this.param(params, colId)} OR t.assignee_collaborator_id = ${this.param(params, colId)})`);
|
|
1651
|
+
}
|
|
1592
1652
|
const whereClause = filters.join(' AND ');
|
|
1593
1653
|
const totalRow = await this.querySingle(`SELECT COUNT(*)::text AS total
|
|
1594
1654
|
FROM operations_task t
|
|
@@ -1621,6 +1681,8 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
1621
1681
|
ac.display_name AS "assigneeName",
|
|
1622
1682
|
au.photo_id AS "assigneeUserPhotoId",
|
|
1623
1683
|
ap.avatar_id AS "assigneePersonAvatarId",
|
|
1684
|
+
COALESCE(task_comments.count, 0)::int AS "commentCount",
|
|
1685
|
+
COALESCE(task_files.count, 0)::int AS "fileCount",
|
|
1624
1686
|
t.created_at AS "createdAt",
|
|
1625
1687
|
t.deleted_at AS "deletedAt"
|
|
1626
1688
|
FROM operations_task t
|
|
@@ -1633,6 +1695,16 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
1633
1695
|
ON au.id = ac.user_id
|
|
1634
1696
|
LEFT JOIN person ap
|
|
1635
1697
|
ON ap.id = ac.person_id
|
|
1698
|
+
LEFT JOIN LATERAL (
|
|
1699
|
+
SELECT COUNT(*) AS count
|
|
1700
|
+
FROM operations_task_comment tc
|
|
1701
|
+
WHERE tc.task_id = t.id
|
|
1702
|
+
) task_comments ON TRUE
|
|
1703
|
+
LEFT JOIN LATERAL (
|
|
1704
|
+
SELECT COUNT(*) AS count
|
|
1705
|
+
FROM operations_task_file tf
|
|
1706
|
+
WHERE tf.operations_task_id = t.id
|
|
1707
|
+
) task_files ON TRUE
|
|
1636
1708
|
JOIN operations_project p
|
|
1637
1709
|
ON p.id = COALESCE(t.project_id, pa.project_id)
|
|
1638
1710
|
WHERE ${whereClause}
|
|
@@ -1844,6 +1916,109 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
1844
1916
|
}
|
|
1845
1917
|
return { success: true };
|
|
1846
1918
|
}
|
|
1919
|
+
async listTaskComments(userId, taskId) {
|
|
1920
|
+
const actor = await this.getActorContext(userId);
|
|
1921
|
+
if (!actor.isCollaborator && !actor.isDirector && !actor.isSupervisor) {
|
|
1922
|
+
throw new common_1.ForbiddenException('Operations collaborator access is required.');
|
|
1923
|
+
}
|
|
1924
|
+
const current = await this.getTaskRecordForActor(this.prisma, actor, taskId);
|
|
1925
|
+
await this.assertProjectAccess(actor, current.projectId);
|
|
1926
|
+
return this.queryRows(`SELECT tc.id,
|
|
1927
|
+
tc.task_id AS "taskId",
|
|
1928
|
+
tc.content,
|
|
1929
|
+
tc.actor_collaborator_id AS "actorCollaboratorId",
|
|
1930
|
+
actor.display_name AS "actorName",
|
|
1931
|
+
actor_user.photo_id AS "actorUserPhotoId",
|
|
1932
|
+
actor_person.avatar_id AS "actorPersonAvatarId",
|
|
1933
|
+
tc.created_at AS "createdAt",
|
|
1934
|
+
tc.updated_at AS "updatedAt"
|
|
1935
|
+
FROM operations_task_comment tc
|
|
1936
|
+
LEFT JOIN operations_collaborator actor
|
|
1937
|
+
ON actor.id = tc.actor_collaborator_id
|
|
1938
|
+
AND actor.deleted_at IS NULL
|
|
1939
|
+
LEFT JOIN "user" actor_user
|
|
1940
|
+
ON actor_user.id = actor.user_id
|
|
1941
|
+
LEFT JOIN person actor_person
|
|
1942
|
+
ON actor_person.id = actor.person_id
|
|
1943
|
+
WHERE tc.task_id = $1
|
|
1944
|
+
ORDER BY tc.created_at ASC, tc.id ASC`, [taskId]);
|
|
1945
|
+
}
|
|
1946
|
+
async addTaskComment(userId, taskId, content) {
|
|
1947
|
+
var _a, _b;
|
|
1948
|
+
const actor = await this.getActorContext(userId);
|
|
1949
|
+
if (!actor.isCollaborator && !actor.isDirector && !actor.isSupervisor) {
|
|
1950
|
+
throw new common_1.ForbiddenException('Operations collaborator access is required.');
|
|
1951
|
+
}
|
|
1952
|
+
const current = await this.getTaskRecordForActor(this.prisma, actor, taskId);
|
|
1953
|
+
await this.assertProjectAccess(actor, current.projectId);
|
|
1954
|
+
const normalizedContent = this.normalizeOptionalText(content);
|
|
1955
|
+
if (!normalizedContent) {
|
|
1956
|
+
throw new common_1.BadRequestException('Comment content is required.');
|
|
1957
|
+
}
|
|
1958
|
+
const inserted = await this.queryRows(`INSERT INTO operations_task_comment (
|
|
1959
|
+
task_id,
|
|
1960
|
+
actor_collaborator_id,
|
|
1961
|
+
content,
|
|
1962
|
+
created_at,
|
|
1963
|
+
updated_at
|
|
1964
|
+
) VALUES ($1, $2, $3, NOW(), NOW())
|
|
1965
|
+
RETURNING id`, [taskId, (_a = actor.collaboratorId) !== null && _a !== void 0 ? _a : null, normalizedContent]);
|
|
1966
|
+
const commentId = (_b = inserted[0]) === null || _b === void 0 ? void 0 : _b.id;
|
|
1967
|
+
const comments = await this.listTaskComments(userId, taskId);
|
|
1968
|
+
const createdComment = comments.find((comment) => comment.id === commentId);
|
|
1969
|
+
if (!createdComment) {
|
|
1970
|
+
throw new common_1.NotFoundException('Task comment could not be loaded.');
|
|
1971
|
+
}
|
|
1972
|
+
return createdComment;
|
|
1973
|
+
}
|
|
1974
|
+
async updateTaskComment(userId, taskId, commentId, content) {
|
|
1975
|
+
var _a;
|
|
1976
|
+
const actor = await this.getActorContext(userId);
|
|
1977
|
+
if (!actor.isCollaborator && !actor.isDirector && !actor.isSupervisor) {
|
|
1978
|
+
throw new common_1.ForbiddenException('Operations collaborator access is required.');
|
|
1979
|
+
}
|
|
1980
|
+
const current = await this.getTaskRecordForActor(this.prisma, actor, taskId);
|
|
1981
|
+
await this.assertProjectAccess(actor, current.projectId);
|
|
1982
|
+
const normalizedContent = this.normalizeOptionalText(content);
|
|
1983
|
+
if (!normalizedContent) {
|
|
1984
|
+
throw new common_1.BadRequestException('Comment content is required.');
|
|
1985
|
+
}
|
|
1986
|
+
const rows = await this.queryRows(`SELECT id, actor_collaborator_id AS "actorCollaboratorId"
|
|
1987
|
+
FROM operations_task_comment
|
|
1988
|
+
WHERE id = $1 AND task_id = $2`, [commentId, taskId]);
|
|
1989
|
+
const row = rows[0];
|
|
1990
|
+
if (!row) {
|
|
1991
|
+
throw new common_1.NotFoundException('Comment not found.');
|
|
1992
|
+
}
|
|
1993
|
+
if (row.actorCollaboratorId !== actor.collaboratorId) {
|
|
1994
|
+
throw new common_1.ForbiddenException('You can only edit your own comments.');
|
|
1995
|
+
}
|
|
1996
|
+
await this.queryRows(`UPDATE operations_task_comment
|
|
1997
|
+
SET content = $1, updated_at = NOW()
|
|
1998
|
+
WHERE id = $2`, [normalizedContent, commentId]);
|
|
1999
|
+
const comments = await this.listTaskComments(userId, taskId);
|
|
2000
|
+
return (_a = comments.find((c) => c.id === commentId)) !== null && _a !== void 0 ? _a : null;
|
|
2001
|
+
}
|
|
2002
|
+
async removeTaskComment(userId, taskId, commentId) {
|
|
2003
|
+
const actor = await this.getActorContext(userId);
|
|
2004
|
+
if (!actor.isCollaborator && !actor.isDirector && !actor.isSupervisor) {
|
|
2005
|
+
throw new common_1.ForbiddenException('Operations collaborator access is required.');
|
|
2006
|
+
}
|
|
2007
|
+
const current = await this.getTaskRecordForActor(this.prisma, actor, taskId);
|
|
2008
|
+
await this.assertProjectAccess(actor, current.projectId);
|
|
2009
|
+
const rows = await this.queryRows(`SELECT id, actor_collaborator_id AS "actorCollaboratorId"
|
|
2010
|
+
FROM operations_task_comment
|
|
2011
|
+
WHERE id = $1 AND task_id = $2`, [commentId, taskId]);
|
|
2012
|
+
const row = rows[0];
|
|
2013
|
+
if (!row) {
|
|
2014
|
+
throw new common_1.NotFoundException('Comment not found.');
|
|
2015
|
+
}
|
|
2016
|
+
if (row.actorCollaboratorId !== actor.collaboratorId) {
|
|
2017
|
+
throw new common_1.ForbiddenException('You can only delete your own comments.');
|
|
2018
|
+
}
|
|
2019
|
+
await this.queryRows(`DELETE FROM operations_task_comment WHERE id = $1`, [commentId]);
|
|
2020
|
+
return { success: true };
|
|
2021
|
+
}
|
|
1847
2022
|
async listTimesheetEntries(userId, paginationParams) {
|
|
1848
2023
|
var _a, _b;
|
|
1849
2024
|
const actor = await this.getActorContext(userId);
|
|
@@ -3055,6 +3230,9 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
3055
3230
|
if (filters.dateTo) {
|
|
3056
3231
|
where.push(`t.week_start_date <= ${this.param(params, filters.dateTo)}::date`);
|
|
3057
3232
|
}
|
|
3233
|
+
if (filters.collaboratorId) {
|
|
3234
|
+
where.push(`t.collaborator_id = ${this.param(params, filters.collaboratorId)}`);
|
|
3235
|
+
}
|
|
3058
3236
|
if (pagination === null || pagination === void 0 ? void 0 : pagination.search) {
|
|
3059
3237
|
const searchPlaceholder = this.param(params, `%${pagination.search}%`);
|
|
3060
3238
|
where.push(`(
|
|
@@ -3088,7 +3266,8 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
3088
3266
|
t.submitted_at AS "submittedAt",
|
|
3089
3267
|
t.reviewed_at AS "reviewedAt",
|
|
3090
3268
|
t.notes,
|
|
3091
|
-
approval.decision_note AS "decisionNote"
|
|
3269
|
+
approval.decision_note AS "decisionNote",
|
|
3270
|
+
approval.id AS "approvalId"
|
|
3092
3271
|
FROM operations_timesheet t
|
|
3093
3272
|
JOIN operations_collaborator c ON c.id = t.collaborator_id
|
|
3094
3273
|
LEFT JOIN operations_collaborator a ON a.id = t.approver_collaborator_id
|
|
@@ -4882,6 +5061,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
4882
5061
|
au.photo_id AS "assigneeUserPhotoId",
|
|
4883
5062
|
ap.avatar_id AS "assigneePersonAvatarId",
|
|
4884
5063
|
t.project_assignment_id AS "projectAssignmentId",
|
|
5064
|
+
COALESCE(task_comments.count, 0)::int AS "commentCount",
|
|
4885
5065
|
t.created_at AS "createdAt"
|
|
4886
5066
|
FROM operations_task t
|
|
4887
5067
|
LEFT JOIN operations_collaborator ac
|
|
@@ -4890,6 +5070,11 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
4890
5070
|
ON au.id = ac.user_id
|
|
4891
5071
|
LEFT JOIN person ap
|
|
4892
5072
|
ON ap.id = ac.person_id
|
|
5073
|
+
LEFT JOIN LATERAL (
|
|
5074
|
+
SELECT COUNT(*) AS count
|
|
5075
|
+
FROM operations_task_comment tc
|
|
5076
|
+
WHERE tc.task_id = t.id
|
|
5077
|
+
) task_comments ON TRUE
|
|
4893
5078
|
WHERE COALESCE(t.project_id, (
|
|
4894
5079
|
SELECT pa.project_id FROM operations_project_assignment pa
|
|
4895
5080
|
WHERE pa.id = t.project_assignment_id AND pa.deleted_at IS NULL
|
|
@@ -4916,6 +5101,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
4916
5101
|
ap.avatar_id AS "assigneePersonAvatarId",
|
|
4917
5102
|
t.project_assignment_id AS "projectAssignmentId",
|
|
4918
5103
|
COALESCE(t.project_id, pa.project_id) AS "projectId",
|
|
5104
|
+
COALESCE(task_comments.count, 0)::int AS "commentCount",
|
|
4919
5105
|
t.created_at AS "createdAt",
|
|
4920
5106
|
t.deleted_at AS "deletedAt"
|
|
4921
5107
|
FROM operations_task t
|
|
@@ -4925,6 +5111,11 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
4925
5111
|
LEFT JOIN person ap ON ap.id = ac.person_id
|
|
4926
5112
|
LEFT JOIN operations_project_assignment pa
|
|
4927
5113
|
ON pa.id = t.project_assignment_id AND pa.deleted_at IS NULL
|
|
5114
|
+
LEFT JOIN LATERAL (
|
|
5115
|
+
SELECT COUNT(*) AS count
|
|
5116
|
+
FROM operations_task_comment tc
|
|
5117
|
+
WHERE tc.task_id = t.id
|
|
5118
|
+
) task_comments ON TRUE
|
|
4928
5119
|
WHERE t.id = $1`, [taskId]);
|
|
4929
5120
|
return (_a = rows[0]) !== null && _a !== void 0 ? _a : null;
|
|
4930
5121
|
}
|
|
@@ -6919,12 +7110,18 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
6919
7110
|
au.photo_id AS "assigneeUserPhotoId",
|
|
6920
7111
|
ap.avatar_id AS "assigneePersonAvatarId",
|
|
6921
7112
|
t.project_assignment_id AS "projectAssignmentId",
|
|
7113
|
+
COALESCE(task_comments.count, 0)::int AS "commentCount",
|
|
6922
7114
|
t.created_at AS "createdAt"
|
|
6923
7115
|
FROM operations_task t
|
|
6924
7116
|
LEFT JOIN operations_collaborator ac
|
|
6925
7117
|
ON ac.id = t.assignee_collaborator_id AND ac.deleted_at IS NULL
|
|
6926
7118
|
LEFT JOIN "user" au ON au.id = ac.user_id
|
|
6927
7119
|
LEFT JOIN person ap ON ap.id = ac.person_id
|
|
7120
|
+
LEFT JOIN LATERAL (
|
|
7121
|
+
SELECT COUNT(*) AS count
|
|
7122
|
+
FROM operations_task_comment tc
|
|
7123
|
+
WHERE tc.task_id = t.id
|
|
7124
|
+
) task_comments ON TRUE
|
|
6928
7125
|
WHERE COALESCE(t.project_id, (
|
|
6929
7126
|
SELECT pa.project_id FROM operations_project_assignment pa
|
|
6930
7127
|
WHERE pa.id = t.project_assignment_id AND pa.deleted_at IS NULL
|
|
@@ -6964,7 +7161,9 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
6964
7161
|
)`,
|
|
6965
7162
|
];
|
|
6966
7163
|
if (actor.collaboratorId) {
|
|
6967
|
-
|
|
7164
|
+
const p1 = this.param(params, actor.collaboratorId);
|
|
7165
|
+
const p2 = this.param(params, actor.collaboratorId);
|
|
7166
|
+
filters.push(`(pa.collaborator_id = ${p1} OR t.assignee_collaborator_id = ${p2})`);
|
|
6968
7167
|
}
|
|
6969
7168
|
if (pagination.search) {
|
|
6970
7169
|
const searchPlaceholder = this.param(params, `%${pagination.search}%`);
|
|
@@ -7010,6 +7209,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
7010
7209
|
ac.display_name AS "assigneeName",
|
|
7011
7210
|
au.photo_id AS "assigneeUserPhotoId",
|
|
7012
7211
|
ap.avatar_id AS "assigneePersonAvatarId",
|
|
7212
|
+
COALESCE(task_comments.count, 0)::int AS "commentCount",
|
|
7013
7213
|
t.created_at AS "createdAt",
|
|
7014
7214
|
t.deleted_at AS "deletedAt"
|
|
7015
7215
|
FROM operations_task t
|
|
@@ -7022,6 +7222,11 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
7022
7222
|
ON au.id = ac.user_id
|
|
7023
7223
|
LEFT JOIN person ap
|
|
7024
7224
|
ON ap.id = ac.person_id
|
|
7225
|
+
LEFT JOIN LATERAL (
|
|
7226
|
+
SELECT COUNT(*) AS count
|
|
7227
|
+
FROM operations_task_comment tc
|
|
7228
|
+
WHERE tc.task_id = t.id
|
|
7229
|
+
) task_comments ON TRUE
|
|
7025
7230
|
JOIN operations_project p
|
|
7026
7231
|
ON p.id = COALESCE(t.project_id, pa.project_id)
|
|
7027
7232
|
WHERE ${whereClause}
|
|
@@ -7173,7 +7378,12 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
7173
7378
|
: scenario === 'conservative'
|
|
7174
7379
|
? { revenue: 0.9, cost: 0.96, backlog: 0.82 }
|
|
7175
7380
|
: { revenue: 1, cost: 1, backlog: 1 };
|
|
7176
|
-
const
|
|
7381
|
+
const fromDate = new Date(`${from}T00:00:00`);
|
|
7382
|
+
const toDate = new Date(`${to}T00:00:00`);
|
|
7383
|
+
const periodDays = Math.max(1, Math.floor((toDate.getTime() - fromDate.getTime()) / 86400000) + 1);
|
|
7384
|
+
const periodWeeks = Math.max(1, Math.ceil(periodDays / 7));
|
|
7385
|
+
const periodMonths = periodDays / 30.4375;
|
|
7386
|
+
const params = [from, to, periodMonths, periodWeeks];
|
|
7177
7387
|
const where = [
|
|
7178
7388
|
'p.deleted_at IS NULL',
|
|
7179
7389
|
'(p.end_date IS NULL OR p.end_date >= $1::date)',
|
|
@@ -7196,6 +7406,8 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
7196
7406
|
COALESCE(assignment_stats.weekly_hours, 0)::text AS "weeklyHours",
|
|
7197
7407
|
COALESCE(time_stats.actual_hours, 0)::text AS "actualHours",
|
|
7198
7408
|
COALESCE(time_stats.billable_hours, 0)::text AS "billableHours",
|
|
7409
|
+
COALESCE(cost_stats.realized_cost, 0)::text AS "realizedCost",
|
|
7410
|
+
COALESCE(alloc_cost_stats.allocated_cost, 0)::text AS "allocatedCost",
|
|
7199
7411
|
COALESCE(task_stats.open_tasks, 0)::text AS "openTasks",
|
|
7200
7412
|
COALESCE(task_stats.backlog_hours, 0)::text AS "backlogHours",
|
|
7201
7413
|
COALESCE(task_stats.future_deliveries, 0)::text AS "futureDeliveries"
|
|
@@ -7224,6 +7436,146 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
7224
7436
|
AND entry.deleted_at IS NULL
|
|
7225
7437
|
AND entry.work_date BETWEEN $1::date AND $2::date
|
|
7226
7438
|
) time_stats ON TRUE
|
|
7439
|
+
LEFT JOIN LATERAL (
|
|
7440
|
+
SELECT COALESCE(
|
|
7441
|
+
SUM(
|
|
7442
|
+
entry.hours
|
|
7443
|
+
* (
|
|
7444
|
+
(
|
|
7445
|
+
COALESCE(collaborator_costs.salary_cost, 0)
|
|
7446
|
+
+ COALESCE(collaborator_costs.benefits_cost, 0)
|
|
7447
|
+
+ COALESCE(collaborator_costs.taxes_cost, 0)
|
|
7448
|
+
+ COALESCE(collaborator_costs.tools_cost, 0)
|
|
7449
|
+
)
|
|
7450
|
+
* $3::numeric
|
|
7451
|
+
/ GREATEST(
|
|
7452
|
+
COALESCE(collaborator_record.weekly_capacity_hours, 40)::numeric * $4::numeric,
|
|
7453
|
+
COALESCE(collaborator_hours.total_hours, 0),
|
|
7454
|
+
1
|
|
7455
|
+
)
|
|
7456
|
+
)
|
|
7457
|
+
),
|
|
7458
|
+
0
|
|
7459
|
+
) AS realized_cost
|
|
7460
|
+
FROM operations_timesheet_entry entry
|
|
7461
|
+
JOIN operations_project_assignment pa
|
|
7462
|
+
ON pa.id = entry.project_assignment_id
|
|
7463
|
+
AND pa.deleted_at IS NULL
|
|
7464
|
+
JOIN operations_collaborator collaborator_record
|
|
7465
|
+
ON collaborator_record.id = pa.collaborator_id
|
|
7466
|
+
AND collaborator_record.deleted_at IS NULL
|
|
7467
|
+
LEFT JOIN LATERAL (
|
|
7468
|
+
SELECT COALESCE(NULLIF(cost_totals.salary_cost, 0), compensation_history.amount, hiring_contract.budget_amount, 0) AS salary_cost,
|
|
7469
|
+
cost_totals.benefits_cost,
|
|
7470
|
+
cost_totals.taxes_cost,
|
|
7471
|
+
cost_totals.tools_cost
|
|
7472
|
+
FROM (
|
|
7473
|
+
SELECT COALESCE(SUM(cost.amount) FILTER (WHERE cost.recurrence::text = 'monthly' AND cost_type.slug IN ('salario-base', 'pro-labore')), 0) AS salary_cost,
|
|
7474
|
+
COALESCE(SUM(cost.amount) FILTER (WHERE cost.recurrence::text = 'monthly' AND cost_type.slug IN ('vale-refeicao', 'vale-alimentacao', 'vale-transporte', 'plano-saude', 'plano-odontologico', 'seguro-vida')), 0) AS benefits_cost,
|
|
7475
|
+
COALESCE(SUM(cost.amount) FILTER (WHERE cost.recurrence::text = 'monthly' AND cost_type.slug IN ('inss-patronal', 'fgts', 'rat-fap', 'terceiros-sistema-s', 'provisao-decimo-terceiro', 'provisao-ferias')), 0) AS taxes_cost,
|
|
7476
|
+
COALESCE(SUM(cost.amount) FILTER (WHERE cost.recurrence::text = 'monthly' AND cost_type.slug IN ('software-licenca', 'equipamento')), 0) AS tools_cost
|
|
7477
|
+
FROM operations_collaborator_cost cost
|
|
7478
|
+
LEFT JOIN operations_cost_type cost_type
|
|
7479
|
+
ON cost_type.id = cost.cost_type_id
|
|
7480
|
+
WHERE cost.collaborator_id = collaborator_record.id
|
|
7481
|
+
AND (cost.start_date IS NULL OR cost.start_date <= $2::date)
|
|
7482
|
+
AND (cost.end_date IS NULL OR cost.end_date >= $1::date)
|
|
7483
|
+
) cost_totals
|
|
7484
|
+
LEFT JOIN LATERAL (
|
|
7485
|
+
SELECT h.amount
|
|
7486
|
+
FROM operations_collaborator_compensation_history h
|
|
7487
|
+
WHERE h.collaborator_id = collaborator_record.id
|
|
7488
|
+
AND (h.effective_date IS NULL OR h.effective_date <= $2::date)
|
|
7489
|
+
ORDER BY h.effective_date DESC NULLS LAST, h.created_at DESC
|
|
7490
|
+
LIMIT 1
|
|
7491
|
+
) compensation_history ON TRUE
|
|
7492
|
+
LEFT JOIN LATERAL (
|
|
7493
|
+
SELECT oc.budget_amount
|
|
7494
|
+
FROM operations_contract oc
|
|
7495
|
+
WHERE oc.related_collaborator_id = collaborator_record.id
|
|
7496
|
+
AND oc.deleted_at IS NULL
|
|
7497
|
+
ORDER BY CASE WHEN oc.origin_type = 'employee_hiring' THEN 0 ELSE 1 END,
|
|
7498
|
+
oc.created_at DESC
|
|
7499
|
+
LIMIT 1
|
|
7500
|
+
) hiring_contract ON TRUE
|
|
7501
|
+
) collaborator_costs ON TRUE
|
|
7502
|
+
LEFT JOIN LATERAL (
|
|
7503
|
+
SELECT COALESCE(SUM(entry2.hours), 0) AS total_hours
|
|
7504
|
+
FROM operations_timesheet_entry entry2
|
|
7505
|
+
JOIN operations_project_assignment pa2
|
|
7506
|
+
ON pa2.id = entry2.project_assignment_id
|
|
7507
|
+
AND pa2.deleted_at IS NULL
|
|
7508
|
+
WHERE pa2.collaborator_id = collaborator_record.id
|
|
7509
|
+
AND entry2.deleted_at IS NULL
|
|
7510
|
+
AND entry2.work_date BETWEEN $1::date AND $2::date
|
|
7511
|
+
) collaborator_hours ON TRUE
|
|
7512
|
+
WHERE pa.project_id = p.id
|
|
7513
|
+
AND entry.deleted_at IS NULL
|
|
7514
|
+
AND entry.work_date BETWEEN $1::date AND $2::date
|
|
7515
|
+
) cost_stats ON TRUE
|
|
7516
|
+
LEFT JOIN LATERAL (
|
|
7517
|
+
SELECT COALESCE(
|
|
7518
|
+
SUM(
|
|
7519
|
+
pa.weekly_hours
|
|
7520
|
+
* (
|
|
7521
|
+
(
|
|
7522
|
+
COALESCE(alloc_costs.salary_cost, 0)
|
|
7523
|
+
+ COALESCE(alloc_costs.benefits_cost, 0)
|
|
7524
|
+
+ COALESCE(alloc_costs.taxes_cost, 0)
|
|
7525
|
+
+ COALESCE(alloc_costs.tools_cost, 0)
|
|
7526
|
+
)
|
|
7527
|
+
* $3::numeric
|
|
7528
|
+
/ GREATEST(
|
|
7529
|
+
COALESCE(alloc_col.weekly_capacity_hours, 40)::numeric,
|
|
7530
|
+
1
|
|
7531
|
+
)
|
|
7532
|
+
)
|
|
7533
|
+
),
|
|
7534
|
+
0
|
|
7535
|
+
) AS allocated_cost
|
|
7536
|
+
FROM operations_project_assignment pa
|
|
7537
|
+
JOIN operations_collaborator alloc_col
|
|
7538
|
+
ON alloc_col.id = pa.collaborator_id
|
|
7539
|
+
AND alloc_col.deleted_at IS NULL
|
|
7540
|
+
LEFT JOIN LATERAL (
|
|
7541
|
+
SELECT COALESCE(NULLIF(ct.salary_cost, 0), ch.amount, hc.budget_amount, 0) AS salary_cost,
|
|
7542
|
+
ct.benefits_cost,
|
|
7543
|
+
ct.taxes_cost,
|
|
7544
|
+
ct.tools_cost
|
|
7545
|
+
FROM (
|
|
7546
|
+
SELECT COALESCE(SUM(c.amount) FILTER (WHERE c.recurrence::text = 'monthly' AND ct2.slug IN ('salario-base', 'pro-labore')), 0) AS salary_cost,
|
|
7547
|
+
COALESCE(SUM(c.amount) FILTER (WHERE c.recurrence::text = 'monthly' AND ct2.slug IN ('vale-refeicao', 'vale-alimentacao', 'vale-transporte', 'plano-saude', 'plano-odontologico', 'seguro-vida')), 0) AS benefits_cost,
|
|
7548
|
+
COALESCE(SUM(c.amount) FILTER (WHERE c.recurrence::text = 'monthly' AND ct2.slug IN ('inss-patronal', 'fgts', 'rat-fap', 'terceiros-sistema-s', 'provisao-decimo-terceiro', 'provisao-ferias')), 0) AS taxes_cost,
|
|
7549
|
+
COALESCE(SUM(c.amount) FILTER (WHERE c.recurrence::text = 'monthly' AND ct2.slug IN ('software-licenca', 'equipamento')), 0) AS tools_cost
|
|
7550
|
+
FROM operations_collaborator_cost c
|
|
7551
|
+
LEFT JOIN operations_cost_type ct2
|
|
7552
|
+
ON ct2.id = c.cost_type_id
|
|
7553
|
+
WHERE c.collaborator_id = alloc_col.id
|
|
7554
|
+
AND (c.start_date IS NULL OR c.start_date <= $2::date)
|
|
7555
|
+
AND (c.end_date IS NULL OR c.end_date >= $1::date)
|
|
7556
|
+
) ct
|
|
7557
|
+
LEFT JOIN LATERAL (
|
|
7558
|
+
SELECT h.amount
|
|
7559
|
+
FROM operations_collaborator_compensation_history h
|
|
7560
|
+
WHERE h.collaborator_id = alloc_col.id
|
|
7561
|
+
AND (h.effective_date IS NULL OR h.effective_date <= $2::date)
|
|
7562
|
+
ORDER BY h.effective_date DESC NULLS LAST, h.created_at DESC
|
|
7563
|
+
LIMIT 1
|
|
7564
|
+
) ch ON TRUE
|
|
7565
|
+
LEFT JOIN LATERAL (
|
|
7566
|
+
SELECT oc.budget_amount
|
|
7567
|
+
FROM operations_contract oc
|
|
7568
|
+
WHERE oc.related_collaborator_id = alloc_col.id
|
|
7569
|
+
AND oc.deleted_at IS NULL
|
|
7570
|
+
ORDER BY CASE WHEN oc.origin_type = 'employee_hiring' THEN 0 ELSE 1 END,
|
|
7571
|
+
oc.created_at DESC
|
|
7572
|
+
LIMIT 1
|
|
7573
|
+
) hc ON TRUE
|
|
7574
|
+
) alloc_costs ON TRUE
|
|
7575
|
+
WHERE pa.project_id = p.id
|
|
7576
|
+
AND pa.deleted_at IS NULL
|
|
7577
|
+
AND pa.status IN ('planned', 'active')
|
|
7578
|
+
) alloc_cost_stats ON TRUE
|
|
7227
7579
|
LEFT JOIN LATERAL (
|
|
7228
7580
|
SELECT COUNT(*) FILTER (WHERE task.status IN ('todo', 'doing', 'review')) AS open_tasks,
|
|
7229
7581
|
COALESCE(SUM(task.estimate_hours) FILTER (WHERE task.status IN ('todo', 'doing', 'review')), 0) AS backlog_hours,
|
|
@@ -7234,18 +7586,20 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
7234
7586
|
) task_stats ON TRUE
|
|
7235
7587
|
WHERE ${where.join(' AND ')}
|
|
7236
7588
|
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
7589
|
const rows = dbRows
|
|
7241
7590
|
.map((row) => {
|
|
7242
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
7591
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
7243
7592
|
const progress = Number((_a = row.progressPercent) !== null && _a !== void 0 ? _a : 0);
|
|
7244
7593
|
const contractedRevenue = Number((_b = row.contractedRevenue) !== null && _b !== void 0 ? _b : 0);
|
|
7245
7594
|
const recognizedRevenue = contractedRevenue * (progress / 100);
|
|
7246
7595
|
const actualHours = Number((_c = row.actualHours) !== null && _c !== void 0 ? _c : 0);
|
|
7247
7596
|
const plannedHours = Math.max(Number((_d = row.weeklyHours) !== null && _d !== void 0 ? _d : 0) * periodWeeks, actualHours);
|
|
7248
|
-
const realizedCost = 0;
|
|
7597
|
+
const realizedCost = Number((_e = row.realizedCost) !== null && _e !== void 0 ? _e : 0);
|
|
7598
|
+
const allocatedCost = Number((_f = row.allocatedCost) !== null && _f !== void 0 ? _f : 0);
|
|
7599
|
+
const consumedHoursCost = realizedCost;
|
|
7600
|
+
const idlenessHours = Math.max(plannedHours - actualHours, 0);
|
|
7601
|
+
const idlenessRate = plannedHours > 0 ? (idlenessHours / plannedHours) * 100 : 0;
|
|
7602
|
+
const idlenessCost = Math.max(allocatedCost - consumedHoursCost, 0);
|
|
7249
7603
|
const reportStatus = row.status === 'paused'
|
|
7250
7604
|
? 'paused'
|
|
7251
7605
|
: row.status === 'at_risk'
|
|
@@ -7261,9 +7615,9 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
7261
7615
|
return {
|
|
7262
7616
|
id: Number(row.id),
|
|
7263
7617
|
name: row.name,
|
|
7264
|
-
client: (
|
|
7265
|
-
manager: (
|
|
7266
|
-
squad: String((
|
|
7618
|
+
client: (_g = row.client) !== null && _g !== void 0 ? _g : '-',
|
|
7619
|
+
manager: (_h = row.manager) !== null && _h !== void 0 ? _h : '-',
|
|
7620
|
+
squad: String((_j = row.squad) !== null && _j !== void 0 ? _j : '-').replace(/_/g, ' '),
|
|
7267
7621
|
status: reportStatus,
|
|
7268
7622
|
contractType: row.contractType === 'monthly_retainer'
|
|
7269
7623
|
? 'retainer'
|
|
@@ -7276,7 +7630,7 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
7276
7630
|
contractedRevenue,
|
|
7277
7631
|
recognizedRevenue,
|
|
7278
7632
|
realizedCost,
|
|
7279
|
-
forecastCost: realizedCost,
|
|
7633
|
+
forecastCost: realizedCost * multiplier.cost,
|
|
7280
7634
|
teamCost: realizedCost,
|
|
7281
7635
|
infraCost: 0,
|
|
7282
7636
|
licenseCost: 0,
|
|
@@ -7284,14 +7638,18 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
7284
7638
|
reworkCost: 0,
|
|
7285
7639
|
plannedHours,
|
|
7286
7640
|
actualHours,
|
|
7287
|
-
billableHours: Number((
|
|
7641
|
+
billableHours: Number((_k = row.billableHours) !== null && _k !== void 0 ? _k : 0),
|
|
7288
7642
|
reworkHours: 0,
|
|
7289
|
-
internalHours: Math.max(actualHours - Number((
|
|
7643
|
+
internalHours: Math.max(actualHours - Number((_l = row.billableHours) !== null && _l !== void 0 ? _l : 0), 0),
|
|
7290
7644
|
allocatedCapacity: plannedHours ? (actualHours / plannedHours) * 100 : 0,
|
|
7291
7645
|
physicalProgress: progress,
|
|
7292
7646
|
financialProgress: contractedRevenue ? (recognizedRevenue / contractedRevenue) * 100 : 0,
|
|
7293
7647
|
backlogValue: Math.max(contractedRevenue - recognizedRevenue, 0),
|
|
7294
|
-
futureDeliveries: Number((
|
|
7648
|
+
futureDeliveries: Number((_m = row.futureDeliveries) !== null && _m !== void 0 ? _m : 0),
|
|
7649
|
+
allocatedCost,
|
|
7650
|
+
consumedHoursCost,
|
|
7651
|
+
idlenessRate,
|
|
7652
|
+
idlenessCost,
|
|
7295
7653
|
risk,
|
|
7296
7654
|
recommendation: risk === 'alto'
|
|
7297
7655
|
? 'Revisar escopo, prazo ou capacidade alocada.'
|
|
@@ -7314,6 +7672,9 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
7314
7672
|
acc.avgDeadline += row.physicalProgress;
|
|
7315
7673
|
acc.avgAllocation += row.allocatedCapacity;
|
|
7316
7674
|
acc.atRisk += row.risk === 'alto' ? 1 : 0;
|
|
7675
|
+
acc.allocatedCost += row.allocatedCost;
|
|
7676
|
+
acc.consumedHoursCost += row.consumedHoursCost;
|
|
7677
|
+
acc.idlenessCost += row.idlenessCost;
|
|
7317
7678
|
return acc;
|
|
7318
7679
|
}, {
|
|
7319
7680
|
contractedRevenue: 0,
|
|
@@ -7331,12 +7692,21 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
7331
7692
|
avgAllocation: 0,
|
|
7332
7693
|
atRisk: 0,
|
|
7333
7694
|
burnRate: 0,
|
|
7695
|
+
allocatedCost: 0,
|
|
7696
|
+
consumedHoursCost: 0,
|
|
7697
|
+
idlenessCost: 0,
|
|
7698
|
+
idlenessRate: 0,
|
|
7699
|
+
plannedProfit: 0,
|
|
7334
7700
|
});
|
|
7335
7701
|
summary.profit = summary.recognizedRevenue - summary.realizedCost;
|
|
7336
7702
|
summary.margin = summary.recognizedRevenue ? (summary.profit / summary.recognizedRevenue) * 100 : 0;
|
|
7337
7703
|
summary.avgDeadline = rows.length ? summary.avgDeadline / rows.length : 0;
|
|
7338
7704
|
summary.avgAllocation = rows.length ? summary.avgAllocation / rows.length : 0;
|
|
7339
7705
|
summary.burnRate = summary.plannedHours ? (summary.actualHours / summary.plannedHours) * 100 : 0;
|
|
7706
|
+
summary.plannedProfit = summary.contractedRevenue - summary.allocatedCost;
|
|
7707
|
+
summary.idlenessRate = summary.plannedHours > 0
|
|
7708
|
+
? Math.max(0, (summary.plannedHours - summary.actualHours) / summary.plannedHours * 100)
|
|
7709
|
+
: 0;
|
|
7340
7710
|
const forecast = Array.from({ length: 12 }, (_, index) => {
|
|
7341
7711
|
const monthDate = new Date(fromDate);
|
|
7342
7712
|
monthDate.setMonth(fromDate.getMonth() + index);
|
|
@@ -7425,6 +7795,11 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
7425
7795
|
: scenario === 'conservative'
|
|
7426
7796
|
? { revenue: 0.9, cost: 0.96, capacity: 0.94 }
|
|
7427
7797
|
: { revenue: 1, cost: 1, capacity: 1 };
|
|
7798
|
+
const fromDate = new Date(`${from}T00:00:00`);
|
|
7799
|
+
const toDate = new Date(`${to}T00:00:00`);
|
|
7800
|
+
const periodDays = Math.max(1, Math.floor((toDate.getTime() - fromDate.getTime()) / 86400000) + 1);
|
|
7801
|
+
const periodWeeks = Math.max(1, Math.ceil(periodDays / 7));
|
|
7802
|
+
const periodMonths = periodDays / 30.4375;
|
|
7428
7803
|
const params = [from, to];
|
|
7429
7804
|
const where = [
|
|
7430
7805
|
'c.deleted_at IS NULL',
|
|
@@ -7451,9 +7826,14 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
7451
7826
|
COALESCE(cost_stats.taxes_cost, 0)::text AS "taxesCost",
|
|
7452
7827
|
COALESCE(cost_stats.tools_cost, 0)::text AS "toolsCost",
|
|
7453
7828
|
COALESCE(value_stats.billable_value, 0)::text AS "billableValue",
|
|
7829
|
+
COALESCE(assignment_stats.planned_allocated_hours, 0)::text AS "plannedAllocatedHours",
|
|
7830
|
+
COALESCE(assignment_stats.planned_billable_hours, 0)::text AS "plannedBillableHours",
|
|
7831
|
+
COALESCE(task_stats.open_task_hours, 0)::text AS "openTaskHours",
|
|
7832
|
+
COALESCE(task_stats.open_task_billable_hours, 0)::text AS "openTaskBillableHours",
|
|
7833
|
+
COALESCE(task_stats.open_tasks, 0)::text AS "openTasks",
|
|
7454
7834
|
COALESCE(value_stats.allocated_hours, 0)::text AS "allocatedHours",
|
|
7455
7835
|
COALESCE(value_stats.billable_hours, 0)::text AS "billableHours",
|
|
7456
|
-
COALESCE(
|
|
7836
|
+
COALESCE(assignment_stats.projects, 0)::text AS projects
|
|
7457
7837
|
FROM operations_collaborator c
|
|
7458
7838
|
LEFT JOIN person person_record ON person_record.id = c.person_id
|
|
7459
7839
|
LEFT JOIN operations_department department_record
|
|
@@ -7466,16 +7846,85 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
7466
7846
|
ON collaborator_type.id = c.collaborator_type_id
|
|
7467
7847
|
AND collaborator_type.deleted_at IS NULL
|
|
7468
7848
|
LEFT JOIN LATERAL (
|
|
7469
|
-
SELECT COALESCE(
|
|
7470
|
-
|
|
7471
|
-
|
|
7472
|
-
|
|
7473
|
-
FROM
|
|
7474
|
-
|
|
7475
|
-
|
|
7476
|
-
|
|
7477
|
-
|
|
7849
|
+
SELECT COALESCE(NULLIF(cost_totals.salary_cost, 0), compensation_history.amount, hiring_contract.budget_amount, 0) AS salary_cost,
|
|
7850
|
+
cost_totals.benefits_cost,
|
|
7851
|
+
cost_totals.taxes_cost,
|
|
7852
|
+
cost_totals.tools_cost
|
|
7853
|
+
FROM (
|
|
7854
|
+
SELECT COALESCE(SUM(cost.amount) FILTER (WHERE cost.recurrence::text = 'monthly' AND cost_type.slug IN ('salario-base', 'pro-labore')), 0) AS salary_cost,
|
|
7855
|
+
COALESCE(SUM(cost.amount) FILTER (WHERE cost.recurrence::text = 'monthly' AND cost_type.slug IN ('vale-refeicao', 'vale-alimentacao', 'vale-transporte', 'plano-saude', 'plano-odontologico', 'seguro-vida')), 0) AS benefits_cost,
|
|
7856
|
+
COALESCE(SUM(cost.amount) FILTER (WHERE cost.recurrence::text = 'monthly' AND cost_type.slug IN ('inss-patronal', 'fgts', 'rat-fap', 'terceiros-sistema-s', 'provisao-decimo-terceiro', 'provisao-ferias')), 0) AS taxes_cost,
|
|
7857
|
+
COALESCE(SUM(cost.amount) FILTER (WHERE cost.recurrence::text = 'monthly' AND cost_type.slug IN ('software-licenca', 'equipamento')), 0) AS tools_cost
|
|
7858
|
+
FROM operations_collaborator_cost cost
|
|
7859
|
+
LEFT JOIN operations_cost_type cost_type
|
|
7860
|
+
ON cost_type.id = cost.cost_type_id
|
|
7861
|
+
WHERE cost.collaborator_id = c.id
|
|
7862
|
+
AND (cost.start_date IS NULL OR cost.start_date <= $2::date)
|
|
7863
|
+
AND (cost.end_date IS NULL OR cost.end_date >= $1::date)
|
|
7864
|
+
) cost_totals
|
|
7865
|
+
LEFT JOIN LATERAL (
|
|
7866
|
+
SELECT h.amount
|
|
7867
|
+
FROM operations_collaborator_compensation_history h
|
|
7868
|
+
WHERE h.collaborator_id = c.id
|
|
7869
|
+
AND (h.effective_date IS NULL OR h.effective_date <= $2::date)
|
|
7870
|
+
ORDER BY h.effective_date DESC NULLS LAST, h.created_at DESC
|
|
7871
|
+
LIMIT 1
|
|
7872
|
+
) compensation_history ON TRUE
|
|
7873
|
+
LEFT JOIN LATERAL (
|
|
7874
|
+
SELECT oc.budget_amount
|
|
7875
|
+
FROM operations_contract oc
|
|
7876
|
+
WHERE oc.related_collaborator_id = c.id
|
|
7877
|
+
AND oc.deleted_at IS NULL
|
|
7878
|
+
ORDER BY CASE WHEN oc.origin_type = 'employee_hiring' THEN 0 ELSE 1 END,
|
|
7879
|
+
oc.created_at DESC
|
|
7880
|
+
LIMIT 1
|
|
7881
|
+
) hiring_contract ON TRUE
|
|
7478
7882
|
) cost_stats ON TRUE
|
|
7883
|
+
LEFT JOIN LATERAL (
|
|
7884
|
+
SELECT COALESCE(
|
|
7885
|
+
SUM(
|
|
7886
|
+
COALESCE(
|
|
7887
|
+
pa.weekly_hours,
|
|
7888
|
+
COALESCE(c.weekly_capacity_hours, 40) * COALESCE(pa.allocation_percent, 0) / 100
|
|
7889
|
+
) * GREATEST(
|
|
7890
|
+
CEIL(
|
|
7891
|
+
(
|
|
7892
|
+
LEAST(COALESCE(pa.end_date, $2::date), $2::date)
|
|
7893
|
+
- GREATEST(COALESCE(pa.start_date, $1::date), $1::date)
|
|
7894
|
+
+ 1
|
|
7895
|
+
) / 7.0
|
|
7896
|
+
),
|
|
7897
|
+
0
|
|
7898
|
+
)
|
|
7899
|
+
),
|
|
7900
|
+
0
|
|
7901
|
+
) AS planned_allocated_hours,
|
|
7902
|
+
COALESCE(
|
|
7903
|
+
SUM(
|
|
7904
|
+
COALESCE(
|
|
7905
|
+
pa.weekly_hours,
|
|
7906
|
+
COALESCE(c.weekly_capacity_hours, 40) * COALESCE(pa.allocation_percent, 0) / 100
|
|
7907
|
+
) * GREATEST(
|
|
7908
|
+
CEIL(
|
|
7909
|
+
(
|
|
7910
|
+
LEAST(COALESCE(pa.end_date, $2::date), $2::date)
|
|
7911
|
+
- GREATEST(COALESCE(pa.start_date, $1::date), $1::date)
|
|
7912
|
+
+ 1
|
|
7913
|
+
) / 7.0
|
|
7914
|
+
),
|
|
7915
|
+
0
|
|
7916
|
+
)
|
|
7917
|
+
) FILTER (WHERE pa.is_billable = true),
|
|
7918
|
+
0
|
|
7919
|
+
) AS planned_billable_hours,
|
|
7920
|
+
COUNT(DISTINCT pa.project_id) AS projects
|
|
7921
|
+
FROM operations_project_assignment pa
|
|
7922
|
+
WHERE pa.collaborator_id = c.id
|
|
7923
|
+
AND pa.deleted_at IS NULL
|
|
7924
|
+
AND pa.status IN ('planned', 'active')
|
|
7925
|
+
AND (pa.start_date IS NULL OR pa.start_date <= $2::date)
|
|
7926
|
+
AND (pa.end_date IS NULL OR pa.end_date >= $1::date)
|
|
7927
|
+
) assignment_stats ON TRUE
|
|
7479
7928
|
LEFT JOIN LATERAL (
|
|
7480
7929
|
SELECT COALESCE(SUM(entry.hours), 0) AS allocated_hours,
|
|
7481
7930
|
COALESCE(SUM(entry.hours) FILTER (WHERE pa.is_billable = true), 0) AS billable_hours,
|
|
@@ -7489,48 +7938,62 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
7489
7938
|
AND entry.work_date BETWEEN $1::date AND $2::date
|
|
7490
7939
|
) value_stats ON TRUE
|
|
7491
7940
|
LEFT JOIN LATERAL (
|
|
7492
|
-
SELECT COUNT(
|
|
7493
|
-
|
|
7494
|
-
|
|
7941
|
+
SELECT COUNT(*) AS open_tasks,
|
|
7942
|
+
COALESCE(SUM(COALESCE(task.estimate_hours, 0)), 0) AS open_task_hours,
|
|
7943
|
+
COALESCE(
|
|
7944
|
+
SUM(COALESCE(task.estimate_hours, 0)) FILTER (WHERE pa.is_billable = true),
|
|
7945
|
+
0
|
|
7946
|
+
) AS open_task_billable_hours
|
|
7947
|
+
FROM operations_task task
|
|
7948
|
+
LEFT JOIN operations_project_assignment pa
|
|
7949
|
+
ON pa.id = task.project_assignment_id
|
|
7495
7950
|
AND pa.deleted_at IS NULL
|
|
7496
|
-
|
|
7497
|
-
|
|
7951
|
+
WHERE task.deleted_at IS NULL
|
|
7952
|
+
AND task.status IN ('todo', 'doing', 'review')
|
|
7953
|
+
AND (
|
|
7954
|
+
task.assignee_collaborator_id = c.id
|
|
7955
|
+
OR pa.collaborator_id = c.id
|
|
7956
|
+
)
|
|
7957
|
+
) task_stats ON TRUE
|
|
7498
7958
|
WHERE ${where.join(' AND ')}
|
|
7499
7959
|
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
7960
|
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);
|
|
7961
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
|
|
7962
|
+
const salaryCost = Number((_a = row.salaryCost) !== null && _a !== void 0 ? _a : 0) * periodMonths;
|
|
7963
|
+
const benefitsCost = Number((_b = row.benefitsCost) !== null && _b !== void 0 ? _b : 0) * periodMonths;
|
|
7964
|
+
const taxesCost = Number((_c = row.taxesCost) !== null && _c !== void 0 ? _c : 0) * periodMonths;
|
|
7965
|
+
const toolsCost = Number((_d = row.toolsCost) !== null && _d !== void 0 ? _d : 0) * periodMonths;
|
|
7509
7966
|
const availableHours = Number((_e = row.weeklyCapacityHours) !== null && _e !== void 0 ? _e : 40) * periodWeeks;
|
|
7510
|
-
const
|
|
7511
|
-
const
|
|
7967
|
+
const plannedAllocatedHours = Number((_f = row.plannedAllocatedHours) !== null && _f !== void 0 ? _f : 0);
|
|
7968
|
+
const plannedBillableHours = Number((_g = row.plannedBillableHours) !== null && _g !== void 0 ? _g : 0);
|
|
7969
|
+
const openTaskHours = Number((_h = row.openTaskHours) !== null && _h !== void 0 ? _h : 0);
|
|
7970
|
+
const openTaskBillableHours = Number((_j = row.openTaskBillableHours) !== null && _j !== void 0 ? _j : 0);
|
|
7971
|
+
const actualAllocatedHours = Number((_k = row.allocatedHours) !== null && _k !== void 0 ? _k : 0);
|
|
7972
|
+
const actualBillableHours = Number((_l = row.billableHours) !== null && _l !== void 0 ? _l : 0);
|
|
7973
|
+
const allocatedHours = Math.max(actualAllocatedHours, plannedAllocatedHours, openTaskHours);
|
|
7974
|
+
const billableHours = Math.max(actualBillableHours, plannedBillableHours, openTaskBillableHours);
|
|
7512
7975
|
const allocation = availableHours ? (allocatedHours / availableHours) * 100 : 0;
|
|
7513
7976
|
const risk = allocation >= 98 ? 'alto' : allocation < 75 ? 'médio' : 'baixo';
|
|
7514
7977
|
return {
|
|
7515
7978
|
id: Number(row.id),
|
|
7516
7979
|
name: row.name,
|
|
7517
|
-
role: (
|
|
7518
|
-
seniority: (
|
|
7519
|
-
department: (
|
|
7520
|
-
contractType: (
|
|
7980
|
+
role: (_m = row.role) !== null && _m !== void 0 ? _m : '-',
|
|
7981
|
+
seniority: (_o = row.seniority) !== null && _o !== void 0 ? _o : '-',
|
|
7982
|
+
department: (_p = row.department) !== null && _p !== void 0 ? _p : '-',
|
|
7983
|
+
contractType: (_q = row.contractType) !== null && _q !== void 0 ? _q : '-',
|
|
7521
7984
|
startDate: row.startDate,
|
|
7522
7985
|
endDate: row.endDate,
|
|
7523
7986
|
salaryCost,
|
|
7524
7987
|
benefitsCost,
|
|
7525
7988
|
taxesCost,
|
|
7526
7989
|
toolsCost,
|
|
7527
|
-
billableValue: Number((
|
|
7990
|
+
billableValue: Number((_r = row.billableValue) !== null && _r !== void 0 ? _r : 0),
|
|
7528
7991
|
availableHours,
|
|
7529
7992
|
allocatedHours,
|
|
7530
7993
|
billableHours,
|
|
7531
7994
|
internalHours: Math.max(allocatedHours - billableHours, 0),
|
|
7532
7995
|
overtimeHours: Math.max(allocatedHours - availableHours, 0),
|
|
7533
|
-
projects: Number((
|
|
7996
|
+
projects: Number((_s = row.projects) !== null && _s !== void 0 ? _s : 0),
|
|
7534
7997
|
risk,
|
|
7535
7998
|
recommendation: risk === 'alto'
|
|
7536
7999
|
? 'Reduzir sobrecarga ou redistribuir entregas.'
|
|
@@ -7578,7 +8041,11 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
7578
8041
|
summary.freeHours = Math.max(summary.availableHours - summary.allocatedHours, 0);
|
|
7579
8042
|
summary.allocation = summary.availableHours ? (summary.allocatedHours / summary.availableHours) * 100 : 0;
|
|
7580
8043
|
summary.utilization = summary.availableHours ? (summary.billableHours / summary.availableHours) * 100 : 0;
|
|
7581
|
-
summary.hourlyCost = summary.allocatedHours
|
|
8044
|
+
summary.hourlyCost = summary.allocatedHours
|
|
8045
|
+
? summary.cost / summary.allocatedHours
|
|
8046
|
+
: summary.availableHours
|
|
8047
|
+
? summary.cost / summary.availableHours
|
|
8048
|
+
: 0;
|
|
7582
8049
|
const forecast = Array.from({ length: 12 }, (_, index) => {
|
|
7583
8050
|
const monthDate = new Date(fromDate);
|
|
7584
8051
|
monthDate.setMonth(fromDate.getMonth() + index);
|
|
@@ -7764,6 +8231,1149 @@ let OperationsService = OperationsService_1 = class OperationsService {
|
|
|
7764
8231
|
await this.prisma.$queryRawUnsafe(`DELETE FROM operations_collaborator_cost WHERE id = $1`, costId);
|
|
7765
8232
|
return { success: true };
|
|
7766
8233
|
}
|
|
8234
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
8235
|
+
// Project Cost Categories
|
|
8236
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
8237
|
+
async listProjectCostCategories(userId, filters = {}) {
|
|
8238
|
+
var _a;
|
|
8239
|
+
await this.getActorContext(userId);
|
|
8240
|
+
const localeId = await this.resolvePreferredLocaleId();
|
|
8241
|
+
const params = [localeId];
|
|
8242
|
+
const where = ['pcc.deleted_at IS NULL'];
|
|
8243
|
+
if (filters.is_active === true) {
|
|
8244
|
+
where.push('pcc.is_active = true');
|
|
8245
|
+
}
|
|
8246
|
+
if ((_a = filters.search) === null || _a === void 0 ? void 0 : _a.trim()) {
|
|
8247
|
+
const p = this.param(params, `%${filters.search.trim()}%`);
|
|
8248
|
+
where.push(`(COALESCE(pccl.name, pcc.slug) ILIKE ${p} OR COALESCE(pcc.slug, '') ILIKE ${p})`);
|
|
8249
|
+
}
|
|
8250
|
+
const whereClause = `WHERE ${where.join(' AND ')}`;
|
|
8251
|
+
return this.queryRows(`SELECT pcc.id,
|
|
8252
|
+
pcc.slug,
|
|
8253
|
+
COALESCE(pccl.name, pcc.slug) AS name,
|
|
8254
|
+
pccl.description,
|
|
8255
|
+
pcc.icon,
|
|
8256
|
+
pcc.color,
|
|
8257
|
+
pcc.is_active AS "isActive",
|
|
8258
|
+
pcc.sort_order AS "sortOrder",
|
|
8259
|
+
pcc.created_at AS "createdAt"
|
|
8260
|
+
FROM operations_project_cost_category pcc
|
|
8261
|
+
LEFT JOIN LATERAL (
|
|
8262
|
+
SELECT l.name, l.description
|
|
8263
|
+
FROM operations_project_cost_category_locale l
|
|
8264
|
+
WHERE l.operations_project_cost_category_id = pcc.id
|
|
8265
|
+
ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC,
|
|
8266
|
+
l.id ASC
|
|
8267
|
+
LIMIT 1
|
|
8268
|
+
) pccl ON TRUE
|
|
8269
|
+
${whereClause}
|
|
8270
|
+
ORDER BY pcc.sort_order ASC, COALESCE(pccl.name, pcc.slug) ASC`, params);
|
|
8271
|
+
}
|
|
8272
|
+
async createProjectCostCategory(userId, data) {
|
|
8273
|
+
var _a;
|
|
8274
|
+
const actor = await this.getActorContext(userId);
|
|
8275
|
+
this.ensureDirector(actor);
|
|
8276
|
+
const slug = (_a = data.slug) === null || _a === void 0 ? void 0 : _a.trim();
|
|
8277
|
+
if (!slug) {
|
|
8278
|
+
throw new common_1.BadRequestException('Cost category slug is required.');
|
|
8279
|
+
}
|
|
8280
|
+
return this.prisma.$transaction(async (tx) => {
|
|
8281
|
+
var _a, _b, _c, _d, _e, _f;
|
|
8282
|
+
const localeId = await this.resolvePreferredLocaleId(tx);
|
|
8283
|
+
const created = (await tx.$queryRawUnsafe(`INSERT INTO operations_project_cost_category (slug, icon, color, is_active, sort_order, created_at, updated_at)
|
|
8284
|
+
VALUES ($1, $2, $3, $4, $5, NOW(), NOW())
|
|
8285
|
+
RETURNING id`, slug, (_a = data.icon) !== null && _a !== void 0 ? _a : null, (_b = data.color) !== null && _b !== void 0 ? _b : null, (_c = data.is_active) !== null && _c !== void 0 ? _c : true, (_d = data.sort_order) !== null && _d !== void 0 ? _d : 0));
|
|
8286
|
+
const createdId = (_e = created[0]) === null || _e === void 0 ? void 0 : _e.id;
|
|
8287
|
+
if (!createdId) {
|
|
8288
|
+
throw new common_1.BadRequestException('Unable to create project cost category.');
|
|
8289
|
+
}
|
|
8290
|
+
const name = typeof data.name === 'string' ? data.name : (data.name ? JSON.stringify(data.name) : slug);
|
|
8291
|
+
const description = typeof data.description === 'string' ? data.description : (data.description ? JSON.stringify(data.description) : null);
|
|
8292
|
+
if (localeId && name) {
|
|
8293
|
+
await tx.$executeRawUnsafe(`INSERT INTO operations_project_cost_category_locale (operations_project_cost_category_id, locale_id, name, description)
|
|
8294
|
+
VALUES ($1, $2, $3, $4)`, createdId, localeId, name, description !== null && description !== void 0 ? description : null);
|
|
8295
|
+
}
|
|
8296
|
+
const rows = (await tx.$queryRawUnsafe(`SELECT pcc.id,
|
|
8297
|
+
pcc.slug,
|
|
8298
|
+
COALESCE(pccl.name, pcc.slug) AS name,
|
|
8299
|
+
pccl.description,
|
|
8300
|
+
pcc.icon,
|
|
8301
|
+
pcc.color,
|
|
8302
|
+
pcc.is_active AS "isActive",
|
|
8303
|
+
pcc.sort_order AS "sortOrder",
|
|
8304
|
+
pcc.created_at AS "createdAt"
|
|
8305
|
+
FROM operations_project_cost_category pcc
|
|
8306
|
+
LEFT JOIN operations_project_cost_category_locale pccl
|
|
8307
|
+
ON pccl.operations_project_cost_category_id = pcc.id AND pccl.locale_id = $2
|
|
8308
|
+
WHERE pcc.id = $1`, createdId, localeId));
|
|
8309
|
+
return (_f = rows[0]) !== null && _f !== void 0 ? _f : null;
|
|
8310
|
+
});
|
|
8311
|
+
}
|
|
8312
|
+
async updateProjectCostCategory(userId, id, data) {
|
|
8313
|
+
const actor = await this.getActorContext(userId);
|
|
8314
|
+
this.ensureDirector(actor);
|
|
8315
|
+
const category = await this.querySingle(`SELECT id FROM operations_project_cost_category WHERE id = $1 AND deleted_at IS NULL LIMIT 1`, [id]);
|
|
8316
|
+
if (!category) {
|
|
8317
|
+
throw new common_1.NotFoundException('Project cost category not found.');
|
|
8318
|
+
}
|
|
8319
|
+
const sets = [];
|
|
8320
|
+
const params = [];
|
|
8321
|
+
if (data.slug !== undefined)
|
|
8322
|
+
sets.push(`slug = ${this.param(params, data.slug)}`);
|
|
8323
|
+
if (data.icon !== undefined)
|
|
8324
|
+
sets.push(`icon = ${this.param(params, data.icon)}`);
|
|
8325
|
+
if (data.color !== undefined)
|
|
8326
|
+
sets.push(`color = ${this.param(params, data.color)}`);
|
|
8327
|
+
if (data.is_active !== undefined)
|
|
8328
|
+
sets.push(`is_active = ${this.param(params, data.is_active)}`);
|
|
8329
|
+
if (data.sort_order !== undefined)
|
|
8330
|
+
sets.push(`sort_order = ${this.param(params, data.sort_order)}`);
|
|
8331
|
+
if (sets.length > 0) {
|
|
8332
|
+
sets.push(`updated_at = NOW()`);
|
|
8333
|
+
await this.prisma.$queryRawUnsafe(`UPDATE operations_project_cost_category SET ${sets.join(', ')} WHERE id = ${this.param(params, id)}`, ...params);
|
|
8334
|
+
}
|
|
8335
|
+
if (data.name !== undefined || data.description !== undefined) {
|
|
8336
|
+
const localeId = await this.resolvePreferredLocaleId();
|
|
8337
|
+
if (localeId) {
|
|
8338
|
+
const name = typeof data.name === 'string' ? data.name : (data.name ? JSON.stringify(data.name) : undefined);
|
|
8339
|
+
const description = typeof data.description === 'string' ? data.description : (data.description ? JSON.stringify(data.description) : null);
|
|
8340
|
+
const existing = await this.querySingle(`SELECT id FROM operations_project_cost_category_locale WHERE operations_project_cost_category_id = $1 AND locale_id = $2 LIMIT 1`, [id, localeId]);
|
|
8341
|
+
if (existing) {
|
|
8342
|
+
const localeSets = [];
|
|
8343
|
+
const localeParams = [];
|
|
8344
|
+
if (name !== undefined)
|
|
8345
|
+
localeSets.push(`name = ${this.param(localeParams, name)}`);
|
|
8346
|
+
if (description !== undefined)
|
|
8347
|
+
localeSets.push(`description = ${this.param(localeParams, description)}`);
|
|
8348
|
+
if (localeSets.length > 0) {
|
|
8349
|
+
await this.prisma.$queryRawUnsafe(`UPDATE operations_project_cost_category_locale SET ${localeSets.join(', ')} WHERE operations_project_cost_category_id = ${this.param(localeParams, id)} AND locale_id = ${this.param(localeParams, localeId)}`, ...localeParams);
|
|
8350
|
+
}
|
|
8351
|
+
}
|
|
8352
|
+
else if (name) {
|
|
8353
|
+
await this.prisma.$queryRawUnsafe(`INSERT INTO operations_project_cost_category_locale (operations_project_cost_category_id, locale_id, name, description) VALUES ($1, $2, $3, $4)`, id, localeId, name, description !== null && description !== void 0 ? description : null);
|
|
8354
|
+
}
|
|
8355
|
+
}
|
|
8356
|
+
}
|
|
8357
|
+
return this.querySingle(`SELECT id, slug FROM operations_project_cost_category WHERE id = $1`, [id]);
|
|
8358
|
+
}
|
|
8359
|
+
async deleteProjectCostCategory(userId, id) {
|
|
8360
|
+
const actor = await this.getActorContext(userId);
|
|
8361
|
+
this.ensureDirector(actor);
|
|
8362
|
+
const category = await this.querySingle(`SELECT id FROM operations_project_cost_category WHERE id = $1 AND deleted_at IS NULL LIMIT 1`, [id]);
|
|
8363
|
+
if (!category) {
|
|
8364
|
+
throw new common_1.NotFoundException('Project cost category not found.');
|
|
8365
|
+
}
|
|
8366
|
+
await this.prisma.$queryRawUnsafe(`UPDATE operations_project_cost_category SET deleted_at = NOW() WHERE id = $1`, id);
|
|
8367
|
+
return { success: true };
|
|
8368
|
+
}
|
|
8369
|
+
async getProjectCostCategory(userId, id) {
|
|
8370
|
+
await this.getActorContext(userId);
|
|
8371
|
+
const localeId = await this.resolvePreferredLocaleId();
|
|
8372
|
+
const row = await this.querySingle(`SELECT pcc.id,
|
|
8373
|
+
pcc.slug,
|
|
8374
|
+
COALESCE(pccl.name, pcc.slug) AS name,
|
|
8375
|
+
pccl.description,
|
|
8376
|
+
pcc.icon,
|
|
8377
|
+
pcc.color,
|
|
8378
|
+
pcc.is_active AS "isActive",
|
|
8379
|
+
pcc.sort_order AS "sortOrder",
|
|
8380
|
+
pcc.created_at AS "createdAt"
|
|
8381
|
+
FROM operations_project_cost_category pcc
|
|
8382
|
+
LEFT JOIN LATERAL (
|
|
8383
|
+
SELECT l.name, l.description
|
|
8384
|
+
FROM operations_project_cost_category_locale l
|
|
8385
|
+
WHERE l.operations_project_cost_category_id = pcc.id
|
|
8386
|
+
ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC,
|
|
8387
|
+
l.id ASC
|
|
8388
|
+
LIMIT 1
|
|
8389
|
+
) pccl ON TRUE
|
|
8390
|
+
WHERE pcc.id = $2 AND pcc.deleted_at IS NULL`, [localeId, id]);
|
|
8391
|
+
if (!row) {
|
|
8392
|
+
throw new common_1.NotFoundException('Project cost category not found.');
|
|
8393
|
+
}
|
|
8394
|
+
return row;
|
|
8395
|
+
}
|
|
8396
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
8397
|
+
// Project Cost Types
|
|
8398
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
8399
|
+
async listProjectCostTypes(userId, filters = {}) {
|
|
8400
|
+
var _a;
|
|
8401
|
+
await this.getActorContext(userId);
|
|
8402
|
+
const localeId = await this.resolvePreferredLocaleId();
|
|
8403
|
+
const params = [localeId];
|
|
8404
|
+
const where = ['pct.deleted_at IS NULL'];
|
|
8405
|
+
if (filters.is_active === true) {
|
|
8406
|
+
where.push('pct.is_active = true');
|
|
8407
|
+
}
|
|
8408
|
+
if (filters.category_id) {
|
|
8409
|
+
where.push(`pct.category_id = ${this.param(params, filters.category_id)}`);
|
|
8410
|
+
}
|
|
8411
|
+
if (filters.default_calculation_type) {
|
|
8412
|
+
where.push(`pct.default_calculation_type = ${this.param(params, filters.default_calculation_type)}`);
|
|
8413
|
+
}
|
|
8414
|
+
if ((_a = filters.search) === null || _a === void 0 ? void 0 : _a.trim()) {
|
|
8415
|
+
const p = this.param(params, `%${filters.search.trim()}%`);
|
|
8416
|
+
where.push(`(COALESCE(pctl.name, pct.slug) ILIKE ${p} OR COALESCE(pct.code, '') ILIKE ${p} OR COALESCE(pct.slug, '') ILIKE ${p})`);
|
|
8417
|
+
}
|
|
8418
|
+
const whereClause = `WHERE ${where.join(' AND ')}`;
|
|
8419
|
+
return this.queryRows(`SELECT pct.id,
|
|
8420
|
+
pct.slug,
|
|
8421
|
+
pct.code,
|
|
8422
|
+
COALESCE(pctl.name, pct.slug) AS name,
|
|
8423
|
+
pctl.description,
|
|
8424
|
+
pct.category_id AS "categoryId",
|
|
8425
|
+
pcc.slug AS "categorySlug",
|
|
8426
|
+
COALESCE(pccl.name, pcc.slug) AS "categoryName",
|
|
8427
|
+
pct.default_unit AS "defaultUnit",
|
|
8428
|
+
pct.default_calculation_type AS "defaultCalculationType",
|
|
8429
|
+
pct.is_recurring_allowed AS "isRecurringAllowed",
|
|
8430
|
+
pct.is_active AS "isActive",
|
|
8431
|
+
pct.sort_order AS "sortOrder",
|
|
8432
|
+
pct.created_at AS "createdAt"
|
|
8433
|
+
FROM operations_project_cost_type pct
|
|
8434
|
+
LEFT JOIN operations_project_cost_category pcc
|
|
8435
|
+
ON pcc.id = pct.category_id AND pcc.deleted_at IS NULL
|
|
8436
|
+
LEFT JOIN LATERAL (
|
|
8437
|
+
SELECT l.name, l.description
|
|
8438
|
+
FROM operations_project_cost_category_locale l
|
|
8439
|
+
WHERE l.operations_project_cost_category_id = pcc.id
|
|
8440
|
+
ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC,
|
|
8441
|
+
l.id ASC
|
|
8442
|
+
LIMIT 1
|
|
8443
|
+
) pccl ON TRUE
|
|
8444
|
+
LEFT JOIN LATERAL (
|
|
8445
|
+
SELECT l.name, l.description
|
|
8446
|
+
FROM operations_project_cost_type_locale l
|
|
8447
|
+
WHERE l.operations_project_cost_type_id = pct.id
|
|
8448
|
+
ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC,
|
|
8449
|
+
l.id ASC
|
|
8450
|
+
LIMIT 1
|
|
8451
|
+
) pctl ON TRUE
|
|
8452
|
+
${whereClause}
|
|
8453
|
+
ORDER BY pct.sort_order ASC, COALESCE(pctl.name, pct.slug) ASC`, params);
|
|
8454
|
+
}
|
|
8455
|
+
async createProjectCostType(userId, data) {
|
|
8456
|
+
var _a, _b, _c;
|
|
8457
|
+
const actor = await this.getActorContext(userId);
|
|
8458
|
+
this.ensureDirector(actor);
|
|
8459
|
+
const slug = (_a = data.slug) === null || _a === void 0 ? void 0 : _a.trim();
|
|
8460
|
+
if (!slug) {
|
|
8461
|
+
throw new common_1.BadRequestException('Cost type slug is required.');
|
|
8462
|
+
}
|
|
8463
|
+
if (data.category_id) {
|
|
8464
|
+
const category = await this.querySingle(`SELECT id FROM operations_project_cost_category WHERE id = $1 AND deleted_at IS NULL LIMIT 1`, [data.category_id]);
|
|
8465
|
+
if (!category) {
|
|
8466
|
+
throw new common_1.BadRequestException(`Category with id ${data.category_id} not found.`);
|
|
8467
|
+
}
|
|
8468
|
+
}
|
|
8469
|
+
const existingSlug = await this.querySingle(`SELECT id FROM operations_project_cost_type WHERE slug = $1 AND deleted_at IS NULL LIMIT 1`, [slug]);
|
|
8470
|
+
if (existingSlug) {
|
|
8471
|
+
throw new common_1.ConflictException(`A cost type with slug '${slug}' already exists.`);
|
|
8472
|
+
}
|
|
8473
|
+
const code = (_c = (_b = data.code) === null || _b === void 0 ? void 0 : _b.trim()) !== null && _c !== void 0 ? _c : slug;
|
|
8474
|
+
const existingCode = await this.querySingle(`SELECT id FROM operations_project_cost_type WHERE code = $1 AND deleted_at IS NULL LIMIT 1`, [code]);
|
|
8475
|
+
if (existingCode) {
|
|
8476
|
+
throw new common_1.ConflictException(`A cost type with code '${code}' already exists.`);
|
|
8477
|
+
}
|
|
8478
|
+
return this.prisma.$transaction(async (tx) => {
|
|
8479
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
8480
|
+
const localeId = await this.resolvePreferredLocaleId(tx);
|
|
8481
|
+
const created = (await tx.$queryRawUnsafe(`INSERT INTO operations_project_cost_type (category_id, slug, code, default_unit, default_calculation_type, is_recurring_allowed, is_active, sort_order, created_at, updated_at)
|
|
8482
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, NOW(), NOW())
|
|
8483
|
+
RETURNING id`, (_a = data.category_id) !== null && _a !== void 0 ? _a : null, slug, (_c = (_b = data.code) === null || _b === void 0 ? void 0 : _b.trim()) !== null && _c !== void 0 ? _c : slug, (_d = data.default_unit) !== null && _d !== void 0 ? _d : null, (_e = data.default_calculation_type) !== null && _e !== void 0 ? _e : 'fixed', (_f = data.is_recurring_allowed) !== null && _f !== void 0 ? _f : true, (_g = data.is_active) !== null && _g !== void 0 ? _g : true, (_h = data.sort_order) !== null && _h !== void 0 ? _h : 0));
|
|
8484
|
+
const createdId = (_j = created[0]) === null || _j === void 0 ? void 0 : _j.id;
|
|
8485
|
+
if (!createdId) {
|
|
8486
|
+
throw new common_1.BadRequestException('Unable to create project cost type.');
|
|
8487
|
+
}
|
|
8488
|
+
const name = typeof data.name === 'string' ? data.name : (data.name ? JSON.stringify(data.name) : slug);
|
|
8489
|
+
const description = typeof data.description === 'string' ? data.description : (data.description ? JSON.stringify(data.description) : null);
|
|
8490
|
+
if (localeId && name) {
|
|
8491
|
+
await tx.$executeRawUnsafe(`INSERT INTO operations_project_cost_type_locale (operations_project_cost_type_id, locale_id, name, description)
|
|
8492
|
+
VALUES ($1, $2, $3, $4)`, createdId, localeId, name, description !== null && description !== void 0 ? description : null);
|
|
8493
|
+
}
|
|
8494
|
+
const rows = (await tx.$queryRawUnsafe(`SELECT pct.id,
|
|
8495
|
+
pct.slug,
|
|
8496
|
+
pct.code,
|
|
8497
|
+
COALESCE(pctl.name, pct.slug) AS name,
|
|
8498
|
+
pctl.description,
|
|
8499
|
+
pct.category_id AS "categoryId",
|
|
8500
|
+
pct.default_unit AS "defaultUnit",
|
|
8501
|
+
pct.default_calculation_type AS "defaultCalculationType",
|
|
8502
|
+
pct.is_recurring_allowed AS "isRecurringAllowed",
|
|
8503
|
+
pct.is_active AS "isActive",
|
|
8504
|
+
pct.sort_order AS "sortOrder",
|
|
8505
|
+
pct.created_at AS "createdAt"
|
|
8506
|
+
FROM operations_project_cost_type pct
|
|
8507
|
+
LEFT JOIN operations_project_cost_type_locale pctl
|
|
8508
|
+
ON pctl.operations_project_cost_type_id = pct.id AND pctl.locale_id = $2
|
|
8509
|
+
WHERE pct.id = $1`, createdId, localeId));
|
|
8510
|
+
return (_k = rows[0]) !== null && _k !== void 0 ? _k : null;
|
|
8511
|
+
});
|
|
8512
|
+
}
|
|
8513
|
+
async updateProjectCostType(userId, id, data) {
|
|
8514
|
+
const actor = await this.getActorContext(userId);
|
|
8515
|
+
this.ensureDirector(actor);
|
|
8516
|
+
const costType = await this.querySingle(`SELECT id FROM operations_project_cost_type WHERE id = $1 AND deleted_at IS NULL LIMIT 1`, [id]);
|
|
8517
|
+
if (!costType) {
|
|
8518
|
+
throw new common_1.NotFoundException('Project cost type not found.');
|
|
8519
|
+
}
|
|
8520
|
+
if (data.category_id !== undefined && data.category_id !== null) {
|
|
8521
|
+
const category = await this.querySingle(`SELECT id FROM operations_project_cost_category WHERE id = $1 AND deleted_at IS NULL LIMIT 1`, [data.category_id]);
|
|
8522
|
+
if (!category) {
|
|
8523
|
+
throw new common_1.BadRequestException(`Category with id ${data.category_id} not found.`);
|
|
8524
|
+
}
|
|
8525
|
+
}
|
|
8526
|
+
if (data.slug !== undefined) {
|
|
8527
|
+
const existingSlug = await this.querySingle(`SELECT id FROM operations_project_cost_type WHERE slug = $1 AND id != $2 AND deleted_at IS NULL LIMIT 1`, [data.slug, id]);
|
|
8528
|
+
if (existingSlug) {
|
|
8529
|
+
throw new common_1.ConflictException(`A cost type with slug '${data.slug}' already exists.`);
|
|
8530
|
+
}
|
|
8531
|
+
}
|
|
8532
|
+
if (data.code !== undefined) {
|
|
8533
|
+
const existingCode = await this.querySingle(`SELECT id FROM operations_project_cost_type WHERE code = $1 AND id != $2 AND deleted_at IS NULL LIMIT 1`, [data.code, id]);
|
|
8534
|
+
if (existingCode) {
|
|
8535
|
+
throw new common_1.ConflictException(`A cost type with code '${data.code}' already exists.`);
|
|
8536
|
+
}
|
|
8537
|
+
}
|
|
8538
|
+
const sets = [];
|
|
8539
|
+
const params = [];
|
|
8540
|
+
if (data.category_id !== undefined)
|
|
8541
|
+
sets.push(`category_id = ${this.param(params, data.category_id)}`);
|
|
8542
|
+
if (data.slug !== undefined)
|
|
8543
|
+
sets.push(`slug = ${this.param(params, data.slug)}`);
|
|
8544
|
+
if (data.code !== undefined)
|
|
8545
|
+
sets.push(`code = ${this.param(params, data.code)}`);
|
|
8546
|
+
if (data.default_unit !== undefined)
|
|
8547
|
+
sets.push(`default_unit = ${this.param(params, data.default_unit)}`);
|
|
8548
|
+
if (data.default_calculation_type !== undefined)
|
|
8549
|
+
sets.push(`default_calculation_type = ${this.param(params, data.default_calculation_type)}`);
|
|
8550
|
+
if (data.is_recurring_allowed !== undefined)
|
|
8551
|
+
sets.push(`is_recurring_allowed = ${this.param(params, data.is_recurring_allowed)}`);
|
|
8552
|
+
if (data.is_active !== undefined)
|
|
8553
|
+
sets.push(`is_active = ${this.param(params, data.is_active)}`);
|
|
8554
|
+
if (data.sort_order !== undefined)
|
|
8555
|
+
sets.push(`sort_order = ${this.param(params, data.sort_order)}`);
|
|
8556
|
+
if (sets.length > 0) {
|
|
8557
|
+
sets.push(`updated_at = NOW()`);
|
|
8558
|
+
await this.prisma.$queryRawUnsafe(`UPDATE operations_project_cost_type SET ${sets.join(', ')} WHERE id = ${this.param(params, id)}`, ...params);
|
|
8559
|
+
}
|
|
8560
|
+
if (data.name !== undefined || data.description !== undefined) {
|
|
8561
|
+
const localeId = await this.resolvePreferredLocaleId();
|
|
8562
|
+
if (localeId) {
|
|
8563
|
+
const name = typeof data.name === 'string' ? data.name : (data.name ? JSON.stringify(data.name) : undefined);
|
|
8564
|
+
const description = typeof data.description === 'string' ? data.description : (data.description ? JSON.stringify(data.description) : null);
|
|
8565
|
+
const existing = await this.querySingle(`SELECT id FROM operations_project_cost_type_locale WHERE operations_project_cost_type_id = $1 AND locale_id = $2 LIMIT 1`, [id, localeId]);
|
|
8566
|
+
if (existing) {
|
|
8567
|
+
const localeSets = [];
|
|
8568
|
+
const localeParams = [];
|
|
8569
|
+
if (name !== undefined)
|
|
8570
|
+
localeSets.push(`name = ${this.param(localeParams, name)}`);
|
|
8571
|
+
if (description !== undefined)
|
|
8572
|
+
localeSets.push(`description = ${this.param(localeParams, description)}`);
|
|
8573
|
+
if (localeSets.length > 0) {
|
|
8574
|
+
await this.prisma.$queryRawUnsafe(`UPDATE operations_project_cost_type_locale SET ${localeSets.join(', ')} WHERE operations_project_cost_type_id = ${this.param(localeParams, id)} AND locale_id = ${this.param(localeParams, localeId)}`, ...localeParams);
|
|
8575
|
+
}
|
|
8576
|
+
}
|
|
8577
|
+
else if (name) {
|
|
8578
|
+
await this.prisma.$queryRawUnsafe(`INSERT INTO operations_project_cost_type_locale (operations_project_cost_type_id, locale_id, name, description) VALUES ($1, $2, $3, $4)`, id, localeId, name, description !== null && description !== void 0 ? description : null);
|
|
8579
|
+
}
|
|
8580
|
+
}
|
|
8581
|
+
}
|
|
8582
|
+
return this.querySingle(`SELECT id, slug FROM operations_project_cost_type WHERE id = $1`, [id]);
|
|
8583
|
+
}
|
|
8584
|
+
async deleteProjectCostType(userId, id) {
|
|
8585
|
+
const actor = await this.getActorContext(userId);
|
|
8586
|
+
this.ensureDirector(actor);
|
|
8587
|
+
const costType = await this.querySingle(`SELECT id FROM operations_project_cost_type WHERE id = $1 AND deleted_at IS NULL LIMIT 1`, [id]);
|
|
8588
|
+
if (!costType) {
|
|
8589
|
+
throw new common_1.NotFoundException('Project cost type not found.');
|
|
8590
|
+
}
|
|
8591
|
+
await this.prisma.$queryRawUnsafe(`UPDATE operations_project_cost_type SET deleted_at = NOW() WHERE id = $1`, id);
|
|
8592
|
+
return { success: true };
|
|
8593
|
+
}
|
|
8594
|
+
async getProjectCostType(userId, id) {
|
|
8595
|
+
await this.getActorContext(userId);
|
|
8596
|
+
const localeId = await this.resolvePreferredLocaleId();
|
|
8597
|
+
const row = await this.querySingle(`SELECT pct.id,
|
|
8598
|
+
pct.slug,
|
|
8599
|
+
pct.code,
|
|
8600
|
+
COALESCE(pctl.name, pct.slug) AS name,
|
|
8601
|
+
pctl.description,
|
|
8602
|
+
pct.default_unit,
|
|
8603
|
+
pct.default_calculation_type,
|
|
8604
|
+
pct.is_recurring_allowed,
|
|
8605
|
+
pct.is_active,
|
|
8606
|
+
pct.sort_order,
|
|
8607
|
+
pct.category_id,
|
|
8608
|
+
CASE WHEN pcc.id IS NOT NULL THEN
|
|
8609
|
+
jsonb_build_object(
|
|
8610
|
+
'id', pcc.id,
|
|
8611
|
+
'slug', pcc.slug,
|
|
8612
|
+
'name', COALESCE(pccl.name, pcc.slug),
|
|
8613
|
+
'color', pcc.color,
|
|
8614
|
+
'icon', pcc.icon
|
|
8615
|
+
)
|
|
8616
|
+
ELSE NULL END AS category
|
|
8617
|
+
FROM operations_project_cost_type pct
|
|
8618
|
+
LEFT JOIN operations_project_cost_category pcc
|
|
8619
|
+
ON pcc.id = pct.category_id AND pcc.deleted_at IS NULL
|
|
8620
|
+
LEFT JOIN LATERAL (
|
|
8621
|
+
SELECT l.name, l.description
|
|
8622
|
+
FROM operations_project_cost_category_locale l
|
|
8623
|
+
WHERE l.operations_project_cost_category_id = pcc.id
|
|
8624
|
+
ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC,
|
|
8625
|
+
l.id ASC
|
|
8626
|
+
LIMIT 1
|
|
8627
|
+
) pccl ON TRUE
|
|
8628
|
+
LEFT JOIN LATERAL (
|
|
8629
|
+
SELECT l.name, l.description
|
|
8630
|
+
FROM operations_project_cost_type_locale l
|
|
8631
|
+
WHERE l.operations_project_cost_type_id = pct.id
|
|
8632
|
+
ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC,
|
|
8633
|
+
l.id ASC
|
|
8634
|
+
LIMIT 1
|
|
8635
|
+
) pctl ON TRUE
|
|
8636
|
+
WHERE pct.id = $2 AND pct.deleted_at IS NULL`, [localeId, id]);
|
|
8637
|
+
if (!row) {
|
|
8638
|
+
throw new common_1.NotFoundException('Project cost type not found.');
|
|
8639
|
+
}
|
|
8640
|
+
return row;
|
|
8641
|
+
}
|
|
8642
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
8643
|
+
// Project Costs
|
|
8644
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
8645
|
+
async listProjectCosts(userId, projectId, filters = {}) {
|
|
8646
|
+
var _a;
|
|
8647
|
+
await this.getActorContext(userId);
|
|
8648
|
+
const localeId = await this.resolvePreferredLocaleId();
|
|
8649
|
+
const params = [localeId, projectId];
|
|
8650
|
+
const where = ['pc.deleted_at IS NULL', 'pc.project_id = $2'];
|
|
8651
|
+
if (filters.cost_type_id) {
|
|
8652
|
+
where.push(`pc.cost_type_id = ${this.param(params, filters.cost_type_id)}`);
|
|
8653
|
+
}
|
|
8654
|
+
if (filters.category_id) {
|
|
8655
|
+
where.push(`COALESCE(pc.category_id, pct.category_id) = ${this.param(params, filters.category_id)}`);
|
|
8656
|
+
}
|
|
8657
|
+
if (filters.recurrence_type) {
|
|
8658
|
+
where.push(`pc.recurrence_type = ${this.param(params, filters.recurrence_type)}`);
|
|
8659
|
+
}
|
|
8660
|
+
if (filters.calculation_type) {
|
|
8661
|
+
where.push(`pc.calculation_type = ${this.param(params, filters.calculation_type)}`);
|
|
8662
|
+
}
|
|
8663
|
+
if (filters.status) {
|
|
8664
|
+
where.push(`pc.status = ${this.param(params, filters.status)}`);
|
|
8665
|
+
}
|
|
8666
|
+
if (filters.is_billable !== undefined) {
|
|
8667
|
+
where.push(`pc.is_billable = ${this.param(params, filters.is_billable)}`);
|
|
8668
|
+
}
|
|
8669
|
+
if (filters.is_reimbursable !== undefined) {
|
|
8670
|
+
where.push(`pc.is_reimbursable = ${this.param(params, filters.is_reimbursable)}`);
|
|
8671
|
+
}
|
|
8672
|
+
if (filters.date_from) {
|
|
8673
|
+
where.push(`pc.cost_date >= ${this.param(params, filters.date_from)}::date`);
|
|
8674
|
+
}
|
|
8675
|
+
if (filters.date_to) {
|
|
8676
|
+
where.push(`pc.cost_date <= ${this.param(params, filters.date_to)}::date`);
|
|
8677
|
+
}
|
|
8678
|
+
if ((_a = filters.search) === null || _a === void 0 ? void 0 : _a.trim()) {
|
|
8679
|
+
const p = this.param(params, `%${filters.search.trim()}%`);
|
|
8680
|
+
where.push(`(COALESCE(pc.description, '') ILIKE ${p} OR COALESCE(pc.notes, '') ILIKE ${p})`);
|
|
8681
|
+
}
|
|
8682
|
+
const whereClause = `WHERE ${where.join(' AND ')}`;
|
|
8683
|
+
const rows = await this.queryRows(`SELECT pc.id,
|
|
8684
|
+
pc.project_id AS "projectId",
|
|
8685
|
+
pc.cost_type_id AS "costTypeId",
|
|
8686
|
+
pct.slug AS "costTypeSlug",
|
|
8687
|
+
pct.code AS "costTypeCode",
|
|
8688
|
+
COALESCE(pctl.name, pct.slug) AS "costTypeName",
|
|
8689
|
+
pc.category_id AS "categoryId",
|
|
8690
|
+
COALESCE(pc.category_id, pct.category_id) AS "resolvedCategoryId",
|
|
8691
|
+
pcc.slug AS "categorySlug",
|
|
8692
|
+
COALESCE(pccl.name, pcc.slug) AS "categoryName",
|
|
8693
|
+
pcc.color AS "categoryColor",
|
|
8694
|
+
pcc.icon AS "categoryIcon",
|
|
8695
|
+
pc.description,
|
|
8696
|
+
pc.amount::text AS amount,
|
|
8697
|
+
pc.quantity::text AS quantity,
|
|
8698
|
+
pc.unit_amount::text AS "unitAmount",
|
|
8699
|
+
pc.currency,
|
|
8700
|
+
TO_CHAR(pc.cost_date, 'YYYY-MM-DD') AS "costDate",
|
|
8701
|
+
TO_CHAR(pc.period_start, 'YYYY-MM-DD') AS "periodStart",
|
|
8702
|
+
TO_CHAR(pc.period_end, 'YYYY-MM-DD') AS "periodEnd",
|
|
8703
|
+
pc.calculation_type AS "calculationType",
|
|
8704
|
+
pc.recurrence_type AS "recurrenceType",
|
|
8705
|
+
pc.is_billable AS "isBillable",
|
|
8706
|
+
pc.is_reimbursable AS "isReimbursable",
|
|
8707
|
+
pc.notes,
|
|
8708
|
+
pc.status,
|
|
8709
|
+
pc.created_at AS "createdAt"
|
|
8710
|
+
FROM operations_project_cost pc
|
|
8711
|
+
LEFT JOIN operations_project_cost_type pct
|
|
8712
|
+
ON pct.id = pc.cost_type_id AND pct.deleted_at IS NULL
|
|
8713
|
+
LEFT JOIN operations_project_cost_category pcc
|
|
8714
|
+
ON pcc.id = COALESCE(pc.category_id, pct.category_id) AND pcc.deleted_at IS NULL
|
|
8715
|
+
LEFT JOIN LATERAL (
|
|
8716
|
+
SELECT l.name
|
|
8717
|
+
FROM operations_project_cost_type_locale l
|
|
8718
|
+
WHERE l.operations_project_cost_type_id = pct.id
|
|
8719
|
+
ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC,
|
|
8720
|
+
l.id ASC
|
|
8721
|
+
LIMIT 1
|
|
8722
|
+
) pctl ON TRUE
|
|
8723
|
+
LEFT JOIN LATERAL (
|
|
8724
|
+
SELECT l.name
|
|
8725
|
+
FROM operations_project_cost_category_locale l
|
|
8726
|
+
WHERE l.operations_project_cost_category_id = pcc.id
|
|
8727
|
+
ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC,
|
|
8728
|
+
l.id ASC
|
|
8729
|
+
LIMIT 1
|
|
8730
|
+
) pccl ON TRUE
|
|
8731
|
+
${whereClause}
|
|
8732
|
+
ORDER BY pc.created_at DESC`, params);
|
|
8733
|
+
return rows.map((row) => ({
|
|
8734
|
+
id: row.id,
|
|
8735
|
+
project_id: row.projectId,
|
|
8736
|
+
cost_type_id: row.costTypeId,
|
|
8737
|
+
category_id: row.categoryId,
|
|
8738
|
+
description: row.description,
|
|
8739
|
+
amount: row.amount,
|
|
8740
|
+
quantity: row.quantity,
|
|
8741
|
+
unit_amount: row.unitAmount,
|
|
8742
|
+
currency: row.currency,
|
|
8743
|
+
cost_date: row.costDate,
|
|
8744
|
+
period_start: row.periodStart,
|
|
8745
|
+
period_end: row.periodEnd,
|
|
8746
|
+
calculation_type: row.calculationType,
|
|
8747
|
+
recurrence_type: row.recurrenceType,
|
|
8748
|
+
is_billable: row.isBillable,
|
|
8749
|
+
is_reimbursable: row.isReimbursable,
|
|
8750
|
+
notes: row.notes,
|
|
8751
|
+
status: row.status,
|
|
8752
|
+
created_at: row.createdAt,
|
|
8753
|
+
cost_type: row.costTypeId
|
|
8754
|
+
? { id: row.costTypeId, slug: row.costTypeSlug, name: row.costTypeName, code: row.costTypeCode }
|
|
8755
|
+
: null,
|
|
8756
|
+
category: row.resolvedCategoryId
|
|
8757
|
+
? { id: row.resolvedCategoryId, slug: row.categorySlug, name: row.categoryName, color: row.categoryColor, icon: row.categoryIcon }
|
|
8758
|
+
: null,
|
|
8759
|
+
}));
|
|
8760
|
+
}
|
|
8761
|
+
async getProjectCostsSummaryGrouped(userId, projectId) {
|
|
8762
|
+
var _a, _b;
|
|
8763
|
+
const items = await this.listProjectCosts(userId, projectId, {});
|
|
8764
|
+
// Group by resolved category
|
|
8765
|
+
const categoryMap = new Map();
|
|
8766
|
+
for (const cost of items) {
|
|
8767
|
+
const cat = (_a = cost.category) !== null && _a !== void 0 ? _a : null;
|
|
8768
|
+
const key = (_b = cat === null || cat === void 0 ? void 0 : cat.id) !== null && _b !== void 0 ? _b : null;
|
|
8769
|
+
if (!categoryMap.has(key)) {
|
|
8770
|
+
categoryMap.set(key, { category: cat, items: [], total_amount: 0 });
|
|
8771
|
+
}
|
|
8772
|
+
const group = categoryMap.get(key);
|
|
8773
|
+
group.items.push(cost);
|
|
8774
|
+
group.total_amount += (parseFloat(String(cost.amount)) || 0) * (parseFloat(String(cost.quantity)) || 1);
|
|
8775
|
+
}
|
|
8776
|
+
const grand_total = Array.from(categoryMap.values()).reduce((sum, g) => sum + g.total_amount, 0);
|
|
8777
|
+
return {
|
|
8778
|
+
categories: Array.from(categoryMap.values()).map((g) => ({
|
|
8779
|
+
category: g.category,
|
|
8780
|
+
items: g.items,
|
|
8781
|
+
total_amount: Math.round(g.total_amount * 100) / 100,
|
|
8782
|
+
count: g.items.length,
|
|
8783
|
+
})),
|
|
8784
|
+
grand_total: Math.round(grand_total * 100) / 100,
|
|
8785
|
+
};
|
|
8786
|
+
}
|
|
8787
|
+
async getProjectCost(userId, projectId, id) {
|
|
8788
|
+
const rows = await this.listProjectCosts(userId, projectId, {});
|
|
8789
|
+
const cost = rows.find((r) => r.id === id);
|
|
8790
|
+
if (!cost) {
|
|
8791
|
+
throw new common_1.NotFoundException('Project cost not found.');
|
|
8792
|
+
}
|
|
8793
|
+
return cost;
|
|
8794
|
+
}
|
|
8795
|
+
async getProjectCostsSummary(userId, projectId) {
|
|
8796
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
8797
|
+
await this.getActorContext(userId);
|
|
8798
|
+
const localeId = await this.resolvePreferredLocaleId();
|
|
8799
|
+
// ── 1. Verify project exists and fetch budget_amount ──────────────────
|
|
8800
|
+
const project = await this.querySingle(`SELECT id, budget_amount::text AS "budgetAmount"
|
|
8801
|
+
FROM operations_project
|
|
8802
|
+
WHERE id = $1 AND deleted_at IS NULL
|
|
8803
|
+
LIMIT 1`, [projectId]);
|
|
8804
|
+
if (!project) {
|
|
8805
|
+
throw new common_1.NotFoundException('Project not found.');
|
|
8806
|
+
}
|
|
8807
|
+
const budgetAmount = parseFloat((_a = project.budgetAmount) !== null && _a !== void 0 ? _a : '0') || 0;
|
|
8808
|
+
// ── 2. Aggregated cost totals ─────────────────────────────────────────
|
|
8809
|
+
const totals = await this.querySingle(`SELECT
|
|
8810
|
+
COALESCE(SUM(CASE WHEN status != 'cancelled' THEN amount * quantity ELSE 0 END), 0)::text AS "extraCostTotal",
|
|
8811
|
+
COALESCE(SUM(CASE WHEN status = 'planned' THEN amount * quantity ELSE 0 END), 0)::text AS "plannedTotal",
|
|
8812
|
+
COALESCE(SUM(CASE WHEN status = 'approved' THEN amount * quantity ELSE 0 END), 0)::text AS "approvedTotal",
|
|
8813
|
+
COALESCE(SUM(CASE WHEN status = 'realized' THEN amount * quantity ELSE 0 END), 0)::text AS "realizedTotal",
|
|
8814
|
+
COALESCE(SUM(CASE WHEN status = 'cancelled' THEN amount * quantity ELSE 0 END), 0)::text AS "cancelledTotal",
|
|
8815
|
+
COALESCE(SUM(CASE WHEN is_billable = true AND status != 'cancelled' THEN amount * quantity ELSE 0 END), 0)::text AS "billableTotal",
|
|
8816
|
+
COALESCE(SUM(CASE WHEN is_billable = false AND status != 'cancelled' THEN amount * quantity ELSE 0 END), 0)::text AS "nonBillableTotal",
|
|
8817
|
+
COALESCE(SUM(CASE WHEN is_reimbursable = true AND status != 'cancelled' THEN amount * quantity ELSE 0 END), 0)::text AS "reimbursableTotal"
|
|
8818
|
+
FROM operations_project_cost
|
|
8819
|
+
WHERE deleted_at IS NULL
|
|
8820
|
+
AND project_id = $1`, [projectId]);
|
|
8821
|
+
const extraCostTotal = Math.round((parseFloat((_b = totals === null || totals === void 0 ? void 0 : totals.extraCostTotal) !== null && _b !== void 0 ? _b : '0') || 0) * 100) / 100;
|
|
8822
|
+
const plannedTotal = Math.round((parseFloat((_c = totals === null || totals === void 0 ? void 0 : totals.plannedTotal) !== null && _c !== void 0 ? _c : '0') || 0) * 100) / 100;
|
|
8823
|
+
const approvedTotal = Math.round((parseFloat((_d = totals === null || totals === void 0 ? void 0 : totals.approvedTotal) !== null && _d !== void 0 ? _d : '0') || 0) * 100) / 100;
|
|
8824
|
+
const realizedTotal = Math.round((parseFloat((_e = totals === null || totals === void 0 ? void 0 : totals.realizedTotal) !== null && _e !== void 0 ? _e : '0') || 0) * 100) / 100;
|
|
8825
|
+
const cancelledTotal = Math.round((parseFloat((_f = totals === null || totals === void 0 ? void 0 : totals.cancelledTotal) !== null && _f !== void 0 ? _f : '0') || 0) * 100) / 100;
|
|
8826
|
+
const billableTotal = Math.round((parseFloat((_g = totals === null || totals === void 0 ? void 0 : totals.billableTotal) !== null && _g !== void 0 ? _g : '0') || 0) * 100) / 100;
|
|
8827
|
+
const nonBillableTotal = Math.round((parseFloat((_h = totals === null || totals === void 0 ? void 0 : totals.nonBillableTotal) !== null && _h !== void 0 ? _h : '0') || 0) * 100) / 100;
|
|
8828
|
+
const reimbursableTotal = Math.round((parseFloat((_j = totals === null || totals === void 0 ? void 0 : totals.reimbursableTotal) !== null && _j !== void 0 ? _j : '0') || 0) * 100) / 100;
|
|
8829
|
+
const teamCostTotal = 0;
|
|
8830
|
+
const totalProjectCost = Math.round((teamCostTotal + extraCostTotal) * 100) / 100;
|
|
8831
|
+
const remainingBudget = Math.round((budgetAmount - totalProjectCost) * 100) / 100;
|
|
8832
|
+
const budgetUsagePercent = budgetAmount > 0
|
|
8833
|
+
? Math.round((totalProjectCost / budgetAmount) * 10000) / 100
|
|
8834
|
+
: 0;
|
|
8835
|
+
// ── 3. cost_by_category ───────────────────────────────────────────────
|
|
8836
|
+
const costByCategory = await this.queryRows(`SELECT
|
|
8837
|
+
COALESCE(pc.category_id, pct.category_id) AS "categoryId",
|
|
8838
|
+
pcc.slug AS "categorySlug",
|
|
8839
|
+
COALESCE(pccl.name, pcc.slug) AS "categoryName",
|
|
8840
|
+
pcc.color AS "categoryColor",
|
|
8841
|
+
pcc.icon AS "categoryIcon",
|
|
8842
|
+
SUM(pc.amount * pc.quantity)::text AS total,
|
|
8843
|
+
COUNT(*)::int AS count
|
|
8844
|
+
FROM operations_project_cost pc
|
|
8845
|
+
LEFT JOIN operations_project_cost_type pct
|
|
8846
|
+
ON pct.id = pc.cost_type_id AND pct.deleted_at IS NULL
|
|
8847
|
+
LEFT JOIN operations_project_cost_category pcc
|
|
8848
|
+
ON pcc.id = COALESCE(pc.category_id, pct.category_id) AND pcc.deleted_at IS NULL
|
|
8849
|
+
LEFT JOIN LATERAL (
|
|
8850
|
+
SELECT l.name
|
|
8851
|
+
FROM operations_project_cost_category_locale l
|
|
8852
|
+
WHERE l.operations_project_cost_category_id = pcc.id
|
|
8853
|
+
ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC,
|
|
8854
|
+
l.id ASC
|
|
8855
|
+
LIMIT 1
|
|
8856
|
+
) pccl ON TRUE
|
|
8857
|
+
WHERE pc.deleted_at IS NULL
|
|
8858
|
+
AND pc.project_id = $2
|
|
8859
|
+
AND pc.status != 'cancelled'
|
|
8860
|
+
GROUP BY COALESCE(pc.category_id, pct.category_id), pcc.slug, pcc.color, pcc.icon, pccl.name
|
|
8861
|
+
ORDER BY SUM(pc.amount * pc.quantity) DESC`, [localeId, projectId]);
|
|
8862
|
+
// ── 4. cost_by_type ───────────────────────────────────────────────────
|
|
8863
|
+
const costByType = await this.queryRows(`SELECT
|
|
8864
|
+
pc.cost_type_id AS "costTypeId",
|
|
8865
|
+
pct.slug AS "costTypeSlug",
|
|
8866
|
+
COALESCE(pctl.name, pct.slug) AS "costTypeName",
|
|
8867
|
+
pct.code AS "costTypeCode",
|
|
8868
|
+
SUM(pc.amount * pc.quantity)::text AS total,
|
|
8869
|
+
COUNT(*)::int AS count
|
|
8870
|
+
FROM operations_project_cost pc
|
|
8871
|
+
LEFT JOIN operations_project_cost_type pct
|
|
8872
|
+
ON pct.id = pc.cost_type_id AND pct.deleted_at IS NULL
|
|
8873
|
+
LEFT JOIN LATERAL (
|
|
8874
|
+
SELECT l.name
|
|
8875
|
+
FROM operations_project_cost_type_locale l
|
|
8876
|
+
WHERE l.operations_project_cost_type_id = pct.id
|
|
8877
|
+
ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC,
|
|
8878
|
+
l.id ASC
|
|
8879
|
+
LIMIT 1
|
|
8880
|
+
) pctl ON TRUE
|
|
8881
|
+
WHERE pc.deleted_at IS NULL
|
|
8882
|
+
AND pc.project_id = $2
|
|
8883
|
+
AND pc.status != 'cancelled'
|
|
8884
|
+
GROUP BY pc.cost_type_id, pct.slug, pct.code, pctl.name
|
|
8885
|
+
ORDER BY SUM(pc.amount * pc.quantity) DESC`, [localeId, projectId]);
|
|
8886
|
+
// ── 5. cost_by_month ──────────────────────────────────────────────────
|
|
8887
|
+
const costByMonth = await this.queryRows(`SELECT
|
|
8888
|
+
TO_CHAR(COALESCE(pc.cost_date, pc.created_at), 'YYYY-MM') AS month,
|
|
8889
|
+
SUM(pc.amount * pc.quantity)::text AS total,
|
|
8890
|
+
COUNT(*)::int AS count
|
|
8891
|
+
FROM operations_project_cost pc
|
|
8892
|
+
WHERE pc.deleted_at IS NULL
|
|
8893
|
+
AND pc.project_id = $1
|
|
8894
|
+
AND pc.status != 'cancelled'
|
|
8895
|
+
GROUP BY TO_CHAR(COALESCE(pc.cost_date, pc.created_at), 'YYYY-MM')
|
|
8896
|
+
ORDER BY month ASC`, [projectId]);
|
|
8897
|
+
// ── 6. top_cost_types (top 5) ─────────────────────────────────────────
|
|
8898
|
+
const topCostTypes = costByType.slice(0, 5).map((ct) => {
|
|
8899
|
+
const typeTotal = Math.round((parseFloat(ct.total) || 0) * 100) / 100;
|
|
8900
|
+
const percentage = extraCostTotal > 0
|
|
8901
|
+
? Math.round((typeTotal / extraCostTotal) * 10000) / 100
|
|
8902
|
+
: 0;
|
|
8903
|
+
return {
|
|
8904
|
+
cost_type_id: ct.costTypeId,
|
|
8905
|
+
cost_type_slug: ct.costTypeSlug,
|
|
8906
|
+
cost_type_name: ct.costTypeName,
|
|
8907
|
+
cost_type_code: ct.costTypeCode,
|
|
8908
|
+
total: typeTotal,
|
|
8909
|
+
percentage,
|
|
8910
|
+
};
|
|
8911
|
+
});
|
|
8912
|
+
return {
|
|
8913
|
+
project_id: projectId,
|
|
8914
|
+
budget_amount: budgetAmount,
|
|
8915
|
+
team_cost_total: teamCostTotal,
|
|
8916
|
+
extra_cost_total: extraCostTotal,
|
|
8917
|
+
total_project_cost: totalProjectCost,
|
|
8918
|
+
remaining_budget: remainingBudget,
|
|
8919
|
+
budget_usage_percent: budgetUsagePercent,
|
|
8920
|
+
planned_total: plannedTotal,
|
|
8921
|
+
approved_total: approvedTotal,
|
|
8922
|
+
realized_total: realizedTotal,
|
|
8923
|
+
cancelled_total: cancelledTotal,
|
|
8924
|
+
billable_total: billableTotal,
|
|
8925
|
+
non_billable_total: nonBillableTotal,
|
|
8926
|
+
reimbursable_total: reimbursableTotal,
|
|
8927
|
+
cost_by_category: costByCategory.map((c) => ({
|
|
8928
|
+
category_id: c.categoryId,
|
|
8929
|
+
category_slug: c.categorySlug,
|
|
8930
|
+
category_name: c.categoryName,
|
|
8931
|
+
category_color: c.categoryColor,
|
|
8932
|
+
category_icon: c.categoryIcon,
|
|
8933
|
+
total: Math.round((parseFloat(c.total) || 0) * 100) / 100,
|
|
8934
|
+
count: Number(c.count),
|
|
8935
|
+
})),
|
|
8936
|
+
cost_by_type: costByType.map((t) => ({
|
|
8937
|
+
cost_type_id: t.costTypeId,
|
|
8938
|
+
cost_type_slug: t.costTypeSlug,
|
|
8939
|
+
cost_type_name: t.costTypeName,
|
|
8940
|
+
cost_type_code: t.costTypeCode,
|
|
8941
|
+
total: Math.round((parseFloat(t.total) || 0) * 100) / 100,
|
|
8942
|
+
count: Number(t.count),
|
|
8943
|
+
})),
|
|
8944
|
+
cost_by_month: costByMonth.map((m) => ({
|
|
8945
|
+
month: m.month,
|
|
8946
|
+
total: Math.round((parseFloat(m.total) || 0) * 100) / 100,
|
|
8947
|
+
count: Number(m.count),
|
|
8948
|
+
})),
|
|
8949
|
+
top_cost_types: topCostTypes,
|
|
8950
|
+
};
|
|
8951
|
+
}
|
|
8952
|
+
async getProjectCostReport(userId, projectId, filters) {
|
|
8953
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
8954
|
+
await this.getActorContext(userId);
|
|
8955
|
+
const localeId = await this.resolvePreferredLocaleId();
|
|
8956
|
+
// ── Verify project ───────────────────────────────────────────────────
|
|
8957
|
+
const project = await this.querySingle(`SELECT id, budget_amount::text AS "budgetAmount"
|
|
8958
|
+
FROM operations_project
|
|
8959
|
+
WHERE id = $1 AND deleted_at IS NULL
|
|
8960
|
+
LIMIT 1`, [projectId]);
|
|
8961
|
+
if (!project) {
|
|
8962
|
+
throw new common_1.NotFoundException('Project not found.');
|
|
8963
|
+
}
|
|
8964
|
+
const budgetAmount = parseFloat((_a = project.budgetAmount) !== null && _a !== void 0 ? _a : '0') || 0;
|
|
8965
|
+
// ── Build dynamic WHERE clause ────────────────────────────────────────
|
|
8966
|
+
const conditions = [
|
|
8967
|
+
'pc.deleted_at IS NULL',
|
|
8968
|
+
'pc.project_id = $1',
|
|
8969
|
+
];
|
|
8970
|
+
const params = [projectId];
|
|
8971
|
+
if (filters.date_from) {
|
|
8972
|
+
params.push(filters.date_from);
|
|
8973
|
+
conditions.push(`COALESCE(pc.cost_date, pc.created_at::date) >= $${params.length}::date`);
|
|
8974
|
+
}
|
|
8975
|
+
if (filters.date_to) {
|
|
8976
|
+
params.push(filters.date_to);
|
|
8977
|
+
conditions.push(`COALESCE(pc.cost_date, pc.created_at::date) <= $${params.length}::date`);
|
|
8978
|
+
}
|
|
8979
|
+
if (filters.category_id !== undefined) {
|
|
8980
|
+
params.push(filters.category_id);
|
|
8981
|
+
conditions.push(`(pc.category_id = $${params.length} OR (pc.category_id IS NULL AND EXISTS (
|
|
8982
|
+
SELECT 1 FROM operations_project_cost_type pct2
|
|
8983
|
+
WHERE pct2.id = pc.cost_type_id AND pct2.category_id = $${params.length} AND pct2.deleted_at IS NULL
|
|
8984
|
+
)))`);
|
|
8985
|
+
}
|
|
8986
|
+
if (filters.cost_type_id !== undefined) {
|
|
8987
|
+
params.push(filters.cost_type_id);
|
|
8988
|
+
conditions.push(`pc.cost_type_id = $${params.length}`);
|
|
8989
|
+
}
|
|
8990
|
+
if (filters.status !== undefined) {
|
|
8991
|
+
params.push(filters.status);
|
|
8992
|
+
conditions.push(`pc.status = $${params.length}`);
|
|
8993
|
+
}
|
|
8994
|
+
if (filters.is_billable !== undefined) {
|
|
8995
|
+
params.push(filters.is_billable);
|
|
8996
|
+
conditions.push(`pc.is_billable = $${params.length}`);
|
|
8997
|
+
}
|
|
8998
|
+
if (filters.is_reimbursable !== undefined) {
|
|
8999
|
+
params.push(filters.is_reimbursable);
|
|
9000
|
+
conditions.push(`pc.is_reimbursable = $${params.length}`);
|
|
9001
|
+
}
|
|
9002
|
+
const whereClause = conditions.join(' AND ');
|
|
9003
|
+
// ── Totals ────────────────────────────────────────────────────────────
|
|
9004
|
+
const totals = await this.querySingle(`SELECT
|
|
9005
|
+
COALESCE(SUM(pc.amount * pc.quantity), 0)::text AS "grandTotal",
|
|
9006
|
+
COALESCE(SUM(CASE WHEN pc.status = 'planned' THEN pc.amount * pc.quantity ELSE 0 END), 0)::text AS "plannedTotal",
|
|
9007
|
+
COALESCE(SUM(CASE WHEN pc.status = 'approved' THEN pc.amount * pc.quantity ELSE 0 END), 0)::text AS "approvedTotal",
|
|
9008
|
+
COALESCE(SUM(CASE WHEN pc.status = 'realized' THEN pc.amount * pc.quantity ELSE 0 END), 0)::text AS "realizedTotal",
|
|
9009
|
+
COALESCE(SUM(CASE WHEN pc.status = 'cancelled' THEN pc.amount * pc.quantity ELSE 0 END), 0)::text AS "cancelledTotal",
|
|
9010
|
+
COALESCE(SUM(CASE WHEN pc.is_billable = true THEN pc.amount * pc.quantity ELSE 0 END), 0)::text AS "billableTotal",
|
|
9011
|
+
COALESCE(SUM(CASE WHEN pc.is_billable = false THEN pc.amount * pc.quantity ELSE 0 END), 0)::text AS "nonBillableTotal",
|
|
9012
|
+
COALESCE(SUM(CASE WHEN pc.is_reimbursable = true THEN pc.amount * pc.quantity ELSE 0 END), 0)::text AS "reimbursableTotal",
|
|
9013
|
+
COUNT(*)::int AS "totalCount"
|
|
9014
|
+
FROM operations_project_cost pc
|
|
9015
|
+
WHERE ${whereClause}`, params);
|
|
9016
|
+
const round2 = (v) => Math.round((parseFloat(v !== null && v !== void 0 ? v : '0') || 0) * 100) / 100;
|
|
9017
|
+
const grandTotal = round2(totals === null || totals === void 0 ? void 0 : totals.grandTotal);
|
|
9018
|
+
const plannedTotal = round2(totals === null || totals === void 0 ? void 0 : totals.plannedTotal);
|
|
9019
|
+
const approvedTotal = round2(totals === null || totals === void 0 ? void 0 : totals.approvedTotal);
|
|
9020
|
+
const realizedTotal = round2(totals === null || totals === void 0 ? void 0 : totals.realizedTotal);
|
|
9021
|
+
const cancelledTotal = round2(totals === null || totals === void 0 ? void 0 : totals.cancelledTotal);
|
|
9022
|
+
const billableTotal = round2(totals === null || totals === void 0 ? void 0 : totals.billableTotal);
|
|
9023
|
+
const nonBillableTotal = round2(totals === null || totals === void 0 ? void 0 : totals.nonBillableTotal);
|
|
9024
|
+
const reimbursableTotal = round2(totals === null || totals === void 0 ? void 0 : totals.reimbursableTotal);
|
|
9025
|
+
// ── By category ───────────────────────────────────────────────────────
|
|
9026
|
+
const costByCategory = await this.queryRows(`SELECT
|
|
9027
|
+
COALESCE(pc.category_id, pct.category_id) AS "categoryId",
|
|
9028
|
+
pcc.slug AS "categorySlug",
|
|
9029
|
+
COALESCE(pccl.name, pcc.slug) AS "categoryName",
|
|
9030
|
+
pcc.color AS "categoryColor",
|
|
9031
|
+
pcc.icon AS "categoryIcon",
|
|
9032
|
+
SUM(pc.amount * pc.quantity)::text AS total,
|
|
9033
|
+
COUNT(*)::int AS count,
|
|
9034
|
+
COALESCE(SUM(CASE WHEN pc.status='planned' THEN pc.amount*pc.quantity ELSE 0 END),0)::text AS "plannedSubtotal",
|
|
9035
|
+
COALESCE(SUM(CASE WHEN pc.status='realized' THEN pc.amount*pc.quantity ELSE 0 END),0)::text AS "realizedSubtotal"
|
|
9036
|
+
FROM operations_project_cost pc
|
|
9037
|
+
LEFT JOIN operations_project_cost_type pct
|
|
9038
|
+
ON pct.id = pc.cost_type_id AND pct.deleted_at IS NULL
|
|
9039
|
+
LEFT JOIN operations_project_cost_category pcc
|
|
9040
|
+
ON pcc.id = COALESCE(pc.category_id, pct.category_id) AND pcc.deleted_at IS NULL
|
|
9041
|
+
LEFT JOIN LATERAL (
|
|
9042
|
+
SELECT l.name
|
|
9043
|
+
FROM operations_project_cost_category_locale l
|
|
9044
|
+
WHERE l.operations_project_cost_category_id = pcc.id
|
|
9045
|
+
ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC, l.id ASC
|
|
9046
|
+
LIMIT 1
|
|
9047
|
+
) pccl ON TRUE
|
|
9048
|
+
WHERE ${whereClause.replace(/\$(\d+)/g, (m, n) => '$' + (Number(n) + 1))}
|
|
9049
|
+
GROUP BY COALESCE(pc.category_id, pct.category_id), pcc.slug, pcc.color, pcc.icon, pccl.name
|
|
9050
|
+
ORDER BY SUM(pc.amount * pc.quantity) DESC`, [localeId, ...params]);
|
|
9051
|
+
// ── By type ───────────────────────────────────────────────────────────
|
|
9052
|
+
const costByType = await this.queryRows(`SELECT
|
|
9053
|
+
pc.cost_type_id AS "costTypeId",
|
|
9054
|
+
pct.slug AS "costTypeSlug",
|
|
9055
|
+
COALESCE(pctl.name, pct.slug) AS "costTypeName",
|
|
9056
|
+
pct.code AS "costTypeCode",
|
|
9057
|
+
SUM(pc.amount * pc.quantity)::text AS total,
|
|
9058
|
+
COUNT(*)::int AS count,
|
|
9059
|
+
COALESCE(SUM(CASE WHEN pc.status='planned' THEN pc.amount*pc.quantity ELSE 0 END),0)::text AS "plannedSubtotal",
|
|
9060
|
+
COALESCE(SUM(CASE WHEN pc.status='realized' THEN pc.amount*pc.quantity ELSE 0 END),0)::text AS "realizedSubtotal"
|
|
9061
|
+
FROM operations_project_cost pc
|
|
9062
|
+
LEFT JOIN operations_project_cost_type pct
|
|
9063
|
+
ON pct.id = pc.cost_type_id AND pct.deleted_at IS NULL
|
|
9064
|
+
LEFT JOIN LATERAL (
|
|
9065
|
+
SELECT l.name
|
|
9066
|
+
FROM operations_project_cost_type_locale l
|
|
9067
|
+
WHERE l.operations_project_cost_type_id = pct.id
|
|
9068
|
+
ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC, l.id ASC
|
|
9069
|
+
LIMIT 1
|
|
9070
|
+
) pctl ON TRUE
|
|
9071
|
+
WHERE ${whereClause.replace(/\$(\d+)/g, (m, n) => '$' + (Number(n) + 1))}
|
|
9072
|
+
GROUP BY pc.cost_type_id, pct.slug, pct.code, pctl.name
|
|
9073
|
+
ORDER BY SUM(pc.amount * pc.quantity) DESC`, [localeId, ...params]);
|
|
9074
|
+
// ── By month ──────────────────────────────────────────────────────────
|
|
9075
|
+
const costByMonth = await this.queryRows(`SELECT
|
|
9076
|
+
TO_CHAR(COALESCE(pc.cost_date, pc.created_at::date), 'YYYY-MM') AS month,
|
|
9077
|
+
SUM(pc.amount * pc.quantity)::text AS total,
|
|
9078
|
+
COALESCE(SUM(CASE WHEN pc.status='planned' THEN pc.amount*pc.quantity ELSE 0 END),0)::text AS "plannedSubtotal",
|
|
9079
|
+
COALESCE(SUM(CASE WHEN pc.status='realized' THEN pc.amount*pc.quantity ELSE 0 END),0)::text AS "realizedSubtotal",
|
|
9080
|
+
COUNT(*)::int AS count
|
|
9081
|
+
FROM operations_project_cost pc
|
|
9082
|
+
WHERE ${whereClause}
|
|
9083
|
+
GROUP BY TO_CHAR(COALESCE(pc.cost_date, pc.created_at::date), 'YYYY-MM')
|
|
9084
|
+
ORDER BY month ASC`, params);
|
|
9085
|
+
// ── Top 5 individual costs ────────────────────────────────────────────
|
|
9086
|
+
const top5Costs = await this.queryRows(`SELECT
|
|
9087
|
+
pc.id,
|
|
9088
|
+
pc.description,
|
|
9089
|
+
pc.amount::text,
|
|
9090
|
+
pc.quantity::text,
|
|
9091
|
+
pc.status,
|
|
9092
|
+
pc.cost_date AS "costDate",
|
|
9093
|
+
COALESCE(pctl.name, pct.slug) AS "costTypeName",
|
|
9094
|
+
COALESCE(pccl.name, pcc.slug) AS "categoryName",
|
|
9095
|
+
pcc.color AS "categoryColor"
|
|
9096
|
+
FROM operations_project_cost pc
|
|
9097
|
+
LEFT JOIN operations_project_cost_type pct
|
|
9098
|
+
ON pct.id = pc.cost_type_id AND pct.deleted_at IS NULL
|
|
9099
|
+
LEFT JOIN LATERAL (
|
|
9100
|
+
SELECT l.name
|
|
9101
|
+
FROM operations_project_cost_type_locale l
|
|
9102
|
+
WHERE l.operations_project_cost_type_id = pct.id
|
|
9103
|
+
ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC, l.id ASC
|
|
9104
|
+
LIMIT 1
|
|
9105
|
+
) pctl ON TRUE
|
|
9106
|
+
LEFT JOIN operations_project_cost_category pcc
|
|
9107
|
+
ON pcc.id = COALESCE(pc.category_id, pct.category_id) AND pcc.deleted_at IS NULL
|
|
9108
|
+
LEFT JOIN LATERAL (
|
|
9109
|
+
SELECT l.name
|
|
9110
|
+
FROM operations_project_cost_category_locale l
|
|
9111
|
+
WHERE l.operations_project_cost_category_id = pcc.id
|
|
9112
|
+
ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC, l.id ASC
|
|
9113
|
+
LIMIT 1
|
|
9114
|
+
) pccl ON TRUE
|
|
9115
|
+
WHERE ${whereClause.replace(/\$(\d+)/g, (m, n) => '$' + (Number(n) + 1))}
|
|
9116
|
+
ORDER BY (pc.amount * pc.quantity) DESC
|
|
9117
|
+
LIMIT 5`, [localeId, ...params]);
|
|
9118
|
+
// ── Detailed list ─────────────────────────────────────────────────────
|
|
9119
|
+
const detailedList = await this.queryRows(`SELECT
|
|
9120
|
+
pc.id,
|
|
9121
|
+
pc.description,
|
|
9122
|
+
pc.amount::text,
|
|
9123
|
+
pc.quantity::text,
|
|
9124
|
+
pc.unit_amount::text AS "unitAmount",
|
|
9125
|
+
pc.currency,
|
|
9126
|
+
pc.calculation_type AS "calculationType",
|
|
9127
|
+
pc.recurrence_type AS "recurrenceType",
|
|
9128
|
+
pc.status,
|
|
9129
|
+
pc.is_billable AS "isBillable",
|
|
9130
|
+
pc.is_reimbursable AS "isReimbursable",
|
|
9131
|
+
pc.cost_date AS "costDate",
|
|
9132
|
+
pc.period_start AS "periodStart",
|
|
9133
|
+
pc.period_end AS "periodEnd",
|
|
9134
|
+
pc.notes,
|
|
9135
|
+
pc.cost_type_id AS "costTypeId",
|
|
9136
|
+
COALESCE(pctl.name, pct.slug) AS "costTypeName",
|
|
9137
|
+
pct.code AS "costTypeCode",
|
|
9138
|
+
COALESCE(pc.category_id, pct.category_id) AS "categoryId",
|
|
9139
|
+
COALESCE(pccl.name, pcc.slug) AS "categoryName",
|
|
9140
|
+
pcc.color AS "categoryColor",
|
|
9141
|
+
pc.created_at::text AS "createdAt"
|
|
9142
|
+
FROM operations_project_cost pc
|
|
9143
|
+
LEFT JOIN operations_project_cost_type pct
|
|
9144
|
+
ON pct.id = pc.cost_type_id AND pct.deleted_at IS NULL
|
|
9145
|
+
LEFT JOIN LATERAL (
|
|
9146
|
+
SELECT l.name
|
|
9147
|
+
FROM operations_project_cost_type_locale l
|
|
9148
|
+
WHERE l.operations_project_cost_type_id = pct.id
|
|
9149
|
+
ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC, l.id ASC
|
|
9150
|
+
LIMIT 1
|
|
9151
|
+
) pctl ON TRUE
|
|
9152
|
+
LEFT JOIN operations_project_cost_category pcc
|
|
9153
|
+
ON pcc.id = COALESCE(pc.category_id, pct.category_id) AND pcc.deleted_at IS NULL
|
|
9154
|
+
LEFT JOIN LATERAL (
|
|
9155
|
+
SELECT l.name
|
|
9156
|
+
FROM operations_project_cost_category_locale l
|
|
9157
|
+
WHERE l.operations_project_cost_category_id = pcc.id
|
|
9158
|
+
ORDER BY CASE WHEN $1::int IS NOT NULL AND l.locale_id = $1 THEN 0 ELSE 1 END ASC, l.id ASC
|
|
9159
|
+
LIMIT 1
|
|
9160
|
+
) pccl ON TRUE
|
|
9161
|
+
WHERE ${whereClause.replace(/\$(\d+)/g, (m, n) => '$' + (Number(n) + 1))}
|
|
9162
|
+
ORDER BY (pc.amount * pc.quantity) DESC, pc.cost_date DESC NULLS LAST`, [localeId, ...params]);
|
|
9163
|
+
return {
|
|
9164
|
+
project_id: projectId,
|
|
9165
|
+
budget_amount: budgetAmount,
|
|
9166
|
+
filters_applied: {
|
|
9167
|
+
date_from: (_b = filters.date_from) !== null && _b !== void 0 ? _b : null,
|
|
9168
|
+
date_to: (_c = filters.date_to) !== null && _c !== void 0 ? _c : null,
|
|
9169
|
+
category_id: (_d = filters.category_id) !== null && _d !== void 0 ? _d : null,
|
|
9170
|
+
cost_type_id: (_e = filters.cost_type_id) !== null && _e !== void 0 ? _e : null,
|
|
9171
|
+
status: (_f = filters.status) !== null && _f !== void 0 ? _f : null,
|
|
9172
|
+
is_billable: (_g = filters.is_billable) !== null && _g !== void 0 ? _g : null,
|
|
9173
|
+
is_reimbursable: (_h = filters.is_reimbursable) !== null && _h !== void 0 ? _h : null,
|
|
9174
|
+
},
|
|
9175
|
+
totals: {
|
|
9176
|
+
grand_total: grandTotal,
|
|
9177
|
+
planned_total: plannedTotal,
|
|
9178
|
+
approved_total: approvedTotal,
|
|
9179
|
+
realized_total: realizedTotal,
|
|
9180
|
+
cancelled_total: cancelledTotal,
|
|
9181
|
+
billable_total: billableTotal,
|
|
9182
|
+
non_billable_total: nonBillableTotal,
|
|
9183
|
+
reimbursable_total: reimbursableTotal,
|
|
9184
|
+
total_count: Number((_j = totals === null || totals === void 0 ? void 0 : totals.totalCount) !== null && _j !== void 0 ? _j : 0),
|
|
9185
|
+
},
|
|
9186
|
+
cost_by_category: costByCategory.map((c) => ({
|
|
9187
|
+
category_id: c.categoryId,
|
|
9188
|
+
category_slug: c.categorySlug,
|
|
9189
|
+
category_name: c.categoryName,
|
|
9190
|
+
category_color: c.categoryColor,
|
|
9191
|
+
category_icon: c.categoryIcon,
|
|
9192
|
+
total: round2(c.total),
|
|
9193
|
+
count: Number(c.count),
|
|
9194
|
+
planned_subtotal: round2(c.plannedSubtotal),
|
|
9195
|
+
realized_subtotal: round2(c.realizedSubtotal),
|
|
9196
|
+
})),
|
|
9197
|
+
cost_by_type: costByType.map((t) => ({
|
|
9198
|
+
cost_type_id: t.costTypeId,
|
|
9199
|
+
cost_type_slug: t.costTypeSlug,
|
|
9200
|
+
cost_type_name: t.costTypeName,
|
|
9201
|
+
cost_type_code: t.costTypeCode,
|
|
9202
|
+
total: round2(t.total),
|
|
9203
|
+
count: Number(t.count),
|
|
9204
|
+
planned_subtotal: round2(t.plannedSubtotal),
|
|
9205
|
+
realized_subtotal: round2(t.realizedSubtotal),
|
|
9206
|
+
})),
|
|
9207
|
+
cost_by_month: costByMonth.map((m) => ({
|
|
9208
|
+
month: m.month,
|
|
9209
|
+
total: round2(m.total),
|
|
9210
|
+
planned_subtotal: round2(m.plannedSubtotal),
|
|
9211
|
+
realized_subtotal: round2(m.realizedSubtotal),
|
|
9212
|
+
count: Number(m.count),
|
|
9213
|
+
})),
|
|
9214
|
+
top_5_costs: top5Costs.map((c) => ({
|
|
9215
|
+
id: c.id,
|
|
9216
|
+
description: c.description,
|
|
9217
|
+
total: round2(String(parseFloat(c.amount) * parseFloat(c.quantity))),
|
|
9218
|
+
amount: round2(c.amount),
|
|
9219
|
+
quantity: parseFloat(c.quantity),
|
|
9220
|
+
status: c.status,
|
|
9221
|
+
cost_type_name: c.costTypeName,
|
|
9222
|
+
category_name: c.categoryName,
|
|
9223
|
+
category_color: c.categoryColor,
|
|
9224
|
+
cost_date: c.costDate,
|
|
9225
|
+
})),
|
|
9226
|
+
detailed_list: detailedList.map((c) => ({
|
|
9227
|
+
id: c.id,
|
|
9228
|
+
description: c.description,
|
|
9229
|
+
amount: round2(c.amount),
|
|
9230
|
+
quantity: parseFloat(c.quantity),
|
|
9231
|
+
unit_amount: c.unitAmount ? round2(c.unitAmount) : null,
|
|
9232
|
+
total: round2(String(parseFloat(c.amount) * parseFloat(c.quantity))),
|
|
9233
|
+
currency: c.currency,
|
|
9234
|
+
calculation_type: c.calculationType,
|
|
9235
|
+
recurrence_type: c.recurrenceType,
|
|
9236
|
+
status: c.status,
|
|
9237
|
+
is_billable: c.isBillable,
|
|
9238
|
+
is_reimbursable: c.isReimbursable,
|
|
9239
|
+
cost_date: c.costDate,
|
|
9240
|
+
period_start: c.periodStart,
|
|
9241
|
+
period_end: c.periodEnd,
|
|
9242
|
+
notes: c.notes,
|
|
9243
|
+
cost_type_id: c.costTypeId,
|
|
9244
|
+
cost_type_name: c.costTypeName,
|
|
9245
|
+
cost_type_code: c.costTypeCode,
|
|
9246
|
+
category_id: c.categoryId,
|
|
9247
|
+
category_name: c.categoryName,
|
|
9248
|
+
category_color: c.categoryColor,
|
|
9249
|
+
created_at: c.createdAt,
|
|
9250
|
+
})),
|
|
9251
|
+
};
|
|
9252
|
+
}
|
|
9253
|
+
async createProjectCost(userId, projectId, data) {
|
|
9254
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
|
|
9255
|
+
const actor = await this.getActorContext(userId);
|
|
9256
|
+
this.ensureSupervisor(actor);
|
|
9257
|
+
const project = await this.querySingle(`SELECT id FROM operations_project WHERE id = $1 AND deleted_at IS NULL LIMIT 1`, [projectId]);
|
|
9258
|
+
if (!project) {
|
|
9259
|
+
throw new common_1.NotFoundException('Project not found.');
|
|
9260
|
+
}
|
|
9261
|
+
if (data.cost_type_id) {
|
|
9262
|
+
const costType = await this.querySingle(`SELECT id FROM operations_project_cost_type WHERE id = $1 AND deleted_at IS NULL LIMIT 1`, [data.cost_type_id]);
|
|
9263
|
+
if (!costType) {
|
|
9264
|
+
throw new common_1.BadRequestException(`Cost type with id ${data.cost_type_id} not found.`);
|
|
9265
|
+
}
|
|
9266
|
+
}
|
|
9267
|
+
if (data.category_id) {
|
|
9268
|
+
const category = await this.querySingle(`SELECT id FROM operations_project_cost_category WHERE id = $1 AND deleted_at IS NULL LIMIT 1`, [data.category_id]);
|
|
9269
|
+
if (!category) {
|
|
9270
|
+
throw new common_1.BadRequestException(`Cost category with id ${data.category_id} not found.`);
|
|
9271
|
+
}
|
|
9272
|
+
}
|
|
9273
|
+
const calcType = (_a = data.calculation_type) !== null && _a !== void 0 ? _a : 'fixed';
|
|
9274
|
+
let effectiveAmount = data.amount;
|
|
9275
|
+
if (['unit', 'hourly', 'monthly'].includes(calcType) && data.unit_amount !== undefined && data.unit_amount !== null) {
|
|
9276
|
+
const qty = (_b = data.quantity) !== null && _b !== void 0 ? _b : 1;
|
|
9277
|
+
effectiveAmount = Math.round(qty * data.unit_amount * 100) / 100;
|
|
9278
|
+
}
|
|
9279
|
+
const created = await this.querySingle(`INSERT INTO operations_project_cost
|
|
9280
|
+
(project_id, cost_type_id, category_id, description, amount, quantity, unit_amount, currency, cost_date, period_start, period_end, calculation_type, recurrence_type, is_billable, is_reimbursable, notes, status, created_at, updated_at)
|
|
9281
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::date, $10::date, $11::date, $12::operations_project_cost_calculation_type_134cdfb49c_enum, $13::operations_project_cost_recurrence_type_09baf0f043_enum, $14, $15, $16, $17::operations_project_cost_status_153e8592ce_enum, NOW(), NOW())
|
|
9282
|
+
RETURNING id`, [
|
|
9283
|
+
projectId,
|
|
9284
|
+
(_c = data.cost_type_id) !== null && _c !== void 0 ? _c : null,
|
|
9285
|
+
(_d = data.category_id) !== null && _d !== void 0 ? _d : null,
|
|
9286
|
+
(_e = data.description) !== null && _e !== void 0 ? _e : null,
|
|
9287
|
+
effectiveAmount,
|
|
9288
|
+
(_f = data.quantity) !== null && _f !== void 0 ? _f : 1,
|
|
9289
|
+
(_g = data.unit_amount) !== null && _g !== void 0 ? _g : null,
|
|
9290
|
+
(_h = data.currency) !== null && _h !== void 0 ? _h : 'BRL',
|
|
9291
|
+
(_j = data.cost_date) !== null && _j !== void 0 ? _j : null,
|
|
9292
|
+
(_k = data.period_start) !== null && _k !== void 0 ? _k : null,
|
|
9293
|
+
(_l = data.period_end) !== null && _l !== void 0 ? _l : null,
|
|
9294
|
+
calcType,
|
|
9295
|
+
(_m = data.recurrence_type) !== null && _m !== void 0 ? _m : 'none',
|
|
9296
|
+
(_o = data.is_billable) !== null && _o !== void 0 ? _o : false,
|
|
9297
|
+
(_p = data.is_reimbursable) !== null && _p !== void 0 ? _p : false,
|
|
9298
|
+
(_q = data.notes) !== null && _q !== void 0 ? _q : null,
|
|
9299
|
+
(_r = data.status) !== null && _r !== void 0 ? _r : 'planned',
|
|
9300
|
+
]);
|
|
9301
|
+
if (!(created === null || created === void 0 ? void 0 : created.id)) {
|
|
9302
|
+
throw new common_1.BadRequestException('Unable to create project cost.');
|
|
9303
|
+
}
|
|
9304
|
+
const rows = await this.listProjectCosts(userId, projectId, {});
|
|
9305
|
+
return (_s = rows.find((r) => r.id === created.id)) !== null && _s !== void 0 ? _s : null;
|
|
9306
|
+
}
|
|
9307
|
+
async updateProjectCost(userId, id, data) {
|
|
9308
|
+
var _a, _b, _c;
|
|
9309
|
+
const actor = await this.getActorContext(userId);
|
|
9310
|
+
this.ensureSupervisor(actor);
|
|
9311
|
+
const cost = await this.querySingle(`SELECT id, project_id AS "projectId", calculation_type AS "calculationType", unit_amount::text AS "unitAmount", quantity::text AS quantity FROM operations_project_cost WHERE id = $1 AND deleted_at IS NULL LIMIT 1`, [id]);
|
|
9312
|
+
if (!cost) {
|
|
9313
|
+
throw new common_1.NotFoundException('Project cost not found.');
|
|
9314
|
+
}
|
|
9315
|
+
// Auto-calculate amount when applicable
|
|
9316
|
+
const effectiveCalcType = (_a = data.calculation_type) !== null && _a !== void 0 ? _a : cost.calculationType;
|
|
9317
|
+
if (['unit', 'hourly', 'monthly'].includes(effectiveCalcType)) {
|
|
9318
|
+
const ua = data.unit_amount !== undefined ? data.unit_amount : (cost.unitAmount !== null ? parseFloat(cost.unitAmount) : null);
|
|
9319
|
+
const qty = data.quantity !== undefined ? data.quantity : parseFloat(cost.quantity);
|
|
9320
|
+
if (ua !== null && ua !== undefined) {
|
|
9321
|
+
data = Object.assign(Object.assign({}, data), { amount: Math.round(qty * ua * 100) / 100 });
|
|
9322
|
+
}
|
|
9323
|
+
}
|
|
9324
|
+
const sets = [];
|
|
9325
|
+
const params = [];
|
|
9326
|
+
if (data.cost_type_id !== undefined)
|
|
9327
|
+
sets.push(`cost_type_id = ${this.param(params, data.cost_type_id)}`);
|
|
9328
|
+
if (data.category_id !== undefined)
|
|
9329
|
+
sets.push(`category_id = ${this.param(params, data.category_id)}`);
|
|
9330
|
+
if (data.description !== undefined)
|
|
9331
|
+
sets.push(`description = ${this.param(params, data.description)}`);
|
|
9332
|
+
if (data.amount !== undefined)
|
|
9333
|
+
sets.push(`amount = ${this.param(params, data.amount)}`);
|
|
9334
|
+
if (data.currency !== undefined)
|
|
9335
|
+
sets.push(`currency = ${this.param(params, data.currency)}`);
|
|
9336
|
+
if (data.quantity !== undefined)
|
|
9337
|
+
sets.push(`quantity = ${this.param(params, data.quantity)}`);
|
|
9338
|
+
if (data.unit_amount !== undefined)
|
|
9339
|
+
sets.push(`unit_amount = ${this.param(params, data.unit_amount)}`);
|
|
9340
|
+
if (data.calculation_type !== undefined)
|
|
9341
|
+
sets.push(`calculation_type = ${this.param(params, data.calculation_type)}::operations_project_cost_calculation_type_134cdfb49c_enum`);
|
|
9342
|
+
if (data.recurrence_type !== undefined)
|
|
9343
|
+
sets.push(`recurrence_type = ${this.param(params, data.recurrence_type)}::operations_project_cost_recurrence_type_09baf0f043_enum`);
|
|
9344
|
+
if (data.is_billable !== undefined)
|
|
9345
|
+
sets.push(`is_billable = ${this.param(params, data.is_billable)}`);
|
|
9346
|
+
if (data.is_reimbursable !== undefined)
|
|
9347
|
+
sets.push(`is_reimbursable = ${this.param(params, data.is_reimbursable)}`);
|
|
9348
|
+
if (data.cost_date !== undefined)
|
|
9349
|
+
sets.push(`cost_date = ${this.param(params, data.cost_date)}::date`);
|
|
9350
|
+
if (data.period_start !== undefined)
|
|
9351
|
+
sets.push(`period_start = ${this.param(params, data.period_start)}::date`);
|
|
9352
|
+
if (data.period_end !== undefined)
|
|
9353
|
+
sets.push(`period_end = ${this.param(params, data.period_end)}::date`);
|
|
9354
|
+
if (data.notes !== undefined)
|
|
9355
|
+
sets.push(`notes = ${this.param(params, data.notes)}`);
|
|
9356
|
+
if (data.status !== undefined)
|
|
9357
|
+
sets.push(`status = ${this.param(params, data.status)}::operations_project_cost_status_153e8592ce_enum`);
|
|
9358
|
+
if (sets.length === 0) {
|
|
9359
|
+
const rows = await this.listProjectCosts(userId, cost.projectId, {});
|
|
9360
|
+
return (_b = rows.find((r) => r.id === id)) !== null && _b !== void 0 ? _b : null;
|
|
9361
|
+
}
|
|
9362
|
+
sets.push(`updated_at = NOW()`);
|
|
9363
|
+
await this.prisma.$queryRawUnsafe(`UPDATE operations_project_cost SET ${sets.join(', ')} WHERE id = ${this.param(params, id)}`, ...params);
|
|
9364
|
+
const rows = await this.listProjectCosts(userId, cost.projectId, {});
|
|
9365
|
+
return (_c = rows.find((r) => r.id === id)) !== null && _c !== void 0 ? _c : null;
|
|
9366
|
+
}
|
|
9367
|
+
async deleteProjectCost(userId, id) {
|
|
9368
|
+
const actor = await this.getActorContext(userId);
|
|
9369
|
+
this.ensureSupervisor(actor);
|
|
9370
|
+
const cost = await this.querySingle(`SELECT id FROM operations_project_cost WHERE id = $1 AND deleted_at IS NULL LIMIT 1`, [id]);
|
|
9371
|
+
if (!cost) {
|
|
9372
|
+
throw new common_1.NotFoundException('Project cost not found.');
|
|
9373
|
+
}
|
|
9374
|
+
await this.prisma.$queryRawUnsafe(`UPDATE operations_project_cost SET deleted_at = NOW() WHERE id = $1`, id);
|
|
9375
|
+
return { success: true };
|
|
9376
|
+
}
|
|
7767
9377
|
};
|
|
7768
9378
|
exports.OperationsService = OperationsService;
|
|
7769
9379
|
exports.OperationsService = OperationsService = OperationsService_1 = __decorate([
|