Merge pull request 'Fix:' (#3) from longbot/API:Feature-add-shipment into main

Reviewed-on: #3
This commit is contained in:
longbot 2025-08-08 11:32:08 +00:00
commit 6ae4476d0c
5 changed files with 96 additions and 83 deletions

View File

@ -165,6 +165,21 @@ export class LogisticsController {
@ApiOkResponse(
{type: BooleanRes}
)
@Post('/getShipmentFee')
async getShipmentFee(
@Body() data: ShipmentBookDTO
) {
try {
const fee = await this.logisticsService.getShipmentFee(data);
return successResponse(fee);
} catch (error) {
return errorResponse(error?.message || '创建失败');
}
}
@ApiOkResponse(
{type: BooleanRes}
)
@Post('/getShipmentLabel/:shipmentId')
async getShipmentLabel(
@Param('shipmentId') shipmentId: string
@ -182,7 +197,6 @@ export class LogisticsController {
}
}
@ApiOkResponse()
@Post('/getPaymentMethods')
async getpaymentmethods() {

View File

@ -219,6 +219,9 @@ export class PackagingEnvelope {
// }
export class ShippingDetailsDTO {
@ApiProperty()
shipmentFee: number;
@ApiProperty({ type: Location })
@Rule(RuleType.object<Location>())
origin: Location;

View File

@ -22,10 +22,6 @@ export class Shipment {
id: string;
@ApiProperty()
@Column({ name: 'order_id', nullable: true })
@Expose()
orderId: string;
@OneToOne(() => Order)
@JoinColumn({ name: 'order_id' })
order: Order;
@ -39,6 +35,10 @@ export class Shipment {
@JoinColumn({ name: 'stock_point_id' })
stockPoint: StockPoint;
@Column({ nullable: false, default: 0})
@Expose()
fee: number;
@ApiProperty()
@Column({ nullable: true })
@Expose()

View File

@ -204,6 +204,40 @@ export class LogisticsService {
}
}
async getShipmentFee(data: ShipmentBookDTO) {
try {
const stock_point = await this.stockPointModel.findOneBy({ id: data.stockPointId});
const reqBody = {
sender: data.details.origin.contact_name,
start_phone: data.details.origin.phone_number,
start_postal_code: data.details.origin.address.postal_code.replace(/\s/g, ''),
pickup_address: data.details.origin.address.address_line_1,
pickup_warehouse: stock_point.upStreamStockPointId,
shipper_country_code: data.details.origin.address.country,
receiver: data.details.destination.contact_name,
city: data.details.destination.address.city,
province: data.details.destination.address.region, // todo待确认
country: data.details.destination.address.country,
postal_code: data.details.destination.address.postal_code.replace(/\s/g, ''),
delivery_address: data.details.destination.address.address_line_1,
receiver_phone: data.details.destination.phone_number.number,
receiver_email: data.details.destination.email_addresses,
// item_description: data.sales, // todo: 货品信息
length: data.details.packaging_properties.packages[0].measurements.cuboid.l,
width: data.details.packaging_properties.packages[0].measurements.cuboid.w,
height: data.details.packaging_properties.packages[0].measurements.cuboid.h,
dimension_uom: data.details.packaging_properties.packages[0].measurements.cuboid.unit,
weight: data.details.packaging_properties.packages[0].measurements.weight.value,
weight_uom: data.details.packaging_properties.packages[0].measurements.weight.unit,
currency: 'CAD',
}
const resShipmentFee = await this.uniExpressService.getRates(reqBody);
return resShipmentFee.data.totalAfterTax * 100;
} catch (e) {
throw e;
}
}
async createShipment(orderId: number, data: ShipmentBookDTO, userId: number) {
const order = await this.orderModel.findOneBy({ id: orderId });
if (!order) {
@ -215,6 +249,8 @@ export class LogisticsService {
) {
throw new Error('订单状态不正确 ');
}
let resShipmentOrder;
try {
const stock_point = await this.stockPointModel.findOneBy({ id: data.stockPointId});
const reqBody = {
@ -238,28 +274,24 @@ export class LogisticsService {
height: data.details.packaging_properties.packages[0].measurements.cuboid.h,
dimension_uom: data.details.packaging_properties.packages[0].measurements.cuboid.unit,
weight: data.details.packaging_properties.packages[0].measurements.weight.value,
weight_uom: `${data.details.packaging_properties.packages[0].measurements.weight.unit}S`, // todo换成KGS和LBS
weight_uom: data.details.packaging_properties.packages[0].measurements.weight.unit,
currency: 'CAD',
}
console.log('body', reqBody);
throw new Error('test ');
// todo: 两个请求做异步 参考promise.all()方法
// 获取预估费率
const resShipmentFee = await this.uniExpressService.getRates(reqBody);
// 添加运单
const resShipmentOrder = await this.uniExpressService.createShipment(reqBody);
resShipmentOrder = await this.uniExpressService.createShipment(reqBody);
// 记录物流信息,并将订单状态转到完成
if (resShipmentOrder.status === 'SUCCESS') {
order.orderStatus = ErpOrderStatus.COMPLETED;
} else {
throw new Error('运单生成失败');
}
const dataSource = this.dataSourceManager.getDataSource('default');
dataSource.transaction(async manager => {
let transactionError = undefined;
await dataSource.transaction(async manager => {
const orderRepo = manager.getRepository(Order);
const shipmentRepo = manager.getRepository(Shipment);
if (order.orderStatus === ErpOrderStatus.COMPLETED) {
const shipment = await shipmentRepo.save({
tracking_provider: 'uniuni-express', // todo: id未确定后写进常数
@ -267,90 +299,38 @@ export class LogisticsService {
stockPointId: '1', // todo
state: resShipmentOrder.data.uni_status_code,
return_tracking_number: resShipmentOrder.data.tno,
order_id: order.id
fee: data.details.shipmentFee,
order_id: orderId
});
order.shipmentId = shipment.id;
// 同步物流信息到woocommerce
const site = this.geSite(order.siteId);
const site = await this.geSite(order.siteId);
await this.wpService.createShipment(site, order.externalOrderId, {
tracking_number: shipment.primary_tracking_number,
tracking_provider: shipment?.rate?.carrier_name,
tracking_number: shipment.return_tracking_number,
tracking_provider: shipment?.tracking_provider,
});
}
await orderRepo.save(order);
}).catch(error => {
transactionError = error
});
if (transactionError !== undefined) {
console.log('err', transactionError);
throw transactionError;
}
return { data: {
resShipmentFee,
resShipmentOrder
} };
} catch(error) {
if (resShipmentOrder.status === 'SUCCESS') {
await this.uniExpressService.deleteShipment(resShipmentOrder.data.tno);
}
throw new Error(`上游请求错误:${error}`);
}
// const dataSource = this.dataSourceManager.getDataSource('default');
// return dataSource.transaction(async manager => {
// const productRepo = manager.getRepository(Product);
// const shipmentRepo = manager.getRepository(Shipment);
// const shipmentItemRepo = manager.getRepository(ShipmentItem);
// const orderShipmentRepo = manager.getRepository(OrderShipment);
// const stockRecordRepo = manager.getRepository(StockRecord);
// const stockRepo = manager.getRepository(Stock);
// const orderRepo = manager.getRepository(Order);
// await shipmentRepo.save(shipment);
// await this.getShipment(shipment.id);
// const shipmentItems = [];
// for (const item of data?.sales) {
// const product = await productRepo.findOne({ where: { sku: item.sku } });
// shipmentItems.push({
// shipment_id: shipment.id,
// productId: product.id,
// name: product.name,
// sku: item.sku,
// quantity: item.quantity,
// });
// const stock = await stockRepo.findOne({
// where: {
// stockPointId: data.stockPointId,
// productSku: item.sku,
// },
// });
// stock.quantity -= item.quantity;
// await stockRepo.save(stock);
// await stockRecordRepo.save({
// stockPointId: data.stockPointId,
// productSku: item.sku,
// operationType: StockRecordOperationType.OUT,
// quantityChange: item.quantity,
// operatorId: userId,
// note: `订单${[orderId, ...data.orderIds].join(',')} 发货`,
// });
// }
// await shipmentItemRepo.save(shipmentItems);
// await orderShipmentRepo.save({
// order_id: orderId,
// shipment_id: shipment.id,
// stockPointId: data.stockPointId,
// });
// for (const orderId of data?.orderIds) {
// await orderShipmentRepo.save({
// order_id: orderId,
// shipment_id: shipment.id,
// stockPointId: data.stockPointId,
// });
// const order = await orderRepo.findOneBy({ id: orderId });
// order.orderStatus = ErpOrderStatus.COMPLETED;
// order.status = OrderStatus.COMPLETED;
// await orderRepo.save(order);
// }
// order.orderStatus = ErpOrderStatus.COMPLETED;
// order.status = OrderStatus.COMPLETED;
// await orderRepo.save(order);
// });
}
async syncShipment() {

View File

@ -84,6 +84,22 @@ export class UniExpressService {
}
}
async deleteShipment(tno: string) {
const body = {
tno
};
const token = await this.getToken();
const config: AxiosRequestConfig= {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`
},
url: `${this.url}/orders/cancelorder`,
data: body
};
return await axios.request(config);
}
async getLabel(tracking_number: string) {
const body = {
packageId: tracking_number