diff --git a/backend/controllers/falukantController.js b/backend/controllers/falukantController.js index 162ce07..8e5c2a3 100644 --- a/backend/controllers/falukantController.js +++ b/backend/controllers/falukantController.js @@ -144,6 +144,14 @@ class FalukantController { this.applyForElections = this._wrapWithUser((userId, req) => this.service.applyForElections(userId, req.body.electionIds)); this.getRegions = this._wrapWithUser((userId) => this.service.getRegions(userId)); + this.getProductPricesInCities = this._wrapWithUser((userId, req) => { + const productId = parseInt(req.query.productId, 10); + const currentPrice = parseFloat(req.query.currentPrice); + if (Number.isNaN(productId) || Number.isNaN(currentPrice)) { + throw new Error('productId and currentPrice are required'); + } + return this.service.getProductPricesInCities(userId, productId, currentPrice); + }); this.renovate = this._wrapWithUser((userId, req) => this.service.renovate(userId, req.body.element)); this.renovateAll = this._wrapWithUser((userId) => this.service.renovateAll(userId)); diff --git a/backend/routers/falukantRouter.js b/backend/routers/falukantRouter.js index 3f9f2b0..53bc3ff 100644 --- a/backend/routers/falukantRouter.js +++ b/backend/routers/falukantRouter.js @@ -70,6 +70,7 @@ router.post('/politics/elections', falukantController.vote); router.get('/politics/open', falukantController.getOpenPolitics); router.post('/politics/open', falukantController.applyForElections); router.get('/cities', falukantController.getRegions); +router.get('/products/prices-in-cities', falukantController.getProductPricesInCities); router.get('/vehicles/types', falukantController.getVehicleTypes); router.post('/vehicles', falukantController.buyVehicles); router.get('/vehicles', falukantController.getVehicles); diff --git a/backend/services/falukantService.js b/backend/services/falukantService.js index 6671cd6..ecf25f8 100644 --- a/backend/services/falukantService.js +++ b/backend/services/falukantService.js @@ -3518,6 +3518,86 @@ class FalukantService extends BaseService { return regions; } + async getProductPricesInCities(hashedUserId, productId, currentPrice) { + const user = await this.getFalukantUserByHashedId(hashedUserId); + const character = await FalukantCharacter.findOne({ where: { userId: user.id } }); + if (!character) { + throw new Error(`No FalukantCharacter found for user with id ${user.id}`); + } + + // Produkt abrufen + const product = await ProductType.findOne({ where: { id: productId } }); + if (!product) { + throw new Error(`Product not found with id ${productId}`); + } + + // Knowledge für dieses Produkt abrufen + const knowledge = await Knowledge.findOne({ + where: { characterId: character.id, productId: productId } + }); + const knowledgeFactor = knowledge?.knowledge || 0; + + // Alle Städte abrufen + const cities = await RegionData.findAll({ + attributes: ['id', 'name'], + include: [ + { + model: RegionType, + as: 'regionType', + where: { labelTr: 'city' }, + attributes: ['labelTr'] + }, + { + model: Branch, + as: 'branches', + where: { falukantUserId: user.id }, + include: [ + { + model: BranchType, + as: 'branchType', + attributes: ['labelTr'] + } + ], + attributes: ['branchTypeId'], + required: false + } + ] + }); + + // Für jede Stadt den Preis berechnen und Branch-Typ bestimmen + const results = []; + for (const city of cities) { + const priceInCity = calcSellPrice(product, knowledgeFactor); + + // Nur Städte zurückgeben, wo der Preis höher ist + if (priceInCity > currentPrice) { + // Branch-Typ bestimmen + let branchType = null; // null = kein Branch + if (city.branches && city.branches.length > 0) { + // Finde den "besten" Branch-Typ (store/fullstack > production) + const branchTypes = city.branches.map(b => b.branchType?.labelTr).filter(Boolean); + if (branchTypes.includes('store') || branchTypes.includes('fullstack')) { + branchType = 'store'; // Grün + } else if (branchTypes.includes('production')) { + branchType = 'production'; // Orange + } + } + + results.push({ + regionId: city.id, + regionName: city.name, + price: priceInCity, + branchType: branchType // 'store' (grün), 'production' (orange), null (rot) + }); + } + } + + // Sortiere nach Preis (höchster zuerst) + results.sort((a, b) => b.price - a.price); + + return results; + } + async renovate(hashedUserId, element) { const user = await getFalukantUserOrFail(hashedUserId); const house = await UserHouse.findOne({ diff --git a/frontend/src/components/falukant/RevenueSection.vue b/frontend/src/components/falukant/RevenueSection.vue index 6ce6466..0edb5ab 100644 --- a/frontend/src/components/falukant/RevenueSection.vue +++ b/frontend/src/components/falukant/RevenueSection.vue @@ -16,6 +16,7 @@ {{ $t('falukant.branch.revenue.perMinute') }} {{ $t('falukant.branch.revenue.profitAbsolute') }} {{ $t('falukant.branch.revenue.profitPerMinute') }} + Bessere Preise @@ -26,6 +27,16 @@ {{ calculateProductRevenue(product).perMinute }} {{ calculateProductProfit(product).absolute }} {{ calculateProductProfit(product).perMinute }} + +
+ + {{ city.regionName }} + +
+ + @@ -34,6 +45,8 @@