Add vehicle management features in Falukant

- Introduced vehicle types and transport management in the backend, including new models and associations for vehicles and transports.
- Implemented service methods to retrieve vehicle types and handle vehicle purchases, ensuring user validation and transaction management.
- Updated the FalukantController and router to expose new endpoints for fetching vehicle types and buying vehicles.
- Enhanced the frontend with a new transport tab in BranchView, allowing users to buy vehicles, and added localization for vehicle-related terms.
- Included initialization logic for vehicle types in the database setup.
This commit is contained in:
Torsten Schulz (local)
2025-11-24 20:15:45 +01:00
parent 5ed27e5a6a
commit 3f043fc315
14 changed files with 544 additions and 12 deletions

View File

@@ -57,6 +57,8 @@ import UndergroundType from '../models/falukant/type/underground.js';
import Notification from '../models/falukant/log/notification.js';
import PoliticalOffice from '../models/falukant/data/political_office.js';
import Underground from '../models/falukant/data/underground.js';
import VehicleType from '../models/falukant/type/vehicle.js';
import Vehicle from '../models/falukant/data/vehicle.js';
function calcAge(birthdate) {
const b = new Date(birthdate); b.setHours(0, 0);
@@ -448,6 +450,55 @@ class FalukantService extends BaseService {
return FalukantStock.findAll({ where: { regionId: b.regionId, userId: u.id } });
}
async getVehicleTypes(hashedUserId) {
// Validate user existence, but we don't filter by user here
await getFalukantUserOrFail(hashedUserId);
return VehicleType.findAll();
}
async buyVehicles(hashedUserId, { vehicleTypeId, quantity, regionId }) {
const user = await getFalukantUserOrFail(hashedUserId);
const qty = Math.max(1, parseInt(quantity, 10) || 0);
const type = await VehicleType.findByPk(vehicleTypeId);
if (!type) {
throw new Error('Vehicle type not found');
}
const totalCost = type.cost * qty;
if (user.money < totalCost) {
throw new PreconditionError('insufficientFunds');
}
// Ensure the region exists (and is part of Falukant map)
const region = await RegionData.findByPk(regionId);
if (!region) {
throw new Error('Region not found');
}
// Update money and create vehicles in a transaction
await sequelize.transaction(async (tx) => {
await updateFalukantUserMoney(
user.id,
-totalCost,
'buy_vehicles',
user.id
);
const records = [];
for (let i = 0; i < qty; i++) {
records.push({
vehicleTypeId: type.id,
falukantUserId: user.id,
regionId: region.id,
});
}
await Vehicle.bulkCreate(records, { transaction: tx });
});
return { success: true };
}
async createStock(hashedUserId, branchId, stockData) {
const u = await getFalukantUserOrFail(hashedUserId);
const b = await getBranchOrFail(u.id, branchId);