Skip to content

Commit

Permalink
feat(email-plugin): Multiple currency support in formatMoney helper (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
StampixSMO authored Nov 20, 2023
1 parent 555942e commit ccf17fb
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 17 deletions.
5 changes: 4 additions & 1 deletion docs/docs/reference/core-plugins/email-plugin/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,10 @@ Dynamic data such as the recipient's name or order items are specified using [Ha

The following helper functions are available for use in email templates:

* `formatMoney`: Formats an amount of money (which are always stored as integers in Vendure) as a decimal, e.g. `123` => `1.23`
* `formatMoney`: Formats an amount of money (which are always stored as integers in Vendure) as a decimal, e.g. `123` => `1.23`.
* Also accepts two additional parameters (`currency` and `locale`), which will format according to the given currency and locale. For example:
* `{{ formatMoney 123 'USD' 'en-US' }}` => `$1.23`
* `{{ formatMoney 123 'EUR' 'de-DE' }}` => `1,23 €`
* `formatDate`: Formats a Date value with the [dateformat](https://www.npmjs.com/package/dateformat) package.

## Extending the default email handlers
Expand Down
25 changes: 19 additions & 6 deletions packages/email-plugin/src/handlebars-mjml-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,24 @@ export class HandlebarsMjmlGenerator implements EmailGenerator {
return dateFormat(date, format);
});

Handlebars.registerHelper('formatMoney', (amount?: number) => {
if (amount == null) {
return amount;
}
return (amount / 100).toFixed(2);
});
Handlebars.registerHelper(
'formatMoney',
(amount?: number, currencyCode?: string, locale?: string) => {
if (amount == null) {
return amount;
}
// Last parameter is a generic "options" object which is not used here.
// If it's supplied, it means the helper function did not receive the additional, optional parameters.
// See https://handlebarsjs.com/api-reference/helpers.html#the-options-parameter
if (!currencyCode || typeof currencyCode === 'object') {
return (amount / 100).toFixed(2);
}
// Same reasoning for `locale` as for `currencyCode` here.
return new Intl.NumberFormat(typeof locale === 'object' ? undefined : locale, {
style: 'currency',
currency: currencyCode,
}).format(amount / 100);
},
);
}
}
11 changes: 7 additions & 4 deletions packages/email-plugin/src/plugin.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { createReadStream, readFileSync } from 'fs';
import path from 'path';
import { Readable } from 'stream';
import { afterEach, beforeEach, describe, expect, it, Mock, vi } from 'vitest';

import { orderConfirmationHandler } from './default-email-handlers';
import { EmailProcessor } from './email-processor';
import { EmailSender } from './email-sender';
Expand Down Expand Up @@ -338,6 +339,8 @@ describe('EmailPlugin', () => {
eventBus.publish(new MockEvent(ctx, true));
await pause();
expect(onSend.mock.calls[0][0].body).toContain('Price: 1.23');
expect(onSend.mock.calls[0][0].body).toContain('Price: €1.23');
expect(onSend.mock.calls[0][0].body).toContain('Price: £1.23');
});
});

Expand Down Expand Up @@ -658,7 +661,7 @@ describe('EmailPlugin', () => {
await pause();

expect(testingLogger.warnSpy.mock.calls[0][0]).toContain(
'Email has a large \'content\' attachment (64k). Consider using the \'path\' instead for improved performance.',
"Email has a large 'content' attachment (64k). Consider using the 'path' instead for improved performance.",
);
});
});
Expand Down Expand Up @@ -881,8 +884,8 @@ describe('EmailPlugin', () => {
return {
type: 'testing',
onSend: () => {},
}
}
};
},
});
const ctx = RequestContext.deserialize({
_channel: { code: DEFAULT_CHANNEL_CODE },
Expand All @@ -891,7 +894,7 @@ describe('EmailPlugin', () => {
module!.get(EventBus).publish(new MockEvent(ctx, true));
await pause();
expect(module).toBeDefined();
expect(typeof (module.get(EmailPlugin) as any).options.transport).toBe('function');
expect(typeof module.get(EmailPlugin).options.transport).toBe('function');
});

it('Passes injector and context to transport function', async () => {
Expand Down
12 changes: 6 additions & 6 deletions packages/email-plugin/templates/order-confirmation/body.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
</mj-column>
<mj-column>
<mj-text css-class="callout-large"><strong>Total Price</strong></mj-text>
<mj-text css-class="callout-small">${{ formatMoney order.total }}</mj-text>
<mj-text css-class="callout-small">${{ formatMoney order.total order.currencyCode 'en' }}</mj-text>
</mj-column>
</mj-section>

Expand Down Expand Up @@ -101,30 +101,30 @@
</td>
<td>{{ quantity }} x {{ productVariant.name }}</td>
<td>{{ productVariant.quantity }}</td>
<td>${{ formatMoney discountedLinePriceWithTax }}</td>
<td>${{ formatMoney discountedLinePriceWithTax ../order.currencyCode 'en' }}</td>
</tr>
{{/each}}
{{#each order.discounts }}
<tr class="order-row">
<td colspan="3">
{{ description }}
</td>
<td>${{ formatMoney amount }}</td>
<td>${{ formatMoney amount ../order.currencyCode 'en' }}</td>
</tr>
{{/each}}
<tr class="order-row">
<td colspan="3">Sub-total:</td>
<td>${{ formatMoney order.subTotalWithTax }}</td>
<td>${{ formatMoney order.subTotalWithTax order.currencyCode 'en' }}</td>
</tr>
{{#each shippingLines }}
<tr class="order-row">
<td colspan="3">Shipping ({{ shippingMethod.name }}):</td>
<td>${{ formatMoney priceWithTax }}</td>
<td>${{ formatMoney priceWithTax ../order.currencyCode 'en' }}</td>
</tr>
{{/each}}
<tr class="order-row total-row">
<td colspan="3">Total:</td>
<td>${{ formatMoney order.totalWithTax }}</td>
<td>${{ formatMoney order.totalWithTax order.currencyCode 'en' }}</td>
</tr>
</mj-table>
</mj-column>
Expand Down
2 changes: 2 additions & 0 deletions packages/email-plugin/test-templates/test-helpers/body.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
<mj-section>
<mj-column>
<mj-text>Price: {{ formatMoney myPrice }}</mj-text>
<mj-text>Price: {{ formatMoney myPrice "EUR" }}</mj-text>
<mj-text>Price: {{ formatMoney myPrice "GBP" "en" }}</mj-text>
<mj-text>Date: {{ formatDate myDate 'UTC:ddd mmm dd yyyy HH:MM:ss' }}</mj-text>
</mj-column>
</mj-section>
Expand Down

0 comments on commit ccf17fb

Please sign in to comment.