diff --git a/backend/controllers/falukantController.js b/backend/controllers/falukantController.js index 0b5473d..4e1261d 100644 --- a/backend/controllers/falukantController.js +++ b/backend/controllers/falukantController.js @@ -217,6 +217,10 @@ class FalukantController { this.getBranchTransports = this._wrapWithUser( (userId, req) => this.service.getBranchTransports(userId, req.params.branchId) ); + this.repairVehicle = this._wrapWithUser( + (userId, req) => this.service.repairVehicle(userId, req.params.vehicleId), + { successStatus: 200 } + ); } diff --git a/backend/routers/falukantRouter.js b/backend/routers/falukantRouter.js index f880f0f..f348d46 100644 --- a/backend/routers/falukantRouter.js +++ b/backend/routers/falukantRouter.js @@ -75,6 +75,7 @@ router.get('/products/prices-in-cities', falukantController.getProductPricesInCi router.get('/vehicles/types', falukantController.getVehicleTypes); router.post('/vehicles', falukantController.buyVehicles); router.get('/vehicles', falukantController.getVehicles); +router.post('/vehicles/:vehicleId/repair', falukantController.repairVehicle); router.post('/transports', falukantController.createTransport); router.get('/transports/route', falukantController.getTransportRoute); router.get('/transports/branch/:branchId', falukantController.getBranchTransports); diff --git a/backend/services/falukantService.js b/backend/services/falukantService.js index fca8061..6909225 100644 --- a/backend/services/falukantService.js +++ b/backend/services/falukantService.js @@ -1118,6 +1118,93 @@ class FalukantService extends BaseService { return { success: true }; } + async repairVehicle(hashedUserId, vehicleId) { + const user = await getFalukantUserOrFail(hashedUserId); + const now = new Date(); + + // Fahrzeug laden mit Typ-Informationen + const vehicle = await Vehicle.findOne({ + where: { + id: vehicleId, + falukantUserId: user.id, + }, + include: [ + { + model: VehicleType, + as: 'type', + required: true, + }, + { + model: Transport, + as: 'transports', + required: false, + attributes: ['id'], + }, + ], + }); + + if (!vehicle) { + throw new Error('Vehicle not found or does not belong to user'); + } + + // Prüfen, ob Fahrzeug in Benutzung ist + const hasActiveTransport = Array.isArray(vehicle.transports) && vehicle.transports.length > 0; + const isBuilding = vehicle.availableFrom && new Date(vehicle.availableFrom).getTime() > now.getTime(); + + if (hasActiveTransport) { + throw new PreconditionError('vehicleInUse'); + } + + if (isBuilding) { + throw new PreconditionError('vehicleAlreadyBuilding'); + } + + // Prüfen, ob Reparatur nötig ist (Zustand < 100) + if (vehicle.condition >= 100) { + throw new PreconditionError('vehicleAlreadyPerfect'); + } + + // Kosten berechnen: Baupreis * 0.8 * (100 - zustand) / 100 + const baseCost = vehicle.type.cost; + const repairCost = Math.round(baseCost * 0.8 * (100 - vehicle.condition) / 100); + + if (user.money < repairCost) { + throw new PreconditionError('insufficientFunds'); + } + + // Bauzeit verwenden (buildTimeMinutes) + const buildTimeMinutes = vehicle.type.buildTimeMinutes || 0; + const buildMs = buildTimeMinutes * 60 * 1000; + const availableFrom = new Date(now.getTime() + buildMs); + + // Reparatur durchführen + await sequelize.transaction(async (tx) => { + // Geld abziehen + const moneyResult = await updateFalukantUserMoney( + user.id, + -repairCost, + 'repair_vehicle', + user.id + ); + if (!moneyResult.success) { + throw new Error('Failed to update money'); + } + + // Fahrzeug reparieren: Zustand auf 100, availableFrom auf jetzt + Bauzeit + await vehicle.update({ + condition: 100, + availableFrom: availableFrom, + }, { transaction: tx }); + }); + + return { + success: true, + repairCost, + buildTimeMinutes, + availableFrom, + }; + } + async createStock(hashedUserId, branchId, stockData) { const u = await getFalukantUserOrFail(hashedUserId); const b = await getBranchOrFail(u.id, branchId); diff --git a/backend/utils/syncDatabase.js b/backend/utils/syncDatabase.js index e6324f6..8aa4806 100644 --- a/backend/utils/syncDatabase.js +++ b/backend/utils/syncDatabase.js @@ -133,6 +133,45 @@ const syncDatabaseForDeployment = async () => { console.warn('⚠️ Konnte traffic_light-Spalte nicht vorab sicherstellen:', e?.message || e); } + // Migration: Transport product_id und size nullable machen + console.log("Making transport product_id and size nullable..."); + try { + await sequelize.query(` + DO $$ + BEGIN + -- Prüfe ob product_id NOT NULL Constraint existiert + IF EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'falukant_data' + AND table_name = 'transport' + AND column_name = 'product_id' + AND is_nullable = 'NO' + ) THEN + ALTER TABLE falukant_data.transport + ALTER COLUMN product_id DROP NOT NULL; + RAISE NOTICE 'product_id NOT NULL Constraint entfernt'; + END IF; + + -- Prüfe ob size NOT NULL Constraint existiert + IF EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'falukant_data' + AND table_name = 'transport' + AND column_name = 'size' + AND is_nullable = 'NO' + ) THEN + ALTER TABLE falukant_data.transport + ALTER COLUMN size DROP NOT NULL; + RAISE NOTICE 'size NOT NULL Constraint entfernt'; + END IF; + END + $$; + `); + console.log("✅ Transport product_id und size sind jetzt nullable"); + } catch (e) { + console.warn('⚠️ Konnte Transport-Spalten nicht nullable machen:', e?.message || e); + } + console.log("Setting up associations..."); setupAssociations(); diff --git a/frontend/src/i18n/locales/de/falukant.json b/frontend/src/i18n/locales/de/falukant.json index e1f876d..c93c37c 100644 --- a/frontend/src/i18n/locales/de/falukant.json +++ b/frontend/src/i18n/locales/de/falukant.json @@ -316,7 +316,21 @@ "noVehiclesSelected": "Keine Fahrzeuge ausgewählt.", "sendSuccess": "Fahrzeuge erfolgreich versendet!", "sendError": "Fehler beim Versenden der Fahrzeuge.", - "cancel": "Abbrechen" + "cancel": "Abbrechen", + "close": "Schließen", + "repair": "Reparieren", + "repairTooltip": "Fahrzeug reparieren", + "repairVehicleTitle": "Fahrzeug reparieren", + "repairVehicleType": "Fahrzeugtyp", + "repairCurrentCondition": "Aktueller Zustand", + "repairCost": "Reparaturkosten", + "repairBuildTime": "Reparaturdauer", + "repairBuildTimeInstant": "Sofort", + "repairBuildTimeHours": "Stunden", + "repairBuildTimeMinutes": "Minuten", + "repairConfirm": "Reparieren", + "repairSuccess": "Fahrzeug wird repariert!", + "repairError": "Fehler beim Reparieren des Fahrzeugs." }, "stocktype": { "wood": "Holzlager", diff --git a/frontend/src/views/falukant/BranchView.vue b/frontend/src/views/falukant/BranchView.vue index f0bf722..68992e8 100644 --- a/frontend/src/views/falukant/BranchView.vue +++ b/frontend/src/views/falukant/BranchView.vue @@ -108,14 +108,24 @@ - - +
+ + + +
@@ -154,7 +164,7 @@