Implement church career management features
- Added endpoints for church career functionalities including overview, available positions, application submission, and application decision-making. - Enhanced the FalukantController to handle church-related requests. - Updated associations and models to support church office types and requirements. - Integrated new routes in the falukantRouter for church career operations. - Implemented service methods for managing church applications and checking church career status. - Updated frontend components to display current positions, available positions, and manage applications with appropriate UI elements and loading states. - Localized new church-related strings in both English and German.
This commit is contained in:
@@ -57,6 +57,10 @@ import PoliticalOfficeHistory from '../models/falukant/log/political_office_hist
|
||||
import UndergroundType from '../models/falukant/type/underground.js';
|
||||
import Notification from '../models/falukant/log/notification.js';
|
||||
import PoliticalOffice from '../models/falukant/data/political_office.js';
|
||||
import ChurchOfficeType from '../models/falukant/type/church_office_type.js';
|
||||
import ChurchOfficeRequirement from '../models/falukant/predefine/church_office_requirement.js';
|
||||
import ChurchOffice from '../models/falukant/data/church_office.js';
|
||||
import ChurchApplication from '../models/falukant/data/church_application.js';
|
||||
import Underground from '../models/falukant/data/underground.js';
|
||||
import VehicleType from '../models/falukant/type/vehicle.js';
|
||||
import Vehicle from '../models/falukant/data/vehicle.js';
|
||||
@@ -1017,6 +1021,11 @@ class FalukantService extends BaseService {
|
||||
|
||||
async createTransport(hashedUserId, { branchId, vehicleTypeId, vehicleIds, productId, quantity, targetBranchId }) {
|
||||
const user = await getFalukantUserOrFail(hashedUserId);
|
||||
|
||||
// Prüfe, ob User eine kirchliche Karriere hat (dann nur Direktoren können transportieren)
|
||||
if (await this.hasChurchCareer(hashedUserId)) {
|
||||
throw new Error('churchCareerNoDirectTransactions');
|
||||
}
|
||||
|
||||
const sourceBranch = await Branch.findOne({
|
||||
where: { id: branchId, falukantUserId: user.id },
|
||||
@@ -1604,6 +1613,12 @@ class FalukantService extends BaseService {
|
||||
|
||||
async createProduction(hashedUserId, branchId, productId, quantity) {
|
||||
const u = await getFalukantUserOrFail(hashedUserId);
|
||||
|
||||
// Prüfe, ob User eine kirchliche Karriere hat (dann nur Direktoren können produzieren)
|
||||
if (await this.hasChurchCareer(hashedUserId)) {
|
||||
throw new Error('churchCareerNoDirectTransactions');
|
||||
}
|
||||
|
||||
const b = await getBranchOrFail(u.id, branchId);
|
||||
const p = await ProductType.findOne({ where: { id: productId } });
|
||||
const runningProductions = await Production.findAll({ where: { branchId: b.id } });
|
||||
@@ -1753,6 +1768,16 @@ class FalukantService extends BaseService {
|
||||
// Konsistenz wie sellAll: nur aus Stocks dieses Branches verkaufen und alles atomar ausführen
|
||||
return await sequelize.transaction(async (t) => {
|
||||
const user = await getFalukantUserOrFail(hashedUserId, { transaction: t });
|
||||
|
||||
// Prüfe, ob User eine kirchliche Karriere hat (dann nur Direktoren können verkaufen)
|
||||
const character = await FalukantCharacter.findOne({ where: { userId: user.id }, transaction: t });
|
||||
if (character) {
|
||||
const churchOffice = await ChurchOffice.findOne({ where: { characterId: character.id }, transaction: t });
|
||||
if (churchOffice) {
|
||||
throw new Error('churchCareerNoDirectTransactions');
|
||||
}
|
||||
}
|
||||
|
||||
const branch = await getBranchOrFail(user.id, branchId);
|
||||
|
||||
const character = await FalukantCharacter.findOne({ where: { userId: user.id }, transaction: t });
|
||||
@@ -1854,6 +1879,16 @@ class FalukantService extends BaseService {
|
||||
// Sonst kann es (wie beobachtet) zu "teilweise verkauft/gelöscht" kommen.
|
||||
return await sequelize.transaction(async (t) => {
|
||||
const falukantUser = await getFalukantUserOrFail(hashedUserId, { transaction: t });
|
||||
|
||||
// Prüfe, ob User eine kirchliche Karriere hat (dann nur Direktoren können verkaufen)
|
||||
const character = await FalukantCharacter.findOne({ where: { userId: falukantUser.id }, transaction: t });
|
||||
if (character) {
|
||||
const churchOffice = await ChurchOffice.findOne({ where: { characterId: character.id }, transaction: t });
|
||||
if (churchOffice) {
|
||||
throw new Error('churchCareerNoDirectTransactions');
|
||||
}
|
||||
}
|
||||
|
||||
const branch = await Branch.findOne({
|
||||
where: { id: branchId, falukantUserId: falukantUser.id },
|
||||
include: [{ model: FalukantStock, as: 'stocks' }],
|
||||
@@ -2191,6 +2226,12 @@ class FalukantService extends BaseService {
|
||||
|
||||
async buyStorage(hashedUserId, branchId, amount, stockTypeId) {
|
||||
const user = await getFalukantUserOrFail(hashedUserId);
|
||||
|
||||
// Prüfe, ob User eine kirchliche Karriere hat (dann nur Direktoren können Lager kaufen)
|
||||
if (await this.hasChurchCareer(hashedUserId)) {
|
||||
throw new Error('churchCareerNoDirectTransactions');
|
||||
}
|
||||
|
||||
const branch = await getBranchOrFail(user.id, branchId);
|
||||
const buyableStocks = await BuyableStock.findAll({
|
||||
where: { regionId: branch.regionId, stockTypeId },
|
||||
@@ -2260,6 +2301,12 @@ class FalukantService extends BaseService {
|
||||
|
||||
async sellStorage(hashedUserId, branchId, amount, stockTypeId) {
|
||||
const user = await getFalukantUserOrFail(hashedUserId);
|
||||
|
||||
// Prüfe, ob User eine kirchliche Karriere hat (dann nur Direktoren können Lager verkaufen)
|
||||
if (await this.hasChurchCareer(hashedUserId)) {
|
||||
throw new Error('churchCareerNoDirectTransactions');
|
||||
}
|
||||
|
||||
const branch = await getBranchOrFail(user.id, branchId);
|
||||
const stock = await FalukantStock.findOne({
|
||||
where: { branchId: branch.id, stockTypeId },
|
||||
@@ -6239,4 +6286,558 @@ async function enrichNotificationsWithCharacterNames(notifications) {
|
||||
n.character_name = resolved;
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== Church Career Methods ====================
|
||||
|
||||
async getChurchOverview(hashedUserId) {
|
||||
// Liefert alle aktuell besetzten kirchlichen Ämter im eigenen Gebiet
|
||||
const user = await getFalukantUserOrFail(hashedUserId);
|
||||
const character = await FalukantCharacter.findOne({
|
||||
where: { userId: user.id },
|
||||
attributes: ['id', 'regionId']
|
||||
});
|
||||
if (!character) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const relevantRegionIds = await this.getRegionAndParentIds(character.regionId);
|
||||
|
||||
const offices = await ChurchOffice.findAll({
|
||||
where: {
|
||||
regionId: {
|
||||
[Op.in]: relevantRegionIds
|
||||
}
|
||||
},
|
||||
include: [
|
||||
{
|
||||
model: ChurchOfficeType,
|
||||
as: 'type',
|
||||
attributes: ['name', 'hierarchyLevel']
|
||||
},
|
||||
{
|
||||
model: RegionData,
|
||||
as: 'region',
|
||||
attributes: ['name']
|
||||
},
|
||||
{
|
||||
model: FalukantCharacter,
|
||||
as: 'holder',
|
||||
attributes: ['id', 'gender'],
|
||||
include: [
|
||||
{
|
||||
model: FalukantPredefineFirstname,
|
||||
as: 'definedFirstName',
|
||||
attributes: ['name']
|
||||
},
|
||||
{
|
||||
model: FalukantPredefineLastname,
|
||||
as: 'definedLastName',
|
||||
attributes: ['name']
|
||||
},
|
||||
{
|
||||
model: TitleOfNobility,
|
||||
as: 'nobleTitle',
|
||||
attributes: ['labelTr']
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
model: FalukantCharacter,
|
||||
as: 'supervisor',
|
||||
attributes: ['id'],
|
||||
include: [
|
||||
{
|
||||
model: FalukantPredefineFirstname,
|
||||
as: 'definedFirstName',
|
||||
attributes: ['name']
|
||||
},
|
||||
{
|
||||
model: FalukantPredefineLastname,
|
||||
as: 'definedLastName',
|
||||
attributes: ['name']
|
||||
}
|
||||
],
|
||||
required: false
|
||||
}
|
||||
],
|
||||
order: [
|
||||
[{ model: ChurchOfficeType, as: 'type' }, 'hierarchyLevel', 'ASC'],
|
||||
[{ model: RegionData, as: 'region' }, 'name', 'ASC']
|
||||
]
|
||||
});
|
||||
|
||||
return offices.map(office => {
|
||||
const o = office.get({ plain: true });
|
||||
return {
|
||||
id: o.id,
|
||||
officeType: {
|
||||
name: o.type.name,
|
||||
hierarchyLevel: o.type.hierarchyLevel
|
||||
},
|
||||
region: {
|
||||
name: o.region.name
|
||||
},
|
||||
character: o.holder ? {
|
||||
id: o.holder.id,
|
||||
name: `${o.holder.definedFirstName?.name || ''} ${o.holder.definedLastName?.name || ''}`.trim(),
|
||||
gender: o.holder.gender,
|
||||
title: o.holder.nobleTitle?.labelTr
|
||||
} : null,
|
||||
supervisor: o.supervisor ? {
|
||||
id: o.supervisor.id,
|
||||
name: `${o.supervisor.definedFirstName?.name || ''} ${o.supervisor.definedLastName?.name || ''}`.trim()
|
||||
} : null
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async getAvailableChurchPositions(hashedUserId) {
|
||||
// Liefert verfügbare kirchliche Positionen, für die sich der User bewerben kann
|
||||
const user = await getFalukantUserOrFail(hashedUserId);
|
||||
const character = await FalukantCharacter.findOne({
|
||||
where: { userId: user.id },
|
||||
attributes: ['id', 'regionId'],
|
||||
include: [
|
||||
{
|
||||
model: TitleOfNobility,
|
||||
as: 'nobleTitle',
|
||||
attributes: ['labelTr', 'level']
|
||||
}
|
||||
]
|
||||
});
|
||||
if (!character) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Prüfe, ob User bereits ein kirchliches Amt hat
|
||||
const existingOffice = await ChurchOffice.findOne({
|
||||
where: { characterId: character.id }
|
||||
});
|
||||
if (existingOffice) {
|
||||
// User hat bereits ein Amt, kann sich nur auf höhere Positionen bewerben
|
||||
const currentOffice = await ChurchOffice.findOne({
|
||||
where: { characterId: character.id },
|
||||
include: [{
|
||||
model: ChurchOfficeType,
|
||||
as: 'type',
|
||||
attributes: ['hierarchyLevel']
|
||||
}]
|
||||
});
|
||||
const currentLevel = currentOffice?.type?.hierarchyLevel || 0;
|
||||
|
||||
// Finde höhere Positionen
|
||||
const availableOffices = await ChurchOfficeType.findAll({
|
||||
where: {
|
||||
hierarchyLevel: {
|
||||
[Op.gt]: currentLevel
|
||||
}
|
||||
},
|
||||
include: [
|
||||
{
|
||||
model: ChurchOfficeRequirement,
|
||||
as: 'requirements'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
return this.filterAvailablePositions(availableOffices, character, existingOffice);
|
||||
} else {
|
||||
// User hat noch kein Amt, kann sich auf Einstiegspositionen bewerben
|
||||
const entryLevelOffices = await ChurchOfficeType.findAll({
|
||||
where: {
|
||||
hierarchyLevel: 1 // Dorfgeistlicher ist Einstieg
|
||||
},
|
||||
include: [
|
||||
{
|
||||
model: ChurchOfficeRequirement,
|
||||
as: 'requirements'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
return this.filterAvailablePositions(entryLevelOffices, character, null);
|
||||
}
|
||||
}
|
||||
|
||||
async filterAvailablePositions(officeTypes, character, existingOffice) {
|
||||
const available = [];
|
||||
const relevantRegionIds = await this.getRegionAndParentIds(character.regionId);
|
||||
|
||||
for (const officeType of officeTypes) {
|
||||
// Prüfe Voraussetzungen
|
||||
const requirements = officeType.requirements || [];
|
||||
let canApply = true;
|
||||
|
||||
// Prüfe, ob Voraussetzung erfüllt ist (niedrigere Position)
|
||||
if (requirements.length > 0 && requirements[0].prerequisiteOfficeTypeId) {
|
||||
if (!existingOffice) {
|
||||
canApply = false; // Benötigt niedrigere Position, aber User hat keine
|
||||
} else {
|
||||
const currentOfficeType = await ChurchOfficeType.findByPk(existingOffice.officeTypeId);
|
||||
if (currentOfficeType?.hierarchyLevel >= officeType.hierarchyLevel) {
|
||||
canApply = false; // Aktuelle Position ist nicht niedrig genug
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!canApply) continue;
|
||||
|
||||
// Finde verfügbare Positionen in relevanten Regionen
|
||||
const filledPositions = await ChurchOffice.count({
|
||||
where: {
|
||||
officeTypeId: officeType.id,
|
||||
regionId: {
|
||||
[Op.in]: relevantRegionIds
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Prüfe, ob noch Plätze frei sind
|
||||
if (filledPositions < officeType.seatsPerRegion) {
|
||||
// Finde Vorgesetzten für diese Position
|
||||
const supervisor = await this.findSupervisorForPosition(officeType, character.regionId);
|
||||
|
||||
// Finde passende Region für diese Position
|
||||
const region = await RegionData.findOne({
|
||||
where: {
|
||||
id: {
|
||||
[Op.in]: relevantRegionIds
|
||||
}
|
||||
},
|
||||
include: [{
|
||||
model: RegionType,
|
||||
as: 'regionType',
|
||||
attributes: ['labelTr']
|
||||
}],
|
||||
order: [['id', 'ASC']] // Nimm die erste passende Region
|
||||
});
|
||||
|
||||
available.push({
|
||||
id: officeType.id,
|
||||
officeType: {
|
||||
name: officeType.name,
|
||||
hierarchyLevel: officeType.hierarchyLevel,
|
||||
seatsPerRegion: officeType.seatsPerRegion,
|
||||
regionType: officeType.regionType
|
||||
},
|
||||
region: region ? {
|
||||
id: region.id,
|
||||
name: region.name
|
||||
} : null,
|
||||
regionId: region?.id || character.regionId,
|
||||
availableSeats: officeType.seatsPerRegion - filledPositions,
|
||||
supervisor: supervisor ? {
|
||||
id: supervisor.id,
|
||||
name: `${supervisor.definedFirstName?.name || ''} ${supervisor.definedLastName?.name || ''}`.trim()
|
||||
} : null
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return available;
|
||||
}
|
||||
|
||||
async findSupervisorForPosition(officeType, regionId) {
|
||||
// Finde den Vorgesetzten (höhere Position in der Hierarchie)
|
||||
const supervisorOfficeType = await ChurchOfficeType.findOne({
|
||||
where: {
|
||||
hierarchyLevel: {
|
||||
[Op.gt]: officeType.hierarchyLevel
|
||||
}
|
||||
},
|
||||
order: [['hierarchyLevel', 'ASC']] // Nimm die nächsthöhere Position
|
||||
});
|
||||
|
||||
if (!supervisorOfficeType) {
|
||||
return null; // Kein Vorgesetzter (z.B. Papst)
|
||||
}
|
||||
|
||||
// Finde den Vorgesetzten in der Region oder übergeordneten Regionen
|
||||
const relevantRegionIds = await this.getRegionAndParentIds(regionId);
|
||||
const supervisorOffice = await ChurchOffice.findOne({
|
||||
where: {
|
||||
officeTypeId: supervisorOfficeType.id,
|
||||
regionId: {
|
||||
[Op.in]: relevantRegionIds
|
||||
}
|
||||
},
|
||||
include: [
|
||||
{
|
||||
model: FalukantCharacter,
|
||||
as: 'holder',
|
||||
attributes: ['id'],
|
||||
include: [
|
||||
{
|
||||
model: FalukantPredefineFirstname,
|
||||
as: 'definedFirstName',
|
||||
attributes: ['name']
|
||||
},
|
||||
{
|
||||
model: FalukantPredefineLastname,
|
||||
as: 'definedLastName',
|
||||
attributes: ['name']
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
return supervisorOffice?.holder || null;
|
||||
}
|
||||
|
||||
async applyForChurchPosition(hashedUserId, officeTypeId, regionId) {
|
||||
// Bewerbung für eine kirchliche Position
|
||||
const user = await getFalukantUserOrFail(hashedUserId);
|
||||
const character = await FalukantCharacter.findOne({
|
||||
where: { userId: user.id },
|
||||
attributes: ['id', 'regionId']
|
||||
});
|
||||
if (!character) {
|
||||
throw new Error('Character not found');
|
||||
}
|
||||
|
||||
// Prüfe, ob bereits eine Bewerbung für diese Position existiert
|
||||
const existingApplication = await ChurchApplication.findOne({
|
||||
where: {
|
||||
characterId: character.id,
|
||||
officeTypeId: officeTypeId,
|
||||
status: 'pending'
|
||||
}
|
||||
});
|
||||
if (existingApplication) {
|
||||
throw new Error('Application already exists');
|
||||
}
|
||||
|
||||
// Finde Vorgesetzten
|
||||
const officeType = await ChurchOfficeType.findByPk(officeTypeId);
|
||||
if (!officeType) {
|
||||
throw new Error('Office type not found');
|
||||
}
|
||||
|
||||
const supervisor = await this.findSupervisorForPosition(officeType, regionId);
|
||||
if (!supervisor) {
|
||||
throw new Error('Supervisor not found');
|
||||
}
|
||||
|
||||
// Erstelle Bewerbung
|
||||
const application = await ChurchApplication.create({
|
||||
officeTypeId: officeTypeId,
|
||||
characterId: character.id,
|
||||
regionId: regionId,
|
||||
supervisorId: supervisor.id,
|
||||
status: 'pending'
|
||||
});
|
||||
|
||||
// Benachrichtige Vorgesetzten
|
||||
const supervisorUser = await FalukantUser.findOne({
|
||||
where: { id: supervisor.userId },
|
||||
attributes: ['id']
|
||||
});
|
||||
if (supervisorUser) {
|
||||
await notifyUser(supervisorUser.id, {
|
||||
tr: 'falukant.church.application.received',
|
||||
characterId: character.id,
|
||||
officeTypeId: officeTypeId
|
||||
});
|
||||
}
|
||||
|
||||
return application;
|
||||
}
|
||||
|
||||
async getSupervisedApplications(hashedUserId) {
|
||||
// Liefert alle Bewerbungen, über die der User als Vorgesetzter entscheiden kann
|
||||
const user = await getFalukantUserOrFail(hashedUserId);
|
||||
const character = await FalukantCharacter.findOne({
|
||||
where: { userId: user.id },
|
||||
attributes: ['id']
|
||||
});
|
||||
if (!character) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const applications = await ChurchApplication.findAll({
|
||||
where: {
|
||||
supervisorId: character.id,
|
||||
status: 'pending'
|
||||
},
|
||||
include: [
|
||||
{
|
||||
model: ChurchOfficeType,
|
||||
as: 'officeType',
|
||||
attributes: ['name', 'hierarchyLevel']
|
||||
},
|
||||
{
|
||||
model: RegionData,
|
||||
as: 'region',
|
||||
attributes: ['name']
|
||||
},
|
||||
{
|
||||
model: FalukantCharacter,
|
||||
as: 'applicant',
|
||||
attributes: ['id', 'gender', 'age'],
|
||||
include: [
|
||||
{
|
||||
model: FalukantPredefineFirstname,
|
||||
as: 'definedFirstName',
|
||||
attributes: ['name']
|
||||
},
|
||||
{
|
||||
model: FalukantPredefineLastname,
|
||||
as: 'definedLastName',
|
||||
attributes: ['name']
|
||||
},
|
||||
{
|
||||
model: TitleOfNobility,
|
||||
as: 'nobleTitle',
|
||||
attributes: ['labelTr']
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
order: [['createdAt', 'DESC']]
|
||||
});
|
||||
|
||||
return applications.map(app => {
|
||||
const a = app.get({ plain: true });
|
||||
return {
|
||||
id: a.id,
|
||||
officeType: {
|
||||
name: a.officeType.name,
|
||||
hierarchyLevel: a.officeType.hierarchyLevel
|
||||
},
|
||||
region: {
|
||||
name: a.region.name
|
||||
},
|
||||
applicant: {
|
||||
id: a.applicant.id,
|
||||
name: `${a.applicant.definedFirstName?.name || ''} ${a.applicant.definedLastName?.name || ''}`.trim(),
|
||||
gender: a.applicant.gender,
|
||||
age: a.applicant.age,
|
||||
title: a.applicant.nobleTitle?.labelTr
|
||||
},
|
||||
createdAt: a.createdAt
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async decideOnChurchApplication(hashedUserId, applicationId, decision) {
|
||||
// Entscheidung über eine Bewerbung (approve/reject)
|
||||
const user = await getFalukantUserOrFail(hashedUserId);
|
||||
const character = await FalukantCharacter.findOne({
|
||||
where: { userId: user.id },
|
||||
attributes: ['id']
|
||||
});
|
||||
if (!character) {
|
||||
throw new Error('Character not found');
|
||||
}
|
||||
|
||||
const application = await ChurchApplication.findOne({
|
||||
where: {
|
||||
id: applicationId,
|
||||
supervisorId: character.id,
|
||||
status: 'pending'
|
||||
},
|
||||
include: [
|
||||
{
|
||||
model: ChurchOfficeType,
|
||||
as: 'officeType'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
if (!application) {
|
||||
throw new Error('Application not found or already processed');
|
||||
}
|
||||
|
||||
if (decision === 'approve') {
|
||||
// Prüfe, ob noch Platz verfügbar ist
|
||||
const filledPositions = await ChurchOffice.count({
|
||||
where: {
|
||||
officeTypeId: application.officeTypeId,
|
||||
regionId: application.regionId
|
||||
}
|
||||
});
|
||||
|
||||
if (filledPositions >= application.officeType.seatsPerRegion) {
|
||||
throw new Error('No available seats');
|
||||
}
|
||||
|
||||
// Erstelle kirchliches Amt
|
||||
await ChurchOffice.create({
|
||||
officeTypeId: application.officeTypeId,
|
||||
characterId: application.characterId,
|
||||
regionId: application.regionId,
|
||||
supervisorId: character.id
|
||||
});
|
||||
|
||||
// Aktualisiere Bewerbung
|
||||
application.status = 'approved';
|
||||
application.decisionDate = new Date();
|
||||
await application.save();
|
||||
|
||||
// Benachrichtige Bewerber
|
||||
const applicantCharacter = await FalukantCharacter.findByPk(application.characterId);
|
||||
if (applicantCharacter && applicantCharacter.userId) {
|
||||
await notifyUser(applicantCharacter.userId, {
|
||||
tr: 'falukant.church.application.approved',
|
||||
officeTypeId: application.officeTypeId
|
||||
});
|
||||
}
|
||||
|
||||
// Wenn User bereits ein niedrigeres Amt hatte, entferne es
|
||||
const lowerOffice = await ChurchOffice.findOne({
|
||||
where: {
|
||||
characterId: application.characterId,
|
||||
officeTypeId: {
|
||||
[Op.ne]: application.officeTypeId
|
||||
}
|
||||
},
|
||||
include: [{
|
||||
model: ChurchOfficeType,
|
||||
as: 'type',
|
||||
attributes: ['hierarchyLevel']
|
||||
}]
|
||||
});
|
||||
|
||||
if (lowerOffice && lowerOffice.type.hierarchyLevel < application.officeType.hierarchyLevel) {
|
||||
await lowerOffice.destroy();
|
||||
}
|
||||
|
||||
} else if (decision === 'reject') {
|
||||
application.status = 'rejected';
|
||||
application.decisionDate = new Date();
|
||||
await application.save();
|
||||
|
||||
// Benachrichtige Bewerber
|
||||
const applicantCharacter = await FalukantCharacter.findByPk(application.characterId);
|
||||
if (applicantCharacter && applicantCharacter.userId) {
|
||||
await notifyUser(applicantCharacter.userId, {
|
||||
tr: 'falukant.church.application.rejected',
|
||||
officeTypeId: application.officeTypeId
|
||||
});
|
||||
}
|
||||
} else {
|
||||
throw new Error('Invalid decision');
|
||||
}
|
||||
|
||||
return application;
|
||||
}
|
||||
|
||||
async hasChurchCareer(hashedUserId) {
|
||||
// Prüft, ob der User eine kirchliche Karriere hat
|
||||
const user = await getFalukantUserOrFail(hashedUserId);
|
||||
const character = await FalukantCharacter.findOne({
|
||||
where: { userId: user.id },
|
||||
attributes: ['id']
|
||||
});
|
||||
if (!character) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const churchOffice = await ChurchOffice.findOne({
|
||||
where: { characterId: character.id }
|
||||
});
|
||||
|
||||
return !!churchOffice;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user