- Build Status
- Installation
- Usage
- Authentication
- Environment
- Create a Work Order
- Work Order Actions and Metadata
- Publish
- Route to Provider
- Route to Group
- Approve
- Cancel
- Attach Documents
- Detach Documents
- Add a Message
- Add a Custom Field
- Add a Label
- Remove a Label
- Resolving a Closeout Requirements
- Add a Shipment
- Delete a Shipment
- Update Schedule
- Get the Entire Work Order
- Get the Status
- Get the Assigned Provider
- Get Progress
- Get Payment
- Get Messages
- Get Attached Documents
- Get Shipments
- Get Your Work Orders
- Get Your Projects
- Get Your Documents
- Convert a Tracking Number to a Shipping ID
- Using Your Classes
- Contributing
- Changelog
- License
- PHP 5.6+
- PHP 7+
- HHVM
Your php runtime needs to have Soap enabled. Follow the installation instructions for enabling the Soap module.
Require the fieldnation/fieldnation-sdk package in your project.
$ composer require fieldnation/fieldnation-sdkThe key concept to successfully integrating with Field Nation is describing how your business objects become Field Nation objects. We provide interfaces for describing how your data can be created on Field Nation.
To use the SDK you need to generate an API key for your company.
You can create an API key at https://app.fieldnation.com/api. Once you have a key you can create a connection.
<?php
$fnCompanyId = $_ENV['FIELD_NATION_COMPANY_ID'];
$fnApiKey = $_ENV['FIELD_NATION_API_KEY'];
$fnEffectiveUser = $_ENV['FIELD_NATION_EFFECTIVE_USER']; // optional - First admin user will be used if not provided.
$credentials = new \FieldNation\LoginCredentials($fnCompanyId, $fnApiKey, $fnEffectiveUser);
$fn = new \FieldNation\SDK($credentials);The default environment for the SDK is set to production. If you would like to use another environment for testing you can do so by setting the value on the credentials object.
<?php
$fnCompanyId = $_ENV['FIELD_NATION_COMPANY_ID'];
$fnApiKey = $_ENV['FIELD_NATION_API_KEY'];
$credentials = new \FieldNation\LoginCredentials($fnCompanyId, $fnApiKey, $fnEffectiveUser);
$credentials->setEnvironment("https://stable.fieldnation.com");
$fn = new \FieldNation\SDK($credentials);First, let's create a simple example of what your data model might look like.
<?php
class MyBusinessTicket
{
private $id;
private $title;
private $description;
private $startDateTime;
// ... setters and getters for the properties
}Now that we have our business logic in our MyBusinessTicket, how can we start creating work orders
on Field Nation? Simple -- we update MyBusinessTicket to implement FieldNation\WorkOrderSerializerInterface
(or better yet, create a new class MyFieldNationBusinessTicket that extends MyBusinessTicket and implements
FieldNation\WorkOrderSerializerInterface).
<?php
use FieldNation\WorkOrderSerializerInterface;
class MyBusinessTicket implements WorkOrderSerializerInterface
{
private $id;
private $title;
private $description;
private $startDateTime;
private $fieldNationId;
// ... setters and getters for the properties
// WorkOrderSerializerInterface methods
/**
* Get the name of the project the work order should be a member of.
*
* If not present, the work order will not belong to a project (default behavior).
* If the project does not already exist, it will be created automatically and your effectiveUser
* (See Login structure) will be the default manager for all newly-created projects.
*
* @return string
*/
public function getGroup()
{
return NULL;
}
/**
* Get the general descriptive information relevant to the job.
*
* @return FieldNation\ServiceDescriptionInterface
*/
public function getDescription()
{
$description = new \FieldNation\ServiceDescription();
$description->setCategoryId(1); // See docs for category IDs
$description->setTitle($this->title);
$description->setDescription($this->description);
return $description;
}
/**
* Get where the job site is located.
*
* @return FieldNation\ServiceLocationInterface
*/
public function getLocation()
{
// My business only has one service location
$locationType = \FieldNation\LocationTypes::COMMERCIAL;
$location = new \FieldNation\ServiceLocation();
$location->setType($locationType);
$location->setName('Foo Co');
$location->setAddress1('123 Main Street');
$location->setCity('Minneapolis');
$location->setState('MN');
$location->setPostalCode('55402');
$location->setCountry('US');
$location->setContactName('Bob');
$location->setContactEmail('bob@mybusiness.com');
$location->setContactPhone('612-555-3485');
return $location;
}
/**
* Get scheduling information for the Work Order, including any applicable end time.
*
* @return FieldNation\TimeRangeInterface
*/
public function getStartTime()
{
$time = new \FieldNation\TimeRange();
$time->setTimeBegin($this->startDateTime);
return $time;
}
/**
* Get payment details to be advertised on the Work Order.
* @return FieldNation\PayInfoInterface
*/
public function getPayInfo()
{
// All of our tickets are a flat $20 rate with a 5 hour max
$info = new \FieldNation\PayInfo();
$rate = new \FieldNation\RatePay();
$rate->setRate(20.0);
$rate->setMaxUnits(5.0);
$info->setPerHour($rate);
return $info;
}
/**
* Get whether to allow the technician to upload files to the Work Order.
* If enabled, this Work Order will inherit required items from the project
* it belongs to or settings your company has configured for all Work Orders.
*
* @return boolean
*/
public function getAllowTechUploads()
{
// We always allow
return true;
}
/**
* Get whether to email any providers about the Work Order.
* Typical usage is true and should only be disabled in certain circumstances.
*
* @return boolean
*/
public function getWillAlertWhenPublished()
{
// Always alert providers
return true;
}
/**
* Get whether to grant technician access to a print-friendly version of work order details.
* It is strongly recommended this is set to true. This should only be set false
* if you will be attaching a printable version as a document.
*
* @return boolean
*/
public function getIsPrintable()
{
// Always allow to print
return true;
}
/**
* Get additional fields (custom fields) and values provided by your company for the Work Order.
*
* @return FieldNation\AdditionalFieldInterface[]
*/
public function getAdditionalFields()
{
// Add my 'TicketId' custom field to all work orders
$ticketId = new \FieldNation\AdditionalField();
$ticketId->setName('Ticket ID');
$ticketId->setValue($this->id);
return array($ticketId);
}
/**
* Get labels that are set on the work order.
*
* @return string[]
*/
public function getLabels()
{
return array();
}
/**
* Get a list of requirements to be met before the Work Order is able to be marked Work Done.
* Currently only configured and satisfied via the API. Should be NULL if not configured.
*
* @return array|NULL
*/
public function getCloseoutRequirements()
{
return NULL;
}
}Let's take a second to break down what we added.
These methods are a requirement of the WorkOrderSerializerInterface. This interface describes how your data
translates to Field Nation data, and is a requirement of the SDK for creating work orders.
Each method is documented with what it does, and what the return type should be. For more information about
the types see the interfaces section.
There are a number of methods that require you to map your data to a type that is provided by the SDK.
Let's use getPayInfo as an example. Field Nation requires a specific schema for describing the pay info for a work order.
Because we don't know how you pay technicians, we provide an interface, PayInfoInterface, for translating your
pay structure into a pay structure that we understand. The PayInfoInterface acts as container for the potential pay
types -- FixedPayInterface, RatePayInterface, and BlendedPayInterface. Each of these pay
interfaces also has a concrete class provided with the SDK (FixedPay, RatePay, BlendedPay).
You can use the classes that we've implemented to translate your pay structure into a Field Nation pay structure, or you
could create your own concrete classes that implement the provided interfaces.
Now that we understand how our business objects translate to Field Nation business objects we can create a work order.
<?php
$fnCompanyId = $_ENV['FIELD_NATION_COMPANY_ID'];
$fnApiKey = $_ENV['FIELD_NATION_API_KEY'];
$fnEffectiveUser = $_ENV['FIELD_NATION_EFFECTIVE_USER'];
$credentials = new \FieldNation\LoginCredentials($fnCompanyId, $fnApiKey, $fnEffectiveUser);
$fn = new \FieldNation\SDK($credentials);
$myTicket = new MyBusinessTicket();
$myTicket->setId(uniqid());
$myTicket->setTitle('Fix something at this place');
$myTicket->setDescription('Something went wrong. Fix it.');
$myTicket->setStartDateTime(new \DateTime());
// Returns a \FieldNation\WorkOrderInterface object
$fnWorkOrder = $fn->createWorkOrder($myTicket);
$myTicket->setFieldNationId($fnWorkOrder->getId());Updating a work order is similar to creating a work order, but there are granular actions that you can make on a work order instance.
There are 2 ways of getting a Field Nation work order object.
- If you just created one through the SDK::createWorkOrder method
- If you called the SDK::getExistingWorkOrder method.
<?php
// Just created a work order
$fnCompanyId = $_ENV['FIELD_NATION_COMPANY_ID'];
$fnApiKey = $_ENV['FIELD_NATION_API_KEY'];
$fnEffectiveUser = $_ENV['FIELD_NATION_EFFECTIVE_USER'];
$credentials = new \FieldNation\LoginCredentials($fnCompanyId, $fnApiKey, $fnEffectiveUser);
$fn = new \FieldNation\SDK($credentials);
$myTicket = new MyBusinessTicket();
$myTicket->setId(uniqid());
$myTicket->setTitle('Fix something at this place');
$myTicket->setDescription('Something went wrong. Fix it.');
$myTicket->setStartDateTime(new \DateTime());
// Returns a \FieldNation\WorkOrderInterface object
$fnWorkOrder = $fn->createWorkOrder($myTicket);
$myTicket->setFieldNationId($fnWorkOrder->getId());
// Fetching a work order after it was created
$ticket = $db->getTicket(1234); // pseudo code for fetching your ticket
$fnWorkOrder = $fn->getExistingWorkOrder($ticket->fnId);Now that you have a Field Nation work order instance you can execute actions or get metadata about it.
Publish your work order on Field Nation
/**
* @returns ResultInterface
*/
$result = $fnWorkOrder->publish();Route your work order to a Provider. When creating a Provider object you need to set the Provider ID so it will be properly routed.
/**
* Create a provider object to route to
*/
$provider = new \FieldNation\Technician();
$provider->setId('1');
/**
* @returns ResultInterface
*/
$result = $fnWorkOrder->routeTo($provider);Route your work order to a Group. When creating a Group object you need to set the Group ID so it will be properly routed.
/**
* Create a group to route to
*/
$group = new \FieldNation\Group();
$group->setId('100');
/**
* @returns ResultInterface
*/
$result = $fnWorkOrder->routeTo($group);Approve your work order.
/**
* @returns ResultInterface
*/
$result = $fnWorkOrder->approve();Sometimes you need to cancel your work order and start over.
/**
* @returns ResultInterface
*/
$result = $fnWorkOrder->cancel();Attach a document to your work order.
/**
* Create your document.
*/
$document = new \FieldNation\Document();
$document->setTitle('Instructions');
$document->setType('application/pdf');
$document->setUpdateTime(new \DateTime('now', new \DateTimeZone('UTC')));
/**
* @returns ResultInterface
*/
$result = $fnWorkOrder->attach($document);Remove a document from your work order.
/**
* Create the document object
*/
$document = new \FieldNation\Document();
$document->setTitle('Instructions');
/**
* @returns ResultInterface
*/
$result = $fnWorkOrder->detach($document);Your work orders are yours. Make them yours by adding a custom field.
/**
* One of your custom fields
*/
$field = new \FieldNation\CustomField();
$field->setName('My Business Ticket ID');
/**
* @returns ResultInterface
*/
$result = $fnWorkOrder->addAdditionalField($field);Add labels to your work order.
/**
* Create a label
*/
$label = new \FieldNation\Label();
$label->setName('New Work');
$label->setHideFromTech(true);
/**
* @returns ResultInterface
*/
$result = $fnWorkOrder->addLabel($label);Remove a label from your work order.
/**
* Create a label
*/
$label = new \FieldNation\Label();
$label->setName('New Work');
/**
* @returns ResultInterface
*/
$result = $fnWorkOrder->removeLabel($label);Mark a closeout requirement as resolved.
/**
* Closeout Requirement
*/
$requirement = new \FieldNation\CloseoutRequirement();
$requirement->setName('Provider upload picture');
/**
* @returns ResultInterface
*/
$result = $fnWorkOrder->resolveCloseoutRequirement($requirement);Add a shipment for Field Nation to track to your work order.
/**
* Shipment
*/
$shipment = new \FieldNation\Shipment();
$shipment->setVendor('USPS');
$shipment->setTrackingId('my-tracking-number');
/**
* @returns ResultInterface
*/
$result = $fnWorkOrder->addShipment($shipment);If a shipment no longer needs to be tracked delete it from your work order.
// If you don't have the Field Nation shipment id, you can get it with your tracking number
$result = $fn->getShippingIdFrom('my-tracking-number');
$shipment = new \FieldNation\Shipment();
$shipment->setId($result->getShipmentId());
/**
* @returns ResultInterface
*/
$result = $fnWorkOrder->deleteShipment($shipment);Things change -- update the schedule for your work order.
/**
* Create a TimRange object
*/
$schedule = new TimeRange();
$schedule->setTimeBegin(new \DateTime('now'));
$schedule->setTimeEnd(new \DateTime('now'));
/**
* @returns ResultInterface
*/
$result = $fnWorkOrder->updateSchedule($schedule);Occasionally it makes sense to get the entire work order. Here's how!
If you need something specific you should be calling that getter directly. This action is expensive so use it when you really need it.
/**
* @returns WorkOrderInterface
*/
$fnWorkOrder = $fnWorkOrder->get(); // get all metadata and reassign the existing variableGet the status of your work order
/**
* @returns string from \FieldNation\WorkOrderStatuses
*/
$status = $fnWorkOrder->getStatus();Get the information about the Provider that is assigned to your work order.
/**
* @returns \FieldNation\TechnicianInterface
*/
$provider = $fnWorkOrder->getAssignedProvider();Get the progress of your work order.
/**
* @returns \FieldNation\ProgressInterface
*/
$progress = $fnWorkOrder->getProgress();Get the payment information about your work order.
/**
* @returns \FieldNation\PaymentInterface
*/
$payment = $fnWorkOrder->getPayment();Get the messages on your work order
/**
* @returns \FieldNation\MessageInterface[]
*/
$messages = $fnWorkOrder->getMessages();Get the attached documents on your work order.
/**
* @returns from \FieldNation\Document[]
*/
$documents = $fnWorkOrder->getAttachedDocuments();Get the tracked shipments for your work order.
/**
* @returns \FieldNation\Shipment[]
*/
$shipments = $fnWorkOrder->getShipments();Sometimes you need to get all of your work orders. Here's how!
/**
* Optionally you can filter your query by the status of the work order.
* If the status is NULL we'll return all work orders.
* You can get statuses from the \FieldNation\WorkOrderStatuses class
*/
$status = \FieldNation\WorkOrderStatuses::PUBLISHED
/**
* @returns \FieldNation\WorkOrderInterface[]
*/
$workOrders = $fn->getWorkOrders($status);Use to get all of the projects for your company.
/**
* @returns \FieldNation\ProjectInterface[]
*/
$projects = $fn->getProjects();Get all of the documents for your company.
/**
* @returns \FieldNation\DocumentInterface[]
*/
$documents = $fn->getDocuments();If you have a tracking number for a shipment you created, but didn't save the Field Nation shipping ID you can get it here.
/**
* @returns string
*/
$shippingId = $fn->getShippingIdFrom('my-tracking-number');You may be wondering if you have to use our PHP classes. The good news is that you don't! We implement all of our own
interfaces with plain ol' php objects as a set of defaults, but if your classes implement our interfaces you can
configure the SDK to use your classes. The only hard rule we have is that your class must implement the interface
for the type you're trying to inject. If your class doesn't implement the interface we will throw a TypeError.
Example replacing our WorkOrder with your class that implements our WorkOrderInterface.
use \FieldNation\SDK;
use \FieldNation\ClassMapFactoryInterface;
SDK::configure(function (ClassMapFactoryInterface $classMap) {
$classMap->setWorkOrder(\MyBusinessNamespace\MyClass::class);
});Here is a list of all of the classes that are considered injectable.
// Default configuration
SDK::configure(function (ClassMapFactoryInterface $classMap) {
$classMap->setAdditionalExpense(\FieldNation\AdditionalExpense::class);
$classMap->setAdditionalField(\FieldNation\AdditionalField::class);
$classMap->setBlendedPay(\FieldNation\BlendedPay::class);
$classMap->setCheckInOut(\FieldNation\CheckInOut::class);
$classMap->setCloseoutRequirement(\FieldNation\CloseoutRequirement::class);
$classMap->setCustomField(\FieldNation\CustomField::class);
$classMap->setDocument(\FieldNation\Document::class);
$classMap->setFixedPay(\FieldNation\FixedPay::class);
$classMap->setGroup(\FieldNation\Group::class);
$classMap->setHistory(\FieldNation\History::class);
$classMap->setLabel(\FieldNation\Label::class);
$classMap->setMessage(\FieldNation\Message::class);
$classMap->setPayInfo(\FieldNation\PayInfo::class);
$classMap->setPaymentDeduction(\FieldNation\PaymentDeduction::class);
$classMap->setPayment(\FieldNation\Payment::class);
$classMap->setProblem(\FieldNation\Problem::class);
$classMap->setProgress(\FieldNation\Progress::class);
$classMap->setProject(\FieldNation\Project::class);
$classMap->setRatePay(\FieldNation\RatePay::class);
$classMap->setServiceDescription(\FieldNation\ServiceDescription::class);
$classMap->setServiceLocation(\FieldNation\ServiceLocation::class);
$classMap->setShipmentHistory(\FieldNation\ShipmentHistory::class);
$classMap->setShipment(\FieldNation\Shipment::class);
$classMap->setTechnician(\FieldNation\Technician::class);
$classMap->setTemplate(\FieldNation\Template::class);
$classMap->setTimeRange(\FieldNation\TimeRange::class);
$classMap->setWorkLog(\FieldNation\WorkLog::class);
$classMap->setWorkOrder(\FieldNation\WorkOrder::class);
});All merge requests require tests passing tests. Merge requests will not be approved if the tests do not pass, and if there are no new tests for the changes.
$ composer testWe follow the PSR-2 coding style. We will lint the code and merge requests will not be approved if they do not pass linting. You can lint using PHP_CodeSniffer.
$ composer lint:checkIf you have lint errors or warnings you can try to auto clean them with composer lint:fix. If the errors/warnings can't be
fixed on their own you will have to manually fix them. composer lint:check should always exit with a 0.
Please see the CHANGELOG or view the releases.
Copyright © 2017 Field Nation
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
