/** * Company info used on every report header (PDF + print). Read from * the environment so the office can edit it without a code change — * the .env.example file lists the keys; defaults below are placeholders * the office should override for production. * * Single source of truth: the API renders the header. The web header * (login + AppShell) still reads the static "Jorge Cuadros & Asociados" * strings for now — those are visual brand, the API's COMPANY_INFO * block is the legal/locator block on printed documents. */ import * as fs from "node:fs"; import * as path from "node:path"; export interface CompanyInfo { name: string; /** Street address — line 1. */ addressLine1: string; /** Street address — line 2 (suite, floor, etc.). Optional. */ addressLine2: string; /** "City, State, ZIP, Country" — single line. */ cityState: string; phone: string; email: string; /** Mexican tax ID ("RFC"). Optional. */ taxId: string; website: string; /** Absolute path to the logo PNG. Null when missing — renderers fall * back to a text mark. */ logoPath: string | null; /** Logo buffer + intrinsic size, eagerly loaded so the PDF renderer * doesn't do a sync read on every report. Null when no logo. */ logo: { buffer: Buffer; width: number; height: number } | null; } function envOr(key: string, fallback: string): string { const v = process.env[key]; return v && v.trim() ? v : fallback; } function resolveLogoPath(): string | null { const explicit = process.env.COMPANY_LOGO_PATH; if (explicit) { return fs.existsSync(explicit) ? explicit : null; } // Default: look in apps/api/assets/company_logo.png (copied from // apps/web/public/images/company_logo.png — single canonical image // kept in lock-step; see .env.example for the override path). const candidates = [ path.resolve(__dirname, "..", "..", "assets", "company_logo.png"), path.resolve(__dirname, "..", "..", "..", "web", "public", "images", "company_logo.png"), ]; for (const c of candidates) { if (fs.existsSync(c)) return c; } return null; } let cached: CompanyInfo | null = null; export function getCompanyInfo(): CompanyInfo { if (cached) return cached; const logoPath = resolveLogoPath(); let logo: CompanyInfo["logo"] = null; if (logoPath) { try { const buf = fs.readFileSync(logoPath); // Intrinsic PNG size: read IHDR (bytes 16-23 of the file). // Width = BE uint32 at offset 16, height = BE uint32 at offset 20. const w = logoPath.endsWith(".png") && buf.length >= 24 ? buf.readUInt32BE(16) : 0; const h = logoPath.endsWith(".png") && buf.length >= 24 ? buf.readUInt32BE(20) : 0; logo = { buffer: buf, width: w, height: h }; } catch { logo = null; } } cached = { name: envOr("COMPANY_NAME", "Jorge Cuadros & Asociados"), addressLine1: envOr( "COMPANY_ADDRESS_LINE1", "Av. Revolución 1234, Int. 5", ), addressLine2: envOr("COMPANY_ADDRESS_LINE2", ""), cityState: envOr( "COMPANY_CITY_STATE", "Tijuana, Baja California 22000, México", ), phone: envOr("COMPANY_PHONE", "(664) 000-0000"), email: envOr("COMPANY_EMAIL", "contacto@jorgecuadros.local"), taxId: envOr("COMPANY_TAX_ID", ""), website: envOr("COMPANY_WEBSITE", "jorgecuadros.local"), logoPath, logo, }; return cached; }