import { Pool } from 'pg' import refreshTokenHelper from "../../utils/refreshTokenHelper" const handleFunc = async (event: any) => { const config = useRuntimeConfig() const dbConfig = { host: config.pgHost, port: parseInt(config.pgPort || '5432'), database: config.pgDatabase, user: config.pgUser, password: config.pgPassword, } if (!dbConfig.host || !dbConfig.database || !dbConfig.user || !dbConfig.password) { throw createError({ statusCode: 500, message: 'PostgreSQL credentials not configured. Please set PG_HOST, PG_DATABASE, PG_USER, PG_PASSWORD in .env' }) } // Limited roles (FrontendMenu == 'c') only see their own organization. Admins // see all orgs (NULL filter is a no-op in the SQL below). const roleCookie = getCookie(event, 'logship_role') let menuType: string = 'Menu' try { const role = roleCookie ? JSON.parse(roleCookie) : null menuType = role?.FrontendMenu?.id || role?.FrontendMenu || 'Menu' } catch {} const orgIdCookie = getCookie(event, 'logship_organization_id') const orgIdNum = orgIdCookie ? parseInt(orgIdCookie) : NaN const orgFilter: number | null = (menuType === 'c' && Number.isFinite(orgIdNum)) ? orgIdNum : null const pool = new Pool(dbConfig) try { // Counts use `shipping_date` (the real "package left" timestamp set when // the shipment is commissioned). `movementdate` was unreliable here because // it's the iDempiere business-movement date, not the actual ship date. // fri_to_mon = the most recent Friday→Monday window. "Last Monday" is the // most recent Monday on or before today; the Friday is that Monday − 3 days. const sql = ` WITH bounds AS ( SELECT CURRENT_DATE AS today, (CURRENT_DATE - ((EXTRACT(DOW FROM CURRENT_DATE)::int + 6) % 7))::date AS last_monday, ((CURRENT_DATE - ((EXTRACT(DOW FROM CURRENT_DATE)::int + 6) % 7)) - 3)::date AS prev_friday ) SELECT COUNT(*) FILTER (WHERE ino.shipping_date::date = b.today) AS today, COUNT(*) FILTER (WHERE ino.shipping_date::date = b.today - 1) AS yesterday, COUNT(*) FILTER (WHERE ino.shipping_date::date >= b.today - 2) AS last_3_days, COUNT(*) FILTER (WHERE ino.shipping_date::date BETWEEN b.prev_friday AND b.last_monday) AS fri_to_mon, COUNT(*) FILTER (WHERE ino.shipping_date::date >= b.today - 6) AS last_7_days, COUNT(*) FILTER (WHERE ino.shipping_date::date >= b.today - 29) AS last_30_days, b.prev_friday::text AS fri_to_mon_from, b.last_monday::text AS fri_to_mon_to FROM m_inout ino CROSS JOIN bounds b WHERE ino.issotrx = 'Y' AND ino.isactive = 'Y' AND ino.docstatus = 'CO' AND ino.c_doctype_id = 1000011 AND ino.movementtype = 'C-' AND ino.ad_client_id = 1000000 AND ($1::int IS NULL OR ino.ad_org_id = $1) GROUP BY b.prev_friday, b.last_monday ` const result = await pool.query(sql, [orgFilter]) const row: any = result.rows[0] || {} return { today: parseInt(row.today || 0), yesterday: parseInt(row.yesterday || 0), last3Days: parseInt(row.last_3_days || 0), friToMon: parseInt(row.fri_to_mon || 0), last7Days: parseInt(row.last_7_days || 0), last30Days: parseInt(row.last_30_days || 0), friToMonFrom: row.fri_to_mon_from || null, friToMonTo: row.fri_to_mon_to || null } } catch (err: any) { console.error('PostgreSQL query error:', err) throw createError({ statusCode: 500, message: `Database error: ${err.message}` }) } finally { await pool.end() } } export default defineEventHandler(async (event) => { try { return await handleFunc(event) } catch(err: any) { try { let authToken: any = await refreshTokenHelper(event) return await handleFunc(event) } catch(error: any) { console.error('Fatal error in shipped-stats:', error) throw error } } })