Files
tripz-llc/backend/src/database/seeds/generate-tariff-sql.ts
T
Hamza-AyedandClaude Opus 4.8 e9beeb90b4 feat: rebuild pricing on Siro's live kazan table + close two fare leaks
## الأرقام صارت من جدول `kazan` الحيّ لا من تقدير
العمولة 11٪/12٪/14٪ مطابقة حرفياً. وثلاثة تصحيحات على الخام:
- **محورا الأردن مقلوبان**: أعمدة الفئات تحمل سعر الدقيقة (0.043) وأعمدة
  النوافذ سعر الكيلومتر (0.184) — عكس مصر وسوريا. قراءتها كما هي تجعل
  رحلة 27 كم بأقلّ من دينارين.
- **نِسَب الفئات في مصر وسوريا معطوبة**: الكهربائي 0.23 والفان 0.25–0.40 من
  الاقتصادي، أي فان أرخص من اقتصادي. نِسَب الأردن وحدها متماسكة فاعتُمدت.
- سعر الكيلو في مصر 4 جنيهات لا 3.5 (قرار المالك).

## البنية صارت مطابقة لسيرو — وهي ما سيكتب فيه بوت استخبارات الأسعار
سعر الكيلو يتغيّر بالفئة فقط (رقم يعرفه السوق)، وسعر الدقيقة بالنافذة فقط.
**لا فتحة عدّاد** (`startPrice = 0` في كل صفوف سيرو): الحدّ الأدنى أرضية لا
رسم يُضاف — «ما في رحلة أقل من 20 جنيهاً». `fuelPrice` محفوظ كمدخل للبوت.

## تسريبان أغلقهما الاختبار الحيّ (بموافقة المالك)
1. **فئة مجهولة = رحلة مجانية**: `service_class` يأتي من الطلب بلا تحقّق،
   والاستثناء كان يُبتلع فتُنشأ رحلة بـ`quoted_fare = null` ثم تُسوّى بصفر.
   صار الطلب يُرفض بـ400 — الفشل الصريح أأمن من الصامت.
2. **`fixed` لا تُزرع**: سعر ثابت بلا جدول خطوط يعني رقماً واحداً لكل
   الوجهات — أظهر الاختبار رحلة 55.8 كم بـ2.5 ديناراً مقابل 23.2 للاقتصادي.

300 اختبار خضر.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-19 01:38:20 +03:00

61 lines
3.2 KiB
TypeScript

import { writeFileSync } from 'fs';
import { join } from 'path';
import {
buildDefinition,
COUNTRY_TARIFFS,
SEEDED_CLASSES,
} from '../../modules/tariff/default-tariffs';
/**
* يولّد `tariffs.sql` لتطبيقها على قاعدة **قائمة** — للمستأجرين الموجودين
* قبل أن يصير الزرع تلقائياً.
*
* التشغيل: `npx ts-node src/database/seeds/generate-tariff-sql.ts`
*
* مستأجر جديد لا يحتاج هذا الملف إطلاقاً: `SeedService` يزرع له التعرفة من
* **نفس** `default-tariffs.ts` عند الإقلاع. هذا المولّد للحاق بالقائم فقط،
* ولذلك يقرأ من مصدر الحقيقة نفسه — نسختان من أرقام التسعير كانتا ستتباعدان.
*
* الناتج **آمن للتكرار**: كل تشغيل يطفئ الفعّالة ويُدرج `max(version)+1`،
* فلا يبقى صفّان فعّالان لنفس (مستأجر، مدينة، فئة) — و`getActive` ترتّب
* `version DESC` فكان الاختيار بينهما غير محدَّد.
*/
function generateSql(): string {
let sql = `-- Tripz — تعرفة الانطلاق الافتراضية لكل بلد.\n`;
sql += `-- مولَّد من src/database/seeds/generate-tariff-sql.ts — لا يُحرَّر يدوياً.\n`;
sql += `-- المصدر: src/modules/tariff/default-tariffs.ts\n`;
sql += `-- آمن للتكرار: كل تشغيل يطفئ الفعّالة ويُدرج نسخة أعلى.\n\n`;
sql += `BEGIN;\n\n`;
for (const country of COUNTRY_TARIFFS) {
sql += `-- ============ ${country.countryPack.toUpperCase()} (${country.currency}) ============\n`;
for (const serviceClass of SEEDED_CLASSES) {
const definition = buildDefinition(country, serviceClass);
const defJson = JSON.stringify(definition).replace(/'/g, "''");
// يطال **كل** مستأجر في هذا البلد لا سيرو وحده: تعرفة الانطلاق عامة،
// وربطها بمستأجر واحد كان يترك كل مستأجر آخر بلا تسعير.
sql += `-- ${serviceClass}\n`;
sql += `UPDATE tripz_tariffs SET active = false\n`;
sql += ` WHERE city = 'default' AND service_class = '${serviceClass}' AND active = true\n`;
sql += ` AND tenant_id IN (SELECT id FROM tripz_tenants WHERE country_pack = '${country.countryPack}');\n`;
sql += `INSERT INTO tripz_tariffs (id, tenant_id, city, service_class, version, active, definition)\n`;
sql += `SELECT uuid_generate_v4(), t.id, 'default', '${serviceClass}',\n`;
sql += ` COALESCE((SELECT MAX(version) FROM tripz_tariffs x\n`;
sql += ` WHERE x.tenant_id = t.id AND x.city = 'default'\n`;
sql += ` AND x.service_class = '${serviceClass}'), 0) + 1,\n`;
sql += ` true, '${defJson}'::jsonb\n`;
sql += ` FROM tripz_tenants t WHERE t.country_pack = '${country.countryPack}';\n\n`;
}
}
sql += `COMMIT;\n`;
return sql;
}
const out = join(__dirname, 'tariffs.sql');
writeFileSync(out, generateSql());
// eslint-disable-next-line no-console
console.log(`✅ generated ${out}`);