php-wms-client/src/QimenWms.php
2020-07-21 15:39:31 +08:00

649 lines
24 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace PdWms;
use App\Service\Goods\GoodsWarehouseService;
use App\Service\Order\OrderItemLackReportService;
use App\Service\Order\OrderProcessReportService;
use App\Service\Order\OrderService;
use App\Service\Order\RefundService;
use GuzzleHttp\Client;
use PdWms\Service\Array2XML;
use PdWms\Service\XML2Array;
class QimenWms extends BaseWms implements WmsInterface
{
protected static $default_shop_name = '北京拍店';
protected static $default_seller_nick = '北京拍店';
protected static $default_sender_name = '北京拍店';
protected static $default_sender_mobile = '';
protected static $default_sender_province = '';
protected static $default_sender_city = '';
protected static $default_sender_area = '';
protected static $default_sender_address = '';
protected static $default_zip_code = '000000';
protected $config = [];
public function __construct($config = null)
{
$this->config = $config;
}
/**
* 销售发货单创建
* @param array $order
* @return array|\DOMDocument
*/
public function deliveryOrderCreate($order)
{
$method = 'taobao.qimen.deliveryorder.create';
$postData = [
'deliveryOrder' => [
'deliveryOrderCode' => $order['order_no'], //出库单号
'orderType' => 'JYCK', //出库单类型, string (50) , 必填, JYCK=一般交易出库单, HHCK=换货出库单,BFCK=补发出库单QTCK=其他出库单
'warehouseCode' => $this->config['warehouse_code'], //仓库编码
'sourcePlatformCode' => 'OTHER', //订单来源平台编码
'createTime' => date('Y-m-d H:i:s'), //发货单创建时间
'placeOrderTime' => $order['created_at'], //前台订单 (店铺订单) 创建时间 (下单时间)
'operateTime' => date('Y-m-d H:i:s'), //操作 (审核) 时间
'shopNick' => (isset($order['order_sender_info']['shop_name']) && $order['order_sender_info']['shop_name']) ? $order['order_sender_info']['shop_name'] : static::$default_shop_name,
'sellerNick' => (isset($order['order_sender_info']['shop_name']) && $order['order_sender_info']['shop_name']) ? $order['order_sender_info']['shop_name'] : static::$default_seller_nick,
'remark' => $order['remark'],
'logisticsCode' => $order['order_sender_info']['logistics_code'],
//发货人信息
'senderInfo' => [
'name' => $order['order_sender_info']['name'] ?? static::$default_sender_name,
'mobile' => $order['order_sender_info']['tel'] ?? static::$default_sender_mobile,
'province' => static::$default_sender_province,
'city' => static::$default_sender_city,
'area' => static::$default_sender_area,
'detailAddress' => static::$default_sender_address,
],
//收件人信息
'receiverInfo' => [
'name' => $this->filterEmoji($order['order_address']['receive_name']),
'zipCode' => $order['order_address']['receive_zip_code'] ?? static::$default_zip_code,
'mobile' => trim($order['order_address']['receive_mobile']),
'province' => $order['order_address']['receive_province'],
'city' => $order['order_address']['receive_city'],
'area' => $order['order_address']['receive_district'],
'detailAddress' => $order['order_address']['receive_address'],
],
],
'orderLines' => [],
];
$orderLine = [];
$ownerCode = $this->config['shipper_code']; //货主编码
foreach ($order['order_goods'] as $item) {
$itemName = $item['goods_name'] . '(' . $item['sku_name'] . ')'; //商品名称
$actualPrice = $item['sku_pay_fee']; //实际成交价
if ($item['sku_type'] == 0) {
array_push($orderLine, [
'ownerCode' => $ownerCode,
'itemCode' => $item['wms_code'], //商品编码
'itemId' => $item['wms_code'], //仓储系统商品编码
'itemName' => $itemName,
'planQty' => $item['sku_quantity'], //应发商品数量
'actualPrice' => $actualPrice,
]);
} elseif ($item['sku_type'] == 1) {
foreach ($item['sku_nexus_snapshot'] as $gv) {
array_push($orderLine, [
'ownerCode' => $ownerCode,
'itemCode' => $gv['wms_code'],
'itemId' => $gv['wms_code'],
'itemName' => $itemName,
'planQty' => $gv['sku_quantity'] * $item['sku_quantity'],
'actualPrice' => $actualPrice,
]);
}
}
}
$postData = array_merge($postData, ['orderLines' => ['orderLine' => $orderLine]]);
$result = $this->xmlPost($method, $postData);
return $result;
}
/**
* 商品库存查询(单个)
* @param $wmsCode
* @param string $inventoryType
* @return array|\DOMDocument
*/
public function inventoryQuerySingle($wmsCode, $inventoryType)
{
return $this->inventoryQuery([['wms_code' => $wmsCode]], $inventoryType);
}
/**
* 商品库存查询(批量)
* @param array $params
* @param string $inventoryType
* @return array|\DOMDocument
*/
public function inventoryQuery($params, $inventoryType)
{
$method = 'taobao.qimen.inventory.query';
$criteria = [];
foreach ($params as $item) {
array_push($criteria, [
'warehouseCode' => $this->config['warehouse_code'],
'ownerCode' => $this->config['shipper_code'],
'itemCode' => $item['wms_code'],
'itemId' => $item['wms_code'],
'inventoryType' => $inventoryType,
]);
}
$postData = [
'criteriaList' => [
'criteria' => $criteria,
],
];
$result = $this->xmlPost($method, $postData);
return $result['success']
? array_merge($result, ['response' => $result['response']['items']['item']])
: $result;
}
/**
* 商品同步(商品创建或修改)
* @param array $params
* @return array|\DOMDocument
*/
public function goodsSkuSync($params)
{
$method = 'taobao.qimen.singleitem.synchronize';
$postData = [
'actionType' => $params['action_type'],
'warehouseCode' => $this->config['warehouse_code'],
'ownerCode' => $this->config['shipper_code'],
'item' => [
[
'itemCode' => $params['wms_code'],
'itemId' => $params['wms_code'],
'itemName' => $params['name'],
'barCode' => $params['upc_code'] ?? $params['wms_code'],
//商品类型 (ZC=正常商品, FX=分销商品, ZH=组合商品, ZP=赠品, BC=包材, HC=耗材, FL=辅料, XN=虚拟品,
//FS=附属品, CC=残次品, OTHER=其它) , string (10) , 必填, (只传英文编码)
'itemType' => 'ZC',
'retailPrice' => $params['unit_price'],
'isValid' => 'Y',
]
],
];
$result = $this->xmlPost($method, $postData);
return $result;
}
/**
* 单据取消
* @param array $params
* @return array|\DOMDocument
*/
public function orderCancel($params)
{
$method = 'taobao.qimen.order.cancel';
$postData = [
'warehouseCode' => $this->config['warehouse_code'],
'orderCode' => $params['order_code'],
'orderId' => $params['order_code'],
'orderType' => $params['order_type'],
'cancelReason' => $params['cancel_reason'],
];
$result = $this->xmlPost($method, $postData);
return $result;
}
/**
* 退货入库单创建(新增销售退货计划)
* @param array $params
* @return array|\DOMDocument
*/
public function returnOrderCreate($params)
{
$method = 'taobao.qimen.returnorder.create';
$ownerCode = $this->config['shipper_code'];
$postData = [
'returnOrder' => [
'returnOrderCode' => $params['refund_no'],
'ownerCode' => $ownerCode,
'warehouseCode' => $this->config['warehouse_code'],
'orderType' => 'THRK',
'preDeliveryOrderCode' => $params['order_info']['order_no'],
'preDeliveryOrderId' => $params['order_info']['order_no'],
'logisticsCode' => strtoupper($params['express_code']),
'logisticsName' => $params['express_company'],
'expressCode' => $params['express_no'],
'returnReason' => $params['desc'],
'senderInfo' => [
'name' => $params['order_address_info']['receive_name'],
'mobile' => $params['order_address_info']['receive_mobile'],
'province' => $params['order_address_info']['receive_province'],
'city' => $params['order_address_info']['receive_city'],
'area' => $params['order_address_info']['receive_district'],
'detailAddress' => $params['order_address_info']['receive_address'],
],
],
'orderLines' => [],
];
$orderLine = [];
foreach ($params['refund_goods'] as $item) {
if ($item['sku_type'] == 0) {
array_push($orderLine, [
'ownerCode' => $ownerCode,
'itemCode' => $item['wms_code'],
'itemId' => $item['wms_code'],
'planQty' => $item['sku_quantity'],
'inventoryType' => 'ZP',
]);
} elseif ($item['sku_type'] == 1) {
foreach ($item['combined_sku'] as $v) {
array_push($orderLine, [
'ownerCode' => $ownerCode,
'itemCode' => $v['wms_code'],
'itemId' => $v['wms_code'],
'planQty' => $v['sku_quantity'],
'inventoryType' => 'ZP',
]);
}
}
}
$postData = array_merge($postData, ['orderLines' => ['orderLine' => $orderLine]]);
$result = $this->xmlPost($method, $postData);
return $result;
}
/**
* 新增入库计划
* @param array $params
* @return array|\DOMDocument
*/
public function stockInCreate($params)
{
$method = 'taobao.qimen.entryorder.create';
$ownerCode = $this->config['shipper_code'];
$postData = [
'entryOrder' => [
'entryOrderCode' => $params['number'],
'ownerCode' => $ownerCode,
'purchaseOrderCode' => $this->goodsWarehouseCgrkNumber(),
'warehouseCode' => $this->config['warehouse_code'],
'orderType' => self::RK_TYPE[$params['type']] ?? 'DBRK',
],
'orderLines' => [],
];
$orderLine = [];
foreach ($params['sku_info'] as $skuV) {
$stockZP = [
'ownerCode' => $ownerCode,
'itemCode' => $skuV['wms_code'],
'itemId' => $skuV['wms_code'],
'planQty' => $skuV['apply_stock'] * 1,
'purchasePrice' => $this->formatNumber($skuV['price']),
'inventoryType' => 'ZP',
];
array_push($orderLine, $stockZP);
if (!empty($skuV["apply_bad_stock"])) {
$stockCC = array_merge($stockZP, [
'planQty' => $skuV['apply_bad_stock'] * 1,
'inventoryType' => 'CC',
]);
array_push($orderLine, $stockCC);
}
}
$postData = array_merge($postData, ['orderLines' => ['orderLine' => $orderLine]]);
$result = $this->xmlPost($method, $postData);
return $result;
}
public function stockOutCreate($params)
{
$method = 'taobao.qimen.stockout.create';
$postData = [
'deliveryOrder' => [
'deliveryOrderCode' => $params['out_warehouse_number'], //出库单号
'orderType' => 'DBCK', //出库单类型
'warehouseCode' => $this->config['warehouse_code'], //仓库编码
'logisticsCode' => $params['order_sender_info']['logistics_code'] ?? 'SF',
'logisticsName' => $params['order_sender_info']['logistics_name'] ?? '顺丰',
'createTime' => date('Y-m-d H:i:s'), //出库单创建时间
'receiverInfo' => [
'name' => $this->filterEmoji($params['address_name']),
'zipCode' => $params['receive_zip_code'] ?? self::$default_zip_code,
'mobile' => $params['address_telephone'],
'province' => $params['address_province'],
'city' => $params['address_city'],
'area' => $params['address_area'],
'detailAddress' => $params['address_detail'],
],
],
'orderLines' => [],
];
$ownerCode = $this->config['shipper_code'];
$orderLine = [];
foreach ($params['order_goods'] as $item) {
$itemName = $item['product_name'] . "(" . $item['sku_name'] . ")"; //商品名称
$actualPrice = $item['sku_pay_fee'] ?? 0;
if (!isset($item['sku_type']) || $item['sku_type'] == 0) {
$goodsZP = [
'ownerCode' => $ownerCode, //货主编码
'itemCode' => $item['wms_code'], //商品编码
'itemId' => $item['wms_code'], //仓储系统商品编码
'itemName' => $itemName,
'planQty' => $item['number'], //应发商品数量
'actualPrice' => $actualPrice,
'inventoryType' => 'ZP', //库存类型ZP=正品, CC=残次,JS=机损, XS= 箱损
];
if ($item['number'] > 0) {
array_push($orderLine, $goodsZP);
}
if (isset($item['bad_num']) && $item['bad_num'] > 0) {
$goodsCC = array_merge($goodsZP, [
'planQty' => $item['number'],
'inventoryType' => 'CC',
]);
array_push($orderLine, $goodsCC);
}
} elseif ($item['sku_type'] == 1) {
foreach ($item['sku_nexus_snapshot'] as $gv) {
array_push($orderLine, [
'ownerCode' => $ownerCode,
'itemCode' => $gv['wms_code'],
'itemId' => $gv['wms_code'],
'itemName' => $itemName,
'planQty' => $gv['sku_quantity'] * $item['sku_quantity'],
'actualPrice' => $actualPrice,
'inventoryType' => 'ZP',
]);
}
}
}
$postData = array_merge($postData, ['orderLines' => ['orderLine' => $orderLine]]);
$result = $this->xmlPost($method, $postData);
return $result;
}
public function createSign($params, $body)
{
ksort($params);
$info = '';
foreach ($params as $key => $val) {
$info .= $key . $val;
}
$appSecret = $this->config['app_secret'];
$info = $appSecret . '' . $info . $body . '' . $appSecret;
$sign = strtoupper(md5($info));
return $sign;
}
public function xmlPost($method, $params)
{
$query = [
'method' => $method,
'timestamp' => date('Y-m-d H:i:s'),
'format' => $this->config['format'],
'app_key' => $this->config['app_key'],
'v' => $this->config['version'],
'sign_method' => $this->config['sign_method'],
'customerId' => $this->config['customerId'],
];
$xml = Array2XML::createXML('request', $params);
$body = $xml->saveXML();
app('log')->info('php-wms-client-body', ['request_body' => $body]);
$sign = $this->createSign($query, $body);
$query['sign'] = $sign;
$response = $this->post($body, $query);
$content = $response->getBody()->getContents();
return $this->getResponse($content);
}
final protected function post($body, $params = [], $headers = ['content-type' => 'text/xml; charset = utf-8'], $ret = []) {
$url = $this->config['base_uri'];
$url = $url . http_build_query($params);
$data = [
'body' => $body,
'headers' => $headers,
];
$client = new Client();
$result = $client->request('POST', $url, $data);
return $result;
}
function getResponse($content) {
$result = XML2Array::createArray($content);
$isSuccess = (empty($result) || !is_array($result) || $result['response']['flag'] != 'success') ? false : true;
$result = [
'success' => $isSuccess,
'response' => $isSuccess ? $result['response'] : $result,
];
return $result;
}
/**
* 确认回传方法
* @var array
*/
private static $callbackMethods = [
'deliveryorder.confirm' => 'deliveryOrderConfirm',
'entryorder.confirm' => 'stockInConfirm',
'itemlack.report' => 'itemLackReport',
'orderprocess.report' => 'orderProcessReport',
'returnorder.confirm' => 'returnOrderConfirm',
'stockchange.report' => 'stockChangeReport',
'stockout.confirm' => 'stockOutConfirm',
];
public function callback($params, $onlyCheckSign = false)
{
if (!isset($params['method']) || empty($params['method'])) {
return $this->response('缺少参数method', false);
}
if (!isset($params['timestamp']) || empty($params['timestamp'])) {
return $this->response('缺少参数timestamp', false);
}
if (!isset($params['format']) || empty($params['format'])) {
return $this->response('缺少参数format', false);
}
if (!isset($params['app_key']) || empty($params['app_key'])) {
return $this->response('缺少参数key', false);
}
if (!isset($params['v']) || empty($params['v'])) {
return $this->response('缺少版本参数', false);
}
if (!isset($params['sign']) || empty($params['sign'])) {
return $this->response('缺少参数sign', false);
}
if (!isset($params['sign_method']) || empty($params['sign_method'])) {
return $this->response('缺少加密方式', false);
}
if (empty(($params['body']))) {
return $this->response('缺少消息body', false);
}
$method = $params['method'];
if (!array_key_exists($method, self::$callbackMethods)) {
return $this->response('调用的方法不存在', false);
}
if (!$this->checkSign($params)) {
return $this->response('签名错误', false);
}
// 用于阿里云自测验签
if ($onlyCheckSign) {
return $this->response();
}
$body = XML2Array::createArray($params['body']);
if (!isset($body['request']) || empty($body['request'])) {
return $this->response('消息body错误', false);
}
$result = call_user_func([$this, self::$callbackMethods[$method]], $body['request']);
return $result;
}
/**
* 奇门自测,仅验签
*/
public function onlyCheckSign($params)
{
return $this->callback($params, true);
}
/**
* 发货单确认
* @param $params
* @return array
*/
private function deliveryOrderConfirm($params)
{
$service = new OrderService();
$result = $service->deliverOrderConfirm($params);
if (!$result) {
return $this->response('失败', false);
}
return $this->response();
}
/**
* 入库单确认
* @param $params
* @return array
*/
private function stockInConfirm($params)
{
$service = new GoodsWarehouseService();
$result = $service->entryorderConfirm($params);
if (!$result) {
return $this->response('失败', false);
}
return $this->response();
}
/**
* 发货单缺货通知
* @param $params
* @return array
*/
private function itemLackReport($params)
{
$service = new OrderItemLackReportService();
$result = $service->createReceipts($params);
if (!$result) {
return $this->response('失败', false);
}
return $this->response();
}
/**
* 订单流水通知
* @param $params
* @return array
*/
private function orderProcessReport($params)
{
$service = new OrderProcessReportService();
$result = $service->orderProcessReport($params);
if (!$result) {
return $this->response('失败', false);
}
return $this->response();
}
/**
* 退货入库单确认
* @param $params
* @return array
*/
private function returnOrderConfirm($params)
{
$service = new RefundService();
$result = $service->refundOrderConfirm($params);
if (!$result) {
return $this->response('失败', false);
}
return $this->response();
}
/**
* 库存异动通知
* @param $params
* @return array
*/
private function stockChangeReport($params)
{
$service = new GoodsWarehouseService();
$result = $service->StockChangeReport($params);
if (!$result) {
return $this->response('失败', false);
}
return $this->response();
}
/**
* 出库单确认
* @param $params
* @return array
*/
private function stockOutConfirm($params)
{
$service = new OrderService();
$result = $service->stockOutOrderConfirm($params);
if (!$result) {
return $this->response('失败', false);
}
return $this->response();
}
/**
* @检查签名
* @param $params
* @return bool
*/
public function checkSign($params)
{
if (empty($params)) {
return false;
}
$query = $params;
unset($query['body'], $query['sign']);
$sign = $this->createSign($query, $params['body']);
if (!hash_equals((string) $sign, (string) $params['sign'])) {
return false;
}
return true;
}
public function response($message = '成功', $flag = true)
{
$response = [
'flag' => $flag ? 'success' : 'failure',
'code' => $flag ? '001' : '002',
'message' => $message,
];
$xml = Array2XML::createXML('response', $response);
$body = $xml->saveXML();
return [
'content' => $body,
'status' => 200,
'headers' => ['Content-Type' => 'utf-8'],
];
}
}