518 lines
18 KiB
PHP
Executable File
518 lines
18 KiB
PHP
Executable File
<?php
|
|
|
|
namespace JR\NetSuite\Mapper;
|
|
|
|
use JR\NetSuite\Mapper\AbstractMapper;
|
|
use Magento\Directory\Model\RegionFactory;
|
|
use Magento\Framework\Exception\LocalizedException;
|
|
use Magento\Store\Api\Data\StoreInterface;
|
|
use Magento\Store\Model\StoreManagerInterface;
|
|
use Magento\Framework\Api\SearchCriteriaBuilder;
|
|
use JR\NetSuite\Mapper\CustomerMapper;
|
|
use JR\NetSuite\Api\LocationRepositoryInterface;
|
|
use Magento\Sales\Api\OrderRepositoryInterface;
|
|
use Magento\Catalog\Api\ProductRepositoryInterface;
|
|
use Magento\Catalog\Model\Product;
|
|
use Magento\Customer\Api\GroupRepositoryInterface;
|
|
use Magento\Tax\Api\TaxRateRepositoryInterface;
|
|
|
|
|
|
// Handles the specifics of mapping order data.
|
|
class OrderMapper
|
|
{
|
|
|
|
const ORDER_STATUS_MAPPER = [
|
|
'pendingApproval' => 'pending',
|
|
'pendingFulfillment' => 'pending',
|
|
'cancelled' => 'canceled',
|
|
'partiallyFulfilled' => 'processing',
|
|
'pendingBillingPartFulfilled' => 'processing',
|
|
'pendingBilling' => 'processing',
|
|
'fullyBilled' => 'complete',
|
|
'closed' => 'closed',
|
|
'undefined' => 'undefined'
|
|
];
|
|
|
|
const SHIP_METHOD_MAPPER = [
|
|
'freeshipping_freeshipping' => '911',
|
|
'will_call' => '911',
|
|
'ups_ups' => '1716'
|
|
];
|
|
|
|
/**
|
|
* Shipping method mapping from NetSuite to Magento.
|
|
*/
|
|
const SHIP_METHOD_MAPPER_R = [
|
|
// Will Call
|
|
'911' => 'instore_pickup', // Will Call
|
|
// UPS
|
|
'xxxx' => 'ups_11', // UPS Standard
|
|
'1716' => 'ups_03', // UPS® Ground
|
|
'7074' => 'ups_59', // UPS 2nd Day Air A.M.
|
|
'6849' => 'ups_59', // UPS 2nd Day Air A.M.®
|
|
'4622' => 'ups_02', // UPS 2nd Day Air®
|
|
'7076' => 'ups_12', // UPS 3 Day Select
|
|
'6845' => 'ups_12', // UPS 3 Day Select®
|
|
'7077' => 'ups_01', // UPS Next Day Air
|
|
'6847' => 'ups_01', // UPS Next Day Air®
|
|
'4623' => 'ups_01', // UPS Next Day Air Saver® [***Closest Match***]
|
|
'4624' => 'ups_14', // UPS Next Day Air® Early A.M.®
|
|
'10720' => 'ups_08', // UPS Worldwide Expedited®
|
|
'10721' => 'ups_07', // UPS Worldwide Express®
|
|
'xxxxx' => 'ups_54', // UPS Worldwide Express Plus
|
|
'10722' => 'ups_65', // UPS Worldwide Saver®
|
|
// Fedex (Due to Fedex unavailability, flatrate_flatrate will be used)
|
|
'4630' => 'flatrate_flatrate', // 'fedex_FEDEX_2_DAY', // FedEx 2Day®
|
|
'4633' => 'flatrate_flatrate', // 'fedex_FEDEX_2_DAY_AM', // FedEx 2Day® A.M.
|
|
'4632' => 'flatrate_flatrate', // 'fedex_FEDEX_3_DAY_FREIGHT', // FedEx 3Day® Freight
|
|
'4631' => 'flatrate_flatrate', // 'fedex_FEDEX_EXPRESS_SAVER', // FedEx Express Saver®
|
|
'4627' => 'flatrate_flatrate', // 'fedex_FIRST_OVERNIGHT', // FedEx First Overnight®
|
|
'4634' => 'flatrate_flatrate', // 'fedex_FEDEX_1_DAY_FREIGHT', // FedEx First Overnight® Freight [***Closest Match***]
|
|
'1717' => 'flatrate_flatrate', // 'fedex_FEDEX_GROUND', // FedEx Ground®
|
|
'4626' => 'flatrate_flatrate', // 'fedex_GROUND_HOME_DELIVERY', // FedEx Home Delivery®
|
|
'4636' => 'flatrate_flatrate', // 'fedex_INTERNATIONAL_ECONOMY', // FedEx International Economy®
|
|
'4635' => 'flatrate_flatrate', // 'fedex_INTERNATIONAL_PRIORITY', // FedEx International Priority®
|
|
'4628' => 'flatrate_flatrate', // 'fedex_PRIORITY_OVERNIGHT', // FedEx Priority Overnight®
|
|
'4629' => 'flatrate_flatrate', // 'fedex_STANDARD_OVERNIGHT', // FedEx Standard Overnight®
|
|
// Others
|
|
'4625' => 'flatrate_flatrate', // USPS/Trucking/International
|
|
'7369' => 'flatrate_flatrate' // Customer Provided
|
|
];
|
|
|
|
/**
|
|
* @var RegionFactory
|
|
*/
|
|
protected $regionFactory;
|
|
|
|
/**
|
|
* @var int
|
|
*/
|
|
protected $storeId;
|
|
|
|
/**
|
|
* @var \JR\NetSuite\Helper\Data
|
|
*/
|
|
protected $helper;
|
|
|
|
/**
|
|
* @var SearchCriteriaBuilder
|
|
*/
|
|
protected $searchCriteriaBuilder;
|
|
|
|
/**
|
|
* @var CustomerMapper
|
|
*/
|
|
protected $customerMapper;
|
|
|
|
/**
|
|
* @var LocationRepository
|
|
*/
|
|
protected $locationRepository;
|
|
|
|
/**
|
|
* @var OrderRepositoryInterface
|
|
*/
|
|
protected $orderRepository;
|
|
|
|
/**
|
|
* @var ProductRepositoryInterface
|
|
*/
|
|
protected $productRepository;
|
|
|
|
/**
|
|
* @var Product
|
|
*/
|
|
protected $productModel;
|
|
|
|
/**
|
|
* @var GroupRepositoryInterface
|
|
*/
|
|
protected $groupRepository;
|
|
|
|
/**
|
|
* @var TaxRateRepositoryInterface
|
|
*/
|
|
protected $taxRateRepository;
|
|
|
|
/**
|
|
* OrderMapper constructor.
|
|
*/
|
|
public function __construct(
|
|
RegionFactory $regionFactory,
|
|
\JR\NetSuite\Helper\Data $helper,
|
|
SearchCriteriaBuilder $searchCriteriaBuilder,
|
|
CustomerMapper $customerMapper,
|
|
LocationRepositoryInterface $locationRepository,
|
|
OrderRepositoryInterface $orderRepository,
|
|
ProductRepositoryInterface $productRepository,
|
|
Product $productModel,
|
|
GroupRepositoryInterface $groupRepository,
|
|
TaxRateRepositoryInterface $taxRateRepository
|
|
) {
|
|
$this->regionFactory = $regionFactory;
|
|
$this->helper = $helper;
|
|
$this->storeId = $this->helper->getDefaultStoreId();
|
|
$this->searchCriteriaBuilder = $searchCriteriaBuilder;
|
|
$this->customerMapper = $customerMapper;
|
|
$this->locationRepository = $locationRepository;
|
|
$this->orderRepository = $orderRepository;
|
|
$this->productRepository = $productRepository;
|
|
$this->productModel = $productModel;
|
|
$this->groupRepository = $groupRepository;
|
|
$this->taxRateRepository = $taxRateRepository;
|
|
}
|
|
|
|
/**
|
|
* Convert data from NetSuite format to Magento format.
|
|
*
|
|
* @param array $netSuiteData
|
|
* @return array
|
|
*/
|
|
public function convertToMagentoFormat($netSuiteData)
|
|
{
|
|
// convert netsuite data to magento data
|
|
$magentoData = [];
|
|
foreach ($netSuiteData as $data) {
|
|
$convertedData = [
|
|
'netsuite_order_location_id' => $data['location'],
|
|
'netsuite_order_id' => $data['internalid'],
|
|
'netsuite_order_number' => $data['tranid'],
|
|
'netsuite_customer_id' => $data['entity'],
|
|
'store_id' => $this->storeId,
|
|
'email' => $data['email'],
|
|
'company' => $data['companyname'],
|
|
'status' => self::ORDER_STATUS_MAPPER[$data['status']] ?? 'undefined',
|
|
'total' => $data['total'],
|
|
'amount' => $data['amount'],
|
|
'tax' => $data['taxtotal'],
|
|
'shipping_amount' => $data['shippingcost'],
|
|
'shipping_description' => $data['shipcarrier'],
|
|
'shipping_method' => $data['shipmethod'],
|
|
'purchase_date' => $data['datecreated'],
|
|
'last_modified_date' => $data['lastmodifieddate'],
|
|
'other_ref_num' => $data['otherrefnum'],
|
|
];
|
|
|
|
if ($data['customer'] ?? false) {
|
|
$convertedData['customer'] = $this->customerMapper->convertToMagentoFormat([$data['customer']]);
|
|
$convertedData['customer'] = reset($convertedData['customer']);
|
|
}
|
|
|
|
// billing address
|
|
if ($data['billaddress'] != '') {
|
|
$billaddressee = $this->filterFirstNonEmpty([
|
|
$data['billattention'],
|
|
$data['billaddressee'],
|
|
]);
|
|
if (empty($billaddressee)) {
|
|
$billaddressee = $this->getNameFromAddressText($data['billaddress']);
|
|
}
|
|
$convertedData['billing_address'] = [
|
|
'firstname' => $convertedData['customer']['billing_address']['firstname'] ??
|
|
$convertedData['customer']['shipping_address']['firstname'] ??
|
|
(count($billaddressee) > 0 ? array_shift($billaddressee) : ''),
|
|
'lastname' => $convertedData['customer']['billing_address']['lastname'] ??
|
|
$convertedData['customer']['shipping_address']['lastname'] ??
|
|
(count($billaddressee) > 0 ? implode(' ', $billaddressee) : $data['companyname']),
|
|
'company' => $data['companyname'],
|
|
'street' => [!empty($data['billaddress1']) ? $data['billaddress1'] : '-', $data['billaddress2']],
|
|
'city' => $data['billcity'],
|
|
'country_id' => $data['billcountry'],
|
|
'region_id' => $this->getRegion($data['billcountry'], $data['billstate'])->getId(),
|
|
'postcode' => !empty($data['billzip']) ? $data['billzip'] : '-' ,
|
|
'telephone' => $data['billphone'],
|
|
'fax' => '',
|
|
'save_in_address_book' => 0
|
|
];
|
|
}
|
|
|
|
if ($data['shipaddress'] != '') {
|
|
$shipaddressee = $this->filterFirstNonEmpty([
|
|
$data['shipattention'],
|
|
$data['shipaddressee'],
|
|
]);
|
|
if (empty($shipaddressee)) {
|
|
$shipaddressee = $this->getNameFromAddressText($data['shipaddress']);
|
|
}
|
|
$convertedData['shipping_address'] = [
|
|
'firstname' => $convertedData['customer']['shipping_address']['firstname'] ??
|
|
$convertedData['customer']['billing_address']['firstname'] ??
|
|
(count($shipaddressee) > 0 ? array_shift($shipaddressee) : ''),
|
|
'lastname' => $convertedData['customer']['shipping_address']['lastname'] ??
|
|
$convertedData['customer']['billing_address']['lastname'] ??
|
|
(count($shipaddressee) > 0 ? implode(' ', $shipaddressee) : $data['companyname']),
|
|
'company' => $data['companyname'],
|
|
'street' => [!empty($data['shipaddress1']) ? $data['shipaddress1'] : '-', $data['shipaddress2']],
|
|
'city' => $data['shipcity'],
|
|
'country_id' => $data['shipcountry'],
|
|
'region_id' => $this->getRegion($data['shipcountry'], $data['shipstate'])->getId(),
|
|
'postcode' => $data['shipzip'],
|
|
'telephone' => $data['shipphone'],
|
|
'fax' => '',
|
|
'save_in_address_book' => 0
|
|
];
|
|
|
|
if (
|
|
isset($convertedData['shipping_address'])
|
|
&& !empty($convertedData['shipping_address']['firstname'])
|
|
&& ($convertedData['shipping_address']['firstname'][0] ?? '' == $convertedData['shipping_address']['street'][0][0] ?? '')
|
|
&& isset($convertedData['billing_address'])
|
|
) {
|
|
$convertedData['shipping_address']['firstname'] = $convertedData['billing_address']['firstname'];
|
|
$convertedData['shipping_address']['lastname'] = $convertedData['billing_address']['lastname'];
|
|
}
|
|
}
|
|
|
|
// item
|
|
$convertedData['items'] = [];
|
|
foreach ($data['items'] as $item) {
|
|
$convertedData['items'][] = [
|
|
'netsuite_order_item_id' => $item['itemId'],
|
|
'sku' => $item['itemText'],
|
|
'description' => $item['description'],
|
|
'qty' => $item['quantity'],
|
|
'type' => $item['type'],
|
|
'price' => $item['rate'],
|
|
'amount' => $item['amount'],
|
|
];
|
|
}
|
|
|
|
// payment
|
|
$convertedData['payment'] = [
|
|
'method' => $data['paymentmethod'],
|
|
'paid_amount' => $data['paidamount'],
|
|
'date_paid' => $data['paymenteventdate'],
|
|
'status' => $data['paymenteventresult'],
|
|
'type' => $data['paymenteventtype'],
|
|
// 'cc_type' => '',
|
|
// 'cc_number_enc' => '',
|
|
// 'cc_last4' => '',
|
|
// 'cc_owner' => '',
|
|
// 'cc_exp_month' => '',
|
|
// 'cc_exp_year' => '',
|
|
// 'cc_ss_start_month' => '',
|
|
// 'cc_ss_start_year' => '',
|
|
// 'cc_ss_issue' => '',
|
|
];
|
|
|
|
$magentoData[] = $convertedData;
|
|
}
|
|
return $magentoData;
|
|
}
|
|
|
|
/**
|
|
* Convert data from Magento format to NetSuite format.
|
|
*
|
|
* @param \Magento\Sales\Model\Order $order
|
|
* @return array
|
|
*/
|
|
public function convertToNetSuiteFormat($order)
|
|
{
|
|
$data = $this->initializeData($order);
|
|
$data['items'] = $this->getOrderItems($order);
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Initialize variables.
|
|
*
|
|
* @param \Magento\Sales\Model\Order $order
|
|
* @param string $netsuiteCustomerId
|
|
* @return array
|
|
*/
|
|
protected function initializeData($order, $netsuiteCustomerId = '')
|
|
{
|
|
$shippingAddress = $order->getShippingAddress();
|
|
$billingAddress = $order->getBillingAddress();
|
|
$phone = preg_replace('/[^0-9]/', '', $shippingAddress->getTelephone());
|
|
if (strlen($phone) == 10) {
|
|
$phone = '0001' . $phone;
|
|
}
|
|
$phone = str_pad($phone, 14, '0', STR_PAD_LEFT);
|
|
try {
|
|
$region = $this->regionFactory->create();
|
|
$region->loadByName($shippingAddress->getRegion(), $shippingAddress->getCountryId());
|
|
} catch (\Exception $e) {
|
|
$region = null;
|
|
}
|
|
|
|
$address = [
|
|
'country' => $shippingAddress->getCountryId(),
|
|
'city' => $shippingAddress->getCity(),
|
|
'state' => $region?->getCode() ?? '',
|
|
'zip' => $shippingAddress->getPostcode(),
|
|
'addressee' => $shippingAddress->getCompany(),
|
|
'attention' => $shippingAddress->getFirstname() . ' ' . $shippingAddress->getLastname(),
|
|
'addrphone' => $phone,
|
|
'addr1' => $shippingAddress->getStreetLine(1),
|
|
'addr2' => $shippingAddress->getStreetLine(2),
|
|
];
|
|
$payment = $order->getPayment();
|
|
$cardInfo = [
|
|
'Card #' => '******' . $payment->getCcLast4(),
|
|
'Billing address' => implode(', ', array_filter($billingAddress->getStreet())) . ', ' . $billingAddress->getCity() . ', ' . $billingAddress->getRegionCode() . ' ' . $billingAddress->getPostcode() . ', ' . $billingAddress->getCountryId(),
|
|
'Card Holder Name' => $billingAddress->getFirstname() . ' ' . $billingAddress->getLastname(),
|
|
];
|
|
$cardInfo = implode("\n", array_map(function ($key, $value) {
|
|
return $key . ': ' . $value;
|
|
}, array_keys($cardInfo), $cardInfo));
|
|
$phone = preg_replace('/[^0-9]/', '', $shippingAddress->getTelephone() ?? '');
|
|
$phone = substr($phone, strlen($phone) - 10, 10);
|
|
|
|
// get location id based on shipping address
|
|
$locationId = $this->getLocationId($shippingAddress->getCity());
|
|
|
|
if (!$locationId) {
|
|
$locationId = $this->getLocationId($region?->getCode() ?? '');
|
|
}
|
|
|
|
$vars = [
|
|
'note' => $order->getCustomerNote() ?? '',
|
|
'address' => $address,
|
|
'orderNo' => $order->getIncrementId(),
|
|
'cardInfo' => $cardInfo,
|
|
'terms' => '12',
|
|
'phone' => $phone,
|
|
'custId' => $netsuiteCustomerId, // TODO: get customer ID from NetSuite
|
|
'company' => $order->getBillingAddress()->getCompany(),
|
|
'location' => '222', // TODO: get location from config
|
|
'shipmethod' => self::SHIP_METHOD_MAPPER[$order->getShippingMethod()],
|
|
'shippingcost' => $order->getShippingAmount(),
|
|
// TODO: get entity from NetSuite
|
|
// 'entity' => $netsuiteEntityId,
|
|
'email' => $order->getCustomerEmail(),
|
|
// 'customer' => $this->customerMapper->convertToNetSuiteFormatByEmail($order->getCustomerEmail()),
|
|
];
|
|
if ($order->getDiscountAmount() < 0) {
|
|
$vars['discountItem'] = $order->getDiscountAmount();
|
|
$vars['discountRate'] = round(($order->getDiscountAmount() / $order->getGrandTotal()) * 100, 2);
|
|
}
|
|
return $vars;
|
|
}
|
|
|
|
/**
|
|
* search location list by name
|
|
*/
|
|
protected function getLocationId($name)
|
|
{
|
|
$searchCriteria = $this->searchCriteriaBuilder
|
|
->addFilter('is_active', 1)
|
|
->addFilter('name', $name)
|
|
->create();
|
|
|
|
$locations = $this->locationRepository->getList($searchCriteria)->getItems();
|
|
if (count($locations) > 0) {
|
|
$location = reset($locations);
|
|
return $location->getLocationId() ?? '';
|
|
}
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* get order by field and value.
|
|
*
|
|
* @param string $field
|
|
* @return \Magento\Sales\Model\Order | null
|
|
*/
|
|
public function getOrderBy($field, $value)
|
|
{
|
|
$searchCriteria = $this->searchCriteriaBuilder
|
|
->addFilter($field, $value)
|
|
->create();
|
|
|
|
$orders = $this->orderRepository->getList($searchCriteria)->getItems();
|
|
$order = count($orders) > 0 ? reset($orders) : null;
|
|
return $order;
|
|
}
|
|
|
|
/**
|
|
* Get order items.
|
|
*
|
|
* @param \Magento\Sales\Model\Order $order
|
|
* @return array
|
|
*/
|
|
protected function getOrderItems($order)
|
|
{
|
|
$items = [];
|
|
foreach ($order->getAllItems() as $item) {
|
|
$product = $this->productModel->load($item->getProductId());
|
|
$items[] = [
|
|
'id' => $product->getCustomAttribute('netsuite_id')?->getValue() ?? '',
|
|
'quantity' => $item->getQtyOrdered(),
|
|
'rate' => $item->getPrice(),
|
|
];
|
|
}
|
|
|
|
return $items;
|
|
}
|
|
|
|
/**
|
|
* filter first non-empty value from array and return as array.
|
|
*
|
|
* @param array $array
|
|
* @return array
|
|
*/
|
|
protected function filterFirstNonEmpty($array)
|
|
{
|
|
foreach ($array as $value) {
|
|
if ($value != '') {
|
|
$value = preg_replace('/[^\p{L}\p{M}\,\-\_\.\'\s\d]/', ' ', $value);
|
|
$value = explode(' ', $value);
|
|
return $value;
|
|
}
|
|
}
|
|
return [];
|
|
}
|
|
|
|
/**
|
|
* Get name from address text.
|
|
*
|
|
* @param string $address
|
|
* @return array
|
|
*/
|
|
protected function getNameFromAddressText($address)
|
|
{
|
|
if (empty($address)) {
|
|
return [];
|
|
}
|
|
return $this->filterFirstNonEmpty(explode("\n", $address));
|
|
}
|
|
|
|
/**
|
|
* Get region by country code and region code.
|
|
*
|
|
* @param string $countryCode
|
|
* @param string $regionCode
|
|
* @return \Magento\Directory\Model\Region
|
|
*/
|
|
protected function getRegion($countryCode, $regionCode)
|
|
{
|
|
try {
|
|
$region = $this->regionFactory->create();
|
|
$region->loadByCode($regionCode, $countryCode);
|
|
return $region;
|
|
} catch (\Exception $e) {
|
|
throw new LocalizedException(__("Error fetching region ID: %1", $e->getMessage()));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Convert a given NetSuite shipping method to a corresponding Magento shipping method.
|
|
* If the given NetSuite shipping method does not exist in the mapping, 'flatrate_flatrate'
|
|
* will be returned.
|
|
*
|
|
* @param string $nsShipMethod NetSuite ship method internal ID.
|
|
* @return string
|
|
*/
|
|
public function parseNetSuiteShippingMethod($nsShipMethod)
|
|
{
|
|
if (array_key_exists($nsShipMethod, self::SHIP_METHOD_MAPPER_R)) {
|
|
return self::SHIP_METHOD_MAPPER_R[$nsShipMethod];
|
|
}
|
|
return 'flatrate_flatrate';
|
|
}
|
|
|
|
}
|
|
|