feat(user): add certificate production tracking and update localization
All checks were successful
Deploy to production / deploy (push) Successful in 2m50s

- Introduced a new field `certificateProductionsCountSince` in the `FalukantUser` model to track the date from which production logs are counted for certificate requirements.
- Updated the `FalukantService` to utilize the new field for calculating completed productions since the specified date.
- Enhanced the UI to display the count of productions since the last promotion, with corresponding translations added for multiple languages including Cebuano, German, English, Spanish, and French.
- Implemented a method to delete old production logs, ensuring efficient data management while maintaining necessary historical records for certificate calculations.
This commit is contained in:
Torsten Schulz (local)
2026-04-09 08:19:19 +02:00
parent f7030bbabe
commit 360bb59a4e
18 changed files with 200 additions and 25 deletions

View File

@@ -14,7 +14,6 @@ import TitleBenefit from '../models/falukant/type/title_benefit.js';
import Branch from '../models/falukant/data/branch.js';
import BranchType from '../models/falukant/type/branch.js';
import Production from '../models/falukant/data/production.js';
import DayProduction from '../models/falukant/log/dayproduction.js';
import ProductType from '../models/falukant/type/product.js';
import Knowledge from '../models/falukant/data/product_knowledge.js';
import Inventory from '../models/falukant/data/inventory.js';
@@ -976,7 +975,7 @@ class FalukantService extends BaseService {
]
},
],
attributes: ['id', 'money', 'creditAmount', 'todayCreditTaken', 'certificate']
attributes: ['id', 'money', 'creditAmount', 'todayCreditTaken', 'certificate', 'certificateProductionsCountSince']
});
if (!u) throw new Error('User not found');
if (u.certificate == null) {
@@ -2952,6 +2951,36 @@ class FalukantService extends BaseService {
return candidates[0] || { rank: 0, name: null };
}
/**
* Zertifikat: abgeschlossene Produktionen über alle Regionen/Niederlassungen.
* Pro (Produkt, Kalendertag) nur ein Zähler mehrere Niederlassungen in verschiedenen Regionen werden zusammengeführt.
* Filter bei gesetztem countSince wie Daemon (GET_PRODUCTION_CERTIFICATE_INPUT_ROWS):
* COALESCE(production_timestamp, production_date::timestamp) >= countSince.
*
* @param {number} producerId falukant_user.id
* @param {Date|null|undefined} countSince null/undefined = gesamte Historie (Bestand / vor erster Stufenänderung)
*/
async getCertificateCompletedProductionCount(producerId, countSince) {
const sinceClause = countSince
? ' AND COALESCE(production_timestamp, production_date::timestamp) >= :countSince'
: '';
const replacements = { producerId };
if (countSince) replacements.countSince = countSince;
const rows = await sequelize.query(
`
SELECT COUNT(*)::int AS cnt
FROM (
SELECT 1
FROM falukant_log.production
WHERE producer_id = :producerId${sinceClause}
GROUP BY product_id, production_date
) AS sub
`,
{ replacements, type: sequelize.QueryTypes.SELECT }
);
return Number(rows[0]?.cnt ?? 0);
}
async buildCertificateProgress(user) {
const character = user?.character || await FalukantCharacter.findOne({
where: { userId: user.id },
@@ -2961,9 +2990,10 @@ class FalukantService extends BaseService {
return null;
}
const productionsSince = user.certificateProductionsCountSince ?? null;
const [avgKnowledge, completedProductions, highestPoliticalOffice, highestChurchOffice, house, title] = await Promise.all([
this.calculateAverageKnowledge(character.id),
DayProduction.count({ where: { producerId: user.id } }),
this.getCertificateCompletedProductionCount(user.id, productionsSince),
this.getHighestPoliticalOfficeInfo(user.id),
this.getHighestChurchOfficeInfo(user.id),
UserHouse.findOne({
@@ -3077,6 +3107,9 @@ class FalukantService extends BaseService {
scoreRequirementMet,
minimumRequirementsMet,
readyForNextCertificate: scoreRequirementMet && minimumRequirementsMet,
certificateProductionsCountSince: productionsSince
? new Date(productionsSince).toISOString()
: null,
};
}
@@ -4112,6 +4145,10 @@ class FalukantService extends BaseService {
}
await candidate.update({ userId: user.id });
await FalukantUser.update(
{ certificateProductionsCountSince: new Date() },
{ where: { id: user.id } }
);
return { success: true, heirId: candidate.id };
}