@@ -83,119 +83,17 @@ public function testChildOfBundleShippedTogetherIsSkippedAndParentDeterminesFulf
8383
8484 $ order ->method ('getState ' )->willReturn (Order::STATE_PROCESSING );
8585 $ order ->method ('getIsInProcess ' )->willReturn (false );
86-
87- // Avoid early return
8886 $ order ->method ('isCanceled ' )->willReturn (false );
8987 $ order ->method ('canUnhold ' )->willReturn (false );
9088 $ order ->method ('canInvoice ' )->willReturn (false );
9189 $ order ->method ('getInvoiceCollection ' )->willReturn ($ this ->createInvoiceCollection ([]));
9290 $ order ->method ('getTotalDue ' )->willReturn (0 );
93-
94- // Closed state preconditions (first branch)
9591 $ order ->method ('canCreditmemo ' )->willReturn (false );
9692 $ order ->method ('getIsNotVirtual ' )->willReturn (true );
9793 $ order ->method ('canShip ' )->willReturn (true );
9894
99- // Build a parent bundle item with shipment together and fully fulfilled
100- $ bundleProduct = new class {
101- public function getShipmentType ()
102- {
103- return AbstractType::SHIPMENT_TOGETHER ;
104- }
105- };
106-
107- $ parentItem = new class ($ bundleProduct ) {
108- /** @var object */
109- private $ bundleProduct ;
110- public function __construct ($ bundleProduct )
111- {
112- $ this ->bundleProduct = $ bundleProduct ;
113- }
114- public function getIsVirtual ()
115- {
116- return false ;
117- }
118- public function getLockedDoShip ()
119- {
120- return false ;
121- }
122- public function getParentItem ()
123- {
124- return null ;
125- }
126- public function getProductType ()
127- {
128- return Type::TYPE_BUNDLE ;
129- }
130- public function getProduct ()
131- {
132- return $ this ->bundleProduct ;
133- }
134- public function getQtyOrdered ()
135- {
136- return 1 ;
137- }
138- public function getQtyCanceled ()
139- {
140- return 0 ;
141- }
142- public function getQtyShipped ()
143- {
144- return 1 ;
145- }
146- public function getQtyRefunded ()
147- {
148- return 0 ;
149- }
150- };
151-
152- // Child item should be skipped due to parent bundle shipped together
153- $ childItem = new class ($ parentItem ) {
154- /** @var object */
155- private $ parentItem ;
156- public function __construct ($ parentItem )
157- {
158- $ this ->parentItem = $ parentItem ;
159- }
160- public function getIsVirtual ()
161- {
162- return false ;
163- }
164- public function getLockedDoShip ()
165- {
166- return false ;
167- }
168- public function getParentItem ()
169- {
170- return $ this ->parentItem ;
171- }
172- public function getProductType ()
173- {
174- return 'simple ' ;
175- }
176- public function getProduct ()
177- {
178- return null ;
179- }
180- public function getQtyOrdered ()
181- {
182- return 1 ;
183- }
184- public function getQtyCanceled ()
185- {
186- return 0 ;
187- }
188- public function getQtyShipped ()
189- {
190- return 0 ;
191- } // would be open if not skipped
192- public function getQtyRefunded ()
193- {
194- return 0 ;
195- }
196- };
197-
198- // Ensure the loop hits the child first to exercise the continue branch
95+ $ parentItem = $ this ->createBundleParentShippedTogetherFulfilled ();
96+ $ childItem = $ this ->createChildItemForParent ($ parentItem , 0 );
19997 $ order ->method ('getAllItems ' )->willReturn ([$ childItem , $ parentItem ]);
20098
20199 $ order ->expects ($ this ->once ())
@@ -423,12 +321,46 @@ public function testSubjectChoosesParentWhenSecondEvaluationMatchesBundleShipped
423321 $ order ->method ('getIsNotVirtual ' )->willReturn (true );
424322 $ order ->method ('canShip ' )->willReturn (true );
425323
426- // Parent whose methods return different values across calls
427- $ parentWithSwitchingBehavior = new class {
428- /** @var int */
429- private $ typeCall = 0 ;
430- /** @var int */
431- private $ productCall = 0 ;
324+ $ parentWithSwitchingBehavior = $ this ->createSwitchingParentForBundleSelection ();
325+ $ childItem = $ this ->createChildItemForParent ($ parentWithSwitchingBehavior , 0 );
326+ $ order ->method ('getAllItems ' )->willReturn ([$ childItem ]);
327+
328+ $ order ->expects ($ this ->once ())
329+ ->method ('setState ' )
330+ ->with (Order::STATE_CLOSED )
331+ ->willReturnSelf ();
332+
333+ $ config ->expects ($ this ->once ())
334+ ->method ('getStateDefaultStatus ' )
335+ ->with (Order::STATE_CLOSED )
336+ ->willReturn ('closed ' );
337+
338+ $ order ->expects ($ this ->once ())
339+ ->method ('setStatus ' )
340+ ->with ('closed ' )
341+ ->willReturnSelf ();
342+
343+ $ order ->method ('getConfig ' )->willReturn ($ config );
344+
345+ $ this ->subject ->check ($ order );
346+ }
347+
348+ private function createBundleParentShippedTogetherFulfilled (): object
349+ {
350+ $ bundleProduct = new class {
351+ public function getShipmentType ()
352+ {
353+ return AbstractType::SHIPMENT_TOGETHER ;
354+ }
355+ };
356+
357+ return new class ($ bundleProduct ) {
358+ /** @var object */
359+ private $ bundleProduct ;
360+ public function __construct ($ bundleProduct )
361+ {
362+ $ this ->bundleProduct = $ bundleProduct ;
363+ }
432364 public function getIsVirtual ()
433365 {
434366 return false ;
@@ -443,27 +375,11 @@ public function getParentItem()
443375 }
444376 public function getProductType ()
445377 {
446- $ this ->typeCall ++;
447- return $ this ->typeCall === 1 ? 'simple ' : Type::TYPE_BUNDLE ;
378+ return Type::TYPE_BUNDLE ;
448379 }
449380 public function getProduct ()
450381 {
451- $ this ->productCall ++;
452- $ callIndex = $ this ->productCall ;
453- return new class ($ callIndex ) {
454- /** @var int */
455- private $ callIndex ;
456- public function __construct ($ callIndex )
457- {
458- $ this ->callIndex = $ callIndex ;
459- }
460- public function getShipmentType ()
461- {
462- return $ this ->callIndex === 1
463- ? AbstractType::SHIPMENT_SEPARATELY
464- : AbstractType::SHIPMENT_TOGETHER ;
465- }
466- };
382+ return $ this ->bundleProduct ;
467383 }
468384 public function getQtyOrdered ()
469385 {
@@ -482,15 +398,15 @@ public function getQtyRefunded()
482398 return 0 ;
483399 }
484400 };
401+ }
485402
486- // Child referencing the parent; child has open qty but should use parent as subject
487- $ childItem = new class ($ parentWithSwitchingBehavior ) {
403+ private function createChildItemForParent (object $ parent , int $ shipped ): object
404+ {
405+ $ child = new class {
488406 /** @var object */
489- private $ parent ;
490- public function __construct ($ parent )
491- {
492- $ this ->parent = $ parent ;
493- }
407+ public $ parent ;
408+ /** @var int */
409+ public $ shipped = 0 ;
494410 public function getIsVirtual ()
495411 {
496412 return false ;
@@ -521,34 +437,78 @@ public function getQtyCanceled()
521437 }
522438 public function getQtyShipped ()
523439 {
524- return 0 ;
440+ return $ this -> shipped ;
525441 }
526442 public function getQtyRefunded ()
527443 {
528444 return 0 ;
529445 }
530446 };
447+ $ child ->parent = $ parent ;
448+ $ child ->shipped = $ shipped ;
449+ return $ child ;
450+ }
531451
532- $ order ->method ('getAllItems ' )->willReturn ([$ childItem ]);
533-
534- $ order ->expects ($ this ->once ())
535- ->method ('setState ' )
536- ->with (Order::STATE_CLOSED )
537- ->willReturnSelf ();
538-
539- $ config ->expects ($ this ->once ())
540- ->method ('getStateDefaultStatus ' )
541- ->with (Order::STATE_CLOSED )
542- ->willReturn ('closed ' );
543-
544- $ order ->expects ($ this ->once ())
545- ->method ('setStatus ' )
546- ->with ('closed ' )
547- ->willReturnSelf ();
548-
549- $ order ->method ('getConfig ' )->willReturn ($ config );
550-
551- $ this ->subject ->check ($ order );
452+ private function createSwitchingParentForBundleSelection (): object
453+ {
454+ return new class {
455+ /** @var int */
456+ private $ typeCall = 0 ;
457+ /** @var int */
458+ private $ productCall = 0 ;
459+ public function getIsVirtual ()
460+ {
461+ return false ;
462+ }
463+ public function getLockedDoShip ()
464+ {
465+ return false ;
466+ }
467+ public function getParentItem ()
468+ {
469+ return null ;
470+ }
471+ public function getProductType ()
472+ {
473+ $ this ->typeCall ++;
474+ return $ this ->typeCall === 1 ? 'simple ' : Type::TYPE_BUNDLE ;
475+ }
476+ public function getProduct ()
477+ {
478+ $ this ->productCall ++;
479+ $ callIndex = $ this ->productCall ;
480+ return new class ($ callIndex ) {
481+ /** @var int */
482+ private $ callIndex ;
483+ public function __construct ($ callIndex )
484+ {
485+ $ this ->callIndex = $ callIndex ;
486+ }
487+ public function getShipmentType ()
488+ {
489+ return $ this ->callIndex === 1
490+ ? AbstractType::SHIPMENT_SEPARATELY
491+ : AbstractType::SHIPMENT_TOGETHER ;
492+ }
493+ };
494+ }
495+ public function getQtyOrdered ()
496+ {
497+ return 1 ;
498+ }
499+ public function getQtyCanceled ()
500+ {
501+ return 0 ;
502+ }
503+ public function getQtyShipped ()
504+ {
505+ return 1 ;
506+ }
507+ public function getQtyRefunded ()
508+ {
509+ return 0 ;
510+ }
511+ };
552512 }
553513
554514 public function testEarlyReturnWhenOpenInvoiceAndTotalDue (): void
0 commit comments