Skip to content

Commit

Permalink
Merge pull request #630 from DiamondLightSource/feature/lims-832/ship…
Browse files Browse the repository at this point in the history
…ping-service-incoming-shipments

[LIMS-832]Feature: Use shipping service for incoming shipments

My Commits checked by @MattPrit
  • Loading branch information
John-Holt-Tessella authored Aug 14, 2023
2 parents b0c9ad1 + 3133f7e commit 2da5639
Show file tree
Hide file tree
Showing 9 changed files with 124 additions and 36 deletions.
1 change: 1 addition & 0 deletions api/config_sample.php
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@

# Shipping service details
$use_shipping_service = null;
$use_shipping_service_incoming_shipments = null;
$shipping_service_url = null;
$shipping_service_links_in_emails = null;

Expand Down
11 changes: 6 additions & 5 deletions api/src/Database/DatabaseFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,26 @@ function __construct($databaseConnectionFactory)

public function get($databaseType = null)
{
global $dbtype;

if ( $databaseType == null) {
// Global variable is named $dbtype in config.php.
global $dbtype;
$databaseType = $dbtype;
}

if (!$databaseType) {
error_log('Database type variable, dbtype, is not specified in config.php - defaulting to MySql.');
$database_type = 'MySQL';
$databaseType = 'MySQL';
}

// Determine fully-qualified class name of database class corresponding to $database_type.
if (key_exists(strtolower($databaseType), $this->database_types)) {
$dbType = $this->database_types[strtolower($databaseType)];
$selectedDBConfig = $this->database_types[strtolower($databaseType)];

$full_class_name = 'SynchWeb\\Database\\Type\\' . $dbType["dbClassName"];;
$full_class_name = 'SynchWeb\\Database\\Type\\' . $selectedDBConfig["dbClassName"];

if (class_exists($full_class_name)) {
$conn = $this->databaseConnectionFactory->get($dbType["dataConnectionName"]);
$conn = $this->databaseConnectionFactory->get($selectedDBConfig["dataConnectionName"]);
return new $full_class_name($conn);
}
else {
Expand Down
104 changes: 83 additions & 21 deletions api/src/Page/Shipment.php
Original file line number Diff line number Diff line change
Expand Up @@ -952,7 +952,7 @@ function _dispatch_dewar_in_shipping_service($dispatch_info, $dewar)
"internal_contact_name" => $this->has_arg('LOCALCONTACT') ? $this->args['LOCALCONTACT'] : null,
"shipment_reference" => $dispatch_info['VISIT'],
"external_id" => (int) $dispatch_info['DEWARID'],
"journey_type" => "FROM_FACILITY",
"journey_type" => ShippingService::JOURNEY_FROM_FACILITY,
"packages" => array(array("external_id" => (int) $dispatch_info['DEWARID']))
);

Expand All @@ -972,13 +972,13 @@ function _dispatch_dewar_in_shipping_service($dispatch_info, $dewar)
if ($create === true) {
$response = $this->shipping_service->create_shipment($shipment_data);
} else {
$this->shipping_service->update_shipment($dispatch_info['DEWARID'], $shipment_data, "FROM_FACILITY");
$response = $this->shipping_service->get_shipment($dispatch_info['DEWARID'], "FROM_FACILITY");
$this->shipping_service->update_shipment($dispatch_info['DEWARID'], $shipment_data, ShippingService::JOURNEY_FROM_FACILITY);
$response = $this->shipping_service->get_shipment($dispatch_info['DEWARID'], ShippingService::JOURNEY_FROM_FACILITY);
}
$shipment_id = $response['shipmentId'];
$this->shipping_service->dispatch_shipment($shipment_id, false);
} catch (Exception $e) {
throw new Exception("Error returned from shipping service: " . $e . "\nShipment data: " . $shipment_data);
throw new Exception("Error returned from shipping service: " . $e . "\nShipment data: " . json_encode($shipment_data));
}

return $shipment_id;
Expand Down Expand Up @@ -2648,9 +2648,65 @@ function _get_default_dewar()
}


function _book_shipment_in_shipping_service($user, $shipment, $dewars, $journey_type) {
$address_lines = explode(PHP_EOL, rtrim($user["address"]));
$contact = array(
"company_name" => $user["company"],
"address_line1" => isset($address_lines[0]) ? $address_lines[0] : null,
"address_line2" => isset($address_lines[1]) ? $address_lines[1] : null,
"address_line3" => isset($address_lines[2]) ? $address_lines[2] : null,
"city" => $user["city"],
"country" => $user["country"],
"post_code" => rtrim($user["postcode"]),
"contact_name" => $user["name"],
"contact_phone_number" => $user["phone"],
"contact_email" => rtrim($user["email"])
);
$shipment_data = array(
"shipment_reference" => $shipment["PROP"],
"external_id" => $shipment['SHIPPINGID'],
"packages" => array_map(
function($dewar) {return array("external_id" => $dewar["DEWARID"]);},
$dewars
)
);

// Create or update shipment in shipping service
try {
$response = $this->shipping_service->get_shipment($shipment['SHIPPINGID'], $journey_type);
$user_shipment_role = $this->has_arg('RETURN') ? "consignee" : "shipper";
$relabelled_contact = array_combine(
array_map(function($key) use ($user_shipment_role) {return $user_shipment_role."_".$key;},
array_keys($contact)),
$contact);
$shipment_data = array_merge($response, $shipment_data, $relabelled_contact);
$this->shipping_service->update_shipment($shipment["SHIPPINGID"], $shipment_data, ShippingService::JOURNEY_TO_FACILITY);
} catch (\Exception $e) {
$shipment_data["contact"] = $contact;
$response = $this->shipping_service->create_shipment_by_journey_type($shipment_data, $journey_type);
}

// Dispatch shipment in shipping service
$shipmentId = $response["shipmentId"];
$dispatch_details = $this->shipping_service->dispatch_shipment($shipmentId, false);

$awb_pieces = array_map(
function($package, $index) {return array("piecenumber" => $index+1, "licenseplate" => $package["tracking_number"]);},
$dispatch_details["packages"],
array_keys($dispatch_details["packages"])
);

return array(
"awb" => $dispatch_details["tracking_number"],
"label" => $dispatch_details["air_waybill"],
"pieces" => $awb_pieces
);
}


function _create_awb()
{
global $dhl_service, $dhl_service_eu, $dhl_acc, $dhl_acc_import, $facility_courier_countries, $facility_courier_countries_nde;
global $dhl_service, $dhl_service_eu, $dhl_acc, $dhl_acc_import, $facility_courier_countries, $facility_courier_countries_nde, $use_shipping_service_incoming_shipments;

if (!$this->has_arg('prop'))
$this->_error('No proposal specified');
Expand Down Expand Up @@ -2766,22 +2822,28 @@ function _create_awb()
$awb = null;
if (!$ship['DELIVERYAGENT_FLIGHTCODE']) {
try {
$awb = $this->dhl->create_awb(array(
'payee' => $payee,
'accountnumber' => $accno,
'shipperid' => $ship['PROP'],
'service' => $product,
'date' => $ship['DELIVERYAGENT_SHIPPINGDATE'],
'declaredvalue' => $this->arg('DECLAREDVALUE'),
'description' => $this->arg('DESCRIPTION'),

'sender' => $this->has_arg('RETURN') ? $facility : $user,
'receiver' => $this->has_arg('RETURN') ? $user : $facility,

'pieces' => $pieces,
'notification' => implode(';', $emails),
'message' => $facility_company . ': Shipment booked from ISPyB for ' . $ship['PROP'] . ' ' . $ship['SHIPPINGNAME'] . ' containing ' . implode(',', $names)
));
if (Utils::getValueOrDefault($use_shipping_service_incoming_shipments) && $accno === $dhl_acc) {
$journey_type = $this->has_arg('RETURN') ? ShippingService::JOURNEY_FROM_FACILITY : ShippingService::JOURNEY_TO_FACILITY;
$awb = $this->_book_shipment_in_shipping_service($user, $ship, $dewars, $journey_type);
} else {
error_log("Not using shipping service for: {$ship['SHIPPINGID']}");
$awb = $this->dhl->create_awb(array(
'payee' => $payee,
'accountnumber' => $accno,
'shipperid' => $ship['PROP'],
'service' => $product,
'date' => $ship['DELIVERYAGENT_SHIPPINGDATE'],
'declaredvalue' => $this->arg('DECLAREDVALUE'),
'description' => $this->arg('DESCRIPTION'),

'sender' => $this->has_arg('RETURN') ? $facility : $user,
'receiver' => $this->has_arg('RETURN') ? $user : $facility,

'pieces' => $pieces,
'notification' => implode(';', $emails),
'message' => $facility_company . ': Shipment booked from ISPyB for ' . $ship['PROP'] . ' ' . $ship['SHIPPINGNAME'] . ' containing ' . implode(',', $names)
));
}

$this->db->pq("UPDATE shipping
SET deliveryagent_flightcode=:1, deliveryagent_flightcodetimestamp=CURRENT_TIMESTAMP, deliveryagent_label=:2, deliveryagent_productcode=:3, deliveryagent_flightcodepersonid=:4, shippingstatus='awb created', deliveryagent_agentname='DHL'
Expand Down
13 changes: 13 additions & 0 deletions api/src/Shipment/ShippingService.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ class ShippingService
{
private $shipping_api_url;
private $headers;
public const JOURNEY_TO_FACILITY = "TO_FACILITY";
public const JOURNEY_FROM_FACILITY = "FROM_FACILITY";

function __construct()
{
Expand Down Expand Up @@ -77,6 +79,17 @@ function create_shipment($shipment_data)
}


function create_shipment_by_journey_type($shipment_data, $journey_type)
{
return $this->_send_request(
$this->shipping_api_url . '/shipments/' . $journey_type,
"POST",
$shipment_data,
201
);
}


function get_shipment($external_id, $journey_type)
{
return $this->_send_request(
Expand Down
2 changes: 1 addition & 1 deletion api/tests/Database/DatabaseFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public function testErrorIsLoggedForDatabaseTypeClassThatDoesNotExist(): void
{
global $dbtype;
$dbtype = "obscure-db2";
$this->dbFactory->database_types[$dbtype] = $dbtype;
$this->dbFactory->database_types[$dbtype] = ["dbClassName" =>$dbtype, "dataConnectionName" => 'MySQL'];

$log = $this->getFunctionMock(__NAMESPACE__, "error_log");
$log->expects($this->once())->with("Database class 'SynchWeb\Database\Type\obscure-db2' does not exist.");
Expand Down
23 changes: 16 additions & 7 deletions client/src/js/modules/shipment/views/createawb.js
Original file line number Diff line number Diff line change
Expand Up @@ -314,11 +314,19 @@ define(['backbone',
},

checkAvailability: function() {
if (this.shipment.get('DELIVERYAGENT_AGENTNAME').toLowerCase() != 'dhl' && (
!(app.options.get('facility_courier_countries').indexOf(this.lc.get('COUNTRY')) > -1) &&
!(app.options.get('facility_courier_countries_nde').indexOf(this.lc.get('COUNTRY')) > -1)
))
app.message({ title: 'Service Not Available', message: 'This service is only available for shipments from '+app.options.get('facility_courier_countries').concat(app.options.get('facility_courier_countries_nde')).join(',')+', or for user with DHL accounts. Please either update your labcontact or the shipment courier'})
const agent_name = this.shipment.get('DELIVERYAGENT_AGENTNAME')
const facility_courier_countries = app.options.get('facility_courier_countries')
const facility_courier_countries_nde = app.options.get('facility_courier_countries_nde')
if ((agent_name==null || agent_name.toLowerCase() != 'dhl') && (
!(facility_courier_countries.indexOf(this.lc.get('COUNTRY')) > -1) &&
!(facility_courier_countries_nde.indexOf(this.lc.get('COUNTRY')) > -1)
)) {
const valid_countries = facility_courier_countries.concat(facility_courier_countries_nde).join(', ')
app.message({
title: 'Service Not Available',
message: 'This service is only available for shipments from ' + valid_countries +
', or for user with DHL accounts. Please either update your labcontact or the shipment courier'})
}
},

populateLC: function() {
Expand Down Expand Up @@ -435,7 +443,7 @@ define(['backbone',
PRODUCTCODE: prod,
},
success: function(resp) {
app.alert({ message: 'Air Waybill Successfully Created'})
app.message({ message: 'Air Waybill Successfully Created'})
setTimeout(function() {
app.trigger('shipment:show', self.shipment.get('SHIPPINGID'))
}, 1000)
Expand All @@ -452,7 +460,8 @@ define(['backbone',
console.error("Error parsing response: ", err)
}
}
app.alert({ message: json.message })
app.alert({ message: json.message, persist:true })
app.alert({ message: json.message})
self.ui.submit.prop('disabled', false)
self.$el.removeClass('loading')
}
Expand Down
2 changes: 2 additions & 0 deletions client/src/js/modules/shipment/views/dispatch.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ define(['marionette', 'views/form',
this.ui.exp.html(this.visits.opts()).val(this.model.get('VISIT'))
this.updateLC()
this.populateCountries()
this.stripPostCode()
this.formatAddress()
},

populateCountries: function() {
Expand Down
2 changes: 1 addition & 1 deletion client/src/js/templates/contact/contactview.html
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ <h1>View Home Lab Contact</h1>
</li>

<li class="clearfix">
<span class="label">Street Address <br />(excluding post code, city)</span>
<span class="label">Street Address <br />(Max 3 lines, excluding post code, city)</span>
<div class="ADDRESS text editable tw-block"><%-ADDRESS%></div>
</li>

Expand Down
2 changes: 1 addition & 1 deletion client/src/js/templates/shipment/createawb.html
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ <h2>Laboratory Details</h2>
</li>

<li class="clearfix">
<span class="label">Laboratory Address <br /><span class="small">(excluding post code)</span></span>
<span class="label">Laboratory Address <br /><span class="small">(Max 3 lines, excluding post code, city)</span></span>
<div class="ADDRESS text"></div>
</li>

Expand Down

0 comments on commit 2da5639

Please sign in to comment.