Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The NetSuite Pro

The NetSuite Pro Logo The NetSuite Pro Logo

The NetSuite Pro Navigation

  • Home
  • About Us
  • Tutorials
    • NetSuite Scripting
    • NetSuite Customization
    • NetSuite Integration
    • NetSuite Advanced PDF Templates
    • NetSuite Reporting & Analytics Guide
    • Real-World NetSuite Examples
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask A Question
  • Home
  • About Us
  • Tutorials
    • NetSuite Scripting
    • NetSuite Customization
    • NetSuite Integration
    • NetSuite Advanced PDF Templates
    • NetSuite Reporting & Analytics Guide
    • Real-World NetSuite Examples
  • Blog
  • Contact Us
Home/ Real-World NetSuite Examples/Invoice PDF with Dynamic Logos (by Subsidiary/Brand)

Invoice PDF with Dynamic Logos (by Subsidiary/Brand)

💼 Business problem

Multi-brand/multi-subsidiary accounts need one invoice template that:

  • Shows the correct logo & brand colors per subsidiary or per customer brand.
  • Displays regional tax/VAT info dynamically.
  • Adds watermarks (e.g., “ON CREDIT HOLD” or “PAST DUE”).
  • Keeps totals/tax neatly aligned and consistent.

🧠 Approach

  1. Use Advanced PDF/HTML with FreeMarker to pick the logo & palette at runtime.
  2. Read branding from the Subsidiary (preferred) or from Customer fields.
  3. Keep layout modular: Header, Bill-To/Ship-To, Lines, Tax Summary, Totals.
  4. Optional UE script to pre-compute flags (e.g., is_past_due, brand code) if you prefer.

📋 Prerequisites (recommend)

  • Subsidiary custom fields:
    • custrecord_brand_logo_url (Text, store file URL or external URL)
    • custrecord_brand_color_hex (Text, e.g., #0A3D62)
    • custrecord_vat_label (Text, e.g., VAT, GST/HST, etc.)
  • (Optional) Customer override fields with same IDs (so customer branding can supersede).
  • (Optional) Custom body fields on Invoice if you want to override per transaction.

Tip: If you don’t want custom fields, you can fall back to ${companyInformation.logoUrl} for a single global logo.


🧱 Header: dynamic logo + brand color

<#-- Resolve brand context: Customer override > Subsidiary > Company default -->
<#assign sub = record.subsidiary />
<#assign customer = record.entity />
<#assign logoUrl =
  (customer.custentity_brand_logo_url?has_content)?then(customer.custentity_brand_logo_url,
  (sub.custrecord_brand_logo_url?has_content)?then(sub.custrecord_brand_logo_url,
  companyInformation.logoUrl)) />

<#assign brandColor =
  (customer.custentity_brand_color_hex?has_content)?then(customer.custentity_brand_color_hex,
  (sub.custrecord_brand_color_hex?has_content)?then(sub.custrecord_brand_color_hex,
  "#222")) />

<table style="width:100%; border-collapse:collapse;">
  <tr>
    <td style="width:50%; padding:8px 0;">
      <#if logoUrl?has_content>
        <img src="${logoUrl}" style="max-height:60px;"/>
      </#if>
    </td>
    <td style="width:50%; text-align:right;">
      <div style="font-size:22px; font-weight:700; color:${brandColor};">INVOICE</div>
      <div style="font-size:12px;">${record.tranid}</div>
      <div style="font-size:12px;">${record.trandate}</div>
    </td>
  </tr>
  <tr>
    <td colspan="2" style="height:3px; background:${brandColor};"></td>
  </tr>
</table>

🧾 Bill/Ship + references (clean block)

<table style="width:100%; margin-top:10px;">
  <tr>
    <td style="vertical-align:top; width:50%;">
      <div style="font-weight:700;">Bill To</div>
      ${record.billaddress?html}
    </td>
    <td style="vertical-align:top; width:50%;">
      <div style="font-weight:700;">Ship To</div>
      ${record.shipaddress?html}
      <#if record.otherrefnum?has_content>
        <div style="margin-top:6px;"><b>Customer PO:</b> ${record.otherrefnum}</div>
      </#if>
    </td>
  </tr>
</table>

📦 Line table with subtle zebra & alignment

<style>
  .tbl { width:100%; border-collapse:collapse; margin-top:14px; }
  .th  { font-size:11px; text-transform:uppercase; letter-spacing:.4px; padding:8px 6px; border-bottom:1px solid #ddd; }
  .td  { font-size:12px; padding:7px 6px; border-bottom:1px solid #eee; }
  .num { text-align:right; white-space:nowrap; }
  .zebra:nth-child(even) td { background:#fafafa; }
</style>

<table class="tbl">
  <tr>
    <th class="th" style="text-align:left;">Item</th>
    <th class="th" style="text-align:left;">Description</th>
    <th class="th num">Qty</th>
    <th class="th num">Rate</th>
    <th class="th num">Amount</th>
  </tr>

  <#list record.item as line>
    <tr class="zebra">
      <td class="td">${line.item@label}</td>
      <td class="td">${line.description}</td>
      <td class="td num">${line.quantity?number}</td>
      <td class="td num">
        <#if line.rate?has_content && (line.rate?string != "")>${line.rate}<#else>0.00</#if>
      </td>
      <td class="td num">${line.amount}</td>
    </tr>
  </#list>
</table>

Note the Qty/Rate guards so empty values don’t crash (common FreeMarker pitfall).


🧮 Tax summary (left) + totals (right) on one row

This keeps the layout tight (you asked a lot for this in earlier work).

<table style="width:100%; margin-top:12px;">
  <tr>
    <!-- LEFT: Tax Summary -->
    <td style="width:55%; vertical-align:top; padding-right:8px;">
      <#assign vatLabel =
        (customer.custentity_vat_label?has_content)?then(customer.custentity_vat_label,
        (sub.custrecord_vat_label?has_content)?then(sub.custrecord_vat_label, "Tax")) />

      <#if record.taxsummary?has_content>
        <table style="width:100%; border-collapse:collapse;">
          <tr>
            <th style="text-align:left; padding:6px 4px;">${vatLabel} Type</th>
            <th style="text-align:left; padding:6px 4px;">Code</th>
            <th class="num" style="padding:6px 4px;">Basis</th>
            <th class="num" style="padding:6px 4px;">Rate</th>
            <th class="num" style="padding:6px 4px;">Amount</th>
          </tr>
          <#list record.taxsummary as t>
            <tr>
              <td style="padding:6px 4px;">${t.taxtype@label}</td>
              <td style="padding:6px 4px;">${t.taxcode@label}</td>
              <td class="num" style="padding:6px 4px;">${t.taxbasis}</td>
              <td class="num" style="padding:6px 4px;">${t.taxrate}</td>
              <td class="num" style="padding:6px 4px;">${t.taxamount}</td>
            </tr>
          </#list>
        </table>
      </#if>
    </td>

    <!-- RIGHT: Totals -->
    <td style="width:45%; vertical-align:top;">
      <table style="width:100%; border-collapse:collapse;">
        <tr>
          <td style="padding:6px 4px;">Subtotal</td>
          <td class="num" style="padding:6px 4px;">${record.subtotal}</td>
        </tr>
        <#if record.taxtotal?has_content>
        <tr>
          <td style="padding:6px 4px;">${vatLabel} Total</td>
          <td class="num" style="padding:6px 4px;">${record.taxtotal}</td>
        </tr>
        </#if>
        <#if record.shippingcost?has_content && record.shippingcost?string != "">
        <tr>
          <td style="padding:6px 4px;">Shipping</td>
          <td class="num" style="padding:6px 4px;">${record.shippingcost}</td>
        </tr>
        </#if>
        <tr>
          <td style="padding:8px 4px; font-weight:700; border-top:1px solid #ddd;">Total</td>
          <td class="num" style="padding:8px 4px; font-weight:700; border-top:1px solid #ddd;">${record.total}</td>
        </tr>
        <#if record.amountremaining?has_content>
        <tr>
          <td style="padding:6px 4px;">Amount Due</td>
          <td class="num" style="padding:6px 4px;">${record.amountremaining}</td>
        </tr>
        </#if>
      </table>
    </td>
  </tr>
</table>

🚩 Watermarks: credit hold / past-due

Show a big translucent banner based on record state.

<#-- Compute flags -->
<#assign isCreditHold = (record.entitycredithold?string('','')?upper_case == 'ON') />
<#assign isPastDue = (record.amountremaining?number > 0 && record.duedate?has_content && (record.duedate?date < .now?date)) />

<style>
  .wm {
    position:fixed; top:40%; left:50%; transform:translate(-50%,-50%);
    font-size:64px; color:#000; opacity:.06; letter-spacing:8px;
    border:6px solid #000; padding:18px 28px; text-transform:uppercase;
  }
</style>

<#if isCreditHold>
  <div class="wm">CREDIT HOLD</div>
<#elseif isPastDue>
  <div class="wm">PAST DUE</div>
</#if>

💳 Payment note / e-Transfer (common request)

<#if record.custbody_payment_note?has_content>
  <div style="margin-top:12px; font-size:12px; line-height:1.4;">
    <b>Payment:</b> ${record.custbody_payment_note}
  </div>
<#else>
  <div style="margin-top:12px; font-size:12px; line-height:1.4;">
    <b>Payment:</b> E-transfer accepted at accounts@yourbrand.com (include invoice # in memo).
  </div>
</#if>

🧰 Optional UE helper (beforeLoad)

If you prefer to pre-compute flags or inject derived fields that the template reads:

/**
 * @NApiVersion 2.1
 * @NScriptType UserEventScript
 * Title: UE | Invoice PDF helpers
 */
define(['N/record','N/format'], (record, format) => {
  const beforeLoad = (ctx) => {
    if (ctx.type !== ctx.UserEventType.VIEW && ctx.type !== ctx.UserEventType.PRINT) return;
    const inv = ctx.newRecord;

    // Example derived boolean: custbody_is_past_due
    const due = inv.getValue('duedate');
    const remaining = Number(inv.getValue('amountremaining') || 0);
    let pastDue = false;
    if (due && remaining > 0) {
      const today = new Date();
      const dueDate = format.parse({ value: due, type: format.Type.DATE });
      pastDue = (dueDate < today);
    }
    inv.setValue({ fieldId: 'custbody_is_past_due', value: pastDue });
  };
  return { beforeLoad };
});

Then your FreeMarker can check ${record.custbody_is_past_due} directly.


✅ Testing checklist

  • Switch between two subsidiaries and confirm logo/color swap.
  • Customer with override fields → their branding wins.
  • Invoice with tax lines → tax table shows; non-taxed invoice → block hides gracefully.
  • Aging test: set past due; verify PAST DUE watermark.
  • Credit-hold customer → CREDIT HOLD watermark appears.
  • Try missing qty/rate values to ensure no template crash.

🧪 Troubleshooting

  • Logo not showing: confirm the URL is accessible to the template (File Cabinet files with Available Without Login or a hosted CDN URL), and variable logoUrl resolves.
  • Weird numbers: guard with ?has_content and ?number where appropriate; avoid math on empty strings.
  • Totals alignment off: keep totals in a separate right-side cell (as above) and avoid nested tables with variable column counts.
  • RTL languages: wrap address blocks in a container and set direction:rtl; text-align:right; conditionally.

🧠 Takeaways

  • Centralize branding on the Subsidiary record; allow Customer overrides for brand-specific clients.
  • Use FreeMarker guards to prevent null/empty crashes.
  • Keep tax and totals in a two-column layout for consistent alignment.
  • Add visual status (watermarks) to reduce AR back-and-forth.
Share
  • Facebook

Leave a ReplyCancel reply

Sidebar

Ask A Question

Stats

  • Questions 6
  • Answers 6
  • Best Answers 0
  • Users 2
  • Popular
  • Answers
  • Rocky

    Issue in running a client script in NetSuite SuiteScript 2.0 ...

    • 1 Answer
  • admin

    How can I send an email with an attachment in ...

    • 1 Answer
  • admin

    How do I avoid SSS_USAGE_LIMIT_EXCEEDED in a Map/Reduce script?

    • 1 Answer
  • admin
    admin added an answer The issue is usually caused by following Wrong script file… September 14, 2025 at 10:33 pm
  • admin
    admin added an answer Steps to send an Invoice PDF by email: define(['N/email', 'N/render',… August 28, 2025 at 3:05 am
  • admin
    admin added an answer This error means your script hit NetSuite’s governance usage limit… August 28, 2025 at 3:02 am

Top Members

Rocky

Rocky

  • 1 Question
  • 22 Points
Begginer
admin

admin

  • 5 Questions
  • 2 Points

Trending Tags

clientscript netsuite scripting suitescript

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help

Footer

© 2025 The NetSuite Pro. All Rights Reserved