* Only show customer name if available

* Map customer data to extended_info in orders API response

* Add gross total and refund total to orders API response

* Hook up report orders endpoint to activity panel orders

* Add on-hold orders to activity panel orders

* Destructure extended_info in case of filtering

* Add customer link to orders panel
This commit is contained in:
Joshua T Flowers 2019-02-20 09:51:43 +08:00 committed by GitHub
parent 0ef2dc27f4
commit 73ff2d37e8
2 changed files with 95 additions and 32 deletions

View File

@ -24,7 +24,7 @@ import {
Section, Section,
} from '@woocommerce/components'; } from '@woocommerce/components';
import { formatCurrency, getCurrencyFormatDecimal } from '@woocommerce/currency'; import { formatCurrency, getCurrencyFormatDecimal } from '@woocommerce/currency';
import { getAdminLink } from '@woocommerce/navigation'; import { getAdminLink, getNewPath } from '@woocommerce/navigation';
/** /**
* Internal dependencies * Internal dependencies
@ -32,7 +32,6 @@ import { getAdminLink } from '@woocommerce/navigation';
import { ActivityCard, ActivityCardPlaceholder } from '../activity-card'; import { ActivityCard, ActivityCardPlaceholder } from '../activity-card';
import ActivityHeader from '../activity-header'; import ActivityHeader from '../activity-header';
import ActivityOutboundLink from '../activity-outbound-link'; import ActivityOutboundLink from '../activity-outbound-link';
import { getOrderRefundTotal } from 'lib/order-values';
import { QUERY_DEFAULTS } from 'wc-api/constants'; import { QUERY_DEFAULTS } from 'wc-api/constants';
import withSelect from 'wc-api/with-select'; import withSelect from 'wc-api/with-select';
@ -64,28 +63,56 @@ function OrdersPanel( { orders, isRequesting, isError } ) {
</EllipsisMenu> </EllipsisMenu>
); );
const orderCardTitle = ( order, address ) => { const getCustomerString = order => {
const name = `${ address.first_name } ${ address.last_name }`; const extended_info = order.extended_info || {};
const { first_name, last_name } = extended_info.customer || {};
if ( ! first_name && ! last_name ) {
return '';
}
const name = [ first_name, last_name ].join( ' ' );
return sprintf(
__(
/* translators: describes who placed an order, e.g. Order #123 placed by John Doe */
'placed by {{customerLink}}%(customerName)s{{/customerLink}}',
'wc-admin'
),
{
customerName: name,
}
);
};
const orderCardTitle = order => {
const { extended_info, order_id } = order;
const { customer } = extended_info || {};
const customerUrl = customer.customer_id
? getNewPath( {}, '/analytics/customers', {
filter: 'single_customer',
customer_id: customer.customer_id,
} )
: null;
return ( return (
<Fragment> <Fragment>
{ interpolateComponents( { { interpolateComponents( {
mixedString: sprintf( mixedString: sprintf(
__( __(
/* eslint-disable-next-line max-len */ 'Order {{orderLink}}#%(orderNumber)s{{/orderLink}} %(customerString)s {{destinationFlag/}}',
'Order {{orderLink}}#%(orderNumber)s{{/orderLink}} placed by {{customerLink}}%(customerName)s{{/customerLink}} {{destinationFlag/}}',
'wc-admin' 'wc-admin'
), ),
{ {
orderNumber: order.number, orderNumber: order_id,
customerName: name, customerString: getCustomerString( order ),
} }
), ),
components: { components: {
orderLink: <Link href={ 'post.php?action=edit&post=' + order.id } type="wp-admin" />, orderLink: <Link href={ 'post.php?action=edit&post=' + order_id } type="wp-admin" />,
// @todo Hook up customer name link destinationFlag: customer.country ? (
customerLink: <Link href={ '#' } type="wp-admin" />, <Flag code={ customer.country } round={ false } />
destinationFlag: <Flag order={ order } round={ false } />, ) : null,
customerLink: customerUrl ? <Link href={ customerUrl } type="wc-admin" /> : <span />,
}, },
} ) } } ) }
</Fragment> </Fragment>
@ -93,20 +120,20 @@ function OrdersPanel( { orders, isRequesting, isError } ) {
}; };
const cards = []; const cards = [];
orders.forEach( ( order, id ) => { orders.forEach( order => {
// We want the billing address, but shipping can be used as a fallback. const extended_info = order.extended_info || {};
const address = { ...order.shipping, ...order.billing }; const productsCount =
const productsCount = order.line_items.reduce( ( total, line ) => total + line.quantity, 0 ); extended_info && extended_info.products ? extended_info.products.length : 0;
const total = order.total; const total = order.gross_total;
const refundValue = getOrderRefundTotal( order ); const refundValue = order.refund_total;
const remainingTotal = getCurrencyFormatDecimal( order.total ) + refundValue; const remainingTotal = getCurrencyFormatDecimal( total ) + refundValue;
cards.push( cards.push(
<ActivityCard <ActivityCard
key={ id } key={ order.order_id }
className="woocommerce-order-activity-card" className="woocommerce-order-activity-card"
title={ orderCardTitle( order, address ) } title={ orderCardTitle( order ) }
date={ order.date_created } date={ order.date_created }
subtitle={ subtitle={
<div> <div>
@ -118,16 +145,15 @@ function OrdersPanel( { orders, isRequesting, isError } ) {
</span> </span>
{ refundValue ? ( { refundValue ? (
<span> <span>
<s>{ formatCurrency( total, order.currency_symbol ) }</s>{' '} <s>{ formatCurrency( total ) }</s> { formatCurrency( remainingTotal ) }
{ formatCurrency( remainingTotal, order.currency_symbol ) }
</span> </span>
) : ( ) : (
<span>{ formatCurrency( total, order.currency_symbol ) }</span> <span>{ formatCurrency( total ) }</span>
) } ) }
</div> </div>
} }
actions={ actions={
<Button isDefault href={ getAdminLink( 'post.php?action=edit&post=' + order.id ) }> <Button isDefault href={ getAdminLink( 'post.php?action=edit&post=' + order.order_id ) }>
{ __( 'Begin fulfillment' ) } { __( 'Begin fulfillment' ) }
</Button> </Button>
} }
@ -162,29 +188,30 @@ function OrdersPanel( { orders, isRequesting, isError } ) {
} }
OrdersPanel.propTypes = { OrdersPanel.propTypes = {
orders: PropTypes.instanceOf( Map ).isRequired, orders: PropTypes.array.isRequired,
isError: PropTypes.bool, isError: PropTypes.bool,
isRequesting: PropTypes.bool, isRequesting: PropTypes.bool,
}; };
OrdersPanel.defaultProps = { OrdersPanel.defaultProps = {
orders: new Map(), orders: [],
isError: false, isError: false,
isRequesting: false, isRequesting: false,
}; };
export default compose( export default compose(
withSelect( select => { withSelect( select => {
const { getItems, getItemsError, isGetItemsRequesting } = select( 'wc-api' ); const { getReportItems, getReportItemsError, isReportItemsRequesting } = select( 'wc-api' );
const ordersQuery = { const ordersQuery = {
page: 1, page: 1,
per_page: QUERY_DEFAULTS.pageSize, per_page: QUERY_DEFAULTS.pageSize,
status: 'processing', status_is: [ 'processing', 'on-hold' ],
extended_info: true,
}; };
const orders = getItems( 'orders', ordersQuery ); const orders = getReportItems( 'orders', ordersQuery ).data;
const isError = Boolean( getItemsError( 'orders', ordersQuery ) ); const isError = Boolean( getReportItemsError( 'orders', ordersQuery ) );
const isRequesting = isGetItemsRequesting( 'orders', ordersQuery ); const isRequesting = isReportItemsRequesting( 'orders', ordersQuery );
return { orders, isError, isRequesting }; return { orders, isError, isRequesting };
} ) } )

View File

@ -30,6 +30,8 @@ class WC_Admin_Reports_Orders_Data_Store extends WC_Admin_Reports_Data_Store imp
'status' => 'strval', 'status' => 'strval',
'customer_id' => 'intval', 'customer_id' => 'intval',
'net_total' => 'floatval', 'net_total' => 'floatval',
'gross_total' => 'floatval',
'refund_total' => 'floatval',
'num_items_sold' => 'intval', 'num_items_sold' => 'intval',
'customer_type' => 'strval', 'customer_type' => 'strval',
); );
@ -45,6 +47,8 @@ class WC_Admin_Reports_Orders_Data_Store extends WC_Admin_Reports_Data_Store imp
'status' => 'REPLACE(status, "wc-", "") as status', 'status' => 'REPLACE(status, "wc-", "") as status',
'customer_id' => 'customer_id', 'customer_id' => 'customer_id',
'net_total' => 'net_total', 'net_total' => 'net_total',
'gross_total' => 'gross_total',
'refund_total' => 'refund_total',
'num_items_sold' => 'num_items_sold', 'num_items_sold' => 'num_items_sold',
'customer_type' => '(CASE WHEN returning_customer <> 0 THEN "returning" ELSE "new" END) as customer_type', 'customer_type' => '(CASE WHEN returning_customer <> 0 THEN "returning" ELSE "new" END) as customer_type',
); );
@ -243,6 +247,8 @@ class WC_Admin_Reports_Orders_Data_Store extends WC_Admin_Reports_Data_Store imp
$mapped_products = $this->map_array_by_key( $products, 'product_id' ); $mapped_products = $this->map_array_by_key( $products, 'product_id' );
$coupons = $this->get_coupons_by_order_ids( array_keys( $mapped_orders ) ); $coupons = $this->get_coupons_by_order_ids( array_keys( $mapped_orders ) );
$product_categories = $this->get_product_categories_by_product_ids( array_keys( $mapped_products ) ); $product_categories = $this->get_product_categories_by_product_ids( array_keys( $mapped_products ) );
$customers = $this->get_customers_by_orders( $orders_data );
$mapped_customers = $this->map_array_by_key( $customers, 'customer_id' );
$mapped_data = array(); $mapped_data = array();
foreach ( $products as $product ) { foreach ( $products as $product ) {
@ -280,8 +286,12 @@ class WC_Admin_Reports_Orders_Data_Store extends WC_Admin_Reports_Data_Store imp
'products' => array(), 'products' => array(),
'categories' => array(), 'categories' => array(),
'coupons' => array(), 'coupons' => array(),
'customer' => array(),
); );
$orders_data[ $key ]['extended_info'] = isset( $mapped_data[ $order_data['order_id'] ] ) ? array_merge( $defaults, $mapped_data[ $order_data['order_id'] ] ) : $defaults; $orders_data[ $key ]['extended_info'] = isset( $mapped_data[ $order_data['order_id'] ] ) ? array_merge( $defaults, $mapped_data[ $order_data['order_id'] ] ) : $defaults;
if ( $order_data['customer_id'] && isset( $mapped_customers[ $order_data['customer_id'] ] ) ) {
$orders_data[ $key ]['extended_info']['customer'] = $mapped_customers[ $order_data['customer_id'] ];
}
} }
} }
@ -324,6 +334,32 @@ class WC_Admin_Reports_Orders_Data_Store extends WC_Admin_Reports_Data_Store imp
return $products; return $products;
} }
/**
* Get customer data from order IDs.
*
* @param array $orders Array of orders.
* @return array
*/
protected function get_customers_by_orders( $orders ) {
global $wpdb;
$customer_lookup_table = $wpdb->prefix . 'wc_customer_lookup';
$customer_ids = array();
foreach ( $orders as $order ) {
if ( $order['customer_id'] ) {
$customer_ids[] = $order['customer_id'];
}
}
$customer_ids = implode( ',', $customer_ids );
$customers = $wpdb->get_results(
"SELECT * FROM {$customer_lookup_table} WHERE customer_id IN ({$customer_ids})",
ARRAY_A
); // WPCS: cache ok, DB call ok, unprepared SQL ok.
return $customers;
}
/** /**
* Get coupon information from order IDs. * Get coupon information from order IDs.
* *