// src/application/requisitionManagement/RequisitionApplicationService.js
class RequisitionApplicationService {
constructor({
requisitionRepository,
userRepository,
departmentRepository,
projectRepository,
companyRepository,
eventBus,
unitOfWork
}) {
this.requisitionRepository = requisitionRepository;
this.userRepository = userRepository;
this.departmentRepository = departmentRepository;
this.projectRepository = projectRepository;
this.companyRepository = companyRepository;
this.eventBus = eventBus;
this.unitOfWork = unitOfWork;
}
/**
* Create a new requisition
* @param {Object} data - The requisition data
* @param {number} userId - The ID of the user creating the requisition
* @returns {Promise<Requisition>} - The created requisition
*/
async createRequisition(data, userId) {
return this.unitOfWork.execute(async () => {
// Validate references
const department = await this.departmentRepository.findById(data.departmentId);
if (!department) {
throw new Error('Department not found');
}
if (data.projectId) {
const project = await this.projectRepository.findById(data.projectId);
if (!project) {
throw new Error('Project not found');
}
}
const company = await this.companyRepository.findById(data.companyId);
if (!company) {
throw new Error('Company not found');
}
// Create domain entity using factory method
const requisition = Requisition.create({
...data,
createdBy: userId,
companyCode: company.code,
});
// Add items if provided
if (data.items && Array.isArray(data.items)) {
for (const itemData of data.items) {
const item = new RequisitionItem(itemData);
requisition.addItem(item);
}
}
// Determine approvers
const approvers = await this._determineApprovers({
category: data.category,
projectId: data.projectId,
departmentId: data.departmentId,
userId,
companyId: data.companyId,
});
// Add approvers to requisition
for (const approverData of approvers) {
const approver = new RequisitionApprover(approverData);
requisition.addApprover(approver);
}
// Save to repository
const savedRequisition = await this.requisitionRepository.save(requisition);
// Publish domain event
this.eventBus.publish(new RequisitionCreatedEvent(savedRequisition));
return savedRequisition;
});
}
/**
* Submit a requisition for approval
* @param {number} requisitionId - The requisition ID
* @param {number} userId - The ID of the user submitting the requisition
* @returns {Promise<Requisition>} - The submitted requisition
*/
async submitRequisition(requisitionId, userId) {
return this.unitOfWork.execute(async () => {
// Get requisition from repository
const requisition = await this.requisitionRepository.findById(requisitionId);
if (!requisition) {
throw new Error('Requisition not found');
}
// Check if user is authorized to submit
if (requisition.createdBy !== userId) {
throw new Error('Only the creator can submit a requisition');
}
// Execute domain logic
const event = requisition.submit();
// Save changes
const savedRequisition = await this.requisitionRepository.save(requisition);
// Publish event
if (event) {
this.eventBus.publish(event);
}
return savedRequisition;
});
}
/**
* Approve a requisition
* @param {number} requisitionId - The requisition ID
* @param {number} approverId - The ID of the approver
* @returns {Promise<Requisition>} - The approved requisition
*/
async approveRequisition(requisitionId, approverId) {
return this.unitOfWork.execute(async () => {
// Get requisition from repository
const requisition = await this.requisitionRepository.findById(requisitionId);
if (!requisition) {
throw new Error('Requisition not found');
}
// Execute domain logic
const event = requisition.approve(approverId);
// Save changes
const savedRequisition = await this.requisitionRepository.save(requisition);
// Publish event if any
if (event) {
this.eventBus.publish(event);
}
return savedRequisition;
});
}
/**
* Reject a requisition
* @param {number} requisitionId - The requisition ID
* @param {number} approverId - The ID of the approver
* @param {string} reason - The reason for rejection
* @returns {Promise<Requisition>} - The rejected requisition
*/
async rejectRequisition(requisitionId, approverId, reason) {
return this.unitOfWork.execute(async () => {
// Get requisition from repository
const requisition = await this.requisitionRepository.findById(requisitionId);
if (!requisition) {
throw new Error('Requisition not found');
}
// Execute domain logic
const event = requisition.reject(approverId, reason);
// Save changes
const savedRequisition = await this.requisitionRepository.save(requisition);
// Publish event
if (event) {
this.eventBus.publish(event);
}
return savedRequisition;
});
}
/**
* Get requisitions by status
* @param {string} status - The status to filter by
* @param {Object} options - Additional options (pagination, etc.)
* @returns {Promise<{data: Requisition[], total: number}>} - The requisitions and total count
*/
async getRequisitionsByStatus(status, options = {}) {
return this.requisitionRepository.findByStatus(status, options);
}
/**
* Get requisitions created by a user
* @param {number} userId - The user ID
* @param {Object} options - Additional options (pagination, etc.)
* @returns {Promise<{data: Requisition[], total: number}>} - The requisitions and total count
*/
async getRequisitionsByCreator(userId, options = {}) {
return this.requisitionRepository.findByCreator(userId, options);
}
/**
* Get a requisition by ID
* @param {number} id - The requisition ID
* @returns {Promise<Requisition|null>} - The requisition or null if not found
*/
async getRequisitionById(id) {
return this.requisitionRepository.findById(id);
}
// Private methods
async _determineApprovers({ category, projectId, departmentId, userId, companyId }) {
// Implementation of approver determination logic
// This would replace the current rsApproversV2 method
// ...
}
}
module.exports = RequisitionApplicationService;