Skip to content

Commit 0031f4f

Browse files
mgascamclaudefrosso
authored
Add evidence matrix for Product Not Received + Booking/Reservation disputes (#11271)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: Francesco <frosso@users.noreply.github.com>
1 parent 016ba2a commit 0031f4f

File tree

5 files changed

+180
-4
lines changed

5 files changed

+180
-4
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Significance: patch
2+
Type: update
3+
4+
Update evidence matrix for Product Not Received + Booking/Reservation disputes

client/disputes/new-evidence/__tests__/cover-letter-generator.test.ts

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -302,20 +302,72 @@ describe( 'Cover Letter Generator', () => {
302302
expect( result ).toContain( 'Cancellation logs (Attachment B)' );
303303
} );
304304

305-
it( 'should not include "Cancellation logs" for non-subscription_canceled disputes', () => {
306-
const nonSubscriptionDispute: ExtendedDispute = {
305+
it( 'should not include cancellation_rebuttal for disputes other than subscription_canceled or product_not_received', () => {
306+
const generalDispute: ExtendedDispute = {
307+
...mockDispute,
308+
reason: 'general' as DisputeReason,
309+
evidence: {
310+
receipt: 'receipt_url',
311+
cancellation_rebuttal: 'cancellation_rebuttal_url',
312+
},
313+
};
314+
const result = generateAttachments( generalDispute );
315+
expect( result ).toContain( 'Order receipt (Attachment A)' );
316+
expect( result ).not.toContain( 'Cancellation logs' );
317+
expect( result ).not.toContain( 'Cancellation confirmation' );
318+
} );
319+
320+
it( 'should include "Cancellation confirmation" for product_not_received disputes when cancellation_rebuttal is provided', () => {
321+
const productNotReceivedDispute: ExtendedDispute = {
307322
...mockDispute,
308323
reason: 'product_not_received' as DisputeReason,
309324
evidence: {
310325
receipt: 'receipt_url',
311326
cancellation_rebuttal: 'cancellation_rebuttal_url',
312327
},
313328
};
314-
const result = generateAttachments( nonSubscriptionDispute );
329+
const result = generateAttachments( productNotReceivedDispute );
315330
expect( result ).toContain( 'Order receipt (Attachment A)' );
331+
expect( result ).toContain(
332+
'Cancellation confirmation (Attachment B)'
333+
);
316334
expect( result ).not.toContain( 'Cancellation logs' );
317335
} );
318336

337+
it( 'should use "Item condition" label for service_documentation in non-product_not_received disputes', () => {
338+
const generalDispute: ExtendedDispute = {
339+
...mockDispute,
340+
reason: 'general' as DisputeReason,
341+
evidence: {
342+
receipt: 'receipt_url',
343+
service_documentation: 'service_documentation_url',
344+
},
345+
};
346+
const result = generateAttachments( generalDispute );
347+
expect( result ).toContain( 'Order receipt (Attachment A)' );
348+
expect( result ).toContain( 'Item condition (Attachment B)' );
349+
expect( result ).not.toContain(
350+
'Reservation or booking confirmation'
351+
);
352+
} );
353+
354+
it( 'should use "Reservation or booking confirmation" label for service_documentation in product_not_received disputes', () => {
355+
const productNotReceivedDispute: ExtendedDispute = {
356+
...mockDispute,
357+
reason: 'product_not_received' as DisputeReason,
358+
evidence: {
359+
receipt: 'receipt_url',
360+
service_documentation: 'service_documentation_url',
361+
},
362+
};
363+
const result = generateAttachments( productNotReceivedDispute );
364+
expect( result ).toContain( 'Order receipt (Attachment A)' );
365+
expect( result ).toContain(
366+
'Reservation or booking confirmation (Attachment B)'
367+
);
368+
expect( result ).not.toContain( 'Item condition' );
369+
} );
370+
319371
it( 'should use "Terms of service" label for cancellation_policy in subscription_canceled disputes', () => {
320372
const subscriptionCanceledDispute: ExtendedDispute = {
321373
...mockDispute,

client/disputes/new-evidence/__tests__/recommended-document-fields.test.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,59 @@ describe( 'Recommended Documents', () => {
459459
expect( result[ 4 ].key ).toBe( 'uncategorized_file' ); // Other documents
460460
expect( result[ 4 ].label ).toBe( 'Other documents' );
461461
} );
462+
463+
it( 'should return matrix fields for product_not_received + booking_reservation when feature flag is enabled', () => {
464+
global.wcpaySettings.featureFlags.isDisputeAdditionalEvidenceTypesEnabled = true;
465+
466+
const result = getRecommendedDocumentFields(
467+
'product_not_received',
468+
undefined,
469+
undefined,
470+
'booking_reservation'
471+
);
472+
473+
// Matrix entry for product_not_received + booking_reservation
474+
expect( result ).toHaveLength( 5 );
475+
expect( result[ 0 ].key ).toBe( 'receipt' );
476+
expect( result[ 0 ].label ).toBe( 'Order receipt' );
477+
expect( result[ 0 ].description ).toBe(
478+
"A copy of the customer's receipt, which can be found in the receipt history for this transaction."
479+
);
480+
expect( result[ 1 ].key ).toBe( 'customer_communication' ); // Base field
481+
expect( result[ 2 ].key ).toBe( 'service_documentation' );
482+
expect( result[ 2 ].label ).toBe(
483+
'Reservation or booking confirmation'
484+
);
485+
expect( result[ 2 ].description ).toBe(
486+
'Any documents showing the service completion, attendance or reservation confirmation.'
487+
);
488+
expect( result[ 3 ].key ).toBe( 'cancellation_rebuttal' ); // Cancellation confirmation
489+
expect( result[ 3 ].label ).toBe( 'Cancellation confirmation' );
490+
expect( result[ 3 ].description ).toBe(
491+
'Documents showing the product or service was canceled, such as cancellation logs, confirmation emails, or account records.'
492+
);
493+
expect( result[ 4 ].key ).toBe( 'uncategorized_file' ); // Other documents
494+
expect( result[ 4 ].label ).toBe( 'Other documents' );
495+
} );
496+
497+
it( 'should fall back to trunk product_not_received fields for physical_product when feature flag is enabled', () => {
498+
global.wcpaySettings.featureFlags.isDisputeAdditionalEvidenceTypesEnabled = true;
499+
500+
const result = getRecommendedDocumentFields(
501+
'product_not_received',
502+
undefined,
503+
undefined,
504+
'physical_product'
505+
);
506+
507+
// Should fall back to trunk product_not_received fields since no matrix entry for physical_product
508+
expect( result ).toHaveLength( 5 );
509+
expect( result[ 0 ].key ).toBe( 'receipt' );
510+
expect( result[ 1 ].key ).toBe( 'customer_communication' );
511+
expect( result[ 2 ].key ).toBe( 'customer_signature' );
512+
expect( result[ 3 ].key ).toBe( 'refund_policy' );
513+
expect( result[ 4 ].key ).toBe( 'uncategorized_file' );
514+
} );
462515
} );
463516

464517
describe( 'Visa Compliance (noncompliant) reason', () => {

client/disputes/new-evidence/cover-letter-generator.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,14 @@ export const generateAttachments = (
125125
{
126126
key: DOCUMENT_FIELD_KEYS.SERVICE_DOCUMENTATION,
127127
label: __( 'Item condition', 'woocommerce-payments' ),
128+
// For product_not_received disputes, this field is labeled "Reservation or booking confirmation"
129+
labelForReasons: {
130+
reasons: [ 'product_not_received' ],
131+
label: __(
132+
'Reservation or booking confirmation',
133+
'woocommerce-payments'
134+
),
135+
},
128136
},
129137
{
130138
// For non-fraudulent disputes, "Subscription logs" appears in its original position
@@ -135,7 +143,15 @@ export const generateAttachments = (
135143
{
136144
key: DOCUMENT_FIELD_KEYS.CANCELLATION_REBUTTAL,
137145
label: __( 'Cancellation logs', 'woocommerce-payments' ),
138-
onlyForReasons: [ 'subscription_canceled' ],
146+
onlyForReasons: [ 'subscription_canceled', 'product_not_received' ],
147+
// For product_not_received disputes, this field is labeled "Cancellation confirmation"
148+
labelForReasons: {
149+
reasons: [ 'product_not_received' ],
150+
label: __(
151+
'Cancellation confirmation',
152+
'woocommerce-payments'
153+
),
154+
},
139155
},
140156
{
141157
key: DOCUMENT_FIELD_KEYS.CANCELLATION_POLICY,

client/disputes/new-evidence/evidence-matrix.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,56 @@ const getSubscriptionCanceledMatrix = (): {
235235
],
236236
} );
237237

238+
/**
239+
* Get evidence matrix entries for product_not_received disputes.
240+
*/
241+
const getProductNotReceivedMatrix = (): {
242+
[ productType: string ]: Array< RecommendedDocument >;
243+
} => ( {
244+
// Booking/Reservation product type
245+
booking_reservation: [
246+
{
247+
key: DOCUMENT_FIELD_KEYS.RECEIPT,
248+
label: __( 'Order receipt', 'woocommerce-payments' ),
249+
description: __(
250+
"A copy of the customer's receipt, which can be found in the receipt history for this transaction.",
251+
'woocommerce-payments'
252+
),
253+
order: 10,
254+
},
255+
{
256+
key: DOCUMENT_FIELD_KEYS.SERVICE_DOCUMENTATION,
257+
label: __(
258+
'Reservation or booking confirmation',
259+
'woocommerce-payments'
260+
),
261+
description: __(
262+
'Any documents showing the service completion, attendance or reservation confirmation.',
263+
'woocommerce-payments'
264+
),
265+
order: 25,
266+
},
267+
{
268+
key: DOCUMENT_FIELD_KEYS.CANCELLATION_REBUTTAL,
269+
label: __( 'Cancellation confirmation', 'woocommerce-payments' ),
270+
description: __(
271+
'Documents showing the product or service was canceled, such as cancellation logs, confirmation emails, or account records.',
272+
'woocommerce-payments'
273+
),
274+
order: 30,
275+
},
276+
{
277+
key: DOCUMENT_FIELD_KEYS.UNCATEGORIZED_FILE,
278+
label: __( 'Other documents', 'woocommerce-payments' ),
279+
description: __(
280+
'Any other relevant documents that will support your case.',
281+
'woocommerce-payments'
282+
),
283+
order: 100,
284+
},
285+
],
286+
} );
287+
238288
/**
239289
* Get evidence matrix entries for fraudulent disputes.
240290
*/
@@ -277,6 +327,7 @@ const getFraudulentMatrix = (): {
277327
*/
278328
export const evidenceMatrix: EvidenceMatrix = {
279329
fraudulent: getFraudulentMatrix(),
330+
product_not_received: getProductNotReceivedMatrix(),
280331
subscription_canceled: getSubscriptionCanceledMatrix(),
281332
duplicate: getDuplicateMatrix(),
282333
};

0 commit comments

Comments
 (0)