Add vehicle repair functionality in Falukant module

- Implemented a new repairVehicle method in FalukantService to handle vehicle repairs, including cost calculation and precondition checks.
- Updated FalukantController to expose the repairVehicle endpoint, allowing users to initiate repairs via the API.
- Enhanced FalukantRouter to include a new route for vehicle repairs.
- Modified BranchView component to add UI elements for repairing vehicles, including a dialog for repair confirmation and displaying repair details.
- Updated German localization files to include translations related to vehicle repair actions, improving user experience for German-speaking users.
This commit is contained in:
Torsten Schulz (local)
2025-12-05 14:40:55 +01:00
parent 0544a3dfde
commit 74a3d59800
6 changed files with 347 additions and 12 deletions

View File

@@ -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 }
);
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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();