Order product count api requests orders store (#33063)
* Add initial Order redux store * Add unit tests for orders store * Update fetch with headers to parse error * Update action names, and consolidate request function * Add chnagelog * Update resolver to always include id and update reducer to prevent data overwrite
This commit is contained in:
parent
0f0b2d5064
commit
83686b2980
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: add
|
||||
|
||||
Add new orders data store, for retrieving orders data. #33063
|
|
@ -35,7 +35,12 @@ const controls = {
|
|||
headers,
|
||||
status,
|
||||
data,
|
||||
} ) );
|
||||
} ) )
|
||||
.catch( ( response ) => {
|
||||
return response.json().then( ( data: unknown ) => {
|
||||
throw data;
|
||||
} );
|
||||
} );
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { RestApiError } from '../types';
|
||||
|
||||
export type SettingProperties = {
|
||||
label?: string;
|
||||
label_class?: string[];
|
||||
|
|
|
@ -17,6 +17,7 @@ export { OPTIONS_STORE_NAME } from './options';
|
|||
export { ITEMS_STORE_NAME } from './items';
|
||||
export { PAYMENT_GATEWAYS_STORE_NAME } from './payment-gateways';
|
||||
export { PRODUCTS_STORE_NAME } from './products';
|
||||
export { ORDERS_STORE_NAME } from './orders';
|
||||
export { PaymentGateway } from './payment-gateways/types';
|
||||
|
||||
// Export hooks
|
||||
|
@ -69,6 +70,7 @@ export * from './countries/types';
|
|||
export * from './onboarding/types';
|
||||
export * from './plugins/types';
|
||||
export * from './products/types';
|
||||
export * from './orders/types';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -86,6 +88,7 @@ import type { ITEMS_STORE_NAME } from './items';
|
|||
import type { COUNTRIES_STORE_NAME } from './countries';
|
||||
import type { PAYMENT_GATEWAYS_STORE_NAME } from './payment-gateways';
|
||||
import type { PRODUCTS_STORE_NAME } from './products';
|
||||
import type { ORDERS_STORE_NAME } from './orders';
|
||||
|
||||
export type WCDataStoreName =
|
||||
| typeof REVIEWS_STORE_NAME
|
||||
|
@ -100,7 +103,8 @@ export type WCDataStoreName =
|
|||
| typeof ITEMS_STORE_NAME
|
||||
| typeof COUNTRIES_STORE_NAME
|
||||
| typeof PAYMENT_GATEWAYS_STORE_NAME
|
||||
| typeof PRODUCTS_STORE_NAME;
|
||||
| typeof PRODUCTS_STORE_NAME
|
||||
| typeof ORDERS_STORE_NAME;
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -111,6 +115,7 @@ import { PluginSelectors } from './plugins/selectors';
|
|||
import { OnboardingSelectors } from './onboarding/selectors';
|
||||
import { OptionsSelectors } from './options/types';
|
||||
import { ProductsSelectors } from './products/selectors';
|
||||
import { OrdersSelectors } from './orders/selectors';
|
||||
|
||||
// As we add types to all the package selectors we can fill out these unknown types with real ones. See one
|
||||
// of the already typed selectors for an example of how you can do this.
|
||||
|
@ -140,6 +145,8 @@ export type WCSelectorType< T > = T extends typeof REVIEWS_STORE_NAME
|
|||
? WPDataSelectors
|
||||
: T extends typeof PRODUCTS_STORE_NAME
|
||||
? ProductsSelectors
|
||||
: T extends typeof ORDERS_STORE_NAME
|
||||
? OrdersSelectors
|
||||
: never;
|
||||
|
||||
export interface WCDataSelector {
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
export enum TYPES {
|
||||
GET_ORDER_SUCCESS = 'GET_ORDER_SUCCESS',
|
||||
GET_ORDER_ERROR = 'GET_ORDER_ERROR',
|
||||
GET_ORDERS_SUCCESS = 'GET_ORDERS_SUCCESS',
|
||||
GET_ORDERS_ERROR = 'GET_ORDERS_ERROR',
|
||||
GET_ORDERS_TOTAL_COUNT_SUCCESS = 'GET_ORDERS_TOTAL_COUNT_SUCCESS',
|
||||
GET_ORDERS_TOTAL_COUNT_ERROR = 'GET_ORDERS_TOTAL_COUNT_ERROR',
|
||||
}
|
||||
|
||||
export default TYPES;
|
|
@ -0,0 +1,76 @@
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import TYPES from './action-types';
|
||||
import { PartialOrder, OrdersQuery } from './types';
|
||||
|
||||
export function getOrderSuccess( id: number, order: PartialOrder ) {
|
||||
return {
|
||||
type: TYPES.GET_ORDER_SUCCESS as const,
|
||||
id,
|
||||
order,
|
||||
};
|
||||
}
|
||||
|
||||
export function getOrderError( query: Partial< OrdersQuery >, error: unknown ) {
|
||||
return {
|
||||
type: TYPES.GET_ORDER_ERROR as const,
|
||||
query,
|
||||
error,
|
||||
};
|
||||
}
|
||||
|
||||
export function getOrdersSuccess(
|
||||
query: Partial< OrdersQuery >,
|
||||
orders: PartialOrder[],
|
||||
totalCount: number
|
||||
) {
|
||||
return {
|
||||
type: TYPES.GET_ORDERS_SUCCESS as const,
|
||||
orders,
|
||||
query,
|
||||
totalCount,
|
||||
};
|
||||
}
|
||||
|
||||
export function getOrdersError(
|
||||
query: Partial< OrdersQuery >,
|
||||
error: unknown
|
||||
) {
|
||||
return {
|
||||
type: TYPES.GET_ORDERS_ERROR as const,
|
||||
query,
|
||||
error,
|
||||
};
|
||||
}
|
||||
|
||||
export function getOrdersTotalCountSuccess(
|
||||
query: Partial< OrdersQuery >,
|
||||
totalCount: number
|
||||
) {
|
||||
return {
|
||||
type: TYPES.GET_ORDERS_TOTAL_COUNT_SUCCESS as const,
|
||||
query,
|
||||
totalCount,
|
||||
};
|
||||
}
|
||||
|
||||
export function getOrdersTotalCountError(
|
||||
query: Partial< OrdersQuery >,
|
||||
error: unknown
|
||||
) {
|
||||
return {
|
||||
type: TYPES.GET_ORDERS_TOTAL_COUNT_ERROR as const,
|
||||
query,
|
||||
error,
|
||||
};
|
||||
}
|
||||
|
||||
export type Actions = ReturnType<
|
||||
| typeof getOrderSuccess
|
||||
| typeof getOrderError
|
||||
| typeof getOrdersSuccess
|
||||
| typeof getOrdersError
|
||||
| typeof getOrdersTotalCountSuccess
|
||||
| typeof getOrdersTotalCountError
|
||||
>;
|
|
@ -0,0 +1,3 @@
|
|||
export const STORE_NAME = 'wc/admin/orders';
|
||||
|
||||
export const WC_ORDERS_NAMESPACE = '/wc/v3/orders';
|
|
@ -0,0 +1,26 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
|
||||
import { registerStore } from '@wordpress/data';
|
||||
import { Reducer } from 'redux';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { STORE_NAME } from './constants';
|
||||
import * as selectors from './selectors';
|
||||
import * as actions from './actions';
|
||||
import * as resolvers from './resolvers';
|
||||
import reducer, { OrdersState, State } from './reducer';
|
||||
import controls from '../controls';
|
||||
|
||||
registerStore< State >( STORE_NAME, {
|
||||
reducer: reducer as Reducer< OrdersState >,
|
||||
actions,
|
||||
controls,
|
||||
selectors,
|
||||
resolvers,
|
||||
} );
|
||||
|
||||
export const ORDERS_STORE_NAME = STORE_NAME;
|
|
@ -0,0 +1,104 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { Reducer } from 'redux';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import TYPES from './action-types';
|
||||
import { Actions } from './actions';
|
||||
import { PartialOrder } from './types';
|
||||
import { getOrderResourceName, getTotalOrderCountResourceName } from './utils';
|
||||
|
||||
export type OrdersState = {
|
||||
orders: Record<
|
||||
string,
|
||||
{
|
||||
data: number[];
|
||||
}
|
||||
>;
|
||||
ordersCount: Record< string, number >;
|
||||
errors: Record< string, unknown >;
|
||||
data: Record< number, PartialOrder >;
|
||||
};
|
||||
|
||||
const reducer: Reducer< OrdersState, Actions > = (
|
||||
state = {
|
||||
orders: {},
|
||||
ordersCount: {},
|
||||
errors: {},
|
||||
data: {},
|
||||
},
|
||||
payload
|
||||
) => {
|
||||
if ( payload && 'type' in payload ) {
|
||||
switch ( payload.type ) {
|
||||
case TYPES.GET_ORDER_SUCCESS:
|
||||
const orderData = state.data || {};
|
||||
return {
|
||||
...state,
|
||||
data: {
|
||||
...orderData,
|
||||
[ payload.id ]: {
|
||||
...( orderData[ payload.id ] || {} ),
|
||||
...payload.order,
|
||||
},
|
||||
},
|
||||
};
|
||||
case TYPES.GET_ORDERS_SUCCESS:
|
||||
const ids: number[] = [];
|
||||
const nextOrders = payload.orders.reduce<
|
||||
Record< number, PartialOrder >
|
||||
>( ( result, order ) => {
|
||||
ids.push( order.id );
|
||||
result[ order.id ] = {
|
||||
...( state.data[ order.id ] || {} ),
|
||||
...order,
|
||||
};
|
||||
return result;
|
||||
}, {} );
|
||||
const resourceName = getOrderResourceName( payload.query );
|
||||
return {
|
||||
...state,
|
||||
orders: {
|
||||
...state.orders,
|
||||
[ resourceName ]: { data: ids },
|
||||
},
|
||||
data: {
|
||||
...state.data,
|
||||
...nextOrders,
|
||||
},
|
||||
};
|
||||
case TYPES.GET_ORDERS_TOTAL_COUNT_SUCCESS:
|
||||
const totalResourceName = getTotalOrderCountResourceName(
|
||||
payload.query
|
||||
);
|
||||
return {
|
||||
...state,
|
||||
ordersCount: {
|
||||
...state.ordersCount,
|
||||
[ totalResourceName ]: payload.totalCount,
|
||||
},
|
||||
};
|
||||
case TYPES.GET_ORDER_ERROR:
|
||||
case TYPES.GET_ORDERS_ERROR:
|
||||
case TYPES.GET_ORDERS_TOTAL_COUNT_ERROR:
|
||||
return {
|
||||
...state,
|
||||
errors: {
|
||||
...state.errors,
|
||||
[ getOrderResourceName(
|
||||
payload.query
|
||||
) ]: payload.error,
|
||||
},
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
return state;
|
||||
};
|
||||
|
||||
export type State = ReturnType< typeof reducer >;
|
||||
export default reducer;
|
|
@ -0,0 +1,60 @@
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { WC_ORDERS_NAMESPACE } from './constants';
|
||||
import { Order, OrdersQuery } from './types';
|
||||
import {
|
||||
getOrdersError,
|
||||
getOrdersSuccess,
|
||||
getOrdersTotalCountError,
|
||||
getOrdersTotalCountSuccess,
|
||||
} from './actions';
|
||||
import { request } from '../utils';
|
||||
|
||||
export function* getOrders( query: Partial< OrdersQuery > ) {
|
||||
// id is always required.
|
||||
const ordersQuery = {
|
||||
...query,
|
||||
};
|
||||
if (
|
||||
ordersQuery &&
|
||||
ordersQuery._fields &&
|
||||
! ordersQuery._fields.includes( 'id' )
|
||||
) {
|
||||
ordersQuery._fields = [ 'id', ...ordersQuery._fields ];
|
||||
}
|
||||
try {
|
||||
const {
|
||||
items,
|
||||
totalCount,
|
||||
}: { items: Order[]; totalCount: number } = yield request<
|
||||
OrdersQuery,
|
||||
Order
|
||||
>( WC_ORDERS_NAMESPACE, ordersQuery );
|
||||
yield getOrdersTotalCountSuccess( query, totalCount );
|
||||
yield getOrdersSuccess( query, items, totalCount );
|
||||
return items;
|
||||
} catch ( error ) {
|
||||
yield getOrdersError( query, error );
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
export function* getOrdersTotalCount( query: Partial< OrdersQuery > ) {
|
||||
try {
|
||||
const totalsQuery = {
|
||||
...query,
|
||||
page: 1,
|
||||
per_page: 1,
|
||||
};
|
||||
const { totalCount } = yield request< OrdersQuery, Order >(
|
||||
WC_ORDERS_NAMESPACE,
|
||||
totalsQuery
|
||||
);
|
||||
yield getOrdersTotalCountSuccess( query, totalCount );
|
||||
return totalCount;
|
||||
} catch ( error ) {
|
||||
yield getOrdersTotalCountError( query, error );
|
||||
return error;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import createSelector from 'rememo';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { getOrderResourceName, getTotalOrderCountResourceName } from './utils';
|
||||
import { OrdersState } from './reducer';
|
||||
import { OrdersQuery, PartialOrder } from './types';
|
||||
import { WPDataSelector, WPDataSelectors } from '../types';
|
||||
|
||||
export const getOrders = createSelector(
|
||||
( state: OrdersState, query: OrdersQuery, defaultValue = undefined ) => {
|
||||
const resourceName = getOrderResourceName( query );
|
||||
const ids = state.orders[ resourceName ]
|
||||
? state.orders[ resourceName ].data
|
||||
: undefined;
|
||||
if ( ! ids ) {
|
||||
return defaultValue;
|
||||
}
|
||||
if ( query._fields ) {
|
||||
return ids.map( ( id ) => {
|
||||
return query._fields.reduce(
|
||||
( product: PartialOrder, field: keyof PartialOrder ) => {
|
||||
return {
|
||||
...product,
|
||||
[ field ]: state.data[ id ][ field ],
|
||||
};
|
||||
},
|
||||
{} as PartialOrder
|
||||
);
|
||||
} );
|
||||
}
|
||||
return ids.map( ( id ) => {
|
||||
return state.data[ id ];
|
||||
} );
|
||||
},
|
||||
( state, query ) => {
|
||||
const resourceName = getOrderResourceName( query );
|
||||
const ids = state.orders[ resourceName ]
|
||||
? state.orders[ resourceName ].data
|
||||
: [];
|
||||
return [
|
||||
state.orders[ resourceName ],
|
||||
...ids.map( ( id: number ) => {
|
||||
return state.data[ id ];
|
||||
} ),
|
||||
];
|
||||
}
|
||||
);
|
||||
|
||||
export const getOrdersTotalCount = (
|
||||
state: OrdersState,
|
||||
query: OrdersQuery,
|
||||
defaultValue = undefined
|
||||
) => {
|
||||
const resourceName = getTotalOrderCountResourceName( query );
|
||||
const totalCount = state.ordersCount.hasOwnProperty( resourceName )
|
||||
? state.ordersCount[ resourceName ]
|
||||
: defaultValue;
|
||||
return totalCount;
|
||||
};
|
||||
|
||||
export const getOrdersError = ( state: OrdersState, query: OrdersQuery ) => {
|
||||
const resourceName = getOrderResourceName( query );
|
||||
return state.errors[ resourceName ];
|
||||
};
|
||||
|
||||
export type OrdersSelectors = {
|
||||
getOrders: WPDataSelector< typeof getOrders >;
|
||||
getOrdersTotalCount: WPDataSelector< typeof getOrdersTotalCount >;
|
||||
getOrdersError: WPDataSelector< typeof getOrdersError >;
|
||||
} & WPDataSelectors;
|
|
@ -0,0 +1,155 @@
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import reducer, { OrdersState } from '../reducer';
|
||||
import TYPES from '../action-types';
|
||||
import { getOrderResourceName, getTotalOrderCountResourceName } from '../utils';
|
||||
import { Actions } from '../actions';
|
||||
import { PartialOrder, OrdersQuery } from '../types';
|
||||
|
||||
const defaultState: OrdersState = {
|
||||
orders: {},
|
||||
ordersCount: {},
|
||||
errors: {},
|
||||
data: {},
|
||||
};
|
||||
|
||||
describe( 'orders reducer', () => {
|
||||
it( 'should return a default state', () => {
|
||||
const state = reducer( undefined, {} as Actions );
|
||||
expect( state ).toEqual( defaultState );
|
||||
expect( state ).not.toBe( defaultState );
|
||||
} );
|
||||
|
||||
it( 'should handle GET_ORDER_SUCCESS', () => {
|
||||
const itemType = 'guyisms';
|
||||
const initialState: OrdersState = {
|
||||
orders: {
|
||||
[ itemType ]: {
|
||||
data: [ 1, 2 ],
|
||||
},
|
||||
},
|
||||
ordersCount: {
|
||||
'total-guyisms:{}': 2,
|
||||
},
|
||||
errors: {},
|
||||
data: {
|
||||
1: { id: 1, status: 'pending' },
|
||||
2: { id: 2, status: 'completed' },
|
||||
},
|
||||
};
|
||||
const update: PartialOrder = {
|
||||
id: 2,
|
||||
status: 'completed',
|
||||
};
|
||||
|
||||
const state = reducer( initialState, {
|
||||
type: TYPES.GET_ORDER_SUCCESS,
|
||||
id: update.id,
|
||||
order: update,
|
||||
} );
|
||||
|
||||
expect( state.orders ).toEqual( initialState.orders );
|
||||
expect( state.errors ).toEqual( initialState.errors );
|
||||
|
||||
expect( state.data[ 1 ] ).toEqual( initialState.data[ 1 ] );
|
||||
expect( state.data[ 2 ].id ).toEqual( initialState.data[ 2 ].id );
|
||||
expect( state.data[ 2 ].title ).toEqual( initialState.data[ 2 ].title );
|
||||
expect( state.data[ 2 ].status ).toEqual( update.status );
|
||||
} );
|
||||
|
||||
it( 'should handle GET_ORDERS_SUCCESS', () => {
|
||||
const orders: PartialOrder[] = [ { id: 1 }, { id: 2 } ];
|
||||
const totalCount = 45;
|
||||
const query: Partial< OrdersQuery > = { status: 'completed' };
|
||||
const state = reducer( defaultState, {
|
||||
type: TYPES.GET_ORDERS_SUCCESS,
|
||||
orders,
|
||||
query,
|
||||
totalCount,
|
||||
} );
|
||||
|
||||
const resourceName = getOrderResourceName( query );
|
||||
|
||||
expect( state.orders[ resourceName ].data ).toHaveLength( 2 );
|
||||
expect( state.orders[ resourceName ].data.includes( 1 ) ).toBeTruthy();
|
||||
expect( state.orders[ resourceName ].data.includes( 2 ) ).toBeTruthy();
|
||||
|
||||
expect( state.data[ 1 ] ).toEqual( orders[ 0 ] );
|
||||
expect( state.data[ 2 ] ).toEqual( orders[ 1 ] );
|
||||
} );
|
||||
|
||||
it( 'GET_ORDERS_SUCCESS, should only update new order data if added', () => {
|
||||
const orders: PartialOrder[] = [
|
||||
{ id: 1 },
|
||||
{ id: 2, total: '36.00', currency: 'CAD' },
|
||||
];
|
||||
const initialState = {
|
||||
...defaultState,
|
||||
data: {
|
||||
1: { id: 1, total: '20.00' },
|
||||
2: { id: 2, total: '34.00' },
|
||||
},
|
||||
};
|
||||
const totalCount = 45;
|
||||
const query: Partial< OrdersQuery > = { status: 'completed' };
|
||||
const state = reducer( initialState, {
|
||||
type: TYPES.GET_ORDERS_SUCCESS,
|
||||
orders,
|
||||
query,
|
||||
totalCount,
|
||||
} );
|
||||
|
||||
const resourceName = getOrderResourceName( query );
|
||||
|
||||
expect( state.data[ 1 ].total ).toEqual( initialState.data[ 1 ].total );
|
||||
expect( state.data[ 2 ] ).toEqual( orders[ 1 ] );
|
||||
} );
|
||||
|
||||
it( 'should handle GET_ORDERS_TOTAL_COUNT_SUCCESS', () => {
|
||||
const initialQuery: Partial< OrdersQuery > = {
|
||||
status: 'completed',
|
||||
page: 1,
|
||||
per_page: 1,
|
||||
_fields: [ 'id' ],
|
||||
};
|
||||
const resourceName = getTotalOrderCountResourceName( initialQuery );
|
||||
const initialState: OrdersState = {
|
||||
...defaultState,
|
||||
ordersCount: {
|
||||
[ resourceName ]: 1,
|
||||
},
|
||||
};
|
||||
|
||||
// Additional coverage for getTotalCountResourceName().
|
||||
const similarQueryForTotals: Partial< OrdersQuery > = {
|
||||
status: 'completed',
|
||||
page: 2,
|
||||
per_page: 10,
|
||||
_fields: [ 'id', 'title', 'status' ],
|
||||
};
|
||||
|
||||
const state = reducer( initialState, {
|
||||
type: TYPES.GET_ORDERS_TOTAL_COUNT_SUCCESS,
|
||||
query: similarQueryForTotals,
|
||||
totalCount: 2,
|
||||
} );
|
||||
|
||||
expect( state.ordersCount ).toEqual( {
|
||||
[ resourceName ]: 2,
|
||||
} );
|
||||
} );
|
||||
|
||||
it( 'should handle GET_ORDERS_ERROR', () => {
|
||||
const query: Partial< OrdersQuery > = { status: 'pending' };
|
||||
const resourceName = getOrderResourceName( query );
|
||||
const error = 'Baaam!';
|
||||
const state = reducer( defaultState, {
|
||||
type: TYPES.GET_ORDERS_ERROR,
|
||||
query,
|
||||
error,
|
||||
} );
|
||||
|
||||
expect( state.errors[ resourceName ] ).toBe( error );
|
||||
} );
|
||||
} );
|
|
@ -0,0 +1,47 @@
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { OrdersQuery } from '../types';
|
||||
import { getTotalOrderCountResourceName } from '../utils';
|
||||
|
||||
describe( 'getTotalOrderCountResourceName()', () => {
|
||||
it( "Ignores query params that don't affect total counts", () => {
|
||||
const fullQuery: Partial< OrdersQuery > = {
|
||||
page: 2,
|
||||
per_page: 10,
|
||||
_fields: [ 'id', 'status', 'total' ],
|
||||
status: 'completed',
|
||||
};
|
||||
|
||||
const slimQuery: Partial< OrdersQuery > = {
|
||||
page: 1,
|
||||
per_page: 1,
|
||||
_fields: [ 'id' ],
|
||||
status: 'completed',
|
||||
};
|
||||
|
||||
expect( getTotalOrderCountResourceName( fullQuery ) ).toEqual(
|
||||
getTotalOrderCountResourceName( slimQuery )
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'Accounts for query params that do affect total counts', () => {
|
||||
const firstQuery: Partial< OrdersQuery > = {
|
||||
page: 2,
|
||||
per_page: 10,
|
||||
_fields: [ 'id', 'status', 'total' ],
|
||||
status: 'completed',
|
||||
};
|
||||
|
||||
const secondQuery: Partial< OrdersQuery > = {
|
||||
page: 1,
|
||||
per_page: 1,
|
||||
_fields: [ 'id' ],
|
||||
status: 'pending',
|
||||
};
|
||||
|
||||
expect( getTotalOrderCountResourceName( firstQuery ) ).not.toEqual(
|
||||
getTotalOrderCountResourceName( secondQuery )
|
||||
);
|
||||
} );
|
||||
} );
|
|
@ -0,0 +1,162 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { Schema } from '@wordpress/core-data';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { BaseQueryParams } from '../types';
|
||||
|
||||
export type Address = {
|
||||
first_name: string;
|
||||
last_name: string;
|
||||
company: string;
|
||||
address_1: string;
|
||||
address_2: string;
|
||||
city: string;
|
||||
state: string;
|
||||
postcode: string;
|
||||
country: string;
|
||||
email: string;
|
||||
phone: string;
|
||||
};
|
||||
|
||||
export type OrderTax = {
|
||||
id: number;
|
||||
total: string;
|
||||
subtotal: string;
|
||||
};
|
||||
|
||||
export type OrderTaxLine = {
|
||||
id: number;
|
||||
rate_code: string;
|
||||
rate_id: string;
|
||||
label: string;
|
||||
compound: boolean;
|
||||
tax_total: string;
|
||||
shipping_tax_total: string;
|
||||
};
|
||||
|
||||
export type OrderFeeLine = {
|
||||
id: number;
|
||||
name: string;
|
||||
tax_class: string;
|
||||
tax_status: string;
|
||||
total: string;
|
||||
total_tax: string;
|
||||
taxes: OrderTax[];
|
||||
};
|
||||
|
||||
export type OrderShippingLine = {
|
||||
id: number;
|
||||
method_title: string;
|
||||
method_id: string;
|
||||
total: string;
|
||||
total_tax: string;
|
||||
taxes: Omit< OrderTax, 'subtotal' >[];
|
||||
};
|
||||
|
||||
export type OrderCoupon = {
|
||||
id: number;
|
||||
code: string;
|
||||
discount: string;
|
||||
discount_tax: string;
|
||||
};
|
||||
|
||||
export type OrderMetaData = {
|
||||
key: string;
|
||||
label: string;
|
||||
value: unknown;
|
||||
};
|
||||
|
||||
export type OrderRefund = {
|
||||
id: number;
|
||||
reason: string;
|
||||
total: string;
|
||||
};
|
||||
|
||||
export type OrderLineItem = {
|
||||
id: number;
|
||||
name: string;
|
||||
sku: string;
|
||||
product_id: string | number;
|
||||
variation_id: number;
|
||||
quantity: number;
|
||||
tax_class: string;
|
||||
price: string;
|
||||
subtotal: string;
|
||||
subtotal_tax: string;
|
||||
total: string;
|
||||
total_tax: string;
|
||||
taxes: OrderTax[];
|
||||
meta_data: OrderMetaData[];
|
||||
};
|
||||
|
||||
export type OrderStatus =
|
||||
| 'processing'
|
||||
| 'pending'
|
||||
| 'on-hold'
|
||||
| 'completed'
|
||||
| 'cancelled'
|
||||
| 'refunded'
|
||||
| 'failed';
|
||||
|
||||
export type Order< Status = OrderStatus > = Omit< Schema.Post, 'status' > & {
|
||||
id: number;
|
||||
number: string;
|
||||
order_key: string;
|
||||
created_via: string;
|
||||
status: Status;
|
||||
currency: string;
|
||||
version: number;
|
||||
prices_include_tax: boolean;
|
||||
customer_id: number;
|
||||
discount_total: string;
|
||||
discount_tax: string;
|
||||
shipping_total: string;
|
||||
shipping_tax: string;
|
||||
cart_tax: string;
|
||||
total: string;
|
||||
total_tax: string;
|
||||
billing: Address;
|
||||
shipping: Address;
|
||||
payment_method: string;
|
||||
payment_method_title: string;
|
||||
set_paid: boolean;
|
||||
transaction_id: string;
|
||||
customer_ip_address: string;
|
||||
customer_user_agent: string;
|
||||
customer_note: string;
|
||||
date_completed: string;
|
||||
date_paid: string;
|
||||
cart_hash: string;
|
||||
line_items: OrderLineItem[];
|
||||
tax_lines: OrderTaxLine[];
|
||||
shipping_lines: OrderShippingLine[];
|
||||
fee_lines: OrderFeeLine[];
|
||||
coupon_lines: OrderCoupon[];
|
||||
refunds: OrderRefund[];
|
||||
};
|
||||
|
||||
export type PartialOrder = Partial< Order > & Pick< Order, 'id' >;
|
||||
|
||||
type OrdersQueryStatus =
|
||||
| 'any'
|
||||
| 'pending'
|
||||
| 'processing'
|
||||
| 'on-hold'
|
||||
| 'completed'
|
||||
| 'cancelled'
|
||||
| 'refunded'
|
||||
| 'failed'
|
||||
| 'trash';
|
||||
|
||||
export type OrdersQuery< Status = OrdersQueryStatus > = BaseQueryParams<
|
||||
keyof Order
|
||||
> & {
|
||||
status: Status;
|
||||
customer: number;
|
||||
product: number;
|
||||
dp: number;
|
||||
};
|
|
@ -0,0 +1,37 @@
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { getResourceName } from '../utils';
|
||||
import { OrdersQuery } from './types';
|
||||
|
||||
const PRODUCT_PREFIX = 'order';
|
||||
|
||||
/**
|
||||
* Generate a resource name for orders.
|
||||
*
|
||||
* @param {Object} query Query for orders.
|
||||
* @return {string} Resource name for orders.
|
||||
*/
|
||||
export function getOrderResourceName( query: Partial< OrdersQuery > ) {
|
||||
return getResourceName( PRODUCT_PREFIX, query );
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a resource name for order totals count.
|
||||
*
|
||||
* It omits query parameters from the identifier that don't affect
|
||||
* totals values like pagination and response field filtering.
|
||||
*
|
||||
* @param {Object} query Query for order totals count.
|
||||
* @return {string} Resource name for order totals.
|
||||
*/
|
||||
export function getTotalOrderCountResourceName(
|
||||
query: Partial< OrdersQuery >
|
||||
) {
|
||||
// Disable eslint rule because we're using this spread to omit properties
|
||||
// that don't affect item totals count results.
|
||||
// eslint-disable-next-line no-unused-vars, camelcase
|
||||
const { _fields, page, per_page, ...totalsQuery } = query;
|
||||
|
||||
return getOrderResourceName( totalsQuery );
|
||||
}
|
Loading…
Reference in New Issue