ProjectPositionCommandService.java
package de.mirkosertic.powerstaff.project.command;
import de.mirkosertic.powerstaff.shared.query.ProjectPositionStatusQueryService;
import de.mirkosertic.powerstaff.shared.query.ProjectPositionStatusView;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional
public class ProjectPositionCommandService {
private final ProjectPositionRepository repository;
private final ProjectPositionStatusQueryService positionStatusQueryService;
public ProjectPositionCommandService(final ProjectPositionRepository repository,
final ProjectPositionStatusQueryService positionStatusQueryService) {
this.repository = repository;
this.positionStatusQueryService = positionStatusQueryService;
}
public ProjectPosition save(final ProjectPosition position) {
try {
return repository.save(position);
} catch (final DataIntegrityViolationException e) {
if (position.getProjectId() != null && position.getFreelancerId() != null) {
throw new FreelancerAlreadyAssignedException(position.getFreelancerId(), position.getProjectId());
}
throw e;
}
}
/**
* Assigns a freelancer to a project. Delegates from FreelancerController (ADR-018).
* If statusId is null, the configured default ProjectPositionStatus is used.
*/
public void assignFreelancerToProject(final long freelancerId, final long projectId, final Long statusId, final String konditionen, final String kommentar) {
Long resolvedStatusId = statusId;
if (resolvedStatusId == null) {
resolvedStatusId = positionStatusQueryService.findDefault()
.map(ProjectPositionStatusView::id)
.orElseThrow(() -> new IllegalStateException(
"Kein Standard-Positionsstatus konfiguriert. Bitte im Administrationsbereich einen Standard-Status festlegen."));
}
final var position = new ProjectPosition();
position.setProjectId(projectId);
position.setFreelancerId(freelancerId);
position.setStatusId(resolvedStatusId);
position.setKonditionen(konditionen);
position.setKommentar(kommentar);
try {
repository.save(position);
} catch (final DataIntegrityViolationException e) {
throw new FreelancerAlreadyAssignedException(freelancerId, projectId);
}
}
/**
* Aktualisiert die editierbaren Felder einer bestehenden Projektposition (Status, Konditionen, Kommentar).
* Lädt den bestehenden Datensatz, um Audit-Informationen und die Freiberufler-Zuordnung zu erhalten.
*/
public ProjectPosition updateEditable(final long positionId, final Long statusId, final String konditionen, final String kommentar, final Long dbVersion) {
final var position = repository.findById(positionId)
.orElseThrow(() -> new IllegalArgumentException("Projektposition nicht gefunden: " + positionId));
position.setStatusId(statusId);
position.setKonditionen(konditionen);
position.setKommentar(kommentar);
position.setDbVersion(dbVersion);
return repository.save(position);
}
public void delete(final Long positionId) {
repository.deleteById(positionId);
}
}