chore: add standard international tariff seeder script
This commit is contained in:
@@ -0,0 +1,165 @@
|
|||||||
|
/**
|
||||||
|
* Tripz - Global Tariffs Seed Script
|
||||||
|
*
|
||||||
|
* This script seeds the `tripz_tariffs` table with internationally recognized
|
||||||
|
* service classes (e.g. Economy, Comfort, XL/Van, Saver) and standard time windows.
|
||||||
|
*
|
||||||
|
* It maps legacy flat pricing models into the modern Tripz JSON-based TariffDefinition.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
// Standard, internationally recognized service classes
|
||||||
|
const ServiceClasses = {
|
||||||
|
ECONOMY: 'economy', // Standard, affordable everyday rides (formerly 'speed')
|
||||||
|
COMFORT: 'comfort', // Newer, more spacious vehicles
|
||||||
|
LADY: 'lady', // Female drivers for female riders
|
||||||
|
ELECTRIC: 'electric', // EV/Green vehicles
|
||||||
|
VAN: 'van', // High capacity (XL) vehicles
|
||||||
|
DELIVERY: 'delivery', // Package / courier delivery
|
||||||
|
VIP: 'vip', // Premium / Luxury rides (formerly 'mishwarVip')
|
||||||
|
FIXED: 'fixed', // Fixed route/fare rides
|
||||||
|
SAVER: 'saver' // Discounted / shared rides (formerly 'awfar')
|
||||||
|
};
|
||||||
|
|
||||||
|
// Global pricing configurations per country
|
||||||
|
const countryConfigs = [
|
||||||
|
{
|
||||||
|
country: 'jordan',
|
||||||
|
currency: 'JOD',
|
||||||
|
commissionPercent: 14.0,
|
||||||
|
baseFare: 0.0,
|
||||||
|
// Note: In the legacy system, Jordan's per-minute and per-km values were structurally swapped.
|
||||||
|
// We have normalized them here to follow standard logic (perKm is higher than perMin).
|
||||||
|
serviceRates: {
|
||||||
|
[ServiceClasses.ECONOMY]: { perKm: 0.184, perMin: 0.043 },
|
||||||
|
[ServiceClasses.COMFORT]: { perKm: 0.212, perMin: 0.056 },
|
||||||
|
[ServiceClasses.LADY]: { perKm: 0.184, perMin: 0.047 },
|
||||||
|
[ServiceClasses.ELECTRIC]: { perKm: 0.184, perMin: 0.052 },
|
||||||
|
[ServiceClasses.VAN]: { perKm: 0.250, perMin: 0.065 }, // Adjusted perKm for Van proportionality
|
||||||
|
[ServiceClasses.DELIVERY]: { perKm: 0.150, perMin: 0.039 },
|
||||||
|
[ServiceClasses.VIP]: { perKm: 0.300, perMin: 0.060 },
|
||||||
|
[ServiceClasses.FIXED]: { perKm: 0.184, perMin: 0.043 },
|
||||||
|
[ServiceClasses.SAVER]: { perKm: 0.150, perMin: 0.037 },
|
||||||
|
},
|
||||||
|
timeMultipliers: {
|
||||||
|
normal: 1.0, // 08:00 - 16:00
|
||||||
|
peak: 1.15, // 16:00 - 22:00 (15% surge)
|
||||||
|
late: 1.0 // 22:00 - 08:00
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
country: 'egypt',
|
||||||
|
currency: 'EGP',
|
||||||
|
commissionPercent: 12.0,
|
||||||
|
baseFare: 0.0,
|
||||||
|
serviceRates: {
|
||||||
|
[ServiceClasses.ECONOMY]: { perKm: 3.5, perMin: 0.8 },
|
||||||
|
[ServiceClasses.COMFORT]: { perKm: 5.0, perMin: 0.9 },
|
||||||
|
[ServiceClasses.LADY]: { perKm: 9.0, perMin: 1.0 },
|
||||||
|
[ServiceClasses.ELECTRIC]: { perKm: 0.8, perMin: 0.5 },
|
||||||
|
[ServiceClasses.VAN]: { perKm: 1.4, perMin: 1.0 },
|
||||||
|
[ServiceClasses.DELIVERY]: { perKm: 1.3, perMin: 0.8 },
|
||||||
|
[ServiceClasses.VIP]: { perKm: 5.5, perMin: 1.2 },
|
||||||
|
[ServiceClasses.FIXED]: { perKm: 5.5, perMin: 1.0 },
|
||||||
|
[ServiceClasses.SAVER]: { perKm: 3.0, perMin: 0.7 },
|
||||||
|
},
|
||||||
|
timeMultipliers: {
|
||||||
|
normal: 1.0,
|
||||||
|
peak: 1.75, // 1.4 EGP peak / 0.8 EGP normal = 1.75x
|
||||||
|
late: 1.87 // 1.5 EGP late / 0.8 EGP normal = 1.87x
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
country: 'syria',
|
||||||
|
currency: 'SYP',
|
||||||
|
commissionPercent: 11.0,
|
||||||
|
baseFare: 0.0,
|
||||||
|
serviceRates: {
|
||||||
|
[ServiceClasses.ECONOMY]: { perKm: 40, perMin: 9 },
|
||||||
|
[ServiceClasses.COMFORT]: { perKm: 44, perMin: 10 },
|
||||||
|
[ServiceClasses.LADY]: { perKm: 63, perMin: 12 },
|
||||||
|
[ServiceClasses.ELECTRIC]: { perKm: 9, perMin: 5 },
|
||||||
|
[ServiceClasses.VAN]: { perKm: 10, perMin: 8 },
|
||||||
|
[ServiceClasses.DELIVERY]: { perKm: 20, perMin: 9 },
|
||||||
|
[ServiceClasses.VIP]: { perKm: 26, perMin: 12 },
|
||||||
|
[ServiceClasses.FIXED]: { perKm: 26, perMin: 10 },
|
||||||
|
[ServiceClasses.SAVER]: { perKm: 36, perMin: 8 },
|
||||||
|
},
|
||||||
|
timeMultipliers: {
|
||||||
|
normal: 1.0,
|
||||||
|
peak: 1.11, // 10 peak / 9 normal = 1.11x
|
||||||
|
late: 1.22 // 11 late / 9 normal = 1.22x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
function generateSql() {
|
||||||
|
let sql = `-- Tripz Standard Tariffs Seed Script\n`;
|
||||||
|
sql += `-- Generated with international ride-hailing naming conventions.\n\n`;
|
||||||
|
|
||||||
|
for (const config of countryConfigs) {
|
||||||
|
const city = config.country;
|
||||||
|
|
||||||
|
for (const [serviceClass, rates] of Object.entries(config.serviceRates)) {
|
||||||
|
|
||||||
|
const definition = {
|
||||||
|
currency: config.currency,
|
||||||
|
mode: 'time_and_distance',
|
||||||
|
booking_fee: config.baseFare,
|
||||||
|
min_fare: config.baseFare > 0 ? config.baseFare : (rates.perKm * 2), // Min fare = 2 KMs default
|
||||||
|
windows: [
|
||||||
|
{
|
||||||
|
name: 'normal',
|
||||||
|
from: '08:00',
|
||||||
|
to: '16:00',
|
||||||
|
flag: config.baseFare,
|
||||||
|
per_km: rates.perKm,
|
||||||
|
per_min: rates.perMin,
|
||||||
|
per_min_waiting: Number((rates.perMin * 1.5).toFixed(3))
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'peak',
|
||||||
|
from: '16:00',
|
||||||
|
to: '22:00',
|
||||||
|
flag: config.baseFare,
|
||||||
|
per_km: Number((rates.perKm * config.timeMultipliers.peak).toFixed(3)),
|
||||||
|
per_min: Number((rates.perMin * config.timeMultipliers.peak).toFixed(3)),
|
||||||
|
per_min_waiting: Number((rates.perMin * config.timeMultipliers.peak * 1.5).toFixed(3))
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'late',
|
||||||
|
from: '22:00',
|
||||||
|
to: '08:00',
|
||||||
|
flag: config.baseFare,
|
||||||
|
per_km: Number((rates.perKm * config.timeMultipliers.late).toFixed(3)),
|
||||||
|
per_min: Number((rates.perMin * config.timeMultipliers.late).toFixed(3)),
|
||||||
|
per_min_waiting: Number((rates.perMin * config.timeMultipliers.late * 1.5).toFixed(3))
|
||||||
|
}
|
||||||
|
],
|
||||||
|
commission: {
|
||||||
|
percent: config.commissionPercent
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const defJson = JSON.stringify(definition).replace(/'/g, "''"); // Escape single quotes for SQL
|
||||||
|
|
||||||
|
sql += `INSERT INTO tripz_tariffs (id, tenant_id, city, service_class, version, active, definition)\n`;
|
||||||
|
sql += `VALUES (\n`;
|
||||||
|
sql += ` gen_random_uuid(),\n`;
|
||||||
|
sql += ` (SELECT id FROM tripz_tenants WHERE slug = 'siro' LIMIT 1),\n`;
|
||||||
|
sql += ` '${city}',\n`;
|
||||||
|
sql += ` '${serviceClass}',\n`;
|
||||||
|
sql += ` 1,\n`;
|
||||||
|
sql += ` true,\n`;
|
||||||
|
sql += ` '${defJson}'::jsonb\n`;
|
||||||
|
sql += `);\n\n`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sqlOutput = generateSql();
|
||||||
|
fs.writeFileSync(__dirname + '/seed-tariffs.sql', sqlOutput);
|
||||||
|
console.log('✅ Generated seed-tariffs.sql successfully with international naming standards.');
|
||||||
Reference in New Issue
Block a user