Release history and version notes
/requirements submission — the cart badge persisted after the success page. Now req.session.cart is emptied alongside the OTP session data./requirements from 4 steps to 5: Parts → Type → Verify Email → Verify Mobile → Details.createNote() helper to Zoho CRM integration library.GET /admin/api/health endpoint returning uptime, memory usage, DB pool stats, system load, active sessions, and chat activity./products/delco-remy (a non-existent brand page), causing 404 errors. Added explicit URL rules to the AI system prompt forbidding invented URLs and clarifying the only valid URL patterns for products, series, and site pages.inherit so the A+ toggle correctly resizes all chat history, bot replies, and transliteration suggestions./api/chat/transliterate endpoint with in-memory caching (separate from admin Translation Center endpoint)./delco-remy/50mt to /products/delco-remy/series/50mt (supports multi-brand series too)./products/{slug} to /products/delco-remy/{sku} matching actual route.newindo.com prefix prepended./products/brand/series/ URL pattern.message parameter from DLT portal template ID to Fast2SMS internal Message ID (215747). SMS OTP now sends successfully.route=otp to route=dlt on the Fast2SMS API as required by TRAI DLT regulations.NEWIN), Template ID, and Entity ID.newindo.com/products).#4A4A4A to #1A1A1A for better readability on the beige background.#1A5F8B) that stands out from message text.category column from articles indexer query.status = 'published' instead of active = 1 (articles table uses status enum).status = 'published' instead of active = 1.active = 1 filter from industries (table has no active column — all rows should be indexed)./api/chat/ to CSRF exempt list (widget has no session token; endpoints are rate-limited).scripts/fix-chat-columns.js) for servers where ALTER TABLE ... ADD COLUMN IF NOT EXISTS silently fails.search_show_prices setting — when prices are disabled, the Price and Total columns, estimate total row, and pricing disclaimer are all omitted from the customer email.search_show_prices is disabled in admin settings, matching the home page search behavior.required off the hidden Company field when Personal is selected — browser constraint validation was silently blocking form submission because the required field was hidden.onFail: 'json' to reCAPTCHA middleware on POST /requirements — without it, failed token verification returned a 302 redirect instead of JSON, which the AJAX handler couldn't process.include('partials/recaptcha') with inline reCAPTCHA integration that acquires the token inside the existing AJAX handler, then submits via fetch.table-responsive wrappers.clamp() for mobile scaling.img { max-width: 100%; height: auto; } safety net for oversized images.createReadStream().pipe() with Express res.download() for archive downloads — fixes files cutting off at ~500MB under Passenger.href references in other page body content..container wrapper matching all other pages.Upload_Images, Upload_Photos, and Upload_Documents_Images are now rendered inline, one per row at full content width.account.Billing_City and account.Billing_State.safeText() sanitizer to strip non-Latin-1 characters that crash pdf-lib's Helvetica (WinAnsi) font.Related_To lookup (falls back to Contact_Name).Billing_City, Billing_State).Customer_Comments) in the main table position after Mileage.Customer_s_Remarks) to the last row of the table, after all observations.Company_No on the Account (via getAccount), not the non-existent Customer_Code field on the Case.Customer_Code from WARRANTY_FIELDS_EXTRA (was causing extended fetch to fail).PO_Number_Quotation_No to base warranty field list so it's always fetched (not lost in extended-field fallback).PO_Number to the correct lookup field PO_Number_Quotation_No in the Zoho fetch list and PDF generator.WARRANTY_FIELDS into base (for search/list) and extended (for single-record PDF fetch) so unrecognised field names don't break the warranty claims listing.Customer_Code, Warranty_Type, Warranty_Coverage, Warranty_Coverage_Used, Customer_City, Customer_State, Failed_Part_Return, Failed_Part_Return_Method, Visual_Observation, PO_Number, Claimed_Value, Related_To.warranty-pdf.js to match the Zoho CRM print layout: merged observations into the main table, added asterisks to mandatory labels, bold values for remarks and observations, Rs. currency format, column dividers in parts table, corrected confirmation text and footer.Product_Quantity save payload and Zoho response.Related_To lookup to the logged-in portal contact, alongside Contact_Name.Related_To so the field stays populated after updates.Electrical_Observation to Electrical_Report across all references (inline edit, claim creation, field fetch list, PDF export, and detail template).File_Id__s (not file_id) when attaching a ZFS-uploaded file to a record field. Fixed the PUT payload in uploadFieldAttachment.POST /crm/v7/files (ZFS upload) endpoint requires ZohoCRM.files.CREATE which was missing from the OAuth scope list. After deploying, disconnect and reconnect Zoho CRM in admin to re-grant with the new scope.uploadFieldAttachment to use Zoho’s two-step ZFS upload flow — files are first uploaded via POST /crm/v7/files, then the returned encrypted file ID is attached to the record field with a PUT. The previous single-endpoint approach (upload_fields_attachment) returned INVALID_REQUEST_METHOD on CRM v7.parseInt(value) || null pattern that could lose valid numeric values; now uses isNaN check instead.node_modules, .git, etc.) via rsync — only the backups/ folder itself is excluded to prevent recursion..env, settings JSON, and uploads.?skus=SKU:qty for both logged-in portal users and anonymous visitors, so the requirements form pre-fills part numbers from the enquiry cart.cartSkuParam middleware variable that builds the SKU:qty query string from the session cart.?skus=… so cart contents pre-fill the portal RFQ form./portal/rfq for logged-in portal users.rfqBase variable used <%= %> (HTML-escaped) inside <script> blocks, turning quotes into ' entities and producing a JS SyntaxError. This killed the entire script block on product detail, products catalog, and enquiry cart pages — breaking both +RFQ buttons and Send RFQ links.<%- %> (raw output) for all in-script EJS expressions. Removed debug logging./portal/rfq instead of passing through the intermediate /requirements 302 redirect that was silently dropping query parameters (SKUs and quantities).['page','query','date'] request. Google suppresses multi-dimensional rows when traffic volume is low, which returned 0 rows for sites with under ~200 impressions./snapshots/* to /archives/* to avoid WAF keyword triggers.ipKeyGenerator() helper for IPv6-safe IP extraction, fixing a ValidationError that prevented the app from starting after an express-rate-limit update.backups/ directory instead of the system temp folder. Archives survive server restarts and are no longer lost after download.gsc_client_id and gsc_client_secret in Admin → Settings → Integrations, then click Connect on the SEO dashboard..tar.gz backup from Reports → Download Backup. Includes the entire app directory (code, views, uploads), a MySQL database dump, .env configuration, and a settings.json export.super_admin role only. Every download is logged in the audit trail.RECAPTCHA_SITE_KEY and RECAPTCHA_SECRET in environment).admin_login_attempts for forensic review.fetch() to /admin/editor/upload. Click the landscape-icon button next to the existing Image button, pick one or more files, and they’re uploaded + inserted inline.POST /admin/editor/upload — accepts image files (JPG, PNG, WebP, GIF up to 10 MB) and returns the Jodit-compatible JSON response./quote) no longer has its own contact form. Instead, a “Send a RFQ” button redirects to the /requirements page with all cart SKUs and quantities pre-filled.?skus= query param (e.g. ?skus=10461457:3,10479048:2) and auto-fill the requirements form with correct quantities. Supported on both the public requirements form and portal RFQ page.REDIS_PREFIX environment variable so theta and production can share the same Redis server without key collisions. Sessions, translation cache, and globals cache are now fully isolated between environments. Defaults to ni: if not set (backward-compatible)./sitemap.xml now gracefully handle missing tables or columns instead of returning a blank error page. Added error logging to help diagnose schema gaps./admin, /portal, API webhooks, and static assets remain accessible while maintenance mode is active so administrators can continue working.Product JSON-LD with SKU, brand, price/availability offers, and technical specs as additionalProperty.Article JSON-LD with headline, author, publisher, and dates.FAQPage structured data with all Q&A pairs for rich snippet eligibility.hreflang alternate links for English, Hindi, and x-default to signal multilingual content to search engines.X-Content-Type-Options: nosniff, restrictive CSP, and Cache-Control: private.return_to parameter is now restricted to /portal/ paths only.viewer role is read-only; finance can edit account details but not submit RFQs or warranty claims./requirements are now automatically redirected to the portal RFQ form with their cart items carried through.?skus=X,Y,Z query parameters for catalog cart passthrough.crm_match_type and crm_deal_id columns in quotations table. Admin quotation detail shows “Existing CRM Account/Contact” badge when a match is detected./requirements/success GET route for post-submit redirect.formatIST() helper replaces inconsistent toLocaleDateString() calls.search_term column). Shown in admin quotation detail, admin email, and customer confirmation email when it differs from the matched SKU./requirements with selected part numbers pre-filled.?skus= query param.POST /quote/remove now returns JSON for AJAX callers.display:block)..lead-badge and .enquiry-add-btn CSS — light fill + thin border (A2 style), body font, high-contrast dark text on white cards.pd.leadTime, cta.enquiryShort, and catalog.added locale keys across all 11 languages.min-width:0 to .admin-main so flex child respects viewport bounds..table-responsive CSS for horizontal scroll on wide tables.biz.* locale keys).scripts/migrate-moq-spec.js) moves “Minimum Order Qty” from product specs into the dedicated moq column and deletes the redundant spec rows.moq (Minimum Order Quantity) column on products, editable in admin product form.On Hand − Hard Committed − Soft Reserved (Incoming no longer added).On Order + ATP.trackingClientIp() helper reads X-Forwarded-For and X-Real-IP headers directly, with ::ffff: prefix stripping for clean IPv4 storage.portal_user_accounts table persists the user–account links, refreshed on each login.Payment_Cycle_Days across all invoices, color-coded green/amber/red (≤30 / ≤60 / >60 days).[object Object] on account view and edit pages — now correctly extracts the lookup name.GSTN_No with GST_Number fallback.getAccountBusinessStats() fetches all invoices and quotes for an account (paginating up to 2,000 records) and computes totals using Indian FY (April–March)./admin/api/cache/stats returns Redis memory and keyspace statistics./contact form to the internal service desk: submissions create a ticket (category “other”) and send a confirmation email, exactly like the Service Desk page.partials/recaptcha.ejs — drop-in include for any form; auto-disabled when keys are unset.lib/recaptcha.js: verifies tokens with Google, blocks low-score bots, fails open on network error to avoid blocking real users.express-rate-limit: 10 submissions / 15 min for forms, 5 / 15 min for login.GET /contact route near the bottom of public.js.t() locale-file override in header.ejs: nav labels now come exclusively from the database via tr(), making the admin Navigation editor the single source of truth.nav.* keys in locale JSON files would silently override whatever the admin had set (e.g. “Contact Us” in admin rendered as “contact us” on the site).lib/redis.js: shared ioredis client (Unix socket at /tmp/redis.sock) with graceful degradation—site works without Redis, just loses the speed-up.lib/globals-cache.js: Redis-backed cache for settings, categories, footer links, and nav items. 1-hour TTL with explicit invalidation on admin saves.lib/i18n.js) now checks Redis first before querying the database, with in-process 5-minute fast path.connect-redis) with automatic fallback to MySQL if Redis is unavailable.server.js replaced four inline DB queries with cached lookups—most page loads now hit Redis instead of MariaDB.tr(item, 'label_en') was looking for label_en_hi (which doesn’t exist) instead of label_hi. Added a property alias in the nav loader so tr() finds Hindi under the expected key.translateRecordToAll and deriveHindi) now respects status flags—reviewed and locked translations are never overwritten by machine translation.status, source_hash, reviewed_by, and reviewed_at columns to the translations table.? in webhook URLs and does not support custom headers, so the auth token is now sent as a Custom Parameter in the webhook body (token field). The server checks req.body.token as a fallback after headers and query params.id and Stage as Module Parameters, plus token as a Custom Parameter—no headers, no query params, no line items needed.updateWarrantyClaim to the Zoho integration layer (Cases API PUT).<datalist> category field with a proper <select> dropdown plus an “Other…” option that reveals a text input for custom categories.uploadRecordAttachment helper in zoho.js for general-purpose record-level attachments (Leads, Deals, Contacts, etc.).Basic_PC), and PO/Quotation (from the invoice’s linked Quote record).PO_Number_Date.PO_Number_Date and Offer_Number./portal/warranty/new. Claims are pushed to Zoho CRM Cases module with Case_Reason = “Failure under Warranty”.multi_brand flag to product series. When enabled, the series page shows products from all brands (not just Delco Remy) and uses a brand-less URL: /products/series/<slug>.flex-wrap to the preference option buttons so the 11 font choices wrap neatly within the sidebar instead of overflowing horizontally.font-size declarations across 47 admin EJS templates and admin-specific CSS selectors from fixed px values to rem units.:hover state connected across the gap.overflow: hidden on .admin-nav and .admin-sidebar was clipping the absolutely-positioned flyout panels. Set overflow: visible on both elements when sidebar is collapsed.data-label attribute and proper styling with dot markers.title attributes to all group toggle buttons for tooltip accessibility..kb-table-dark CSS class for dark-header tables in KB articles. Site-level th styles were overriding inline backgrounds, making header text invisible.scripts/seed-kb-str001.sql.scripts/seed-kb-articles.sql for inserting articles into kb_articles table.route=v3 (Quick Transactional, returned empty responses) to route=otp with variables_values parameter. The OTP route uses Fast2SMS’s built-in OTP template and returns proper JSON responses./uploads/rfq/ and linked in the admin quotation detail view.phone_verified column on quotations, new phone_verifications table) and displayed with a badge in the admin quotation detail view.quotations) and displayed in the admin quotation detail view with a “verified” badge.‘ ’ “ ”) with straight quotes in views/404.ejs. EJS compiles template code blocks to JavaScript, which does not recognize smart quotes as string delimiters — causing a 500 “Invalid or unexpected token” error whenever the 404 page was rendered.translations table so tr(doc, 'file_path') resolves correctly.translatePdfToAll() wrapper in lib/pdf-translator.js — loops through all languages, stores Hindi path in file_path_hi column and regional paths in translations table.translatePdf() now accepts an opts.targetLang parameter (default 'hi').s.id in the SELECT list, so the _id tag was undefined and tr() could never build a valid cache key. Added s.id to the query — series taglines now resolve correctly for all regional languages.tr(it, 'label') looked up cache key ...:label but translations were stored under field name label_en. Changed to tr(it, 'label_en') so the field name matches the translations table. Affects both top-level nav items and dropdown children._table / _id tags to all DB row loaders so tr() can look up non-Hindi Indic translations from the translations cache. Previously, industries, products (listing), product series (home), categories, equipment, FAQs, and KB articles/documents (listing) were missing these tags, causing tr() to skip the cache and fall straight through to English.pages database table. All four pages are now fully editable from /admin/pages using the Jodit rich-text editor.translateRecordToAll() pipeline — saving a legal page with “Retranslate” checked auto-generates translations for Hindi + 9 Indic languages. The /admin/translations audit page now correctly tracks legal page body translations.db/legal-bodies.js with extracted HTML content; seed.js migration (safe for --migrate-only) inserts body content for existing databases without overwriting admin edits.<%- pageBody %> only — no more inline fallback HTML.tr() now falls back directly from the selected regional language to English, skipping the intermediate Hindi step. All Indic languages have their own translations via translateRecordToAll(), so the Hindi detour is no longer needed.translateRecordToAll() helper upserts translations for a single record into the translations table across all Indic languages — runs fire-and-forget after the redirect so admin saves stay fast._hi columns via deriveHindi() instead of relying on manual Hindi input.t() locale keys (auth, catalog, legal, hero, KB, nav, product detail, series, etc.) to en.json, hi.json, and all 9 Indic locale files — fixes mixed-language rendering on Terms, Privacy, Warranty, Knowledge Base, and catalog pages.isHindi checks with t() locale keys — headlines, bodies, reference label, phone line, and CTA all translate correctly in every language.en.json (error.*, compat.*, form.other, pd.notesPlaceholder, product.starter/alternator, table.partNum, about.registrations/testimonials) and translations for all 9 Indic locales.tr() instead of isHindi — all Indic languages get proper translations.isHindi to tr()._table / _id so tr() can resolve non-Hindi Indic translations from the translations table cache (series detail, product detail, KB articles/documents, generic pages, loadPage()).pageMeta() and pickRow() to use i18n.tr() instead of manual column checks, enabling the same translations-cache lookup for meta tags and JSON APIs.isHi Hindi-only checks in public routes with a language-aware cascade (_<lang> column → _hi fallback → English). Affected routes: series detail, product detail, KB articles/documents, generic pages, and the shared pageMeta() / pickRow() helpers.series.keyFeatures, series.needQuote, series.productsIn, cta.equipmentSearch, etc.) to all 9 Indic locale files (bn, mr, te, ta, gu, kn, or, ml, pa)..admin-table elements convert from horizontal tables to stacked card layouts at ≤900px. Each row becomes a bordered card with label–value pairs, eliminating horizontal scrolling..portal-table elements convert to card layout at ≤768px with the same label–value pattern.<th> headers and applies data-label attributes to every <td>, so no template changes are needed.max-width instead of fixed width.[New Indo v3.81.0] 404 Error (/app/).scripts/translate-all.js — runs from SSH with a live terminal progress bar, bypassing Passenger entirely. Supports any language or all, with --table, --db-only, --static-only, and --dry-run flags.generateDbContent with per-table and per-field error handling so a missing column skips gracefully.generateDbContent so a missing column or query failure skips that table gracefully instead of aborting the whole job.lsnode wrapper buffers entire HTTP responses, making SSE unusable. Replaced with a polling approach: POST starts the job and returns a jobId immediately; the client polls a GET endpoint every second for progress updates.<%= → <%-)./api/part-search, reducing response size.portal.impersonate permission instead of requiring super_admin role. Managers and staff with this permission enabled can now impersonate portal users.format: html so fields containing HTML markup (body, description, features, etc.) have their tags preserved during translation instead of being mangled.translations database table stores non-Hindi translations without adding columns to every content table. Hindi continues to use dedicated _hi columns.tr() helper now checks the translations table for languages beyond Hindi, with an in-process cache (5-minute TTL) for zero-latency lookups.product_series record with the correct brand and category, ensuring the frontend page exists immediately.product_series table and existing brands..jpg with the correct angle suffix (_ANG, _FRO, _SID, _BAC), fixing the mismatch where non-JPG uploads did not render on the public product detail page.rfq_attachments database table; files stored in /uploads/rfq/..htaccess to .gitignore — this file is managed by cPanel/Passenger on the server and should not be tracked in the repo.git pull conflicts when cPanel rewrites Passenger directives on server restart..page-legend — muted 13px text with pipe-separated tips, auto-breaks to its own line in flex containers..st-toolbar and .st-footer with consistent padding: 5px, margin: 16px, gap: 12px, and a top border on the footer for visual separation..admin-main overrides to match; capped search input at 420px max-width to prevent full-width stretching.multipart/form-data bodies, so req.body._csrf was always empty. Middleware now defers validation on multipart requests; each route handler validates the token after multer has parsed the body.robots.txt allowing crawlers, blocking /admin/, /portal/, and /api/ paths.npm run migrate:safe no longer re-runs the series auto-populate query when the product_series table already has rows, preventing duplicate entries caused by whitespace or casing variations in product data.portal_activity_log table tracks every customer login, logout, page view, RFQ submission, PDF download, profile edit, and warranty upload with IP, user-agent, and entity context.email_log table records every outbound email with status progression (queued, sent, opened, clicked, bounced, failed), timestamps, and engagement counts.products.power when no spec-table entry exists — fixes dashes on EMEA range products.listing_columns (JSON) to product_series table.scripts/fix-emea-applications.js — run with --dry-run to preview, without to apply.scripts/populate-series-content-batch2.js — run with --dry-run to preview, without to apply.products.series field for all products in that series. Previously, renaming a series orphaned its products because products link to series by name.alternator-units products into their own “Delco Remy Alternator Units” series instead of lumping them into Spares. Script now creates three consolidated series: Spares, Starter Motor, and Alternator Units.scripts/dr-series-consolidation.js to consolidate SKU-named Delco Remy series into “Delco Remy Spares” and “Delco Remy Starter Motor”. Retains all MT, SI, PG260D, PG260N2, and REFRIGERATION/AUX series. Dry-run by default, --execute to apply.loading="lazy" not triggering inside overflow containers.<form> so saves work exactly as before, but the editing experience is far less overwhelming. Tab state toggles via CSS classes with no page reloads.requireRole() middleware. First admin auto-promoted to super_admin on migration. Role stored in session and exposed to views.admin_audit_log table records who did what, when, and from where. Logs admin logins, product create/update/delete, and settings changes. Non-blocking — audit failures never break the main flow.show_on_home = 1 but no tagline now display correctly on the homepage (removed hidden tagline IS NOT NULL SQL filter).zoho_stock_sync and zoho_sync_lines tables track every processed webhook with per-line-item reservation and deduction quantities for auditing and debugging.X-Zoho-Webhook-Token header, configurable through ZOHO_WEBHOOK_TOKEN environment variable.products table and recalculated only when stock transactions occur. This eliminates expensive multi-JOIN queries on every page load.recalcStock(), recalcStockAll(), and recalcStockBatch() functions in the inventory engine, called automatically after every reservation create/release/convert, product save, and inventory import.getProductAtpBatch() calls from product listings, detail pages, series pages, part search, and compatibility search. Stock badges now read product.atp directly.getStockOverview() and getNegativeAtpProducts() now read materialized values from the products table instead of computing them dynamically.import-inventory.js now calls recalcStockAll() after importing warehouse stock instead of manually updating total_stock.atpByProduct (the getStockOverview result) instead of missing fields on the inline product query row. Previously showed zeros for all ATP-related columns.getProductAtpBatch returning a Map instead of a plain object, causing all bracket-notation lookups (atpMap[id]) to silently return undefined. Service parts, compatibility search, and product listings were falling back to raw total_stock instead of true ATP.product_stock sum (same as public pages) instead of stale products.total_stock denormalized column. ON HAND column also updated.currentColor.{SKU}_ANG.jpg). Includes option to remove existing image.public/images/products/ and public/images/brands/ so server-only uploaded images are never wiped by git operations. Fixed stray {views/ typo.qty_on_hand — parts with stock and positive ATP no longer show “Out of Stock”.TypeError: Cannot read properties of undefined (reading 'contains') in series thumbnail hover tooltip./admin/stock — ATP queries referenced p.name instead of p.product_name.stock_reservations and incoming_stock tables for hard/soft inventory reservations and purchase order tracking. Eight new ATP settings columns in settings table.lib/inventory.js with ATP calculation (On Hand − Hard Committed − Soft Reserved + Incoming), row-level locking via FOR UPDATE, configurable soft reservation percentage, scarce-stock override, auto-expiry, and 60-second settings cache..portal-btn-reorder outline button style with hover fill effect.portal.reorder key in English and Hindi.Name_of_Engineer Zoho lookup field (object → name) in warranty detail page.Upload_Documents_Images and Upload_Images fields).downloadFieldAttachment and uploadFieldAttachment functions to lib/zoho.js for Zoho CRM file-upload field support (multipart upload).white-space:nowrap to the Date column in warranty tables (dashboard & warranty list) to prevent date values from wrapping.white-space:nowrap to the Warranty Claim No column on both the dashboard and warranty list pages so values stay on a single line.Warranty_Applicable field value from Zoho instead of hardcoded Yes/No/Pending logic on dashboard and warranty list.Invoice_No), Date, Amount (Invoice_Amount), Balance (Short_Payment), Date Paid (Payment_Received_Date), Status, and Days (Payment_Cycle_Days).portal.balance, portal.datePaid, portal.paymentDays keys in English and Hindi.PO_Number_Date in the RFQ Reference column.PO_Number_Date is present on a quote, display it with PO_Date below; otherwise fall back to Deal_Name.Deal_Name data on dashboard and quotes list.portal.rfqReference key in English and Hindi.Offer_Number (with Subject fallback) instead of Zoho record IDs on dashboard and quotes list.Invoice_Number is a Zoho lookup field (object with .name), not a plain string — unwrap it before rendering in the warranty claim PDF to avoid “[object Object]”.₹ (₹) symbol with “INR” prefix in warranty claim PDF — pdf-lib’s standard fonts only support WinAnsi encoding.Promise.all to Promise.allSettled so a single Zoho API failure no longer hides quotes, invoices, and account data.Case_Number instead of record ID to Zoho template print-preview API for Cases module.Case_Number to warranty fields list so it’s fetched from Zoho./settings/templates/ instead of /settings/inventory_templates/ for Cases module PDF generation.actions/download endpoint.ReferenceError: quoteId is not defined in the quote PDF error handler that crashed the app on PDF download failures.t() translation calls — the entire customer portal is translatable via the language picker.Company_No) displayed on account view and edit pages.updateAccount() to Zoho API wrapper for updating company-level fields.portal_contact_updates for audit trail even though approval is no longer required.products.total_stock column instead of the legacy stock_positions table, so search results show correct inventory.DB_PASSWORD env var as fallback (matching .env convention).oem_status tracks manufacturer lifecycle (active / discontinued / obsolete / superseded / private label) and listing_status controls portal visibility (available / catalogue only / discontinued / hidden).warehouses and product_stock tables. Admin sees per-warehouse breakdown; public sees combined total.node scripts/import-inventory.js <warehouse> <file> handles CSV/TSV/UTF-16, upserts stock, and auto-reactivates products.footer_trademark, footer_trademark_hi, footer_disclaimer, footer_disclaimer_hi columns to settings. Run npm run migrate:safe.hidden attribute was overridden by inline display:flex. Switched to style.display toggling.footer_links table creation and default seed into the --migrate-only code path so npm run migrate:safe correctly creates the table on production databases./careers, /distributors). Pages can be set to Draft while under construction.pages table at its slug URL. Only published pages are visible; draft pages return 404.footer_links table. Run npm run migrate:safe to create the table and seed default links.Mount Type spec label (in addition to Mount, Mounting, Mounting Type) for both starter and alternator series product tables.product_specs (labels: Rated Output / Output / Rated Amps / Amps, Mount / Mounting / Mounting Type, Ground Terminal / Ground / Grounding)..series-card-thumbs so thumbnails no longer touch the bottom separator border (overrides the global * { padding: 0 } reset).justify-content: center, equal top/bottom padding).category_id = 'starter-motors'). Values are pulled from the product_specs table (labels: Pinion, Pinion Teeth, Mount, Mounting, Mounting Type).category_id IN ('starter-motors','alternator-units')), excluding service/child parts.src now appends _ANG.jpg to the base image_url (matching the product detail page convention), with a fallback for URLs that already include a file extension.deriveHindi() — empty Hindi fields are auto-translated from English on every save, and the retranslate checkbox forces a full refresh of all Hindi content.scripts/translate-series.js — one-shot migration to populate Hindi translations for all series description, features, applications, tagline, and meta fields using the Google Translate API.show_thumbs_on_home column on product_series./admin/portal/rfqs) listing all RFQs across all portal users with customer info, urgency, and status.zoho_quote_id column to portal_rfqs table./admin/portal/updates) where admins can review, approve, or reject pending profile changes. Approving pushes the changes to the Zoho CRM Contact record via the API.apiPut and updateContact to lib/zoho.js for updating Zoho CRM records.portal_contact_updates table for tracking self-service profile edit requests with approval workflow.Current_Requirement, TIN_CST_Number (GST), and Next_Step custom fields alongside the mandatory Last_Name, Lead_Source, and Lead_Status./portal/login. When a portal user is logged in, the header shows their name with a link to the dashboard./login now redirects (301) to /portal/login for a unified customer entry point using email-based magic links.Product_Code (starts_with) instead of also matching Product_Name, which caused false positives. Active status is filtered using the correct Status picklist field (Status:equals:Active) in the Zoho search criteria itself rather than post-filtering on the wrong field name.Product_Name (not product) for line item product references. Inactive products are now skipped and fall back to free-text line items instead of causing a 400 error.Layout, Pipeline, Stage, Current_Requirement, and Closing_Date fields, and successfully create Deals linked to the customer's Account and Contact in Zoho CRM.{} instead of a PDF when the template is not available. Added validation to detect non-PDF responses and show a user-friendly error message instead of raw JSON.OAUTH_SCOPE_MISMATCH) because per-module READ scopes don't cover the /actions/download endpoint. Switched to ZohoCRM.modules.ALL which covers all module operations including PDF download and record creation.main.js for theme/font/language preference persistence via cookies.portal_rfqs table stores all RFQ submissions for history tracking, with fallback if Zoho API is unavailable.apiPost, createDeal, createLead, searchAccounts, searchZohoProducts methods added to zoho.js.apiDownload, downloadQuotePdf, downloadInvoicePdf).Total_Tax_on_Items.nd_Received_Amount (exact Zoho API name).nd_Check_NEFT_RTGS_No (exact Zoho API name).Freight1) displayed as text instead of currency — it holds values like "To-Pay", not a number.Invoiced_Items array with proper Product_Name.name extraction, Tax column, and discount percentage display./admin/portal/debug-quote and /admin/portal/debug-invoice) and debug console.log from quote detail route.Quoted_Items array (was looking for Product_Details which doesn't exist on Quotes).Product_Name.name object — shows product code alongside name.Tax_Forms (Zoho's actual field name, not Tax_Type).[object Object] — now correctly extracts the name from Zoho CRM's nested product lookup field.list_price and Unit_Price variants./portal/exit-impersonation route clears portal session and redirects back to admin portal management./portal with magic-link (passwordless) login, linked to Zoho CRM contacts.lib/zoho.js — OAuth2 server-based application module with auto-refreshing access tokens and CRM API v7 wrappers for Contacts, Accounts, Quotes, Invoices, and Sales Orders.zoho_tokens, portal_users, portal_magic_links.portal.css) with sidebar navigation, responsive layout, KPI cards, data tables, and detail views.<iframe> tags in its own <jodit> wrapper, destroying the embed on save. Switched to a placeholder-based approach: the editor stores a data-youtube-id div with a thumbnail preview; the footer script converts it to a real iframe on page load.cleanHTML was silently removing <iframe> tags. Changed deny-list to only block <script>, allowing iframes to persist..kb-article-body, series, and page content areas so embedded videos scale correctly on all screen sizes.public/css/style.css and views/changelog.ejs which were accidentally removed from the git repository in v3.33.1 due to a work-tree mismatch during commit.* { margin: 0; padding: 0; }) was stripping list bullets/numbers and indentation.kb-article-form.ejs, page-form.ejs, series-form.ejs — both English and Hindi body fields.pdf-lib for PDF splitting and merging.lib/pdf-translator.js to use Google Cloud Translation v3 Document Translation API. Hindi PDFs now preserve the original layout, images, tables, branding, and formatting — only the text is translated./admin/settings under "GCP Service Account JSON" (textarea input for the full JSON blob).pdf-parse and pdfkit dependencies — no longer needed since Google's API handles PDF processing end-to-end.crypto module — no extra npm packages required.lib/pdf-translator.js to use pdf-parse v2 API — constructor-based new PDFParse({ data }) instead of the removed default-function export. Fixes "pdfParse is not a function" error when translating KB document PDFs.-- 1 of 5 --) from extracted text before sending to translator.file_path_hi column to kb_documents table.pdf-parse (text extraction) and pdfkit (PDF generation)./fonts for server-side PDF rendering.<datalist> autocomplete with a proper <select> dropdown for KB Article category — same fix as KB Documents (v3.29.4). Includes "Other…" option for custom categories with backend __other sentinel resolution.t() calls site-wide — every public page is fully translatable.<datalist> autocomplete with a proper <select> dropdown for KB Document category — now reliably shows all category options. Includes "Other…" option for custom categories.email_verifications table for OTP-based email verification with expiry and attempt tracking./api/send-otp and /api/verify-otp endpoints with in-memory rate limiting.customer_type, designation, and email_verified columns to quotations table.product_series.category_id still had old values from before the category consolidation.migrate:safe to update series category_id to the consolidated IDs (starter-motors, alternator-units, spares).compatRows query from homepage route, saving a DB call on every page load./api/part-search endpoint searches across products.sku and product_oe_refs.manufacturer_part, returning matched products with full cross-reference data.consolidate-categories.js script remaps all products and series from old category IDs to new ones. Supports --dry-run preview.import-korea-range.js) for the Delphi Technologies / Delco Remy Korea Range — 155 products (109 starters + 46 alternators) covering passenger cars, light commercial vehicles, and heavy-duty equipment.EU.import-emea-range.js) for the Delco Remy 2025 EMEA "New to Range" catalog — 326 products (180 starters + 146 alternators) covering European highway trucks, buses, and construction equipment.import-delcoremy.js --scrape-only mode backfills specs, applications, and service parts directly from delcoremy.com without requiring the Excel file. Works for a single SKU (--scrape-only --sku 61032288) or in bulk for all active products missing specs.--text-mid and --brown for proper contrast against the light blue hero background. The current SKU is highlighted in --text-dark.brands table: auto-populated from the product catalog. Stores light and dark logo variants, website URL, and display settings for each brand.migrate:safe. Also adds a fallback application row for products that have none.--migrate-only flag: npm run migrate:safe runs schema upgrades and series content population without truncating product specs, applications, or stock data. Use this instead of npm run seed on any database with imported catalog data./products/delco-remy/10479339 (brand/SKU pattern). Old slug-based URLs (/products/50mt) automatically 301-redirect to the new canonical URL./products/delco-remy/series/50mt showing an overview, specs, features, applications, and all products in the series.product_series table: auto-populated from the catalog on first seed. Stores series-level content (description, features, applications, voltage/power ranges, SEO fields) with Hindi translations.productUrl() helper, ensuring brand/SKU URLs everywhere — product cards, compatibility results, quote cart, service parts, related products.product_series / qty undefined bugs, added separate Part # and Series columns with correct field references.product_oe_refs.manufacturer_part expanded from VARCHAR(255) to TEXT to support values up to 2500+ characters without truncation.product_oe_refs.manufacturer_part expanded from VARCHAR(60) to VARCHAR(255) so long part numbers are no longer truncated on import.product_oe_refs table.--steel, --chrome, --silver, --ink-on-dark overrides in dark token block for full var() consistency.ink-on-dark, steel, silver, and chrome text colors with solid text-dark (#4A4A4A) on light-blue surfaces — hero, stats bar, section-dark, page-hero, CTA strip, footer, compatibility notes.equipment and product_equipment tables from product applications data, enabling the /compatibility search to return results for imported products.scripts/import-catalog.js reads Combined_Product_Catalog.xlsx and populates all tables — 2,588 products, 3,009 specs, 13,494 applications, 3,417 service parts, 1,317 cross-references, 590 customer codes. Run migration first: node scripts/migrate-v3.15.js.scripts/backfill-images.js — downloads product images from the Delco Remy CDN for all service/spare parts that were skipped by the main importer. Downloads all 4 angles (main, front, side, back) and updates the database image_url column.--sku 1945853 (single SKU) and --dry-run (check availability only) flags.data-smart-table for parts used in many assemblies.product_id, causing NaN in the SQL WHERE clause. Added server-side validation guard on POST /quote/add.settings table with 16 new columns for CMS controls. Added body/body_hi to pages. Expanded nav_items.kind ENUM to include auth./register and log in at /login. Accounts require admin approval before prices become visible./admin/customers page to review, approve, reject, or delete customer accounts. Dashboard shows customer counts and pending approvals.customers table with bcrypt password hashing and status workflow (pending/approved/rejected).label column but nav_items uses label_en/label_hi; switched to direct column access..nav-links a to .nav-links > a so uppercase/nav-bar styles no longer leak into dropdown items. Reduced nav gap and letter-spacing to prevent multi-word items wrapping to two lines.--cream from warm golden (#F5EDE0) to neutral (#F3F0EB). Fixed dark navy background (#1E2D3D) leaking into light-mode image viewer, card images, and thumbnails. Image containers now use white to match product photo backgrounds.parent_id column with foreign key constraint.<nav>. Trademark notice condensed and merged inline with copyright. Version number now dynamic via package.json. Mobile breakpoint at 600px.?v=<version> from package.json, auto-updated on every version bump.22AAAAA0000A1Z5). Stored in a new gst_number column on the quotations table.form-validate.js module provides instant on-blur feedback for GST format, email address, and Indian mobile number (10 digits starting 6–9, with optional +91 prefix). Invalid fields show red borders and inline error messages; corrected fields turn green. First error auto-scrolls into view on submit./quote/submit and /requirements POST handlers now re-check GST, email, and phone format server-side before inserting into the database. Invalid submissions redirect back to the form.data-validate attribute — the script auto-discovers fields by name and attaches blur/submit handlers. No per-page wiring needed./knowledge-base public page with tabbed navigation (All / Articles / Documents), category filter pills, and a responsive card grid showing all published content./knowledge-base/articles/:slug.public/uploads/kb/ with sanitized filenames.multer for file upload handling.new Image() on page load, so rotation between views is instant with no loading flicker. A brief loading state (opacity fade) handles edge cases on slow connections.geoip-lite) for sub-millisecond in-memory lookups with no external API calls.Accept-Language header is parsed as a secondary signal. This catches users with Indian language preferences set in their browser even when browsing from abroad.newindo.lang cookie immediately, so the GeoIP lookup only runs once per visitor. Users can still override via the language picker at any time.adminNav) so sidebar groups stay where you left them across page loads. Groups containing the active page are always forced open..legal-page CSS class with readable max-width, section dividers, styled headings, FAQ formatting, and dark-mode support.data-smart-table attribute. Features include live search, click-to-sort columns, and configurable pagination (10/20/50/100/200/All). Applied to all public and admin tables.scripts/reindex-equipment.js) to sync product_applications into the equipment + product_equipment tables used by the compatibility page.scripts/deactivate-no-sku.js) to set products without a SKU to inactive status.db.q() destructure errors fixed in utility scripts.--font-display and --font-body CSS variables at the <html> level so the chosen font applies site-wide.font-size declarations from px to rem. The 5 text-size stops (xs/s/m/l/xl) now change the root font-size (13.6px–19.2px), scaling the entire page uniformly — nav, hero, footer, cards, tables, everything.font-variant-caps: small-caps for the classic large-initial/small-caps look. Dark variant auto-generated with consistent cream outlines. Logo text scales with font-size preferences since it's HTML.nodemailer ^6.9 → ^8.0.5 (addresses GHSA-mm7p-fcc7-pg87, GHSA-rcmh-qjqh-p98v, GHSA-c7w3-x93f-qmm8, GHSA-vvjj-xcjg-gr5g).section-dark blocks now use dedicated --ink-on-dark / --ink-on-dark-muted CSS tokens so cream ink reads cleanly against navy surfaces in both themes..compat-table, .spec-table, .apps-table, .cart-table) rewired onto theme tokens.#D0D5DD borders to var(--border).lib/translator.js wraps Google Cloud Translate; admin save handlers auto-populate *_hi columns on create/edit.res.locals.tr(row, field) for DB rows and res.locals.t(key, fallback) for static chrome (dictionaries at locales/en.json / locales/hi.json).t(); admin panel remains English-only.views/404.ejs.LOGROCKET_APP_ID).lib/error-reporter.js emails deduped, rate-limited alerts to ERROR_ALERT_TO.lib/secret-store.js + SECRET_KEY env) with a dedicated admin UI.body-parser in favour of Express built-ins.trust proxy, sameSite+secure cookies.X-Content-Type-Options / X-Frame-Options / Referrer-Policy via .htaccess.utf8mb4 (was utf8mb4_unicode_ci).