Bỏ qua, tới nội dung chính
Mục lục tài liệu

Tích hợp từ một hệ thống TMS, ERP hoặc WMS

Ánh xạ dữ liệu từ hệ thống bạn đang dùng vào đúng một nội dung gửi job tối ưu tuyến.

Mở trong ChatGPTMở trong Claude

SDK chưa phát hành. Nội dung dưới đây dùng HTTP thuần.

Dù dữ liệu đang nằm ở hệ thống nào, chỉ có một cách để đưa vào: nội dung gửi job tối ưu tuyến. Một TMS, một ERP và một WMS chỉ khác nhau ở chỗ hệ thống nào đã sẵn có phần nào trong ba đầu vào, gồm xe, đơn hàng và địa chỉ đằng sau chúng, và gọi tên chúng ra sao. TMS thường đã có sẵn đơn hàng kèm khung giờ và danh sách xe. ERP thường có đơn bán hàng hoặc đơn giao hàng cùng địa chỉ khách, nhưng hiếm khi có danh sách xe, và địa chỉ của nó cần được định vị tọa độ trước khi đưa vào nội dung gửi job. WMS thường có các lô hàng đã lấy và đóng gói với trọng lượng đo thực tế và thời điểm sẵn sàng ở bến, nhưng không có danh sách xe và cũng không có khung giờ giao hàng riêng.

Ba hệ thống, một nội dung gửi job

Từ một TMS

Hệ thống quản lý vận tải thường đã có sẵn đơn hàng kèm khung giờ và danh sách xe, nên phần lớn các trường vehicles và orders trong nội dung gửi job ánh xạ trực tiếp. Khoảng trống thường gặp là kích thước vật lý của xe: height_of_vehicle, length_of_vehicle và width_of_vehicle. Hầu hết TMS chỉ theo dõi tải trọng theo cân nặng, không theo ba kích thước mà nội dung gửi job yêu cầu, nên hãy giữ một bảng tham chiếu đội xe lập một lần, thay vì kỳ vọng lấy được từ mỗi job.

Từ một ERP

ERP thường có đơn bán hàng hoặc đơn giao hàng cùng địa chỉ khách, nhưng không có danh sách xe, vì dữ liệu đội xe thường nằm ở bộ phận điều phối. Địa chỉ của nó là văn bản, không phải tọa độ, nên hãy định vị tọa độ thành pickup_lat, pickup_lon, drop_lat và drop_lon trước khi đưa vào nội dung gửi job, đồng thời vẫn giữ văn bản gốc cho pickup_address và drop_address vì trường này vẫn bắt buộc song song với tọa độ. Các dòng đơn hàng của ERP cũng thường được tách theo từng sản phẩm chứ không theo từng điểm giao: hãy gộp trọng lượng và số kiện của mọi dòng cùng giao tới một địa chỉ thành một mục đơn hàng trước khi gửi, vì rweight và count_of_parcels mỗi trường mô tả một đơn hàng, không phải một dòng sản phẩm.

Từ một WMS

Hệ thống quản lý kho thường có sẵn các lô hàng đã lấy và đóng gói với trọng lượng đo thực tế và thời điểm sẵn sàng ở bến, ánh xạ trực tiếp vào rweight, count_of_parcels và pickup_date_time. Thứ nó thường không có là danh sách xe hoặc khung giờ giao hàng: hai thứ đó nằm ở phía hãng vận chuyển hoặc TMS ở đầu nhận, nên một tích hợp chỉ dựa vào WMS cần lấy vehicles, drop_date_time và drop_date_time_to từ nơi việc lập kế hoạch đó đang diễn ra, chứ không tự bịa ra tại kho.

Ánh xạ trường: nội dung gửi job sang Mẫu dữ liệu Pilot

Cột giữa là đúng tên cột của Mẫu dữ liệu Pilot, trên bảng tính orders, vehicles hoặc sites. Một trường không có cột tương ứng là một khoảng trống thật sự: thứ tích hợp của bạn phải lấy từ nơi khác, không phải thứ cố ép ra từ mẫu.

Trường trong nội dung jobCột trong bảng dữ liệuGhi chú
external_vehicle_idvehicles.external_idDùng lại đúng id trong bảng tính.
from_address, from_lat, from_lonvehicles.depot_codeTra kho theo depot_code trong bảng sites rồi lấy địa chỉ, lat và lon của kho đó.
from_working_time, to_working_timevehicles.shift_availabilityTách khung ca làm việc thành hai mốc thời gian ISO 8601; bảng tính lưu chúng dưới một giá trị duy nhất.
vehicle_tonnagevehicles.capacity_kgLấy trực tiếp, đã tính bằng kilogam.
height_of_vehicle, length_of_vehicle, width_of_vehiclekhông cóKhông có cột tương ứng. Mẫu chỉ ghi tải trọng, không ghi kích thước vật lý; hãy giữ một bảng tham chiếu đội xe lập một lần cho ba trường này.
handling_unit_idskhông cóKhông có cột tương ứng. Đây là mã nội bộ do phía tích hợp của bạn gán, không phải dữ liệu khách hàng.
external_order_idorders.external_idDùng lại đúng id trong bảng tính.
pickup_address, pickup_lat, pickup_lonorders.site_code (dòng nhận hàng)Tra điểm theo site_code trong bảng sites rồi lấy địa chỉ, lat và lon của điểm đó.
drop_address, drop_lat, drop_lonorders.site_code (dòng giao hàng)Tra cứu tương tự, ở dòng mà direction đánh dấu là giao hàng.
pickup_date_time, pickup_date_time_toorders.ready_time, orders.due_time (dòng nhận hàng)Lấy trực tiếp, sau khi chuyển sang ISO 8601.
drop_date_time, drop_date_time_toorders.ready_time, orders.due_time (dòng giao hàng)Lấy trực tiếp, sau khi chuyển sang ISO 8601.
rweightorders.weight_kgLấy trực tiếp, đã tính bằng kilogam.
count_of_parcelsorders.package_countLấy trực tiếp.
rlength, rwidth, rheightorders.volume_m3Không có cột trực tiếp. Mẫu chỉ ghi một chỉ số thể tích, không ghi ba chiều kích thước; hãy gửi một bộ kích thước cố định theo từng loại kiện nếu bạn có theo dõi.
transport_modekhông cóKhông có cột tương ứng. Đặt một lần cho cả tích hợp chứ không theo từng dòng; một bảng tính giao hàng đơn lẻ thường dùng lastmile.
handling_unit_id, service_id, resource_idkhông cóKhông có cột tương ứng. Đây là các mã nội bộ do phía tích hợp của bạn gán.
import { randomUUID } from "node:crypto";

const BASE = "https://<your-host>/api/v1/partners/addons/route-optimization";
const headers = { "api-key": process.env.ROUTE4GREEN_API_KEY! };

// Shaped like the Pilot data template: one row per site, one row per
// vehicle, one row per order leg (direction "pickup" or "drop").
type Site = { site_code: string; address: string; lat: number; lon: number };
type Vehicle = {
  external_id: string;
  depot_code: string;
  capacity_kg: number;
  // Parsed once from the sheet's single shift_availability value.
  shiftFrom: string;
  shiftTo: string;
};
type OrderLeg = {
  external_id: string;
  direction: "pickup" | "drop";
  site_code: string;
  weight_kg: number;
  package_count: number;
  ready_time: string;
  due_time: string;
};

function buildJobBody(sites: Site[], vehicles: Vehicle[], orderLegs: OrderLeg[]) {
  const siteByCode = new Map(sites.map((site) => [site.site_code, site]));

  const jobVehicles = vehicles.map((vehicle) => {
    const depot = siteByCode.get(vehicle.depot_code)!;
    return {
      external_vehicle_id: vehicle.external_id,
      resource_id: 1, // assigned by your integration, not in the workbook
      from_address: depot.address,
      from_lat: depot.lat,
      from_lon: depot.lon,
      from_working_time: vehicle.shiftFrom,
      to_working_time: vehicle.shiftTo,
      handling_unit_ids: [1], // assigned by your integration, not in the workbook
      // No workbook column for physical dimensions: keep a one-time fleet
      // reference alongside the vehicle list rather than pulling these per job.
      height_of_vehicle: 180,
      length_of_vehicle: 420,
      width_of_vehicle: 190,
      vehicle_tonnage: vehicle.capacity_kg,
    };
  });

  // Pair each order's pickup and drop rows into one job order item.
  const legsByExternalId = new Map<string, OrderLeg[]>();
  for (const leg of orderLegs) {
    legsByExternalId.set(leg.external_id, [...(legsByExternalId.get(leg.external_id) ?? []), leg]);
  }

  const jobOrders = [...legsByExternalId.entries()].map(([externalId, legs]) => {
    const pickup = legs.find((leg) => leg.direction === "pickup")!;
    const drop = legs.find((leg) => leg.direction === "drop")!;
    const pickupSite = siteByCode.get(pickup.site_code)!;
    const dropSite = siteByCode.get(drop.site_code)!;

    return {
      external_order_id: externalId,
      // No workbook column: set once per integration, not per row.
      transport_mode: "lastmile" as const,
      handling_unit_id: 1, // assigned by your integration, not in the workbook
      service_id: 1, // assigned by your integration, not in the workbook
      pickup_address: pickupSite.address,
      pickup_lat: pickupSite.lat,
      pickup_lon: pickupSite.lon,
      pickup_date_time: pickup.ready_time,
      pickup_date_time_to: pickup.due_time,
      drop_address: dropSite.address,
      drop_lat: dropSite.lat,
      drop_lon: dropSite.lon,
      drop_date_time: drop.ready_time,
      drop_date_time_to: drop.due_time,
      // No workbook column for per-package dimensions: the template only
      // carries a total weight, so length/width/height need their own source.
      rlength: 40,
      rwidth: 30,
      rheight: 30,
      rweight: pickup.weight_kg,
      count_of_parcels: pickup.package_count,
    };
  });

  return {
    external_reference: `cutoff-${new Date().toISOString().slice(0, 10)}`,
    optimization_options: {
      delivery_mode: "1",
      kind_of_plan: "1",
      tour_mode: "1;0;0;0;0;0",
    },
    vehicles: jobVehicles,
    orders: jobOrders,
  };
}

// Submit once per cut-off, with a fresh Idempotency-Key for this submit.
async function submitFromWorkbook(sites: Site[], vehicles: Vehicle[], orderLegs: OrderLeg[]) {
  const body = buildJobBody(sites, vehicles, orderLegs);
  const idempotencyKey = randomUUID();

  const response = await fetch(`${BASE}/jobs`, {
    method: "POST",
    headers: { ...headers, "Idempotency-Key": idempotencyKey, "content-type": "application/json" },
    body: JSON.stringify(body),
  });
  const envelope = await response.json();
  if (!envelope.success) throw new Error(String(envelope.error_code));

  return envelope.data.id as string;
}

Mô hình lặp lại

Gửi một job cho mỗi mốc chốt đơn, dùng external_reference làm id job của riêng bạn và header Idempotency-Key để gửi lại an toàn; cả hai nhánh phát lại đều đã nêu trong hướng dẫn xử lý job bất đồng bộ. Theo dõi job vừa gửi bằng cách hỏi trạng thái theo chu kỳ hoặc bằng webhook, nêu trong hướng dẫn kiểm tra chữ ký webhook, rồi ghi kết quả ngược về hệ thống nguồn theo đúng các id riêng bạn đã gửi. Xử lý unassigned_orders một cách rõ ràng: một job có mục chưa gán vẫn là một job đã hoàn tất, nên đưa các mục đó ngược về hệ thống nguồn thay vì âm thầm bỏ qua.

Giới hạn vẫn áp dụng

Các giới hạn gửi yêu cầu và phạm vi last mile nêu ở phần tham chiếu tối ưu tuyến áp dụng cho một tích hợp TMS, ERP hoặc WMS giống hệt như với bất kỳ tích hợp nào khác. Không có gì ở hệ thống nguồn làm nới lỏng các giới hạn đó.

Tên cột dùng trong bảng bên dưới chính là tên cột của Mẫu dữ liệu Pilot, nên một khách hàng đã điền sẵn bảng tính sẽ thấy đúng bảng thuật ngữ họ đã quen. Yêu cầu mẫu dữ liệu này ở trang tài nguyên ngay khi bạn biết mình đã có thể điền được cột nào.

Tích hợp từ một hệ thống TMS, ERP hoặc WMS | Route4Green