1. 【新增】售后单售后原因类型支持搜索

2. 【新增】手工创建订单折扣可输入正数

3. 【优化】盘点申请单确认

4. 【修复】采购退货单模拟出库失败问题

5. 【新增】订单金额客户实付与结算金额

6. 【优化】仓库发货统计报表物料名称显示

7. 【优化】自有仓储虚拟发货逻辑

8. 【修复】基础物料分类管理问题
This commit is contained in:
chenping
2026-04-01 11:59:17 +08:00
parent 9341122827
commit 61783b7d01
754 changed files with 46179 additions and 5700 deletions

View File

@@ -0,0 +1,216 @@
<?php
declare(strict_types=1);
use PHPUnit\Framework\TestCase;
class aftersalesv2Test extends TestCase
{
// 在每个测试方法之前执行
protected function setUp(): void
{
parent::setUp();
// 在 setUp 方法中引入依赖文件
include_once 'config/config.php';
include_once 'app/base/defined.php';
include_once 'app/base/kernel.php';
}
public function testAdd(): void
{
set_time_limit(0);
ignore_user_abort(true);
@ini_set('memory_limit', '2048M');
$str = '{
"refund_id": "R4326826108801688",
"status": "SUCCESS",
"trade_status": "4",
"has_good_return": "true",
"company_name": "顺丰速运",
"modified": "2024-04-05 22:09:56",
"extend_field": "{\"_matrix_msg_id\":\"6613C6600AB201316N2XZ2SNZ8H6Y72S\",\"status\":\"4\",\"refundStatus\":\"2\",\"reasonId\":\"700000\"}",
"buyer_nick": "5cfbe211000000000602e878",
"oid": "",
"refund_type": "return",
"spider_type": "小红书_refund",
"sid": "SF3100695246153",
"refund_version": "",
"reason": "尺码/尺寸不合适",
"attribute": "",
"cost_shipping": "0",
"seller_nick": "",
"tid": "R2584935102342489",
"desc": "",
"refund_fee": "49.9",
"refund_item_list": "{\"return_item\":[{\"item_id\":\"65fa79253403350001f45621\",\"num\":1,\"price\":\"49.9\",\"oid\":\"65fa79253403350001f45621\",\"outer_id\":\"\",\"modified\":\"2024-04-05 22:09:56\"}]}",
"operation_constraint": "",
"created": "2024-04-03 09:46:42",
"payment_id": "",
"outer_id": "",
"refund_phase": "",
"jsrefund_flag": "true"
}';
$sdf = json_decode($str,true);
$sdf['method'] = 'ome.aftersalev2.add';
$sdf['node_id'] = '1044116135';
$erpapiMethod = erpapi_router_mapping::rspServiceMapping('', $sdf['method'], $sdf['node_id']);
if(!$erpapiMethod) {
$erpapiMethod = $sdf['method'];
}
$rs = kernel::single('erpapi_router_response')
->set_node_id($sdf['node_id'])
->set_api_name($erpapiMethod)
->dispatch($sdf);
echo '<pre>';
print_r($rs);
echo '</pre>';
$this->assertTrue(true,$rs['msg']);
}
/**
* 为京东价保退款测试造订单并置为已发货(订单号 tid平台 skuid item_id 对应子单 oid/sku_uuid
* 依赖node_id 对应 360buy 店铺,否则跳过
*/
private function create360buyPriceProtectOrderAndShip(string $nodeId, string $orderBn, string $platformSkuId): bool
{
$shop = app::get('ome')->model('shop')->dump(['node_id' => $nodeId], 'shop_id,shop_type');
if (!$shop || empty($shop['shop_id'])) {
return false;
}
$shopId = $shop['shop_id'];
$db = kernel::database();
$order = $db->selectrow(
'SELECT order_id FROM sdb_ome_orders WHERE order_bn = ' . $db->quote($orderBn) . ' AND shop_id = ' . $db->quote($shopId)
);
if ($order && !empty($order['order_id'])) {
$orderId = (int) $order['order_id'];
$db->exec('UPDATE sdb_ome_orders SET ship_status = \'1\', pay_status = \'1\', payed = 3, total_amount = 3, process_status = \'splited\' WHERE order_id = ' . $orderId);
$hasItem = $db->selectrow(
'SELECT 1 FROM sdb_ome_order_items oi INNER JOIN sdb_ome_order_objects oo ON oi.obj_id = oo.obj_id '
. 'WHERE oi.order_id = ' . $orderId . ' AND oi.shop_product_id = ' . $db->quote($platformSkuId) . ' AND oi.delete = \'false\' AND oo.delete = \'false\' LIMIT 1'
);
if (!$hasItem) {
$oid = '1012_jbp_' . $orderBn . '_' . $platformSkuId;
$skuUuid = 'jbp_uuid_' . $orderBn . '_' . $platformSkuId;
$db->exec(
"INSERT INTO sdb_ome_order_objects (order_id, obj_type, shop_goods_id, goods_id, quantity, price, amount, oid, sku_uuid, divide_order_fee, `delete`) "
. "VALUES ({$orderId}, 'goods', " . $db->quote($platformSkuId) . ", 0, 1, '3', '3', " . $db->quote($oid) . ", " . $db->quote($skuUuid) . ", '3', 'false')"
);
$objId = $db->lastinsertid();
if ($objId) {
$db->exec(
"INSERT INTO sdb_ome_order_items (order_id, obj_id, shop_goods_id, shop_product_id, product_id, bn, nums, sendnum, price, amount, `delete`) "
. "VALUES ({$orderId}, {$objId}, " . $db->quote($platformSkuId) . ", " . $db->quote($platformSkuId) . ", 0, " . $db->quote('TEST-JBP-' . $platformSkuId) . ", 1, 1, '3', '3', 'false')"
);
}
}
return true;
}
$createtime = time();
$db->exec(
"INSERT INTO sdb_ome_orders (order_bn, shop_id, shop_type, createtime, pay_status, ship_status, total_amount, payed, process_status, status, confirm, is_delivery) "
. "VALUES (" . $db->quote($orderBn) . ", " . $db->quote($shopId) . ", " . $db->quote($shop['shop_type'] ?? '360buy') . ", {$createtime}, '1', '1', '3', '3', 'splited', 'active', 'Y', 'Y')"
);
$orderId = $db->lastinsertid();
if (!$orderId) {
return false;
}
$oid = '1012_jbp_' . $orderBn . '_' . $platformSkuId;
$skuUuid = 'jbp_uuid_' . $orderBn . '_' . $platformSkuId;
$db->exec(
"INSERT INTO sdb_ome_order_objects (order_id, obj_type, shop_goods_id, goods_id, quantity, price, amount, oid, sku_uuid, divide_order_fee, `delete`) "
. "VALUES ({$orderId}, 'goods', " . $db->quote($platformSkuId) . ", 0, 1, '3', '3', " . $db->quote($oid) . ", " . $db->quote($skuUuid) . ", '3', 'false')"
);
$objId = $db->lastinsertid();
if (!$objId) {
return false;
}
$db->exec(
"INSERT INTO sdb_ome_order_items (order_id, obj_id, shop_goods_id, shop_product_id, product_id, bn, nums, sendnum, price, amount, `delete`) "
. "VALUES ({$orderId}, {$objId}, " . $db->quote($platformSkuId) . ", " . $db->quote($platformSkuId) . ", 0, " . $db->quote('TEST-JBP-' . $platformSkuId) . ", 1, 1, '3', '3', 'false')"
);
return true;
}
/**
* 京东价保退款验证入参refund_item_list 无 oid/sku_uuid经 360buy 转换后能正确走价保逻辑
* 依赖node_id 对应 360buy 店铺;测试前会造订单并置为已发货,便于通过 item_id 匹配回填 oid/sku_uuid
*/
public function test360buyPriceProtectRefundAdd(): void
{
set_time_limit(0);
ignore_user_abort(true);
@ini_set('memory_limit', '2048M');
$nodeId = '1272110034';
$orderBn = '3345225012691859';
$platformSkuId = '10172864876538';
$shop = app::get('ome')->model('shop')->dump(['node_id' => $nodeId], 'shop_id');
if (!$shop || empty($shop['shop_id'])) {
$this->markTestSkipped('需要 node_id=1272110034 的 360buy 店铺方可运行本测试');
return;
}
$this->create360buyPriceProtectOrderAndShip($nodeId, $orderBn, $platformSkuId);
$params = [
'status' => 'SUCCESS',
'to_node_id' => '1592150435',
'venderUndertakeAmount' => '',
'refund_id' => '34141299377',
'from_node_id' => '1272110034',
'attribute' => '',
'oid' => '',
'app_id' => 'ecos.ome',
'source_status' => '价保成功',
'node_id' => '1272110034',
'date' => '2026-02-02 15:17:40',
'sign' => 'D6D361DE8E47EAF1E2230126473F43CF',
'refund_fee' => '3.00',
'desc' => '价保服务',
'refund_item_list' => json_encode([
'return_item' => [
[
'price' => '',
'oid' => '',
'modified' => '',
'num' => 1,
'item_id' => '10172864876538',
'outer_id' => '',
],
],
]),
'created' => '2025-12-30 19:37:38',
'modified' => '',
'popPlatformCouponRateAmount' => '3.0',
'tid' => '3345225012691859',
'has_good_return' => 'false',
'method' => 'ome.aftersalev2.add',
'refund_type' => 'refund',
'tag_type' => '价保退款',
];
$sdf = $params;
$erpapiMethod = erpapi_router_mapping::rspServiceMapping('', $sdf['method'], $sdf['node_id']);
if (!$erpapiMethod) {
$erpapiMethod = $sdf['method'];
}
$rs = kernel::single('erpapi_router_response')
->set_node_id($sdf['node_id'])
->set_api_name($erpapiMethod)
->dispatch($sdf);
$this->assertArrayHasKey('rsp', $rs, '响应应包含 rsp');
$this->assertContains($rs['rsp'], ['succ', 'fail'], 'rsp 应为 succ 或 fail');
$this->assertArrayHasKey('msg', $rs, '响应应包含 msg');
$this->assertTrue(true, $rs['msg']);
}
}

View File

@@ -0,0 +1,257 @@
<?php
declare(strict_types=1);
use PHPUnit\Framework\TestCase;
class fxordermsgTest extends TestCase
{
// 在每个测试方法之前执行
protected function setUp(): void
{
parent::setUp();
// 在 setUp 方法中引入依赖文件
include_once 'config/config.php';
include_once 'app/base/defined.php';
include_once 'app/base/kernel.php';
}
/**
* 测试fxordermsg方法 - 成功添加卖家备注
*
* @return void
* @author system
*/
public function testFxordermsgSuccess(): void
{
// 测试参数
$params = array(
'order_bn' => 'L202507252210006274',
'shop_id' => 'shop_20250704203141_6586',
'sellerComments' => '供销供应商测试备注 - ' . date('Y-m-d H:i:s')
);
// 调用fxordermsg方法
$bookingrefundProcess = kernel::single('erpapi_shop_response_process_bookingrefund');
$result = $bookingrefundProcess->fxordermsg($params);
// 验证返回结果
$this->assertIsArray($result);
$this->assertArrayHasKey('rsp', $result);
$this->assertArrayHasKey('msg', $result);
// 如果订单存在,应该成功
if ($result['rsp'] === 'succ') {
$this->assertEquals('succ', $result['rsp']);
$this->assertEquals('订单信息同步成功', $result['msg']);
} else {
// 如果订单不存在,应该返回失败
$this->assertEquals('fail', $result['rsp']);
$this->assertStringContainsString('订单不存在', $result['msg']);
}
echo "测试结果: " . json_encode($result, JSON_UNESCAPED_UNICODE) . "\n";
}
/**
* 测试fxordermsg方法 - 订单不存在
*
* @return void
* @author system
*/
public function testFxordermsgOrderNotExist(): void
{
// 测试参数 - 使用不存在的订单号
$params = array(
'order_bn' => 'NOT_EXIST_ORDER_' . time(),
'shop_id' => 'shop_20250704203141_6586',
'sellerComments' => '供销供应商测试备注'
);
// 调用fxordermsg方法
$bookingrefundProcess = kernel::single('erpapi_shop_response_process_bookingrefund');
$result = $bookingrefundProcess->fxordermsg($params);
// 验证返回结果
$this->assertIsArray($result);
$this->assertArrayHasKey('rsp', $result);
$this->assertArrayHasKey('msg', $result);
$this->assertEquals('fail', $result['rsp']);
$this->assertEquals('订单不存在', $result['msg']);
echo "测试结果: " . json_encode($result, JSON_UNESCAPED_UNICODE) . "\n";
}
/**
* 测试fxordermsg方法 - 没有sellerComments参数
*
* @return void
* @author system
*/
public function testFxordermsgNoSellerComments(): void
{
// 测试参数 - 不包含sellerComments
$params = array(
'order_bn' => 'L202507252210006274',
'shop_id' => 'shop_20250704203141_6586'
// 故意不传sellerComments
);
// 调用fxordermsg方法
$bookingrefundProcess = kernel::single('erpapi_shop_response_process_bookingrefund');
$result = $bookingrefundProcess->fxordermsg($params);
// 验证返回结果
$this->assertIsArray($result);
$this->assertArrayHasKey('rsp', $result);
$this->assertArrayHasKey('msg', $result);
// 如果没有sellerComments应该直接返回成功因为暂时只处理sellerComments
if ($result['rsp'] === 'succ') {
$this->assertEquals('succ', $result['rsp']);
$this->assertEquals('订单信息同步成功', $result['msg']);
} else {
// 如果订单不存在,应该返回失败
$this->assertEquals('fail', $result['rsp']);
$this->assertStringContainsString('订单不存在', $result['msg']);
}
echo "测试结果: " . json_encode($result, JSON_UNESCAPED_UNICODE) . "\n";
}
/**
* 测试fxordermsg方法 - 空sellerComments
*
* @return void
* @author system
*/
public function testFxordermsgEmptySellerComments(): void
{
// 测试参数 - sellerComments为空
$params = array(
'order_bn' => 'L202507252210006274',
'shop_id' => 'shop_20250704203141_6586',
'sellerComments' => ''
);
// 调用fxordermsg方法
$bookingrefundProcess = kernel::single('erpapi_shop_response_process_bookingrefund');
$result = $bookingrefundProcess->fxordermsg($params);
// 验证返回结果
$this->assertIsArray($result);
$this->assertArrayHasKey('rsp', $result);
$this->assertArrayHasKey('msg', $result);
// 如果sellerComments为空应该直接返回成功因为暂时只处理sellerComments
if ($result['rsp'] === 'succ') {
$this->assertEquals('succ', $result['rsp']);
$this->assertEquals('订单信息同步成功', $result['msg']);
} else {
// 如果订单不存在,应该返回失败
$this->assertEquals('fail', $result['rsp']);
$this->assertStringContainsString('订单不存在', $result['msg']);
}
echo "测试结果: " . json_encode($result, JSON_UNESCAPED_UNICODE) . "\n";
}
/**
* 测试fxordermsg方法 - 验证备注是否成功添加到订单
*
* @return void
* @author system
*/
public function testFxordermsgVerifyMarkAdded(): void
{
// 测试参数
$testComment = '供销供应商验证备注 - ' . date('Y-m-d H:i:s');
$params = array(
'order_bn' => 'L202507252210006274',
'shop_id' => 'shop_20250704203141_6586',
'sellerComments' => $testComment
);
// 调用fxordermsg方法
$bookingrefundProcess = kernel::single('erpapi_shop_response_process_bookingrefund');
$result = $bookingrefundProcess->fxordermsg($params);
// 如果成功,验证备注是否真的添加到了订单中
if ($result['rsp'] === 'succ') {
// 查询订单信息
$orderModel = app::get('ome')->model('orders');
$orderInfo = $orderModel->dump(array('order_bn' => $params['order_bn']), 'order_id,mark_text');
if ($orderInfo) {
$markText = unserialize($orderInfo['mark_text']);
$this->assertIsArray($markText);
// 查找最新的备注
$latestMark = end($markText);
$this->assertIsArray($latestMark);
$this->assertArrayHasKey('op_content', $latestMark);
$this->assertArrayHasKey('op_name', $latestMark);
// 验证备注内容
$this->assertEquals($testComment, $latestMark['op_content']);
$this->assertEquals('供销供应商', $latestMark['op_name']);
echo "验证成功: 备注已添加到订单中\n";
echo "最新备注: " . json_encode($latestMark, JSON_UNESCAPED_UNICODE) . "\n";
} else {
$this->fail('订单查询失败');
}
} else {
echo "订单不存在或处理失败,跳过验证\n";
}
}
/**
* 测试ome_order_marktext::add方法
*
* @return void
* @author system
*/
public function testOrderMarktextAdd(): void
{
// 测试参数
$order_bn = 'L202507252210006274';
$testContent = 'ome_order_marktext测试备注 - ' . date('Y-m-d H:i:s');
$op_name = '测试操作员';
// 先查询订单ID
$orderModel = app::get('ome')->model('orders');
$orderInfo = $orderModel->dump(array('order_bn' => $order_bn), 'order_id');
if (!$orderInfo) {
$this->markTestSkipped('测试订单不存在,跳过此测试');
return;
}
// 调用ome_order_marktext::add方法
list($success, $message) = kernel::single('ome_order_marktext')->add($orderInfo['order_id'], $testContent, $op_name);
// 验证返回结果
$this->assertIsBool($success);
$this->assertIsString($message);
if ($success) {
$this->assertTrue($success);
$this->assertEquals('订单备注添加成功', $message);
// 验证备注是否真的添加了
$orderInfo = $orderModel->dump(array('order_id' => $orderInfo['order_id']), 'mark_text');
$markText = unserialize($orderInfo['mark_text']);
$latestMark = end($markText);
$this->assertEquals($testContent, $latestMark['op_content']);
$this->assertEquals($op_name, $latestMark['op_name']);
echo "ome_order_marktext::add测试成功\n";
} else {
$this->assertFalse($success);
echo "ome_order_marktext::add测试失败: " . $message . "\n";
}
}
}
?>