diff --git a/CHANGES.rst b/CHANGES.rst
index 36f6409034..8c5c1dd4c8 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -46,6 +46,7 @@ Changelog
**Removed**
+- #1531 Remove sampling rounds from core
- #1551 Removed dependency to plone.app.iterate
- #1530 Removed ARImport
- #1530 Removed stale type registrations
diff --git a/bika/lims/__init__.py b/bika/lims/__init__.py
index 14815547ae..e2d227abad 100644
--- a/bika/lims/__init__.py
+++ b/bika/lims/__init__.py
@@ -126,7 +126,6 @@ def initialize(context):
from content.samplepoint import SamplePoint # noqa
from content.sampletype import SampleType # noqa
from content.samplingdeviation import SamplingDeviation # noqa
- from content.srtemplate import SRTemplate # noqa
from content.storagelocation import StorageLocation # noqa
from content.subgroup import SubGroup # noqa
from content.supplier import Supplier # noqa
@@ -164,7 +163,6 @@ def initialize(context):
from controlpanel.bika_samplepoints import SamplePoints # noqa
from controlpanel.bika_sampletypes import SampleTypes # noqa
from controlpanel.bika_samplingdeviations import SamplingDeviations # noqa
- from controlpanel.bika_srtemplates import SRTemplates # noqa
from controlpanel.bika_storagelocations import StorageLocations # noqa
from controlpanel.bika_subgroups import SubGroups # noqa
from controlpanel.bika_suppliers import Suppliers # noqa
diff --git a/bika/lims/adapters/widgetvisibility.py b/bika/lims/adapters/widgetvisibility.py
index 255fd438a9..d94f65e34a 100644
--- a/bika/lims/adapters/widgetvisibility.py
+++ b/bika/lims/adapters/widgetvisibility.py
@@ -132,7 +132,7 @@ class ScheduledSamplingFieldsVisibility(SenaiteATWidgetVisibility):
def __init__(self, context):
super(ScheduledSamplingFieldsVisibility, self).__init__(
context=context, sort=10,
- field_names=["ScheduledSamplingSampler", "SamplingRound"])
+ field_names=["ScheduledSamplingSampler"])
def isVisible(self, field, mode="view", default="visible"):
if not self.context.bika_setup.getScheduleSamplingEnabled():
diff --git a/bika/lims/browser/analysisrequest/add2.py b/bika/lims/browser/analysisrequest/add2.py
index 8367c2a11d..f9c4185fc0 100644
--- a/bika/lims/browser/analysisrequest/add2.py
+++ b/bika/lims/browser/analysisrequest/add2.py
@@ -855,9 +855,6 @@ def get_client_info(self, obj):
"Specification": {
"getClientUID": [uid, ""],
},
- "SamplingRound": {
- "getParentUID": [uid],
- },
"Sample": {
"getClientUID": [uid],
},
diff --git a/bika/lims/browser/client/__init__.py b/bika/lims/browser/client/__init__.py
index 03bc80b542..8559819301 100644
--- a/bika/lims/browser/client/__init__.py
+++ b/bika/lims/browser/client/__init__.py
@@ -22,11 +22,9 @@
from views.analysisrequests import ClientAnalysisRequestsView
from views.analysisprofiles import ClientAnalysisProfilesView
from views.artemplates import ClientARTemplatesView
-from views.srtemplates import ClientSamplingRoundTemplatesView
from views.samplepoints import ClientSamplePointsView
from views.analysisspecs import ClientAnalysisSpecsView
from views.attachments import ClientAttachmentsView
from views.orders import ClientOrdersView
from views.contacts import ClientContactsView
from views.contacts import ClientContactVocabularyFactory
-from views.samplingrounds import ClientSamplingRoundsView
diff --git a/bika/lims/browser/client/configure.zcml b/bika/lims/browser/client/configure.zcml
index 3edad7d5d4..88ad63ce32 100644
--- a/bika/lims/browser/client/configure.zcml
+++ b/bika/lims/browser/client/configure.zcml
@@ -43,14 +43,6 @@
layer="bika.lims.interfaces.IBikaLIMS"
/>
-
-
-
-
diff --git a/bika/lims/browser/client/views/samplingrounds.py b/bika/lims/browser/client/views/samplingrounds.py
deleted file mode 100644
index e65de349bc..0000000000
--- a/bika/lims/browser/client/views/samplingrounds.py
+++ /dev/null
@@ -1,47 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of SENAITE.CORE.
-#
-# SENAITE.CORE is free software: you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free Software
-# Foundation, version 2.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program; if not, write to the Free Software Foundation, Inc., 51
-# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-#
-# Copyright 2018-2020 by it's authors.
-# Some rights reserved, see README and LICENSE.
-
-from Products.CMFCore.utils import getToolByName
-from bika.lims import bikaMessageFactory as _
-from bika.lims.controlpanel.bika_samplingrounds import SamplingRoundsView
-
-
-class ClientSamplingRoundsView(SamplingRoundsView):
- """This is displayed in the "Sampling Rounds" tab on each client
- """
-
- def __init__(self, context, request):
- super(ClientSamplingRoundsView, self).__init__(context, request)
- self.contentFilter = {
- 'portal_type': 'SamplingRound',
- 'sort_on': 'sortable_title',
- 'path': {
- "query": "/".join(self.context.getPhysicalPath()),
- "level": 0},
- }
- self.title = self.context.translate(_("Client Sampling Rounds"))
- self.context_actions = {
- _('Add'): {'url': '++add++SamplingRound', # To work with dexterity
- 'permission': 'Add portal content',
- 'icon': '++resource++bika.lims.images/add.png'}}
-
- def __call__(self):
- mtool = getToolByName(self.context, 'portal_membership')
- return super(ClientSamplingRoundsView, self).__call__()
diff --git a/bika/lims/browser/client/views/srtemplates.py b/bika/lims/browser/client/views/srtemplates.py
deleted file mode 100644
index 3ca1ae8a1f..0000000000
--- a/bika/lims/browser/client/views/srtemplates.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of SENAITE.CORE.
-#
-# SENAITE.CORE is free software: you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free Software
-# Foundation, version 2.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program; if not, write to the Free Software Foundation, Inc., 51
-# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-#
-# Copyright 2018-2020 by it's authors.
-# Some rights reserved, see README and LICENSE.
-
-from bika.lims.controlpanel.bika_srtemplates import SamplingRoundTemplatesView
-
-
-class ClientSamplingRoundTemplatesView(SamplingRoundTemplatesView):
- """
- Displays the client-specific Sampling Round Templates.
- """
-
- def __init__(self, context, request):
- super(ClientSamplingRoundTemplatesView, self).__init__(context, request)
diff --git a/bika/lims/browser/configure.zcml b/bika/lims/browser/configure.zcml
index 26a222aac9..2a9148364c 100644
--- a/bika/lims/browser/configure.zcml
+++ b/bika/lims/browser/configure.zcml
@@ -48,8 +48,6 @@
-
-
diff --git a/bika/lims/browser/images/srtemplate.png b/bika/lims/browser/images/srtemplate.png
deleted file mode 100644
index 9ee64c26c5..0000000000
Binary files a/bika/lims/browser/images/srtemplate.png and /dev/null differ
diff --git a/bika/lims/browser/images/srtemplate_big.png b/bika/lims/browser/images/srtemplate_big.png
deleted file mode 100644
index 7ad8601dbd..0000000000
Binary files a/bika/lims/browser/images/srtemplate_big.png and /dev/null differ
diff --git a/bika/lims/browser/js/bika.lims.loader.js b/bika/lims/browser/js/bika.lims.loader.js
index e650b05f31..28bda70961 100644
--- a/bika/lims/browser/js/bika.lims.loader.js
+++ b/bika/lims/browser/js/bika.lims.loader.js
@@ -72,14 +72,6 @@ window.bika.lims.controllers = {
"div.overlay #client-base-edit":
['ClientOverlayHandler'],
- // Client Sampling Rounds
- ".template-bika-lims-content-samplingsround.portaltype-client":
- ['ClientSamplingRoundAddEditView'],
-
- // Sampling Rounds PrintView
- "#sr_publish_container":
- ['SamplingRoundPrintView'],
-
// Reference Samples
".portaltype-referencesample.template-analyses":
['ReferenceSampleAnalysesView'],
diff --git a/bika/lims/browser/js/bika.lims.samplinground.print.js b/bika/lims/browser/js/bika.lims.samplinground.print.js
deleted file mode 100644
index 87b26a635a..0000000000
--- a/bika/lims/browser/js/bika.lims.samplinground.print.js
+++ /dev/null
@@ -1,379 +0,0 @@
-/**
- * Controller class for SamplingRound Print View
- */
-function SamplingRoundPrintView() {
-
- var that = this;
- var referrer_cookie_name = '_srpv';
-
- // Allowed Paper sizes and default margins, in mm
- var papersize_default = "A4";
- var default_margins = [20, 20, 20, 20];
- var papersize = {
- 'A4': {
- dimensions: [210, 297],
- margins: [20, 20, 20, 20] },
-
- 'letter': {
- dimensions: [215.9, 279.4],
- margins: [20, 20, 20, 20] },
- };
-
- /**
- * Entry-point method for AnalysisRequestPublishView
- */
- that.load = function() {
-
- // The report will be loaded dynamically by reloadReport()
- $('#report').html('').hide();
-
- // Load the report
- reloadReport();
-
- // Store referrer in cookie in case it is lost due to a page reload
- var cookiename = "sr.publish.view.referrer";
- var backurl = document.referrer;
- if (backurl) {
- createCookie(cookiename, backurl);
- } else {
- backurl = readCookie(cookiename);
- // Fallback to portal_url instead of staying inside publish.
- if (!backurl) {
- backurl = portal_url;
- }
- }
-
- $('#sel_format').change(function(e) {
- reloadReport();
- });
-
- $('#sel_layout').change(function(e) {
- $('body').removeClass($('body').attr('data-layout'));
- $('body').attr('data-layout', $(this).val());
- $('body').addClass($(this).val());
- reloadReport();
- });
-
- $('#cancel_button').click(function(e) {
- location.href=backurl;
- });
- $('#print_button').click(function(e) {
- e.preventDefault();
- var url = window.location.href;
- $('#sr_publish_container').animate({opacity:0.4}, 'slow');
- var count = $('#sr_publish_container #report .report_body').length;
- $('#sr_publish_container #report .report_body').each(function(){
- var rephtml = $(this).clone().wrap('
').parent().html();
- var repstyle = $('#report-style').clone().wrap('
';
- }
- $(this).find('.page-header').remove();
- }
-
- // Footer defined for this AR Report?
- // Note that if the footer of the report is taller than the
- // margin, the footer will be dismissed
- var footer_html = '';
- var footer_height = $(footer_html).outerHeight(true);
- if ($(this).find('.page-footer').length > 0) {
- var pgf = $(this).find('.page-footer').first();
- footer_height = parseFloat($(pgf).outerHeight(true));
- if (footer_height > mmTopx(dim.marginBottom)) {
- // Footer too tall
- footer_html = "";
- footer_height = parseFloat($(footer_html));
- } else {
- footer_html = '';
- }
- $(this).find('.page-footer').remove();
- }
-
- // Remove undesired and orphan page breaks
- $(this).find('.page-break').remove();
- if ($(this).find('div').last().hasClass('manual-page-break')) {
- $(this).find('div').last().remove();
- }
- if ($(this).find('div').first().hasClass('manual-page-break')) {
- $(this).find('div').first().remove();
- }
-
- // Top offset by default. The position in which the report
- // starts relative to the top of the window. Used later to
- // calculate when a page-break is needed.
- var topOffset = $(this).position().top;
- var maxHeight = mmTopx(dim.height);
- var elCurrent = null;
- var elOutHeight = 0;
- var contentHeight = 0;
- var pagenum = 1;
- var pagecounts = Array();
-
- // Iterate through all div children to find the suitable
- // page-break points, split the report and add the header
- // and footer as well as pagination count as required.
- //
- // IMPORTANT
- // Please note that only first-level div elements from
- // within div.ar_publish_body are checked and will be
- // treated as nob-breakable elements. So, if a div element
- // from within a div.ar_publish_body is taller than the
- // maximum allowed height, that element will be omitted.
- // Further improvements may solve this and handle deeply
- // elements from the document, such as tables, etc. Other
- // elements could be then labeled with "no-break" class to
- // prevent the system to break them.
- //console.log("OFF\tABS\tREL\tOUT\tHEI\tMAX");
- $(this).children('div:visible').each(function(z) {
-
- // Is the first page?
- if (elCurrent === null) {
- // Add page header if required
- $(header_html).insertBefore($(this));
- topOffset = $(this).position().top;
- }
-
- // Instead of using the height css of each element to
- // know if the total height at this iteration is above
- // the maximum health, we use the element's position.
- // This way, we will prevent underestimations due
- // non-div elements or plain text directly set inside
- // the div.ar_publish_body container, not wrapped by
- // other div element.
- var elAbsTopPos = $(this).position().top;
- var elRelTopPos = elAbsTopPos - topOffset;
- var elNext = $(this).next();
- elOutHeight = parseFloat($(this).outerHeight(true));
- if ($(elNext).length > 0) {
- // Calculate the height of the element according to
- // the position of the next element instead of
- // using the outerHeight.
- elOutHeight = $(elNext).position().top-elAbsTopPos;
- }
-
- // The current element is taller than the maximum?
- if (elOutHeight > maxHeight) {
- console.warn("Element with id "+$(this).attr('id')+
- " has a height above the maximum: "+
- elOutHeight);
- }
-
- // Accumulated height
- contentHeight = elRelTopPos + elOutHeight;
- /*console.log(Math.floor(topOffset) + "\t" +
- Math.floor(elAbsTopPos) + "\t" +
- Math.floor(elRelTopPos) + "\t" +
- Math.floor(elOutHeight) + "\t" +
- Math.floor(contentHeight) + "\t" +
- Math.floor(maxHeight) + "\t" +
- '#'+$(this).attr('id')+"."+$(this).attr('class'));*/
-
- if (contentHeight > maxHeight ||
- $(this).hasClass('manual-page-break')) {
- // The content is taller than the allowed height
- // or a manual page break reached. Add a page break.
- var paddingTopFoot = maxHeight - elRelTopPos;
- var manualbreak = $(this).hasClass('manual-page-break');
- var restartcount = manualbreak && $(this).hasClass('restart-page-count');
- var aboveBreakHtml = "";
- var pageBreak = "";
- $(aboveBreakHtml + footer_html + pageBreak + header_html).insertBefore($(this));
- topOffset = $(this).position().top;
- if (manualbreak) {
- $(this).hide();
- if (restartcount) {
- // The page count needs to be restarted!
- pagecounts.push(pagenum);
- pagenum = 0;
- }
- }
- contentHeight = $(this).outerHeight(true);
- pagenum += 1;
- }
- $(this).css('width', '100%');
- elCurrent = $(this);
- });
-
- // Document end-footer
- if (elCurrent !== null) {
- var paddingTopFoot = maxHeight - contentHeight;
- var aboveBreakHtml = "";
- var pageBreak = "";
- pagecounts.push(pagenum);
- $(aboveBreakHtml + footer_html + pageBreak).insertAfter($(elCurrent));
- }
-
- // Wrap all elements in pages
- var split_at = 'div.page-header';
- $(this).find(split_at).each(function() {
- $(this).add($(this).nextUntil(split_at)).wrapAll("");
- });
-
- // Move headers and footers out of the wrapping and assign
- // the top and bottom margins
- $(this).find('div.page-header').each(function() {
- var baseheight = $(this).height();
- $(this).css({'height': pxTomm(baseheight)+"mm",
- 'margin': 0,
- 'padding': (pxTomm(mmTopx(dim.marginTop) - baseheight)+"mm 0 0 0")});
- $(this).parent().before(this);
- });
- $(this).find('div.page-break').each(function() {
- $(this).parent().after(this);
- });
- $(this).find('div.page-footer').each(function() {
- $(this).css({'height': dim.marginBottom+"mm",
- 'margin': 0,
- 'padding': 0});
- $(this).parent().after(this);
- });
-
- // Page numbering
- pagenum = 1;
- var pagecntidx = 0;
- $(this).find('.page-current-num,.page-total-count,div.page-break').each(function() {
- if ($(this).hasClass('page-break')) {
- if ($(this).hasClass('restart-page-count')) {
- pagenum = 1;
- pagecntidx += 1;
- } else {
- pagenum = parseInt($(this).attr('data-pagenum')) + 1;
- }
- } else if ($(this).hasClass('page-current-num')) {
- $(this).html(pagenum);
- } else {
- $(this).html(pagecounts[pagecntidx]);
- }
- });
- });
- // Remove manual page breaks
- $('.manual-page-break').remove();
- }
-}
-var mmTopx = function(mm) {
- var px = parseFloat(mm*$('#my_mm').height());
- return px > 0 ? Math.ceil(px) : Math.floor(px);
-};
-var pxTomm = function(px){
- var mm = parseFloat(px/$('#my_mm').height());
- return mm > 0 ? Math.floor(mm) : Math.ceil(mm);
-};
diff --git a/bika/lims/browser/js/bika.lims.samplingrounds.js b/bika/lims/browser/js/bika.lims.samplingrounds.js
deleted file mode 100644
index c9291b6f03..0000000000
--- a/bika/lims/browser/js/bika.lims.samplingrounds.js
+++ /dev/null
@@ -1,116 +0,0 @@
-/**
- * Controller class for Client's sampling round Add/Edit view
- */
-function ClientSamplingRoundAddEditView() {
-
- var that = this;
-
- /**
- * Entry-point method for ClientSamplingRoundAddEditView
- */
- that.load = function() {
- /**
- * Allow to fill out the fields manually or automatically by selecting a Sampling Round Template (ReferenceField).
- */
- $('select#form-widgets-sr_template')
- .bind('click', function () {
- if (hasSamplingRoundTemplateData()) {
- setSamplingRoundTemplateData();
- }
- else {
- unsetSamplingRoundTemplate();
- }
- });
- // Hiding not needed buttons
- $("button[name='upButton']").hide();
- $("button[name='downButton']").hide();
- };
-
- function hasSamplingRoundTemplateData() {
- /**
- * Checks if the sampling rounds template field has a sampling rounds template selected.
- * :returns: True if the field has data and False if the field contains "--NOVALUE--"
- */
- var value = $('select#form-widgets-sr_template').val();
- return value != "--NOVALUE--";
- }
-
- function setSamplingRoundTemplateData() {
- /**
- * This function gets the Sampling Rounds Template information and fill out each field.
- * The function also binds an event to each filled field in order to unset the Sampling Round Template
- * if one of the fields change.
- */
-
- // Getting the Sampling Round Template's data
- var srt_uid = $('select#form-widgets-sr_template').val();
- if (srt_uid === undefined || srt_uid === null){
- srt_uid='';
- }
- var sampler, department, samp_freq, instructions, artemplates_uids;
- var request_data = {
- catalog_name: "portal_catalog",
- UID: srt_uid,
- content_type: 'SRTemplate'
- };
- window.bika.lims.jsonapi_read(request_data, function (data) {
- if (data.objects && data.objects.length > 0) {
- var ob = data.objects[0];
- sampler = ob['Sampler'];
- department = ob['Department_uid'];
- samp_freq = ob['SamplingDaysFrequency'];
- instructions = ob['Instructions'];
- artemplates_uids = ob['ARTemplates_uid'];
- }
- // Writing the Sampling Round Template's data
- $("select#form-widgets-sampler").val(sampler);
- $("select#form-widgets-department").val(department);
- $("input#form-widgets-sampling_freq").val(samp_freq);
- $("textarea#form-widgets-instructions").val(instructions);
- // Setting Analysis Request Template
- // Moving all options contained in "to" box to the "from" box
- var to_options = $("select#form-widgets-model-to option");
- if(to_options.length >=1) {
- to_options.each(function () {
- $(this).click();
- });
- $("button[name='to2fromButton']").click(); // Trigger the widget JS
- }
- if (artemplates_uids) {
- for (var i = 0; i <= artemplates_uids.length; i++) {
- $("option[value='" + artemplates_uids[i] + "']").attr('selected', 'selected');
- }
- }
- if(artemplates_uids.length > 0) {
- $("button[name='from2toButton']").click(); // Trigger the widget JS
- }
- });
- // Binding the unsetSamplingRoundsTemplate function
- $("select#form-widgets-sampler," +
- "select#form-widgets-department," +
- "input#form-widgets-sampling_freq," +
- "textarea#form-widgets-instructions")
- .bind('change copy selected', function () {
- unsetSamplingRoundTemplate();
- });
- $("button[name='to2fromButton'], button[name='from2toButton']")
- .bind('blur', function () {
- unsetSamplingRoundTemplate();
- });
- }
-
- function unsetSamplingRoundTemplate(){
- /**
- This function disables the selected sampling Round Template and cut all bindings done in
- setSamplingRoundTemplate().
- */
- $('select#form-widgets-sr_template').val("--NOVALUE--");
- $("select#form-widgets-sampler," +
- "select#form-widgets-department," +
- "input#form-widgets-sampling_freq," +
- "textarea#form-widgets-instructions," +
- "textarea#form-widgets-instructions," +
- "button[name='to2fromButton']," +
- "button[name='from2toButton']").unbind();
- }
-}
diff --git a/bika/lims/browser/samplinground/__init__.py b/bika/lims/browser/samplinground/__init__.py
deleted file mode 100644
index b54d2c0984..0000000000
--- a/bika/lims/browser/samplinground/__init__.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of SENAITE.CORE.
-#
-# SENAITE.CORE is free software: you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free Software
-# Foundation, version 2.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program; if not, write to the Free Software Foundation, Inc., 51
-# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-#
-# Copyright 2018-2020 by it's authors.
-# Some rights reserved, see README and LICENSE.
diff --git a/bika/lims/browser/samplinground/add.py b/bika/lims/browser/samplinground/add.py
deleted file mode 100644
index 2f42aa5652..0000000000
--- a/bika/lims/browser/samplinground/add.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of SENAITE.CORE.
-#
-# SENAITE.CORE is free software: you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free Software
-# Foundation, version 2.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program; if not, write to the Free Software Foundation, Inc., 51
-# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-#
-# Copyright 2018-2020 by it's authors.
-# Some rights reserved, see README and LICENSE.
-
-from plone.dexterity.browser import add
-from AccessControl import Unauthorized
-
-class AddForm(add.DefaultAddForm):
- """
- This class is made to check the add form permissions against client's contact users
- """
- def __call__(self):
- # Checking current user permissions
- if self.context.hasUserAddEditPermission():
- return add.DefaultAddForm.__call__(self)
- else:
- raise Unauthorized
diff --git a/bika/lims/browser/samplinground/analysisrequests.py b/bika/lims/browser/samplinground/analysisrequests.py
deleted file mode 100644
index ffa24f1c29..0000000000
--- a/bika/lims/browser/samplinground/analysisrequests.py
+++ /dev/null
@@ -1,267 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of SENAITE.CORE.
-#
-# SENAITE.CORE is free software: you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free Software
-# Foundation, version 2.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program; if not, write to the Free Software Foundation, Inc., 51
-# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-#
-# Copyright 2018-2020 by it's authors.
-# Some rights reserved, see README and LICENSE.
-
-from bika.lims import bikaMessageFactory as _
-from bika.lims.browser.analysisrequest import AnalysisRequestAddView as _ARAV
-from bika.lims.browser.analysisrequest import AnalysisRequestsView as _ARV
-from bika.lims.permissions import *
-from plone.app.layout.globals.interfaces import IViewView
-from Products.CMFCore.utils import getToolByName
-from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
-from zope.interface import implements
-from bika.lims.utils import t
-from copy import copy
-
-
-class AnalysisRequestsView(_ARV, _ARAV):
- implements(IViewView)
-
- def __init__(self, context, request):
- super(AnalysisRequestsView, self).__init__(context, request)
- self.catalog = "portal_catalog"
- SamplingWorkflowEnabled = self.context.bika_setup.getSamplingWorkflowEnabled()
- self.columns = {
- 'securitySealIntact': {'title': _('Security Seal Intact'),
- 'toggle': True},
- 'samplingRoundTemplate': {'title': _('Sampling Round Template'),
- 'toggle': True},
- 'getId': {'title': _('Request ID'),
- 'index': 'getId'},
- 'getDateSampled': {'title': _('Date Sampled'),
- 'index': 'getDateSampled',
- 'toggle': True,
- 'input_class': 'datetimepicker',
- 'input_width': '10'},
- 'state_title': {'title': _('State'),
- 'index': 'review_state'},
- 'getProfilesTitle': {'title': _('Profile'),
- 'toggle': False},
- 'getTemplateTitle': {'title': _('Template'),
- 'toggle': False},
- }
-
- self.review_states = [
- {'id': 'default',
- 'title': _('Active'),
- 'contentFilter': {'is_active': True,
- 'sort_on': 'created',
- 'sort_order': 'reverse'},
- 'transitions': [{'id': 'sample'},
- {'id': 'preserve'},
- {'id': 'receive'},
- {'id': 'retract'},
- {'id': 'verify'},
- {'id': 'prepublish'},
- {'id': 'publish'},
- {'id': 'republish'},
- {'id': 'cancel'},
- {'id': 'reinstate'}],
- 'custom_transitions': [],
- 'columns': ['securitySealIntact',
- 'getId',
- 'samplingRoundTemplate',
- 'getDateSampled',
- 'state_title']},
- {'id': 'sample_due',
- 'title': _('Due'),
- 'contentFilter': {'review_state': ('to_be_sampled',
- 'to_be_preserved',
- 'sample_due'),
- 'sort_on': 'created',
- 'sort_order': 'reverse'},
- 'transitions': [{'id': 'sample'},
- {'id': 'preserve'},
- {'id': 'receive'},
- {'id': 'cancel'},
- {'id': 'reinstate'}],
- 'custom_transitions': [],
- 'columns': ['securitySealIntact',
- 'getId',
- 'samplingRoundTemplate',
- 'getDateSampled',
- 'state_title']},
- {'id': 'sample_received',
- 'title': _('Received'),
- 'contentFilter': {'review_state': 'sample_received',
- 'sort_on': 'created',
- 'sort_order': 'reverse'},
- 'transitions': [{'id': 'prepublish'},
- {'id': 'cancel'},
- {'id': 'reinstate'}],
- 'custom_transitions': [],
- 'columns': ['securitySealIntact',
- 'getId',
- 'samplingRoundTemplate',
- 'getDateSampled',
- 'state_title']},
- {'id': 'to_be_verified',
- 'title': _('To be verified'),
- 'contentFilter': {'review_state': 'to_be_verified',
- 'sort_on': 'created',
- 'sort_order': 'reverse'},
- 'transitions': [{'id': 'retract'},
- {'id': 'verify'},
- {'id': 'prepublish'},
- {'id': 'cancel'},
- {'id': 'reinstate'}],
- 'custom_transitions': [],
- 'columns': ['securitySealIntact',
- 'getId',
- 'samplingRoundTemplate',
- 'getDateSampled',
- 'state_title']},
- {'id': 'verified',
- 'title': _('Verified'),
- 'contentFilter': {'review_state': 'verified',
- 'sort_on': 'created',
- 'sort_order': 'reverse'},
- 'transitions': [{'id': 'publish'}],
- 'custom_transitions': [],
- 'columns': ['securitySealIntact',
- 'getId',
- 'samplingRoundTemplate',
- 'getDateSampled',
- 'state_title']},
- {'id': 'published',
- 'title': _('Published'),
- 'contentFilter': {'review_state': ('published', 'invalid'),
- 'sort_on': 'created',
- 'sort_order': 'reverse'},
- 'transitions': [{'id': 'republish'}],
- 'custom_transitions': [],
- 'columns': ['securitySealIntact',
- 'getId',
- 'samplingRoundTemplate',
- 'getDateSampled',
- 'state_title']},
- {'id': 'cancelled',
- 'title': _('Cancelled'),
- 'contentFilter': {'is_active': False,
- 'sort_on': 'created',
- 'sort_order': 'reverse'},
- 'transitions': [{'id': 'reinstate'}],
- 'custom_transitions': [],
- 'columns': ['securitySealIntact',
- 'getId',
- 'samplingRoundTemplate',
- 'getDateSampled',
- 'state_title']},
- {'id': 'invalid',
- 'title': _('Invalid'),
- 'contentFilter': {'review_state': 'invalid',
- 'sort_on': 'created',
- 'sort_order': 'reverse'},
- 'transitions': [],
- 'custom_transitions': [],
- 'columns': ['securitySealIntact',
- 'getId',
- 'samplingRoundTemplate',
- 'getDateSampled',
- 'state_title']},
- {'id': 'assigned',
- 'title': "" % (
- t(_("Assigned")), self.portal_url),
- 'contentFilter': {'assigned_state': 'assigned',
- 'review_state': ('sample_received', 'to_be_verified',
- 'attachment_due', 'verified',
- 'published'),
- 'sort_on': 'created',
- 'sort_order': 'reverse'},
- 'transitions': [{'id': 'retract'},
- {'id': 'verify'},
- {'id': 'prepublish'},
- {'id': 'publish'},
- {'id': 'republish'},
- {'id': 'cancel'},
- {'id': 'reinstate'}],
- 'custom_transitions': [],
- 'columns': ['securitySealIntact',
- 'getId',
- 'samplingRoundTemplate',
- 'getDateSampled',
- 'state_title']},
- {'id': 'unassigned',
- 'title': "" % (
- t(_("Unassigned")), self.portal_url),
- 'contentFilter': {'assigned_state': 'unassigned',
- 'review_state': ('sample_received', 'to_be_verified',
- 'attachment_due', 'verified',
- 'published'),
- 'sort_on': 'created',
- 'sort_order': 'reverse'},
- 'transitions': [{'id': 'receive'},
- {'id': 'retract'},
- {'id': 'verify'},
- {'id': 'prepublish'},
- {'id': 'publish'},
- {'id': 'republish'},
- {'id': 'cancel'},
- {'id': 'reinstate'}],
- 'custom_transitions': [],
- 'columns': ['securitySealIntact',
- 'getId',
- 'samplingRoundTemplate',
- 'getDateSampled',
- 'state_title']},
- ]
-
- def contentsMethod(self, contentFilter):
- pc = getToolByName(self.context, 'portal_catalog')
- if 'SamplingRoundUID' not in contentFilter.keys():
- contentFilter['SamplingRoundUID'] = self.context.UID()
- return pc(contentFilter)
-
- def __call__(self):
- self.context_actions = {}
- mtool = getToolByName(self.context, 'portal_membership')
- if mtool.checkPermission(AddAnalysisRequest, self.portal):
- # We give the number of analysis request templates in order to fill out the analysis request form
- # automatically :)
- num_art = len(self.context.ar_templates)
- self.context_actions[self.context.translate(_('Add new'))] = {
- 'url': self.context.aq_parent.absolute_url() + \
- "/portal_factory/"
- "AnalysisRequest/Request new analyses/ar_add?samplinground="
- + self.context.UID() + "&ar_count=" + str(num_art),
- 'icon': '++resource++bika.lims.images/add.png'}
- return super(AnalysisRequestsView, self).__call__()
-
- def folderitem(self, obj, item, index):
- # Call the folderitem method from the base class
- item = _ARV.folderitem(self, obj, item, index)
- # In sampling rounds, analysis request list will be listed per Sample
- # Partition/Container Obtaining analysis requests
- # TODO-performance: don't get the full object
- obj = obj.getObject()
- # Getting the sampling round template uid
- srTemplateUID = obj.getSamplingRound().sr_template if obj.getSamplingRound().sr_template else ''
- # Getting the sampling round object
- catalog = getToolByName(self.context, 'uid_catalog')
- srTemplateObj = catalog(UID=srTemplateUID)[0].getObject() if catalog(UID=srTemplateUID) else None
- item['samplingRoundTemplate'] = ''
- if srTemplateObj:
- item['samplingRoundTemplate'] = srTemplateObj.title
- item['replace']['samplingRoundTemplate'] = \
- "%s" % (
- srTemplateObj.absolute_url, item['samplingRoundTemplate'])
- item['securitySealIntact'] = ''
- return item
diff --git a/bika/lims/browser/samplinground/configure.zcml b/bika/lims/browser/samplinground/configure.zcml
deleted file mode 100644
index bb2ca73b03..0000000000
--- a/bika/lims/browser/samplinground/configure.zcml
+++ /dev/null
@@ -1,37 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/bika/lims/browser/samplinground/edit.py b/bika/lims/browser/samplinground/edit.py
deleted file mode 100644
index e2b1ff3d60..0000000000
--- a/bika/lims/browser/samplinground/edit.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of SENAITE.CORE.
-#
-# SENAITE.CORE is free software: you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free Software
-# Foundation, version 2.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program; if not, write to the Free Software Foundation, Inc., 51
-# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-#
-# Copyright 2018-2020 by it's authors.
-# Some rights reserved, see README and LICENSE.
-
-from plone.dexterity.browser import edit
-from AccessControl import Unauthorized
-
-class EditForm(edit.DefaultEditForm):
- """
- This class is made to check the edit form permissions against client's contact users
- """
- def __call__(self):
- # Checking current user permissions
- if self.context.hasUserAddEditPermission():
- return edit.DefaultEditForm.__call__(self)
- else:
- raise Unauthorized
diff --git a/bika/lims/browser/samplinground/printform.py b/bika/lims/browser/samplinground/printform.py
deleted file mode 100644
index bf68dfde21..0000000000
--- a/bika/lims/browser/samplinground/printform.py
+++ /dev/null
@@ -1,265 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of SENAITE.CORE.
-#
-# SENAITE.CORE is free software: you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free Software
-# Foundation, version 2.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program; if not, write to the Free Software Foundation, Inc., 51
-# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-#
-# Copyright 2018-2020 by it's authors.
-# Some rights reserved, see README and LICENSE.
-
-from bika.lims import bikaMessageFactory as _, t
-from Products.CMFCore.utils import getToolByName
-from Products.CMFPlone.utils import safe_unicode
-from bika.lims.utils import to_utf8, createPdf
-from bika.lims.browser import BrowserView
-from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
-from plone.resource.utils import iterDirectoriesOfType, queryResourceDirectory
-import App
-import tempfile
-import os
-import glob
-import traceback
-
-
-class PrintForm(BrowserView):
- template = ViewPageTemplateFile("templates/print_form.pt")
- _DEFAULT_TEMPLATE = 'default_form.pt'
- _TEMPLATES_DIR = 'templates/print'
- _TEMPLATES_ADDON_DIR = 'samplingrounds'
- _current_sr_index = 0
- _samplingrounds = []
-
- def __call__(self):
- if self.context.portal_type == 'SamplingRound':
- self._samplingrounds = [self.context]
-
- elif self.context.portal_type == 'SamplingRounds' \
- and self.request.get('items', ''):
- uids = self.request.get('items').split(',')
- uc = getToolByName(self.context, 'uid_catalog')
- self._samplingrounds = [obj.getObject() for obj in uc(UID=uids)]
-
- else:
- # Warn and redirect to referer
- logger.warning('PrintView: type not allowed: %s' %
- self.context.portal_type)
- self.destination_url = self.request.get_header("referer",
- self.context.absolute_url())
-
- # Do print?
- if self.request.form.get('pdf', '0') == '1':
- response = self.request.response
- response.setHeader("Content-type", "application/pdf")
- response.setHeader("Content-Disposition", "inline")
- response.setHeader("filename", "temp.pdf")
- return self.pdfFromPOST()
- else:
- return self.template()
-
- def getSamplingRoundObj(self):
- """Returns the sampling round object
- """
- return self.context
-
- def getSRTemplates(self):
- """
- Returns a DisplayList with the available templates found in
- browser/samplinground/templates/
- """
- this_dir = os.path.dirname(os.path.abspath(__file__))
- templates_dir = os.path.join(this_dir, self._TEMPLATES_DIR)
- tempath = '%s/%s' % (templates_dir, '*.pt')
- templates = [t.split('/')[-1] for t in glob.glob(tempath)]
- out = []
- for template in templates:
- out.append({'id': template, 'title': template[:-3]})
- for templates_resource in iterDirectoriesOfType(self._TEMPLATES_ADDON_DIR):
- prefix = templates_resource.__name__
- templates = [
- tpl for tpl in templates_resource.listDirectory()
- if tpl.endswith('.pt')
- ]
- for template in templates:
- out.append({
- 'id': '{0}:{1}'.format(prefix, template),
- 'title': '{0} ({1})'.format(template[:-3], prefix),
- })
- return out
-
- def getFormTemplate(self):
- """Returns the current samplinground rendered with the template
- specified in the request (param 'template').
- Moves the iterator to the next samplinground available.
- """
- templates_dir = self._TEMPLATES_DIR
- embedt = self.request.get('template', self._DEFAULT_TEMPLATE)
- if embedt.find(':') >= 0:
- prefix, embedt = embedt.split(':')
- templates_dir = queryResourceDirectory(self._TEMPLATES_ADDON_DIR, prefix).directory
- embed = ViewPageTemplateFile(os.path.join(templates_dir, embedt))
- reptemplate = ""
- try:
- reptemplate = embed(self)
- except:
- tbex = traceback.format_exc()
- wsid = self._samplingrounds[self._current_sr_index].id
- reptemplate = "
%s - %s '%s':
%s
" % (wsid, _("Unable to load the template"), embedt, tbex)
- if self._current_sr_index < len(self._samplingrounds):
- self._current_sr_index += 1
- return reptemplate
-
- def getCSS(self):
- """ Returns the css style to be used for the current template.
- If the selected template is 'default.pt', this method will
- return the content from 'default.css'. If no css file found
- for the current template, returns empty string
- """
- template = self.request.get('template', self._DEFAULT_TEMPLATE)
- content = ''
- if template.find(':') >= 0:
- prefix, template = template.split(':')
- resource = queryResourceDirectory(
- self._TEMPLATES_ADDON_DIR, prefix)
- css = '{0}.css'.format(template[:-3])
- if css in resource.listDirectory():
- content = resource.readFile(css)
- else:
- this_dir = os.path.dirname(os.path.abspath(__file__))
- templates_dir = os.path.join(this_dir, self._TEMPLATES_DIR)
- path = '%s/%s.css' % (templates_dir, template[:-3])
- with open(path, 'r') as content_file:
- content = content_file.read()
- return content
-
- def getAnalysisRequestTemplatesInfo(self):
- """
- Returns a lost of dicts with the analysis request templates infomration
- [{'uid':'xxxx','id':'xxxx','title':'xxx','url':'xxx'}, ...]
- """
- arts_list = []
- for art in self.context.ar_templates:
- pc = getToolByName(self.context, 'portal_catalog')
- contentFilter = {'portal_type': 'ARTemplate',
- 'UID': art}
- art_brain = pc(contentFilter)
- if len(art_brain) == 1:
- art_obj = art_brain[0].getObject()
- arts_list.append({
- 'uid': art_obj.UID(),
- 'id': art_obj.id,
- 'title': art_obj.title,
- 'url': art_obj.absolute_url(),
- })
- return arts_list
-
- def getAnalysisRequestBySample(self):
- """
- Returns a list of dictionaries sorted by Sample Partition/Container
- [{'requests and partition info'}, ...]
- """
- # rows will contain the data for each html row
- rows = []
- # columns will be used to sort and define the columns
- columns = {
- 'column_order': [
- 'sample_id',
- 'sample_type',
- 'sampling_point',
- 'sampling_date',
- 'analyses',
- ],
- 'titles': {
- 'sample_id': _('Sample ID'),
- 'sample_type': _('Sample Type'),
- 'sampling_point': _('Sampling Point'),
- 'sampling_date': _('Sampling Date'),
- 'analyses': _('Analysis'),
- }
- }
- ars = self.context.getAnalysisRequests()
- for ar in ars:
- ar = ar.getObject()
- arcell = False
- analyses = ar.getAnalyses()
- numans = len(analyses)
- for analysis in analyses:
- row = {
- 'sample_id': {
- 'hidden': True if arcell else False,
- 'rowspan': numans,
- 'value': ar.getId(),
- },
- 'sample_type': {
- 'hidden': True if arcell else False,
- 'rowspan': numans,
- 'value': ar.getSampleType().title,
- },
- 'sampling_point': {
- 'hidden': True if arcell else False,
- 'rowspan': numans,
- 'value': ar.getSamplePoint().title if ar.getSamplePoint() else '',
- },
- 'sampling_date': {
- 'hidden': True if arcell else False,
- 'rowspan': numans,
- 'value': self.context.sampling_date,
- },
- 'analyses': {
- 'title': analysis.title,
- 'units': analysis.getUnit,
- },
- }
- rows.append(row)
- arcell = True
-
- # table will contain the data that from where the html
- # will take the info
- table = {
- 'columns': columns,
- 'rows': rows,
- }
- return table
-
- def getLab(self):
- return self.context.bika_setup.laboratory.getLabURL()
-
- def getLogo(self):
- portal = self.context.portal_url.getPortalObject()
- return "%s/logo_print.png" % portal.absolute_url()
-
- def pdfFromPOST(self):
- """
- It returns the pdf for the sampling rounds printed
- """
- html = self.request.form.get('html')
- style = self.request.form.get('style')
- reporthtml = "%s
%s" % (style, html)
- return self.printFromHTML(safe_unicode(reporthtml).encode('utf-8'))
-
- def printFromHTML(self, sr_html):
- """
- Tis function generates a pdf file from the html
- :sr_html: the html to use to generate the pdf
- """
- # HTML written to debug file
- debug_mode = App.config.getConfiguration().debug_mode
- if debug_mode:
- tmp_fn = tempfile.mktemp(suffix=".html")
- open(tmp_fn, "wb").write(sr_html)
-
- # Creates the pdf
- # we must supply the file ourself so that createPdf leaves it alone.
- pdf_fn = tempfile.mktemp(suffix=".pdf")
- pdf_report = createPdf(htmlreport=sr_html, outfile=pdf_fn)
- return pdf_report
diff --git a/bika/lims/browser/samplinground/templates/print/default_form.css b/bika/lims/browser/samplinground/templates/print/default_form.css
deleted file mode 100644
index 96c36c588c..0000000000
--- a/bika/lims/browser/samplinground/templates/print/default_form.css
+++ /dev/null
@@ -1,60 +0,0 @@
-
-.barcode-container {
- width:100%;
-}
-div.report_body {
- font-size:0.8em;
-}
-div.report_body a {
- color:#000;
- text-decoration:none;
-}
-div.report_body h1 {
- padding-top:15px;
- font-size:1.7em;
-}
-div#title{
- font-size:1.5em;
-}
-.label {
- font-weight: bold;
-}
-.table-text {
- position: relative;
-}
-.data-input{
- border: 1px solid #cdcdcd;
- height: 20px;
-}
-table.samples-grid tr td {
- vertical-align:top;
- border: 1px solid #cdcdcd;
- padding: 5px;
-}
-table thead th{
- border: 1px solid #cdcdcd;
- background-color: #808080;
-}
-#sampling-round-info div {
- float: left;
- font-size: 0.85em;
- position: relative;
- width: 150px;
-}
-span.units {
- font-size: 0.7em;
-}
-div.lab-logo {
- float: right;
- position: relative;
-}
-p {
- margin:0;
- padding:0;
-}
-.clearfix {
- clear:both;
-}
-#sampling-round-data-entry {
- clear: both;
-}
diff --git a/bika/lims/browser/samplinground/templates/print/default_form.pt b/bika/lims/browser/samplinground/templates/print/default_form.pt
deleted file mode 100644
index ba7c04f27d..0000000000
--- a/bika/lims/browser/samplinground/templates/print/default_form.pt
+++ /dev/null
@@ -1,134 +0,0 @@
-
-
-
-