Skip to content

Commit 24a4b21

Browse files
committed
simple project details export
1 parent 3150a2b commit 24a4b21

File tree

7 files changed

+97
-2
lines changed

7 files changed

+97
-2
lines changed

backend/src/controllers/projects.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,20 @@ export const deleteProject = async (req: ConfacRequest, res: Response) => {
6666
emitEntityEvent(req, SocketEventTypes.EntityDeleted, CollectionNames.PROJECTS, id, null);
6767
return res.send(id);
6868
};
69+
70+
71+
72+
const PROJECTS_EXCEL_HEADERS = [
73+
'Consultant', 'Consultant Type', 'Start datum', 'Eind datum', 'Onderaannemer',
74+
'Uurtarief', 'Dagtarief', 'Klant', 'Klant uurtarief', 'Klant dagtarief',
75+
'Margin', 'Margin %', 'Eindklant', 'Raamcontract', 'Contract werkopdracht',
76+
];
77+
78+
/** Create simple CSV output of the project._ids passed in the body */
79+
export const generateExcelForProjectsController = async (req: Request, res: Response) => {
80+
const separator = ';';
81+
const excelHeader = `${PROJECTS_EXCEL_HEADERS.join(separator)}\r\n`;
82+
const excelBody = req.body.map((record: any) => record.join(separator)).join('\r\n');
83+
const excel = `${excelHeader}${excelBody}`;
84+
return res.send(excel);
85+
};

backend/src/routes/projects.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {Router} from 'express';
2-
import {deleteProject, getProjects, saveProject} from '../controllers/projects';
2+
import {deleteProject, getProjects, saveProject, generateExcelForProjectsController} from '../controllers/projects';
33
import {
44
getProjectsPerMonthController, createProjectsMonthController,
55
patchProjectsMonthController, getProjectsPerMonthOverviewController,
@@ -11,6 +11,7 @@ const projectsRouter = Router();
1111
projectsRouter.get('/', getProjects);
1212
projectsRouter.post('/', saveProject as any);
1313
projectsRouter.delete('/', deleteProject as any);
14+
projectsRouter.post('/excel', generateExcelForProjectsController);
1415

1516
projectsRouter.get('/month', getProjectsPerMonthController);
1617
projectsRouter.get('/month/overview', getProjectsPerMonthOverviewController);

frontend/src/actions/downloadActions.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,20 @@ export function downloadInvoicesExcel(ids: string[]) {
7777
};
7878
}
7979

80+
export function downloadProjectsExcel(ids: any[]) {
81+
return dispatch => {
82+
request.post(buildUrl('/projects/excel'))
83+
.responseType('blob')
84+
.set('Authorization', authService.getBearer())
85+
.send(ids)
86+
.then(res => {
87+
console.log('downloaded', res); // eslint-disable-line
88+
const fileName = `projects-${moment().format('YYYY-MM-DD')}.csv`;
89+
downloadAttachment(fileName, res.body);
90+
});
91+
};
92+
}
93+
8094

8195
export function downloadInvoicesZip(ids: string[]) {
8296
return dispatch => {

frontend/src/components/project/ProjectsList.tsx

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
import {useDispatch, useSelector} from 'react-redux';
22
import {useNavigate} from 'react-router-dom';
33
import {ConfacState} from '../../reducers/app-state';
4-
import {saveProject, updateAppFilters} from '../../actions';
4+
import {downloadProjectsExcel, saveProject, updateAppFilters} from '../../actions';
55
import {ListPage} from '../controls/table/ListPage';
66
import {projectFeature, ProjectFeatureBuilderConfig} from './models/getProjectFeature';
77
import {LinkToButton} from '../controls/form-controls/button/LinkToButton';
88
import {useDocumentTitle} from '../hooks/useDocumentTitle';
99
import {Claim} from '../users/models/UserModel';
1010
import {useProjects} from '../hooks/useProjects';
1111
import {Features} from '../controls/feature/feature-models';
12+
import {t} from '../utils';
13+
import {Button} from '../controls/form-controls/Button';
14+
import {getProjectMarkup} from './utils/getProjectMarkup';
15+
import {getFullTariffs} from './utils/getTariffs';
1216

1317

1418
import './ProjectsList.scss';
@@ -29,8 +33,44 @@ export const ProjectsList = () => {
2933
setFilters: f => dispatch(updateAppFilters(Features.projects, f)),
3034
};
3135

36+
const downloadExcel = () => {
37+
const projectDetails = projects.map(proj => {
38+
const markup = getProjectMarkup(proj.details);
39+
const partnerTariff = getFullTariffs(proj, 'partner');
40+
const clientTariff = getFullTariffs(proj, 'client');
41+
return {
42+
consultant: proj.consultantName,
43+
consultantType: proj.consultant.type,
44+
startDate: proj.details.startDate.format('YYYY-MM-DD'),
45+
endDate: proj.details.endDate && proj.details.endDate.format('YYYY-MM-DD'),
46+
partner: proj.partner?.name,
47+
partnerHourly: partnerTariff?.hourlyRate,
48+
partnerDaily: partnerTariff?.dailyRate,
49+
client: proj.client.name,
50+
clientHourly: clientTariff?.hourlyRate,
51+
clientDaily: clientTariff?.dailyRate,
52+
margin: markup.amount,
53+
marginPercentage: markup.percentage.toFixed(0) + '%',
54+
endCustomer: proj.endCustomer?.name,
55+
contractFramework: proj.client.frameworkAgreement?.status,
56+
contractProject: proj.details.contract?.status,
57+
};
58+
});
59+
console.log('details', projectDetails);
60+
const mappedData = projectDetails.map(Object.values);
61+
console.log('mapped', mappedData);
62+
dispatch(downloadProjectsExcel(mappedData) as any);
63+
};
64+
3265
const TopToolbar = (
3366
<>
67+
<Button
68+
variant="light"
69+
onClick={downloadExcel}
70+
title={t('project.listDownloadExcel')}
71+
icon="fa fa-file-excel"
72+
className="tst-download-excel"
73+
/>
3474
<LinkToButton claim={Claim.ViewConsultants} to="/consultants/projects" label="project.contractCheck" />
3575
<LinkToButton claim={Claim.ViewConsultants} to="/consultants" label="consultant.title" />
3676
</>

frontend/src/components/project/utils/getTariffs.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {EditProjectRateType} from '../../../models';
22
import {DefaultHoursInDay} from '../../client/models/getNewClient';
3+
import {FullProjectModel} from '../models/FullProjectModel';
34
import {ProjectClientModel} from '../models/IProjectModel';
45

56

@@ -9,6 +10,26 @@ export type ProjectClientTariff = {
910
rateType: EditProjectRateType;
1011
}
1112

13+
export function getFullTariffs(project: FullProjectModel, type: 'client' | 'partner') {
14+
const projectClient = type === 'client' ? project.details.client : project.details.partner;
15+
if (!projectClient?.clientId) {
16+
return undefined;
17+
}
18+
19+
const client = type === 'client' ? project.client : project.partner!;
20+
const tariff = getTariffs(projectClient);
21+
if (tariff.rateType === 'daily') {
22+
return {
23+
dailyRate: tariff.tariff,
24+
hourlyRate: tariff.tariff / client.hoursInDay,
25+
};
26+
}
27+
return {
28+
dailyRate: tariff.tariff * client.hoursInDay,
29+
hourlyRate: tariff.tariff,
30+
};
31+
}
32+
1233

1334
export function getTariffs(projectClient: ProjectClientModel): ProjectClientTariff {
1435
return {

frontend/src/trans.en.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,7 @@ export const trans = {
480480
endDate: 'End date',
481481
forEndCustomer: 'Project is at end customer',
482482
comments: 'Comments',
483+
listDownloadExcel: 'Download clients as Excel',
483484
deleteConfirm: {
484485
title: 'Delete project',
485486
content: 'Project will be permanently deleted.',

frontend/src/trans.nl.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,7 @@ export const trans = {
480480
endDate: 'Eind datum',
481481
forEndCustomer: 'Project is bij eindklant',
482482
comments: 'Commentaar',
483+
listDownloadExcel: 'Download klanten als Excel',
483484
deleteConfirm: {
484485
title: 'Project verwijderen',
485486
content: 'Project wordt definitief verwijderd.',

0 commit comments

Comments
 (0)