diff --git a/backend/services/falukantService.js b/backend/services/falukantService.js index 4dbfa1b..efe7855 100644 --- a/backend/services/falukantService.js +++ b/backend/services/falukantService.js @@ -2946,27 +2946,24 @@ class FalukantService extends BaseService { return parseFloat(averageKnowledge[0]?.avgKnowledge || 0); } + /** + * Höchstes kirchliches Amt (Stufe + Typ-Name), inkl. Karrierehöchstwert: + * aktuelle `church_office`-Zeilen plus genehmigte Bewerbungen (wie `getChurchCareerInfo`), + * nicht nur die aktuelle Besetzung — analog zu Politik mit `PoliticalOfficeHistory`. + */ async getHighestChurchOfficeInfo(userId) { - const character = await FalukantCharacter.findOne({ - where: { userId }, - attributes: ['id'] - }); - if (!character) return { rank: 0, name: null }; + const characterId = await this.resolveCharacterIdForOfficeChecks(userId); + if (!characterId) return { rank: 0, name: null }; - const churchOffices = await ChurchOffice.findAll({ - where: { characterId: character.id }, - include: [{ model: ChurchOfficeType, as: 'type', attributes: ['name', 'hierarchyLevel'] }], - attributes: ['officeTypeId'] - }); - - const candidates = churchOffices - .map((office) => ({ - rank: Number(office.type?.hierarchyLevel || 0), - name: office.type?.name || null, - })) - .sort((a, b) => b.rank - a.rank); - - return candidates[0] || { rank: 0, name: null }; + const career = await this.getChurchCareerInfo(characterId); + const highest = career?.highestEverOffice; + if (!highest?.name) { + return { rank: 0, name: null }; + } + return { + rank: Number(highest.hierarchyLevel ?? 0), + name: highest.name + }; } /** @@ -3055,6 +3052,9 @@ class FalukantService extends BaseService { completedProductions, highestPoliticalOfficeRank, highestChurchOfficeRank, + /** Technischer Schlüssel (z. B. assessor, lay-preacher) für UI-Übersetzung */ + highestPoliticalOfficeName: highestPoliticalOffice?.name ?? null, + highestChurchOfficeName: highestChurchOffice?.name ?? null, highestOfficeRank, nobilityLevel, reputation, @@ -3686,17 +3686,19 @@ class FalukantService extends BaseService { lover.politicalFreeMaintenance = idx < politicalFreeLoverSlots; lover.monthlyCost = lover.politicalFreeMaintenance ? 0 : base; }); - const derivedHouseholdTension = this.calculateHouseholdTension({ - lovers, - marriageSatisfaction, - userHouse, - children - }); + const tensionRefresh = await this.refreshHouseholdTensionState(user, character); + const baseTension = tensionRefresh + || this.calculateHouseholdTension({ + lovers, + marriageSatisfaction, + userHouse, + children + }); const householdTension = { - score: Number(userHouse?.householdTensionScore ?? derivedHouseholdTension.score), - reasons: Array.isArray(userHouse?.householdTensionReasonsJson) ? userHouse.householdTensionReasonsJson : derivedHouseholdTension.reasons + score: baseTension.score, + reasons: baseTension.reasons, + label: baseTension.label }; - householdTension.label = this.getHouseholdTensionLabel(householdTension.score); const family = { relationships: activeRelationships.map((r) => ({ ...r, @@ -6158,7 +6160,7 @@ class FalukantService extends BaseService { const characterId = await this.resolveCharacterIdForOfficeChecks(userId); if (!characterId) return { rank: 0, name: null, source: null }; - const [politicalOffices, politicalHistories, churchOffices] = await Promise.all([ + const [politicalOffices, politicalHistories, churchCareer] = await Promise.all([ PoliticalOffice.findAll({ where: { characterId }, include: [{ model: PoliticalOfficeType, as: 'type', attributes: ['name'] }], @@ -6169,13 +6171,18 @@ class FalukantService extends BaseService { include: [{ model: PoliticalOfficeType, as: 'officeTypeHistory', attributes: ['name'] }], attributes: ['officeTypeId'] }), - ChurchOffice.findAll({ - where: { characterId }, - include: [{ model: ChurchOfficeType, as: 'type', attributes: ['name', 'hierarchyLevel'] }], - attributes: ['officeTypeId'] - }) + this.getChurchCareerInfo(characterId) ]); + const churchEver = churchCareer?.highestEverOffice; + const churchCandidates = churchEver?.name + ? [{ + rank: Number(churchEver.hierarchyLevel ?? 0), + name: churchEver.name, + source: 'church' + }] + : []; + const candidates = [ ...politicalOffices.map((office) => ({ rank: POLITICAL_OFFICE_RANKS[office.type?.name] || 0, @@ -6187,11 +6194,7 @@ class FalukantService extends BaseService { name: history.officeTypeHistory?.name || null, source: 'political' })), - ...churchOffices.map((office) => ({ - rank: Number(office.type?.hierarchyLevel || 0), - name: office.type?.name || null, - source: 'church' - })) + ...churchCandidates ].sort((a, b) => b.rank - a.rank); return candidates[0] || { rank: 0, name: null, source: null }; diff --git a/frontend/src/views/falukant/OverviewView.vue b/frontend/src/views/falukant/OverviewView.vue index c739592..24736de 100644 --- a/frontend/src/views/falukant/OverviewView.vue +++ b/frontend/src/views/falukant/OverviewView.vue @@ -155,11 +155,11 @@
{{ $t('falukant.overview.certificate.factor.highestPoliticalOfficeRank') }} - {{ certificateProgress.currentValues.highestPoliticalOfficeRank }} + {{ formatCertificateHighestOffice('political') }}
{{ $t('falukant.overview.certificate.factor.highestChurchOfficeRank') }} - {{ certificateProgress.currentValues.highestChurchOfficeRank }} + {{ formatCertificateHighestOffice('church') }}
{{ $t('falukant.overview.certificate.factor.nobilityLevel') }} @@ -752,6 +752,24 @@ export default { maximumFractionDigits: digits, }).format(value); }, + /** Höchstes politisches/kirchliches Amt als übersetzter Amtsname statt Rohzahl (z. B. 1,00). */ + formatCertificateHighestOffice(kind) { + const cv = this.certificateProgress?.currentValues; + if (!cv) return '---'; + const name = kind === 'church' ? cv.highestChurchOfficeName : cv.highestPoliticalOfficeName; + const rankKey = kind === 'church' ? 'highestChurchOfficeRank' : 'highestPoliticalOfficeRank'; + const rank = Math.round(Number(cv[rankKey] ?? 0)); + const baseKey = kind === 'church' ? 'falukant.church.offices' : 'falukant.politics.offices'; + if (!name && rank <= 0) { + return this.$t('falukant.nobility.none'); + } + if (name) { + return this.$te(`${baseKey}.${name}`) + ? this.$t(`${baseKey}.${name}`) + : name; + } + return String(rank); + }, formatCertificateRequirement(current, required) { const currentDigits = typeof current === 'number' && !Number.isInteger(current) ? 1 : 0; const requiredDigits = typeof required === 'number' && !Number.isInteger(required) ? 1 : 0;