diff --git a/src/dto/logistics.dto.ts b/src/dto/logistics.dto.ts index fa57e60..b49b72c 100644 --- a/src/dto/logistics.dto.ts +++ b/src/dto/logistics.dto.ts @@ -34,7 +34,7 @@ export class ShipmentFeeBookDTO { @ApiProperty() sender: string; @ApiProperty() - startPhone: string; + startPhone: string|any; @ApiProperty() startPostalCode: string; @ApiProperty() diff --git a/src/entity/shipping_address.entity.ts b/src/entity/shipping_address.entity.ts index 7af0422..e6fea86 100644 --- a/src/entity/shipping_address.entity.ts +++ b/src/entity/shipping_address.entity.ts @@ -47,6 +47,11 @@ export class ShippingAddress { @Expose() phone_number_country: string; + @ApiProperty() + @Column() + @Expose() + email: string; + @ApiProperty({ example: '2022-12-12 11:11:11', description: '创建时间', diff --git a/src/service/freightwaves.service.ts b/src/service/freightwaves.service.ts index fa030ee..04f51fe 100644 --- a/src/service/freightwaves.service.ts +++ b/src/service/freightwaves.service.ts @@ -314,250 +314,10 @@ export class FreightwavesService { ...params, partner: this.config.partner, }; - const response = await this.sendRequest('/shipService/order/refundOrder', requestData); return response.data; } - /** - * 测试创建订单方法 - * 用于演示如何使用createOrder方法 - */ - async testCreateOrder() { - try { - // 设置必要的配置 - this.setConfig({ - appSecret: 'gELCHguGmdTLo!zfihfM91hae8G@9Sz23Mh6pHrt', - apiBaseUrl: 'http://tms.freightwaves.ca:8901/', - partner: '25072621035200000060' - }); - - // 准备测试数据 - const testParams: Omit = { - shipCompany: 'UPSYYZ7000NEW', - partnerOrderNumber: `test-order-${Date.now()}`, - warehouseId: '25072621030107400060', - shipper: { - name: 'John Doe', - phone: '123-456-7890', - company: 'Test Company', - countryCode: 'CA', - city: 'Toronto', - state: 'ON', - address1: '123 Main St', - address2: 'Suite 400', - postCode: 'M5V 2T6', - countryName: 'Canada', - cityName: 'Toronto', - stateName: 'Ontario', - companyName: 'Test Company Inc.' - }, - reciver: { - name: 'Jane Smith', - phone: '987-654-3210', - company: 'Receiver Company', - countryCode: 'CA', - city: 'Vancouver', - state: 'BC', - address1: '456 Oak St', - address2: '', - postCode: 'V6J 2A9', - countryName: 'Canada', - cityName: 'Vancouver', - stateName: 'British Columbia', - companyName: 'Receiver Company Ltd.' - }, - packages: [ - { - dimensions: { - length: 10, - width: 8, - height: 6, - lengthUnit: 'IN', - weight: 5, - weightUnit: 'LB' - }, - currency: 'CAD', - description: 'Test Package' - } - ], - declaration: { - boxNo: 'BOX-001', - sku: 'TEST-SKU-001', - cnname: '测试产品', - enname: 'Test Product', - declaredPrice: 100, - declaredQty: 1, - material: 'Plastic', - intendedUse: 'General use', - cweight: 5, - hsCode: '39269090', - battery: 'No' - }, - signService: 0 - }; - - // 调用创建订单方法 - this.log('开始测试创建订单...'); - this.log('测试参数:', testParams); - - // 注意:在实际环境中取消注释以下行来执行真实请求 - const result = await this.createOrder(testParams); - this.log('创建订单成功:', result); - - - // 返回模拟结果 - return { - partnerOrderNumber: testParams.partnerOrderNumber, - shipOrderId: `simulated-shipOrderId-${Date.now()}` - }; - } catch (error) { - this.log('测试创建订单失败:', error); - throw error; - } - } - - /** - * 测试费用试算方法 - * @returns 费用试算结果 - */ - async testRateTry() { - try { - // 设置必要的配置 - this.setConfig({ - appSecret: 'gELCHguGmdTLo!zfihfM91hae8G@9Sz23Mh6pHrt', - apiBaseUrl: 'http://tms.freightwaves.ca:8901', - partner: '25072621035200000060' - }); - - // 准备测试数据 - 符合RateTryRequest接口要求 - const testParams: Omit = { - shipCompany: 'UPSYYZ7000NEW', - partnerOrderNumber: `test-rate-try-${Date.now()}`, - warehouseId: '25072621030107400060', - shipper: { - name: 'John Doe', - phone: '123-456-7890', - company: 'Test Company', - countryCode: 'CA', - city: 'Toronto', - state: 'ON', - address1: '123 Main St', - address2: 'Suite 400', - postCode: 'M5V 2T6', - countryName: 'Canada', - cityName: 'Toronto', - stateName: 'Ontario', - companyName: 'Test Company Inc.' - }, - reciver: { - name: 'Jane Smith', - phone: '987-654-3210', - company: 'Receiver Company', - countryCode: 'CA', - city: 'Vancouver', - state: 'BC', - address1: '456 Oak St', - address2: '', - postCode: 'V6J 2A9', - countryName: 'Canada', - cityName: 'Vancouver', - stateName: 'British Columbia', - companyName: 'Receiver Company Ltd.' - }, - packages: [ - { - dimensions: { - length: 10, - width: 8, - height: 6, - lengthUnit: 'IN', - weight: 5, - weightUnit: 'LB' - }, - currency: 'CAD', - description: 'Test Package' - } - ], - signService: 0 - }; - - // 调用费用试算方法 - this.log('开始测试费用试算...'); - this.log('测试参数:', testParams); - - // 注意:在实际环境中取消注释以下行来执行真实请求 - const result = await this.rateTry(testParams); - this.log('费用试算成功:', result); - - this.log('测试完成:费用试算方法调用成功(模拟)'); - this.log('提示:在实际环境中,取消注释代码中的rateTry调用行来执行真实请求'); - - // 返回模拟结果 - return { - shipCompany: 'DHL', - channelCode: 'DHL-EXPRESS', - totalAmount: 125.50, - currency: 'CAD' - }; - } catch (error) { - this.log('测试费用试算失败:', error); - throw error; - } - } - - /** - * 测试查询订单方法 - * @returns 查询订单结果 - */ - async testQueryOrder() { - try { - // 设置必要的配置 - this.setConfig({ - appSecret: 'gELCHguGmdTLo!zfihfM91hae8G@9Sz23Mh6pHrt', - apiBaseUrl: 'http://freightwaves.ca:8901', - partner: '25072621035200000060' - }); - - // 准备测试数据 - 可以通过partnerOrderNumber或shipOrderId查询 - const testParams: Omit = { - // 选择其中一个参数进行测试 - partnerOrderNumber: 'test-order-123456789', // 示例订单号 - // shipOrderId: 'simulated-shipOrderId-123456789' // 或者使用运单号 - }; - - // 调用查询订单方法 - this.log('开始测试查询订单...'); - this.log('测试参数:', testParams); - - // 注意:在实际环境中取消注释以下行来执行真实请求 - const result = await this.queryOrder(testParams); - this.log('查询订单成功:', result); - - this.log('测试完成:查询订单方法调用成功(模拟)'); - - // 返回模拟结果 - return { - thirdOrderId: 'thirdOrder-123456789', - shipCompany: 'DHL', - expressFinish: 0, - expressFailMsg: '', - expressOrder: { - mainTrackingNumber: '1234567890', - labelPath: ['https://example.com/label.pdf'], - totalAmount: 100, - currency: 'CAD', - balance: 50 - }, - partnerOrderNumber: testParams.partnerOrderNumber, - shipOrderId: 'simulated-shipOrderId-123456789' - }; - } catch (error) { - this.log('测试查询订单失败:', error); - throw error; - } - } - /** * 辅助日志方法,处理logger可能未定义的情况 * @param message 日志消息 diff --git a/src/service/logistics.service.ts b/src/service/logistics.service.ts index baf2a43..4e472be 100644 --- a/src/service/logistics.service.ts +++ b/src/service/logistics.service.ts @@ -279,8 +279,7 @@ export class LogisticsService { shipmentRepo.remove(shipment); - const res = await this.uniExpressService.deleteShipment(shipment.return_tracking_number); - console.log('res', res.data); // todo + await this.uniExpressService.deleteShipment(shipment.return_tracking_number); await orderRepo.save(order); @@ -310,7 +309,6 @@ export class LogisticsService { console.log('同步到woocommerce失败', error); return true; } - return true; } catch { throw new Error('删除运单失败'); @@ -330,14 +328,12 @@ export class LogisticsService { if (data.shipmentPlatform === 'uniuni') { resShipmentFee = await this.uniExpressService.getRates(reqBody); } else if (data.shipmentPlatform === 'freightwaves') { - - - // resShipmentFee = await this.freightwavesService.rateTry(reqBody); + const fre_reqBody = await this.convertToFreightwavesRateTry(data); + resShipmentFee = await this.freightwavesService.rateTry(fre_reqBody); } else { throw new Error('不支持的运单平台'); } - if (resShipmentFee.status !== 'SUCCESS') { throw new Error(resShipmentFee.ret_msg); } @@ -362,10 +358,9 @@ export class LogisticsService { let resShipmentOrder; try { - resShipmentOrder = await this.mepShipment(data, order); - // 记录物流信息,并将订单状态转到完成 + // 记录物流信息,并将订单状态转到完成,uniuni状态为SUCCESS,tms.freightwaves状态为00000200 if (resShipmentOrder.status === 'SUCCESS' || resShipmentOrder.code === '00000200') { order.orderStatus = ErpOrderStatus.COMPLETED; } else { @@ -443,25 +438,23 @@ export class LogisticsService { shipmentId = shipment.id; } if (order.status !== OrderStatus.COMPLETED) { + // shopyy未提供更新订单接口,暂不更新订单状态 // await this.shopyyService.updateOrder(site, order.externalOrderId, { // status: OrderStatus.COMPLETED, // }); order.status = OrderStatus.COMPLETED; } } - order.orderStatus = ErpOrderStatus.COMPLETED; - await orderRepo.save(order); }).catch(error => { transactionError = error + throw new Error(`请求错误:${error}`); }); if (transactionError !== undefined) { - console.log('err', transactionError); throw transactionError; } - // 更新产品发货信息 this.orderService.updateOrderSales(order.id, sales); @@ -733,7 +726,6 @@ export class LogisticsService { 'order_id': order.externalOrderId // todo: 需要获取订单的externalOrderId } }; - // 添加运单 resShipmentOrder = await this.uniExpressService.createShipment(reqBody); } @@ -742,7 +734,7 @@ export class LogisticsService { // 根据TMS系统对接说明文档格式化参数 const reqBody: any = { shipCompany: 'UPSYYZ7000NEW', - partnerOrderNumber: order.siteId + '-1-' + order.externalOrderId, + partnerOrderNumber: order.siteId + '-' + order.externalOrderId, warehouseId: '25072621030107400060', shipper: { name: data.details.origin.contact_name, // 姓名 @@ -806,10 +798,9 @@ export class LogisticsService { }; resShipmentOrder = await this.freightwavesService.createOrder(reqBody); // 创建订单 + //tms只返回了物流订单号,需要查询一次来获取完整的物流信息 const queryRes = await this.freightwavesService.queryOrder({ shipOrderId: resShipmentOrder.shipOrderId }); // 查询订单 - console.log('queryRes:', queryRes); // 打印查询结果 resShipmentOrder.push(queryRes); - } return resShipmentOrder; @@ -824,41 +815,49 @@ export class LogisticsService { * @param data ShipmentFeeBookDTO数据 * @returns RateTryRequest格式的数据 */ - convertToFreightwavesRateTry(data: ShipmentFeeBookDTO): Omit { + async convertToFreightwavesRateTry(data: ShipmentFeeBookDTO): Promise> { + + const shipments = await this.shippingAddressModel.findOne({ + where: { + id: data.address_id, + }, + }) + + const address = shipments?.address; // 转换为RateTryRequest格式 - return { + const r = { shipCompany: 'UPSYYZ7000NEW', // 必填,但ShipmentFeeBookDTO中缺少 partnerOrderNumber: `order-${Date.now()}`, // 必填,使用时间戳生成 warehouseId: '25072621030107400060', // 可选,使用stockPointId转换 shipper: { name: data.sender, // 必填 - phone: data.startPhone, // 必填 - company: '', // 必填,但ShipmentFeeBookDTO中缺少 + phone: data.startPhone.phone, // 必填 + company: address.country, // 必填,但ShipmentFeeBookDTO中缺少 countryCode: data.shipperCountryCode, // 必填 - city: '', // 必填,但ShipmentFeeBookDTO中缺少 - state: '', // 必填,但ShipmentFeeBookDTO中缺少 - address1: data.pickupAddress, // 必填 - address2: '', // 必填,但ShipmentFeeBookDTO中缺少 + city: address.city || '', // 必填,但ShipmentFeeBookDTO中缺少 + state: address.region || '', // 必填,但ShipmentFeeBookDTO中缺少 + address1: address.address_line_1, // 必填 + address2: address.address_line_1 || '', // 必填,但ShipmentFeeBookDTO中缺少 postCode: data.startPostalCode, // 必填 - countryName: '', // 必填,但ShipmentFeeBookDTO中缺少 - cityName: '', // 必填,但ShipmentFeeBookDTO中缺少 - stateName: '', // 必填,但ShipmentFeeBookDTO中缺少 - companyName: '', // 必填,但ShipmentFeeBookDTO中缺少 + countryName: address.country || '', // 必填,但ShipmentFeeBookDTO中缺少 + cityName: address.city || '', // 必填,但ShipmentFeeBookDTO中缺少 + stateName: address.region || '', // 必填,但ShipmentFeeBookDTO中缺少 + companyName: address.country || '', // 必填,但ShipmentFeeBookDTO中缺少 }, reciver: { name: data.receiver, // 必填 phone: data.receiverPhone, // 必填 - company: '', // 必填,但ShipmentFeeBookDTO中缺少 + company: address.country,// 必填,但ShipmentFeeBookDTO中缺少 countryCode: data.country, // 必填,使用country代替countryCode city: data.city, // 必填 state: data.province, // 必填,使用province代替state address1: data.deliveryAddress, // 必填 - address2: '', // 必填,但ShipmentFeeBookDTO中缺少 + address2: data.deliveryAddress, // 必填,但ShipmentFeeBookDTO中缺少 postCode: data.postalCode, // 必填 - countryName: '', // 必填,但ShipmentFeeBookDTO中缺少 - cityName: data.city, // 必填,使用city代替cityName - stateName: data.province, // 必填,使用province代替stateName - companyName: '', // 必填,但ShipmentFeeBookDTO中缺少 + countryName: address.country, // 必填,但ShipmentFeeBookDTO中缺少 + cityName: data.city || '', // 必填,使用city代替cityName + stateName: data.province || '', // 必填,使用province代替stateName + companyName: address.country || '', // 必填,但ShipmentFeeBookDTO中缺少 }, packages: [ { @@ -866,9 +865,9 @@ export class LogisticsService { length: data.length, // 必填 width: data.width, // 必填 height: data.height, // 必填 - lengthUnit: (data.dimensionUom.toUpperCase() === 'CM' ? 'CM' : 'IN') as 'CM' | 'IN', // 必填,转换为有效的单位 + lengthUnit: (data.dimensionUom === 'IN' ? 'IN' : 'CM') as 'IN' | 'CM', // 必填,转换为有效的单位 weight: data.weight, // 必填 - weightUnit: (data.weightUom.toUpperCase() === 'KG' ? 'KG' : 'LB') as 'KG' | 'LB', // 必填,转换为有效的单位 + weightUnit: (data.weightUom === 'LBS' ? 'LB' : 'KG') as 'LB' | 'KG', // 必填,转换为有效的单位 }, currency: 'CAD', // 必填,但ShipmentFeeBookDTO中缺少,使用默认值 description: 'Package', // 必填,但ShipmentFeeBookDTO中缺少,使用默认值 @@ -876,31 +875,6 @@ export class LogisticsService { ], signService: 0, // 可选,默认不使用签名服务 }; - } - - /** - * 获取ShipmentFeeBookDTO缺少的freightwaves必填字段 - * @returns 缺少的必填字段列表 - */ - getMissingFreightwavesFields(): string[] { - return [ - 'shipCompany', // 渠道 - 'partnerOrderNumber', // 第三方客户订单编号 - 'shipper.company', // 发货人公司 - 'shipper.city', // 发货人城市 - 'shipper.state', // 发货人州/省Code - 'shipper.address2', // 发货人详细地址2 - 'shipper.countryName', // 发货人国家名称 - 'shipper.cityName', // 发货人城市名称 - 'shipper.stateName', // 发货人州/省名称 - 'shipper.companyName', // 发货人公司名称 - 'reciver.company', // 收货人公司 - 'reciver.address2', // 收货人详细地址2 - 'reciver.countryName', // 收货人国家名称 - 'reciver.companyName', // 收货人公司名称 - 'packages[0].currency', // 包裹币种 - 'packages[0].description', // 包裹描述 - 'partner', // 商户ID - ]; + return r as any; } } diff --git a/src/service/order.service.ts b/src/service/order.service.ts index 8625380..41e15b3 100644 --- a/src/service/order.service.ts +++ b/src/service/order.service.ts @@ -141,8 +141,8 @@ export class OrderService { updated: 0, errors: [] }; - console.log('开始进入循环同步订单', result.length, '个订单') - console.log('开始进入循环同步订单', result.length, '个订单') + console.log('开始进入循环同步订单', result.length, '个订单') + console.log('开始进入循环同步订单', result.length, '个订单') // 遍历每个订单进行同步 for (const order of result) { try { @@ -166,7 +166,7 @@ export class OrderService { } else { syncResult.created++; } - // console.log('updated', syncResult.updated, 'created:', syncResult.created) + // console.log('updated', syncResult.updated, 'created:', syncResult.created) } catch (error) { // 记录错误但不中断整个同步过程 syncResult.errors.push({ @@ -176,8 +176,8 @@ export class OrderService { syncResult.processed++; } } - console.log('同步完成', syncResult.updated, 'created:', syncResult.created) - + console.log('同步完成', syncResult.updated, 'created:', syncResult.created) + this.logger.debug('syncOrders result', syncResult) return syncResult; } @@ -337,26 +337,26 @@ export class OrderService { // 自动更新订单状态(如果需要) await this.autoUpdateOrderStatus(siteId, order); - if(existingOrder){ - // 矫正数据库中的订单数据 - const updateData: any = { status: order.status }; - if (this.canUpdateErpStatus(existingOrder.orderStatus)) { - updateData.orderStatus = this.mapOrderStatus(order.status as any); - } - // 更新订单主数据 - await this.orderModel.update({ externalOrderId: String(order.id), siteId: siteId }, updateData); - // 更新 fulfillments 数据 - await this.saveOrderFulfillments({ - siteId, - orderId: existingOrder.id, - externalOrderId:order.id, - fulfillments: fulfillments, - }); + if (existingOrder) { + // 矫正数据库中的订单数据 + const updateData: any = { status: order.status }; + if (this.canUpdateErpStatus(existingOrder.orderStatus)) { + updateData.orderStatus = this.mapOrderStatus(order.status as any); + } + // 更新订单主数据 + await this.orderModel.update({ externalOrderId: String(order.id), siteId: siteId }, updateData); + // 更新 fulfillments 数据 + await this.saveOrderFulfillments({ + siteId, + orderId: existingOrder.id, + externalOrderId: order.id, + fulfillments: fulfillments, + }); } - const externalOrderId = String(order.id); + const externalOrderId = String(order.id); // 这里的 saveOrder 已经包括了创建订单和更新订单 let orderRecord: Order = await this.saveOrder(siteId, orderData); - // 如果订单从未完成变为完成状态,则更新库存 + // 如果订单从未完成变为完成状态,则更新库存 if ( orderRecord && orderRecord.orderStatus !== ErpOrderStatus.COMPLETED && @@ -377,7 +377,7 @@ export class OrderService { await this.saveOrderRefunds({ siteId, orderId, - externalOrderId , + externalOrderId, refunds, }); // 保存费用信息 @@ -731,12 +731,12 @@ export class OrderService { await this.orderSaleModel.delete(currentOrderSale.map(v => v.id)); } if (!orderItem.sku) return; - - // 从数据库查询产品,关联查询组件 + + // 从数据库查询产品,关联查询组件 const productDetail = await this.productService.getComponentDetailFromSiteSku({ sku: orderItem.sku, name: orderItem.name }); - - if (!productDetail || !productDetail.quantity) return; - const {product, quantity} = productDetail + + if (!productDetail || !productDetail.quantity) return; + const { product, quantity } = productDetail const componentDetails: { product: Product, quantity: number }[] = product.components?.length > 0 ? await Promise.all(product.components.map(async comp => { return { product: await this.productModel.findOne({ @@ -770,7 +770,7 @@ export class OrderService { }); return orderSale }).filter(v => v !== null) - console.log("orderSales",orderSales) + console.log("orderSales", orderSales) if (orderSales.length > 0) { await this.orderSaleModel.save(orderSales); } @@ -2676,10 +2676,10 @@ export class OrderService { // 辅助函数:删除指定位置的括号对及其内容 const removeParenthesesAt = (s: string, leftIndex: number): string => { if (leftIndex === -1) return s; - + let rightIndex = -1; let parenCount = 0; - + for (let i = leftIndex; i < s.length; i++) { const char = s[i]; if (char === '(') { @@ -2692,17 +2692,17 @@ export class OrderService { } } } - + if (rightIndex !== -1) { return s.substring(0, leftIndex) + s.substring(rightIndex + 1); } - + return s; }; // 1. 处理每个分号前面的括号对 let result = str; - + // 找出所有分号的位置 const semicolonIndices: number[] = []; for (let i = 0; i < result.length; i++) { @@ -2710,11 +2710,11 @@ export class OrderService { semicolonIndices.push(i); } } - + // 从后向前处理每个分号,避免位置变化影响后续处理 for (let i = semicolonIndices.length - 1; i >= 0; i--) { const semicolonIndex = semicolonIndices[i]; - + // 从分号位置向前查找最近的左括号 let lastLeftParenIndex = -1; for (let j = semicolonIndex - 1; j >= 0; j--) { @@ -2723,7 +2723,7 @@ export class OrderService { break; } } - + // 如果找到左括号,删除该括号对及其内容 if (lastLeftParenIndex !== -1) { result = removeParenthesesAt(result, lastLeftParenIndex);