diff --git a/api/src/Page.php b/api/src/Page.php index a5ee4c334..efb2d1cbb 100644 --- a/api/src/Page.php +++ b/api/src/Page.php @@ -649,6 +649,11 @@ function argOrNull($key) return $this->has_arg($key) ? $this->arg($key) : null; } + function argIfNotEmptyString($key) + { + return $this->has_arg($key) && $this->arg($key) !== '' ? $this->arg($key) : null; + } + # ------------------------------------------------------------------------ # Misc Helpers diff --git a/api/src/Page/Shipment.php b/api/src/Page/Shipment.php index 0019c1e77..31637312f 100644 --- a/api/src/Page/Shipment.php +++ b/api/src/Page/Shipment.php @@ -2607,8 +2607,8 @@ function _add_container() $tem = $this->has_arg('STORAGETEMPERATURE') ? $this->arg('STORAGETEMPERATURE') : null; $crid = $this->has_arg('CONTAINERREGISTRYID') ? $this->arg('CONTAINERREGISTRYID') : null; - $pcid = $this->has_arg('PARENTCONTAINERID') ? $this->arg('PARENTCONTAINERID') : null; - $pcl = $this->has_arg('PARENTCONTAINERLOCATION') ? $this->arg('PARENTCONTAINERLOCATION') : null; + $pcid = $this->argIfNotEmptyString('PARENTCONTAINERID'); + $pcl = $this->argIfNotEmptyString('PARENTCONTAINERLOCATION'); $pipeline = $this->has_arg('PROCESSINGPIPELINEID') ? $this->arg('PROCESSINGPIPELINEID') : null; $source = $this->has_arg('SOURCE') ? $this->arg('SOURCE') : null; @@ -2640,7 +2640,7 @@ function _add_container() if ($e->getCode() == 1062) { $this->_error('Barcode is not unique. Please enter a different barcode.', 409); } else { - $this->_error('An unexpected error occurred.', 500); + $this->_error('An unexpected error occurred: ' . $e->getMessage(), 500); } } } @@ -3451,6 +3451,10 @@ function _create_awb() && in_array($this->arg('COUNTRY'), $facility_courier_countries) && Utils::getValueOrDefault($use_shipping_service_redirect_incoming_shipments) ) { + if ($ship['EXTERNALSHIPPINGIDTOSYNCHROTRON']) { + $this->_error("Shipping service error: Booking already exists"); + return; + } try { $this->_create_shipment_shipment_request($ship, $dewars); $this->_output(array('EXTERNAL' => "1")); diff --git a/client/package-lock.json b/client/package-lock.json index 3c41082c6..12a9e713e 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -22,8 +22,6 @@ "d3-scale": "^3.2.3", "d3-scale-chromatic": "^2.0.0", "d3-selection": "^2.0.0", - "date-fns": "^2.16.1", - "date-fns-tz": "^1.0.12", "flot-axislabels": "^1.0.0", "flot-pie": "^1.0.0", "font-awesome": "^4.2.0", @@ -1741,6 +1739,7 @@ "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.5.tgz", "integrity": "sha512-ecjvYlnAaZ/KVneE/OdKYBYfgXV3Ptu6zQWmgEF7vwKhQnvVS6bjMD2XYgj+SNvQ1GfK/pjgokfPkC/2CO8CuA==", + "dev": true, "dependencies": { "regenerator-runtime": "^0.13.11" }, @@ -5815,29 +5814,6 @@ "node": ">=12" } }, - "node_modules/date-fns": { - "version": "2.30.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", - "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", - "dependencies": { - "@babel/runtime": "^7.21.0" - }, - "engines": { - "node": ">=0.11" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/date-fns" - } - }, - "node_modules/date-fns-tz": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/date-fns-tz/-/date-fns-tz-1.3.8.tgz", - "integrity": "sha512-qwNXUFtMHTTU6CFSFjoJ80W8Fzzp24LntbjFFBgL/faqds4e5mo9mftoRLgr3Vi1trISsg4awSpYVsOQCRnapQ==", - "peerDependencies": { - "date-fns": ">=2.0.0" - } - }, "node_modules/de-indent": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", @@ -14438,7 +14414,8 @@ "node_modules/regenerator-runtime": { "version": "0.13.11", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "dev": true }, "node_modules/regenerator-transform": { "version": "0.15.1", diff --git a/client/package.json b/client/package.json index 4808d17de..befe08a72 100644 --- a/client/package.json +++ b/client/package.json @@ -70,8 +70,6 @@ "d3-scale": "^3.2.3", "d3-scale-chromatic": "^2.0.0", "d3-selection": "^2.0.0", - "date-fns": "^2.16.1", - "date-fns-tz": "^1.0.12", "flot-axislabels": "^1.0.0", "flot-pie": "^1.0.0", "font-awesome": "^4.2.0", diff --git a/client/src/js/models/shipment.js b/client/src/js/models/shipment.js index b55ddc00e..384881b21 100644 --- a/client/src/js/models/shipment.js +++ b/client/src/js/models/shipment.js @@ -11,6 +11,7 @@ define(['backbone'], function (Backbone) { SHIPPINGNAME: { required: true, pattern: 'wwsdash', + msg: 'The Shipment Name is required', }, 'FCODES[]': { @@ -18,8 +19,14 @@ define(['backbone'], function (Backbone) { pattern: 'fcode', }, + SAFETYLEVEL: { + required: true, + msg: 'The Safety Level field is required', + }, + DYNAMIC: { required: true, + msg: 'The Scheduling field is required', }, FIRSTEXPERIMENTID: { @@ -50,10 +57,11 @@ define(['backbone'], function (Backbone) { SENDINGLABCONTACTID: { required: true, + msg: 'The Outgoing Lab Contact field is required', }, RETURNLABCONTACTID: { - required: true, + required: false, }, DELIVERYAGENT_AGENTCODE: { diff --git a/client/src/js/modules/imaging/views/imageviewer.js b/client/src/js/modules/imaging/views/imageviewer.js index 8f39b5c68..6ea6bf3f2 100644 --- a/client/src/js/modules/imaging/views/imageviewer.js +++ b/client/src/js/modules/imaging/views/imageviewer.js @@ -1029,11 +1029,11 @@ define(['marionette', if (options.o.get('DISPENSEX') && options.o.get('DISPENSEY')) { var disx = parseInt(options.o.get('DISPENSEX')) var disy = parseInt(options.o.get('DISPENSEY')) - this.ctx.strokeStyle = 'white' + this.ctx.strokeStyle = 'deeppink' this.ctx.beginPath() this.ctx.arc(disx, disy, 50, 0, 2*Math.PI) this.ctx.stroke() - this.ctx.fillStyle = 'white' + this.ctx.fillStyle = 'deeppink' this.ctx.fillText('D',disx-5*m, disy+5*m) this.ctx.closePath() } diff --git a/client/src/js/modules/shipment/views/container.js b/client/src/js/modules/shipment/views/container.js index fc1031715..755e4a18a 100644 --- a/client/src/js/modules/shipment/views/container.js +++ b/client/src/js/modules/shipment/views/container.js @@ -19,7 +19,6 @@ define(['marionette', 'views/table', 'utils', - 'formatDate', 'utils/editable', 'templates/shipment/container.html'], function(Marionette, Backbone, @@ -40,7 +39,7 @@ define(['marionette', TableView, - utils, formatDate, + utils, Editable, template){ return Marionette.LayoutView.extend({ @@ -186,7 +185,7 @@ define(['marionette', app.alert({ message: 'Container Successfully Queued' }) self.model.set({ CONTAINERQUEUEID: resp.CONTAINERQUEUEID, - QUEUEDTIMESTAMP: formatDate.default(new Date(), 'dd-MM-yyyy HH:mm') + QUEUEDTIMESTAMP: new Date().toLocaleString() }) self.updateAutoCollection() self.sampletable.toggleAuto(true) diff --git a/client/src/js/modules/shipment/views/createawb.js b/client/src/js/modules/shipment/views/createawb.js index 62b3e43b0..9d5373bb9 100644 --- a/client/src/js/modules/shipment/views/createawb.js +++ b/client/src/js/modules/shipment/views/createawb.js @@ -468,6 +468,14 @@ define(['backbone', return } + const ss_url = app.options.get("shipping_service_app_url_incoming") + const externalid = this.shipment.get('EXTERNALSHIPPINGIDTOSYNCHROTRON') + if (externalid && ss_url) { + const link = ss_url+'/shipment-requests/'+externalid+'/incoming' + window.location.assign(link) + return + } + var prod = null if ( ( @@ -510,7 +518,7 @@ define(['backbone', }, success: function(resp) { if ( - app.options.get("shipping_service_app_url_incoming") + ss_url && (Number(self.terms.get('ACCEPTED')) === 1) // terms.ACCEPTED could be undefined, 1, or "1" && app.options.get("facility_courier_countries").includes(country) ) { @@ -521,9 +529,7 @@ define(['backbone', app.alert({message: "Error performing redirect: external shipping id is null"}) return; } - window.location.assign( - `${app.options.get("shipping_service_app_url")}/shipment-requests/${external_id}/incoming` - ) + window.location.assign(`${ss_url}/shipment-requests/${external_id}/incoming`) }) } else { app.message({ message: 'Air Waybill Successfully Created'}) diff --git a/client/src/js/modules/shipment/views/dewarregistry.js b/client/src/js/modules/shipment/views/dewarregistry.js index 806868bf1..e9ee0bd9a 100644 --- a/client/src/js/modules/shipment/views/dewarregistry.js +++ b/client/src/js/modules/shipment/views/dewarregistry.js @@ -6,13 +6,12 @@ define(['marionette', 'backgrid', 'views/form', 'views/filter', 'views/table', 'utils/table', - 'formatDate', 'templates/shipment/dewarregistryadd.html', 'templates/shipment/dewarregistry.html'], function(Marionette, Backgrid, DewarRegistry, DewarProposal, Proposals, LabContacts, FormView, FilterView, - TableView, table, formatDate, addtemplate, template) { + TableView, table, addtemplate, template) { var ClickableRow = table.ClickableRow.extend({ event: 'rdewar:show', @@ -49,7 +48,7 @@ define(['marionette', 'backgrid', this.ui.fc.val('') this.ui.date.val('') this.ui.serial.val('') - this.model.set({ DEWARS: 0, REPORTS: 0, BLTIMESTAMP: formatDate.default(new Date(), 'yyyy-MM-dd HH:mm:ss') }) + this.model.set({ DEWARS: 0, REPORTS: 0, BLTIMESTAMP: new Date().toLocaleString('sv-SE') }) this.trigger('model:saved', this.model) this.setupValidation() }, diff --git a/client/src/js/modules/shipment/views/shipment.js b/client/src/js/modules/shipment/views/shipment.js index 8465c49a5..4e3ea848d 100644 --- a/client/src/js/modules/shipment/views/shipment.js +++ b/client/src/js/modules/shipment/views/shipment.js @@ -49,6 +49,8 @@ define(['marionette', 'click #add_dewar': 'addDewar', 'click a.send': 'sendShipment', 'click a.pdf': utils.signHandler, + 'click a.labels': 'printLabels', + 'click a.awb': 'createAWB', 'click a.cancel_pickup': 'cancelPickup', 'click a.ready': 'markAsReady', }, @@ -61,7 +63,6 @@ define(['marionette', sent: '.sent', booking: '.booking', dhlmessage: '.dhlmessage', - buttons: '.buttons', }, @@ -102,13 +103,31 @@ define(['marionette', }) }, + printLabels: function(e) { + e.preventDefault() + const errors = this.model.validate(this.model.attributes) + if (errors) { + app.alert({ message: 'Cannot print labels: ' + Object.values(errors)[0] }) + } else { + utils.signHandler(e) + } + }, + + createAWB: function(e) { + const errors = this.model.validate(this.model.attributes) + if (errors) { + e.preventDefault() + app.alert({ message: 'Cannot create air waybill: ' + Object.values(errors)[0] }) + } + }, + sendShipment: function(e) { e.preventDefault() var self = this Backbone.ajax({ url: app.apiurl+'/shipment/send/'+this.model.get('SHIPPINGID'), success: function() { - self.model.set({ SHIPPINGSTATUS: 'send to DLS' }) + self.model.set({ SHIPPINGSTATUS: 'sent to facility' }) app.alert({ className: 'message notify', message: 'Shipment successfully marked as sent' }) self.render() }, @@ -209,11 +228,6 @@ define(['marionette', }, showButtons: function() { - if (this.model.get('LCOUT') && this.model.get('SAFETYLEVEL')) { - this.ui.buttons.show() - } else { - this.ui.buttons.hide() - } const status = this.model.get('SHIPPINGSTATUS') const proc = this.model.get('PROCESSING') if ((status === 'opened' || status === 'awb created' || status === 'pickup booked') && proc == 0) { @@ -242,7 +256,7 @@ define(['marionette', this.ui.booking.html(' Create Air Waybill - Disabled') } else if (externalid && ss_url) { const link = ss_url+'/shipment-requests/'+externalid+'/incoming' - this.ui.booking.html(' Manage shipment booking') + this.ui.booking.html(' Manage Shipment Booking') } else { this.ui.booking.html(' Create DHL Air Waybill') } @@ -257,13 +271,12 @@ define(['marionette', const safetylevel = this.model.get('SAFETYLEVEL') const country = this.model.get('COUNTRY') const courier = this.model.get('DELIVERYAGENT_AGENTNAME') - const lcout = this.model.get('LCOUT') const fac_country_nde = app.options.get('facility_courier_countries_nde') const fac_country_link = app.options.get('facility_courier_countries_link') const fac_country = app.options.get('facility_courier_countries') - if (!lcout || !safetylevel) { - this.ui.dhlmessage.html('

Set an Outgoing Lab Contact and Safety Level in order to manage shipping.

') - } else if (label == '1') { + const ss_url = app.options.get("shipping_service_app_url_incoming") + const externalid = this.model.get('EXTERNALSHIPPINGIDTOSYNCHROTRON') + if (label == '1') { this.ui.dhlmessage.html('

You can print your Air Waybill by clicking "Print Air Waybill" below.

') } else if (safetylevel && safetylevel == "Red") { this.ui.dhlmessage.html('

Shipping of red samples is not available through this application.

') @@ -273,6 +286,8 @@ define(['marionette', this.ui.dhlmessage.html('

International shipping is not available through this application. If you're arranging your own shipping, enter your tracking numbers below after booking and include printed return labels in the dewar case.

') } else if (courier && courier.toLowerCase().trim() != 'dhl') { this.ui.dhlmessage.html('

Shipping through this application is only available using DHL.

') + } else if (externalid && ss_url) { + this.ui.dhlmessage.html('

You can now manage your shipment with DHL using "Manage Shipment Booking" below.

') } else { this.ui.dhlmessage.html('

You can now book your shipment with DHL using "Create Air Waybill" below.

') } diff --git a/client/src/js/modules/stats/views/breakdown.js b/client/src/js/modules/stats/views/breakdown.js index 4a29a2f5d..dc2e055b5 100644 --- a/client/src/js/modules/stats/views/breakdown.js +++ b/client/src/js/modules/stats/views/breakdown.js @@ -1,12 +1,11 @@ define(['marionette', 'templates/stats/breakdown.html', 'utils', - 'formatDate', 'jquery', 'jquery.flot', 'jquery.flot.time', 'jquery.flot.selection', 'jquery.flot.tooltip', - ], function(Marionette, template, utils, formatDate, $) { + ], function(Marionette, template, utils, $) { return Marionette.ItemView.extend({ template: template, @@ -101,8 +100,8 @@ define(['marionette', 'templates/stats/breakdown.html', if (this.hideOverview) return var opts = this.main.getOptions() - var from = formatDate.default(opts.xaxes[0].min, 'MMMM do yyyy') - var to = formatDate.default(opts.xaxes[0].max, 'MMMM do yyyy') + var from = new Date(opts.xaxes[0].min).toLocaleDateString(); + var to = new Date(opts.xaxes[0].max).toLocaleDateString(); if (from != to) this.ui.span.text(from+' - '+to) else this.ui.span.text(from) diff --git a/client/src/js/modules/types/mx/shipment/views/mx-container-add.vue b/client/src/js/modules/types/mx/shipment/views/mx-container-add.vue index 6c60d3878..c2875533a 100644 --- a/client/src/js/modules/types/mx/shipment/views/mx-container-add.vue +++ b/client/src/js/modules/types/mx/shipment/views/mx-container-add.vue @@ -751,8 +751,8 @@ export default { this.NAME = '' this.BARCODE = '' this.CONTAINERREGISTRYID = '' - this.PARENTCONTAINERID = '' - this.PARENTCONTAINERLOCATION = '' + this.PARENTCONTAINERID = null + this.PARENTCONTAINERLOCATION = null // Trigger default setting of UDC fields if selected this.selectQueueForUDC(this.AUTOMATED) diff --git a/client/src/js/modules/types/mx/shipment/views/mx-container-view.vue b/client/src/js/modules/types/mx/shipment/views/mx-container-view.vue index 9a24cd7fb..cf62bd2b5 100644 --- a/client/src/js/modules/types/mx/shipment/views/mx-container-view.vue +++ b/client/src/js/modules/types/mx/shipment/views/mx-container-view.vue @@ -288,7 +288,6 @@