├── .DS_Store ├── .gitignore ├── LICENSE ├── README.md ├── composer.json ├── config └── linnworks.php ├── phpunit.xml.dist ├── src ├── Api │ ├── ApiClient.php │ ├── Auth.php │ ├── Inventory.php │ ├── Locations.php │ ├── OrderItems.php │ ├── Orders.php │ ├── Permissions.php │ ├── Picking.php │ ├── PostalServices.php │ ├── PrintService.php │ ├── PrintZone.php │ ├── ReturnsRefunds.php │ ├── ShippingService.php │ └── Stock.php ├── Exceptions │ ├── LinnworksAuthenticationException.php │ └── LinnworksResponseCouldNotBeParsed.php ├── Linnworks.php ├── LinnworksFacade.php └── LinnworksServiceProvider.php └── tests ├── LinnworksTest.php └── stubs └── AuthorizeByApplication.json /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnpatel91/linnworks/f6883e7604bb44028cf1f5b10c0d1147ce9a6781/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | composer.lock 3 | docs 4 | vendor 5 | coverage 6 | .phpunit.result.cache -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 onfuro 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # linnworks 2 | 3 | ## About 4 | 5 | Wrapper for the Linnworks API, as documented at [http://apps.linnworks.net/Api](http://apps.linnworks.net/Api) 6 | 7 | ## Install 8 | 9 | composer require onfuro/linnworks 10 | 11 | ## Usage 12 | 13 | $linnworks = Linnworks::make('applicationId', 'applicationSecret', 'token'); 14 | $orders = $linnworks->Orders()->getOpenOrders( 15 | 25, 16 | 1, 17 | null, 18 | null, 19 | 'e41b4701-0885-430d-9623-d840d9d46dd6', 20 | null); 21 | 22 | ## Depriciated SDK 23 | 24 | Only some of the API has been created. The depreciated SKD folder contains a wide range of the potential API calls. -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "onfuro/linnworks", 3 | "description": "Linnworks API wrapper", 4 | "license": "MIT", 5 | "authors": [ 6 | { 7 | "name": "Onfuro", 8 | "email": "parth@onfuro.com" 9 | } 10 | ], 11 | "require": { 12 | "guzzlehttp/guzzle": "^6.3" 13 | }, 14 | "require-dev": { 15 | "phpunit/phpunit": "^9.0" 16 | }, 17 | "autoload": { 18 | "psr-4": { 19 | "Onfuro\\Linnworks\\": "src" 20 | } 21 | }, 22 | "autoload-dev": { 23 | "psr-4": { 24 | "Onfuro\\Linnworks\\Tests\\": "tests" 25 | } 26 | }, 27 | "extra": { 28 | "laravel": { 29 | "providers": [ 30 | "Onfuro\\Linnworks\\LinnworksServiceProvider" 31 | ], 32 | "aliases": { 33 | "Linnworks": "Onfuro\\Linnworks\\LinnworksFacade" 34 | } 35 | } 36 | }, 37 | "minimum-stability": "dev", 38 | "prefer-stable": true 39 | } 40 | -------------------------------------------------------------------------------- /config/linnworks.php: -------------------------------------------------------------------------------- 1 | env('LINNWORKS_APP_ID'), 5 | 'applicationSecret' => env('LINNWORKS_SECRET'), 6 | 'token' => env('LINNWORKS_TOKEN'), 7 | 'timeout' => null 8 | ]; -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | tests 15 | 16 | 17 | 18 | 19 | src/ 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/Api/ApiClient.php: -------------------------------------------------------------------------------- 1 | client = $client; 23 | $this->server = $server; 24 | $this->bearer = $bearer; 25 | } 26 | 27 | public function get($url = null, array $parameters = []): array 28 | { 29 | return $this->parse(function() use($url, $parameters){ 30 | return $this->client->get($this->server.$url, [ 31 | 'form_params' => $parameters, 32 | 'headers' => [ 33 | 'Content-Type' => 'application/x-www-form-urlencoded', 34 | 'Authorization' => $this->bearer 35 | ] 36 | ]); 37 | }); 38 | } 39 | 40 | public function post($url = null, array $parameters = []): array 41 | { 42 | return $this->parse(function() use($url, $parameters){ 43 | return $this->client->post($this->server.$url, [ 44 | 'form_params' => $parameters, 45 | 'headers' => [ 46 | 'Content-Type' => 'application/x-www-form-urlencoded', 47 | 'Accept' => 'application/json', 48 | 'Authorization' => $this->bearer ?? '' 49 | ] 50 | ]); 51 | }); 52 | } 53 | 54 | public function post_int($url = null, array $parameters = []): int 55 | { 56 | return $this->parse(function() use($url, $parameters){ 57 | return $this->client->post($this->server.$url, [ 58 | 'form_params' => $parameters, 59 | 'headers' => [ 60 | 'Content-Type' => 'application/x-www-form-urlencoded', 61 | 'Accept' => 'application/json', 62 | 'Authorization' => $this->bearer ?? '' 63 | ] 64 | ]); 65 | }); 66 | } 67 | 68 | private function parse(callable $callback) 69 | { 70 | $response = call_user_func($callback); 71 | 72 | $json = json_decode((string) $response->getBody(), true); 73 | 74 | if(json_last_error() !== JSON_ERROR_NONE){ 75 | throw new LinnworksResponseCouldNotBeParsed((string) $response->getBody()); 76 | } 77 | 78 | return $json; 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/Api/Auth.php: -------------------------------------------------------------------------------- 1 | get('Auth/AuthorizeByApplication', $params); 10 | } 11 | } -------------------------------------------------------------------------------- /src/Api/Inventory.php: -------------------------------------------------------------------------------- 1 | post('Inventory/GetInventoryItems', [ 11 | "view" => json_encode($view), 12 | "stockLocationIds" => $stockLocationIds, 13 | "startIndex" => $startIndex, 14 | "itemsCount" => $itemsCount 15 | ]); 16 | } 17 | 18 | public function GetInventoryItemById(string $StockItemId = "") 19 | { 20 | return $this->post('Inventory/GetInventoryItemById', [ 21 | "id" => $StockItemId 22 | ]); 23 | } 24 | 25 | public function GetStockItemIdsBySKU(string $SKUS = '{"SKUS":""}') 26 | { 27 | return $this->post('Inventory/GetStockItemIdsBySKU', [ 28 | "request" => $SKUS 29 | ]); 30 | } 31 | 32 | 33 | 34 | public function GetInventoryItemImages(string $inventoryItemId = "") 35 | { 36 | return $this->post('Inventory/GetInventoryItemImages', [ 37 | "inventoryItemId" => $inventoryItemId 38 | ]); 39 | } 40 | 41 | public function GetInventoryItemCompositions(string $inventoryItemId = "") 42 | { 43 | return $this->post('Inventory/GetInventoryItemCompositions', [ 44 | "inventoryItemId" => $inventoryItemId 45 | ]); 46 | } 47 | 48 | public function GetStockLocations() 49 | { 50 | return $this->post('Inventory/GetStockLocations', []); 51 | } 52 | 53 | public function GetPreDefinedViews() 54 | { 55 | return $this->post('Inventory/GetPreDefinedViews', []); 56 | } 57 | 58 | public function GetInventoryItemsCount(bool $includeDeleted = true, bool $includeArchived = true) 59 | { 60 | return $this->post_int('Inventory/GetInventoryItemsCount', [ 61 | "includeDeleted" => $includeDeleted, 62 | "includeArchived" => $includeArchived 63 | ]); 64 | } 65 | 66 | public function GetInventoryItemExtendedProperties(string $inventoryItemId = "",array $propertyParams = []) 67 | { 68 | return $this->post('Inventory/GetInventoryItemExtendedProperties', [ 69 | "inventoryItemId" => $inventoryItemId, 70 | "propertyParams" => json_encode($propertyParams) 71 | ]); 72 | } 73 | 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/Api/Locations.php: -------------------------------------------------------------------------------- 1 | get('Inventory/GetCountries'); 13 | } 14 | 15 | } -------------------------------------------------------------------------------- /src/Api/OrderItems.php: -------------------------------------------------------------------------------- 1 | orderItems[] = array_filter([ 28 | 'TaxCostInclusive'=> $taxCostInclusive, 29 | 'UseChannelTax'=> $useChannelTax, 30 | 'PricePerUnit'=> $pricePerUnit < 0 ? 0 : $pricePerUnit, 31 | 'Qty'=> $qty, 32 | 'TaxRate'=> $taxRate, 33 | 'LineDiscount'=> $lineDiscount, 34 | 'ItemNumber'=> $this->itemNumber++, 35 | 'ChannelSKU'=> $sku, 36 | 'IsService'=> $isService, 37 | 'ItemTitle'=> $title 38 | ], function($row){ 39 | return ! is_null($row); 40 | }); 41 | } 42 | 43 | public function getOrderItems() 44 | { 45 | return $this->orderItems; 46 | } 47 | } -------------------------------------------------------------------------------- /src/Api/Orders.php: -------------------------------------------------------------------------------- 1 | get('Orders/GetOpenOrders', [ 11 | "entriesPerPage" => $entriesPerPage, 12 | "pageNumber" => $pageNumber, 13 | "filters" => $filters, 14 | "sorting" => $sorting, 15 | "fulfilmentCenter" => $fulfilmentCenter, 16 | "additionalFilter" => $additionalFilters 17 | ]); 18 | } 19 | 20 | public function getAllOpenOrders(string $fulfilmentCenter = '00000000-0000-0000-0000-000000000000', string $filters = "", string $sorting = "[]", string $additionalFilter = "") 21 | { 22 | return $this->post('Orders/GetAllOpenOrders', [ 23 | "filters" => $filters, 24 | "sorting" => $sorting, 25 | "fulfilmentCenter" => $fulfilmentCenter, 26 | "additionalFilter" => $additionalFilter, 27 | "exactMatch" => true 28 | ]); 29 | } 30 | 31 | public function GetOrdersById(array $pkOrderIds = []) 32 | { 33 | return $this->get('Orders/GetOrdersById', [ 34 | "pkOrderIds" => json_encode($pkOrderIds) 35 | ]); 36 | } 37 | 38 | public function GetOrdersByNumOrderId(int $numOrderId = 1) 39 | { 40 | return $this->get('Orders/GetOrderDetailsByNumOrderId', [ 41 | "OrderId" => $numOrderId 42 | ]); 43 | } 44 | 45 | public function SearchProcessedOrdersPaged(int $pageNum = 1, int $numEntriesPerPage = 50, string $from = "-7200 days", string $to = "now", string $dateType = "PROCESSED", string $searchField = "", string $exactMatch = "false", string $searchTerm = "") 46 | { 47 | return $this->get('ProcessedOrders/SearchProcessedOrdersPaged', [ 48 | "from" => date('Y-m-d\TH:i:sP', strtotime($from)), 49 | "to" => date('Y-m-d\TH:i:sP', strtotime($to)), 50 | "dateType" => $dateType, 51 | "searchField" => $searchField, 52 | "exactMatch" => $exactMatch, 53 | "searchTerm" => $searchTerm, 54 | "pageNum" => $pageNum, 55 | "numEntriesPerPage" => $numEntriesPerPage, 56 | ]); 57 | } 58 | 59 | public function MoveToLocation(array $orderIds = [], string $pkStockLocationId = "") 60 | { 61 | return $this->get('Orders/MoveToLocation', [ 62 | "orderIds" => json_encode($orderIds), 63 | "pkStockLocationId" => $pkStockLocationId 64 | ]); 65 | } 66 | 67 | public function ChangeShippingMethod(array $orderIds = [], string $shippingMethod = "") 68 | { 69 | return $this->get('Orders/ChangeShippingMethod', [ 70 | "orderIds" => json_encode($orderIds), 71 | "shippingMethod" => $shippingMethod 72 | ]); 73 | } 74 | 75 | public function AssignToFolder(array $orderIds = [], string $folder = "") 76 | { 77 | 78 | return $this->get('Orders/AssignToFolder', [ 79 | "orderIds" => json_encode($orderIds), 80 | "folder" => $folder 81 | ]); 82 | } 83 | 84 | public function changeStatus(array $orderIds = [], string $status = "") 85 | { 86 | return $this->get('Orders/ChangeStatus', [ 87 | "orderIds" => json_encode($orderIds), 88 | "status" => $status 89 | ]); 90 | } 91 | 92 | public function SetLabelsPrinted(array $orderIds = []) 93 | { 94 | return $this->post('Orders/SetLabelsPrinted', [ 95 | "orderIds" => json_encode($orderIds) 96 | ]); 97 | } 98 | 99 | public function setShippingInfo(string $orderId = "", array $info = []) 100 | { 101 | return $this->post('Orders/SetOrderShippingInfo',[ 102 | 'orderId' => $orderId, 103 | 'info' => json_encode($info) 104 | ]); 105 | } 106 | 107 | public function processOrder(string $orderId = "", bool $scanPerformed = true, string $locationId = "", bool $allowZeroAndNegativeBatchQty = true) 108 | { 109 | return $this->post('Orders/ProcessOrder',[ 110 | 'orderId' => $orderId, 111 | 'scanPerformed' => $scanPerformed, 112 | 'locationId' => $locationId, 113 | 'allowZeroAndNegativeBatchQty' => $allowZeroAndNegativeBatchQty, 114 | ]); 115 | } 116 | 117 | public function processFulfilmentCentreOrder(string $orderId = "") 118 | { 119 | return $this->post('Orders/ProcessFulfilmentCentreOrder',[ 120 | 'orderId' => $orderId 121 | ]); 122 | } 123 | 124 | public function createOrder( 125 | OrderItems $orderItems, 126 | string $locationName = "", 127 | string $matchPostalServiceTag = "", 128 | string $source = "", 129 | string $subSource = "", 130 | string $referenceNumber = "", 131 | string $externalReference = "", 132 | string $receivedDate = "", 133 | string $dispatchBy = "", 134 | string $currency = "", 135 | string $channelBuyerName = "", 136 | string $deliveryEmailAddress = "", 137 | string $deliveryAddress1 = "", 138 | string $deliveryAddress2 = "", 139 | string $deliveryAddress3 = "", 140 | string $deliveryTown = "", 141 | string $deliveryRegion = "", 142 | string $deliveryPostCode = "", 143 | string $deliveryCountryName = "", 144 | string $deliveryFullName = "", 145 | string $deliveryCompanyName = "", 146 | string $deliveryPhoneNumber = "", 147 | string $deliveryIso3CountryCode = "", 148 | string $billingAddress1 = "", 149 | string $billingAddress2 = "", 150 | string $billingAddress3 = "", 151 | string $billingTown = "", 152 | string $billingRegion = "", 153 | string $billingPostCode = "", 154 | string $billingCountryName = "", 155 | string $billingFullName = "", 156 | string $billingCompanyName = "", 157 | string $billingPhoneNumber = "", 158 | string $billingIso3CountryCode = "", 159 | float $shippingCost = 0, 160 | int $shippingTaxRate = 20, 161 | string $mappingSource = "", 162 | string $orderState = "None", 163 | string $paymentStatus = "Paid", 164 | string $paidDate = "Today", 165 | array $notes = [] 166 | ){ 167 | $order = [ 168 | //minimum requirements 169 | "Source" => $source, 170 | "SubSource" => $subSource, 171 | "ReferenceNumber" => $referenceNumber, 172 | "ExternalReference" => $externalReference, 173 | "ReceivedDate" => date('c', strtotime($receivedDate)), 174 | "DispatchBy" => date('c', strtotime($dispatchBy)), 175 | // "DeliveryStartDate" => date('c', strtotime('15th January 2018')), 176 | // "DeliveryEndDate" => "2019-02-12T11:12:33.4177471+01:00", 177 | 178 | //Basic info 179 | "Currency" => $currency, //USD EUR 180 | 'ChannelBuyerName' => $channelBuyerName, 181 | 'MatchPostalServiceTag' => $matchPostalServiceTag, 182 | 'DeliveryAddress' => [ 183 | 'EmailAddress' => $deliveryEmailAddress, 184 | 'Address1' => $deliveryAddress1, 185 | 'Address2' => $deliveryAddress2, 186 | 'Address3' => $deliveryAddress3, 187 | 'Town' => $deliveryTown, 188 | 'Region' => $deliveryRegion, 189 | 'PostCode' => $deliveryPostCode, 190 | 'Country' => $deliveryCountryName, 191 | 'FullName' => $deliveryFullName, 192 | 'Company' => $deliveryCompanyName, 193 | 'PhoneNumber' => $deliveryPhoneNumber, 194 | 'MatchCountryCode' => $deliveryIso3CountryCode 195 | ], 196 | 'BillingAddress' => [ 197 | 'Address1' => $billingAddress1, 198 | 'Address2' => $billingAddress2, 199 | 'Address3' => $billingAddress3, 200 | 'Town' => $billingTown, 201 | 'Region' => $billingRegion, 202 | 'PostCode' => $billingPostCode, 203 | 'Country' => $billingCountryName, 204 | 'FullName' => $billingFullName, 205 | 'Company' => $billingCompanyName, 206 | 'PhoneNumber' => $billingPhoneNumber, 207 | 'MatchCountryCode' => $billingIso3CountryCode 208 | ], 209 | 210 | // items 211 | 'AutomaticallyLinkBySKU' => true, //if channel mapping does not work, link by sku 212 | 'MappingSource' => $mappingSource, //use mapping from another channel for SKU's 213 | 'OrderItems'=> $orderItems->getOrderItems(), 214 | 'PostalServiceCost' => $shippingCost, //cost inclusive of cost and after discount 215 | 'PostalServiceTaxRate' => $shippingTaxRate, 216 | 217 | //state 218 | 'OrderState' => $orderState, 219 | 'PaymentStatus' => $paymentStatus, 220 | 'PaidOn' => $paidDate ? date('c', strtotime($paidDate)) : null, 221 | 222 | //Notes - Particularly useful for personalised items. 223 | 'Notes' => $notes 224 | 225 | ]; 226 | 227 | // remove null 228 | $order = array_filter($order, function($row){ 229 | return ! is_null($row); 230 | }); 231 | 232 | return $this->post('Orders/CreateOrders',[ 233 | 'Location' => $locationName, 234 | 'Orders' => json_encode([$order]) 235 | ]); 236 | } 237 | 238 | public function cancelOrder(string $orderId, string $fulfilmentCenter, float $refund = 0, string $note = "" ) 239 | { 240 | return $this->post('Orders/CancelOrder',[ 241 | 'orderId' => $orderId, 242 | 'fulfilmentCenter' => $fulfilmentCenter, 243 | 'refund' => $refund, 244 | 'note' => $note, 245 | ]); 246 | } 247 | 248 | public function GetAvailableFolders() 249 | { 250 | return $this->post('Orders/GetAvailableFolders',[]); 251 | } 252 | 253 | 254 | 255 | // public function setGeneralInfo( 256 | // $guid 257 | // ){ 258 | // $order = [ 259 | //// "Status" => 1, 260 | //// "LabelPrinted" => true, 261 | //// "LabelError" => "sample string 3", 262 | //// "InvoicePrinted" => true, 263 | //// "PickListPrinted" => true, 264 | //// "IsRuleRun" => true, 265 | //// "Notes" => 7, 266 | //// "PartShipped" => true, 267 | //// "Marker" => 64, 268 | //// "IsParked" => true, 269 | //// "Identifiers" => null, 270 | //// "ReferenceNum" => "sample string 10", 271 | //// "SecondaryReference" => "sample string 11", 272 | //// "ExternalReferenceNum" => "sample string 12", 273 | //// "ReceivedDate" => "2018-10-03T11:12:33.5138145+01:00", 274 | //// "Source" => "sample string 14", 275 | //// "SubSource" => "sample string 15", 276 | //// "SiteCode" => "sample string 16", 277 | //// "HoldOrCancel" => true, 278 | //// "DespatchByDate" => "2018-10-03T11:12:33.5138145+01:00", 279 | // "ScheduledDelivery" => [ 280 | // "From" => "2019-02-12T11:12:33.5138145+01:00", 281 | // "To" => "2019-12-12T11:12:33.5138145+01:00" 282 | // ], 283 | // "HasScheduledDelivery" => true, 284 | //// "Location" => "201863fe-a0a0-4af3-8956-ac185631f82b", 285 | //// "NumItems" => 20, 286 | //// "StockAllocationType" => 0 287 | // ]; 288 | // 289 | // return $this->post('Orders/SetOrderGeneralInfo',[ 290 | // 'orderId' => $guid, 291 | // 'info' => json_encode($order), 292 | // 'wasDraft' => false, 293 | // ]); 294 | // 295 | // } 296 | 297 | } 298 | -------------------------------------------------------------------------------- /src/Api/Permissions.php: -------------------------------------------------------------------------------- 1 | get('permissions/getUsers'); 10 | } 11 | } -------------------------------------------------------------------------------- /src/Api/Picking.php: -------------------------------------------------------------------------------- 1 | get('Picking/GetAllPickingWaveHeaders', [ 10 | "State" => $State, 11 | "LocationId" => $LocationId, 12 | "DetailLevel" => $DetailLevel 13 | ]); 14 | } 15 | 16 | public function GetAllPickingWaves(int $State = null,string $LocationId = '00000000-0000-0000-0000-000000000000', string $DetailLevel = "") 17 | { 18 | return $this->get('Picking/GetAllPickingWaves', [ 19 | "State" => $State, 20 | "LocationId" => $LocationId, 21 | "DetailLevel" => $DetailLevel 22 | ]); 23 | } 24 | 25 | public function GetMyPickingWaveHeaders(int $State = null,string $LocationId = '00000000-0000-0000-0000-000000000000', string $DetailLevel = "") 26 | { 27 | return $this->get('Picking/GetMyPickingWaves', [ 28 | "State" => $State, 29 | "LocationId" => $LocationId, 30 | "DetailLevel" => $DetailLevel 31 | ]); 32 | } 33 | 34 | public function GetMyPickingWaves(int $State = null,string $LocationId = '00000000-0000-0000-0000-000000000000', string $DetailLevel = "") 35 | { 36 | return $this->get('Picking/GetMyPickingWaves', [ 37 | "State" => $State, 38 | "LocationId" => $LocationId, 39 | "DetailLevel" => $DetailLevel 40 | ]); 41 | } 42 | 43 | public function GetPickwaveUsersWithSummary(int $State = null,string $LocationId = '00000000-0000-0000-0000-000000000000', string $DetailLevel = "") 44 | { 45 | return $this->get('Picking/GetPickwaveUsersWithSummary', [ 46 | "State" => $State, 47 | "LocationId" => $LocationId, 48 | "DetailLevel" => $DetailLevel 49 | ]); 50 | } 51 | 52 | public function GetPickingWave(int $PickingWaveId = 0) 53 | { 54 | return $this->get('Picking/GetPickingWave', [ 55 | "PickingWaveId" => $PickingWaveId 56 | ]); 57 | } 58 | 59 | public function UpdatePickedItemDelta(string $Deltas = null) 60 | { 61 | return $this->post('Picking/UpdatePickedItemDelta', [ 62 | "Deltas" => $Deltas 63 | ]); 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/Api/PostalServices.php: -------------------------------------------------------------------------------- 1 | get('PostalServices/GetPostalServices'); 10 | } 11 | } -------------------------------------------------------------------------------- /src/Api/PrintService.php: -------------------------------------------------------------------------------- 1 | get('PrintService/CreatePDFfromJobForceTemplate', [ 10 | "templateType" => $templateType, 11 | "IDs" => json_encode($IDs), 12 | "templateID" => $templateID, 13 | "parameters" => $parameters, 14 | "printerName" => $printerName, 15 | "printZoneCode" => $printZoneCode, 16 | "pageStartNumber" => $pageStartNumber, 17 | "operationId" => $operationId, 18 | "context" => $context, 19 | ]); 20 | } 21 | 22 | public function CreateReturnShippingLabelsPDF(array $IDs = [], array $orderItemIdsAndQuantities = [],string $returnPostalServiceName = '') 23 | { 24 | return $this->get('PrintService/CreateReturnShippingLabelsPDF', [ 25 | "IDs" => json_encode($IDs), 26 | "orderItemIdsAndQuantities" => $orderItemIdsAndQuantities, 27 | "returnPostalServiceName" => $returnPostalServiceName, 28 | ]); 29 | } 30 | 31 | public function VP_GetPrinters() 32 | { 33 | return $this->post('PrintService/VP_GetPrinters', []); 34 | } 35 | 36 | public function GetTemplateList() 37 | { 38 | return $this->post('PrintService/GetTemplateList', []); 39 | } 40 | 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/Api/PrintZone.php: -------------------------------------------------------------------------------- 1 | post('PrintZone/GetAllPrintZones', []); 10 | } 11 | 12 | public function GetTemplateOverridesForZone(string $PrintZoneCode = 'No override') 13 | { 14 | return $this->post('PrintZone/GetTemplateOverridesForZone', [ 15 | "PrintZoneCode" => $PrintZoneCode 16 | ]); 17 | } 18 | 19 | public function UpdateTemplateOverrides(array $ToUpdate = []) 20 | { 21 | /*dd([ 22 | "ToUpdate" => [json_encode($ToUpdate)] 23 | ]);*/ 24 | return $this->post('PrintZone/UpdateTemplateOverrides', [ 25 | "ToUpdate" => [json_encode($ToUpdate)] 26 | ]); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Api/ReturnsRefunds.php: -------------------------------------------------------------------------------- 1 | get('ReturnsRefunds/GetWarehouseLocations'); 11 | } 12 | } -------------------------------------------------------------------------------- /src/Api/ShippingService.php: -------------------------------------------------------------------------------- 1 | get('ShippingService/CancelOrderShippingLabel', [ 11 | "request" => $pkOrderIds 12 | ]); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/Api/Stock.php: -------------------------------------------------------------------------------- 1 | get('Stock/GetStockConsumption', [ 10 | "stockItemId" => $stockItemId, 11 | "locationId" => $locationId, 12 | "startDate" => $startDate, 13 | "endDate" => $endDate, 14 | ]); 15 | } 16 | 17 | public function getStockItems(string $keyWord = "",string $locationId = "",int $entriesPerPage = 100, int $pageNumber = 1, bool $excludeComposites = true, bool $excludeVariations = true, bool $excludeBatches = true) 18 | { 19 | return $this->get('Stock/GetStockItems', [ 20 | "keyWord" => $keyWord, 21 | "locationId" => $locationId, 22 | "entriesPerPage" => $entriesPerPage, 23 | "pageNumber" => $pageNumber, 24 | "excludeComposites" => $excludeComposites, 25 | "excludeVariations" => $excludeVariations, 26 | "excludeBatches" => $excludeBatches, 27 | ]); 28 | } 29 | 30 | public function getStockHistory(string $stockItemId, string $locationId, int $entriesPerPage = 100, int $pageNumber = 1) 31 | { 32 | return $this->get('Stock/GetItemChangesHistory', [ 33 | "stockItemId" => $stockItemId, 34 | "locationId" => $locationId, 35 | "entriesPerPage" => $entriesPerPage, 36 | "pageNumber" => $pageNumber 37 | ]); 38 | } 39 | 40 | public function getStockItemByKey(string $locationId = "", string $key = "") : array 41 | { 42 | return $this->get('Stock/GetStockItemsByKey', [ 43 | "stockIdentifier" => json_encode([ 44 | "Key" => $key, 45 | "LocationId" => $locationId, 46 | ]), 47 | ]); 48 | } 49 | 50 | } -------------------------------------------------------------------------------- /src/Exceptions/LinnworksAuthenticationException.php: -------------------------------------------------------------------------------- 1 | client = $client ?: $this->makeClient(); 42 | $this->config = $config; 43 | 44 | if(! $this->bearer){ 45 | $this->refreshToken(); 46 | } 47 | } 48 | 49 | public static function make(array $config, GuzzleClient $client = null): self 50 | { 51 | return new static ($config, $client); 52 | } 53 | 54 | private function makeClient(): GuzzleClient 55 | { 56 | return new GuzzleClient([ 57 | 'timeout' => $this->config['timeout'] ?? 15 58 | ]); 59 | } 60 | 61 | private function refreshToken() : void 62 | { 63 | $parameters = [ 64 | "ApplicationId" => $this->config['applicationId'], 65 | "ApplicationSecret" => $this->config['applicationSecret'], 66 | "Token" => $this->config['token'] 67 | ]; 68 | 69 | $response = (new Auth($this->client, self::BASE_URI.'/api/', null)) 70 | ->AuthorizeByApplication($parameters); 71 | 72 | if(! ($response['Token'] ?? null)){ 73 | throw new LinnworksAuthenticationException($response['message'] ?? ''); 74 | } 75 | 76 | $this->bearer = $response['Token']; 77 | 78 | $this->server = $response['Server'] .'/api/'; 79 | 80 | $this->response = $response; 81 | } 82 | 83 | public function response() 84 | { 85 | return $this->response; 86 | } 87 | 88 | public function auth(): Auth 89 | { 90 | return new Auth($this->client, $this->server, $this->bearer); 91 | } 92 | 93 | public function orders(): Orders 94 | { 95 | return new Orders($this->client, $this->server, $this->bearer); 96 | } 97 | 98 | public function locations(): Locations 99 | { 100 | return new Locations($this->client, $this->server, $this->bearer); 101 | } 102 | 103 | public function postalServices(): PostalServices 104 | { 105 | return new PostalServices($this->client, $this->server, $this->bearer); 106 | } 107 | 108 | public function returnsRefunds(): ReturnsRefunds 109 | { 110 | return new ReturnsRefunds($this->client, $this->server, $this->bearer); 111 | } 112 | 113 | public function stock(): Stock 114 | { 115 | return new Stock($this->client, $this->server, $this->bearer); 116 | } 117 | 118 | public function printService(): PrintService 119 | { 120 | return new PrintService($this->client, $this->server, $this->bearer); 121 | } 122 | 123 | public function printZone(): PrintZone 124 | { 125 | return new PrintZone($this->client, $this->server, $this->bearer); 126 | } 127 | 128 | public function picking(): Picking 129 | { 130 | return new Picking($this->client, $this->server, $this->bearer); 131 | } 132 | 133 | public function shippingService(): ShippingService 134 | { 135 | return new ShippingService($this->client, $this->server, $this->bearer); 136 | } 137 | 138 | public function permissions(): Permissions 139 | { 140 | return new Permissions($this->client, $this->server, $this->bearer); 141 | } 142 | 143 | public function inventory(): Inventory 144 | { 145 | return new Inventory($this->client, $this->server, $this->bearer); 146 | } 147 | 148 | } -------------------------------------------------------------------------------- /src/LinnworksFacade.php: -------------------------------------------------------------------------------- 1 | publishes([ 17 | __DIR__.'/../config/linnworks.php' => config_path('linnworks.php'), 18 | ], 'linnworks'); 19 | } 20 | 21 | /** 22 | * Get the services provided by the provider. 23 | * This will defer loading of the service until it is requested. 24 | * 25 | * @return array 26 | */ 27 | public function provides() 28 | { 29 | return [Linnworks::class]; 30 | } 31 | 32 | /** 33 | * Register services. 34 | * 35 | * @return void 36 | */ 37 | public function register() 38 | { 39 | $this->mergeConfigFrom( __DIR__.'/../config/linnworks.php', 'linnworks'); 40 | 41 | $this->app->singleton(Linnworks::class, function ($app) { 42 | $config = $app->make('config'); 43 | 44 | $applicationId = $config->get('linnworks.applicationId'); 45 | $applicationSecret = $config->get('linnworks.applicationSecret'); 46 | $token = $config->get('linnworks.token'); 47 | 48 | return new Linnworks(compact('applicationId', 'applicationSecret', 'token')); 49 | }); 50 | 51 | $this->app->alias(Linnworks::class, 'linnworks'); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /tests/LinnworksTest.php: -------------------------------------------------------------------------------- 1 | config = [ 32 | 'applicationId' => '80003999e8-b1cc-4d62-axj5-jd883cccb481', 33 | 'applicationSecret' => '87hhhbd72-d51a-4eed-8eab-98jjdb02c1b08', 34 | 'token' => 'g89605ef47af205819b9ccc96a98c8bcf', 35 | ]; 36 | 37 | $this->mock = new MockHandler([]); 38 | 39 | $this->mock->append(new Response(200, [], 40 | file_get_contents(__DIR__.'/stubs/AuthorizeByApplication.json'))); 41 | 42 | $handlerStack = HandlerStack::create($this->mock); 43 | 44 | $this->client = new Client(['handler' => $handlerStack]); 45 | } 46 | 47 | /** @test */ 48 | public function it_can_connect_to_linnworks() 49 | { 50 | $this->mock->append(new Response(200, [], json_encode(['hello' => 'world']))); 51 | 52 | $linnworks = Linnworks::make($this->config, $this->client); 53 | 54 | $orders = $linnworks->orders()->getOpenOrders('abc'); 55 | 56 | $this->assertSame(['hello' => 'world'], $orders); 57 | } 58 | 59 | /** @test */ 60 | public function it_will_throw_an_exception_with_invalid_json() 61 | { 62 | $this->expectException(LinnworksResponseCouldNotBeParsed::class); 63 | 64 | $this->mock->append(new Response(200, [], 'abc')); 65 | 66 | $linnworks = Linnworks::make($this->config, $this->client); 67 | 68 | $linnworks->orders()->getOpenOrders('abc'); 69 | } 70 | 71 | /** @test */ 72 | public function it_will_throw_an_exception_when_not_logged_in() 73 | { 74 | $this->expectException(LinnworksAuthenticationException::class); 75 | 76 | $this->mock->reset(); 77 | 78 | $this->mock->append(new Response(200, [], json_encode(['noToken' => 'abc']))); 79 | 80 | $linnworks = Linnworks::make($this->config, $this->client); 81 | 82 | $linnworks->orders()->getOpenOrders('abc'); 83 | } 84 | 85 | 86 | } -------------------------------------------------------------------------------- /tests/stubs/AuthorizeByApplication.json: -------------------------------------------------------------------------------- 1 | { 2 | "MaxImagesPerStockItem": 0, 3 | "FullName": "A Name", 4 | "Company": "A Company", 5 | "DatabaseName": "anemailcouk", 6 | "DatabaseServer": null, 7 | "DatabaseUser": null, 8 | "DatabasePassword": null, 9 | "DseName": null, 10 | "ProductName": "Linnworks PRO", 11 | "ExpirationDate": "2019-12-07T16:09:42.94Z", 12 | "SuperAdmin": true, 13 | "SessionUserId": 1, 14 | "TTL": null, 15 | "Device": "ByApplication", 16 | "DeviceType": "ByApplication", 17 | "Server": "https://eu-ext.linnworks.net", 18 | "PushServer": "https://eu-push.linnworks.net", 19 | "Status": { 20 | "State": "AVAILABLE", 21 | "Reason": "", 22 | "Parameters": {} 23 | }, 24 | "Id": "abcd", 25 | "Email": "abc@def.com", 26 | "UserName": "user", 27 | "UserType": "Application", 28 | "Token": "abcd", 29 | "EntityId": "abcd", 30 | "GroupName": "group", 31 | "Locality": "EU", 32 | "Properties": {}, 33 | "Md5Hash": "abcd", 34 | "IsAccountHolder": true, 35 | "UserId": "abcd" 36 | } --------------------------------------------------------------------------------