shipping.ts raw
1 import { econtCalculate } from './econt';
2 import { getDb } from './db';
3
4 export function getShippingConfig() {
5 const db = getDb();
6 const rows = db.query("SELECT key, value FROM content WHERE key LIKE 'shipping_%' OR key = 'free_shipping_threshold'").all() as { key: string; value: string }[];
7 const cfg: Record<string, string> = {};
8 for (const r of rows) cfg[r.key] = r.value;
9 return {
10 ratePerKg: parseInt(cfg.shipping_rate_per_kg || '500') || 500, // cents per kg
11 freeThreshold: parseInt(cfg.free_shipping_threshold || '5000') || 5000, // cents
12 };
13 }
14
15 // Fixed courier rates (in euro cents) for orders up to 1 kg. Anything over
16 // 1 kg falls back to the Econt API or the per-kg rate.
17 const FIXED_ADDRESS_RATE = 522; // €5.22 - delivery to personal address
18 const FIXED_OFFICE_RATE = 395; // €3.95 - delivery to office / APS locker
19 const FIXED_RATE_MAX_KG = 1;
20
21 // shippingCostCents returns shipping cost in stotinki (euro cents).
22 // If econt is configured, calls the Econt API. Otherwise uses
23 // the configurable per-kg rate with free-shipping threshold.
24 export async function shippingCostCents(
25 totalKg: number,
26 subtotalCents: number,
27 city?: string,
28 courier?: string,
29 codAmount?: number,
30 officeDelivery?: boolean,
31 ): Promise<number> {
32 try {
33 const { freeThreshold } = getShippingConfig();
34 if (freeThreshold > 0 && subtotalCents >= freeThreshold) return 0;
35
36 // fixed rate for packages up to 1 kg (not prorated)
37 if (totalKg <= FIXED_RATE_MAX_KG) {
38 return officeDelivery ? FIXED_OFFICE_RATE : FIXED_ADDRESS_RATE;
39 }
40
41 // try econt if configured
42 const econtCfg = getEcontConfigured();
43 if (econtCfg && courier === 'econt' && city) {
44 const result = await econtCalculate(city, totalKg, codAmount || 0, officeDelivery || false);
45 if (!result.error) {
46 return Math.round(result.totalPrice * 100);
47 }
48 }
49
50 // fallback: per-kg rate
51 const { ratePerKg } = getShippingConfig();
52 return Math.round(ratePerKg * totalKg);
53 } catch {
54 const { ratePerKg, freeThreshold } = getShippingConfig();
55 if (freeThreshold > 0 && subtotalCents >= freeThreshold) return 0;
56 return Math.round(ratePerKg * totalKg);
57 }
58 }
59
60 function getEcontConfigured(): boolean {
61 try {
62 const db = getDb();
63 const row = db.query("SELECT value FROM content WHERE key = 'econt_username'").get() as { value: string } | null;
64 const pw = db.query("SELECT value FROM content WHERE key = 'econt_password'").get() as { value: string } | null;
65 return !!(row?.value && pw?.value);
66 } catch { return false; }
67 }
68
69 // sync version for stripe payment intent creation
70 export function shippingCostCentsSync(totalKg: number, subtotalCents: number, officeDelivery = false): number {
71 const { ratePerKg, freeThreshold } = getShippingConfig();
72 if (freeThreshold > 0 && subtotalCents >= freeThreshold) return 0;
73 if (totalKg <= FIXED_RATE_MAX_KG) {
74 return officeDelivery ? FIXED_OFFICE_RATE : FIXED_ADDRESS_RATE;
75 }
76 return Math.round(ratePerKg * totalKg);
77 }
78