Merge pull request woocommerce/woocommerce-admin#1342 from woocommerce/fix/1090-currency-settings-formatting
Honor WooCommerce Settings When Formatting Currency/Price
This commit is contained in:
commit
4407e05bc4
|
@ -105,7 +105,7 @@ class OrdersReportTable extends Component {
|
|||
line_items,
|
||||
items_sold,
|
||||
coupon_lines,
|
||||
currency,
|
||||
currency_symbol,
|
||||
net_revenue,
|
||||
} = row;
|
||||
|
||||
|
@ -167,7 +167,7 @@ class OrdersReportTable extends Component {
|
|||
value: coupons.map( item => item.code ).join( ' ' ),
|
||||
},
|
||||
{
|
||||
display: formatCurrency( net_revenue, currency ),
|
||||
display: formatCurrency( net_revenue, currency_symbol ),
|
||||
value: net_revenue,
|
||||
},
|
||||
];
|
||||
|
|
|
@ -133,11 +133,11 @@ function OrdersPanel( { orders, isRequesting, isError } ) {
|
|||
</span>
|
||||
{ refundValue ? (
|
||||
<span>
|
||||
<s>{ formatCurrency( total, order.currency ) }</s>{' '}
|
||||
{ formatCurrency( remainingTotal, order.currency ) }
|
||||
<s>{ formatCurrency( total, order.currency_symbol ) }</s>{' '}
|
||||
{ formatCurrency( remainingTotal, order.currency_symbol ) }
|
||||
</span>
|
||||
) : (
|
||||
<span>{ formatCurrency( total, order.currency ) }</span>
|
||||
<span>{ formatCurrency( total, order.currency_symbol ) }</span>
|
||||
) }
|
||||
</div>
|
||||
}
|
||||
|
|
|
@ -1,20 +1,36 @@
|
|||
/** @format */
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { get } from 'lodash';
|
||||
const number_format = require( 'locutus/php/strings/number_format' );
|
||||
|
||||
/**
|
||||
* Formats a number using site's current locale
|
||||
*
|
||||
* @format
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat
|
||||
* @param {Number|String} number number to format
|
||||
* @returns {?String} A formatted string.
|
||||
* @see http://locutus.io/php/strings/number_format/
|
||||
* @param {Number|String} number number to format
|
||||
* @param {int|null} [precision=null] optional decimal precision
|
||||
* @returns {?String} A formatted string.
|
||||
*/
|
||||
|
||||
export function numberFormat( number ) {
|
||||
const locale = wcSettings.siteLocale || 'en-US'; // Default so we don't break.
|
||||
|
||||
export function numberFormat( number, precision = null ) {
|
||||
if ( 'number' !== typeof number ) {
|
||||
number = parseFloat( number );
|
||||
}
|
||||
|
||||
if ( isNaN( number ) ) {
|
||||
return '';
|
||||
}
|
||||
return new Intl.NumberFormat( locale ).format( number );
|
||||
|
||||
const decimalSeparator = get( wcSettings, [ 'currency', 'decimal_separator' ], '.' );
|
||||
const thousandSeparator = get( wcSettings, [ 'currency', 'thousand_separator' ], ',' );
|
||||
precision = parseInt( precision );
|
||||
|
||||
if ( isNaN( precision ) ) {
|
||||
const [ , decimals ] = number.toString().split( '.' );
|
||||
precision = decimals ? decimals.length : 0;
|
||||
}
|
||||
|
||||
return number_format( number, precision, decimalSeparator, thousandSeparator );
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
import { numberFormat } from '../index';
|
||||
|
||||
describe( 'numberFormat', () => {
|
||||
it( 'should default to en-US formatting', () => {
|
||||
it( 'should default to precision=null decimal=. thousands=,', () => {
|
||||
expect( numberFormat( 1000 ) ).toBe( '1,000' );
|
||||
} );
|
||||
|
||||
|
@ -16,4 +16,20 @@ describe( 'numberFormat', () => {
|
|||
it( 'should accept a string', () => {
|
||||
expect( numberFormat( '10000' ) ).toBe( '10,000' );
|
||||
} );
|
||||
|
||||
it( 'maintains all decimals if no precision specified', () => {
|
||||
expect( numberFormat( '10000.123456' ) ).toBe( '10,000.123456' );
|
||||
} );
|
||||
|
||||
it( 'maintains all decimals if invalid precision specified', () => {
|
||||
expect( numberFormat( '10000.123456', 'not a number' ) ).toBe( '10,000.123456' );
|
||||
} );
|
||||
|
||||
it( 'uses store currency settings, not locale', () => {
|
||||
global.wcSettings.siteLocale = 'en-US';
|
||||
global.wcSettings.currency.decimal_separator = ',';
|
||||
global.wcSettings.currency.thousand_separator = '.';
|
||||
|
||||
expect( numberFormat( '12345.6789', 3 ) ).toBe( '12.345,679' );
|
||||
} );
|
||||
} );
|
||||
|
|
|
@ -72,6 +72,9 @@ class WC_Admin_Api_Init {
|
|||
add_action( self::CUSTOMERS_BATCH_ACTION, array( __CLASS__, 'customer_lookup_process_batch' ) );
|
||||
add_action( self::ORDERS_BATCH_ACTION, array( __CLASS__, 'orders_lookup_process_batch' ) );
|
||||
add_action( self::ORDERS_LOOKUP_BATCH_INIT, array( __CLASS__, 'orders_lookup_batch_init' ) );
|
||||
|
||||
// Add currency symbol to orders endpoint response.
|
||||
add_filter( 'woocommerce_rest_prepare_shop_order_object', array( __CLASS__, 'add_currency_symbol_to_order_response' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -757,6 +760,22 @@ class WC_Admin_Api_Init {
|
|||
add_action( 'woocommerce_after_register_post_type', array( __CLASS__, 'regenerate_report_data' ), 20 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the currency symbol (in addition to currency code) to each Order
|
||||
* object in REST API responses. For use in formatCurrency().
|
||||
*
|
||||
* @param {WP_REST_Response} $response REST response object.
|
||||
* @returns {WP_REST_Response}
|
||||
*/
|
||||
public static function add_currency_symbol_to_order_response( $response ) {
|
||||
$response_data = $response->get_data();
|
||||
$currency_code = $response_data['currency'];
|
||||
$currency_symbol = get_woocommerce_currency_symbol( $currency_code );
|
||||
$response_data['currency_symbol'] = html_entity_decode( $currency_symbol );
|
||||
$response->set_data( $response_data );
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
||||
new WC_Admin_Api_Init();
|
||||
|
|
|
@ -269,10 +269,13 @@ function wc_admin_currency_settings() {
|
|||
return apply_filters(
|
||||
'wc_currency_settings',
|
||||
array(
|
||||
'code' => $code,
|
||||
'precision' => wc_get_price_decimals(),
|
||||
'symbol' => get_woocommerce_currency_symbol( $code ),
|
||||
'position' => get_option( 'woocommerce_currency_pos' ),
|
||||
'code' => $code,
|
||||
'precision' => wc_get_price_decimals(),
|
||||
'symbol' => html_entity_decode( get_woocommerce_currency_symbol( $code ) ),
|
||||
'position' => get_option( 'woocommerce_currency_pos' ),
|
||||
'decimal_separator' => wc_get_price_decimal_separator(),
|
||||
'thousand_separator' => wc_get_price_thousand_separator(),
|
||||
'price_format' => html_entity_decode( get_woocommerce_price_format() ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -12910,6 +12910,11 @@
|
|||
"semver": "^5.4.1"
|
||||
}
|
||||
},
|
||||
"locutus": {
|
||||
"version": "2.0.10",
|
||||
"resolved": "https://registry.npmjs.org/locutus/-/locutus-2.0.10.tgz",
|
||||
"integrity": "sha512-AZg2kCqrquMJ5FehDsBidV0qHl98NrsYtseUClzjAQ3HFnsDBJTCwGVplSQ82t9/QfgahqvTjaKcZqZkHmS0wQ=="
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.11",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
|
||||
|
|
|
@ -136,6 +136,7 @@
|
|||
"history": "4.7.2",
|
||||
"html-to-react": "1.3.4",
|
||||
"interpolate-components": "1.1.1",
|
||||
"locutus": "^2.0.10",
|
||||
"lodash": "^4.17.11",
|
||||
"marked": "0.6.0",
|
||||
"prismjs": "^1.15.0",
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
# 1.4.1 (unreleased)
|
||||
- Chart component: format numbers and prices using store currency settings.
|
||||
|
||||
# 1.4.0
|
||||
- Add download log ip address autocompleter to search component
|
||||
- Add order number autocompleter to search component
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
import { __, sprintf } from '@wordpress/i18n';
|
||||
import classNames from 'classnames';
|
||||
import { Component, createRef, Fragment } from '@wordpress/element';
|
||||
import { decodeEntities } from '@wordpress/html-entities';
|
||||
import { formatDefaultLocale as d3FormatDefaultLocale } from 'd3-format';
|
||||
import { get, isEqual, partial } from 'lodash';
|
||||
import Gridicon from 'gridicons';
|
||||
|
@ -26,11 +25,28 @@ import ChartPlaceholder from './placeholder';
|
|||
import { H, Section } from '../section';
|
||||
import { D3Chart, D3Legend } from './d3chart';
|
||||
|
||||
function getD3CurrencyFormat( symbol, position ) {
|
||||
switch ( position ) {
|
||||
case 'left_space':
|
||||
return [ symbol + ' ', '' ];
|
||||
case 'right':
|
||||
return [ '', symbol ];
|
||||
case 'right_space':
|
||||
return [ '', ' ' + symbol ];
|
||||
case 'left':
|
||||
default:
|
||||
return [ symbol, '' ];
|
||||
}
|
||||
}
|
||||
|
||||
const currencySymbol = get( wcSettings, [ 'currency', 'symbol' ], '' );
|
||||
const symbolPosition = get( wcSettings, [ 'currency', 'position' ], 'left' );
|
||||
|
||||
d3FormatDefaultLocale( {
|
||||
decimal: '.',
|
||||
thousands: ',',
|
||||
decimal: get( wcSettings, [ 'currency', 'decimal_separator' ], '.' ),
|
||||
thousands: get( wcSettings, [ 'currency', 'thousand_separator' ], ',' ),
|
||||
grouping: [ 3 ],
|
||||
currency: [ decodeEntities( get( wcSettings, 'currency.symbol', '' ) ), '' ],
|
||||
currency: getD3CurrencyFormat( currencySymbol, symbolPosition ),
|
||||
} );
|
||||
|
||||
function getOrderedKeys( props, previousOrderedKeys = [] ) {
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
# 1.1.0 (unreleased)
|
||||
|
||||
- Format using store currency settings (instead of locale)
|
||||
- Add optional currency symbol parameter
|
||||
|
||||
# 1.0.0
|
||||
|
||||
- Released package
|
||||
|
|
|
@ -17,10 +17,9 @@ _This package assumes that your code will run in an **ES2015+** environment. If
|
|||
```JS
|
||||
import { formatCurrency, getCurrencyFormatDecimal, getCurrencyFormatString } from '@woocommerce/currency';
|
||||
|
||||
// Formats money with a given currency code. Uses site's current locale for symbol formatting,
|
||||
// from the wcSettings global. Defaults to `en-US`. If no currency provided, this is also
|
||||
// pulled from wcSettings, and defaults to USD.
|
||||
const total = formatCurrency( 20.923, 'USD' ); // '$20.92'
|
||||
// Formats money with a given currency symbol. Uses site's currency settings for formatting,
|
||||
// from the wcSettings global. Defaults to symbol=`$`, precision=2, decimalSeparator=`.`, thousandSeparator=`,`
|
||||
const total = formatCurrency( 20.923, '$' ); // '$20.92'
|
||||
|
||||
// Get the rounded decimal value of a number at the precision used for the current currency,
|
||||
// from the wcSettings global. Defaults to 2.
|
||||
|
|
|
@ -3,28 +3,35 @@
|
|||
* External dependencies
|
||||
*/
|
||||
import { get, isNaN } from 'lodash';
|
||||
import { sprintf } from '@wordpress/i18n';
|
||||
|
||||
/**
|
||||
* Formats money with a given currency code. Uses site's current locale for symbol formatting
|
||||
*
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat
|
||||
* @param {Number|String} number number to format
|
||||
* @param {String} currency currency code e.g. 'USD'
|
||||
* @returns {?String} A formatted string.
|
||||
* Internal dependencies
|
||||
*/
|
||||
export function formatCurrency( number, currency ) {
|
||||
const locale = wcSettings.siteLocale || 'en-US'; // Default so we don't break.
|
||||
// default to wcSettings if currency is not passed in
|
||||
if ( ! currency ) {
|
||||
currency = get( wcSettings, 'currency.code', 'USD' );
|
||||
import { numberFormat } from 'lib/number';
|
||||
|
||||
/**
|
||||
* Formats money with a given currency code. Uses site's currency settings for formatting.
|
||||
*
|
||||
* @param {Number|String} number number to format
|
||||
* @param {String} currencySymbol currency code e.g. '$'
|
||||
* @returns {?String} A formatted string.
|
||||
*/
|
||||
export function formatCurrency( number, currencySymbol ) {
|
||||
// default to wcSettings (and then to $) if currency symbol is not passed in
|
||||
if ( ! currencySymbol ) {
|
||||
currencySymbol = get( wcSettings, [ 'currency', 'symbol' ], '$' );
|
||||
}
|
||||
if ( 'number' !== typeof number ) {
|
||||
number = parseFloat( number );
|
||||
|
||||
const precision = get( wcSettings, [ 'currency', 'precision' ], 2 );
|
||||
const formattedNumber = numberFormat( number, precision );
|
||||
const priceFormat = get( wcSettings, [ 'currency', 'price_format' ], '%1$s%2$s' );
|
||||
|
||||
if ( '' === formattedNumber ) {
|
||||
return formattedNumber;
|
||||
}
|
||||
if ( isNaN( number ) ) {
|
||||
return '';
|
||||
}
|
||||
return new Intl.NumberFormat( locale, { style: 'currency', currency } ).format( number );
|
||||
|
||||
return sprintf( priceFormat, currencySymbol, formattedNumber );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -10,32 +10,22 @@ describe( 'formatCurrency', () => {
|
|||
expect( formatCurrency( 30 ) ).toBe( '$30.00' );
|
||||
} );
|
||||
|
||||
it( 'should round a number to 2 decimal places in USD', () => {
|
||||
expect( formatCurrency( 9.49258, 'USD' ) ).toBe( '$9.49' );
|
||||
expect( formatCurrency( 30, 'USD' ) ).toBe( '$30.00' );
|
||||
expect( formatCurrency( 3.0002, 'USD' ) ).toBe( '$3.00' );
|
||||
} );
|
||||
it( 'should uses store currency settings, not locale-based', () => {
|
||||
global.wcSettings.currency.code = 'JPY';
|
||||
global.wcSettings.currency.precision = 3;
|
||||
global.wcSettings.currency.decimal_separator = ',';
|
||||
global.wcSettings.currency.thousand_separator = '.';
|
||||
global.wcSettings.currency.price_format = '%2$s%1$s';
|
||||
|
||||
it( 'should round a number to 2 decimal places in GBP', () => {
|
||||
expect( formatCurrency( 8.9272, 'GBP' ) ).toBe( '£8.93' );
|
||||
expect( formatCurrency( 11, 'GBP' ) ).toBe( '£11.00' );
|
||||
expect( formatCurrency( 7.0002, 'GBP' ) ).toBe( '£7.00' );
|
||||
} );
|
||||
|
||||
it( 'should round a number to 0 decimal places in JPY', () => {
|
||||
expect( formatCurrency( 1239.88, 'JPY' ) ).toBe( '¥1,240' );
|
||||
expect( formatCurrency( 1500, 'JPY' ) ).toBe( '¥1,500' );
|
||||
expect( formatCurrency( 33715.02, 'JPY' ) ).toBe( '¥33,715' );
|
||||
} );
|
||||
|
||||
it( 'should correctly convert and round a string', () => {
|
||||
expect( formatCurrency( '19.80', 'USD' ) ).toBe( '$19.80' );
|
||||
expect( formatCurrency( 9.49258, '¥' ) ).toBe( '9,493¥' );
|
||||
expect( formatCurrency( 3000, '¥' ) ).toBe( '3.000,000¥' );
|
||||
expect( formatCurrency( 3.0002, '¥' ) ).toBe( '3,000¥' );
|
||||
} );
|
||||
|
||||
it( "should return empty string when given an input that isn't a number", () => {
|
||||
expect( formatCurrency( 'abc', 'USD' ) ).toBe( '' );
|
||||
expect( formatCurrency( false, 'USD' ) ).toBe( '' );
|
||||
expect( formatCurrency( null, 'USD' ) ).toBe( '' );
|
||||
expect( formatCurrency( 'abc' ) ).toBe( '' );
|
||||
expect( formatCurrency( false ) ).toBe( '' );
|
||||
expect( formatCurrency( null ) ).toBe( '' );
|
||||
} );
|
||||
} );
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ wooCommercePackages.forEach( lib => {
|
|||
global.wcSettings = {
|
||||
adminUrl: 'https://vagrant.local/wp/wp-admin/',
|
||||
locale: 'en-US',
|
||||
currency: { code: 'USD', precision: 2, symbol: '$' },
|
||||
currency: { code: 'USD', precision: 2, symbol: '$' },
|
||||
date: {
|
||||
dow: 0,
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue