@@ -384,6 +384,21 @@ protected static function create_refund( $order, $force_partial = false ) {
384384 $ force_partial = true ;
385385 }
386386
387+ // Calculate already refunded quantities per item
388+ $ refunded_qty_by_item = array ();
389+ foreach ( $ existing_refunds as $ existing_refund ) {
390+ foreach ( $ existing_refund ->get_items ( array ( 'line_item ' , 'fee ' ) ) as $ refund_item ) {
391+ $ item_id = $ refund_item ->get_meta ( '_refunded_item_id ' );
392+ if ( ! $ item_id ) {
393+ continue ;
394+ }
395+ if ( ! isset ( $ refunded_qty_by_item [ $ item_id ] ) ) {
396+ $ refunded_qty_by_item [ $ item_id ] = 0 ;
397+ }
398+ $ refunded_qty_by_item [ $ item_id ] += abs ( $ refund_item ->get_quantity () );
399+ }
400+ }
401+
387402 // Refunds will be split evenly between partial and full (unless forced)
388403 $ is_full_refund = $ force_partial ? false : (bool ) wp_rand ( 0 , 1 );
389404
@@ -392,18 +407,34 @@ protected static function create_refund( $order, $force_partial = false ) {
392407 if ( $ is_full_refund ) {
393408 // Full refund - include all line items and fees
394409 foreach ( $ order ->get_items ( array ( 'line_item ' , 'fee ' ) ) as $ item_id => $ item ) {
410+ // Calculate remaining quantity after previous refunds
411+ $ original_qty = $ item ->get_quantity ();
412+ $ refunded_qty = isset ( $ refunded_qty_by_item [ $ item_id ] ) ? $ refunded_qty_by_item [ $ item_id ] : 0 ;
413+ $ remaining_qty = $ original_qty - $ refunded_qty ;
414+
415+ // Skip if nothing left to refund
416+ if ( $ remaining_qty <= 0 ) {
417+ continue ;
418+ }
419+
395420 $ taxes = $ item ->get_taxes ();
396421 $ refund_tax = array ();
397422
398423 if ( ! empty ( $ taxes ['total ' ] ) ) {
399424 foreach ( $ taxes ['total ' ] as $ tax_id => $ tax_amount ) {
400- $ refund_tax [ $ tax_id ] = $ tax_amount * -1 ;
425+ // Prorate tax based on remaining quantity
426+ $ tax_per_unit = $ tax_amount / $ original_qty ;
427+ $ refund_tax [ $ tax_id ] = ( $ tax_per_unit * $ remaining_qty ) * -1 ;
401428 }
402429 }
403430
431+ // Prorate the refund total based on remaining quantity
432+ $ total_per_unit = $ item ->get_total () / $ original_qty ;
433+ $ refund_total = $ total_per_unit * $ remaining_qty ;
434+
404435 $ line_items [ $ item_id ] = array (
405- 'qty ' => $ item -> get_quantity () ,
406- 'refund_total ' => $ item -> get_total () * -1 ,
436+ 'qty ' => $ remaining_qty ,
437+ 'refund_total ' => $ refund_total * -1 ,
407438 'refund_tax ' => $ refund_tax ,
408439 );
409440 }
@@ -428,37 +459,62 @@ protected static function create_refund( $order, $force_partial = false ) {
428459 foreach ( $ items_to_refund as $ index ) {
429460 $ item = $ items_array [ $ index ];
430461 $ item_id = $ item ->get_id ();
462+
463+ // Calculate remaining quantity after previous refunds
464+ $ original_qty = $ item ->get_quantity ();
465+ $ refunded_qty = isset ( $ refunded_qty_by_item [ $ item_id ] ) ? $ refunded_qty_by_item [ $ item_id ] : 0 ;
466+ $ remaining_qty = $ original_qty - $ refunded_qty ;
467+
468+ // Skip if nothing left to refund
469+ if ( $ remaining_qty <= 0 ) {
470+ continue ;
471+ }
472+
431473 $ taxes = $ item ->get_taxes ();
432474 $ refund_tax = array ();
433475
434476 if ( ! empty ( $ taxes ['total ' ] ) ) {
435477 foreach ( $ taxes ['total ' ] as $ tax_id => $ tax_amount ) {
436- $ refund_tax [ $ tax_id ] = $ tax_amount * -1 ;
478+ // Prorate tax based on remaining quantity
479+ $ tax_per_unit = $ tax_amount / $ original_qty ;
480+ $ refund_tax [ $ tax_id ] = ( $ tax_per_unit * $ remaining_qty ) * -1 ;
437481 }
438482 }
439483
484+ // Prorate the refund total based on remaining quantity
485+ $ total_per_unit = $ item ->get_total () / $ original_qty ;
486+ $ refund_total = $ total_per_unit * $ remaining_qty ;
487+
440488 $ line_items [ $ item_id ] = array (
441- 'qty ' => $ item -> get_quantity () ,
442- 'refund_total ' => $ item -> get_total () * -1 ,
489+ 'qty ' => $ remaining_qty ,
490+ 'refund_total ' => $ refund_total * -1 ,
443491 'refund_tax ' => $ refund_tax ,
444492 );
445493 }
446494 } else {
447495 // Refund partial quantities of items
448496 foreach ( $ items as $ item_id => $ item ) {
449- $ quantity = $ item ->get_quantity ();
497+ // Calculate remaining quantity after previous refunds
498+ $ original_qty = $ item ->get_quantity ();
499+ $ refunded_qty = isset ( $ refunded_qty_by_item [ $ item_id ] ) ? $ refunded_qty_by_item [ $ item_id ] : 0 ;
500+ $ remaining_qty = $ original_qty - $ refunded_qty ;
501+
502+ // Skip if nothing left to refund or if only 1 remaining
503+ if ( $ remaining_qty <= 1 ) {
504+ continue ;
505+ }
450506
451- // Only refund line items with quantity > 1
452- if ( 'line_item ' === $ item ->get_type () && $ quantity > 1 ) {
453- // Refund between 1 and quantity -1 items
454- $ refund_qty = wp_rand ( 1 , $ quantity - 1 );
455- $ refund_amount = ( $ item ->get_total () / $ quantity ) * $ refund_qty ;
507+ // Only refund line items with remaining quantity > 1
508+ if ( 'line_item ' === $ item ->get_type () ) {
509+ // Refund between 1 and remaining_qty -1 items
510+ $ refund_qty = wp_rand ( 1 , $ remaining_qty - 1 );
511+ $ refund_amount = ( $ item ->get_total () / $ original_qty ) * $ refund_qty ;
456512 $ taxes = $ item ->get_taxes ();
457513 $ refund_tax = array ();
458514
459515 if ( ! empty ( $ taxes ['total ' ] ) ) {
460516 foreach ( $ taxes ['total ' ] as $ tax_id => $ tax_amount ) {
461- $ refund_tax [ $ tax_id ] = ( $ tax_amount / $ quantity ) * $ refund_qty * -1 ;
517+ $ refund_tax [ $ tax_id ] = ( $ tax_amount / $ original_qty ) * $ refund_qty * -1 ;
462518 }
463519 }
464520
@@ -471,25 +527,47 @@ protected static function create_refund( $order, $force_partial = false ) {
471527 }
472528 }
473529
474- // If no items were added (all quantities were 1) , refund one complete item
530+ // If no items were added, refund one complete remaining item
475531 if ( empty ( $ line_items ) && count ( $ items ) > 0 ) {
532+ // Find an item with remaining quantity
476533 $ items_array = array_values ( $ items );
477- $ item = $ items_array [ array_rand ( $ items_array ) ];
478- $ item_id = $ item ->get_id ();
479- $ taxes = $ item ->get_taxes ();
480- $ refund_tax = array ();
534+ shuffle ( $ items_array );
481535
482- if ( ! empty ( $ taxes ['total ' ] ) ) {
483- foreach ( $ taxes ['total ' ] as $ tax_id => $ tax_amount ) {
484- $ refund_tax [ $ tax_id ] = $ tax_amount * -1 ;
536+ foreach ( $ items_array as $ item ) {
537+ $ item_id = $ item ->get_id ();
538+
539+ // Calculate remaining quantity after previous refunds
540+ $ original_qty = $ item ->get_quantity ();
541+ $ refunded_qty = isset ( $ refunded_qty_by_item [ $ item_id ] ) ? $ refunded_qty_by_item [ $ item_id ] : 0 ;
542+ $ remaining_qty = $ original_qty - $ refunded_qty ;
543+
544+ // Skip if nothing left to refund
545+ if ( $ remaining_qty <= 0 ) {
546+ continue ;
485547 }
486- }
487548
488- $ line_items [ $ item_id ] = array (
489- 'qty ' => $ item ->get_quantity (),
490- 'refund_total ' => $ item ->get_total () * -1 ,
491- 'refund_tax ' => $ refund_tax ,
492- );
549+ $ taxes = $ item ->get_taxes ();
550+ $ refund_tax = array ();
551+
552+ if ( ! empty ( $ taxes ['total ' ] ) ) {
553+ foreach ( $ taxes ['total ' ] as $ tax_id => $ tax_amount ) {
554+ // Prorate tax based on remaining quantity
555+ $ tax_per_unit = $ tax_amount / $ original_qty ;
556+ $ refund_tax [ $ tax_id ] = ( $ tax_per_unit * $ remaining_qty ) * -1 ;
557+ }
558+ }
559+
560+ // Prorate the refund total based on remaining quantity
561+ $ total_per_unit = $ item ->get_total () / $ original_qty ;
562+ $ refund_total = $ total_per_unit * $ remaining_qty ;
563+
564+ $ line_items [ $ item_id ] = array (
565+ 'qty ' => $ remaining_qty ,
566+ 'refund_total ' => $ refund_total * -1 ,
567+ 'refund_tax ' => $ refund_tax ,
568+ );
569+ break ; // Only refund one item
570+ }
493571 }
494572 }
495573 }
0 commit comments