diff --git a/changelog/mobile-tpv-tracking-channel b/changelog/mobile-tpv-tracking-channel new file mode 100644 index 00000000000..a7b990214df --- /dev/null +++ b/changelog/mobile-tpv-tracking-channel @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Add support for showing `In-Person (POS)` as the transaction channel for mobile POS transactions in wp-admin Payments, enhancing visibility in both transaction lists and details. diff --git a/client/data/transactions/hooks.ts b/client/data/transactions/hooks.ts index d0a983e75f9..e65ba57a03f 100644 --- a/client/data/transactions/hooks.ts +++ b/client/data/transactions/hooks.ts @@ -32,7 +32,7 @@ export interface Transaction { transaction_id: string; date: string; type: 'charge' | 'refund' | 'financing_payout' | 'financing_paydown'; - channel: 'in_person' | 'online'; + channel: 'in_person' | 'in_person_pos' | 'online'; // A field to identify the payment's source. // Usually last 4 digits for card payments, bank name for bank transfers... source_identifier: string; diff --git a/client/payment-details/summary/index.tsx b/client/payment-details/summary/index.tsx index 06699f07147..1e5b6bfcac9 100644 --- a/client/payment-details/summary/index.tsx +++ b/client/payment-details/summary/index.tsx @@ -123,7 +123,8 @@ const composePaymentSummaryItems = ( { { isTapToPay( metadata?.reader_model ) ? getTapToPayChannel( metadata?.platform ) : getChargeChannel( - charge.payment_method_details?.type + charge.payment_method_details?.type, + metadata ) } ), diff --git a/client/transactions/list/index.tsx b/client/transactions/list/index.tsx index b2c1cb4c9a7..a8c437d6d2c 100644 --- a/client/transactions/list/index.tsx +++ b/client/transactions/list/index.tsx @@ -55,7 +55,7 @@ import { formatExplicitCurrency, formatExportAmount, } from 'multi-currency/interface/functions'; -import { getChargeChannel } from 'utils/charge'; +import { getTransactionChannel } from 'utils/charge'; import Deposit from './deposit'; import ConvertedAmount from './converted-amount'; import autocompleter from 'transactions/autocompleter'; @@ -473,10 +473,10 @@ export const TransactionsList = ( ), }, channel: { - value: getChargeChannel( txn.channel ), + value: getTransactionChannel( txn.channel ), display: clickable( - { getChargeChannel( txn.channel ) } + { getTransactionChannel( txn.channel ) } { txn.source_device && getSourceDeviceIcon( txn ) } ), diff --git a/client/utils/charge/index.ts b/client/utils/charge/index.ts index 2690279e2cb..0169a45b4e0 100755 --- a/client/utils/charge/index.ts +++ b/client/utils/charge/index.ts @@ -173,24 +173,48 @@ export const getChargeAmounts = ( charge: Charge ): ChargeAmounts => { }; /** - * Displays the transaction's channel: Online | In-Person. + * Displays the transaction's channel: Online | In-Person | In-Person (POS). + * This method is called on the list of transactions page. * - * This method is called in two places: The individual transaction page, and the list of transactions page. - * In the individual transaction page, we are getting the data from Stripe, so we pass the transaction.type - * which can be card_present or interac_present for In-Person payments. * In the list of transactions, the type holds the brand of the payment method, so we aren't passing it. - * Instead, we pass the transaction.channel directly, which might be in_person|online. + * Instead, we pass the transaction.channel directly, which might be in_person|in_person_pos|online. * - * @param {string} type The transaction type. - * @return {string} Online or In-Person. + * @param {string} channel The transaction channel. + * @return {string} Online, In-Person, or In-Person (POS). + */ +export const getTransactionChannel = ( channel: string ): string => { + switch ( channel ) { + case 'in_person': + return __( 'In-Person', 'woocommerce-payments' ); + case 'in_person_pos': + return __( 'In-Person (POS)', 'woocommerce-payments' ); + default: + return __( 'Online', 'woocommerce-payments' ); + } +}; + +/** + * Displays the channel based on the charge data from Stripe and metadata for a transaction: Online | In-Person | In-Person (POS). + * This method is called in the individual transaction page. + * + * In the individual transaction page, we are getting the data from Stripe, so we pass the charge.type + * which can be card_present or interac_present for In-Person payments. In addition, we pass the transaction metadata + * whose ipp_channel value can be mobile_store_management or mobile_pos that indicates whether the channel is from store + * management or POS in the mobile apps. + * + * @param {string} type The transaction charge type, which can be card_present or interac_present for In-Person payments. + * @param {Record} metadata The transaction metadata, which may include ipp_channel indicating the channel source. + * @return {string} Returns 'Online', 'In-Person', or 'In-Person (POS)' based on the transaction type and metadata. * */ -export const getChargeChannel = ( type: string ): string => { - if ( - type === 'card_present' || - type === 'interac_present' || - type === 'in_person' - ) { +export const getChargeChannel = ( + type: string, + metadata: Record< string, any > +): string => { + if ( type === 'card_present' || type === 'interac_present' ) { + if ( metadata?.ipp_channel === 'mobile_pos' ) { + return __( 'In-Person (POS)', 'woocommerce-payments' ); + } return __( 'In-Person', 'woocommerce-payments' ); } diff --git a/client/utils/charge/test/index.js b/client/utils/charge/test/index.js index 99bc3172f07..07b7f64f312 100755 --- a/client/utils/charge/test/index.js +++ b/client/utils/charge/test/index.js @@ -482,3 +482,59 @@ describe( 'Charge utilities / getChargeAmounts', () => { } ); } ); } ); + +jest.mock( '@wordpress/i18n', () => ( { + __: jest.fn( ( text ) => text ), +} ) ); + +describe( 'Charge utilities / get channel string', () => { + describe( 'getTransactionChannel', () => { + test( 'should return "In-Person (POS)" for in_person_pos channel', () => { + const result = utils.getTransactionChannel( 'in_person_pos' ); + expect( result ).toBe( 'In-Person (POS)' ); + } ); + + test( 'should return "In-Person" for in_person channel', () => { + const result = utils.getTransactionChannel( 'in_person' ); + expect( result ).toBe( 'In-Person' ); + } ); + + test( 'should return "Online" for online channel', () => { + const result = utils.getTransactionChannel( 'online' ); + expect( result ).toBe( 'Online' ); + } ); + + test( 'should return "Online" for null channel', () => { + const result = utils.getTransactionChannel( null ); + expect( result ).toBe( 'Online' ); + } ); + } ); + + describe( 'getChargeChannel', () => { + test( 'should return "In-Person (POS)" for card_present type with mobile_pos metadata', () => { + const result = utils.getChargeChannel( 'card_present', { + ipp_channel: 'mobile_pos', + } ); + expect( result ).toBe( 'In-Person (POS)' ); + } ); + + test( 'should return "In-Person" for card_present type with mobile_store_management metadata', () => { + const result = utils.getChargeChannel( 'card_present', { + ipp_channel: 'mobile_store_management', + } ); + expect( result ).toBe( 'In-Person' ); + } ); + + test( 'should return "In-Person" for card_present type with null ipp_channel metadata', () => { + const result = utils.getChargeChannel( 'card_present', {} ); + expect( result ).toBe( 'In-Person' ); + } ); + + test( 'should return "Online" for online type', () => { + const result = utils.getChargeChannel( 'online', { + ipp_channel: 'mobile_pos', + } ); + expect( result ).toBe( 'Online' ); + } ); + } ); +} );