'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'; } }