feat: Add services, company documents, company timesheet export
- Add services table and CRUD endpoints (/api/services) - Add company documents upload/download functionality - Add company timesheet XLSX export endpoint - Remove admin requirement from event routes (all authenticated users can manage events) - Add service validators Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
88
src/controllers/company-document.controller.js
Normal file
88
src/controllers/company-document.controller.js
Normal file
@@ -0,0 +1,88 @@
|
||||
import * as companyDocumentService from '../services/company-document.service.js';
|
||||
|
||||
/**
|
||||
* Get all documents for a company
|
||||
* GET /api/companies/:companyId/documents
|
||||
*/
|
||||
export const getDocuments = async (req, res, next) => {
|
||||
try {
|
||||
const { companyId } = req.params;
|
||||
|
||||
const documents = await companyDocumentService.getDocumentsByCompanyId(companyId);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
count: documents.length,
|
||||
data: documents,
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Upload a document for a company
|
||||
* POST /api/companies/:companyId/documents
|
||||
*/
|
||||
export const uploadDocument = async (req, res, next) => {
|
||||
try {
|
||||
const { companyId } = req.params;
|
||||
const userId = req.userId;
|
||||
const { description } = req.body;
|
||||
|
||||
const document = await companyDocumentService.uploadDocument({
|
||||
companyId,
|
||||
userId,
|
||||
file: req.file,
|
||||
description,
|
||||
});
|
||||
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
data: document,
|
||||
message: 'Dokument bol nahraný',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Download a document
|
||||
* GET /api/companies/:companyId/documents/:docId/download
|
||||
*/
|
||||
export const downloadDocument = async (req, res, next) => {
|
||||
try {
|
||||
const { companyId, docId } = req.params;
|
||||
|
||||
const { filePath, fileName, fileType } = await companyDocumentService.getDocumentForDownload(
|
||||
companyId,
|
||||
docId
|
||||
);
|
||||
|
||||
res.setHeader('Content-Type', fileType);
|
||||
res.setHeader('Content-Disposition', `attachment; filename="${encodeURIComponent(fileName)}"`);
|
||||
res.sendFile(filePath);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete a document
|
||||
* DELETE /api/companies/:companyId/documents/:docId
|
||||
*/
|
||||
export const deleteDocument = async (req, res, next) => {
|
||||
try {
|
||||
const { companyId, docId } = req.params;
|
||||
|
||||
const result = await companyDocumentService.deleteDocument(companyId, docId);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
message: result.message,
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
101
src/controllers/service.controller.js
Normal file
101
src/controllers/service.controller.js
Normal file
@@ -0,0 +1,101 @@
|
||||
import * as serviceService from '../services/service.service.js';
|
||||
|
||||
/**
|
||||
* Get all services
|
||||
* GET /api/services
|
||||
*/
|
||||
export const getAllServices = async (req, res, next) => {
|
||||
try {
|
||||
const services = await serviceService.getAllServices();
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
count: services.length,
|
||||
data: services,
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get service by ID
|
||||
* GET /api/services/:serviceId
|
||||
*/
|
||||
export const getServiceById = async (req, res, next) => {
|
||||
try {
|
||||
const { serviceId } = req.params;
|
||||
|
||||
const service = await serviceService.getServiceById(serviceId);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: service,
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create new service
|
||||
* POST /api/services
|
||||
* Body: { name, price, description }
|
||||
*/
|
||||
export const createService = async (req, res, next) => {
|
||||
try {
|
||||
const userId = req.userId;
|
||||
const data = req.body;
|
||||
|
||||
const service = await serviceService.createService(userId, data);
|
||||
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
data: service,
|
||||
message: 'Služba bola vytvorená',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Update service
|
||||
* PUT /api/services/:serviceId
|
||||
* Body: { name, price, description }
|
||||
*/
|
||||
export const updateService = async (req, res, next) => {
|
||||
try {
|
||||
const { serviceId } = req.params;
|
||||
const data = req.body;
|
||||
|
||||
const service = await serviceService.updateService(serviceId, data);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: service,
|
||||
message: 'Služba bola aktualizovaná',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete service
|
||||
* DELETE /api/services/:serviceId
|
||||
*/
|
||||
export const deleteService = async (req, res, next) => {
|
||||
try {
|
||||
const { serviceId } = req.params;
|
||||
|
||||
const result = await serviceService.deleteService(serviceId);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
message: result.message,
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
@@ -325,3 +325,37 @@ export const getMonthlyStats = async (req, res, next) => {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate timesheet for a specific company
|
||||
* POST /api/time-tracking/generate-company-timesheet
|
||||
* Body: { year, month, companyId }
|
||||
*/
|
||||
export const generateCompanyTimesheet = async (req, res, next) => {
|
||||
try {
|
||||
const userId = req.userId;
|
||||
const { year, month, companyId } = req.body;
|
||||
|
||||
if (!year || !month || !companyId) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Year, month and companyId are required',
|
||||
});
|
||||
}
|
||||
|
||||
const result = await timeTrackingService.generateCompanyTimesheet(
|
||||
userId,
|
||||
parseInt(year),
|
||||
parseInt(month),
|
||||
companyId
|
||||
);
|
||||
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
data: result,
|
||||
message: `Timesheet pre firmu ${result.companyName} bol vygenerovaný`,
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user