couriers.ts raw
1 import { getDb } from './db';
2
3 export interface CourierOffice {
4 id: string;
5 name: string;
6 city: string;
7 address: string;
8 courier: string;
9 }
10
11 export async function refreshEcontOffices(): Promise<number> {
12 const resp = await fetch('https://ee.econt.com/services/Nomenclatures/NomenclaturesService.getOffices.json', {
13 method: 'POST',
14 headers: { 'Content-Type': 'application/json' },
15 body: JSON.stringify({ countryCode: 'BGR' }),
16 });
17 if (!resp.ok) throw new Error(`econt api: ${resp.status}`);
18 const data = await resp.json() as any;
19 const offices = data.offices || [];
20 if (offices.length === 0) throw new Error('econt returned no offices');
21 const db = getDb();
22 const upsert = db.prepare(`INSERT INTO offices (id, courier, name, city, address, updated_at)
23 VALUES (?, 'econt', ?, ?, ?, datetime('now'))
24 ON CONFLICT(id) DO UPDATE SET name = excluded.name, city = excluded.city, address = excluded.address, updated_at = excluded.updated_at`);
25 const tx = db.transaction((items: any[]) => {
26 for (const o of items) {
27 upsert.run(String(o.id), o.name, o.address?.city?.name || '', o.address?.fullAddress || o.name);
28 }
29 });
30 tx(offices);
31 return offices.length;
32 }
33
34 export async function refreshSpeedyOffices(): Promise<number> {
35 const user = process.env.SPEEDY_USERNAME;
36 const pass = process.env.SPEEDY_PASSWORD;
37 if (!user || !pass) return 0;
38 const resp = await fetch('https://api.speedy.bg/v1/location/office', {
39 method: 'POST',
40 headers: { 'Content-Type': 'application/json' },
41 body: JSON.stringify({ userName: user, password: pass, language: 'BG' }),
42 });
43 if (!resp.ok) throw new Error(`speedy api: ${resp.status}`);
44 const data = await resp.json() as any;
45 const offices = data.offices || [];
46 if (offices.length === 0) throw new Error('speedy returned no offices');
47 const db = getDb();
48 const upsert = db.prepare(`INSERT INTO offices (id, courier, name, city, address, updated_at)
49 VALUES (?, 'speedy', ?, ?, ?, datetime('now'))
50 ON CONFLICT(id) DO UPDATE SET name = excluded.name, city = excluded.city, address = excluded.address, updated_at = excluded.updated_at`);
51 const tx = db.transaction((items: any[]) => {
52 for (const o of items) {
53 upsert.run(String(o.id), o.name, o.address?.siteName || o.siteName || '', o.address?.fullAddress || o.address?.localAddress || o.name);
54 }
55 });
56 tx(offices);
57 return offices.length;
58 }
59
60 export async function refreshAllOffices(): Promise<{ econt: number; speedy: number }> {
61 const db = getDb();
62 db.run('DELETE FROM offices');
63 const econt = await refreshEcontOffices().catch(() => 0);
64 const speedy = await refreshSpeedyOffices().catch(() => 0);
65 return { econt, speedy };
66 }
67
68 export function getOfficesByCourier(courier: string): CourierOffice[] {
69 return getDb().query('SELECT id, courier, name, city, address FROM offices WHERE courier = ? ORDER BY city, name').all(courier) as CourierOffice[];
70 }
71
72 export function getCities(): string[] {
73 const rows = getDb().query('SELECT DISTINCT city FROM offices ORDER BY city').all() as { city: string }[];
74 if (rows.length > 0) return rows.map(r => r.city);
75 return ['София', 'Пловдив', 'Варна', 'Бургас', 'Русе', 'Стара Загора', 'Плевен', 'Велико Търново', 'Благоевград', 'Добрич', 'Шумен', 'Перник'];
76 }
77
78 export function courierSupportsOffices(courier: string): boolean {
79 return courier === 'econt';
80 }
81