costLines([ $this->row('platform', '955859844397', '3767501840732782900', 20, 2800), $this->row('internal', 'DSG005016N11V01', '3767501840732782900', 20, 2800), ]); $this->assertSame(1, $placeholders); $this->assertSame(0, $bundles); $this->assertSame('DSG005016N11V01', $lines[0]['sku']); $this->assertSame(20.0, $lines[0]['qty']); } public function test_bundle_parent_is_removed_but_component_remains(): void { [$lines, $placeholders, $bundles] = $this->costLines([ $this->row('bundle-parent', '运营组合套装12994', '3832980288011632985', 2, 696), $this->row( 'bundle-child', 'SJ038017N1410V02', '3832980288011632985', 2, 625.26, '运营组合套装12994', ), ]); $this->assertSame(0, $placeholders); $this->assertSame(1, $bundles); $this->assertSame('SJ038017N1410V02', $lines[0]['sku']); $this->assertSame(2.0, $lines[0]['qty']); } public function test_plus_parent_is_removed_when_component_amounts_match(): void { [$lines, $placeholders, $bundles, $plusParents] = $this->costLines([ $this->row('plus-parent', 'GBGJ-A+V01+GBGJ-B+V01', '993830851777', 1, 648.06), $this->row('plus-child-a', 'GBGJ-AV01', '993830851777', 1, 450.03), $this->row('plus-child-b', 'GBGJ-BV01', '993830851777', 1, 198.03), ]); $this->assertSame(0, $placeholders); $this->assertSame(0, $bundles); $this->assertSame(1, $plusParents); $this->assertSame(['GBGJ-AV01', 'GBGJ-BV01'], array_column($lines, 'sku')); } public function test_plus_parent_remains_when_component_amounts_do_not_match(): void { [$lines, $placeholders, $bundles, $plusParents] = $this->costLines([ $this->row('plus-parent', 'GBGJ-A+V01+GBGJ-B+V01', '993830851777', 1, 1142), $this->row('plus-child-a', 'GBGJ-AV01', '993830851777', 1, 255.20), $this->row('plus-child-b', 'GBGJ-BV01', '993830851777', 1, 328.56), ]); $this->assertSame(0, $placeholders); $this->assertSame(0, $bundles); $this->assertSame(0, $plusParents); $this->assertContains('GBGJ-A+V01+GBGJ-B+V01', array_column($lines, 'sku')); } public function test_non_numeric_variant_parent_is_removed_when_quantity_and_amount_match(): void { [$lines, $placeholders, $bundles, $plusParents, $variantParents] = $this->costLines([ $this->row('variant-parent', 'TESTP001', '3767501840732782900', 1, 91), $this->row('variant-child', 'TESTP001N1150V01', '3767501840732782900', 1, 91), ]); $this->assertSame(0, $placeholders); $this->assertSame(0, $bundles); $this->assertSame(0, $plusParents); $this->assertSame(1, $variantParents); $this->assertSame(['TESTP001N1150V01'], array_column($lines, 'sku')); } public function test_non_numeric_variant_parent_remains_when_amounts_do_not_match(): void { [$lines, , , , $variantParents] = $this->costLines([ $this->row('variant-parent', 'TESTP001', '3767501840732782900', 1, 91), $this->row('variant-child', 'TESTP001N1150V01', '3767501840732782900', 1, 90), ]); $this->assertSame(0, $variantParents); $this->assertContains('TESTP001', array_column($lines, 'sku')); $this->assertContains('TESTP001N1150V01', array_column($lines, 'sku')); } public function test_non_numeric_variant_parent_does_not_cross_platform_item_groups(): void { [$lines, , , , $variantParents] = $this->costLines([ $this->row('variant-parent', 'TESTP001', '3767501840732782900', 1, 91), $this->row('variant-child', 'TESTP001N1150V01', '3767501840732782901', 1, 91), ]); $this->assertSame(0, $variantParents); $this->assertContains('TESTP001', array_column($lines, 'sku')); $this->assertContains('TESTP001N1150V01', array_column($lines, 'sku')); } public function test_unique_platform_sku_is_replaced_after_line_filtering(): void { [$lines] = $this->costLines([ $this->row( 'platform', '958530694526', '3768947181063635112', 1, 93.25, ), ]); $mapper = new StoreReceiptPlatformSkuMapper([ [ 'shop_sku_id' => '3533103677164802', 'shop_i_id' => '3768947181063635112', 'sku_id' => 'SG006367N1028V01', 'i_id' => 'SG0063', 'properties_value' => '经典北美黑胡桃色', ], ]); $command = new SyncStoreReceiptProductCost(); $method = new ReflectionMethod($command, 'mapPlatformSkus'); $method->setAccessible(true); $result = $method->invoke($command, $lines, $mapper); $this->assertSame(1, $result['auto_replaced_rows']); $this->assertSame('SG006367N1028V01', $result['lines'][0]['sku']); $this->assertSame('958530694526', $result['lines'][0]['original_platform_sku']); $this->assertSame('unique', $result['lines'][0]['platform_mapping_status']); } public function test_yunfei_and_bcj_are_excluded_globally_and_from_bundle_components(): void { $command = new SyncStoreReceiptProductCost(); $constant = (new \ReflectionClass($command))->getReflectionConstant('EXCLUDED_SKUS'); $excludedSkus = $constant->getValue(); $this->assertContains('YUNFEI', $excludedSkus); $this->assertContains('BCJ', $excludedSkus); $method = new ReflectionMethod($command, 'expandComboSku'); $method->setAccessible(true); $components = $method->invoke( null, 'MKH-YUNFEI-1-BCJ-1-SG006367N1028V01-2', 3.0, ); $this->assertSame([['SG006367N1028V01', 6.0, true]], $components); } public function test_priced_line_always_calculates_cost_without_unit_mismatch_override(): void { $command = new SyncStoreReceiptProductCost(); $method = new ReflectionMethod($command, 'calculateLineCost'); $method->setAccessible(true); $result = $method->invoke($command, 9524.0, 58.0); $this->assertSame(9524.0, $result['quantity']); $this->assertSame(58.0, $result['unit_price']); $this->assertSame(552392.0, $result['cost']); } /** @return array{0:list>,1:int,2:int,3:int,4:int} */ private function costLines(array $rows): array { $command = new SyncStoreReceiptProductCost(); $method = new ReflectionMethod($command, 'costLines'); $method->setAccessible(true); $placeholders = 0; $bundles = 0; $plusParents = 0; $variantParents = 0; $lines = $method->invokeArgs($command, [$rows, &$placeholders, &$bundles, &$plusParents, &$variantParents]); return [$lines, $placeholders, $bundles, $plusParents, $variantParents]; } /** @return array */ private function row( string $rowKey, string $sku, string $shopItemId, float $quantity, float $amount, string $bundleParent = '', ): array { return [ 'source' => 'jst', 'sku' => $sku, 'qty' => $quantity, 'amount' => $amount, 'warehouse' => '简尚-新苏北仓', 'o_id' => '8986587', 'parent_o_id' => '', 'row_key' => $rowKey, 'status' => 'Sent', 'shop_i_id' => $shopItemId, 'shop_sku_id' => '3533103677164802', 'outer_oi_id' => 'OUTER-TEST-1', 'properties_value' => '颜色分类:六格-90*30*75cm-木纹胡桃色?赠送后背板', 'src_combine_sku_id' => $bundleParent, ]; } }