Füge Spalte product_quality zur Tabelle stock hinzu und erstelle Migration für weather_type_id in production

This commit is contained in:
Torsten Schulz (local)
2025-12-16 13:00:29 +01:00
parent 43d86cce18
commit ee4b0ee7c2
11 changed files with 325 additions and 16 deletions

View File

@@ -133,6 +133,64 @@ async function calcRegionalSellPrice(product, knowledgeFactor, regionId, worthPe
return parseFloat(val) || 0;
}
// Returns cumulative tax percent for a region, but excludes regions where the user holds
// a political office that grants tax exemption according to the rules.
// exemptionsMap maps political office.name -> array of regionType labelTr that are exempted
const POLITICAL_TAX_EXEMPTIONS = {
'council': ['city'],
'taxman': ['city', 'county'],
'treasurerer': ['city', 'county', 'shire'],
'super-state-administrator': ['city', 'county', 'shire', 'markgrave', 'duchy'],
'chancellor': ['city','county','shire','markgrave','duchy','duchy'] // chancellor = all types; we'll handle as wildcard
};
async function getCumulativeTaxPercentWithExemptions(userId, regionId) {
if (!regionId) return 0;
// fetch user's political offices (active) and their region types
const offices = await PoliticalOffice.findAll({
where: { userId },
include: [{ model: PoliticalOfficeType, as: 'type', attributes: ['name'] }, { model: RegionData, as: 'region', include: [{ model: RegionType, as: 'regionType', attributes: ['labelTr'] }] }]
});
// build set of exempt region type labels from user's offices
const exemptTypes = new Set();
let hasChancellor = false;
for (const o of offices) {
const name = o.type?.name;
if (!name) continue;
if (name === 'chancellor') { hasChancellor = true; break; }
const allowed = POLITICAL_TAX_EXEMPTIONS[name];
if (allowed && Array.isArray(allowed)) {
for (const t of allowed) exemptTypes.add(t);
}
}
// If chancellor, exempt all region types -> tax = 0
if (hasChancellor) return 0;
// Now compute cumulative tax but exclude regions whose regionType.labelTr is in exemptTypes
const rows = await sequelize.query(
`WITH RECURSIVE ancestors AS (
SELECT r.id, r.parent_id, r.tax_percent, rt.label_tr as region_type
FROM falukant_data.region r
JOIN falukant_type.region_type rt ON rt.id = r.region_type_id
WHERE r.id = :id
UNION ALL
SELECT reg.id, reg.parent_id, reg.tax_percent, rt2.label_tr
FROM falukant_data.region reg
JOIN falukant_type.region_type rt2 ON rt2.id = reg.region_type_id
JOIN ancestors a ON reg.id = a.parent_id
)
SELECT COALESCE(SUM(CASE WHEN :exempt_types::text[] && ARRAY[region_type] THEN 0 ELSE tax_percent END),0) AS total FROM ancestors;`,
{
replacements: { id: regionId, exempt_types: Array.from(exemptTypes) },
type: sequelize.QueryTypes.SELECT
}
);
const val = rows?.[0]?.total ?? 0;
return parseFloat(val) || 0;
}
function calculateMarriageCost(titleOfNobility, age) {
const minTitle = 1;
const adjustedTitle = titleOfNobility - minTitle + 1;
@@ -1527,8 +1585,8 @@ class FalukantService extends BaseService {
const knowledgeVal = item.knowledges?.[0]?.knowledge || 0;
const pricePerUnit = await calcRegionalSellPrice(item, knowledgeVal, branch.regionId);
// compute cumulative tax (region + ancestors) and inflate price so seller net is unchanged
const cumulativeTax = await getCumulativeTaxPercent(branch.regionId);
// compute cumulative tax (region + ancestors) with political exemptions and inflate price so seller net is unchanged
const cumulativeTax = await getCumulativeTaxPercentWithExemptions(user.id, branch.regionId);
const inflationFactor = cumulativeTax >= 100 ? 1 : (1 / (1 - cumulativeTax / 100));
const adjustedPricePerUnit = Math.round(pricePerUnit * inflationFactor * 100) / 100;
const revenue = quantity * adjustedPricePerUnit;
@@ -1614,7 +1672,7 @@ class FalukantService extends BaseService {
const knowledgeVal = item.productType.knowledges[0]?.knowledge || 0;
const regionId = item.stock.branch.regionId;
const pricePerUnit = await calcRegionalSellPrice(item.productType, knowledgeVal, regionId);
const cumulativeTax = await getCumulativeTaxPercent(regionId);
const cumulativeTax = await getCumulativeTaxPercentWithExemptions(user.id, regionId);
const inflationFactor = cumulativeTax >= 100 ? 1 : (1 / (1 - cumulativeTax / 100));
const adjustedPricePerUnit = Math.round(pricePerUnit * inflationFactor * 100) / 100;
total += item.quantity * adjustedPricePerUnit;
@@ -4363,15 +4421,26 @@ class FalukantService extends BaseService {
// Unikate nach character.id
const map = new Map();
const POLITICAL_TAX_EXEMPTIONS = {
'council': ['city'],
'taxman': ['city','county'],
'treasurerer': ['city','county','shire'],
'super-state-administrator': ['city','county','shire','markgrave','duchy'],
'chancellor': ['*']
};
histories.forEach(h => {
const c = h.holder;
if (c && c.id && !map.has(c.id)) {
const officeName = h.type?.name;
const benefit = POLITICAL_TAX_EXEMPTIONS[officeName] || [];
map.set(c.id, {
id: c.id,
name: `${c.definedFirstName.name} ${c.definedLastName.name}`,
title: c.nobleTitle.labelTr,
officeType: h.type.name,
gender: c.gender
officeType: officeName,
gender: c.gender,
benefit
});
}
});