rjdeliveryomaha/courierinvoice

Tools for using the Courier Invoice API

2.7.1 2023-01-18 17:22 UTC

README

A set of classes for the Courier Invoice API

Instalation

composer require "rjdeliveryomaha/courierinvoice"

Or add "rjdeliveryomaha/courierinvoice":"^2.0.0" to composer.json

Classes

extras

SecureSessionHandler

Extends PHPs SessionHandler class.

Provides static functions for session management.

Throws exception on error.

Properties settable in $config

These properties are described in extras/includes.

  • session_name

  • domain

  • lifetime

  • path

  • samesite

  • secure

  • alternateHijackingTest

Public Methods:

try {
  SecureSessionHandler::start_session($config);
} catch (Exception $e) {
  echo $e->getMessage();
  exit;
}

This function will try to use ini_set() to apply the following settings:

If these settings fail an exception will be thrown.

If a valid session name is not provided an exception will be thrown.

If a domain name is not provided $_SERVER['SERVER_NAME'] will be used.

If $config['alternateHijackingTest'] is false the users IP address and user agent will be stored in the session and compared to the values provided by the server upon each validation call which is done every time SecureSessionHandler::start_session() is called. It is not recommended that this value be set to true however, it may be necessary in situations where a users IP address is frequently changed due to network instability or service provider issues.

$_SESSION['formKey'] is set to a value generated by bin2hex(random_bytes(32)). Validation will compare this value to $_POST['formKey'] on every POST request then iterate the value. An exception with the message "Session Error" will be thrown if these values don't match.

echo SecureSessionHandler::outputKey();

Returns the current value of $_SESSION['formKey'].

SecureSessionHandler::destroySession();
  • Clears session values.

  • Preserves the value of $_SESSION['formKey'].

  • Destroys the current session.

  • Regenerates the session ID.

try {
  SecureSessionHandler::start_session($config);
} catch(Exception $e) {
  echo "<span data-value=\"error\">{$e->getMessage()}</span>";
  return FALSE;
}
echo SecureSessionHandler::outputKey();
return FALSE;

Sets and returns a new value for $_SESSION['formKey'].

If there is no active session an attempt will be made to create one.

CommonFunctions

Utility class with functions used throughout the project.

Throws exception on error.

Usage:

try {
  $functions = new CommonFunctions($config, $data);
} catch(Exception $e) {
  return $e->getMessage();
}

Properties settable in $data

  • noSession
    • Numeric

    • If set and equal to 1 the class will not try to use any session values.

Public Methods:

$functions->getProperty($property);

Returns a value if the property exists.

Returns false if the property does not exist.

$functions->updateProperty($property, $value);

Sets property to new value.

Returns true on success.

Returns false if the property does not exist.

$functions->addToProperty($property, $value);

Adds $value to property.

Returns true on success.

Returns false if the property does not exists.

Returns false if $value or the property is not numeric.

$functions->substractFromProperty($property, $value);

Subtract $value from the property.

Returns true on success.

Returns false if the property does not exists.

Returns false if $value or the property is not numeric.

$functions->compareProperties($obj1, $obj2, $property, $strict=FALSE);

Returns false if property does not exist in both objects.

Returns true if the properties hold equal values.

$strict compares type as well as value.

Returns true is properties hold identical values.

$functions->debug();

Pretty print properties and values.

$functions->getError();

Return the last error.

Query

Extends CommonFunctions

This class handles the creation and execution of calls to the API.

Throws exception on error.

Usage:

try {
  $query = new Query($config, $data);
} catch(Exception $e) {
  return $e->getMessage();
}

Properties settable in $data:

  • primaryKey

    • Integer

    • Should not be set unless using PUT or DELETE.

    • An Exception will be thrown if the PUT or DELETE method are used with this property being not set.

  • endPoint

    • String

    • Valid end points:

      config, tickets, invoices, clients, o_clients, contract_locations, contract_runs, schedule_override, drivers, dispatchers

  • method

    • String

    • Valid values:

      POST, PUT, GET, DELETE

  • queryParams

    • Associative array

    • keys:

      • include / exclude:

        Indexed array of resources to retrieve / ignore from the end point. Include will be favored if both are provided. If omitted all resources are returned. Ex:

        Return only ticket number, billed client, and ticket price.

        $data['queryParams']['include'] = [ 'TicketNumber', 'BillTo', 'TicketPrice' ];

        Return all resources except pick up country.

        $data['queryParams']['exclude'] = [ 'pCountry' ];
      • filter:

        • simple "and" query:

          Indexed array of associative arrays. Ex:

          Select tickets with charge equal to 5 and billed to clients other than client 1.

          $data['queryParams']['filter'] = [];
          
          $data['queryParams']['filter'][] = ['Resource'=>'Charge', 'Filter'=>'eq', 'Value'=>5];
          
          $data['queryParams']['filter'][] = ['Resource'=>'BillTo', 'Filter'=>'neq', 'Value'=>1];
        • complex "and" & "or" query:

          Indexed array of simple "and" queries. Ex:

          Select tickets with charge between 1 and 5 and billed to client 1 or tickets billed to client 2.

          $filter1 = [];
          
          $filter1[] = ['Resource'=>'Charge', 'Filter'=>'bt', 'Value'=>'1,5'];
          
          $filter1[] = ['Resource'=>'BillTo', 'Filter'=>'eq', 'Value'=>1];
          
          $filter2 = [ ['Resource'=>'BillTo', 'Filter'=>'eq', 'Value'=>2] ];
          
          $data['queryParams']['filter'] = [$filter1, $filter2];
        • available filters:

          • cs: contain string (string contains value)
          • sw: start with (string starts with value)
          • ew: end with (string end with value)
          • eq: equal (string or number matches exactly)
          • lt: lower than (number is lower than value)
          • le: lower or equal (number is lower than or equal to value)
          • ge: greater or equal (number is higher than or equal to value)
          • gt: greater than (number is higher than value)
          • bt: between (number is between two comma separated values)
          • in: in (number is in comma separated list of values)
          • is: is null (field contains "NULL" value)
          • You can negate all filters by prepending a 'n' character, so that 'eq' becomes 'neq'.
      • order

        Indexed array of ordering parameters. With the "order" parameter you can sort. By default the sort is in ascending order, but by specifying "desc" this can be reversed. Ex:

        $data['queryParams']['order'] = ['DispatchTimeStamp,desc'];

        or

        $data['queryParams']['order'] = ['BillTo', 'DispatchTimeStamp,desc'];
      • page

        • string

        • The "page" parameter holds the requested page. The default page size is 20, but can be adjusted (e.g. to 50). Pages that are not ordered cannot be paginated. EX:

        $data['queryParams']['page'] = '1';

        or

        $data['queryParams']['page'] = '1,50';
      • join

        • Indexed array of tables to join via Foreign Key Constraint.
          $data['endPoint'] = 'clients';
          $data['method'] = 'get';
          $data['queryParams']['filter'][] = ['Resource'=>'ClientID', 'Filter'=>'eq', 'Value'=>0];
          $data['queryParams']['join'] = [ 'config' ];

        This query will return the client with all resources plus an extra resource named 'config' with all of the resources from that end point.

        If the filter were left off all clients would have the extra resource however, it would only be populated for Client 0.

        • Joins currently available:

          • Client 0 to config

          • Invoice to tickets

          • Contract run to contract locations

          • Contract run to contract run schedule

          • Contract run to schedule override

          • Route to route schedule

          • Route to schedule override

          • Route to route tickets

Public Methods

$query->buildURI();

Processes the queryParams property into a query string then returns itself so that it can be chained with the call method.

$query->call();

Uses the cURL library to execute the query string. Returns the result of the query or throws Exception on error. Ex:

try {
  $query->buildURI()->call();
} catch (Exception $e) {
  return $e->getMessage();
}

LoginHandler

Extends CommonFunctions

Processes login credentials.

Populates $_SESSION with user data.

Returns string; either '/clients' or '/drivers'.

Throws exception on error.

Usage

try {
  $handler = new LoginHandler($config, $data);
} catch(Exception $e) {
  return $e->getMessage();
}

Properties settable in $data:

  • clientID

    Users login name.

    • Repeat clients: Integer. ClientID.
    • Non-repeat clients: String. ClientID preceded by the letter 't'. t1
    • Organizations: String. orgLogin
    • Drivers: String. DriverID preceded by the word 'driver'. driver1
    • Dispatchers: String. DispatchID preceded by the word 'dispatch'. dispatch23
  • upw

    Users password.

Public Methods

try {
  $type = $handler->login();
} catch(Exception $e) {
  echo $e->getMessage();
  return false;
}
echo $type;
return false;

Sets the following session values:

$_SESSION['config']

This associative array contains resources from the config endpoint as well as all Client 0 resources except Password, AdminPassword, and Deleted.

$_SESSION['mobile']

If true this Boolean value indicates that the user logged in from a portal located at ../mobileLogin that passed the LoginHandler class a dataset that included the key "mobile" and will redirect there instead of / upon log out.

$_SESSION['ulevel']

Describes the type of user logged in:

  • 0: Organization

  • 1: Admin Client

  • 2: Daily Client

  • driver

  • dispatch

All resources from the end point corresponding to the client, organization, driver, or dispatcher except Deleted and passwords.

Dispatchers have $_SESSION['CanDispatch'] set to a value of 2.

Clients, Dispatchers, and Drivers with dispatch privileges have $_SESSION['config']['ContractDiscount'] and $_SESSION['config']['GeneralDiscount'] set. These are associative arrays with keys being Client IDs (preceded with the letter 't' for non-repeat clients) and the values being the discount values for each (or current) client.

Clients and organizations will have $_SESSION['pwWarning'] set.

Integer

  • 0: password did not match the default password for the login type.

  • 1: daily client password is default

  • 2: admin client password is default

  • 3: both daily and admin clients are default

  • 4: organization password is default

These values are used to notify the user that the password should be changed.

Organizations have $_SESSION['members'] set. This is an associative array with the keys being client IDs (preceded by the letter 't' for non-repeat clients) and the values being an associative array containing the data stored at the clients end point for each client associated with the organization.

Ticket

Extends CommonFunctions

Processes and displays tickets individually or batched

Usage:

try {
  $ticket = new Ticket($config, $data);
} catch(Exception $e) {
  return $e->getMessage();
}

Properties settable in $data:

A list of standard properties can be found in the API Documentation

The step property is used by $ticket->updateStep().

The action property is used by $ticket->cancelTicket().

The special property multiTicket can contain an array of ticket data sets for batch creation or updating. Ex: Update two tickets; one is being delivered the other returned to the same address.

$data['multiTicket'] = [
  [
    'ticket_index': 1,
    'step': 'delivered',
    'notes': 'This ticket was delivered'
  ],
  [
    'ticket_index': 2,
    'step': 'returned'
  ]
]

Public Methods:

echo $ticket->regenTicket();

Display single ticket for client review or for dispatch.

If coordinates are available for pick up, delivery, or return step the will be displayed in a span with the class coordinates.

echo $ticket->displaySingleTicket();

Display single ticket for drivers.

Addresses are displayed as a.addressLink that by default will open Google Maps searching the address in a new window.

echo $ticket->displayMultiTicket();

Display groups of tickets with common pick up or drop off location and time.

Addresses are displayed as a.addressLink that by default will open Google Maps searching the address in a new window.

echo $ticket->ticketsToDispatch();

Check database for tickets that have not been dispatched.

echo $ticket->ticketForm();

Has 4 states based on $data passed to Ticket.

1 initial: Generates an empty ticket entry form.

This form is followed by <div class="mapContainer" id="map"></div> for use with a javascript map api.

This div is only generated on this step.

2 edit: Generates a ticket entry form populated with provided $data.

3 confirmation: Generates a read-only form for validation.

TicketPrice is solved here.

4 process: Submit ticket to the server.

TicketNumber is set here.

echo $ticket->runPriceForm();

Generates a simplified ticket form that only accepts 2 addresses, charge, and dry ice weight.

The data from this form should be passed to $ticket->calculateRunPrice().

This form is followed by <div class="mapContainer" id="map2"></div> for use with a javascript map api.

echo $ticket->calculateRunPrice();

Note: The private method self::getTicketBase() is called here as well as when creating on call tickets and contract tickets that have a PriceOverride value of 0. This method uses geocoder-php to get the coordinates of the pick up and delivery locations. It expects that the properties $ticket->pAddress2 and $ticket->dAddress2 will be in the format Locality/City, State/Province Zip/Postal Code. This format is important as the function tests the value between the first comma and the final whitespace in the string (the one prior to Zip/Postal Code) against values returned by the geocoder to determine if accurate data has been received.

Returns a json encoded array with the following properties:

  • billTo: ClientID.
  • address1: Pick up address.
  • address2: Delivery address.
  • result1: Coordinates of address1.
  • result2: Coordinates of address2.
  • center: Coordinates of mid-point between result1 and result2.
  • pRangeTest: The distance between address1 and RangeCenter.
  • dRangeTest: The distance between address2 and RangeCenter.
  • rangeDisplay: The distance between address1 and address2.
  • runPrice: The calculated price without dry ice.
  • ticketPrice: The calculated price with dry ice.
  • diWeight: The weight of dry ice provided for the calculation.
  • diPrice: The calculated price of dry ice.
echo $ticket->fetchTodaysTickets();

Checks for tickets for a given client on the current date.

Returns the results of $ticket->regenTicket() for each ticket found, false if none exist.

$ticket->processRouteTicket();

Note: This does not display a map when calculating TicketPrice. Consult geocoder providers for limitations.

Process tickets generated by the Route class:

  • Sets TicketNumber

  • Solves TicketPrice or uses the resource stored at the contract_runs end point if PriceOverride is true

  • Submits tickets

  • Updates LastCompleted date at the contract_runs end point

Returns true on success, false, and optionally logs an error, on failure.

$ticket->processReturnTicket();

Processes and submits a change of charge from 5 to 6 using the value stored in TicketBase.

echo $ticket->updateTicketProperty();

Updates values in $ticket->updateTicketDatabaseKeys if provided.

$ticket->updateTicketDatabaseKeys = ['BillTo',
                                     'Charge',
                                     'EmailAddress',
                                     'EmailConfirm',
                                     'Telephone',
                                     'RequestedBy',
                                     'pClient',
                                     'pAddress1',
                                     'pAddress2',
                                     'pCountry',
                                     'pContact',
                                     'pTelephone',
                                     'dClient',
                                     'dAddress1',
                                     'dAddress2',
                                     'dCountry',
                                     'dContact',
                                     'dTelephone',
                                     'dryIce',
                                     'diWeight',
                                     'diPrice',
                                     'DispatchedTo',
                                     'Transfers',
                                     'TicketBase',
                                     'RunPrice',
                                     'TicketPrice',
                                     'Notes',
                                     'pSigReq',
                                     'dSigReq',
                                     'd2SigReq',
                                     'pLat',
                                     'pLng',
                                     'dLat',
                                     'dLng',
                                     'd2Lat',
                                     'd2Lng'
                                   ];
echo $ticket->stepTicket();

Sets the time stamp and submits that, notes and other values for the given step for a single ticket or multiTicket array.

Sends confirmation email if indicated.

Returns a string on success and error.

On error the string will contain the word 'error'.

Valid values:

  • 'pickedUp'

    Checks for $ticket->sigImage

    Checks for $ticket->latitude and $ticket->longitude

    Submits pSigPrint, pSig, pSigType, pLat, and pLng

  • 'delivered'

    Checks for $ticket->sigImage

    Checks for $ticket->latitude and $ticket->longitude

    Submits dSigPrint, dSig, dSigType, dLat, and dLng

    Solves for TicketPrice is Charge is 7 and d2SigReq is 0.

  • 'returned'

    Checks for $ticket->sigImage

    Checks for $ticket->latitude and $ticket->longitude

    Submits d2SigPrint, d2Sig, d2SigType, d2Lat, and d2Lng

    Solves for TicketPrice if Charge is 7 and d2SigReq is 1.

  • 'dispatched'

    Sets DispatchTimeStamp, DispatchMicroTime, DispatchedTo, and DispatchedBy.

echo $ticket->cancelTicket();

Processes the given action for a single ticket or multiTicket array.

Valid values:

  • 'delete'

    The API uses a 'soft delete' method.

    The ticket will not be removed from the database. Instead the Charge will be set to 0.

  • 'cancel'

    The tickets Charge will be set to 0.

  • 'deadRun'

    The tickets Charge will be set to 8.

  • 'declined'

    Only available on the delivery step.

    The tickets Charge will be set 6 and the TicketPrice recalculated.

  • 'transfer'

    The tickets TransferState will be updated.

Route

Extends CommonFunctions

Creates contract tickets for a driver as defined on the Manage Runs page.

Checks for and displays contract tickets.

Updates LastSeen resource.

Checks for and displays on call tickets.

Checks for and displays transferred tickets.

Usage:

try {
  $route = new Route($config, $data);
} catch(Exception $e) {
  echo $e->getMessage();
  return FALSE;
}

Properties settable in $data:

All properties of this class are set by the session.

Public Methods:

$route->routeTickets();

Checks drivers LastSeen resource. If it is not the current date a call is made to check for cancelations and rescheduling then tickets defined on the Manage Runs page are created. The LastSeen resource is then updated.

Note: The above order is overriding. Canceling a run on a given date supersedes rescheduling that run to the same date. Rescheduling a run supersedes its date, pick up time, delivery time, and return time defined on the Manage Runs page.

A call is made to fetch incomplete routine and round trip contract tickets. If this result is empty a call is made to check if any contract tickets were created for the driver on the current day.

Multiple tickets with the same location and scheduled step time will be grouped together.

Soft delete (setting the 'Deleted' property to 1) of contract runs, contract run schedules, rescheduling events, cancelations, and route schedules is respected.

Soft delete of contract locations that are associated with active contract runs is not respected.

A span.timing element is added to each div.sortable for use by javascript to order tickets based on their ReadyDate and step.

Returns HTML content.

$route->onCallTickets();

Checks for incomplete on call tickets that have been dispatched to the current driver in the past 7 days.

Returns HTML content.

$route->transferredTickets();

Checks for tickets that have been transferred to or by the current driver.

Returns HTML content.

Scheduling

Static Class with tools for interpreting scheduling codes, literals, and indices.

Public methods

Scheduling::codeFromIndex($index);

Returns the schedule code ([a-g][1-9] or h5 - h28) associated with a given schedule_index (1-87).

Scheduling::literalFromIndex($index);

Returns the schedule literal (Every Day, Every Last Thursday, Every 22nd) associated with a given schedule_index (1-87).

Scheduling::indexFromCode($code);

Returns the schedule_index (1-87) associated with a given schedule code ([a-g][1-9] or h5 - h28).

Scheduling::literalFromCode($code);

Returns the schedule literal (Every Day, Every Last Thursday, Every 22nd) associated with a schedule code ([a-g][1-9] or h5 - h28).

Scheduling::indexFromLiteral($literal);

Returns the schedule_index (1-87) associated with a given schedule literal (Every Day, Every Last Thursday, Every 22nd).

Scheduling::codeFromLiteral($literal);

Returns the schedule code ([a-g][1-9] or h5 - h28) associated with a given schedule literal (Every Day, Every Last Thursday, Every 22nd).

Scheduling::testIndex($index, $startDate, $testDate);
Scheduling::testCode($code, $startDate, $testDate);
Scheduling::testLiteral($literal, $startDate, $testDate);

Compares two DateTime objects to determine if the scheduled interval has elapsed.

Returns boolean.

Note: For 'Every Other' schedules with day names, if the start date does not land on the named day, perhaps because the schedule changed, the date will be tested against the day of the scheduled name in the same Sunday through Saturday week as the start date.

For example if an Every Other Saturday schedule starting on 01 JUL 2017 were changed to an Every Other Tuesday schedule the current date would be tested against the Tuesday in the same week as 01 JUL 2017 which is 27 JUN 2017.

Invoice

Extends CommonFunctions

Displays invoices for review.

By default canceled thickets are not displayed. This behavior can be adjusted on a per client basis in the config file located at extras/includes.

By default contract tickets are consolidated by run number. This behavior can be adjusted on a per client basis in the config file located at extras/includes.

Usage:

try {
  $invoice = new Invoice($config, $data);
} catch(Exception $e) {
  echo $e->getMessage();
  return FALSE;
}

Properties settable in $data:

A list of standard properties can be found in the API Documentation

The invoiceQueryResult property is an indexed array of invoice datasets used for receiving data from the SearchHandler class.

Public Methods:

$invoice->regenInvoice();

Uses the data provided in invoiceQueryResult to display an invoice.

If dompdf is detected a button will be added to display/download the invoice as a pdf.

If multiple datasets are provided a form will be returned to select which invoice to recreate by invoice number.

Soft delete (setting the 'Deleted' property to 1) will result in a discreet message being displayed at the top of the invoice indicating that it has been flagged for deletion.

$invoice->invoiceQueryForm();

Creates a query form for admin and organization clients. The parameters provided by this form are processed by the SearchHandler class.

SearchHandler

Extends CommonFunctions

Accepts input from ticket and invoice query forms.

Returns html result of datasets processed by those respective classes.

Usage:

try {
  $handler = new SearchHandler($config, $data);
} catch (Exception $e) {
  echo $e->getMessage();
  return false;
}

Properties settable in $data:

  • ticketNumber

    Used by the ticketLookup() method.

  • endPoint

    • tickets

    • invoices

  • display

    • tickets: displays the result of $ticket->regenTicket() for each ticket matching query parameters.

    • invoice: displays the result of $invoice->regenInvoice() for the matching invoice.

    • chart: displays a bar graph generated by either the TicketChart or InvoiceChart class.

  • compare

    When a chart query is made that spans multiple months the default behavior is to display each month in the query. Setting this value to 1 will compare the first and last month of the query parameters.

  • compareMembers

    When a chart query is made that includes multiple members of an organization the default behavior is to display charts for each member. Setting this value to 1 will compare members in a query set.

  • clientID

    Client associated with an invoice.

  • billTo

    Client associated with a ticket.

  • repeatClient

    • 0: non-repeat client

    • 1: repeat client

  • startDate

    Lower date boundary for a query. Format YYYY-mm-dd.

  • endDate

    Upper boundary for a query. Format YYYY-mm-dd.

  • invoiceNumber

    Specific invoice to query for a client.

    Expected formats:

    • ##EX####-## regex: /(^[\d]{2}EX[\d]+-[\d]+$)/

    • ##EX####-t## regex: /(^[\d]{2}EX[\d]+-t[\d]+$)/

  • dateIssued

    Month to query for invoices for a given client. Format YYYY-mm.

  • ticketNumber

    Specific ticket to query for a given client.

  • charge

    Specific ticket charge to query for a given client.

    Values from 0 - 9.

  • type

    Type of ticket to query for a given client.

    • 2: All

    • 1: Contract

    • 0: On Call

  • allTime

    Default 0. If set to 1:

    • If display is "tickets" all tickets from account creation to current date will be displayed.

    • If display is "chart" data will be displayed for the period between the current date and the pervious allTimeChartLimit months.

Public Methods:

$handler->ticketLookup() ;

Uses a ticket number to get ticket timing information.

Returns a JSON encoded string.

The object will have the property queryError if there was a problem with the query or if the tickets Charge property is 0 (canceled).

If no error and Charge does not equal 0 the object will have the following properties:

  • Charge

    Integer

    The values of the Charge property for the queried ticket.

  • pTimeStamp

    String

    The values of the pTimeStamp property for the queried ticket in the format d M Y \a\t h:i A or "Pending" if the value is NULL.

  • dTimeStamp

    String

    The values of the dTimeStamp property for the queried ticket in the format d M Y \a\t h:i A or "Pending" if the value is NULL.

  • d2TimeStamp

    String

    The values of the d2TimeStamp property for the queried ticket in the format d M Y \a\t h:i A, "Pending" if the value is NULL or "Not Scheduled" if Charge is not 6 or if Charge is 7 but d2SigReq is not 1.

$handler->handleSearch();

Returns the html content generated by the indicated class; Ticket, Invoice, TicketChart, or InvoiceChart.

TicketChart

Extends CommonFunctions

Receives a dataset of tickets grouped by month from the SearchHandler class.

Displays a table containing the number of each type of ticket over a given period.

Displays a simple bar chart depicting the number of each type of ticket over a given period.

Contract and On Call are always displayed.

With Ice and Without Ice are always displayed.

All other values are only displayed if they are not zero.

example

If dompdf is detected a button will be added to display/download the chart as a pdf.

Properties settable in $config can be found here

Usage:

try {
  $ticketChart = new TicketChart($config, $data);
} catch(Exception $e) {
  echo $e->getMessage();
  return FALSE;
}

Properties settable in $data:

  • dataSet

    Associative array of data sorted by month.

    $monthLabel is the ReceivedDate of a ticket in the format 'M Y'.

    $this->months[$monthLabel][$ticket['BillTo']] = [
                                                     'billTo'=>$ticket['BillTo'],
                                                     'monthTotal'=>1,
                                                     'contract'=>0,
                                                     'credit'=>0,
                                                     'canceled'=>0,
                                                     'onCall'=>0,
                                                     'routine'=>0,
                                                     'fourHour'=>0,
                                                     'threeHour'=>0,
                                                     'twoHour'=>0,
                                                     'oneHour'=>0,
                                                     'roundTrip'=>0,
                                                     'deadRun'=>0,
                                                     'dedicatedRun'=>0,
                                                     'withIce'=>0,
                                                     'withoutIce'=>0,
                                                     'startDate'=>$receivedDate->format('Y-m-d'),
                                                     'endDate'=>$receivedDate->format('Y-m-d')
                                                   ];
  • organizationFlag

    Boolean

    Indicates if the query if for an organization (true) or an individual client (false)

  • clientID

    Array or Integer

    If organizationFlag is true this is an indexed array of members to be queried.

    If organizationFlag is false this is the ID of the current client.

  • compare

    Boolean

    Indicates if dataset is two months compared or a series of months.

  • compareMembers

    Boolean

    Indicates if members are being compared of displayed separately.

Public Methods:

$chart = $ticketChart->displayChart();

if ($chart === false) {
  echo $ticketChart->getError();
} else {
  echo $chart;
}

Returns html content or false on error.

InvoiceChart

Extends CommonFunctions

Receives a dataset of tickets grouped by month from the SearchHandler class.

Displays a table containing the expense of each type of ticket over a given period.

Displays a simple bar chart depicting the expense of each type of ticket over a given period.

Only non-zero values are displayed.

example

If dompdf is detected a button will be added to display/download the chart as a pdf.

Properties settable in $config can be found here

Usage:

try {
  $invoiceChart = new InvoiceChart($config, $data);
} catch(Exception $e) {
  echo $e->getMessage();
  return FALSE;
}

Properties settable in $data:

  • dataSet

    Associative array of data sorted by month.

    $invoiceLabel is the DateIssued of an invoice in the format 'M Y'.

$this->months[$invoiceLabel] = [
                                'invoices'=>[0 => $invoice['InvoiceNumber']],
                                'billTo'=>$invoice['ClientID'],
                                'monthTotal'=>$invoice['InvoiceSubTotal'] - $invoice['BalanceForwarded'],
                                'contract'=>0,
                                'credit'=>0,
                                'canceled'=>0,
                                'onCall'=>0,
                                'routine'=>0,
                                'fourHour'=>0,
                                'threeHour'=>0,
                                'twoHour'=>0,
                                'oneHour'=>0,
                                'roundTrip'=>0,
                                'deadRun'=>0,
                                'dedicatedRun'=>0,
                                'dryIce'=>0,
                                'iceDelivery'=>0,
                               ];
  • organizationFlag

Boolean

Indicates if the query if for an organization (true) or an individual client (false)

  • clientID

Array or Integer

If organizationFlag is true this is an indexed array of members to be queried.

If organizationFlag is false this is the ID of the current client.

  • compare

Boolean

Indicates if dataset is two months compared or a series of months.

  • compareMembers

Boolean

Indicates if members are being compared of displayed separately.

Public Methods:

$chart = $invoiceChart->displayChart();

if ($chart === false) {
  echo $invoiceChart->getError();
} else {
  echo $chart;
}

Returns html content or false on error.

Client

Extends CommonFunctions

Manages information for clients, drivers, and dispatchers.

Usage:

try {
  $client = new Client($config, $data);
} catch(Exception $e) {
  echo $e->getMessage();
  return FALSE;
}

Properties settable in $data:

A list of standard properties can be found in the API Documentation

  • same

    Indicates that a clients billing information is identical to their shipping information.

  • currentPw

    The current password of the user to be change.

  • newPw1

    The new password.

  • newPw2

    Confirmation of the new password.

Public methods:

$client->getAllClientInfo();

Returns array of client information.

$client->changePassword();

echos message and returns false on error.

Requires current password to be submitted.

Validates submitted current password against database stored hash.

Tests that newPw1 meets criteria

  • At least 8 characters long.
  • At least one upper case letter. A..Z
  • At least one lower case letter. a..z
  • At least one number. 0..9
  • At least one special character. ! @ # $ % ^ & * ( ) { } [ ] - _ . : ; , = +

Compares newPw1 and newPw2 for confirmation.

Ensures that admin and daily user passwords do not match.

Hashes the new password using PASSWORD_DEFAULT algorithm and a cost of 12.

$client->updateInfo();

echos message and returns false on error.

If $client->same is equal to 1 billing information is set equal to shipping information.

Phone numbers are validated preg_match('/(\d{3})-(\d{3})-(\d{4})x(\d+)/i', $val) || preg_match('/(\d{3})-(\d{3})-(\d{4})/', $val);. These patters are slated for update.

$client->adminPasswordForm();

Returns a password update form with admin user values set.

$client->dailyPasswordForm();

Returns a password update form with daily user values set.

$client->orgPasswordForm();

Returns a password update form with organization user values set.

$client->driverPasswordForm();

Returns a password update form with driver values set.

$client->dispatchPasswordForm();

Returns a password update form with dispatch values set.

$client->updateInfoForm();

Only available to admin user level.

Returns an contact information update form.

InvoiceCron

Extends CommonFunctions

Automatically creates invoices with a cron job run on the day following the end of a monthly billing cycle.

Updates tickets with new invoice number.

Can be configured to ignore clients and non-repeat clients.

Checks for past due invoices according to the terms defined per invoice.

Respects soft delete (setting the 'Deleted' property to 1) when checking for past due invoices.

Usage:

  require_once '../includes/api_config.php';
  require_once '../vendor/autoload.php';

  use rjdeliveryomaha\courierinvoice\InvoiceCron;

  try {
    $cron = new InvoiceCron($config);
  } catch(Exception $e) {
    // log $e->getMessage();
    exit;
  }

  try {
    $cron->createInvoices();
  } catch(Exception $e) {
    // log $e->getMessage();
    exit;
  }
  exit;

extras

This is an extendable drop-in implementation of this set of classes using vanilla javascript.

Functionality is enclosed in a global variable: rjdci.

The Fetch API is used extensively and a template is provided. This will retry a call failing due to network issues 20 times waiting n * 250 milliseconds between calls where n is the retry count. If a FormData instance is sent the headers Content-Type and Content-Length will be tested for and removed to prevent conflicts. If the postData property is not empty and the Content-Type header is set as www-form-urlencoded the post body will be encoded. If the Content-Type header is not defined and the postData property has a length greater than 0 the Content-Type will be set to "application/json". As a result the PHP $_POST super global is not populated; data must be read from the stream.

Ex:

  if (empty($_POST)) $_POST = json_decode(file_get_contents('php://input'),true);
  rjdci.fetch_template = async ({ url, method = "POST", headers = {}, postData = {}, retry = 0 }) => {
    if (!url) throw new Error("URL not defined");
    let fetchOptions = {
        method: method.toUpperCase(),
        headers: headers
      };
    if (postData instanceof FormData || Object.keys(postData).length > 0) {
      let body;
      if (postData instanceof FormData) {
        body = postData;
        fetchOptions.headers["Content-Type"] &&
        delete fetchOptions.headers["Content-Type"];
        fetchOptions.headers["Content-Length"] &&
        delete fetchOptions.headers["Content-Length"];
      } else if (fetchOptions.headers["Content-Type"].indexOf("x-www-form-urlencoded") != -1) {
        body = Object.entries(postData).map(([key, value]) =>
          `${encodeURIComponent(key)}=${encodeURIComponent(value)}`).join("&");
      } else {
        body = JSON.stringify(postData);
        if (!headers.hasOwnProperty("Content-Type")) {
          fetchOptions.headers["Content-Type"] = "application/json";
        }
      }
      fetchOptions.method = "POST";
      fetchOptions.body = body;
    }
    try {
      return await fetch(url, fetchOptions);
    } catch(err) {
      retry++;
      if (retry === 20) throw err;
      await rjdci.pause(250 * retry)
      return await rjdci.fetch_template({
        url: url,
        method: method,
        headers: headers,
        postData: postData,
        retry: retry
      });
    }
  }

The rjdci.pause function is setTimeout turned into a promise.

  rjdci.pause = duration => { return new Promise(resolve => setTimeout(resolve, duration)) };

Usage:

  // example taken from rjdci.refreshRoute()
  let postData =  { "formKey": document.querySelector("#formKey").value };
  await rjdci.fetch_templet({ url: "./refreshRoute.php", postData: postData })
  .then(result => {
    if (typeof result === "undefined") throw new Error("Result Undefined");
    if (result.ok) {
      return result.text();
    } else {
      throw new Error(result.status + " " + result.statusText);
    }
  })
  .then(data => {
    if (data.indexOf("error") !== -1) throw new Error(data);
    let parser = new DOMParser(),
      newDom = parser.parseFromString(data, "text/html"),
      docFrag = document.createDocumentFragment();
    Array.from(newDom.querySelectorAll(".sortable")).forEach(element => {
      docFrag.appendChild(element);
    });
    setTimeout(() => {
      document.querySelector("#route").appendChild(docFrag);
      rjdci.assignListeners();
      rjdci.fixDeadRunButton();
      sortRoute();
    }, 2000);
  })
  .then(rjdci.refreshFormKey)
  .catch(error => {
    document.querySelector("#route").innerHTML = '<p class="center"><span class="error">Error</span>: ' + error.message + "</p>";
  });

A place holder for a map function is provided and should be redefined if a map api is implemented.

  rjdci.updateMap = ({ coords1, address1, coords2, address2, center, mapDivID }) => { return false; }

Signature Pad v2.3.2 is preconfigured with extras/public_html/app_js/sigPad.js to collect signatures in conjunction with the functions $ticket->displaySingleTicket() and $ticket->displayMultiTicket().

The method rjdci.toast(msg, options) is available. It creates and deletes a toast-like div to show messages to the user.

Usage:

  let options = {},
      msg = "Test Message"; // or ['Test Message'] or [ 'Multi-line', 'Test Message' ];
  options.title = "sample div title"; // title attribute of toast div. default ""
  options.time = 3000; // milliseconds to show toast div. div will be removed 1 second after it is hidden. default 4000
  options.eleClass = "ticketOncallReceived"; // custom class for the toast div. The function will display only the newest of a custom class, removing previous messages. All default class divs will be displayed for the configured time. default "toast__msg"
  options.datatime = 1512574797926; // unix time stamp to parse for display with message. default new Date().getTime();
  rjdci.toast(msg, options);

The method rjdci.getSpinner() can be called to return a copy of the loading icon.

Features

Uses ticket information from database to populate datalist elements to assist form completion.

Single page design navigated by either swipe or menu. Offers unique features based upon user type. Once each page has been loaded a window event rjdci_loaded is triggered.

Swipe is used for navigation. Public methods can be accessed via the rjdci.Swipe instance.

At the end of the transition between pages scroll(0,0) is called and a window event rjdci_pageChange is triggered.

Setting the order of menu items as well as adding custom menu items (with or without matching pages), and javascript files is handled in the config file located at extras/includes.

Drivers

If the user agent supports both the permissions and geolocation APIs an attempt will be made to get the coordinates when a ticket is picked up, delivered, returned, declined, or marked as a dead run. The above described function rjdci.toast is used to create notifications with the class deliveryLocation to inform the user of the status of updating the delivery location.

  • Route

    • Uses Route class to create or fetch contract tickets for a given driver.

    • Groups tickets with matching location and time.

    • Displays single and grouped tickets with ability to:

      • collect signatures

      • update step

      • update notes

      • cancel

      • mark as dead run

      • transfer

    • Can be independently refreshed.

      • CustomEvent rjdci_refreshed with e.detail.type() = "route"
        is dispatched when the refresh is completed.
  • On Call

    • Uses Route class to fetch on call tickets for a given driver.

    • Displays single tickets with ability to:

      • collect signatures

      • update step

      • update notes

      • cancel

      • mark as dead run

      • transfer

    • Can be independently refreshed.

      • CustomEvent rjdci_refreshed with e.detail.type() = "oncall"
        is dispatched when the refresh is completed.
  • Transfers

    • Uses Route class to fetch tickets either transferred by or transferred to a given driver.

    • Groups contract tickets with matching location and time.

    • Displays single and grouped tickets with ability to:

      • accept transfer

      • decline transfer

      • cancel transfer

    • Can be independently refreshed.

      • CustomEvent rjdci_refreshed with e.detail.type() = "transfers"
        is dispatched when the refresh is completed.
  • Ticket Entry and Dispatch page for drivers with dispatch privileges. Described below.

  • Active Tickets page for drivers with dispatch privileges. Described below.

  • Change Password

    • Provides a simple form to update the password

Dispatchers

  • Dispatch

    • Uses Ticket class to check for tickets that have not been dispatched.

    • Displays single tickets with ability to dispatch.

    • Can be independently refreshed.

      • CustomEvent rjdci_refreshed with e.detail.type() = "dispatch"
        is dispatched when the refresh is completed.
  • Price Calculator

    • Compact ticket form

    • Accepts only pick up address, delivery address, charge, and dry ice information

    • Uses Ticket class to compute the price of a ticket

    • <div class="mapContainer" id="map2"></div> available to display a map

  • Active Tickets

    • Query contract or on call tickets for a given driver

    • Displays single tickets with ability to edit

  • Ticket Entry

    • Form to submit tickets to the API

    • <div class="mapContainer" id="map"></div> available to display a map

    • Can be independently refreshed.

      • CustomEvent rjdci_refreshed with e.detail.type() = "ticketEntry"
        is dispatched when the refresh is completed.
  • Change Password

    • Provides a simple form to update the password

Clients

  • Admin User

    • Delivery request form

      • Form to submit tickets to the API
    • Ticket Query

      • Can search tickets by ticket number or by date

      • Displays either individual tickets or a bar graph depicting ticket volume

      • Bar graph can cover a range of months (maximum can be configured) or compare two months

      • Ticket charges available to be queried can be configured separately for admin and daily users

    • Invoice Query

      • Can search invoices by invoice number or by date

      • Displays either individual invoices or bar graph depicting expenses

      • Bar graph can cover a range of months (maximum can be configured) or compare two months

    • Password management

      • Update password for either admin or daily users

      • Warns to change from default password

    • Contact information management

    • Price Calculator

  • Daily User

    • Delivery request form

    • Ticket Query

    • Price Calculator

Organizations

  • Price Calculator

  • Ticket Query

    • Can search ticket by ticket number or by date

    • Can search by individual member or by groups of members

    • Displays either individual tickets or bar graph depicting ticket volume

    • Bar graph can cover a range of months (maximum can be configured) or compare two months

  • Invoice Query

    • Can search invoices by invoice number or by date

    • Can search by individual member or by groups of members

    • Displays either individual invoices or bar graph depicting expenses

    • Bar graph can cover a range of months (maximum can be configured) or compare two months

  • Password management