diff --git a/api/config_sample.php b/api/config_sample.php
index 164e4735b..b2e40c6e4 100644
--- a/api/config_sample.php
+++ b/api/config_sample.php
@@ -261,6 +261,7 @@
# Shipping service details
$use_shipping_service = null;
+ $use_shipping_service_nde = False;
$use_shipping_service_incoming_shipments = null;
$use_shipping_service_redirect = null;
$use_shipping_service_redirect_incoming_shipments = null;
diff --git a/api/index.php b/api/index.php
index 286e85749..f4116e816 100644
--- a/api/index.php
+++ b/api/index.php
@@ -72,6 +72,7 @@ function setupApplication($mode): Slim
$visit_persist_storage_dir_segment, $dhl_enable, $scale_grid, $scale_grid_end_date, $preset_proposal, $timezone,
$valid_components, $enabled_container_types, $synchweb_version, $redirects,
$shipping_service_app_url, $use_shipping_service_redirect, $use_shipping_service_redirect_incoming_shipments,
+ $use_shipping_service_nde,
$dials_rest_url_rings, $closed_proposal_link, $ccp4_cloud_upload_url,
$only_staff_can_assign, $industrial_prop_codes, $upstream_reprocessing_pipelines, $downstream_reprocessing_pipelines,
$prop_codes_data_deleted, $container_types_with_parents, $bl_capacity;
@@ -99,8 +100,9 @@ function setupApplication($mode): Slim
'enabled_container_types' => $enabled_container_types,
'synchweb_version' => $synchweb_version,
'redirects' => $redirects,
- 'shipping_service_app_url' => $use_shipping_service_redirect || $use_shipping_service_redirect_incoming_shipments ? $shipping_service_app_url : null,
+ 'shipping_service_app_url' => $use_shipping_service_redirect ? $shipping_service_app_url : null,
'shipping_service_app_url_incoming' => $use_shipping_service_redirect_incoming_shipments ? $shipping_service_app_url : null,
+ 'use_shipping_service_nde' => $use_shipping_service_nde,
'dials_rest_url_rings' => $dials_rest_url_rings,
'closed_proposal_link' => $closed_proposal_link,
'ccp4_cloud_upload_url' => $ccp4_cloud_upload_url,
diff --git a/api/src/Page/Shipment.php b/api/src/Page/Shipment.php
index c62014c69..63131f168 100644
--- a/api/src/Page/Shipment.php
+++ b/api/src/Page/Shipment.php
@@ -209,7 +209,7 @@ class Shipment extends Page
array('/dewars/transfer', 'post', '_transfer_dewar'),
array('/dewars/dispatch', 'post', '_dispatch_dewar'),
- array('/dewars/confirmdispatch/did/:did/token/:TOKEN', 'post', '_dispatch_dewar_confirmation'),
+ array('/dewars/confirmdispatch/did/:did/token/:TOKEN', 'post', '_outgoing_shipment_confirmation'),
array('/dewars/confirmpickup/sid/:sid/token/:TOKEN', 'post', '_incoming_shipment_confirmation'),
array('/dewars/tracking(/:DEWARID)', 'get', '_get_dewar_tracking'),
@@ -1180,11 +1180,11 @@ function _dispatch_dewar_in_shipping_service($dispatch_info, $dewar)
function _dispatch_dewar()
{
global $facility_country;
- global $facility_courier_countries;
+ global $facility_courier_countries, $facility_courier_countries_nde;
global $dispatch_email;
global $dispatch_email_regex;
global $dispatch_email_intl;
- global $use_shipping_service;
+ global $use_shipping_service, $use_shipping_service_nde;
global $shipping_service_links_in_emails;
global $use_shipping_service_redirect;
global $shipping_service_app_url;
@@ -1266,7 +1266,10 @@ function _dispatch_dewar()
$data = $this->args;
$data['TERMSACCEPTED'] = $terms_accepted;
- if (Utils::getValueOrDefault($use_shipping_service) && in_array($country, $facility_courier_countries)) {
+ $domestic = in_array($country, $facility_courier_countries);
+ $nde = $use_shipping_service_nde && in_array($country, $facility_courier_countries_nde);
+
+ if (Utils::getValueOrDefault($use_shipping_service) && ($domestic || $nde)) {
if ($terms_accepted) {
if (Utils::getValueOrDefault($use_shipping_service_redirect)) {
try {
@@ -1300,7 +1303,7 @@ function _dispatch_dewar()
# Prepare e-mail response for dispatch request
$use_dispatch_lite_template = (
Utils::getValueOrDefault($use_shipping_service)
- && in_array($country, $facility_courier_countries)
+ && ($domestic || $nde)
&& $terms_accepted
&& Utils::getValueOrDefault($use_shipping_service_redirect)
);
@@ -1389,6 +1392,23 @@ function _dispatch_dewar()
}
}
+ function _outgoing_shipment_confirmation()
+ {
+ if (!$this->has_arg('did'))
+ $this->_error('No dewar specified');
+ if (!$this->has_arg('TOKEN'))
+ $this->_error('No token specified');
+ if (!$this->has_arg('status'))
+ $this->_error('No status specified');
+
+ if ($this->arg('status') === 'CREATED') {
+ $this->_cancel_dispatch_dewar_confirmation();
+ } else if ($this->arg('status') === 'BOOKED' || $this->arg('status') === 'PENDING') {
+ $this->_dispatch_dewar_confirmation();
+ } else {
+ $this->_error('Invalid status');
+ }
+ }
function _dispatch_dewar_confirmation()
{
@@ -1396,9 +1416,6 @@ function _dispatch_dewar_confirmation()
global $dispatch_email_regex;
global $shipping_service_app_url;
- if (!$this->has_arg('did'))
- $this->_error('No dewar specified');
-
# Check token against dewar given
$dew = $this->db->pq(
"SELECT d.dewarid, d.barcode, d.storagelocation, d.dewarstatus, d.externalShippingIdFromSynchrotron, d.facilitycode, d.trackingNumberFromSynchrotron,
@@ -1420,7 +1437,18 @@ function _dispatch_dewar_confirmation()
$this->_error('Incorrect token');
}
- # Prepare e-mail to stores
+ if ($this->arg('status') === 'BOOKED') {
+ if (!$this->has_arg('tracking_number'))
+ $this->_error('No tracking number specified');
+ $tracking = $this->arg('tracking_number');
+ $status = 'dispatch-booked';
+ } else if ($this->arg('status') === 'PENDING') {
+ $tracking = '';
+ $status = 'dispatch-requested';
+ } else {
+ $this->_error('Invalid status');
+ }
+
$data = $this->args;
if (!array_key_exists('prop', $data))
$data['prop'] = $dew['PROPOSAL'];
@@ -1434,54 +1462,89 @@ function _dispatch_dewar_confirmation()
$data['LOCATION'] = $dew['STORAGELOCATION'];
if (!array_key_exists('EMAILADDRESS', $data))
$data['EMAILADDRESS'] = $dew['EMAILADDRESS'];;
- if (!array_key_exists('tracking_number', $data))
- $data['tracking_number'] = $dew['TRACKINGNUMBERFROMSYNCHROTRON'];
- $subject_line = '*** Dispatch requested for Dewar ' . $dew['BARCODE'] . ' from ' . $data['LOCATION'] . ' ***';
- $email_template = 'dewar-dispatch-lite';
- $email = new Email($email_template, $subject_line);
- $email->data = $data;
- // 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
- $local_contact = $this->has_arg('LOCALCONTACT') ? $this->args['LOCALCONTACT'] : '';
- if ($local_contact) {
- $this->args['LCEMAIL'] = $this->_get_email_fn($local_contact);
- if (!$this->args['LCEMAIL']) {
- $this->args['LCEMAIL'] = $this->_get_ispyb_email_fn($local_contact);
+ if ($this->arg('status') === 'BOOKED') {
+ # Prepare e-mail to stores
+ $subject_line = '*** Dispatch requested for Dewar ' . $dew['BARCODE'] . ' from ' . $data['LOCATION'] . ' ***';
+ $email_template = 'dewar-dispatch-lite';
+ $email = new Email($email_template, $subject_line);
+ $email->data = $data;
+
+ // 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
+ $local_contact = $this->has_arg('LOCALCONTACT') ? $this->args['LOCALCONTACT'] : '';
+ if ($local_contact) {
+ $this->args['LCEMAIL'] = $this->_get_email_fn($local_contact);
+ if (!$this->args['LCEMAIL']) {
+ $this->args['LCEMAIL'] = $this->_get_ispyb_email_fn($local_contact);
+ }
}
- }
- $recpts = $dispatch_email;
- if ($data['EMAILADDRESS']) $recpts .= ', ' . $data['EMAILADDRESS'];
- $local_contact_email = $this->has_arg('LCEMAIL') ? $this->args['LCEMAIL'] : '';
- if ($local_contact_email) $recpts .= ', ' . $local_contact_email;
+ $recpts = $dispatch_email;
+ if ($data['EMAILADDRESS']) $recpts .= ', ' . $data['EMAILADDRESS'];
+ $local_contact_email = $this->has_arg('LCEMAIL') ? $this->args['LCEMAIL'] : '';
+ if ($local_contact_email) $recpts .= ', ' . $local_contact_email;
- if (!is_null($dispatch_email_regex)) {
- foreach ($dispatch_email_regex as $address => $pattern) {
- if (preg_match($pattern, $data['BARCODE'])) {
- $recpts .= ', ' . $address;
+ if (!is_null($dispatch_email_regex)) {
+ foreach ($dispatch_email_regex as $address => $pattern) {
+ if (preg_match($pattern, $data['BARCODE'])) {
+ $recpts .= ', ' . $address;
+ }
}
}
- }
- $email->send($recpts);
+ $email->send($recpts);
+ }
// Also update the dewar status and storage location to keep it in sync with history...
$this->db->pq(
"UPDATE dewar
- set dewarstatus='dispatch-requested', storagelocation=lower(:2), trackingnumberfromsynchrotron=:3
- WHERE dewarid=:1",
- array($dew['DEWARID'], $data['LOCATION'], $data['tracking_number'])
+ set dewarstatus=:1, storagelocation=lower(:2), trackingnumberfromsynchrotron=:3
+ WHERE dewarid=:4",
+ array($status, $data['LOCATION'], $tracking, $dew['DEWARID'])
);
- $this->db->pq("UPDATE shipping set shippingstatus='dispatch-requested' WHERE shippingid=:1", array($dew['SHIPPINGID']));
+ $this->db->pq("UPDATE shipping set shippingstatus=:1 WHERE shippingid=:2", array($status, $dew['SHIPPINGID']));
// Update dewar transport history with provided location.
$this->db->pq(
"INSERT INTO dewartransporthistory (dewartransporthistoryid,dewarid,dewarstatus,storagelocation,arrivaldate)
- VALUES (s_dewartransporthistory.nextval,:1,'dispatch-requested',:2,CURRENT_TIMESTAMP)
+ VALUES (s_dewartransporthistory.nextval,:1,:2,:3,CURRENT_TIMESTAMP)
+ RETURNING dewartransporthistoryid INTO :id",
+ array($dew['DEWARID'], $status, $data['LOCATION'])
+ );
+
+ $this->_output(1);
+ }
+
+ function _cancel_dispatch_dewar_confirmation()
+ {
+ // Check token against dewar
+ $dew = $this->db->pq(
+ "SELECT d.dewarid, d.shippingid, json_unquote(json_extract(d.extra, '$.token')) as token, storagelocation
+ FROM dewar d
+ WHERE d.dewarid=:1",
+ array($this->arg('did'))
+ );
+
+ $dew = $dew[0];
+
+ if (!$this->has_arg('TOKEN') || $this->arg('TOKEN') !== $dew['TOKEN']) {
+ $this->_error('Incorrect token');
+ }
+
+ $this->db->pq("UPDATE shipping set shippingstatus='dispatch request cancelled' WHERE shippingid=:1", array($dew['SHIPPINGID']));
+
+ // Update the dewar status
+ $this->db->pq("UPDATE dewar set dewarstatus='dispatch request cancelled' WHERE dewarid=:1", array($dew['DEWARID']));
+
+ // Update dewar transport history
+ $loc = Utils::getValueOrDefault($dew['STORAGELOCATION'], '');
+ $this->db->pq(
+ "INSERT INTO dewartransporthistory (dewartransporthistoryid,dewarid,dewarstatus,storagelocation,arrivaldate)
+ VALUES (s_dewartransporthistory.nextval,:1,'dispatch request cancelled',:2,CURRENT_TIMESTAMP)
RETURNING dewartransporthistoryid INTO :id",
- array($dew['DEWARID'], $data['LOCATION'])
+ array($dew['DEWARID'], $loc)
);
$this->_output(1);
diff --git a/client/src/js/modules/shipment/views/dewars.js b/client/src/js/modules/shipment/views/dewars.js
index 70a7726eb..6d41d1a58 100644
--- a/client/src/js/modules/shipment/views/dewars.js
+++ b/client/src/js/modules/shipment/views/dewars.js
@@ -147,6 +147,8 @@ define(['marionette', 'backbone',
if (app.options.get('shipping_service_app_url') && this.model.get('EXTERNALSHIPPINGIDFROMSYNCHROTRON')) {
let link = app.options.get('shipping_service_app_url')+'/shipment-requests/'+this.model.get('EXTERNALSHIPPINGIDFROMSYNCHROTRON')+'/outgoing'
this.ui.ssd.attr('href', link)
+ this.ui.dispatch.hide()
+ this.ui.transfer.hide()
} else {
this.ui.ssd.hide()
}
diff --git a/client/src/js/modules/shipment/views/dispatch.js b/client/src/js/modules/shipment/views/dispatch.js
index 3304e00b6..2918c5462 100644
--- a/client/src/js/modules/shipment/views/dispatch.js
+++ b/client/src/js/modules/shipment/views/dispatch.js
@@ -119,11 +119,7 @@ define(['marionette', 'views/form',
},
success: function() {
- if (
- app.options.get("shipping_service_app_url")
- && (Number(this.terms.get('ACCEPTED')) === 1) // terms.ACCEPTED could be undefined, 1, or "1"
- && app.options.get("facility_courier_countries").includes(this.dispatchCountry)
- ) {
+ if (this.usingShippingService()) {
this.getOption('dewar').fetch().done((dewar) => {
const external_id = dewar.EXTERNALSHIPPINGIDFROMSYNCHROTRON;
if (external_id === null){
@@ -289,21 +285,29 @@ define(['marionette', 'views/form',
this.ui.facc.show()
}
- if (
- this.terms.get("ACCEPTED")
- && app.options.get("shipping_service_app_url")
- && app.options.get("facility_courier_countries").includes(this.dispatchCountry)
- ){
- this.disableValidation()
- this.ui.dispatchDetails.hide()
- this.ui.submit.text("Proceed")
- this.ui.shippingadvice.html("On clicking 'Proceed' you will be redirected to the new Diamond shipping service to book the shipment. Please ensure all stages of the form are completed.
")
+ if (this.usingShippingService()) {
+ this.useShortFormForShippingService()
} else {
this.ui.submit.text("Request Dewar Dispatch")
this.ui.shippingadvice.html("")
}
},
+ usingShippingService: function() {
+ const domestic = app.options.get("facility_courier_countries").includes(this.ui.country.val())
+ const nde = app.options.get("use_shipping_service_nde") && app.options.get("facility_courier_countries_nde").includes(this.ui.country.val())
+ return this.terms.get("ACCEPTED")
+ && app.options.get("shipping_service_app_url")
+ && (domestic || nde)
+ },
+
+ useShortFormForShippingService: function() {
+ this.disableValidation()
+ this.ui.dispatchDetails.hide()
+ this.ui.submit.text("Proceed")
+ this.ui.shippingadvice.html("On clicking 'Proceed' you will be redirected to the new Diamond shipping service to book the shipment. Please ensure all stages of the form are completed.
")
+ },
+
enableValidation: function() {
this.model.visitRequired = true
this.model.dispatchDetailsRequired = true
@@ -358,14 +362,8 @@ 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.country.val())
- ){
- this.disableValidation()
- this.ui.dispatchDetails.hide()
- this.ui.submit.text("Proceed")
- this.ui.shippingadvice.html("On clicking 'Proceed' you will be redirected to the new Diamond shipping service to book the shipment. Please ensure all stages of the form are completed.
")
+ if (this.usingShippingService()) {
+ this.useShortFormForShippingService()
}
},
diff --git a/client/src/js/utils/validation.js b/client/src/js/utils/validation.js
index fcaf23509..d5a09c1c1 100644
--- a/client/src/js/utils/validation.js
+++ b/client/src/js/utils/validation.js
@@ -15,7 +15,7 @@ define(['backbone', 'backbone-validation'], function(Backbone) {
sequence: /^[>;\s\w+\n\(\)\.\|]+$/,
address: /^(\w|\s|\-|\n|,)+$/,
array: /^[\d+(.\d+)?),]+$/,
- country: /^(\w|\s|\-|,|\(|\)|')+$/,
+ country: /^(\w|\s|\-|,|\(|\)|'|\.)+$/,
visit: /^\w+\d+-\d+$/,
visitornull: /^\w+\d+-\d+$|^(?![\s\S])/,
twopath: /^(\w|-)+\/?(\w|-)+$/,
@@ -38,7 +38,7 @@ define(['backbone', 'backbone-validation'], function(Backbone) {
sequence: 'This field may only contain word characters and line returns',
address: 'This field may only contain word character, spaces, and line returns',
array: 'This field may only contain numbers and commas',
- country: 'This field must contain only letters, numbers, spaces, underscores, dashes, and commas',
+ country: 'This field must contain only letters, numbers, spaces, underscores, dashes, dots and commas',
visit: 'This field must be of the format xxx123-123',
visitornull: 'This field must be of the format xxx123-123 or a null value',
twopath: 'This field can hold a path with two folders',