Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LIMS-1113: Shipping service dispatch redirect #698

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions api/assets/emails/html/dewar-dispatch-lite.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<h1 style="font-size: 16px; font-weight: normal; border-bottom: 1px solid #000; margin: 1%; margin-bottom: 1.5%">Dewar ready to leave diamond</h1>

<div class="inset" style="margin: 1%; padding: 1%; margin-bottom: 2%; border-radius: 5px; background: #82d180">
Goods Handling, please arrange dispatch of the following Dewar.
</div>

<table class="details" style="background: #f4f4f4; margin: 1%">
<tbody>
<tr>
<td style="padding: 1.5%; font-weight: bold">Proposal</td>
<td><?php echo $data['prop'] ?></td>
</tr>
<tr>
<td style="padding: 1.5%; font-weight: bold">Air Waybill</td>
<td><a href="<?php echo $data['AWBURL']; ?>"><?php echo $data['AWBURL']; ?></a></td>
</tr>
<tr>
<td style="padding: 1.5%; font-weight: bold">Dewar Facility Code</td>
<td><?php echo $data['FACILITYCODE'] ?></td>
</tr>
<tr>
<td style="padding: 1.5%; font-weight: bold">Dewar Barcode</td>
<td><?php echo $data['BARCODE'] ?></td>
</tr>
<tr>
<td style="padding: 1.5%; font-weight: bold">Current Location</td>
<td><?php echo $data['LOCATION'] ?></td>
</tr>
</tbody>
</table>
1 change: 1 addition & 0 deletions api/config_sample.php
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@
# Shipping service details
$use_shipping_service = null;
$use_shipping_service_incoming_shipments = null;
$use_shipping_service_redirect = null;
$shipping_service_api_url = null;
$shipping_service_api_user = null;
$shipping_service_api_password = null;
Expand Down
5 changes: 3 additions & 2 deletions api/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ function setupApplication($mode): Slim
global $motd, $authentication_type, $cas_url, $cas_sso, $sso_url, $package_description,
$facility_courier_countries, $facility_courier_countries_nde,
$dhl_enable, $dhl_link, $scale_grid, $scale_grid_end_date, $preset_proposal, $timezone,
$valid_components, $enabled_container_types, $ifsummary, $synchweb_version;
$valid_components, $enabled_container_types, $ifsummary, $synchweb_version, $shipping_service_app_url, $use_shipping_service_redirect;
$app->contentType('application/json');
$options = $app->container['options'];
$app->response()->body(json_encode(array(
Expand All @@ -91,7 +91,8 @@ function setupApplication($mode): Slim
'valid_components' => $valid_components,
'enabled_container_types' => $enabled_container_types,
'ifsummary' => $ifsummary,
'synchweb_version' => $synchweb_version
'synchweb_version' => $synchweb_version,
'shipping_service_app_url' => $use_shipping_service_redirect ? $shipping_service_app_url : null,
)));
});
return $app;
Expand Down
100 changes: 90 additions & 10 deletions api/src/Page/Shipment.php
Original file line number Diff line number Diff line change
Expand Up @@ -924,6 +924,60 @@ function _transfer_dewar()
$this->_output(1);
}

function _dispatch_dewar_shipment_request($dewar)
{
if (!is_null($dewar['EXTERNALSHIPPINGIDFROMSYNCHROTRON'])) {
return $dewar['EXTERNALSHIPPINGIDFROMSYNCHROTRON'];
}
$server_port = ($_SERVER['SERVER_PORT']==='443') ? '' : ":{$_SERVER['SERVER_PORT']}";
$shipment_request_info = array(
"proposal" => $dewar['PROPOSAL'],
"external_id" => (int) $dewar['DEWARID'],
"origin_url" => "https://{$_SERVER['SERVER_NAME']}{$server_port}/shipments/sid/{$dewar['SHIPPINGID']}",
"packages" => [
[
"external_id" => (int) $dewar['DEWARID'],
"shippable_item_type" => "CRYOGENIC_DRY_SHIPPER_CASE",
"line_items" => [
[
"shippable_item_type" => "CRYOGENIC_DRY_SHIPPER",
"quantity" => 1
],
[
"shippable_item_type" => "SHELVED_UNI_PUCK_SHIPPING_CANE",
"quantity" => 1
],
]
]
]
);
if ((int) $dewar['NUM_PUCKS'] > 0) {
array_push(
$shipment_request_info["packages"][0]["line_items"],
[
"shippable_item_type"=> "UNI_PUCK",
"quantity" => (int) $dewar['NUM_PUCKS']
]
);
}
if ((int) $dewar['NUM_SAMPLES'] > 0) {
array_push(
$shipment_request_info["packages"][0]["line_items"],
[
"shippable_item_type"=> "SPINE_SAMPLE_HOLDER",
"quantity" => (int) $dewar['NUM_SAMPLES']
]
);
}
$response = $this->shipping_service->create_shipment_request($shipment_request_info);
$external_shipping_id = $response['shipmentRequestId'];
$this->db->pq(
"UPDATE dewar SET externalShippingIdFromSynchrotron=:1 WHERE dewarid=:2",
array($external_shipping_id, $dewar['DEWARID'])
);
return $external_shipping_id;
}

function _dispatch_dewar_in_shipping_service($dispatch_info, $dewar)
{
global $facility_company;
Expand Down Expand Up @@ -1000,6 +1054,8 @@ function _dispatch_dewar()
global $dispatch_email_intl;
global $use_shipping_service;
global $shipping_service_links_in_emails;
global $use_shipping_service_redirect;
global $shipping_service_app_url;
// Variable to store where the dewar is (Synchrotron or eBIC building)
// Could map this to dewar storage locations in ISPyB to make more generic...?
$dispatch_from_location = 'Synchrotron';
Expand All @@ -1014,10 +1070,12 @@ function _dispatch_dewar()
}

$dew = $this->db->pq(
"SELECT d.dewarid, d.barcode, d.storagelocation, d.dewarstatus, s.shippingid, CONCAT(p.proposalcode, p.proposalnumber) as proposal
"SELECT d.dewarid, d.barcode, d.storagelocation, d.dewarstatus, d.externalShippingIdFromSynchrotron, s.shippingid, CONCAT(p.proposalcode, p.proposalnumber) as proposal, count(distinct c.containerId) as num_pucks, count(b.blsampleId) as num_samples
FROM dewar d
INNER JOIN shipping s ON s.shippingid = d.shippingid
INNER JOIN proposal p ON p.proposalid = s.proposalid
LEFT JOIN container c on c.dewarid = d.dewarid
LEFT JOIN BLSample b on b.containerId = c.containerId
WHERE d.dewarid=:1 and p.proposalid=:2",
array($this->arg('DEWARID'), $this->proposalid)
);
Expand All @@ -1032,7 +1090,7 @@ function _dispatch_dewar()
// If no location specified (i.e. deleted), then read from dewar transport history.
// If no dewar transport history fall back to dewar location
// We still update history based on provided location to record action from user
$dewar_location = $this->has_arg('LOCATION)') ? $this->arg('LOCATION') : "";
$dewar_location = $this->has_arg('LOCATION') ? $this->arg('LOCATION') : "";

if (empty($dewar_location)) {
// What was the last history entry for this dewar?
Expand Down Expand Up @@ -1080,21 +1138,42 @@ function _dispatch_dewar()

if (Utils::getValueOrDefault($use_shipping_service) && in_array($country, $facility_courier_countries)) {
if ($terms_accepted) {
try {
$shipment_id = $this->_dispatch_dewar_in_shipping_service($data, $dew);
if (Utils::getValueOrDefault($use_shipping_service_redirect)) {
try {
$shipment_id = $this->_dispatch_dewar_shipment_request($dew);
} catch (Exception $e) {
error_log("Error returned from shipping service: " . $e . "\nDewar data: " . json_encode($dew));
$error_response = json_decode($e->getMessage());
$this->_error($error_response->content->detail, $error_response->status);
}
if (Utils::getValueOrDefault($shipping_service_links_in_emails)) {
$data['AWBURL'] = "{$shipping_service_app_url}/shipment-requests/{$shipment_id}/outgoing";
}
} else {
try {
$shipment_id = $this->_dispatch_dewar_in_shipping_service($data, $dew);
} catch (Exception $e) {
error_log($e);
$data['AWBURL'] = "";
}
if (Utils::getValueOrDefault($shipping_service_links_in_emails)) {
$data['AWBURL'] = $this->shipping_service->get_awb_pdf_url($shipment_id);
}
} catch (Exception $e) {
error_log($e);
$data['AWBURL'] = "";
}
}
}

# Prepare e-mail response for dispatch request
$subject_line = '*** Dispatch requested for Dewar ' . $dew['BARCODE'] . ' from ' . $dispatch_from_location . ' - Pickup Date: ' . $this->args['DELIVERYAGENT_SHIPPINGDATE'] . ' ***';
$email = new Email('dewar-dispatch', $subject_line);
$use_dispatch_lite_template = (
Utils::getValueOrDefault($use_shipping_service)
&& in_array($country, $facility_courier_countries)
&& $terms_accepted
&& Utils::getValueOrDefault($use_shipping_service_redirect)
);
$subject_pickup_date = $use_dispatch_lite_template ? '' : ' - Pickup Date: ' . $this->args['DELIVERYAGENT_SHIPPINGDATE'];
$subject_line = '*** Dispatch requested for Dewar ' . $dew['BARCODE'] . ' from ' . $dispatch_from_location . $subject_pickup_date . ' ***';
$email_template = $use_dispatch_lite_template ? 'dewar-dispatch-lite' : 'dewar-dispatch';
$email = new Email($email_template, $subject_line);

// If a local contact is given, try to find their email address
// First try LDAP, if unsuccessful look at the ISPyB person record for a matching staff user
Expand All @@ -1120,6 +1199,7 @@ function _dispatch_dewar()
$data['LOCALCONTACT'] = $local_contact;
if (!array_key_exists('LCEMAIL', $data))
$data['LCEMAIL'] = '';
$data['BARCODE'] = $dew['BARCODE'];
$email->data = $data;

if ($country != $facility_country && !is_null($dispatch_email_intl)) {
Expand Down Expand Up @@ -1343,7 +1423,7 @@ function _get_dewars()
$order = $cols[$this->arg('sort_by')] . ' ' . $dir;
}

$dewars = $this->db->paginate("SELECT CONCAT(p.proposalcode, p.proposalnumber) as prop, CONCAT(p.proposalcode, p.proposalnumber, '-', se.visit_number) as firstexperiment, r.labcontactid, se.beamlineoperator as localcontact, se.beamlinename, TO_CHAR(se.startdate, 'HH24:MI DD-MM-YYYY') as firstexperimentst, d.firstexperimentid, s.shippingid, s.shippingname, d.facilitycode, count(c.containerid) as ccount, (case when se.visit_number > 0 then (CONCAT(p.proposalcode, p.proposalnumber, '-', se.visit_number)) else '' end) as exp, d.code, d.barcode, d.storagelocation, d.dewarstatus, d.dewarid, d.trackingnumbertosynchrotron, d.trackingnumberfromsynchrotron, s.deliveryagent_agentname, d.weight, d.deliveryagent_barcode, GROUP_CONCAT(c.code SEPARATOR ', ') as containers, s.sendinglabcontactid, s.returnlabcontactid, pe.givenname, pe.familyname
$dewars = $this->db->paginate("SELECT CONCAT(p.proposalcode, p.proposalnumber) as prop, CONCAT(p.proposalcode, p.proposalnumber, '-', se.visit_number) as firstexperiment, r.labcontactid, se.beamlineoperator as localcontact, se.beamlinename, TO_CHAR(se.startdate, 'HH24:MI DD-MM-YYYY') as firstexperimentst, d.firstexperimentid, s.shippingid, s.shippingname, d.facilitycode, count(c.containerid) as ccount, (case when se.visit_number > 0 then (CONCAT(p.proposalcode, p.proposalnumber, '-', se.visit_number)) else '' end) as exp, d.code, d.barcode, d.storagelocation, d.dewarstatus, d.dewarid, d.trackingnumbertosynchrotron, d.trackingnumberfromsynchrotron, d.externalShippingIdFromSynchrotron, s.deliveryagent_agentname, d.weight, d.deliveryagent_barcode, GROUP_CONCAT(c.code SEPARATOR ', ') as containers, s.sendinglabcontactid, s.returnlabcontactid, pe.givenname, pe.familyname
FROM dewar d
LEFT OUTER JOIN container c ON c.dewarid = d.dewarid
INNER JOIN shipping s ON d.shippingid = s.shippingid
Expand Down
10 changes: 10 additions & 0 deletions api/src/Shipment/ShippingService.php
Original file line number Diff line number Diff line change
Expand Up @@ -151,4 +151,14 @@ function get_awb_pdf_url($shipment_id)
{
return $this->shipping_app_url . '/shipments/' . $shipment_id . '/awb';
}

function create_shipment_request($shipment_request_data)
{
return $this->_send_request(
$this->shipping_api_url . '/shipment_requests/',
"POST",
$shipment_request_data,
201
);
}
}
5 changes: 4 additions & 1 deletion client/src/js/modules/shipment/models/dispatch.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ define(['backbone'], function(Backbone) {
},

VISIT: {
required: true,
required: function() {
return this.visitRequired
},
pattern: 'visit',
},

Expand Down Expand Up @@ -108,6 +110,7 @@ define(['backbone'], function(Backbone) {

courierDetailsRequired: false, // We want to set this default to false unless 'DELIVERYAGENT_AGENTCODE' has a value in the shipment model
postCodeRequired: false,
visitRequired: true,
})

})
66 changes: 64 additions & 2 deletions client/src/js/modules/shipment/views/dispatch.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ define(['marionette', 'views/form',
'click @ui.facc': 'showTerms',
'blur @ui.postCode': 'stripPostCode',
'blur @ui.addr': 'formatAddress',
'change @ui.country': 'checkPostCodeRequired'
'change @ui.country': 'checkPostCodeRequired',
'change @ui.dispatchCountry': 'showDispatchForm'
},

ui: {
Expand All @@ -80,17 +81,24 @@ define(['marionette', 'views/form',
ph: 'input[name=PHONENUMBER]',
lab: 'input[name=LABNAME]',

submit: 'button[name=submit]',

facc: 'a.facc',
accountNumber: 'input[NAME=DELIVERYAGENT_AGENTCODE]',
courier: 'input[name=DELIVERYAGENT_AGENTNAME]',
courierDetails: '.courierDetails',
dispatchDetails: '.dispatchDetails',
facilityCourier: '.facilityCourier',
awbNumber: 'input[name=AWBNUMBER]',
useAnotherCourierAccount: 'input[name=USE_ANOTHER_COURIER_ACCOUNT]',
dispatchState: '.dispatch-state'
dispatchState: '.dispatch-state',

dispatchCountry: 'select[name=DISPATCHCOUNTRY]',
courierSection: '.courierSection'
},

labContactCountry : null,
dispatchCountry: null,

templateHelpers: function() {
return {
Expand All @@ -112,6 +120,18 @@ define(['marionette', 'views/form',

success: function() {
app.trigger('shipment:show', this.getOption('dewar').get('SHIPPINGID'))
if (
app.options.get("shipping_service_app_url")
&& (Number(this.terms.get('ACCEPTED')) === 1) // terms.ACCPETED could be undefined, 1, or "1"
&& app.options.get("facility_courier_countries").includes(this.dispatchCountry)
) {
this.getOption('dewar').fetch().done((dewar) => {
const external_id = dewar.EXTERNALSHIPPINGIDFROMSYNCHROTRON;
window.location.assign(
`${app.options.get("shipping_service_app_url")}/shipment-requests/${external_id}/outgoing`
)
})
}
},

failure: function() {
Expand All @@ -120,6 +140,7 @@ define(['marionette', 'views/form',

onRender: function() {
this.date('input[name=DELIVERYAGENT_SHIPPINGDATE]')
this.$el.hide()

var d = new Date()
var today = (d.getDate() < 10 ? '0'+d.getDate() : d.getDate()) + '-' + (d.getMonth() < 9 ? '0'+(d.getMonth()+1) : d.getMonth()+1) + '-' + d.getFullYear()
Expand Down Expand Up @@ -147,9 +168,20 @@ define(['marionette', 'views/form',
doOnRender: function() {
this.ui.exp.html(this.visits.opts()).val(this.model.get('VISIT'))
this.updateLC()
this.populateDispatchCountries()
this.populateCountries()
this.stripPostCode()
this.formatAddress()
this.$el.show()
this.hideDispatchForm();
},

populateDispatchCountries: function () {
const dispatchCountryOptions = [...app.options.get("facility_courier_countries"), 'Other']
.map((country) => `<option>${country}</option>`)
.join("");
this.ui.dispatchCountry.html(dispatchCountryOptions);
this.ui.dispatchCountry.val('');
},

populateCountries: function() {
Expand Down Expand Up @@ -240,6 +272,28 @@ define(['marionette', 'views/form',
}
},

hideDispatchForm: function () {
this.ui.courierSection.hide();
this.ui.dispatchDetails.hide();
this.ui.submit.hide();
},

showDispatchForm: function() {
this.dispatchCountry = this.ui.dispatchCountry.val()
this.ui.courierSection.show();
this.ui.dispatchDetails.show();
this.ui.submit.show();
if (
this.terms.get("ACCEPTED")
&& app.options.get("shipping_service_app_url")
&& app.options.get("facility_courier_countries").includes(this.dispatchCountry)
){
this.model.visitRequired = false
this.ui.dispatchDetails.hide()
this.ui.submit.text("Proceed")
}
},

showTerms: function() {
var terms = new TCDialog({ model: this.terms })
this.listenTo(terms, 'terms:accepted', this.termsAccepted, this)
Expand All @@ -266,6 +320,14 @@ define(['marionette', 'views/form',
this.ui.courierDetails.hide()
this.ui.facilityCourier.show()
this.model.courierDetailsRequired = false
if (
app.options.get("shipping_service_app_url")
&& app.options.get("facility_courier_countries").includes(this.ui.dispatchCountry.val())
){
this.model.visitRequired = false
this.ui.dispatchDetails.hide()
this.ui.submit.text("Proceed")
}
},

checkPostCodeRequired: function() {
Expand Down
7 changes: 6 additions & 1 deletion client/src/js/templates/shipment/dewarlistrow.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@
<a class="button button-notext add" title="Click to add a container" href="/containers/add/did/<%-DEWARID%>"><i class="fa fa-plus"></i> <span>Add Container</span></a>

<% if (STORAGELOCATION != 'stores-out') { %>
<a class="button button-notext" title="Click to dispatch dewar back to your lab" href="/dewars/dispatch/<%-DEWARID%>"><i class="fa fa-home"></i> <span>Dispatch Dewar</span></a>
<% if (app.options.get("shipping_service_app_url") && EXTERNALSHIPPINGIDFROMSYNCHROTRON) { %>
<% const link = `${app.options.get("shipping_service_app_url")}/shipment-requests/${EXTERNALSHIPPINGIDFROMSYNCHROTRON}/outgoing` %>
<a class="button button-notext" title="Click to view dispatch information" href="<%-link%>"><i class="fa fa-home"></i> <span>Dispatch Dewar</span></a>
<% } else { %>
<a class="button button-notext" title="Click to dispatch dewar back to your lab" href="/dewars/dispatch/<%-DEWARID%>"><i class="fa fa-home"></i> <span>Dispatch Dewar</span></a>
<% } %>
<a class="button button-notext" title="Click to transfer dewar to another beamline" href="/dewars/transfer/<%-DEWARID%>"><i class="fa fa-arrows-h"></i> <span>Transfer Dewar</span></a>
<% } %>
</td>
Expand Down
Loading
Loading