From 4efc12217263158eed24dc14bf8f1a36f550ebf4 Mon Sep 17 00:00:00 2001 From: joshzamor Date: Mon, 24 Mar 2014 17:37:11 +0000 Subject: [PATCH 01/80] Format coverage rate and wastage rate as % Coverage rate and wastage rate on Child Coverage screen now formatted as a string with a % sign at the end. --- .../js/distribution/controller/child-coverage-controller.js | 4 ++++ .../pages/logistics/distribution/partials/child-coverage.html | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/modules/openlmis-web/src/main/webapp/public/js/distribution/controller/child-coverage-controller.js b/modules/openlmis-web/src/main/webapp/public/js/distribution/controller/child-coverage-controller.js index 799e4d6e9a..868d5b5820 100644 --- a/modules/openlmis-web/src/main/webapp/public/js/distribution/controller/child-coverage-controller.js +++ b/modules/openlmis-web/src/main/webapp/public/js/distribution/controller/child-coverage-controller.js @@ -104,6 +104,10 @@ function ChildCoverageController($scope, $routeParams, distributionService) { return (isUndefined(targetGroup) ? null : (targetGroup === 0 ? null : Math.round((total / targetGroup) * 100))); }; + $scope.formatAsPercent = function (number) { + return (number === null) ? '' : number + ' %'; + }; + $scope.calculateWastageRate = function (productsForVaccination) { var totalDosesConsumed = 0; var totalVaccinations = 0; diff --git a/modules/openlmis-web/src/main/webapp/public/pages/logistics/distribution/partials/child-coverage.html b/modules/openlmis-web/src/main/webapp/public/pages/logistics/distribution/partials/child-coverage.html index 81110fe644..c6362844d6 100644 --- a/modules/openlmis-web/src/main/webapp/public/pages/logistics/distribution/partials/child-coverage.html +++ b/modules/openlmis-web/src/main/webapp/public/pages/logistics/distribution/partials/child-coverage.html @@ -112,7 +112,7 @@

+ ng-bind="formatAsPercent(calculateCoverageRate(getTotal(lineItem.healthCenter11Months, lineItem.outreach11Months), lineItem.targetGroup))"> @@ -196,7 +196,7 @@

ng-hide="hideCell('{{lineItem.vaccination}}')" rowspan="{{productsMap[lineItem.vaccination].rowSpan}}"> + ng-bind="formatAsPercent(calculateWastageRate(productsMap[lineItem.vaccination]))"> From 130f83640bc9539f3e5b70b0212c84a5144dce74 Mon Sep 17 00:00:00 2001 From: joshzamor Date: Mon, 24 Mar 2014 21:20:56 +0000 Subject: [PATCH 02/80] Adding no visit reason: per diem funds unavailable Original no visit reason grouped fuel funds as well as per diem fund unavailability into one reason. This separates them out. --- .../openlmis/distribution/domain/ReasonForNotVisiting.java | 1 + .../openlmis-web/src/main/resources/messages_en.properties | 3 ++- .../openlmis-web/src/main/resources/messages_pt.properties | 3 ++- .../js/distribution/controller/visit-info-controller.js | 1 + .../pages/logistics/distribution/partials/visit-info.html | 7 +++++++ 5 files changed, 13 insertions(+), 2 deletions(-) diff --git a/modules/distribution/src/main/java/org/openlmis/distribution/domain/ReasonForNotVisiting.java b/modules/distribution/src/main/java/org/openlmis/distribution/domain/ReasonForNotVisiting.java index 3feb22d204..72ee1798ad 100644 --- a/modules/distribution/src/main/java/org/openlmis/distribution/domain/ReasonForNotVisiting.java +++ b/modules/distribution/src/main/java/org/openlmis/distribution/domain/ReasonForNotVisiting.java @@ -19,6 +19,7 @@ public enum ReasonForNotVisiting { TRANSPORT_UNAVAILABLE, HEALTH_CENTER_CLOSED, FUEL_FUNDS_UNAVAILABLE, + PERDIEM_FUNDS_UNAVAILABLE, HEALTH_CENTER_NOT_IN_DLS, OTHER } diff --git a/modules/openlmis-web/src/main/resources/messages_en.properties b/modules/openlmis-web/src/main/resources/messages_en.properties index d81797c3a2..2328c8b0bd 100644 --- a/modules/openlmis-web/src/main/resources/messages_en.properties +++ b/modules/openlmis-web/src/main/resources/messages_en.properties @@ -1075,7 +1075,8 @@ label.reason.for.no.visit = Why did no one visit? label.bad.weather = Road impassable or bad weather label.no.transport = Transport not available label.facility.closed = The health centre was closed at the time of visit -label.unavailable.funds = Per diem or fuel funds not available +label.unavailable.funds = Fuel funds not available +label.unavailable.perdiemfunds = Per diem funds not available label.not.part.of.program = The health center is not part of the DLS program msg.pod.save.success = POD saved successfully! diff --git a/modules/openlmis-web/src/main/resources/messages_pt.properties b/modules/openlmis-web/src/main/resources/messages_pt.properties index 15559946a2..b7179fd1cb 100644 --- a/modules/openlmis-web/src/main/resources/messages_pt.properties +++ b/modules/openlmis-web/src/main/resources/messages_pt.properties @@ -1059,7 +1059,8 @@ label.bad.weather=Road im\u2211\u00B4\u2020\u00A5\u00A8\u02C6eather label.no.transport=Tra\u02DC\u00DF\u2202\u0192\u00A9\u02D9\u2206\u02DA\u00E7\u221A\u222Bilable label.facility.closed=The \u02DC\u0192\u02D9\u2206\u00A9\u02D9\u2206\u00A9\u0192\u02D9\u0192\u00A9e time of visit label.not.part.of.program=The health center is not p\u2202\u0192\u00A9\u02D9\u2206art of the DLS program\ -label.unavailable.funds=Per di\u00AE\u00B4\u2020\u2206\u00A5\u00A5\u2020\u00A8\u00A5\u02C6\u00A8\u2026\u02C6\u02DA\u02D9\u2206\u00A9\u00B5\u0192t available +label.unavailable.funds=Fundos de combustível não disponível +label.unavailable.perdiemfunds=Fundos de per diem não disponível msg.pod.save.success = POD saved succe™ssfully! diff --git a/modules/openlmis-web/src/main/webapp/public/js/distribution/controller/visit-info-controller.js b/modules/openlmis-web/src/main/webapp/public/js/distribution/controller/visit-info-controller.js index 3a01baf0e6..aeb56bea30 100644 --- a/modules/openlmis-web/src/main/webapp/public/js/distribution/controller/visit-info-controller.js +++ b/modules/openlmis-web/src/main/webapp/public/js/distribution/controller/visit-info-controller.js @@ -17,6 +17,7 @@ function VisitInfoController($scope, distributionService, $routeParams) { noTransport: "TRANSPORT_UNAVAILABLE", facilityClosed: "HEALTH_CENTER_CLOSED", unavailableFunds: "FUEL_FUNDS_UNAVAILABLE", + unavailablePerDiemFunds: "PERDIEM_FUNDS_UNAVAILABLE", notPartOfProgram: "HEALTH_CENTER_NOT_IN_DLS", other: "OTHER" }; diff --git a/modules/openlmis-web/src/main/webapp/public/pages/logistics/distribution/partials/visit-info.html b/modules/openlmis-web/src/main/webapp/public/pages/logistics/distribution/partials/visit-info.html index f122041afe..c48ce5e579 100644 --- a/modules/openlmis-web/src/main/webapp/public/pages/logistics/distribution/partials/visit-info.html +++ b/modules/openlmis-web/src/main/webapp/public/pages/logistics/distribution/partials/visit-info.html @@ -119,6 +119,13 @@

+
+ + +
Date: Thu, 3 Apr 2014 11:38:35 -0700 Subject: [PATCH 03/80] Making clear name of fuel funds unavailable Name of funds unavailable for fuel was ambiguous. Also added messages to spanish translation from Google Translate. --- .../src/main/resources/messages_en.properties | 4 ++-- .../src/main/resources/messages_es.properties | 3 ++- .../src/main/resources/messages_pt.properties | 4 ++-- .../distribution/controller/visit-info-controller.js | 2 +- .../logistics/distribution/partials/visit-info.html | 10 +++++----- 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/modules/openlmis-web/src/main/resources/messages_en.properties b/modules/openlmis-web/src/main/resources/messages_en.properties index 2328c8b0bd..feaf4344c2 100644 --- a/modules/openlmis-web/src/main/resources/messages_en.properties +++ b/modules/openlmis-web/src/main/resources/messages_en.properties @@ -1075,8 +1075,8 @@ label.reason.for.no.visit = Why did no one visit? label.bad.weather = Road impassable or bad weather label.no.transport = Transport not available label.facility.closed = The health centre was closed at the time of visit -label.unavailable.funds = Fuel funds not available -label.unavailable.perdiemfunds = Per diem funds not available +label.unavailable.funds.fuel = Fuel funds not available +label.unavailable.funds.perdiem = Per diem funds not available label.not.part.of.program = The health center is not part of the DLS program msg.pod.save.success = POD saved successfully! diff --git a/modules/openlmis-web/src/main/resources/messages_es.properties b/modules/openlmis-web/src/main/resources/messages_es.properties index cb12819eef..7d222dd867 100644 --- a/modules/openlmis-web/src/main/resources/messages_es.properties +++ b/modules/openlmis-web/src/main/resources/messages_es.properties @@ -1056,7 +1056,8 @@ label.bad.weather=Road imp\u2202\u0192\u00A9\u02D9\u2206\u02DAweather label.no.transport=Tran\u2211\u00B4\u2020\u00A5\u00A8\u02C6\u2248\u00E7\u221A\u222B\u02DClable label.facility.closed=The hea\u2202\u2206\u02DA\u00AC\u221A\u222B\u02DC visit label.not.part.of.program=The health c\u00A9\u2202\u00DF\u0192\u2202\u0192\u00A9\u02DA\u00AC\u02D9\u00A9\u0192\u2206\u02D9\u0192\u2248\u00A9\u222B\u221Ae DLS program\ -label.unavailable.funds=Per die\u2206\u00A9\u0192\u02D9\u00B5\u00A9\u2206\u2264\u02D9\u00B5 ds not available +label.unavailable.funds.fuel=Fondos de combustible no está disponible +label.unavailable.funds.perdiem=Per diem fondos no disponible msg.pod.save.success = POD saved˚ succes¬sfully! create.user.order.fulfillment.roles = Order Fulfillment Roles∂ diff --git a/modules/openlmis-web/src/main/resources/messages_pt.properties b/modules/openlmis-web/src/main/resources/messages_pt.properties index b7179fd1cb..003f7288c5 100644 --- a/modules/openlmis-web/src/main/resources/messages_pt.properties +++ b/modules/openlmis-web/src/main/resources/messages_pt.properties @@ -1059,8 +1059,8 @@ label.bad.weather=Road im\u2211\u00B4\u2020\u00A5\u00A8\u02C6eather label.no.transport=Tra\u02DC\u00DF\u2202\u0192\u00A9\u02D9\u2206\u02DA\u00E7\u221A\u222Bilable label.facility.closed=The \u02DC\u0192\u02D9\u2206\u00A9\u02D9\u2206\u00A9\u0192\u02D9\u0192\u00A9e time of visit label.not.part.of.program=The health center is not p\u2202\u0192\u00A9\u02D9\u2206art of the DLS program\ -label.unavailable.funds=Fundos de combustível não disponível -label.unavailable.perdiemfunds=Fundos de per diem não disponível +label.unavailable.funds.fuel=Fundos de combustível não disponível +label.unavailable.funds.perdiem=Fundos de per diem não disponível msg.pod.save.success = POD saved succe™ssfully! diff --git a/modules/openlmis-web/src/main/webapp/public/js/distribution/controller/visit-info-controller.js b/modules/openlmis-web/src/main/webapp/public/js/distribution/controller/visit-info-controller.js index aeb56bea30..156265a491 100644 --- a/modules/openlmis-web/src/main/webapp/public/js/distribution/controller/visit-info-controller.js +++ b/modules/openlmis-web/src/main/webapp/public/js/distribution/controller/visit-info-controller.js @@ -16,7 +16,7 @@ function VisitInfoController($scope, distributionService, $routeParams) { badWeather: "ROAD_IMPASSABLE", noTransport: "TRANSPORT_UNAVAILABLE", facilityClosed: "HEALTH_CENTER_CLOSED", - unavailableFunds: "FUEL_FUNDS_UNAVAILABLE", + unavailableFuelFunds: "FUEL_FUNDS_UNAVAILABLE", unavailablePerDiemFunds: "PERDIEM_FUNDS_UNAVAILABLE", notPartOfProgram: "HEALTH_CENTER_NOT_IN_DLS", other: "OTHER" diff --git a/modules/openlmis-web/src/main/webapp/public/pages/logistics/distribution/partials/visit-info.html b/modules/openlmis-web/src/main/webapp/public/pages/logistics/distribution/partials/visit-info.html index c48ce5e579..eae0ae75d5 100644 --- a/modules/openlmis-web/src/main/webapp/public/pages/logistics/distribution/partials/visit-info.html +++ b/modules/openlmis-web/src/main/webapp/public/pages/logistics/distribution/partials/visit-info.html @@ -113,18 +113,18 @@

openlmis-message="label.facility.closed">

- - + value="{{reasons.unavailableFuelFunds}}"/> +
+ openlmis-message="label.unavailable.funds.perdiem">
Date: Thu, 3 Apr 2014 16:40:48 -0700 Subject: [PATCH 04/80] Adding format coverage and wastage rate as percent to adult coverage Also directed functions from controllers to shared utility function. --- .../js/distribution/controller/adult-coverage-controller.js | 4 ++++ .../js/distribution/controller/child-coverage-controller.js | 4 ++-- modules/openlmis-web/src/main/webapp/public/js/shared/util.js | 4 ++++ .../pages/logistics/distribution/partials/adult-coverage.html | 4 ++-- .../pages/logistics/distribution/partials/child-coverage.html | 4 ++-- 5 files changed, 14 insertions(+), 6 deletions(-) diff --git a/modules/openlmis-web/src/main/webapp/public/js/distribution/controller/adult-coverage-controller.js b/modules/openlmis-web/src/main/webapp/public/js/distribution/controller/adult-coverage-controller.js index 423745e126..f6b86d1244 100644 --- a/modules/openlmis-web/src/main/webapp/public/js/distribution/controller/adult-coverage-controller.js +++ b/modules/openlmis-web/src/main/webapp/public/js/distribution/controller/adult-coverage-controller.js @@ -41,4 +41,8 @@ function AdultCoverageController($scope, $routeParams, distributionService) { $scope.adultCoverage.setNotRecorded(); }); }; + + $scope.getFormattedPercent = function (number) { + return utils.getFormattedPercent(number); + }; } diff --git a/modules/openlmis-web/src/main/webapp/public/js/distribution/controller/child-coverage-controller.js b/modules/openlmis-web/src/main/webapp/public/js/distribution/controller/child-coverage-controller.js index 868d5b5820..e21ff6065a 100644 --- a/modules/openlmis-web/src/main/webapp/public/js/distribution/controller/child-coverage-controller.js +++ b/modules/openlmis-web/src/main/webapp/public/js/distribution/controller/child-coverage-controller.js @@ -104,8 +104,8 @@ function ChildCoverageController($scope, $routeParams, distributionService) { return (isUndefined(targetGroup) ? null : (targetGroup === 0 ? null : Math.round((total / targetGroup) * 100))); }; - $scope.formatAsPercent = function (number) { - return (number === null) ? '' : number + ' %'; + $scope.getFormattedAsPercent = function (number) { + return utils.getFormattedPercent(number); }; $scope.calculateWastageRate = function (productsForVaccination) { diff --git a/modules/openlmis-web/src/main/webapp/public/js/shared/util.js b/modules/openlmis-web/src/main/webapp/public/js/shared/util.js index 2fd05235d5..5887f3d62f 100644 --- a/modules/openlmis-web/src/main/webapp/public/js/shared/util.js +++ b/modules/openlmis-web/src/main/webapp/public/js/shared/util.js @@ -53,6 +53,10 @@ var utils = { } }); return sum; + }, + + getFormattedPercent: function(number) { + return (number === null) ? '' : number + '%'; } }; diff --git a/modules/openlmis-web/src/main/webapp/public/pages/logistics/distribution/partials/adult-coverage.html b/modules/openlmis-web/src/main/webapp/public/pages/logistics/distribution/partials/adult-coverage.html index 82926f0edc..f24e916bd8 100644 --- a/modules/openlmis-web/src/main/webapp/public/pages/logistics/distribution/partials/adult-coverage.html +++ b/modules/openlmis-web/src/main/webapp/public/pages/logistics/distribution/partials/adult-coverage.html @@ -119,7 +119,7 @@

- + @@ -136,7 +136,7 @@

- + diff --git a/modules/openlmis-web/src/main/webapp/public/pages/logistics/distribution/partials/child-coverage.html b/modules/openlmis-web/src/main/webapp/public/pages/logistics/distribution/partials/child-coverage.html index c6362844d6..f3f1dbd73e 100644 --- a/modules/openlmis-web/src/main/webapp/public/pages/logistics/distribution/partials/child-coverage.html +++ b/modules/openlmis-web/src/main/webapp/public/pages/logistics/distribution/partials/child-coverage.html @@ -112,7 +112,7 @@

+ ng-bind="getFormattedAsPercent(calculateCoverageRate(getTotal(lineItem.healthCenter11Months, lineItem.outreach11Months), lineItem.targetGroup))"> @@ -196,7 +196,7 @@

ng-hide="hideCell('{{lineItem.vaccination}}')" rowspan="{{productsMap[lineItem.vaccination].rowSpan}}"> + ng-bind="getFormattedAsPercent(calculateWastageRate(productsMap[lineItem.vaccination]))"> From ed538f069aae9f99f8930031b83cb595f7a25482 Mon Sep 17 00:00:00 2001 From: joshzamor Date: Fri, 25 Apr 2014 14:25:48 -0700 Subject: [PATCH 05/80] Fixed app title on login screen App title was always showing as Open-LMIS on login screen, fixed so that it uses the message defined --- modules/openlmis-web/src/main/webapp/public/pages/login.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/openlmis-web/src/main/webapp/public/pages/login.html b/modules/openlmis-web/src/main/webapp/public/pages/login.html index 41d38d894c..f2aa04d734 100644 --- a/modules/openlmis-web/src/main/webapp/public/pages/login.html +++ b/modules/openlmis-web/src/main/webapp/public/pages/login.html @@ -9,11 +9,11 @@ ~ You should have received a copy of the GNU Affero General Public License along with this program.  If not, see http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  --> - + - Open-LMIS + From 02ecfc69108810ee44c1610965ad01584373c1ee Mon Sep 17 00:00:00 2001 From: joshzamor Date: Tue, 13 May 2014 04:37:53 -0700 Subject: [PATCH 06/80] Added basic "powered by OpenLMIS" message to login screen. --- .../openlmis-web/src/main/webapp/public/less/login.less | 7 +++++++ .../src/main/webapp/public/pages/login-form.html | 3 +++ 2 files changed, 10 insertions(+) diff --git a/modules/openlmis-web/src/main/webapp/public/less/login.less b/modules/openlmis-web/src/main/webapp/public/less/login.less index 93a61d658b..31db67ca45 100644 --- a/modules/openlmis-web/src/main/webapp/public/less/login.less +++ b/modules/openlmis-web/src/main/webapp/public/less/login.less @@ -187,6 +187,13 @@ input[type="color"]:focus, padding: 0 0 12px 3px; } +.powered-by { + text-align: right; + padding: 0px 10px; + font-size: 0.7em; + color: #a2a2a2; +} + .modal-login { width: 692px; margin-left: -346px; diff --git a/modules/openlmis-web/src/main/webapp/public/pages/login-form.html b/modules/openlmis-web/src/main/webapp/public/pages/login-form.html index c0339081cb..1c1a8b0d17 100644 --- a/modules/openlmis-web/src/main/webapp/public/pages/login-form.html +++ b/modules/openlmis-web/src/main/webapp/public/pages/login-form.html @@ -40,5 +40,8 @@

+
+ Powered by OpenLMIS +
From ac656ddff942b526ca58a43429badc5af5b2f039 Mon Sep 17 00:00:00 2001 From: joshzamor Date: Fri, 23 May 2014 15:00:23 -0700 Subject: [PATCH 07/80] Fixed bug in utf-8 compatibility of upload files. Without explicitly setting file input as having utf-8 formatting, the encoding is left to a java charset default, which in some java implementations will not be utf-8. --- .../src/main/java/org/openlmis/upload/parser/CsvBeanReader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/upload/src/main/java/org/openlmis/upload/parser/CsvBeanReader.java b/modules/upload/src/main/java/org/openlmis/upload/parser/CsvBeanReader.java index df22bfb6e0..9f4fba8ba7 100644 --- a/modules/upload/src/main/java/org/openlmis/upload/parser/CsvBeanReader.java +++ b/modules/upload/src/main/java/org/openlmis/upload/parser/CsvBeanReader.java @@ -50,7 +50,7 @@ private void configureDozerBeanReader(InputStream inputStream) throws IOExceptio CsvPreference csvPreference = new CsvPreference.Builder(CsvPreference.STANDARD_PREFERENCE) .surroundingSpacesNeedQuotes(true).build(); - BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); dozerBeanReader = new CsvDozerBeanReader(bufferedReader, csvPreference); headers = readHeaders(); String[] mappings = modelClass.getFieldNameMappings(headers); From 25ca8e8b7fda2c53543a8001d2b7fa7914ca9f2d Mon Sep 17 00:00:00 2001 From: joshzamor Date: Mon, 9 Jun 2014 16:48:16 -0700 Subject: [PATCH 08/80] OA-30 adding dosage unit methods for upload files Added dosage unit mapper methods to insert and find by id as well as integration tests. Added dosage unit service and repository and persistence handler stubs. --- .../org/openlmis/core/domain/DosageUnit.java | 7 ++- .../core/repository/DosageUnitRepository.java | 36 ++++++++++++++ .../repository/mapper/DosageUnitMapper.java | 11 +++++ .../core/service/DosageUnitService.java | 30 ++++++++++++ .../upload/DosageUnitPersistenceHandler.java | 48 ++++++++++++++++++ .../repository/mapper/DosageUnitMapperIT.java | 49 +++++++++++++++++++ 6 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 modules/core/src/main/java/org/openlmis/core/repository/DosageUnitRepository.java create mode 100644 modules/core/src/main/java/org/openlmis/core/service/DosageUnitService.java create mode 100644 modules/core/src/main/java/org/openlmis/core/upload/DosageUnitPersistenceHandler.java create mode 100644 modules/core/src/test/java/org/openlmis/core/repository/mapper/DosageUnitMapperIT.java diff --git a/modules/core/src/main/java/org/openlmis/core/domain/DosageUnit.java b/modules/core/src/main/java/org/openlmis/core/domain/DosageUnit.java index da94632cde..c24501904e 100644 --- a/modules/core/src/main/java/org/openlmis/core/domain/DosageUnit.java +++ b/modules/core/src/main/java/org/openlmis/core/domain/DosageUnit.java @@ -14,6 +14,8 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; +import org.openlmis.upload.Importable; +import org.openlmis.upload.annotation.ImportField; /** * DosageUnit represents the Dosage Unit for any product. @@ -22,7 +24,10 @@ @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode(callSuper = false) -public class DosageUnit extends BaseModel { +public class DosageUnit extends BaseModel implements Importable { + @ImportField(mandatory = true, name="Dosage Unit Code") private String code; + + @ImportField(mandatory = true, name="Display Order") private int displayOrder; } diff --git a/modules/core/src/main/java/org/openlmis/core/repository/DosageUnitRepository.java b/modules/core/src/main/java/org/openlmis/core/repository/DosageUnitRepository.java new file mode 100644 index 0000000000..2eb345f6a7 --- /dev/null +++ b/modules/core/src/main/java/org/openlmis/core/repository/DosageUnitRepository.java @@ -0,0 +1,36 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2013 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + *   + * 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 Affero General Public License for more details. + * You should have received a copy of the GNU Affero General Public License along with this program.  If not, see http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  + */ + +package org.openlmis.core.repository; + +import lombok.NoArgsConstructor; +import org.openlmis.core.domain.DosageUnit; +import org.openlmis.core.exception.DataException; +import org.openlmis.core.repository.mapper.DosageUnitMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.stereotype.Repository; + +@Repository +@NoArgsConstructor +public class DosageUnitRepository { + + private DosageUnitMapper duMapper; + + @Autowired + public DosageUnitRepository(DosageUnitMapper dosageUnitMapper) { + this.duMapper = dosageUnitMapper; + } + + public DosageUnit getByCode(String code) { + return duMapper.getByCode(code); + } +} diff --git a/modules/core/src/main/java/org/openlmis/core/repository/mapper/DosageUnitMapper.java b/modules/core/src/main/java/org/openlmis/core/repository/mapper/DosageUnitMapper.java index 22fc267419..56dcb999cd 100644 --- a/modules/core/src/main/java/org/openlmis/core/repository/mapper/DosageUnitMapper.java +++ b/modules/core/src/main/java/org/openlmis/core/repository/mapper/DosageUnitMapper.java @@ -10,6 +10,8 @@ package org.openlmis.core.repository.mapper; +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Options; import org.apache.ibatis.annotations.Select; import org.openlmis.core.domain.DosageUnit; import org.springframework.stereotype.Repository; @@ -20,8 +22,17 @@ @Repository public interface DosageUnitMapper { + @Insert({"INSERT INTO dosage_units", + "(code, displayOrder, createdDate)", + "VALUES", + "(#{code}, #{displayOrder}, NOW())"}) + @Options(useGeneratedKeys = true) + public void insert(DosageUnit dosageUnit); + // Used by mapper @Select("SELECT * FROM dosage_units WHERE id = #{id}") DosageUnit getById(Long id); + @Select("SELECT * FROM dosage_units WHERE LOWER(code) = LOWER(#{code})") + DosageUnit getByCode(String code); } diff --git a/modules/core/src/main/java/org/openlmis/core/service/DosageUnitService.java b/modules/core/src/main/java/org/openlmis/core/service/DosageUnitService.java new file mode 100644 index 0000000000..ff499424e3 --- /dev/null +++ b/modules/core/src/main/java/org/openlmis/core/service/DosageUnitService.java @@ -0,0 +1,30 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2013 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + *   + * 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 Affero General Public License for more details. + * You should have received a copy of the GNU Affero General Public License along with this program.  If not, see http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  + */ + +package org.openlmis.core.service; + +import lombok.NoArgsConstructor; +import org.openlmis.core.domain.DosageUnit; +import org.openlmis.core.repository.DosageUnitRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + + +@Service +@NoArgsConstructor +public class DosageUnitService { + + private DosageUnitRepository duRep; + + @Autowired + public DosageUnitService(DosageUnitRepository dosageUnitRepository) { + this.duRep = dosageUnitRepository; + } +} diff --git a/modules/core/src/main/java/org/openlmis/core/upload/DosageUnitPersistenceHandler.java b/modules/core/src/main/java/org/openlmis/core/upload/DosageUnitPersistenceHandler.java new file mode 100644 index 0000000000..2c68bc09a9 --- /dev/null +++ b/modules/core/src/main/java/org/openlmis/core/upload/DosageUnitPersistenceHandler.java @@ -0,0 +1,48 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2013 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + *   + * 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 Affero General Public License for more details. + * You should have received a copy of the GNU Affero General Public License along with this program.  If not, see http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  + */ + +package org.openlmis.core.upload; + +import org.openlmis.core.domain.BaseModel; +import org.openlmis.core.domain.DosageUnit; +import org.openlmis.core.service.DosageUnitService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class DosageUnitPersistenceHandler extends AbstractModelPersistenceHandler { + + private DosageUnitService dosageUnitService; + + @Autowired + public DosageUnitPersistenceHandler(DosageUnitService dosageUnitService) { + this.dosageUnitService = dosageUnitService; + } + + @Override + protected BaseModel getExisting(BaseModel record) { + // use dosage unit service to find an existing dosage unit + //return productCategoryService.getExisting((ProductCategory) record); + return null; + } + + @Override + protected void save(BaseModel modelClass) { + // use dosage unit service to save the dosage unit + //dosageUnitService.save((DosageUnit) modelClass); + //productCategoryService.save((ProductCategory) modelClass); + } + + @Override + public String getMessageKey() { + //TODO: create a dosage unit duplicate error message code + return "error.duplicate.product.category"; + } +} \ No newline at end of file diff --git a/modules/core/src/test/java/org/openlmis/core/repository/mapper/DosageUnitMapperIT.java b/modules/core/src/test/java/org/openlmis/core/repository/mapper/DosageUnitMapperIT.java new file mode 100644 index 0000000000..14dbae7c4e --- /dev/null +++ b/modules/core/src/test/java/org/openlmis/core/repository/mapper/DosageUnitMapperIT.java @@ -0,0 +1,49 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2013 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + *   + * 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 Affero General Public License for more details. + * You should have received a copy of the GNU Affero General Public License along with this program.  If not, see http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  + */ + +package org.openlmis.core.repository.mapper; + +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; +import org.openlmis.core.domain.DosageUnit; +import org.openlmis.db.categories.IntegrationTests; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.transaction.TransactionConfiguration; +import org.springframework.transaction.annotation.Transactional; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = "classpath:test-applicationContext-core.xml") +@TransactionConfiguration(defaultRollback = true, transactionManager = "openLmisTransactionManager") +@Transactional +@Category(IntegrationTests.class) +public class DosageUnitMapperIT { + + @Autowired + private DosageUnitMapper duMapper; + + @Test + public void shouldInsertDosageUnitByCode() { + DosageUnit du = new DosageUnit(); + du.setCode("du code"); + du.setDisplayOrder(1); + duMapper.insert(du); + + DosageUnit returnedDu = duMapper.getByCode("DU CODE"); + + assertThat(returnedDu.getCode(), is(du.getCode())); + assertThat(returnedDu.getDisplayOrder(), is(du.getDisplayOrder())); + } +} \ No newline at end of file From 13c7d648ca32cd443a4ebc48d895b5ca7881599b Mon Sep 17 00:00:00 2001 From: joshzamor Date: Tue, 10 Jun 2014 16:45:53 -0700 Subject: [PATCH 09/80] OA-30 completed file upload of dosage units. Filled out persistence handler, mapper, repository and service classes. Added 2 translatable messages with spanish and portuguese translations from Google Translate. --- .../org/openlmis/core/domain/BaseModel.java | 7 ++ .../org/openlmis/core/domain/DosageUnit.java | 8 +++ .../core/repository/DosageUnitRepository.java | 19 ++++++ .../repository/mapper/DosageUnitMapper.java | 15 +++-- .../core/service/DosageUnitService.java | 9 +++ .../upload/DosageUnitPersistenceHandler.java | 11 +-- .../repository/DosageUnitRepositoryTest.java | 67 +++++++++++++++++++ .../repository/mapper/DosageUnitMapperIT.java | 16 +++++ ..._add_created_and_modified_dosage_units.sql | 14 ++++ .../src/main/resources/applicationContext.xml | 9 +++ .../src/main/resources/messages_en.properties | 2 + .../src/main/resources/messages_es.properties | 4 +- .../src/main/resources/messages_pt.properties | 2 + 13 files changed, 170 insertions(+), 13 deletions(-) create mode 100644 modules/core/src/test/java/org/openlmis/core/repository/DosageUnitRepositoryTest.java create mode 100644 modules/db/src/main/resources/db/migration/V131__add_created_and_modified_dosage_units.sql diff --git a/modules/core/src/main/java/org/openlmis/core/domain/BaseModel.java b/modules/core/src/main/java/org/openlmis/core/domain/BaseModel.java index d31539c8da..b9b35c89f7 100644 --- a/modules/core/src/main/java/org/openlmis/core/domain/BaseModel.java +++ b/modules/core/src/main/java/org/openlmis/core/domain/BaseModel.java @@ -39,4 +39,11 @@ public abstract class BaseModel { @JsonIgnore protected Date modifiedDate; + /** + * Determines if this BaseModel has an id set or not. + * @return true if it has an id set, false otherwise + */ + public boolean hasId() { + return id != null ? true : false; + } } diff --git a/modules/core/src/main/java/org/openlmis/core/domain/DosageUnit.java b/modules/core/src/main/java/org/openlmis/core/domain/DosageUnit.java index c24501904e..068787dd66 100644 --- a/modules/core/src/main/java/org/openlmis/core/domain/DosageUnit.java +++ b/modules/core/src/main/java/org/openlmis/core/domain/DosageUnit.java @@ -30,4 +30,12 @@ public class DosageUnit extends BaseModel implements Importable { @ImportField(mandatory = true, name="Display Order") private int displayOrder; + + /** + * Validation method for an instantiated DosageUnit. A valid dosage unit has a code and a display order. + * @return true if this dosage unit is a defined well (valid), false otherwise. + */ + public boolean isValid() { + return code != null && code.length() > 0 && displayOrder > 0; + } } diff --git a/modules/core/src/main/java/org/openlmis/core/repository/DosageUnitRepository.java b/modules/core/src/main/java/org/openlmis/core/repository/DosageUnitRepository.java index 2eb345f6a7..1191699368 100644 --- a/modules/core/src/main/java/org/openlmis/core/repository/DosageUnitRepository.java +++ b/modules/core/src/main/java/org/openlmis/core/repository/DosageUnitRepository.java @@ -33,4 +33,23 @@ public DosageUnitRepository(DosageUnitMapper dosageUnitMapper) { public DosageUnit getByCode(String code) { return duMapper.getByCode(code); } + + public DosageUnit getExisting(DosageUnit du) { + return duMapper.getByCode(du.getCode()); + } + + public void insert(DosageUnit du) { + if(du.isValid() == false) throw new DataException("error.reference.data.missing"); + if(getByCode(du.getCode()) != null) throw new DataException("error.duplicate.dosage.unit.code"); + + try { + duMapper.insert(du); + } catch(DataIntegrityViolationException dive) { + throw new DataException("error.incorrect.length", dive); + } + } + + public void update(DosageUnit du) { + duMapper.update(du); + } } diff --git a/modules/core/src/main/java/org/openlmis/core/repository/mapper/DosageUnitMapper.java b/modules/core/src/main/java/org/openlmis/core/repository/mapper/DosageUnitMapper.java index 56dcb999cd..c722f6799b 100644 --- a/modules/core/src/main/java/org/openlmis/core/repository/mapper/DosageUnitMapper.java +++ b/modules/core/src/main/java/org/openlmis/core/repository/mapper/DosageUnitMapper.java @@ -13,6 +13,7 @@ import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Options; import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.Update; import org.openlmis.core.domain.DosageUnit; import org.springframework.stereotype.Repository; @@ -23,13 +24,19 @@ public interface DosageUnitMapper { @Insert({"INSERT INTO dosage_units", - "(code, displayOrder, createdDate)", + "(code, displayOrder, createdBy, createdDate, modifiedBy, modifiedDate)", "VALUES", - "(#{code}, #{displayOrder}, NOW())"}) + "(#{code}, #{displayOrder}, #{createdBy}, NOW(), #{modifiedBy}, NOW())"}) @Options(useGeneratedKeys = true) - public void insert(DosageUnit dosageUnit); + void insert(DosageUnit dosageUnit); + + @Update({"UPDATE dosage_units SET code = #{code},", + "displayOrder = #{displayOrder},", + "modifiedBy=#{modifiedBy},", + "modifiedDate=NOW()", + "WHERE id = #{id}"}) + void update(DosageUnit dosageUnit); - // Used by mapper @Select("SELECT * FROM dosage_units WHERE id = #{id}") DosageUnit getById(Long id); diff --git a/modules/core/src/main/java/org/openlmis/core/service/DosageUnitService.java b/modules/core/src/main/java/org/openlmis/core/service/DosageUnitService.java index ff499424e3..c5b07a730b 100644 --- a/modules/core/src/main/java/org/openlmis/core/service/DosageUnitService.java +++ b/modules/core/src/main/java/org/openlmis/core/service/DosageUnitService.java @@ -27,4 +27,13 @@ public class DosageUnitService { public DosageUnitService(DosageUnitRepository dosageUnitRepository) { this.duRep = dosageUnitRepository; } + + public DosageUnit getExisting(DosageUnit du) { + return duRep.getExisting(du); + } + + public void save(DosageUnit du) { + if( du.hasId() ) duRep.update(du); + else duRep.insert(du); + } } diff --git a/modules/core/src/main/java/org/openlmis/core/upload/DosageUnitPersistenceHandler.java b/modules/core/src/main/java/org/openlmis/core/upload/DosageUnitPersistenceHandler.java index 2c68bc09a9..4f57c47787 100644 --- a/modules/core/src/main/java/org/openlmis/core/upload/DosageUnitPersistenceHandler.java +++ b/modules/core/src/main/java/org/openlmis/core/upload/DosageUnitPersistenceHandler.java @@ -28,21 +28,16 @@ public DosageUnitPersistenceHandler(DosageUnitService dosageUnitService) { @Override protected BaseModel getExisting(BaseModel record) { - // use dosage unit service to find an existing dosage unit - //return productCategoryService.getExisting((ProductCategory) record); - return null; + return dosageUnitService.getExisting((DosageUnit) record); } @Override protected void save(BaseModel modelClass) { - // use dosage unit service to save the dosage unit - //dosageUnitService.save((DosageUnit) modelClass); - //productCategoryService.save((ProductCategory) modelClass); + dosageUnitService.save((DosageUnit) modelClass); } @Override public String getMessageKey() { - //TODO: create a dosage unit duplicate error message code - return "error.duplicate.product.category"; + return "error.duplicate.dosage.unit.code"; } } \ No newline at end of file diff --git a/modules/core/src/test/java/org/openlmis/core/repository/DosageUnitRepositoryTest.java b/modules/core/src/test/java/org/openlmis/core/repository/DosageUnitRepositoryTest.java new file mode 100644 index 0000000000..8852ad65bd --- /dev/null +++ b/modules/core/src/test/java/org/openlmis/core/repository/DosageUnitRepositoryTest.java @@ -0,0 +1,67 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2013 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + *   + * 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 Affero General Public License for more details. + * You should have received a copy of the GNU Affero General Public License along with this program.  If not, see http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  + */ + +package org.openlmis.core.repository; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import static org.mockito.Mockito.*; + +import org.mockito.runners.MockitoJUnitRunner; +import org.openlmis.core.domain.DosageUnit; +import org.openlmis.core.exception.DataException; +import org.openlmis.core.repository.mapper.DosageUnitMapper; +import org.openlmis.db.categories.UnitTests; + +@Category(UnitTests.class) +@RunWith(MockitoJUnitRunner.class) +public class DosageUnitRepositoryTest { + + @Mock + private DosageUnitMapper duMapper; + + private DosageUnitRepository duRep; + private DosageUnit du; + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Before + public void setUp() { + duRep = new DosageUnitRepository(duMapper); + du = new DosageUnit(); + du.setCode("some code"); + du.setDisplayOrder(1); + } + + @Test + public void shouldThrowExceptionIfInsertingDuplicateCode() { + when(duMapper.getByCode("some code")).thenReturn(du); + expectedException.expect(DataException.class); + expectedException.expectMessage("error.duplicate.dosage.unit.code"); + + duRep.insert(du); + } + + @Test + public void shouldThrowExceptionIfInsertingInvalidObject() { + DosageUnit badDu = new DosageUnit(); + badDu.setCode("blah"); // forget to set display order + expectedException.expect(DataException.class); + expectedException.expectMessage("error.reference.data.missing"); + + duRep.insert(badDu); + } +} diff --git a/modules/core/src/test/java/org/openlmis/core/repository/mapper/DosageUnitMapperIT.java b/modules/core/src/test/java/org/openlmis/core/repository/mapper/DosageUnitMapperIT.java index 14dbae7c4e..48850bc723 100644 --- a/modules/core/src/test/java/org/openlmis/core/repository/mapper/DosageUnitMapperIT.java +++ b/modules/core/src/test/java/org/openlmis/core/repository/mapper/DosageUnitMapperIT.java @@ -46,4 +46,20 @@ public void shouldInsertDosageUnitByCode() { assertThat(returnedDu.getCode(), is(du.getCode())); assertThat(returnedDu.getDisplayOrder(), is(du.getDisplayOrder())); } + + @Test + public void shouldUpdateDosageUnit() { + DosageUnit du = new DosageUnit(); + du.setCode("someCode"); + du.setDisplayOrder(1); + duMapper.insert(du); + + DosageUnit returnedDu = duMapper.getByCode("someCode"); + returnedDu.setCode("someOtherCode"); + duMapper.update(returnedDu); + + DosageUnit updatedDu = duMapper.getByCode("someOtherCode"); + assertThat(updatedDu.getCode(), is(returnedDu.getCode())); + assertThat(updatedDu.getDisplayOrder(), is(returnedDu.getDisplayOrder())); + } } \ No newline at end of file diff --git a/modules/db/src/main/resources/db/migration/V131__add_created_and_modified_dosage_units.sql b/modules/db/src/main/resources/db/migration/V131__add_created_and_modified_dosage_units.sql new file mode 100644 index 0000000000..9462dfccdc --- /dev/null +++ b/modules/db/src/main/resources/db/migration/V131__add_created_and_modified_dosage_units.sql @@ -0,0 +1,14 @@ +-- +-- This program is part of the OpenLMIS logistics management information system platform software. +-- Copyright © 2013 VillageReach +-- +-- This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +--   +-- 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 Affero General Public License for more details. +-- You should have received a copy of the GNU Affero General Public License along with this program.  If not, see http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  +-- + +ALTER TABLE dosage_units ADD COLUMN createdBy INTEGER; +ALTER TABLE dosage_units ADD COLUMN modifiedBy INTEGER; +ALTER TABLE dosage_units ADD COLUMN modifiedDate TIMESTAMP WITH TIME ZONE DEFAULT NOW(); + diff --git a/modules/openlmis-web/src/main/resources/applicationContext.xml b/modules/openlmis-web/src/main/resources/applicationContext.xml index 1076847732..3b7aeec92d 100644 --- a/modules/openlmis-web/src/main/resources/applicationContext.xml +++ b/modules/openlmis-web/src/main/resources/applicationContext.xml @@ -279,6 +279,15 @@ + + + + + org.openlmis.core.domain.DosageUnit + + + + diff --git a/modules/openlmis-web/src/main/resources/messages_en.properties b/modules/openlmis-web/src/main/resources/messages_en.properties index d4a5c55a88..efece4886c 100644 --- a/modules/openlmis-web/src/main/resources/messages_en.properties +++ b/modules/openlmis-web/src/main/resources/messages_en.properties @@ -141,6 +141,7 @@ upload.record.error = {0} in Record No. "{1}" error.upload.invalid.header = "Invalid Headers in upload file: {0}" error.upload.missing.mandatory.columns = "Missing Mandatory columns in upload file: {0}" error.upload.header.missing = "Header for column ({0}) is missing." +label.dosage.units = Dosage Units error.duplicate.product.code.program.code = Duplicate entry for Product Code and Program Code combination found error.duplicate.facility.approved.product = Duplicate facility approved product. @@ -148,6 +149,7 @@ error.duplicate.facility.code = Duplicate Facility Code error.duplicate.delivery.zone = Duplicate delivery zone Code found error.duplicate.delivery.zone.program = Duplicate Delivery zone code and Program code combination found error.duplicate.delivery.zone.member = Duplicate Delivery Zone code and Member code combination found +error.duplicate.dosage.unit.code = Duplicate Dosage Unit code found error.duplicate.geographic.zone.code = Duplicate Geographic Zone Code error.duplicate.product.category = Duplicate Product Category error.duplicate.product.group.code = Duplicate Product Group Code diff --git a/modules/openlmis-web/src/main/resources/messages_es.properties b/modules/openlmis-web/src/main/resources/messages_es.properties index bdeb7ed7be..b7d4e3442b 100644 --- a/modules/openlmis-web/src/main/resources/messages_es.properties +++ b/modules/openlmis-web/src/main/resources/messages_es.properties @@ -1206,4 +1206,6 @@ header.unit.of.measure = Un∆˙it of measure header.max.months.of.stock = Max mon¬ths of stock header.min.months.of.stock = Min mon∆ƒths of stock header.global.active = Global act˙†∂ive -header.active.at.program = Act˙∂ive at program \ No newline at end of file +header.active.at.program = Act˙∂ive at program +label.dosage.units=Dosis Unidades +error.duplicate.dosage.unit.code=Código de unidad de dosificación es duplicado \ No newline at end of file diff --git a/modules/openlmis-web/src/main/resources/messages_pt.properties b/modules/openlmis-web/src/main/resources/messages_pt.properties index e008c89bd0..b0fad6c354 100644 --- a/modules/openlmis-web/src/main/resources/messages_pt.properties +++ b/modules/openlmis-web/src/main/resources/messages_pt.properties @@ -1207,3 +1207,5 @@ header.max.months.of.stock = Max mon˚¨ths of stock header.min.months.of.stock = Min montåßhs of stock header.global.active = Global a©ctive header.active.at.program = Acti∆˙©ve at program +label.dosage.units=Unidades de dosagem +error.duplicate.dosage.unit.code=Código Duplicate Unidade Dosagem encontrado From 94b2492070aa8369705d68cd41e364a557b7dbaf Mon Sep 17 00:00:00 2001 From: joshzamor Date: Fri, 13 Jun 2014 12:12:59 -0700 Subject: [PATCH 10/80] OA-31, adding methods to product form mapper to insert and update, also added integration tests as well as DB migration to add missing fields from BaseModel class. --- .../org/openlmis/core/domain/ProductForm.java | 7 +- .../repository/mapper/ProductFormMapper.java | 19 ++++++ .../mapper/ProductFormMapperIT.java | 66 +++++++++++++++++++ ...add_created_and_modified_product_forms.sql | 13 ++++ 4 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 modules/core/src/test/java/org/openlmis/core/repository/mapper/ProductFormMapperIT.java create mode 100644 modules/db/src/main/resources/db/migration/V132__add_created_and_modified_product_forms.sql diff --git a/modules/core/src/main/java/org/openlmis/core/domain/ProductForm.java b/modules/core/src/main/java/org/openlmis/core/domain/ProductForm.java index 638aff251d..392257c2f2 100644 --- a/modules/core/src/main/java/org/openlmis/core/domain/ProductForm.java +++ b/modules/core/src/main/java/org/openlmis/core/domain/ProductForm.java @@ -13,6 +13,8 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; +import org.openlmis.upload.Importable; +import org.openlmis.upload.annotation.ImportField; /** * ProductForm represents real world entity for product form. @@ -20,7 +22,10 @@ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = false) -public class ProductForm extends BaseModel { +public class ProductForm extends BaseModel implements Importable { + @ImportField(name="Product Form Code", mandatory=true) private String code; + + @ImportField(name="Display Order", mandatory=true) private int displayOrder; } diff --git a/modules/core/src/main/java/org/openlmis/core/repository/mapper/ProductFormMapper.java b/modules/core/src/main/java/org/openlmis/core/repository/mapper/ProductFormMapper.java index 26cbfacb02..1d02829d2f 100644 --- a/modules/core/src/main/java/org/openlmis/core/repository/mapper/ProductFormMapper.java +++ b/modules/core/src/main/java/org/openlmis/core/repository/mapper/ProductFormMapper.java @@ -10,7 +10,10 @@ package org.openlmis.core.repository.mapper; +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Options; import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.Update; import org.openlmis.core.domain.ProductForm; import org.springframework.stereotype.Repository; @@ -25,4 +28,20 @@ public interface ProductFormMapper { @Select("SELECT * FROM product_forms WHERE id = #{id}") ProductForm getById(Integer id); + @Select("SELECT * FROM product_forms WHERE LOWER(code) = LOWER(#{code})") + ProductForm getByCode(String code); + + @Insert({"INSERT INTO product_forms", + "(code, displayOrder, createdBy, createdDate, modifiedBy, modifiedDate)", + "VALUES", + "(#{code}, #{displayOrder}, #{createdBy}, NOW(), #{modifiedBy}, NOW())"}) + @Options(useGeneratedKeys = true) + void insert(ProductForm productForm); + + @Update({"UPDATE product_forms SET code = #{code},", + "displayOrder = #{displayOrder},", + "modifiedBy=#{modifiedBy},", + "modifiedDate=NOW()", + "WHERE id = #{id}"}) + void update(ProductForm productForm); } diff --git a/modules/core/src/test/java/org/openlmis/core/repository/mapper/ProductFormMapperIT.java b/modules/core/src/test/java/org/openlmis/core/repository/mapper/ProductFormMapperIT.java new file mode 100644 index 0000000000..ede49ee3ea --- /dev/null +++ b/modules/core/src/test/java/org/openlmis/core/repository/mapper/ProductFormMapperIT.java @@ -0,0 +1,66 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2013 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + *   + * 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 Affero General Public License for more details. + * You should have received a copy of the GNU Affero General Public License along with this program.  If not, see http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  + */ + +package org.openlmis.core.repository.mapper; + +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; +import org.openlmis.core.domain.ProductForm; +import org.openlmis.db.categories.IntegrationTests; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.transaction.TransactionConfiguration; +import org.springframework.transaction.annotation.Transactional; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = "classpath:test-applicationContext-core.xml") +@TransactionConfiguration(defaultRollback = true, transactionManager = "openLmisTransactionManager") +@Transactional +@Category(IntegrationTests.class) +public class ProductFormMapperIT { + + @Autowired + private ProductFormMapper pfMapper; + + @Test + public void shouldInsertProductFormByCode() { + ProductForm pf = new ProductForm(); + pf.setCode("somecode"); + pf.setDisplayOrder(1); + pfMapper.insert(pf); + + ProductForm retPf = pfMapper.getByCode("somecode"); + + assertThat(retPf.getCode(), is(pf.getCode())); + assertThat(retPf.getDisplayOrder(), is(pf.getDisplayOrder())); + } + + @Test + public void shouldUpdateProductFormByCode() { + // insert seed + ProductForm pf = new ProductForm(); + pf.setCode("somecode"); + pf.setDisplayOrder(1); + pfMapper.insert(pf); + + // update seed + pf.setDisplayOrder(2); + pfMapper.update(pf); + + // pull from DB and check update worked + ProductForm pfRet = pfMapper.getByCode("somecode"); + assertThat(pfRet.getDisplayOrder(), is(2)); + } +} diff --git a/modules/db/src/main/resources/db/migration/V132__add_created_and_modified_product_forms.sql b/modules/db/src/main/resources/db/migration/V132__add_created_and_modified_product_forms.sql new file mode 100644 index 0000000000..27fce5a2cf --- /dev/null +++ b/modules/db/src/main/resources/db/migration/V132__add_created_and_modified_product_forms.sql @@ -0,0 +1,13 @@ +-- +-- This program is part of the OpenLMIS logistics management information system platform software. +-- Copyright © 2013 VillageReach +-- +-- This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +--   +-- 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 Affero General Public License for more details. +-- You should have received a copy of the GNU Affero General Public License along with this program.  If not, see http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  +-- + +ALTER TABLE product_forms ADD COLUMN createdBy INTEGER; +ALTER TABLE product_forms ADD COLUMN modifiedBy INTEGER; +ALTER TABLE product_forms ADD COLUMN modifiedDate TIMESTAMP WITH TIME ZONE DEFAULT NOW(); From 599e690bd460b44a6ab1c24a77bcf43c61b828d9 Mon Sep 17 00:00:00 2001 From: joshzamor Date: Fri, 13 Jun 2014 16:07:54 -0700 Subject: [PATCH 11/80] OA-31, added product form repository, repository tests and service for handling product form uploads. --- .../org/openlmis/core/domain/ProductForm.java | 8 +- .../repository/ProductFormRepository.java | 71 ++++++++++++++++++ .../repository/mapper/ProductFormMapper.java | 4 +- .../core/service/ProductFormService.java | 37 ++++++++++ .../repository/ProductFormRepositoryTest.java | 74 +++++++++++++++++++ 5 files changed, 191 insertions(+), 3 deletions(-) create mode 100644 modules/core/src/main/java/org/openlmis/core/repository/ProductFormRepository.java create mode 100644 modules/core/src/main/java/org/openlmis/core/service/ProductFormService.java create mode 100644 modules/core/src/test/java/org/openlmis/core/repository/ProductFormRepositoryTest.java diff --git a/modules/core/src/main/java/org/openlmis/core/domain/ProductForm.java b/modules/core/src/main/java/org/openlmis/core/domain/ProductForm.java index 392257c2f2..bb8c64981f 100644 --- a/modules/core/src/main/java/org/openlmis/core/domain/ProductForm.java +++ b/modules/core/src/main/java/org/openlmis/core/domain/ProductForm.java @@ -27,5 +27,11 @@ public class ProductForm extends BaseModel implements Importable { private String code; @ImportField(name="Display Order", mandatory=true) - private int displayOrder; + private Integer displayOrder; + + public boolean isValid() { + return code != null + && code.length() > 0 + && displayOrder != null; + } } diff --git a/modules/core/src/main/java/org/openlmis/core/repository/ProductFormRepository.java b/modules/core/src/main/java/org/openlmis/core/repository/ProductFormRepository.java new file mode 100644 index 0000000000..1275b165af --- /dev/null +++ b/modules/core/src/main/java/org/openlmis/core/repository/ProductFormRepository.java @@ -0,0 +1,71 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2013 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + *   + * 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 Affero General Public License for more details. + * You should have received a copy of the GNU Affero General Public License along with this program.  If not, see http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  + */ + +package org.openlmis.core.repository; + +import lombok.NoArgsConstructor; +import org.openlmis.core.domain.ProductForm; +import org.openlmis.core.exception.DataException; +import org.openlmis.core.repository.mapper.ProductFormMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.stereotype.Repository; + +/** + * Repository relating to persisting {@link org.openlmis.core.domain.ProductForm} entities. + */ +@Repository +@NoArgsConstructor +public class ProductFormRepository { + + @Autowired + private ProductFormMapper pfMapper; + + ProductFormRepository(ProductFormMapper productFormMapper) {this.pfMapper = productFormMapper;} + + /** + * Gets existing {@link org.openlmis.core.domain.ProductForm} by it's code. + * @param code the ProductForm's code. + * @return The ProductForm that has that code, or null if no such ProductForm + * exists with the given code. + */ + public ProductForm getByCode(String code) {return pfMapper.getByCode(code);} + + /** + * Insert a new {@link org.openlmis.core.domain.ProductForm}. + * @param pf {@link org.openlmis.core.domain.ProductForm} to insert. + * @throws DataException if entity is invalid or already exists. + */ + public void insert(ProductForm pf) { + if(pf.isValid() == false) throw new DataException("error.reference.data.missing"); + if(getByCode(pf.getCode()) != null) throw new DataException("error.duplicate.dosage.unit.code"); + + try { + pfMapper.insert(pf); + } catch(DataIntegrityViolationException dive) { + throw new DataException("error.incorrect.length", dive); + } + } + + /** + * Updates an existing {@link org.openlmis.core.domain.ProductForm}. + * @param pf {@link org.openlmis.core.domain.ProductForm} to update. + * @throws DataException if entity is invalid. + */ + public void update(ProductForm pf) { + if(pf.isValid() == false) throw new DataException("error.reference.data.missing"); + + try { + pfMapper.update(pf); + } catch(DataIntegrityViolationException dive) { + throw new DataException("error.incorrect.length", dive); + } + } +} \ No newline at end of file diff --git a/modules/core/src/main/java/org/openlmis/core/repository/mapper/ProductFormMapper.java b/modules/core/src/main/java/org/openlmis/core/repository/mapper/ProductFormMapper.java index 1d02829d2f..47be6778f2 100644 --- a/modules/core/src/main/java/org/openlmis/core/repository/mapper/ProductFormMapper.java +++ b/modules/core/src/main/java/org/openlmis/core/repository/mapper/ProductFormMapper.java @@ -25,8 +25,8 @@ public interface ProductFormMapper { // Used by ProductMapper @SuppressWarnings("unused") - @Select("SELECT * FROM product_forms WHERE id = #{id}") - ProductForm getById(Integer id); + @Select("SELECT * FROM product_forms WHERE id = #{id}") + ProductForm getById(Integer id); @Select("SELECT * FROM product_forms WHERE LOWER(code) = LOWER(#{code})") ProductForm getByCode(String code); diff --git a/modules/core/src/main/java/org/openlmis/core/service/ProductFormService.java b/modules/core/src/main/java/org/openlmis/core/service/ProductFormService.java new file mode 100644 index 0000000000..008f23e318 --- /dev/null +++ b/modules/core/src/main/java/org/openlmis/core/service/ProductFormService.java @@ -0,0 +1,37 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2013 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + *   + * 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 Affero General Public License for more details. + * You should have received a copy of the GNU Affero General Public License along with this program.  If not, see http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  + */ + +package org.openlmis.core.service; + +import lombok.NoArgsConstructor; +import org.openlmis.core.domain.ProductForm; +import org.openlmis.core.repository.ProductFormRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * Service responsible for managing {@link org.openlmis.core.domain.ProductForm} entities. + */ +@Service +@NoArgsConstructor +public class ProductFormService { + + private ProductFormRepository pfRep; + + @Autowired + public ProductFormService(ProductFormRepository productFormRepository) {pfRep = productFormRepository;} + + public ProductForm getExisting(ProductForm pf) {return pfRep.getByCode(pf.getCode());} + + public void save(ProductForm pf) { + if(pf.hasId()) pfRep.update(pf); + else pfRep.insert(pf); + } +} diff --git a/modules/core/src/test/java/org/openlmis/core/repository/ProductFormRepositoryTest.java b/modules/core/src/test/java/org/openlmis/core/repository/ProductFormRepositoryTest.java new file mode 100644 index 0000000000..24bd0b4819 --- /dev/null +++ b/modules/core/src/test/java/org/openlmis/core/repository/ProductFormRepositoryTest.java @@ -0,0 +1,74 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2013 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + *   + * 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 Affero General Public License for more details. + * You should have received a copy of the GNU Affero General Public License along with this program.  If not, see http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  + */ + +package org.openlmis.core.repository; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.openlmis.core.domain.ProductForm; +import org.openlmis.core.exception.DataException; +import org.openlmis.core.repository.mapper.ProductFormMapper; +import org.openlmis.db.categories.UnitTests; +import org.springframework.beans.factory.annotation.Autowired; + +import static org.mockito.Mockito.when; + +@Category(UnitTests.class) +@RunWith(MockitoJUnitRunner.class) +public class ProductFormRepositoryTest { + + @Mock + private ProductFormMapper pfMapper; + + private ProductFormRepository pfRep; + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Before + public void setup() { + pfRep = new ProductFormRepository(pfMapper); + } + + @Test + public void shouldThrowExceptionIfInsertingInValid() { + ProductForm pf = new ProductForm(); + expectedException.expect(DataException.class); + expectedException.expectMessage("error.reference.data.missing"); + pfRep.insert(pf); + } + + + @Test + public void shouldThrowExceptionIfInsertingDuplicate() { + ProductForm pf = new ProductForm(); + pf.setCode("somecode"); + pf.setDisplayOrder(2); + + when(pfMapper.getByCode(pf.getCode())).thenReturn(pf); + expectedException.expect(DataException.class); + expectedException.expectMessage("error.duplicate.dosage.unit.code"); + + pfRep.insert(pf); + } + + @Test + public void shouldThrowExceptionIfUpdatingInvalid() { + ProductForm pf = new ProductForm(); + expectedException.expect(DataException.class); + pfRep.update(pf); + } +} From d1d8ca13c0ab3b5d651ae4c1d897f3adf1ac3302 Mon Sep 17 00:00:00 2001 From: joshzamor Date: Fri, 13 Jun 2014 16:35:42 -0700 Subject: [PATCH 12/80] OA-31, added product forms persistence handler and message properties (with spanish and portuguese google translations) for uploading product forms. --- .../upload/ProductFormPersistenceHandler.java | 43 +++++++++++++++++++ .../src/main/resources/applicationContext.xml | 9 ++++ .../src/main/resources/messages_en.properties | 2 + .../src/main/resources/messages_es.properties | 4 +- .../src/main/resources/messages_pt.properties | 2 + 5 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 modules/core/src/main/java/org/openlmis/core/upload/ProductFormPersistenceHandler.java diff --git a/modules/core/src/main/java/org/openlmis/core/upload/ProductFormPersistenceHandler.java b/modules/core/src/main/java/org/openlmis/core/upload/ProductFormPersistenceHandler.java new file mode 100644 index 0000000000..deb8d028a6 --- /dev/null +++ b/modules/core/src/main/java/org/openlmis/core/upload/ProductFormPersistenceHandler.java @@ -0,0 +1,43 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2013 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + *   + * 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 Affero General Public License for more details. + * You should have received a copy of the GNU Affero General Public License along with this program.  If not, see http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  + */ + +package org.openlmis.core.upload; + +import org.openlmis.core.domain.BaseModel; +import org.openlmis.core.domain.ProductForm; +import org.openlmis.core.service.ProductFormService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class ProductFormPersistenceHandler extends AbstractModelPersistenceHandler { + + private ProductFormService productFormService; + + @Autowired + public ProductFormPersistenceHandler(ProductFormService productFormService) { + this.productFormService = productFormService; + } + + @Override + protected BaseModel getExisting(BaseModel record) { + return productFormService.getExisting((ProductForm) record); + } + + @Override + protected void save(BaseModel modelClass) { + productFormService.save((ProductForm) modelClass); + } + + @Override + public String getMessageKey() { + return "error.duplicate.product.form.code"; + } +} diff --git a/modules/openlmis-web/src/main/resources/applicationContext.xml b/modules/openlmis-web/src/main/resources/applicationContext.xml index 3b7aeec92d..c7ff4fb0e8 100644 --- a/modules/openlmis-web/src/main/resources/applicationContext.xml +++ b/modules/openlmis-web/src/main/resources/applicationContext.xml @@ -288,6 +288,15 @@ + + + + + org.openlmis.core.domain.ProductForm + + + + diff --git a/modules/openlmis-web/src/main/resources/messages_en.properties b/modules/openlmis-web/src/main/resources/messages_en.properties index efece4886c..5d17aa2435 100644 --- a/modules/openlmis-web/src/main/resources/messages_en.properties +++ b/modules/openlmis-web/src/main/resources/messages_en.properties @@ -142,6 +142,7 @@ error.upload.invalid.header = "Invalid Headers in upload file: {0}" error.upload.missing.mandatory.columns = "Missing Mandatory columns in upload file: {0}" error.upload.header.missing = "Header for column ({0}) is missing." label.dosage.units = Dosage Units +label.product.forms = Product Forms error.duplicate.product.code.program.code = Duplicate entry for Product Code and Program Code combination found error.duplicate.facility.approved.product = Duplicate facility approved product. @@ -150,6 +151,7 @@ error.duplicate.delivery.zone = Duplicate delivery zone Code found error.duplicate.delivery.zone.program = Duplicate Delivery zone code and Program code combination found error.duplicate.delivery.zone.member = Duplicate Delivery Zone code and Member code combination found error.duplicate.dosage.unit.code = Duplicate Dosage Unit code found +error.duplicate.product.form.code = Duplicate Product Form code found error.duplicate.geographic.zone.code = Duplicate Geographic Zone Code error.duplicate.product.category = Duplicate Product Category error.duplicate.product.group.code = Duplicate Product Group Code diff --git a/modules/openlmis-web/src/main/resources/messages_es.properties b/modules/openlmis-web/src/main/resources/messages_es.properties index b7d4e3442b..231679c06c 100644 --- a/modules/openlmis-web/src/main/resources/messages_es.properties +++ b/modules/openlmis-web/src/main/resources/messages_es.properties @@ -1208,4 +1208,6 @@ header.min.months.of.stock = Min mon∆ƒths of stock header.global.active = Global act˙†∂ive header.active.at.program = Act˙∂ive at program label.dosage.units=Dosis Unidades -error.duplicate.dosage.unit.code=Código de unidad de dosificación es duplicado \ No newline at end of file +error.duplicate.dosage.unit.code=Código de unidad de dosificación es duplicado +error.duplicate.product.form.code=Duplicar el código de forma del producto foun +label.product.forms=Formas del producto \ No newline at end of file diff --git a/modules/openlmis-web/src/main/resources/messages_pt.properties b/modules/openlmis-web/src/main/resources/messages_pt.properties index b0fad6c354..b8ffcc4180 100644 --- a/modules/openlmis-web/src/main/resources/messages_pt.properties +++ b/modules/openlmis-web/src/main/resources/messages_pt.properties @@ -1209,3 +1209,5 @@ header.global.active = Global a©ctive header.active.at.program = Acti∆˙©ve at program label.dosage.units=Unidades de dosagem error.duplicate.dosage.unit.code=Código Duplicate Unidade Dosagem encontrado +error.duplicate.product.form.code=Duplicar código formar o produto fundações +label.product.forms=Formulários de produtos From f2178eebf59a4e0de5305397401f56d6aa777ddd Mon Sep 17 00:00:00 2001 From: joshzamor Date: Tue, 24 Jun 2014 18:06:14 -0700 Subject: [PATCH 13/80] OA-32, added importable to Program --- .../org/openlmis/core/domain/Program.java | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/modules/core/src/main/java/org/openlmis/core/domain/Program.java b/modules/core/src/main/java/org/openlmis/core/domain/Program.java index b6131867af..76afe4e6f0 100644 --- a/modules/core/src/main/java/org/openlmis/core/domain/Program.java +++ b/modules/core/src/main/java/org/openlmis/core/domain/Program.java @@ -15,6 +15,8 @@ import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.codehaus.jackson.map.annotate.JsonSerialize; +import org.openlmis.upload.Importable; +import org.openlmis.upload.annotation.ImportField; import static org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion.NON_EMPTY; @@ -26,15 +28,30 @@ @NoArgsConstructor @EqualsAndHashCode(callSuper = false) @JsonSerialize(include = NON_EMPTY) -public class Program extends BaseModel { +public class Program extends BaseModel implements Importable { + @ImportField(name="Program Code", mandatory=true) private String code; + + @ImportField(name="Name", mandatory=true) private String name; + + @ImportField(name="Description") private String description; + + @ImportField(name="Is Active", mandatory=true) private Boolean active; + + @ImportField(name="Does Budgeting Apply") private Boolean budgetingApplies; + + @ImportField(name="Is Template Configured") private boolean templateConfigured; + + @ImportField(name="Is Regimen Template Configured") private boolean regimenTemplateConfigured; + + @ImportField(name="Is Push", mandatory=false) private boolean push; public Program(Long id) { From 653e53ee0373fa40b195d49c04717f68a24cb11c Mon Sep 17 00:00:00 2001 From: joshzamor Date: Tue, 1 Jul 2014 11:21:18 -0700 Subject: [PATCH 14/80] OA-32, completed persistence handling of programs from UI down. --- .../org/openlmis/core/domain/Program.java | 12 +++--- .../core/repository/ProgramRepository.java | 8 ++++ .../core/repository/mapper/ProgramMapper.java | 33 +++++++++++--- .../openlmis/core/service/ProgramService.java | 5 +++ .../AbstractModelPersistenceHandler.java | 6 +++ .../upload/ProgramPersistenceHandler.java | 43 +++++++++++++++++++ .../repository/mapper/ProgramMapperIT.java | 41 ++++++++++++++---- .../src/main/resources/applicationContext.xml | 9 ++++ .../src/main/resources/messages_en.properties | 2 + .../src/main/resources/messages_es.properties | 4 +- .../src/main/resources/messages_pt.properties | 4 +- 11 files changed, 142 insertions(+), 25 deletions(-) create mode 100644 modules/core/src/main/java/org/openlmis/core/upload/ProgramPersistenceHandler.java diff --git a/modules/core/src/main/java/org/openlmis/core/domain/Program.java b/modules/core/src/main/java/org/openlmis/core/domain/Program.java index 76afe4e6f0..1973ba767b 100644 --- a/modules/core/src/main/java/org/openlmis/core/domain/Program.java +++ b/modules/core/src/main/java/org/openlmis/core/domain/Program.java @@ -39,19 +39,17 @@ public class Program extends BaseModel implements Importable { @ImportField(name="Description") private String description; - @ImportField(name="Is Active", mandatory=true) - private Boolean active; + @ImportField(name="Active", mandatory=true, type="boolean") + private Boolean active = true; - @ImportField(name="Does Budgeting Apply") - private Boolean budgetingApplies; + @ImportField(name="Budgeting Applies", type="boolean") + private Boolean budgetingApplies = false; - @ImportField(name="Is Template Configured") private boolean templateConfigured; - @ImportField(name="Is Regimen Template Configured") private boolean regimenTemplateConfigured; - @ImportField(name="Is Push", mandatory=false) + @ImportField(name="Push", mandatory=true, type="boolean") private boolean push; public Program(Long id) { diff --git a/modules/core/src/main/java/org/openlmis/core/repository/ProgramRepository.java b/modules/core/src/main/java/org/openlmis/core/repository/ProgramRepository.java index 1754d5f286..d084894e9e 100644 --- a/modules/core/src/main/java/org/openlmis/core/repository/ProgramRepository.java +++ b/modules/core/src/main/java/org/openlmis/core/repository/ProgramRepository.java @@ -40,6 +40,14 @@ public ProgramRepository(ProgramMapper programMapper) { this.mapper = programMapper; } + public void insert(Program p) { + mapper.insert(p); + } + + public void update(Program p) { + mapper.update(p); + } + public List getByFacility(Long facilityId) { return mapper.getByFacilityId(facilityId); } diff --git a/modules/core/src/main/java/org/openlmis/core/repository/mapper/ProgramMapper.java b/modules/core/src/main/java/org/openlmis/core/repository/mapper/ProgramMapper.java index f4526a88ed..aae5d5548e 100644 --- a/modules/core/src/main/java/org/openlmis/core/repository/mapper/ProgramMapper.java +++ b/modules/core/src/main/java/org/openlmis/core/repository/mapper/ProgramMapper.java @@ -23,11 +23,35 @@ @Repository public interface ProgramMapper { - @Insert({"INSERT INTO programs(code, name, description, active, push)", - "VALUES (#{code}, #{name}, #{description}, #{active}, #{push})"}) + @Insert({"INSERT INTO programs(code" + , ", name" + , ", description" + , ", active" + , ", budgetingApplies" + , ", templateConfigured" + , ", regimenTemplateConfigured, push)" + , " VALUES (#{code}" + , ", #{name}" + , ", #{description}" + , ", #{active}" + , ", #{budgetingApplies}" + , ", #{templateConfigured}" + , ", #{regimenTemplateConfigured}" + , ", #{push})"}) @Options(useGeneratedKeys = true) Integer insert(Program program); + @Update({"UPDATE programs SET name = #{name}" + , ", code = #{code}" + , ", description = #{description}" + , ", active = #{active}" + , ", budgetingApplies = #{budgetingApplies}" + , ", templateConfigured = #{templateConfigured}" + , ", regimenTemplateConfigured = #{regimenTemplateConfigured}" + , ", push = #{push}" + , " WHERE id = #{id}"}) + void update(Program p); + @Select({"SELECT P.*", "FROM programs P, programs_supported PS", "WHERE P.id = PS.programId AND", @@ -117,14 +141,9 @@ List getProgramsForUserByFacilityAndRights(@Param("facilityId") Long fa @Select("SELECT * FROM programs ORDER BY templateConfigured DESC, name") List getAll(); - - @Select("SELECT * FROM programs WHERE LOWER(code) = LOWER(#{code})") Program getByCode(String code); - @Select("SELECT * FROM programs ORDER BY regimenTemplateConfigured DESC, name") - List getAllByRegimenTemplate(); - @Update("UPDATE programs SET regimenTemplateConfigured = true WHERE id = #{id}") void setRegimenTemplateConfigured(Long id); diff --git a/modules/core/src/main/java/org/openlmis/core/service/ProgramService.java b/modules/core/src/main/java/org/openlmis/core/service/ProgramService.java index afc4fdf803..64d93472e1 100644 --- a/modules/core/src/main/java/org/openlmis/core/service/ProgramService.java +++ b/modules/core/src/main/java/org/openlmis/core/service/ProgramService.java @@ -98,6 +98,11 @@ public void setFeedSendFlag(Program program, Boolean sendFeed) { programRepository.setFeedSendFlag(program, sendFeed); } + public void save(Program program) { + if(program.hasId()) programRepository.update(program); + else programRepository.insert(program); + } + public void notifyProgramChange() { List programsForNotifications = programRepository.getProgramsForNotification(); for (Program program : programsForNotifications) { diff --git a/modules/core/src/main/java/org/openlmis/core/upload/AbstractModelPersistenceHandler.java b/modules/core/src/main/java/org/openlmis/core/upload/AbstractModelPersistenceHandler.java index 4909ffa5c3..1360e3b25f 100644 --- a/modules/core/src/main/java/org/openlmis/core/upload/AbstractModelPersistenceHandler.java +++ b/modules/core/src/main/java/org/openlmis/core/upload/AbstractModelPersistenceHandler.java @@ -34,6 +34,12 @@ public abstract class AbstractModelPersistenceHandler implements RecordHandler programs = programMapper.getAllByRegimenTemplate(); - assertThat(programs.size(), is(6)); - assertThat(programs.get(0).getCode(), is(ProgramBuilder.PROGRAM_CODE)); - } - @Test public void shouldSetRegimenTemplateConfigured() { Program program = insertProgram(make(a(defaultProgram, with(programCode, "p1"), with(regimenTemplateConfigured, false)))); diff --git a/modules/openlmis-web/src/main/resources/applicationContext.xml b/modules/openlmis-web/src/main/resources/applicationContext.xml index c7ff4fb0e8..f2db486094 100644 --- a/modules/openlmis-web/src/main/resources/applicationContext.xml +++ b/modules/openlmis-web/src/main/resources/applicationContext.xml @@ -297,6 +297,15 @@ + + + + + org.openlmis.core.domain.Program + + + + diff --git a/modules/openlmis-web/src/main/resources/messages_en.properties b/modules/openlmis-web/src/main/resources/messages_en.properties index 53ea11d228..cbc7436a9d 100644 --- a/modules/openlmis-web/src/main/resources/messages_en.properties +++ b/modules/openlmis-web/src/main/resources/messages_en.properties @@ -156,6 +156,7 @@ error.duplicate.geographic.zone.code = Duplicate Geographic Zone Code error.duplicate.product.category = Duplicate Product Category error.duplicate.product.group.code = Duplicate Product Group Code error.duplicate.product.code = Duplicate Product Code +error.duplicate.program.code = Duplicate Program Code error.duplicate.program.product.combination = Duplicate entry for Product Code and Program Code combination found error.duplicate.program.product = Duplicate Program Product error.duplicate.program.supported = Facility has already been mapped to the program @@ -786,6 +787,7 @@ label.supply.lines = Supply Lines label.facility.approved.products = Facility Approved Products label.product.prices.program = Product Prices per Program label.product.category = Product Category +label.programs = Programs label.geographic.zones = Geographic Zones label.product.groups = Product Groups label.delivery.zones = Delivery Zones diff --git a/modules/openlmis-web/src/main/resources/messages_es.properties b/modules/openlmis-web/src/main/resources/messages_es.properties index d73f1faeb6..c24d9b6845 100644 --- a/modules/openlmis-web/src/main/resources/messages_es.properties +++ b/modules/openlmis-web/src/main/resources/messages_es.properties @@ -1216,4 +1216,6 @@ label.clear.search = Clƒ©ç√˙ ©¨∆rch msg.enter.product.code.name = Enter prod∆©˚uct code or name label.program.and.schedules = Progr˙¬ƒ∂ß˚am and Sched©ƒ≈ules facilities.added.successfully = Facilitie skdfjjsi v,fm -add.selected.facilities = Add ©∆˚˙…¬˚∆ææææµ ƒå \ No newline at end of file +add.selected.facilities = Add ©∆˚˙…¬˚∆ææææµ ƒå +error.duplicate.program.code=Duplicar Código del Programa +label.programs=Programas \ No newline at end of file diff --git a/modules/openlmis-web/src/main/resources/messages_pt.properties b/modules/openlmis-web/src/main/resources/messages_pt.properties index 93e1dd9a27..230c8340c6 100644 --- a/modules/openlmis-web/src/main/resources/messages_pt.properties +++ b/modules/openlmis-web/src/main/resources/messages_pt.properties @@ -1216,4 +1216,6 @@ label.clear.search = Cleƒ©∆˙ ∆˙˚ø˚rch msg.enter.product.code.name = Enter prod∆©∆˙uct code or name label.program.and.schedules = Program an©∂∂߬¬d Sche∆˙ƒdules facilities.added.successfully = Facilities Ad©˚∆˙¬∆…… fully -add.selected.facilities = Add S´´†´∂ fa爬ˆ†ˆ´ \ No newline at end of file +add.selected.facilities = Add S´´†´∂ fa爬ˆ†ˆ´ +error.duplicate.program.code=Duplicar código do programa +label.programs=Programas \ No newline at end of file From 858e2b95f3123d0a9725b0444c8ac237783edfa1 Mon Sep 17 00:00:00 2001 From: joshzamor Date: Wed, 2 Jul 2014 17:14:11 -0700 Subject: [PATCH 15/80] OA-33, added insert and update methods and IT for geographic levels. Also modified existing integration tests to not be dependant on the database seed. --- .../mapper/GeographicLevelMapper.java | 29 ++++++++++ .../mapper/GeographicLevelMapperIT.java | 58 +++++++++++++++++-- ...created_and_modified_geographic_levels.sql | 13 +++++ 3 files changed, 94 insertions(+), 6 deletions(-) create mode 100644 modules/db/src/main/resources/db/migration/V133__add_created_and_modified_geographic_levels.sql diff --git a/modules/core/src/main/java/org/openlmis/core/repository/mapper/GeographicLevelMapper.java b/modules/core/src/main/java/org/openlmis/core/repository/mapper/GeographicLevelMapper.java index 9633c9518b..f97b5f644b 100644 --- a/modules/core/src/main/java/org/openlmis/core/repository/mapper/GeographicLevelMapper.java +++ b/modules/core/src/main/java/org/openlmis/core/repository/mapper/GeographicLevelMapper.java @@ -10,7 +10,9 @@ package org.openlmis.core.repository.mapper; +import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.Update; import org.openlmis.core.domain.GeographicLevel; import org.springframework.stereotype.Repository; @@ -28,4 +30,31 @@ public interface GeographicLevelMapper { @Select("SELECT * FROM geographic_levels ORDER BY levelNumber") List getAll(); + + @Select("SELECT * FROM geographic_levels WHERE LOWER(code) = LOWER(#{code})") + GeographicLevel getByCode(String code); + + @Insert({"INSERT INTO geographic_levels (code" + , ", name" + , ", levelNumber" + , ", createdBy" + , ", createdDate" + , ", modifiedBy" + , ", modifiedDate)" + , "VALUES (#{code}" + , ", #{name}" + , ", #{levelNumber}" + , ", #{createdBy}" + , ", NOW()" + , ", #{modifiedBy}" + , ", NOW())"}) + void insert(GeographicLevel geographicLevel); + + @Update({"UPDATE geographic_levels SET code = #{code}" + , ", name = #{name}" + , ", levelNumber = #{levelNumber}" + , ", modifiedBy = #{modifiedBy}" + , ", modifiedDate = #{modifiedDate}" + , "WHERE LOWER(code) = LOWER(#{code})"}) + void update(GeographicLevel geographicLevel); } diff --git a/modules/core/src/test/java/org/openlmis/core/repository/mapper/GeographicLevelMapperIT.java b/modules/core/src/test/java/org/openlmis/core/repository/mapper/GeographicLevelMapperIT.java index f0faae2540..281536ec5c 100644 --- a/modules/core/src/test/java/org/openlmis/core/repository/mapper/GeographicLevelMapperIT.java +++ b/modules/core/src/test/java/org/openlmis/core/repository/mapper/GeographicLevelMapperIT.java @@ -10,6 +10,7 @@ package org.openlmis.core.repository.mapper; +import org.junit.Before; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; @@ -21,9 +22,13 @@ import org.springframework.test.context.transaction.TransactionConfiguration; import org.springframework.transaction.annotation.Transactional; +import java.util.Collections; +import java.util.Comparator; import java.util.List; import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.CoreMatchers.notNullValue; import static org.junit.Assert.assertThat; @Category(IntegrationTests.class) @@ -36,18 +41,59 @@ public class GeographicLevelMapperIT { @Autowired private GeographicLevelMapper mapper; + private GeographicLevel provGeoLevel; + + @Before + public void init() { + provGeoLevel = new GeographicLevel(); + provGeoLevel.setCode("myprov"); + provGeoLevel.setName("Province"); + provGeoLevel.setLevelNumber(2); + mapper.insert(provGeoLevel); + } + @Test public void shouldGetLowestGeographicLevel() { - assertThat(mapper.getLowestGeographicLevel(), is(4)); + // get a list of all the geo levels and the lowest level that should be present + List allGeoLevels = mapper.getAll(); + int lowestLevel = mapper.getLowestGeographicLevel(); + + // sort all geo levels by their level number + Collections.sort(allGeoLevels, new Comparator() { + public int compare(GeographicLevel o1, GeographicLevel o2) { + return Integer.compare(o1.getLevelNumber(), o2.getLevelNumber()); + } + }); + GeographicLevel lowestGeoLevel = allGeoLevels.get(allGeoLevels.size() - 1); + + assertThat(lowestLevel, is(lowestGeoLevel.getLevelNumber())); } @Test public void shouldReturnAllTheGeoLevels() { List levels = mapper.getAll(); - assertThat(levels.size(), is(4)); - assertThat(levels.get(0).getLevelNumber(), is(1)); - assertThat(levels.get(1).getLevelNumber(), is(2)); - assertThat(levels.get(2).getLevelNumber(), is(3)); - assertThat(levels.get(3).getLevelNumber(), is(4)); + assertThat(levels.size(), not(0)); + } + + @Test + public void shouldRetrieveGeographicLevelByCode() { + String code = provGeoLevel.getCode(); + GeographicLevel geoRet = mapper.getByCode(code); + + assertThat(geoRet, notNullValue()); + assertThat(geoRet, is(provGeoLevel)); + } + + @Test + public void shouldUpdateGeographicLevel() { + GeographicLevel geoRet = mapper.getByCode(provGeoLevel.getCode()); + geoRet.setName("Some new name"); + geoRet.setLevelNumber(3); + + mapper.update(geoRet); + + GeographicLevel geoUpdated = mapper.getByCode(provGeoLevel.getCode()); + assertThat(geoUpdated, notNullValue()); + assertThat(geoUpdated, is(geoRet)); } } diff --git a/modules/db/src/main/resources/db/migration/V133__add_created_and_modified_geographic_levels.sql b/modules/db/src/main/resources/db/migration/V133__add_created_and_modified_geographic_levels.sql new file mode 100644 index 0000000000..32b321b5c8 --- /dev/null +++ b/modules/db/src/main/resources/db/migration/V133__add_created_and_modified_geographic_levels.sql @@ -0,0 +1,13 @@ +-- +-- This program is part of the OpenLMIS logistics management information system platform software. +-- Copyright © 2013 VillageReach +-- +-- This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +--   +-- 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 Affero General Public License for more details. +-- You should have received a copy of the GNU Affero General Public License along with this program.  If not, see http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  +-- + +ALTER TABLE geographic_levels ADD COLUMN createdBy INTEGER; +ALTER TABLE geographic_levels ADD COLUMN modifiedBy INTEGER; +ALTER TABLE geographic_levels ADD COLUMN modifiedDate TIMESTAMP WITH TIME ZONE DEFAULT NOW(); From b3e9cdd47074a62a26bd48c9f98eb319df85e94d Mon Sep 17 00:00:00 2001 From: joshzamor Date: Thu, 3 Jul 2014 14:17:59 -0700 Subject: [PATCH 16/80] OA-33, added geographic level repository, persistence handler, associated tests and messages. --- .../openlmis/core/domain/GeographicLevel.java | 11 +++- .../repository/GeographicLevelRepository.java | 62 +++++++++++++++++++ .../GeographicLevelPersistenceHandler.java | 43 +++++++++++++ .../GeographicLevelRepositoryTest.java | 57 +++++++++++++++++ .../src/main/resources/applicationContext.xml | 9 +++ .../src/main/resources/messages_en.properties | 2 + .../src/main/resources/messages_es.properties | 4 +- .../src/main/resources/messages_pt.properties | 4 +- 8 files changed, 189 insertions(+), 3 deletions(-) create mode 100644 modules/core/src/main/java/org/openlmis/core/repository/GeographicLevelRepository.java create mode 100644 modules/core/src/main/java/org/openlmis/core/upload/GeographicLevelPersistenceHandler.java create mode 100644 modules/core/src/test/java/org/openlmis/core/repository/GeographicLevelRepositoryTest.java diff --git a/modules/core/src/main/java/org/openlmis/core/domain/GeographicLevel.java b/modules/core/src/main/java/org/openlmis/core/domain/GeographicLevel.java index 25e9283ddb..a13d4465b1 100644 --- a/modules/core/src/main/java/org/openlmis/core/domain/GeographicLevel.java +++ b/modules/core/src/main/java/org/openlmis/core/domain/GeographicLevel.java @@ -16,6 +16,8 @@ import lombok.NoArgsConstructor; import org.codehaus.jackson.annotate.JsonIgnore; import org.codehaus.jackson.map.annotate.JsonSerialize; +import org.openlmis.upload.Importable; +import org.openlmis.upload.annotation.ImportField; import static org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion.NON_EMPTY; @@ -28,10 +30,17 @@ @NoArgsConstructor @EqualsAndHashCode(callSuper = false) @JsonSerialize(include = NON_EMPTY) -public class GeographicLevel extends BaseModel { +public class GeographicLevel extends BaseModel implements Importable { + + @ImportField(name="Geographic Level Code", mandatory = true) String code; + + @ImportField(name="Name", mandatory = true) String name; + + @ImportField(name="Level Number", mandatory = true) Integer levelNumber; + private static Integer ROOT_LEVEL_NUMBER = 1; public GeographicLevel(Long id, String code, String name, Integer levelNumber) { diff --git a/modules/core/src/main/java/org/openlmis/core/repository/GeographicLevelRepository.java b/modules/core/src/main/java/org/openlmis/core/repository/GeographicLevelRepository.java new file mode 100644 index 0000000000..8de461e97e --- /dev/null +++ b/modules/core/src/main/java/org/openlmis/core/repository/GeographicLevelRepository.java @@ -0,0 +1,62 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2013 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + *   + * 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 Affero General Public License for more details. + * You should have received a copy of the GNU Affero General Public License along with this program.  If not, see http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  + */ + +package org.openlmis.core.repository; + +import lombok.NoArgsConstructor; +import org.openlmis.core.domain.GeographicLevel; +import org.openlmis.core.exception.DataException; +import org.openlmis.core.repository.mapper.GeographicLevelMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.stereotype.Repository; + +import java.util.List; + +/** + * Repository for managing persistent storage/retrieval of {@link org.openlmis.core.domain.GeographicLevel} entities. + */ +@Repository +@NoArgsConstructor +public class GeographicLevelRepository { + private GeographicLevelMapper mapper; + + @Autowired + public GeographicLevelRepository(GeographicLevelMapper mapper) { + this.mapper = mapper; + } + + public int getLowestGeographicLevel() {return mapper.getLowestGeographicLevel();} + + public List getAll() {return mapper.getAll();} + + public GeographicLevel getByCode(String code) {return mapper.getByCode(code);} + + /** + * Saves the given {@link org.openlmis.core.domain.GeographicLevel}. If the GeographicLevel already exists, + * then it will attempt to update it. + * @param geographicLevel the geographic level to save. + * @throws DataException if unable - malformed or attempting to save a new geographic level with a duplicate identity. + * i.e. {@link #getByCode(String)} is not null. + */ + public void save(GeographicLevel geographicLevel) { + try { + if(geographicLevel.hasId()) + mapper.update(geographicLevel); + else + mapper.insert(geographicLevel); + } catch(DuplicateKeyException dke) { + throw new DataException("error.duplicate.geographic.level.code"); + } catch(DataIntegrityViolationException dive) { + throw new DataException("error.incorrect.length"); + } + } +} diff --git a/modules/core/src/main/java/org/openlmis/core/upload/GeographicLevelPersistenceHandler.java b/modules/core/src/main/java/org/openlmis/core/upload/GeographicLevelPersistenceHandler.java new file mode 100644 index 0000000000..b0f6751d0f --- /dev/null +++ b/modules/core/src/main/java/org/openlmis/core/upload/GeographicLevelPersistenceHandler.java @@ -0,0 +1,43 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2013 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + *   + * 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 Affero General Public License for more details. + * You should have received a copy of the GNU Affero General Public License along with this program.  If not, see http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  + */ + +package org.openlmis.core.upload; + +import org.openlmis.core.domain.BaseModel; +import org.openlmis.core.domain.GeographicLevel; +import org.openlmis.core.repository.GeographicLevelRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class GeographicLevelPersistenceHandler extends AbstractModelPersistenceHandler { + private GeographicLevelRepository repository; + + @Autowired + public GeographicLevelPersistenceHandler(GeographicLevelRepository repository) { + this.repository = repository; + } + + @Override + protected BaseModel getExisting(BaseModel record) { + GeographicLevel geoLevel = (GeographicLevel) record; + return repository.getByCode(geoLevel.getCode()); + } + + @Override + protected void save(BaseModel record) { + repository.save((GeographicLevel) record); + } + + @Override + public String getMessageKey() { + return "error.duplicate.geographic.level.code"; + } +} diff --git a/modules/core/src/test/java/org/openlmis/core/repository/GeographicLevelRepositoryTest.java b/modules/core/src/test/java/org/openlmis/core/repository/GeographicLevelRepositoryTest.java new file mode 100644 index 0000000000..dbdfc895b6 --- /dev/null +++ b/modules/core/src/test/java/org/openlmis/core/repository/GeographicLevelRepositoryTest.java @@ -0,0 +1,57 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2013 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + *   + * 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 Affero General Public License for more details. + * You should have received a copy of the GNU Affero General Public License along with this program.  If not, see http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  + */ + +package org.openlmis.core.repository; + +import org.junit.Before; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.openlmis.core.domain.GeographicLevel; +import org.openlmis.core.repository.mapper.GeographicLevelMapper; +import org.openlmis.db.categories.UnitTests; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@Category(UnitTests.class) +@RunWith(MockitoJUnitRunner.class) +public class GeographicLevelRepositoryTest { + + @Mock + private GeographicLevelMapper mapper; + + private GeographicLevelRepository repo; + + @Before + public void setup() { + repo = new GeographicLevelRepository(mapper); + } + + @Test + public void shouldUpdateOnSave() { + GeographicLevel geoLevel = new GeographicLevel(); + geoLevel.setId(123L); + + repo.save(geoLevel); + verify(mapper).update(geoLevel); + } + + @Test + public void shouldInsertOnSave() { + GeographicLevel geoLevel = new GeographicLevel(); + geoLevel.setCode("abc"); + + repo.save(geoLevel); + verify(mapper).insert(geoLevel); + } +} diff --git a/modules/openlmis-web/src/main/resources/applicationContext.xml b/modules/openlmis-web/src/main/resources/applicationContext.xml index f2db486094..c33575c015 100644 --- a/modules/openlmis-web/src/main/resources/applicationContext.xml +++ b/modules/openlmis-web/src/main/resources/applicationContext.xml @@ -306,6 +306,15 @@ + + + + + org.openlmis.core.domain.GeographicLevel + + + + diff --git a/modules/openlmis-web/src/main/resources/messages_en.properties b/modules/openlmis-web/src/main/resources/messages_en.properties index cbc7436a9d..ef1eca91aa 100644 --- a/modules/openlmis-web/src/main/resources/messages_en.properties +++ b/modules/openlmis-web/src/main/resources/messages_en.properties @@ -153,6 +153,7 @@ error.duplicate.delivery.zone.member = Duplicate Delivery Zone code and Member c error.duplicate.dosage.unit.code = Duplicate Dosage Unit code found error.duplicate.product.form.code = Duplicate Product Form code found error.duplicate.geographic.zone.code = Duplicate Geographic Zone Code +error.duplicate.geographic.level.code = Duplicate Geographic Level Code error.duplicate.product.category = Duplicate Product Category error.duplicate.product.group.code = Duplicate Product Group Code error.duplicate.product.code = Duplicate Product Code @@ -788,6 +789,7 @@ label.facility.approved.products = Facility Approved Products label.product.prices.program = Product Prices per Program label.product.category = Product Category label.programs = Programs +label.geographic.levels = Geographic Levels label.geographic.zones = Geographic Zones label.product.groups = Product Groups label.delivery.zones = Delivery Zones diff --git a/modules/openlmis-web/src/main/resources/messages_es.properties b/modules/openlmis-web/src/main/resources/messages_es.properties index c24d9b6845..6966bc8eb7 100644 --- a/modules/openlmis-web/src/main/resources/messages_es.properties +++ b/modules/openlmis-web/src/main/resources/messages_es.properties @@ -1218,4 +1218,6 @@ label.program.and.schedules = Progr˙¬ƒ∂ß˚am and Sched©ƒ≈ules facilities.added.successfully = Facilitie skdfjjsi v,fm add.selected.facilities = Add ©∆˚˙…¬˚∆ææææµ ƒå error.duplicate.program.code=Duplicar Código del Programa -label.programs=Programas \ No newline at end of file +label.programs=Programas +label.geographic.levels=Niveles Geográficos +error.duplicate.geographic.level.code=Duplicate Code Nivel Geographic \ No newline at end of file diff --git a/modules/openlmis-web/src/main/resources/messages_pt.properties b/modules/openlmis-web/src/main/resources/messages_pt.properties index 230c8340c6..422c983410 100644 --- a/modules/openlmis-web/src/main/resources/messages_pt.properties +++ b/modules/openlmis-web/src/main/resources/messages_pt.properties @@ -1218,4 +1218,6 @@ label.program.and.schedules = Program an©∂∂߬¬d Sche∆˙ƒdules facilities.added.successfully = Facilities Ad©˚∆˙¬∆…… fully add.selected.facilities = Add S´´†´∂ fa爬ˆ†ˆ´ error.duplicate.program.code=Duplicar código do programa -label.programs=Programas \ No newline at end of file +label.programs=Programas +label.geographic.levels=Níveis Geográficos +error.duplicate.geographic.level.code=Duplicate Código Geográfico Nível \ No newline at end of file From 12db2634aa31bd446aa80f618c90403c2ffa647c Mon Sep 17 00:00:00 2001 From: joshzamor Date: Thu, 3 Jul 2014 14:56:15 -0700 Subject: [PATCH 17/80] OA-33, refactored geo level operations out of geo zone repo into geo level repo. --- .../repository/GeographicLevelRepository.java | 2 ++ .../repository/GeographicZoneRepository.java | 15 +++++--------- .../mapper/GeographicLevelMapper.java | 3 +++ .../mapper/GeographicZoneMapper.java | 3 --- .../core/service/GeographicZoneService.java | 6 +++++- .../GeographicLevelRepositoryTest.java | 1 - .../GeographicZoneRepositoryTest.java | 18 +++-------------- .../mapper/GeographicLevelMapperIT.java | 7 +++++++ .../mapper/GeographicZoneMapperIT.java | 8 -------- .../service/GeographicZoneServiceTest.java | 20 +++++++++++-------- 10 files changed, 37 insertions(+), 46 deletions(-) diff --git a/modules/core/src/main/java/org/openlmis/core/repository/GeographicLevelRepository.java b/modules/core/src/main/java/org/openlmis/core/repository/GeographicLevelRepository.java index 8de461e97e..98f5c75a50 100644 --- a/modules/core/src/main/java/org/openlmis/core/repository/GeographicLevelRepository.java +++ b/modules/core/src/main/java/org/openlmis/core/repository/GeographicLevelRepository.java @@ -36,6 +36,8 @@ public GeographicLevelRepository(GeographicLevelMapper mapper) { public int getLowestGeographicLevel() {return mapper.getLowestGeographicLevel();} + public GeographicLevel getGeographicLevelByCode(String code) {return mapper.getGeographicLevelByCode(code);} + public List getAll() {return mapper.getAll();} public GeographicLevel getByCode(String code) {return mapper.getByCode(code);} diff --git a/modules/core/src/main/java/org/openlmis/core/repository/GeographicZoneRepository.java b/modules/core/src/main/java/org/openlmis/core/repository/GeographicZoneRepository.java index e464e99936..800aa58c06 100644 --- a/modules/core/src/main/java/org/openlmis/core/repository/GeographicZoneRepository.java +++ b/modules/core/src/main/java/org/openlmis/core/repository/GeographicZoneRepository.java @@ -15,7 +15,6 @@ import org.openlmis.core.domain.GeographicZone; import org.openlmis.core.domain.Pagination; import org.openlmis.core.exception.DataException; -import org.openlmis.core.repository.mapper.GeographicLevelMapper; import org.openlmis.core.repository.mapper.GeographicZoneMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataIntegrityViolationException; @@ -33,12 +32,12 @@ public class GeographicZoneRepository { private GeographicZoneMapper mapper; - private GeographicLevelMapper geographicLevelMapper; + private GeographicLevelRepository geographicLevelRepository; @Autowired - public GeographicZoneRepository(GeographicZoneMapper mapper, GeographicLevelMapper geographicLevelMapper) { + public GeographicZoneRepository(GeographicZoneMapper mapper, GeographicLevelRepository geographicLevelRepository) { this.mapper = mapper; - this.geographicLevelMapper = geographicLevelMapper; + this.geographicLevelRepository = geographicLevelRepository; } public GeographicZone getByCode(String code) { @@ -46,7 +45,7 @@ public GeographicZone getByCode(String code) { } public Integer getLowestGeographicLevel() { - return geographicLevelMapper.getLowestGeographicLevel(); + return geographicLevelRepository.getLowestGeographicLevel(); } public List getAllGeographicZones() { @@ -67,10 +66,6 @@ public void save(GeographicZone zone) { } } - public GeographicLevel getGeographicLevelByCode(String code) { - return mapper.getGeographicLevelByCode(code); - } - public GeographicZone getById(Long id) { return mapper.getWithParentById(id); } @@ -84,7 +79,7 @@ public List searchByName(String searchParam, Pagination paginati } public List getAllGeographicLevels() { - return geographicLevelMapper.getAll(); + return geographicLevelRepository.getAll(); } public List getAllGeographicZonesAbove(GeographicLevel geographicLevel) { diff --git a/modules/core/src/main/java/org/openlmis/core/repository/mapper/GeographicLevelMapper.java b/modules/core/src/main/java/org/openlmis/core/repository/mapper/GeographicLevelMapper.java index f97b5f644b..254871cba1 100644 --- a/modules/core/src/main/java/org/openlmis/core/repository/mapper/GeographicLevelMapper.java +++ b/modules/core/src/main/java/org/openlmis/core/repository/mapper/GeographicLevelMapper.java @@ -34,6 +34,9 @@ public interface GeographicLevelMapper { @Select("SELECT * FROM geographic_levels WHERE LOWER(code) = LOWER(#{code})") GeographicLevel getByCode(String code); + @Select("SELECT * FROM geographic_levels WHERE LOWER(code) = LOWER(#{code})") + GeographicLevel getGeographicLevelByCode(String code); + @Insert({"INSERT INTO geographic_levels (code" , ", name" , ", levelNumber" diff --git a/modules/core/src/main/java/org/openlmis/core/repository/mapper/GeographicZoneMapper.java b/modules/core/src/main/java/org/openlmis/core/repository/mapper/GeographicZoneMapper.java index 15c93fb90c..b053901f0c 100644 --- a/modules/core/src/main/java/org/openlmis/core/repository/mapper/GeographicZoneMapper.java +++ b/modules/core/src/main/java/org/openlmis/core/repository/mapper/GeographicZoneMapper.java @@ -35,9 +35,6 @@ public interface GeographicZoneMapper { @Options(useGeneratedKeys = true) Integer insert(GeographicZone geographicZone); - @Select("SELECT * FROM geographic_levels WHERE LOWER(code) = LOWER(#{code})") - GeographicLevel getGeographicLevelByCode(String code); - @Select({"SELECT GZ.id, GZ.code, GZ.name, GL.id AS levelId, GL.code AS levelCode, GL.name AS levelName, GL.levelNumber AS levelNumber ", "FROM geographic_zones GZ, geographic_levels GL ", "WHERE GZ.levelId = GL.id AND LOWER(GZ.code) <> 'root' AND ", diff --git a/modules/core/src/main/java/org/openlmis/core/service/GeographicZoneService.java b/modules/core/src/main/java/org/openlmis/core/service/GeographicZoneService.java index fedb7e8d31..11804ccfe9 100644 --- a/modules/core/src/main/java/org/openlmis/core/service/GeographicZoneService.java +++ b/modules/core/src/main/java/org/openlmis/core/service/GeographicZoneService.java @@ -14,6 +14,7 @@ import org.openlmis.core.domain.GeographicLevel; import org.openlmis.core.domain.GeographicZone; import org.openlmis.core.domain.Pagination; +import org.openlmis.core.repository.GeographicLevelRepository; import org.openlmis.core.repository.GeographicZoneRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -36,6 +37,9 @@ public class GeographicZoneService { @Autowired GeographicZoneRepository repository; + @Autowired + GeographicLevelRepository geoLevelRepo; + @Autowired public void setPageSize(@Value("${search.page.size}") String pageSize) { this.pageSize = Integer.parseInt(pageSize); @@ -43,7 +47,7 @@ public void setPageSize(@Value("${search.page.size}") String pageSize) { public void save(GeographicZone geographicZone) { geographicZone.validateMandatoryFields(); - geographicZone.setLevel(repository.getGeographicLevelByCode(geographicZone.getLevel().getCode())); + geographicZone.setLevel(geoLevelRepo.getGeographicLevelByCode(geographicZone.getLevel().getCode())); geographicZone.validateLevel(); if (!geographicZone.isRootLevel()) { diff --git a/modules/core/src/test/java/org/openlmis/core/repository/GeographicLevelRepositoryTest.java b/modules/core/src/test/java/org/openlmis/core/repository/GeographicLevelRepositoryTest.java index dbdfc895b6..538f55e270 100644 --- a/modules/core/src/test/java/org/openlmis/core/repository/GeographicLevelRepositoryTest.java +++ b/modules/core/src/test/java/org/openlmis/core/repository/GeographicLevelRepositoryTest.java @@ -21,7 +21,6 @@ import org.openlmis.db.categories.UnitTests; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; @Category(UnitTests.class) @RunWith(MockitoJUnitRunner.class) diff --git a/modules/core/src/test/java/org/openlmis/core/repository/GeographicZoneRepositoryTest.java b/modules/core/src/test/java/org/openlmis/core/repository/GeographicZoneRepositoryTest.java index 58bf8bfe27..881cf53530 100644 --- a/modules/core/src/test/java/org/openlmis/core/repository/GeographicZoneRepositoryTest.java +++ b/modules/core/src/test/java/org/openlmis/core/repository/GeographicZoneRepositoryTest.java @@ -20,7 +20,6 @@ import org.mockito.runners.MockitoJUnitRunner; import org.openlmis.core.domain.GeographicLevel; import org.openlmis.core.domain.GeographicZone; -import org.openlmis.core.repository.mapper.GeographicLevelMapper; import org.openlmis.core.repository.mapper.GeographicZoneMapper; import org.openlmis.db.categories.UnitTests; import org.springframework.dao.DataIntegrityViolationException; @@ -46,13 +45,13 @@ public class GeographicZoneRepositoryTest { private GeographicZoneMapper mapper; @Mock - private GeographicLevelMapper geographicLevelMapper; + private GeographicLevelRepository geographicLevelRepository; private GeographicZone geographicZone; @Before public void setUp() throws Exception { - repository = new GeographicZoneRepository(mapper, geographicLevelMapper); + repository = new GeographicZoneRepository(mapper, geographicLevelRepository); geographicZone = new GeographicZone(); geographicZone.setCode("some code"); geographicZone.setModifiedDate(new Date()); @@ -102,21 +101,10 @@ public void shouldGetZoneByCode() throws Exception { @Test public void shouldGetLowestGeographicLevel() { - when(geographicLevelMapper.getLowestGeographicLevel()).thenReturn(1); + when(geographicLevelRepository.getLowestGeographicLevel()).thenReturn(1); assertThat(repository.getLowestGeographicLevel(), is(1)); } - @Test - public void shouldGetLevelByCode() throws Exception { - GeographicLevel level = new GeographicLevel(); - when(mapper.getGeographicLevelByCode("code")).thenReturn(level); - - GeographicLevel actualLevel = repository.getGeographicLevelByCode("code"); - - assertThat(actualLevel, is(level)); - verify(mapper).getGeographicLevelByCode("code"); - } - @Test public void shouldGetGeographicZoneById() { GeographicZone expectedGeographicZone = new GeographicZone(); diff --git a/modules/core/src/test/java/org/openlmis/core/repository/mapper/GeographicLevelMapperIT.java b/modules/core/src/test/java/org/openlmis/core/repository/mapper/GeographicLevelMapperIT.java index 281536ec5c..8b5fc53289 100644 --- a/modules/core/src/test/java/org/openlmis/core/repository/mapper/GeographicLevelMapperIT.java +++ b/modules/core/src/test/java/org/openlmis/core/repository/mapper/GeographicLevelMapperIT.java @@ -69,6 +69,13 @@ public int compare(GeographicLevel o1, GeographicLevel o2) { assertThat(lowestLevel, is(lowestGeoLevel.getLevelNumber())); } + @Test + public void shouldGetGeographicLevelByCode() throws Exception { + String code = provGeoLevel.getCode(); + GeographicLevel geographicLevel = mapper.getGeographicLevelByCode(code); + assertThat(geographicLevel, is(provGeoLevel)); + } + @Test public void shouldReturnAllTheGeoLevels() { List levels = mapper.getAll(); diff --git a/modules/core/src/test/java/org/openlmis/core/repository/mapper/GeographicZoneMapperIT.java b/modules/core/src/test/java/org/openlmis/core/repository/mapper/GeographicZoneMapperIT.java index 8ca6ce1d17..534170dac1 100644 --- a/modules/core/src/test/java/org/openlmis/core/repository/mapper/GeographicZoneMapperIT.java +++ b/modules/core/src/test/java/org/openlmis/core/repository/mapper/GeographicZoneMapperIT.java @@ -57,14 +57,6 @@ public void shouldSaveGeographicZone() throws Exception { assertThat(returnedZone, is(geographicZone)); } - @Test - public void shouldGetGeographicLevelByCode() throws Exception { - String code = "state"; - GeographicLevel geographicLevel = mapper.getGeographicLevelByCode(code); - assertThat(geographicLevel.getName(), is("State")); - assertThat(geographicLevel.getId(), is(2L)); - } - @Test public void shouldGetNullIfZoneNotPresent() throws Exception { GeographicZone nullZone = mapper.getGeographicZoneByCode("some random code"); diff --git a/modules/core/src/test/java/org/openlmis/core/service/GeographicZoneServiceTest.java b/modules/core/src/test/java/org/openlmis/core/service/GeographicZoneServiceTest.java index 0c2d543665..bfbc24fb0e 100644 --- a/modules/core/src/test/java/org/openlmis/core/service/GeographicZoneServiceTest.java +++ b/modules/core/src/test/java/org/openlmis/core/service/GeographicZoneServiceTest.java @@ -20,6 +20,7 @@ import org.openlmis.core.domain.GeographicLevel; import org.openlmis.core.domain.GeographicZone; import org.openlmis.core.domain.Pagination; +import org.openlmis.core.repository.GeographicLevelRepository; import org.openlmis.core.repository.GeographicZoneRepository; import org.openlmis.db.categories.UnitTests; import org.powermock.core.classloader.annotations.PrepareForTest; @@ -45,6 +46,9 @@ public class GeographicZoneServiceTest { @Mock GeographicZoneRepository repository; + @Mock + GeographicLevelRepository geoLevelRepo; + @InjectMocks GeographicZoneService service; @@ -60,12 +64,12 @@ public void shouldSaveGeographicZone() throws Exception { GeographicLevel childLevel = new GeographicLevel(2345L, "child level", "child level", 2); GeographicZone childZone = new GeographicZone(null, "child zone", "child zone", childLevel, rootZone); - when(repository.getGeographicLevelByCode(childZone.getLevel().getCode())).thenReturn(childLevel); + when(geoLevelRepo.getGeographicLevelByCode(childZone.getLevel().getCode())).thenReturn(childLevel); when(repository.getByCode(childZone.getParent().getCode())).thenReturn(rootZone); service.save(childZone); - verify(repository).getGeographicLevelByCode("child level"); + verify(geoLevelRepo).getGeographicLevelByCode("child level"); verify(repository).getByCode("root zone"); assertThat(childZone.getLevel().getId(), is(2345L)); assertThat(childZone.getParent().getId(), is(1234L)); @@ -79,7 +83,7 @@ public void shouldThrowAnExceptionIfParentCodeIsInvalid() throws Exception { GeographicLevel childLevel = new GeographicLevel(2345L, "child level", "child level", 2); GeographicZone childZone = new GeographicZone(null, "child zone", "child zone", childLevel, invalidZone); - when(repository.getGeographicLevelByCode(childZone.getLevel().getCode())).thenReturn(childLevel); + when(geoLevelRepo.getGeographicLevelByCode(childZone.getLevel().getCode())).thenReturn(childLevel); when(repository.getByCode(childZone.getParent().getCode())).thenReturn(null); expectedEx.expect(dataExceptionMatcher("error.geo.zone.parent.invalid")); @@ -119,7 +123,7 @@ public void shouldThrowExceptionIfLevelIsNotRootAndStillParentIsNull() throws Ex GeographicLevel level = new GeographicLevel(1L, "abc", "abc", 2); GeographicZone zone = new GeographicZone(1L, "xyz", "xyz", level, null); - when(repository.getGeographicLevelByCode(zone.getLevel().getCode())).thenReturn(level); + when(geoLevelRepo.getGeographicLevelByCode(zone.getLevel().getCode())).thenReturn(level); expectedEx.expect(dataExceptionMatcher("error.invalid.hierarchy")); @@ -133,7 +137,7 @@ public void shouldUpdateZoneIfZonePreviouslyPresent() throws Exception { GeographicLevel childLevel = new GeographicLevel(2345L, "child level", "child level", 2); GeographicZone childZone = new GeographicZone(2345L, "child zone", "child zone", childLevel, rootZone); - when(repository.getGeographicLevelByCode(childZone.getLevel().getCode())).thenReturn(childLevel); + when(geoLevelRepo.getGeographicLevelByCode(childZone.getLevel().getCode())).thenReturn(childLevel); when(repository.getByCode(childZone.getParent().getCode())).thenReturn(rootZone); childZone.setName("new name"); @@ -161,7 +165,7 @@ public void shouldThrowExceptionIfParentSpecifiedForTopMostLevelGeographicZone() GeographicZone parent = new GeographicZone(1L, "xyz", "xyz", null, null); GeographicZone country = new GeographicZone(1L, "xyz", "xyz", level, parent); - when(repository.getGeographicLevelByCode(country.getLevel().getCode())).thenReturn(level); + when(geoLevelRepo.getGeographicLevelByCode(country.getLevel().getCode())).thenReturn(level); when(repository.getByCode(country.getParent().getCode())).thenReturn(parent); expectedEx.expect(dataExceptionMatcher("error.invalid.hierarchy")); @@ -174,7 +178,7 @@ public void shouldThrowExceptionIfParentSetToItsSiblingInHierarchy() throws Exce GeographicZone country1 = new GeographicZone(1L, "xyz", "xyz", level, null); GeographicZone country2 = new GeographicZone(1L, "xyz", "xyz", level, country1); - when(repository.getGeographicLevelByCode(country2.getLevel().getCode())).thenReturn(level); + when(geoLevelRepo.getGeographicLevelByCode(country2.getLevel().getCode())).thenReturn(level); when(repository.getByCode(country2.getParent().getCode())).thenReturn(country1); expectedEx.expect(dataExceptionMatcher("error.invalid.hierarchy")); @@ -188,7 +192,7 @@ public void shouldThrowExceptionIfParentIsSetToALowerInHierarchyGeographicLevel( GeographicZone country1 = new GeographicZone(1L, "xyz", "xyz", lowerLevel, null); GeographicZone country2 = new GeographicZone(1L, "xyz", "xyz", higherLevel, country1); - when(repository.getGeographicLevelByCode(country2.getLevel().getCode())).thenReturn(higherLevel); + when(geoLevelRepo.getGeographicLevelByCode(country2.getLevel().getCode())).thenReturn(higherLevel); when(repository.getByCode(country2.getParent().getCode())).thenReturn(country1); expectedEx.expect(dataExceptionMatcher("error.invalid.hierarchy")); From af269f4d366395386a412f76fbdb59b8da204a21 Mon Sep 17 00:00:00 2001 From: joshzamor Date: Mon, 7 Jul 2014 09:57:37 -0700 Subject: [PATCH 18/80] OA-35, added importable fields to facility operator entity and updated the database to finnish fields present in the BaseModel (createdby, modifiedby, modifieddate) --- .../org/openlmis/core/domain/FacilityOperator.java | 11 +++++++++-- ..._add_created_and_modified_facility_operators.sql | 13 +++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 modules/db/src/main/resources/db/migration/V134__add_created_and_modified_facility_operators.sql diff --git a/modules/core/src/main/java/org/openlmis/core/domain/FacilityOperator.java b/modules/core/src/main/java/org/openlmis/core/domain/FacilityOperator.java index 2a6eb40c86..fbc799442f 100644 --- a/modules/core/src/main/java/org/openlmis/core/domain/FacilityOperator.java +++ b/modules/core/src/main/java/org/openlmis/core/domain/FacilityOperator.java @@ -12,14 +12,21 @@ import lombok.Data; import lombok.EqualsAndHashCode; +import org.openlmis.upload.Importable; +import org.openlmis.upload.annotation.ImportField; /** * FacilityOperator represents the operator for facility, the authority responsible for running a facility (for eg. WHO, MOH etc.) */ @Data @EqualsAndHashCode(callSuper = false) -public class FacilityOperator extends BaseModel { +public class FacilityOperator extends BaseModel implements Importable { + @ImportField(name="Facility Operator Code", mandatory=true) private String code; + + @ImportField(name="Name", mandatory=true) private String text; + + @ImportField(name="Display Order", mandatory=true) private Integer displayOrder; -} +} \ No newline at end of file diff --git a/modules/db/src/main/resources/db/migration/V134__add_created_and_modified_facility_operators.sql b/modules/db/src/main/resources/db/migration/V134__add_created_and_modified_facility_operators.sql new file mode 100644 index 0000000000..d1ef76caee --- /dev/null +++ b/modules/db/src/main/resources/db/migration/V134__add_created_and_modified_facility_operators.sql @@ -0,0 +1,13 @@ +-- +-- This program is part of the OpenLMIS logistics management information system platform software. +-- Copyright © 2013 VillageReach +-- +-- This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +--   +-- 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 Affero General Public License for more details. +-- You should have received a copy of the GNU Affero General Public License along with this program.  If not, see http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  +-- + +ALTER TABLE facility_operators ADD COLUMN createdBy INTEGER; +ALTER TABLE facility_operators ADD COLUMN modifiedBy INTEGER; +ALTER TABLE facility_operators ADD COLUMN modifiedDate TIMESTAMP WITH TIME ZONE DEFAULT NOW(); From 3d817da94e73168c49bc41887f91df4db4aa1052 Mon Sep 17 00:00:00 2001 From: joshzamor Date: Mon, 7 Jul 2014 10:24:21 -0700 Subject: [PATCH 19/80] OA-35, added facility operator mapper with insert, update and search by code operations along with integration test. --- .../mapper/FacilityOperatorMapper.java | 30 +++++++++ .../mapper/FacilityOperatorMapperIT.java | 61 +++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 modules/core/src/main/java/org/openlmis/core/repository/mapper/FacilityOperatorMapper.java create mode 100644 modules/core/src/test/java/org/openlmis/core/repository/mapper/FacilityOperatorMapperIT.java diff --git a/modules/core/src/main/java/org/openlmis/core/repository/mapper/FacilityOperatorMapper.java b/modules/core/src/main/java/org/openlmis/core/repository/mapper/FacilityOperatorMapper.java new file mode 100644 index 0000000000..ead8856850 --- /dev/null +++ b/modules/core/src/main/java/org/openlmis/core/repository/mapper/FacilityOperatorMapper.java @@ -0,0 +1,30 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2013 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + *   + * 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 Affero General Public License for more details. + * You should have received a copy of the GNU Affero General Public License along with this program.  If not, see http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  + */ + +package org.openlmis.core.repository.mapper; + +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.Update; +import org.openlmis.core.domain.FacilityOperator; +import org.springframework.stereotype.Repository; + +@Repository +public interface FacilityOperatorMapper { + + @Insert("INSERT INTO facility_operators (code, text, displayOrder) VALUES (#{code}, #{text}, #{displayOrder})") + void insert(FacilityOperator facilityOperator); + + @Update("UPDATE facility_operators SET code=#{code}, text=#{text}, displayOrder=#{displayOrder} WHERE id=#{id}") + void update(FacilityOperator facilityOperator); + + @Select("SELECT * FROM facility_operators WHERE code = #{code}") + FacilityOperator getByCode(String code); +} \ No newline at end of file diff --git a/modules/core/src/test/java/org/openlmis/core/repository/mapper/FacilityOperatorMapperIT.java b/modules/core/src/test/java/org/openlmis/core/repository/mapper/FacilityOperatorMapperIT.java new file mode 100644 index 0000000000..9782d1aa0e --- /dev/null +++ b/modules/core/src/test/java/org/openlmis/core/repository/mapper/FacilityOperatorMapperIT.java @@ -0,0 +1,61 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2013 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + *   + * 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 Affero General Public License for more details. + * You should have received a copy of the GNU Affero General Public License along with this program.  If not, see http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  + */ + +package org.openlmis.core.repository.mapper; + +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; +import org.openlmis.core.domain.FacilityOperator; +import org.openlmis.db.categories.IntegrationTests; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.transaction.TransactionConfiguration; +import org.springframework.transaction.annotation.Transactional; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; + +@Category(IntegrationTests.class) +@ContextConfiguration(locations = "classpath:test-applicationContext-core.xml") +@RunWith(SpringJUnit4ClassRunner.class) +@Transactional +@TransactionConfiguration(defaultRollback = true, transactionManager = "openLmisTransactionManager") +public class FacilityOperatorMapperIT { + + @Autowired + private FacilityOperatorMapper mapper; + + @Test + public void shouldInsertAndUpdateFacilityOperatorByCode() { + FacilityOperator facOp = new FacilityOperator(); + facOp.setCode("someCode"); + facOp.setText("someText"); + facOp.setDisplayOrder(1); + + // insert + mapper.insert(facOp); + + // test insert + FacilityOperator retFacOp = mapper.getByCode(facOp.getCode()); + assertThat(retFacOp, is(facOp)); + + // update + retFacOp.setCode("aNewCode"); + retFacOp.setText("aNewText"); + retFacOp.setDisplayOrder(2); + mapper.update(retFacOp); + + // test update + FacilityOperator updatedFacOp = mapper.getByCode(retFacOp.getCode()); + assertThat(updatedFacOp, is(retFacOp)); + } +} From a9120b70912b9c4076c7e646c44e6001da869a59 Mon Sep 17 00:00:00 2001 From: joshzamor Date: Mon, 7 Jul 2014 12:29:21 -0700 Subject: [PATCH 20/80] OA-35, added persistence handler, repository and associated unit tests and IT for uploading facility operators --- .../FacilityOperatorRepository.java | 65 +++++++++++++++++++ .../mapper/FacilityOperatorMapper.java | 2 +- .../FacilityOperatorPersistenceHandler.java | 43 ++++++++++++ .../FacilityOperatorRepositoryTest.java | 64 ++++++++++++++++++ .../mapper/FacilityOperatorMapperIT.java | 17 ++++- .../src/main/resources/applicationContext.xml | 9 +++ .../src/main/resources/messages_en.properties | 2 + .../src/main/resources/messages_es.properties | 4 +- .../src/main/resources/messages_pt.properties | 4 +- 9 files changed, 206 insertions(+), 4 deletions(-) create mode 100644 modules/core/src/main/java/org/openlmis/core/repository/FacilityOperatorRepository.java create mode 100644 modules/core/src/main/java/org/openlmis/core/upload/FacilityOperatorPersistenceHandler.java create mode 100644 modules/core/src/test/java/org/openlmis/core/repository/FacilityOperatorRepositoryTest.java diff --git a/modules/core/src/main/java/org/openlmis/core/repository/FacilityOperatorRepository.java b/modules/core/src/main/java/org/openlmis/core/repository/FacilityOperatorRepository.java new file mode 100644 index 0000000000..7aa6881b9c --- /dev/null +++ b/modules/core/src/main/java/org/openlmis/core/repository/FacilityOperatorRepository.java @@ -0,0 +1,65 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2013 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + *   + * 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 Affero General Public License for more details. + * You should have received a copy of the GNU Affero General Public License along with this program.  If not, see http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  + */ + +package org.openlmis.core.repository; + +import lombok.NoArgsConstructor; +import org.openlmis.core.domain.FacilityOperator; +import org.openlmis.core.exception.DataException; +import org.openlmis.core.repository.mapper.FacilityOperatorMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.stereotype.Repository; + +/** + * Repository for managing {@link org.openlmis.core.domain.FacilityOperator} entities and related operations. + */ +@Repository +@NoArgsConstructor +public class FacilityOperatorRepository { + private FacilityOperatorMapper facilityOperatorMapper; + + @Autowired + public FacilityOperatorRepository(FacilityOperatorMapper facilityOperatorMapper) { + this.facilityOperatorMapper = facilityOperatorMapper; + } + + /** + * Gets the FacilityOperator that has the given code. + * @param code the code, case insensitive + * @return the FacilityOperator with the given code or null if no such FacilityOperator exists with the given code. + */ + public FacilityOperator getByCode(String code) { + if(code == null) return null; + return facilityOperatorMapper.getByCode(code); + } + + /** + * Saves a FacilityOperator entity to persistent storage. If + * {@link org.openlmis.core.domain.FacilityOperator#hasId()} is true, then the id will be used to + * update the existing entity, otherwise a new one will be created. + * @param facilityOperator the FacilityOperator to save. + * @throws org.openlmis.core.exception.DataException if unable to save entity. + * @throws java.lang.NullPointerException if facilityOperator is null. + */ + public void save(FacilityOperator facilityOperator) { + if(facilityOperator == null) throw new NullPointerException("facilityOperator argument is null"); + + try { + if(facilityOperator.hasId()) facilityOperatorMapper.update(facilityOperator); + else facilityOperatorMapper.insert(facilityOperator); + } catch(DuplicateKeyException dke) { + throw new DataException("error.duplicate.facility.operator.code"); + } catch(DataIntegrityViolationException dive) { + throw new DataException("error.incorrect.length"); + } + } +} \ No newline at end of file diff --git a/modules/core/src/main/java/org/openlmis/core/repository/mapper/FacilityOperatorMapper.java b/modules/core/src/main/java/org/openlmis/core/repository/mapper/FacilityOperatorMapper.java index ead8856850..a11af20b99 100644 --- a/modules/core/src/main/java/org/openlmis/core/repository/mapper/FacilityOperatorMapper.java +++ b/modules/core/src/main/java/org/openlmis/core/repository/mapper/FacilityOperatorMapper.java @@ -25,6 +25,6 @@ public interface FacilityOperatorMapper { @Update("UPDATE facility_operators SET code=#{code}, text=#{text}, displayOrder=#{displayOrder} WHERE id=#{id}") void update(FacilityOperator facilityOperator); - @Select("SELECT * FROM facility_operators WHERE code = #{code}") + @Select("SELECT * FROM facility_operators WHERE LOWER(code) = LOWER(#{code})") FacilityOperator getByCode(String code); } \ No newline at end of file diff --git a/modules/core/src/main/java/org/openlmis/core/upload/FacilityOperatorPersistenceHandler.java b/modules/core/src/main/java/org/openlmis/core/upload/FacilityOperatorPersistenceHandler.java new file mode 100644 index 0000000000..354a4dd21b --- /dev/null +++ b/modules/core/src/main/java/org/openlmis/core/upload/FacilityOperatorPersistenceHandler.java @@ -0,0 +1,43 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2013 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + *   + * 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 Affero General Public License for more details. + * You should have received a copy of the GNU Affero General Public License along with this program.  If not, see http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  + */ + +package org.openlmis.core.upload; + +import org.openlmis.core.domain.BaseModel; +import org.openlmis.core.domain.FacilityOperator; +import org.openlmis.core.repository.FacilityOperatorRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class FacilityOperatorPersistenceHandler extends AbstractModelPersistenceHandler { + private FacilityOperatorRepository facOpRepo; + + @Autowired + public FacilityOperatorPersistenceHandler(FacilityOperatorRepository facilityOperatorRepository) { + this.facOpRepo = facilityOperatorRepository; + } + + @Override + protected BaseModel getExisting(BaseModel record) { + FacilityOperator facOp = (FacilityOperator) record; + return facOpRepo.getByCode(facOp.getCode()); + } + + @Override + protected void save(BaseModel record) { + facOpRepo.save((FacilityOperator) record); + } + + @Override + public String getMessageKey() { + return "error.duplicate.facility.operator.code"; + } +} diff --git a/modules/core/src/test/java/org/openlmis/core/repository/FacilityOperatorRepositoryTest.java b/modules/core/src/test/java/org/openlmis/core/repository/FacilityOperatorRepositoryTest.java new file mode 100644 index 0000000000..4774615825 --- /dev/null +++ b/modules/core/src/test/java/org/openlmis/core/repository/FacilityOperatorRepositoryTest.java @@ -0,0 +1,64 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2013 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + *   + * 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 Affero General Public License for more details. + * You should have received a copy of the GNU Affero General Public License along with this program.  If not, see http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  + */ + +package org.openlmis.core.repository; + +import org.junit.Before; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.openlmis.core.domain.FacilityOperator; +import org.openlmis.core.repository.mapper.FacilityOperatorMapper; +import org.openlmis.db.categories.UnitTests; + +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.Mockito.*; + +@Category(UnitTests.class) +@RunWith(MockitoJUnitRunner.class) +public class FacilityOperatorRepositoryTest { + @Mock + private FacilityOperatorMapper mapper; + + private FacilityOperatorRepository repo; + + @Before + public void setup() { + repo = new FacilityOperatorRepository(mapper); + } + + @Test + public void shouldReturnNullIfGetByCodeGivenNull() { + assertThat(repo.getByCode(null), nullValue()); + } + + @Test(expected=NullPointerException.class) + public void shouldThrowNullPointerExceptionOnSaveWithNull() { + repo.save(null); + } + + @Test + public void shouldUpdateOrInsertOnSaveUsingHasId() { + FacilityOperator facOp = mock(FacilityOperator.class); + + // when hasId() is true, call update + when(facOp.hasId()).thenReturn(true); + repo.save(facOp); + verify(mapper).update(facOp); + + // when hasId() is false, call insert + when(facOp.hasId()).thenReturn(false); + repo.save(facOp); + verify(mapper).insert(facOp); + } +} diff --git a/modules/core/src/test/java/org/openlmis/core/repository/mapper/FacilityOperatorMapperIT.java b/modules/core/src/test/java/org/openlmis/core/repository/mapper/FacilityOperatorMapperIT.java index 9782d1aa0e..b9b1b4ae60 100644 --- a/modules/core/src/test/java/org/openlmis/core/repository/mapper/FacilityOperatorMapperIT.java +++ b/modules/core/src/test/java/org/openlmis/core/repository/mapper/FacilityOperatorMapperIT.java @@ -22,7 +22,8 @@ import org.springframework.transaction.annotation.Transactional; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.core.Is.is; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; @Category(IntegrationTests.class) @ContextConfiguration(locations = "classpath:test-applicationContext-core.xml") @@ -58,4 +59,18 @@ public void shouldInsertAndUpdateFacilityOperatorByCode() { FacilityOperator updatedFacOp = mapper.getByCode(retFacOp.getCode()); assertThat(updatedFacOp, is(retFacOp)); } + + @Test + public void shouldIgnoreCaseWhenFindByCode() { + FacilityOperator facOp = new FacilityOperator(); + facOp.setCode("somecode"); + facOp.setText("sometext"); + facOp.setDisplayOrder(1); + + mapper.insert(facOp); + + FacilityOperator retFacOp = mapper.getByCode("SOMECODE"); + assertThat(retFacOp, notNullValue()); + assertThat(retFacOp, is(facOp)); + } } diff --git a/modules/openlmis-web/src/main/resources/applicationContext.xml b/modules/openlmis-web/src/main/resources/applicationContext.xml index c33575c015..af09d6bd97 100644 --- a/modules/openlmis-web/src/main/resources/applicationContext.xml +++ b/modules/openlmis-web/src/main/resources/applicationContext.xml @@ -315,6 +315,15 @@ + + + + + org.openlmis.core.domain.FacilityOperator + + + + diff --git a/modules/openlmis-web/src/main/resources/messages_en.properties b/modules/openlmis-web/src/main/resources/messages_en.properties index ef1eca91aa..5124d759da 100644 --- a/modules/openlmis-web/src/main/resources/messages_en.properties +++ b/modules/openlmis-web/src/main/resources/messages_en.properties @@ -170,6 +170,7 @@ error.duplicate.employee.id = Duplicate employee id error.duplicate.email = Duplicate email address error.duplicate.user.name = Duplicate User Name error.duplicate.role = Duplicate Role found +error.duplicate.facility.operator.code = Duplicate Facility Operator Code message.user.created.success.email.sent = User "{0} {1}" has been successfully created, password link has been sent on registered Email address message.user.updated.success = User "{0} {1}" has been successfully updated email.sent = Email sent @@ -1244,3 +1245,4 @@ msg.enter.product.code.name = Enter product code or name label.program.and.schedules = Program and Schedules facilities.added.successfully = Facilities added successfully add.selected.facilities = Add selected facilities +label.facility.operators = Facility Operators \ No newline at end of file diff --git a/modules/openlmis-web/src/main/resources/messages_es.properties b/modules/openlmis-web/src/main/resources/messages_es.properties index 6966bc8eb7..671b835571 100644 --- a/modules/openlmis-web/src/main/resources/messages_es.properties +++ b/modules/openlmis-web/src/main/resources/messages_es.properties @@ -1220,4 +1220,6 @@ add.selected.facilities = Add ©∆˚˙…¬˚∆ææææµ ƒå error.duplicate.program.code=Duplicar Código del Programa label.programs=Programas label.geographic.levels=Niveles Geográficos -error.duplicate.geographic.level.code=Duplicate Code Nivel Geographic \ No newline at end of file +error.duplicate.geographic.level.code=Duplicate Code Nivel Geographic +error.duplicate.facility.operator.code=Duplicar Facilidad Código Operador +label.facility.operators=Operadores de Instalaciones \ No newline at end of file diff --git a/modules/openlmis-web/src/main/resources/messages_pt.properties b/modules/openlmis-web/src/main/resources/messages_pt.properties index 422c983410..43b35c4cc2 100644 --- a/modules/openlmis-web/src/main/resources/messages_pt.properties +++ b/modules/openlmis-web/src/main/resources/messages_pt.properties @@ -1220,4 +1220,6 @@ add.selected.facilities = Add S´´†´∂ fa爬ˆ†ˆ´ error.duplicate.program.code=Duplicar código do programa label.programs=Programas label.geographic.levels=Níveis Geográficos -error.duplicate.geographic.level.code=Duplicate Código Geográfico Nível \ No newline at end of file +error.duplicate.geographic.level.code=Duplicate Código Geográfico Nível +error.duplicate.facility.operator.code=Duplicar Facilidade Código de operador +label.facility.operators=Operadores de instalações \ No newline at end of file From c140d2460ac1ac5817decc26fcbaa952c635e0cd Mon Sep 17 00:00:00 2001 From: joshzamor Date: Mon, 7 Jul 2014 13:53:29 -0700 Subject: [PATCH 21/80] OA-35, refactored facility operator operations out of Facility Service, Facility Repository and mapper to instead use the Facility Operator Repo and mapper. --- .../FacilityOperatorRepository.java | 19 ++++++++++++ .../core/repository/FacilityRepository.java | 9 +++--- .../repository/mapper/FacilityMapper.java | 17 ++++------- .../mapper/FacilityOperatorMapper.java | 8 +++++ .../core/service/FacilityService.java | 4 --- .../repository/FacilityRepositoryTest.java | 5 +++- .../repository/mapper/FacilityMapperIT.java | 19 ------------ .../mapper/FacilityOperatorMapperIT.java | 30 +++++++++++++++++-- .../web/controller/FacilityController.java | 6 +++- .../controller/FacilityControllerTest.java | 8 +++-- 10 files changed, 79 insertions(+), 46 deletions(-) diff --git a/modules/core/src/main/java/org/openlmis/core/repository/FacilityOperatorRepository.java b/modules/core/src/main/java/org/openlmis/core/repository/FacilityOperatorRepository.java index 7aa6881b9c..eeae5f85db 100644 --- a/modules/core/src/main/java/org/openlmis/core/repository/FacilityOperatorRepository.java +++ b/modules/core/src/main/java/org/openlmis/core/repository/FacilityOperatorRepository.java @@ -19,6 +19,8 @@ import org.springframework.dao.DuplicateKeyException; import org.springframework.stereotype.Repository; +import java.util.List; + /** * Repository for managing {@link org.openlmis.core.domain.FacilityOperator} entities and related operations. */ @@ -62,4 +64,21 @@ public void save(FacilityOperator facilityOperator) { throw new DataException("error.incorrect.length"); } } + + /** + * Gets the FacilityOperator by it's persistence id. + * @param id the id of the FacilityOperator to find + * @return the FacilityOperator with the given id or null if no such FacilityOperator exists with given id. + */ + public FacilityOperator getById(long id) { + return facilityOperatorMapper.getById(id); + } + + /** + * Gets all the FacilityOperators in descending order by their display order. + * @return a list of all persisted FacilityOperator entities. + */ + public List getAll() { + return facilityOperatorMapper.getAll(); + } } \ No newline at end of file diff --git a/modules/core/src/main/java/org/openlmis/core/repository/FacilityRepository.java b/modules/core/src/main/java/org/openlmis/core/repository/FacilityRepository.java index 3db1d8e734..38a0b780b5 100644 --- a/modules/core/src/main/java/org/openlmis/core/repository/FacilityRepository.java +++ b/modules/core/src/main/java/org/openlmis/core/repository/FacilityRepository.java @@ -42,6 +42,9 @@ public class FacilityRepository { @Autowired private GeographicZoneRepository geographicZoneRepository; + @Autowired + private FacilityOperatorRepository facilityOperatorRepository; + public List getAll() { return mapper.getAll(); } @@ -111,17 +114,13 @@ private void validateAndSetFacilityOperatedBy(Facility facility) { if (operatedById == null) throw new DataException("error.reference.data.invalid.operated.by"); - facility.setOperatedBy(mapper.getFacilityOperatorById(operatedById)); + facility.setOperatedBy(facilityOperatorRepository.getById(operatedById)); } public List getAllTypes() { return mapper.getAllTypes(); } - public List getAllOperators() { - return mapper.getAllOperators(); - } - public Facility getHomeFacility(Long userId) { return mapper.getHomeFacility(userId); } diff --git a/modules/core/src/main/java/org/openlmis/core/repository/mapper/FacilityMapper.java b/modules/core/src/main/java/org/openlmis/core/repository/mapper/FacilityMapper.java index e0e57bc755..08a428d2d5 100644 --- a/modules/core/src/main/java/org/openlmis/core/repository/mapper/FacilityMapper.java +++ b/modules/core/src/main/java/org/openlmis/core/repository/mapper/FacilityMapper.java @@ -12,7 +12,6 @@ import org.apache.ibatis.annotations.*; import org.openlmis.core.domain.Facility; -import org.openlmis.core.domain.FacilityOperator; import org.openlmis.core.domain.FacilityType; import org.springframework.stereotype.Repository; @@ -60,12 +59,6 @@ public interface FacilityMapper { @Select("SELECT * FROM facility_types WHERE id = #{id}") public FacilityType getFacilityTypeById(Long id); - @Select("SELECT * FROM facility_operators ORDER BY displayOrder") - List getAllOperators(); - - @Select("SELECT * FROM facility_operators WHERE id = #{id}") - public FacilityOperator getFacilityOperatorById(Long id); - @Select("SELECT code FROM facility_operators WHERE id = #{id}") @SuppressWarnings("unused") public String getFacilityOperatorCodeFor(Long id); @@ -80,7 +73,7 @@ public interface FacilityMapper { @Result(property = "facilityType", column = "typeId", javaType = Long.class, one = @One(select = "getFacilityTypeById")), @Result(property = "operatedBy", column = "operatedById", javaType = Long.class, - one = @One(select = "getFacilityOperatorById")) + one = @One(select = "org.openlmis.core.repository.mapper.FacilityOperatorMapper.getById")) }) Facility getById(Long id); @@ -95,7 +88,7 @@ public interface FacilityMapper { @Result(property = "facilityType", column = "typeId", javaType = Long.class, one = @One(select = "getFacilityTypeById")), @Result(property = "operatedBy", column = "operatedById", javaType = Long.class, - one = @One(select = "getFacilityOperatorById")), + one = @One(select = "org.openlmis.core.repository.mapper.FacilityOperatorMapper.getById")), @Result(property = "supportedPrograms", column = "id", javaType = List.class, many = @Many(select = "org.openlmis.core.repository.mapper.ProgramSupportedMapper.getAllByFacilityId")) }) @@ -140,7 +133,7 @@ public interface FacilityMapper { @Result(property = "facilityType", column = "typeId", javaType = Long.class, one = @One(select = "getFacilityTypeById")), @Result(property = "operatedBy", column = "operatedById", javaType = Long.class, - one = @One(select = "getFacilityOperatorById")) + one = @One(select = "org.openlmis.core.repository.mapper.FacilityOperatorMapper.getById")) }) List getFacilitiesBy(@Param(value = "programId") Long programId, @Param(value = "requisitionGroupIds") String requisitionGroupIds); @@ -164,7 +157,7 @@ List searchFacilitiesByCodeOrNameAndVirtualFacilityFlag(@Param("search @Results(value = { @Result(property = "geographicZone.id", column = "geographicZoneId"), @Result(property = "facilityType", column = "typeId", javaType = Long.class, one = @One(select = "getFacilityTypeById")), - @Result(property = "operatedBy", column = "operatedById", javaType = Long.class, one = @One(select = "getFacilityOperatorById")) + @Result(property = "operatedBy", column = "operatedById", javaType = Long.class, one = @One(select = "org.openlmis.core.repository.mapper.FacilityOperatorMapper.getById")) }) Facility getHomeFacilityWithRights(@Param("userId") Long userId, @Param("commaSeparatedRights") String commaSeparatedRights); @@ -177,7 +170,7 @@ Facility getHomeFacilityWithRights(@Param("userId") Long userId, @Result(property = "facilityType", column = "typeId", javaType = Long.class, one = @One(select = "getFacilityTypeById")), @Result(property = "operatedBy", column = "operatedById", javaType = Long.class, - one = @One(select = "getFacilityOperatorById")) + one = @One(select = "org.openlmis.core.repository.mapper.FacilityOperatorMapper.getById")) }) List getAllInRequisitionGroups(@Param("requisitionGroupIds") String requisitionGroupIds); diff --git a/modules/core/src/main/java/org/openlmis/core/repository/mapper/FacilityOperatorMapper.java b/modules/core/src/main/java/org/openlmis/core/repository/mapper/FacilityOperatorMapper.java index a11af20b99..d9425888ba 100644 --- a/modules/core/src/main/java/org/openlmis/core/repository/mapper/FacilityOperatorMapper.java +++ b/modules/core/src/main/java/org/openlmis/core/repository/mapper/FacilityOperatorMapper.java @@ -16,6 +16,8 @@ import org.openlmis.core.domain.FacilityOperator; import org.springframework.stereotype.Repository; +import java.util.List; + @Repository public interface FacilityOperatorMapper { @@ -27,4 +29,10 @@ public interface FacilityOperatorMapper { @Select("SELECT * FROM facility_operators WHERE LOWER(code) = LOWER(#{code})") FacilityOperator getByCode(String code); + + @Select("SELECT * FROM facility_operators ORDER BY displayOrder") + List getAll(); + + @Select("SELECT * FROM facility_operators WHERE id = #{id}") + FacilityOperator getById(Long id); } \ No newline at end of file diff --git a/modules/core/src/main/java/org/openlmis/core/service/FacilityService.java b/modules/core/src/main/java/org/openlmis/core/service/FacilityService.java index d4ef119576..8a31dffd40 100644 --- a/modules/core/src/main/java/org/openlmis/core/service/FacilityService.java +++ b/modules/core/src/main/java/org/openlmis/core/service/FacilityService.java @@ -78,10 +78,6 @@ public List getAllTypes() { return facilityRepository.getAllTypes(); } - public List getAllOperators() { - return facilityRepository.getAllOperators(); - } - public List getAllZones() { return geographicZoneRepository.getAllGeographicZones(); } diff --git a/modules/core/src/test/java/org/openlmis/core/repository/FacilityRepositoryTest.java b/modules/core/src/test/java/org/openlmis/core/repository/FacilityRepositoryTest.java index f0ba77dd58..fecb1e8d04 100644 --- a/modules/core/src/test/java/org/openlmis/core/repository/FacilityRepositoryTest.java +++ b/modules/core/src/test/java/org/openlmis/core/repository/FacilityRepositoryTest.java @@ -57,6 +57,9 @@ public class FacilityRepositoryTest { @Mock private FacilityMapper mapper; + @Mock + private FacilityOperatorRepository facilityOperatorRepository; + @Mock private GeographicZoneRepository geographicZoneRepository; @@ -154,7 +157,7 @@ public void shouldSetFacilityOperatorIdWhenCodeIsValid() throws Exception { facilityOperator.setId(1l); facilityOperator.setCode(operatedByCode); facilityOperator.setText(operatedByName); - when(mapper.getFacilityOperatorById(1l)).thenReturn(facilityOperator); + when(facilityOperatorRepository.getById(1l)).thenReturn(facilityOperator); repository.save(facility); assertThat(facility.getOperatedBy().getId(), is(facilityOperatorId)); diff --git a/modules/core/src/test/java/org/openlmis/core/repository/mapper/FacilityMapperIT.java b/modules/core/src/test/java/org/openlmis/core/repository/mapper/FacilityMapperIT.java index 13a67040e2..edcdee7f11 100644 --- a/modules/core/src/test/java/org/openlmis/core/repository/mapper/FacilityMapperIT.java +++ b/modules/core/src/test/java/org/openlmis/core/repository/mapper/FacilityMapperIT.java @@ -164,16 +164,6 @@ public void shouldGetAllParentFacilitiesByModifiedDate() { assertThat(facilities.get(0).getId(), is(facility2.getId())); } - @Test - public void shouldGetAllOperators() throws Exception { - List allOperators = mapper.getAllOperators(); - assertThat(allOperators.size(), is(4)); - FacilityOperator facilityOperator = allOperators.get(0); - assertThat(facilityOperator.getCode(), is("MoH")); - assertThat(facilityOperator.getText(), is("MoH")); - assertThat(facilityOperator.getDisplayOrder(), is(1)); - } - @Test public void shouldReturnFacilityForAUser() throws Exception { mapper.insert(make(a(defaultFacility))); @@ -320,15 +310,6 @@ public void shouldReturnFacilityTypeById() { assertThat(facilityType.getCode(), is(FACILITY_TYPE_CODE)); } - @Test - public void shouldReturnFacilityOperatorById() throws Exception { - Long id = mapper.getOperatedByIdForCode(OPERATED_BY_MOH); - - FacilityOperator operator = mapper.getFacilityOperatorById(id); - assertThat(operator.getId(), is(id)); - assertThat(operator.getCode(), is(OPERATED_BY_MOH)); - } - @Test public void shouldUpdateEnabledAndActiveForAFacility() throws Exception { Facility facility = make(a(defaultFacility)); diff --git a/modules/core/src/test/java/org/openlmis/core/repository/mapper/FacilityOperatorMapperIT.java b/modules/core/src/test/java/org/openlmis/core/repository/mapper/FacilityOperatorMapperIT.java index b9b1b4ae60..a36a3e0df6 100644 --- a/modules/core/src/test/java/org/openlmis/core/repository/mapper/FacilityOperatorMapperIT.java +++ b/modules/core/src/test/java/org/openlmis/core/repository/mapper/FacilityOperatorMapperIT.java @@ -13,6 +13,7 @@ import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; +import org.openlmis.core.domain.Facility; import org.openlmis.core.domain.FacilityOperator; import org.openlmis.db.categories.IntegrationTests; import org.springframework.beans.factory.annotation.Autowired; @@ -21,9 +22,13 @@ import org.springframework.test.context.transaction.TransactionConfiguration; import org.springframework.transaction.annotation.Transactional; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.Matchers.greaterThan; @Category(IntegrationTests.class) @ContextConfiguration(locations = "classpath:test-applicationContext-core.xml") @@ -73,4 +78,25 @@ public void shouldIgnoreCaseWhenFindByCode() { assertThat(retFacOp, notNullValue()); assertThat(retFacOp, is(facOp)); } + + @Test + public void shouldReturnNullWithInvalidId() { + assertThat(mapper.getById(9919L), nullValue()); + } + + @Test + public void shouldGetAllByDisplayOrder() { + List allManualSort = mapper.getAll(); + + Collections.sort(allManualSort, new Comparator() { + @Override + public int compare(FacilityOperator o1, FacilityOperator o2) { + return o1.getDisplayOrder().compareTo(o2.getDisplayOrder()); + } + }); + + List all = mapper.getAll(); + assertThat(all.size(), greaterThan(1)); + assertThat(all, equalTo(allManualSort)); + } } diff --git a/modules/openlmis-web/src/main/java/org/openlmis/web/controller/FacilityController.java b/modules/openlmis-web/src/main/java/org/openlmis/web/controller/FacilityController.java index dc5245ff13..0f2f4e7ab5 100644 --- a/modules/openlmis-web/src/main/java/org/openlmis/web/controller/FacilityController.java +++ b/modules/openlmis-web/src/main/java/org/openlmis/web/controller/FacilityController.java @@ -14,6 +14,7 @@ import org.openlmis.core.domain.Facility; import org.openlmis.core.domain.FacilityType; import org.openlmis.core.exception.DataException; +import org.openlmis.core.repository.FacilityOperatorRepository; import org.openlmis.core.service.FacilityService; import org.openlmis.core.service.ProgramService; import org.openlmis.web.model.FacilityReferenceData; @@ -55,6 +56,9 @@ public class FacilityController extends BaseController { @Autowired private FacilityService facilityService; + @Autowired + private FacilityOperatorRepository facilityOperatorRepository; + @Autowired private ProgramService programService; @@ -94,7 +98,7 @@ public List getHomeFacility(HttpServletRequest httpServletRequest) { public Map getReferenceData() { FacilityReferenceData facilityReferenceData = new FacilityReferenceData(); return facilityReferenceData.addFacilityTypes(facilityService.getAllTypes()). - addFacilityOperators(facilityService.getAllOperators()). + addFacilityOperators(facilityOperatorRepository.getAll()). addGeographicZones(facilityService.getAllZones()). addPrograms(programService.getAll()).get(); } diff --git a/modules/openlmis-web/src/test/java/org/openlmis/web/controller/FacilityControllerTest.java b/modules/openlmis-web/src/test/java/org/openlmis/web/controller/FacilityControllerTest.java index ab44913f21..fa305d165d 100644 --- a/modules/openlmis-web/src/test/java/org/openlmis/web/controller/FacilityControllerTest.java +++ b/modules/openlmis-web/src/test/java/org/openlmis/web/controller/FacilityControllerTest.java @@ -19,6 +19,7 @@ import org.mockito.Mockito; import org.openlmis.core.domain.*; import org.openlmis.core.exception.DataException; +import org.openlmis.core.repository.FacilityOperatorRepository; import org.openlmis.core.service.FacilityService; import org.openlmis.core.service.MessageService; import org.openlmis.core.service.ProgramService; @@ -61,6 +62,9 @@ public class FacilityControllerTest { @Mock private FacilityService facilityService; + @Mock + private FacilityOperatorRepository facilityOperatorRepository; + @Mock private MessageService messageService; @@ -80,7 +84,7 @@ public void setUp() { public void shouldFetchRequiredReferenceDataForFacility() { List facilityOperators = new ArrayList<>(); - when(facilityService.getAllOperators()).thenReturn(facilityOperators); + when(facilityOperatorRepository.getAll()).thenReturn(facilityOperators); List facilityTypes = new ArrayList<>(); when(facilityService.getAllTypes()).thenReturn(facilityTypes); List allZones = new ArrayList<>(); @@ -91,7 +95,7 @@ public void shouldFetchRequiredReferenceDataForFacility() { Map referenceData = facilityController.getReferenceData(); - verify(facilityService).getAllOperators(); + verify(facilityOperatorRepository).getAll(); assertThat((List) referenceData.get(FACILITY_OPERATORS), is(equalTo(facilityOperators))); verify(facilityService).getAllTypes(); assertThat((List) referenceData.get(FACILITY_TYPES), is(equalTo(facilityTypes))); From ce56a00b95f1c1ef7df689e1b87a0cb33788736b Mon Sep 17 00:00:00 2001 From: joshzamor Date: Mon, 7 Jul 2014 14:07:19 -0700 Subject: [PATCH 22/80] OA-35, removed unused method in facility mapper that dealt with Facility Operators. --- .../org/openlmis/core/repository/mapper/FacilityMapper.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/modules/core/src/main/java/org/openlmis/core/repository/mapper/FacilityMapper.java b/modules/core/src/main/java/org/openlmis/core/repository/mapper/FacilityMapper.java index 08a428d2d5..5f2994bc91 100644 --- a/modules/core/src/main/java/org/openlmis/core/repository/mapper/FacilityMapper.java +++ b/modules/core/src/main/java/org/openlmis/core/repository/mapper/FacilityMapper.java @@ -59,10 +59,6 @@ public interface FacilityMapper { @Select("SELECT * FROM facility_types WHERE id = #{id}") public FacilityType getFacilityTypeById(Long id); - @Select("SELECT code FROM facility_operators WHERE id = #{id}") - @SuppressWarnings("unused") - public String getFacilityOperatorCodeFor(Long id); - @Select("SELECT id FROM facility_operators WHERE LOWER(code) = LOWER(#{code})") Long getOperatedByIdForCode(String code); From 9d2682b3694b2d2c446daf8d8c42a6453d58d7c6 Mon Sep 17 00:00:00 2001 From: joshzamor Date: Mon, 7 Jul 2014 14:32:42 -0700 Subject: [PATCH 23/80] OA-34, added importable to facility type entity, added missing base model fields for createdby, modifiedby and modified date to facility type table. --- .../openlmis/core/domain/FacilityType.java | 19 ++++++++++++++++++- ...dd_created_and_modified_facility_types.sql | 13 +++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 modules/db/src/main/resources/db/migration/V135__add_created_and_modified_facility_types.sql diff --git a/modules/core/src/main/java/org/openlmis/core/domain/FacilityType.java b/modules/core/src/main/java/org/openlmis/core/domain/FacilityType.java index 866e0a9b8f..27af7ec0c1 100644 --- a/modules/core/src/main/java/org/openlmis/core/domain/FacilityType.java +++ b/modules/core/src/main/java/org/openlmis/core/domain/FacilityType.java @@ -14,6 +14,8 @@ import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.codehaus.jackson.map.annotate.JsonSerialize; +import org.openlmis.upload.Importable; +import org.openlmis.upload.annotation.ImportField; import static org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion.NON_EMPTY; @@ -25,14 +27,29 @@ @NoArgsConstructor @JsonSerialize(include = NON_EMPTY) @EqualsAndHashCode(callSuper = false) -public class FacilityType extends BaseModel { +public class FacilityType extends BaseModel implements Importable { + + @ImportField(name="Facilty Type Code", mandatory=true) private String code; + + @ImportField(name="Name", mandatory=true) private String name; + + @ImportField(name="Description") private String description; + private Integer levelId; + + @ImportField(name="Nominal Max Month", mandatory=true) private Integer nominalMaxMonth; + + @ImportField(name="Nominal EOP", mandatory=true) private Double nominalEop; + + @ImportField(name="Display Order", mandatory=true) private Integer displayOrder; + + @ImportField(name="Active", mandatory=true) private boolean active; public FacilityType(String code) { diff --git a/modules/db/src/main/resources/db/migration/V135__add_created_and_modified_facility_types.sql b/modules/db/src/main/resources/db/migration/V135__add_created_and_modified_facility_types.sql new file mode 100644 index 0000000000..eba9aa12f1 --- /dev/null +++ b/modules/db/src/main/resources/db/migration/V135__add_created_and_modified_facility_types.sql @@ -0,0 +1,13 @@ +-- +-- This program is part of the OpenLMIS logistics management information system platform software. +-- Copyright © 2013 VillageReach +-- +-- This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +--   +-- 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 Affero General Public License for more details. +-- You should have received a copy of the GNU Affero General Public License along with this program.  If not, see http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  +-- + +ALTER TABLE facility_types ADD COLUMN createdBy INTEGER; +ALTER TABLE facility_types ADD COLUMN modifiedBy INTEGER; +ALTER TABLE facility_types ADD COLUMN modifiedDate TIMESTAMP WITH TIME ZONE DEFAULT NOW(); From 76bc47bfdc8b5fd82935a3d73f6d69c3ba801812 Mon Sep 17 00:00:00 2001 From: joshzamor Date: Mon, 7 Jul 2014 15:33:01 -0700 Subject: [PATCH 24/80] OA-34, added facility type mapper and integration tests for insert, update and get by code operations --- .../repository/mapper/FacilityTypeMapper.java | 64 +++++++++++++++++++ .../mapper/FacilityTypeMapperIT.java | 60 +++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 modules/core/src/main/java/org/openlmis/core/repository/mapper/FacilityTypeMapper.java create mode 100644 modules/core/src/test/java/org/openlmis/core/repository/mapper/FacilityTypeMapperIT.java diff --git a/modules/core/src/main/java/org/openlmis/core/repository/mapper/FacilityTypeMapper.java b/modules/core/src/main/java/org/openlmis/core/repository/mapper/FacilityTypeMapper.java new file mode 100644 index 0000000000..0dc5181ab1 --- /dev/null +++ b/modules/core/src/main/java/org/openlmis/core/repository/mapper/FacilityTypeMapper.java @@ -0,0 +1,64 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2013 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + *   + * 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 Affero General Public License for more details. + * You should have received a copy of the GNU Affero General Public License along with this program.  If not, see http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  + */ + +package org.openlmis.core.repository.mapper; + +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.Update; +import org.openlmis.core.domain.FacilityType; +import org.springframework.stereotype.Repository; + +@Repository +public interface FacilityTypeMapper { + + @Select("SELECT * FROM facility_types WHERE LOWER(code) = LOWER(#{lower})") + public FacilityType getByCode(String code); + + @Insert({"INSERT INTO facility_types (code" + , ", name" + , ", description" + , ", levelId" + , ", nominalMaxMonth" + , ", nominalEop" + , ", displayOrder" + , ", active" + , ", createdBy" + , ", createdDate" + , ", modifiedBy" + , ", modifiedDate" + , ") VALUES (#{code}" + , ", #{name}" + , ", #{description}" + , ", #{levelId}" + , ", #{nominalMaxMonth}" + , ", #{nominalEop}" + , ", #{displayOrder}" + , ", #{active}" + , ", #{createdBy}" + , ", NOW()" + , ", #{modifiedBy}" + , ", NOW()" + , ")"}) + public void insert(FacilityType facilityType); + + @Update({"UPDATE facility_types SET code = #{code}" + , ", name = #{name}" + , ", description = #{description}" + , ", levelId = #{levelId}" + , ", nominalMaxMonth = #{nominalMaxMonth}" + , ", nominalEop = #{nominalEop}" + , ", displayOrder = #{displayOrder}" + , ", active = #{active}" + , ", modifiedBy = #{modifiedBy}" + , ", modifiedDate = NOW()" + , "WHERE id = #{id}"}) + public void update(FacilityType facilityType); +} diff --git a/modules/core/src/test/java/org/openlmis/core/repository/mapper/FacilityTypeMapperIT.java b/modules/core/src/test/java/org/openlmis/core/repository/mapper/FacilityTypeMapperIT.java new file mode 100644 index 0000000000..5b775459c5 --- /dev/null +++ b/modules/core/src/test/java/org/openlmis/core/repository/mapper/FacilityTypeMapperIT.java @@ -0,0 +1,60 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2013 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + *   + * 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 Affero General Public License for more details. + * You should have received a copy of the GNU Affero General Public License along with this program.  If not, see http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  + */ + +package org.openlmis.core.repository.mapper; + +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; +import org.openlmis.core.domain.FacilityType; +import org.openlmis.db.categories.IntegrationTests; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.transaction.TransactionConfiguration; +import org.springframework.transaction.annotation.Transactional; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.is; + +@Category(IntegrationTests.class) +@ContextConfiguration(locations = "classpath:test-applicationContext-core.xml") +@RunWith(SpringJUnit4ClassRunner.class) +@Transactional +@TransactionConfiguration(defaultRollback = true, transactionManager = "openLmisTransactionManager") +public class FacilityTypeMapperIT { + @Autowired + private FacilityTypeMapper mapper; + + @Test + public void shouldInsertAndUpdateByCodeCaseInsensitive() { + FacilityType facType = new FacilityType(); + facType.setCode("someCode"); + facType.setName("someName"); + facType.setDescription("someDescription"); + facType.setNominalMaxMonth(1); + facType.setNominalEop(1D); + facType.setDisplayOrder(1); + facType.setActive(true); + + // insert and test get by code + mapper.insert(facType); + FacilityType retFacType = mapper.getByCode("someCode"); + assertThat(retFacType, notNullValue()); + assertThat(retFacType, is(facType)); + + // update and test get by code case insensitive + retFacType.setCode("someOtherCode"); + mapper.update(retFacType); + FacilityType updatedFacType = mapper.getByCode("SOMEOTHERCODE"); + assertThat(updatedFacType, is(retFacType)); + } +} From ec27542aff2ce42fe238556f8380b8ba1c10f2a8 Mon Sep 17 00:00:00 2001 From: joshzamor Date: Mon, 7 Jul 2014 16:02:40 -0700 Subject: [PATCH 25/80] OA-34, added facility type repository and unit tests --- .../repository/FacilityTypeRepository.java | 63 +++++++++++++++++++ .../FacilityTypeRepositoryTest.java | 63 +++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 modules/core/src/main/java/org/openlmis/core/repository/FacilityTypeRepository.java create mode 100644 modules/core/src/test/java/org/openlmis/core/repository/FacilityTypeRepositoryTest.java diff --git a/modules/core/src/main/java/org/openlmis/core/repository/FacilityTypeRepository.java b/modules/core/src/main/java/org/openlmis/core/repository/FacilityTypeRepository.java new file mode 100644 index 0000000000..0f85aecf2f --- /dev/null +++ b/modules/core/src/main/java/org/openlmis/core/repository/FacilityTypeRepository.java @@ -0,0 +1,63 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2013 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + *   + * 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 Affero General Public License for more details. + * You should have received a copy of the GNU Affero General Public License along with this program.  If not, see http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  + */ + +package org.openlmis.core.repository; + +import lombok.NoArgsConstructor; +import org.openlmis.core.domain.FacilityType; +import org.openlmis.core.exception.DataException; +import org.openlmis.core.repository.mapper.FacilityTypeMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.stereotype.Repository; + +/** + * Repository for managing persistence of {@link org.openlmis.core.domain.FacilityType} entities and related operations. + */ +@Repository +@NoArgsConstructor +public class FacilityTypeRepository { + private FacilityTypeMapper mapper; + + @Autowired + public FacilityTypeRepository(FacilityTypeMapper facilityTypeMapper) { + this.mapper = facilityTypeMapper; + } + + /** + * Gets a FacilityType by it's associated code. + * @param code the code of the FacilityType. + * @return the FacilityType with the given code or null if no FacilityType exists with the given code. + */ + public FacilityType getByCode(String code) { + if(code == null) return null; + return mapper.getByCode(code); + } + + /** + * Saves the given FacilityType to persistent storage. + * @param facilityType the FacilityType to save. + * @throws java.lang.NullPointerException if facilityType is null. + * @throws DataException if unable to save facilityType. + */ + public void save(FacilityType facilityType) { + if(facilityType == null) throw new NullPointerException("FacilityType argument is null"); + + try { + if(facilityType.hasId()) mapper.update(facilityType); + else mapper.insert(facilityType); + } catch(DuplicateKeyException dke) { + throw new DataException("error.duplicate.facility.type.code", dke); + } catch(DataIntegrityViolationException dive) { + throw new DataException("error.incorrect.length", dive); + } + } +} diff --git a/modules/core/src/test/java/org/openlmis/core/repository/FacilityTypeRepositoryTest.java b/modules/core/src/test/java/org/openlmis/core/repository/FacilityTypeRepositoryTest.java new file mode 100644 index 0000000000..366c9aee10 --- /dev/null +++ b/modules/core/src/test/java/org/openlmis/core/repository/FacilityTypeRepositoryTest.java @@ -0,0 +1,63 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2013 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + *   + * 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 Affero General Public License for more details. + * You should have received a copy of the GNU Affero General Public License along with this program.  If not, see http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  + */ + +package org.openlmis.core.repository; + +import org.junit.Before; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.openlmis.core.domain.FacilityType; +import org.openlmis.core.repository.mapper.FacilityTypeMapper; +import org.openlmis.db.categories.UnitTests; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; + +@Category(UnitTests.class) +@RunWith(MockitoJUnitRunner.class) +public class FacilityTypeRepositoryTest { + + @Mock + private FacilityTypeMapper mapper; + + private FacilityTypeRepository repo; + + @Before + public void setup() { + repo = new FacilityTypeRepository(mapper); + } + + @Test + public void shouldUpdateOrInsertOnSaveBasedOnHasId() { + FacilityType facType = mock(FacilityType.class); + + // when hasId is true, update should be called + when(facType.hasId()).thenReturn(true); + repo.save(facType); + verify(mapper).update(facType); + + // when hasId is false, insert should be called + when(facType.hasId()).thenReturn(false); + repo.save(facType); + verify(mapper).insert(facType); + } + + @Test + public void shouldReturnNullWhenGetByCodeIsNull() { + assertThat(repo.getByCode(null), nullValue()); + } +} From 15a8700d4f2af6e0626b2dcef2655c96bc997da9 Mon Sep 17 00:00:00 2001 From: joshzamor Date: Mon, 7 Jul 2014 16:14:02 -0700 Subject: [PATCH 26/80] OA-34, added basic integration tests to see how DB handled null codes and empty strings. --- .../mapper/FacilityTypeMapperIT.java | 35 ++++++++++++++----- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/modules/core/src/test/java/org/openlmis/core/repository/mapper/FacilityTypeMapperIT.java b/modules/core/src/test/java/org/openlmis/core/repository/mapper/FacilityTypeMapperIT.java index 5b775459c5..d5614c8a93 100644 --- a/modules/core/src/test/java/org/openlmis/core/repository/mapper/FacilityTypeMapperIT.java +++ b/modules/core/src/test/java/org/openlmis/core/repository/mapper/FacilityTypeMapperIT.java @@ -10,6 +10,7 @@ package org.openlmis.core.repository.mapper; +import org.junit.Before; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; @@ -21,6 +22,7 @@ import org.springframework.test.context.transaction.TransactionConfiguration; import org.springframework.transaction.annotation.Transactional; +import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.is; @@ -34,10 +36,13 @@ public class FacilityTypeMapperIT { @Autowired private FacilityTypeMapper mapper; - @Test - public void shouldInsertAndUpdateByCodeCaseInsensitive() { - FacilityType facType = new FacilityType(); - facType.setCode("someCode"); + private FacilityType facType; + + @Before + public void setup() { + // seed a facility type to use for testing + facType = new FacilityType(); + facType.setCode("someCode"); // this code is used for later testing, don't change facType.setName("someName"); facType.setDescription("someDescription"); facType.setNominalMaxMonth(1); @@ -45,16 +50,30 @@ public void shouldInsertAndUpdateByCodeCaseInsensitive() { facType.setDisplayOrder(1); facType.setActive(true); - // insert and test get by code mapper.insert(facType); - FacilityType retFacType = mapper.getByCode("someCode"); + } + + @Test + public void shouldUpdateByCodeCaseInsensitive() { + // test get by code + FacilityType retFacType = mapper.getByCode(facType.getCode()); assertThat(retFacType, notNullValue()); assertThat(retFacType, is(facType)); // update and test get by code case insensitive - retFacType.setCode("someOtherCode"); + retFacType.setName("someOtherName"); mapper.update(retFacType); - FacilityType updatedFacType = mapper.getByCode("SOMEOTHERCODE"); + FacilityType updatedFacType = mapper.getByCode(facType.getCode().toUpperCase()); assertThat(updatedFacType, is(retFacType)); } + + @Test + public void shouldReturnNullIfGetByCodeIsNull() { + assertThat(mapper.getByCode(null), nullValue()); + } + + @Test + public void shouldReturnNullIfGetByCodeIsEmptyString() { + assertThat(mapper.getByCode(""), nullValue()); + } } From 41def50ab52ec949a82a63e779d83d6356e39576 Mon Sep 17 00:00:00 2001 From: joshzamor Date: Mon, 7 Jul 2014 16:18:38 -0700 Subject: [PATCH 27/80] OA-34, added null pointer test on facility type repo save operation and added duplicate code message to messages resource. --- .../openlmis/core/repository/FacilityTypeRepositoryTest.java | 5 +++++ .../openlmis-web/src/main/resources/messages_en.properties | 1 + .../openlmis-web/src/main/resources/messages_es.properties | 3 ++- .../openlmis-web/src/main/resources/messages_pt.properties | 3 ++- 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/modules/core/src/test/java/org/openlmis/core/repository/FacilityTypeRepositoryTest.java b/modules/core/src/test/java/org/openlmis/core/repository/FacilityTypeRepositoryTest.java index 366c9aee10..f68ca66b2c 100644 --- a/modules/core/src/test/java/org/openlmis/core/repository/FacilityTypeRepositoryTest.java +++ b/modules/core/src/test/java/org/openlmis/core/repository/FacilityTypeRepositoryTest.java @@ -56,6 +56,11 @@ public void shouldUpdateOrInsertOnSaveBasedOnHasId() { verify(mapper).insert(facType); } + @Test(expected = NullPointerException.class) + public void shouldThrowNullPointerExceptionOnSaveWithNull() { + repo.save(null); + } + @Test public void shouldReturnNullWhenGetByCodeIsNull() { assertThat(repo.getByCode(null), nullValue()); diff --git a/modules/openlmis-web/src/main/resources/messages_en.properties b/modules/openlmis-web/src/main/resources/messages_en.properties index 5124d759da..f25ffb6c15 100644 --- a/modules/openlmis-web/src/main/resources/messages_en.properties +++ b/modules/openlmis-web/src/main/resources/messages_en.properties @@ -171,6 +171,7 @@ error.duplicate.email = Duplicate email address error.duplicate.user.name = Duplicate User Name error.duplicate.role = Duplicate Role found error.duplicate.facility.operator.code = Duplicate Facility Operator Code +error.duplicate.facility.type.code = Duplicate Facility Type Code message.user.created.success.email.sent = User "{0} {1}" has been successfully created, password link has been sent on registered Email address message.user.updated.success = User "{0} {1}" has been successfully updated email.sent = Email sent diff --git a/modules/openlmis-web/src/main/resources/messages_es.properties b/modules/openlmis-web/src/main/resources/messages_es.properties index 671b835571..e8b03261af 100644 --- a/modules/openlmis-web/src/main/resources/messages_es.properties +++ b/modules/openlmis-web/src/main/resources/messages_es.properties @@ -1222,4 +1222,5 @@ label.programs=Programas label.geographic.levels=Niveles Geográficos error.duplicate.geographic.level.code=Duplicate Code Nivel Geographic error.duplicate.facility.operator.code=Duplicar Facilidad Código Operador -label.facility.operators=Operadores de Instalaciones \ No newline at end of file +label.facility.operators=Operadores de Instalaciones +error.duplicate.facility.type.code=Duplicar Tipo de instalación Código \ No newline at end of file diff --git a/modules/openlmis-web/src/main/resources/messages_pt.properties b/modules/openlmis-web/src/main/resources/messages_pt.properties index 43b35c4cc2..c793acc320 100644 --- a/modules/openlmis-web/src/main/resources/messages_pt.properties +++ b/modules/openlmis-web/src/main/resources/messages_pt.properties @@ -1222,4 +1222,5 @@ label.programs=Programas label.geographic.levels=Níveis Geográficos error.duplicate.geographic.level.code=Duplicate Código Geográfico Nível error.duplicate.facility.operator.code=Duplicar Facilidade Código de operador -label.facility.operators=Operadores de instalações \ No newline at end of file +label.facility.operators=Operadores de instalações +error.duplicate.facility.type.code=Duplicar Tipo de Instalação Código \ No newline at end of file From 1aa9a968adda213add10bbdab16d8b950f648470 Mon Sep 17 00:00:00 2001 From: joshzamor Date: Mon, 7 Jul 2014 16:53:04 -0700 Subject: [PATCH 28/80] OA-34, added facility type persistence handler and made small corrections to messages and upload field names. --- .../openlmis/core/domain/FacilityType.java | 4 +- .../repository/FacilityTypeRepository.java | 2 +- .../FacilityTypePersistenceHandler.java | 45 +++++++++++++++++++ .../src/main/resources/applicationContext.xml | 9 ++++ .../src/main/resources/messages_en.properties | 5 ++- .../src/main/resources/messages_es.properties | 3 +- .../src/main/resources/messages_pt.properties | 3 +- 7 files changed, 64 insertions(+), 7 deletions(-) create mode 100644 modules/core/src/main/java/org/openlmis/core/upload/FacilityTypePersistenceHandler.java diff --git a/modules/core/src/main/java/org/openlmis/core/domain/FacilityType.java b/modules/core/src/main/java/org/openlmis/core/domain/FacilityType.java index 27af7ec0c1..933827c35e 100644 --- a/modules/core/src/main/java/org/openlmis/core/domain/FacilityType.java +++ b/modules/core/src/main/java/org/openlmis/core/domain/FacilityType.java @@ -29,7 +29,7 @@ @EqualsAndHashCode(callSuper = false) public class FacilityType extends BaseModel implements Importable { - @ImportField(name="Facilty Type Code", mandatory=true) + @ImportField(name="Facility Type Code", mandatory=true) private String code; @ImportField(name="Name", mandatory=true) @@ -49,7 +49,7 @@ public class FacilityType extends BaseModel implements Importable { @ImportField(name="Display Order", mandatory=true) private Integer displayOrder; - @ImportField(name="Active", mandatory=true) + @ImportField(name="Active", mandatory=true, type="boolean") private boolean active; public FacilityType(String code) { diff --git a/modules/core/src/main/java/org/openlmis/core/repository/FacilityTypeRepository.java b/modules/core/src/main/java/org/openlmis/core/repository/FacilityTypeRepository.java index 0f85aecf2f..9fc4e74c22 100644 --- a/modules/core/src/main/java/org/openlmis/core/repository/FacilityTypeRepository.java +++ b/modules/core/src/main/java/org/openlmis/core/repository/FacilityTypeRepository.java @@ -55,7 +55,7 @@ public void save(FacilityType facilityType) { if(facilityType.hasId()) mapper.update(facilityType); else mapper.insert(facilityType); } catch(DuplicateKeyException dke) { - throw new DataException("error.duplicate.facility.type.code", dke); + throw new DataException("error.duplicate.facility.type", dke); } catch(DataIntegrityViolationException dive) { throw new DataException("error.incorrect.length", dive); } diff --git a/modules/core/src/main/java/org/openlmis/core/upload/FacilityTypePersistenceHandler.java b/modules/core/src/main/java/org/openlmis/core/upload/FacilityTypePersistenceHandler.java new file mode 100644 index 0000000000..d1d308d8d1 --- /dev/null +++ b/modules/core/src/main/java/org/openlmis/core/upload/FacilityTypePersistenceHandler.java @@ -0,0 +1,45 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2013 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + *   + * 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 Affero General Public License for more details. + * You should have received a copy of the GNU Affero General Public License along with this program.  If not, see http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  + */ + +package org.openlmis.core.upload; + +import lombok.NoArgsConstructor; +import org.openlmis.core.domain.BaseModel; +import org.openlmis.core.domain.FacilityType; +import org.openlmis.core.repository.FacilityTypeRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +@NoArgsConstructor +public class FacilityTypePersistenceHandler extends AbstractModelPersistenceHandler { + private FacilityTypeRepository repo; + + @Autowired + public FacilityTypePersistenceHandler(FacilityTypeRepository facilityTypeRepository) { + this.repo = facilityTypeRepository; + } + + @Override + protected BaseModel getExisting(BaseModel record) { + FacilityType facType = (FacilityType) record; + return repo.getByCode(facType.getCode()); + } + + @Override + protected void save(BaseModel record) { + repo.save((FacilityType) record); + } + + @Override + public String getMessageKey() { + return "error.duplicate.facility.type"; + } +} diff --git a/modules/openlmis-web/src/main/resources/applicationContext.xml b/modules/openlmis-web/src/main/resources/applicationContext.xml index af09d6bd97..8b994f3e3f 100644 --- a/modules/openlmis-web/src/main/resources/applicationContext.xml +++ b/modules/openlmis-web/src/main/resources/applicationContext.xml @@ -324,6 +324,15 @@ + + + + + org.openlmis.core.domain.FacilityType + + + + diff --git a/modules/openlmis-web/src/main/resources/messages_en.properties b/modules/openlmis-web/src/main/resources/messages_en.properties index f25ffb6c15..66c4abf5bf 100644 --- a/modules/openlmis-web/src/main/resources/messages_en.properties +++ b/modules/openlmis-web/src/main/resources/messages_en.properties @@ -171,7 +171,7 @@ error.duplicate.email = Duplicate email address error.duplicate.user.name = Duplicate User Name error.duplicate.role = Duplicate Role found error.duplicate.facility.operator.code = Duplicate Facility Operator Code -error.duplicate.facility.type.code = Duplicate Facility Type Code +error.duplicate.facility.type = Duplicate Facility Type message.user.created.success.email.sent = User "{0} {1}" has been successfully created, password link has been sent on registered Email address message.user.updated.success = User "{0} {1}" has been successfully updated email.sent = Email sent @@ -1246,4 +1246,5 @@ msg.enter.product.code.name = Enter product code or name label.program.and.schedules = Program and Schedules facilities.added.successfully = Facilities added successfully add.selected.facilities = Add selected facilities -label.facility.operators = Facility Operators \ No newline at end of file +label.facility.operators = Facility Operators +label.facility.types = Facility Types \ No newline at end of file diff --git a/modules/openlmis-web/src/main/resources/messages_es.properties b/modules/openlmis-web/src/main/resources/messages_es.properties index e8b03261af..87256b4ec1 100644 --- a/modules/openlmis-web/src/main/resources/messages_es.properties +++ b/modules/openlmis-web/src/main/resources/messages_es.properties @@ -1223,4 +1223,5 @@ label.geographic.levels=Niveles Geográficos error.duplicate.geographic.level.code=Duplicate Code Nivel Geographic error.duplicate.facility.operator.code=Duplicar Facilidad Código Operador label.facility.operators=Operadores de Instalaciones -error.duplicate.facility.type.code=Duplicar Tipo de instalación Código \ No newline at end of file +error.duplicate.facility.type=Duplicar Tipo de Instalaciones +label.facility.types=Tipos de Instalaciones \ No newline at end of file diff --git a/modules/openlmis-web/src/main/resources/messages_pt.properties b/modules/openlmis-web/src/main/resources/messages_pt.properties index c793acc320..aa1e13b906 100644 --- a/modules/openlmis-web/src/main/resources/messages_pt.properties +++ b/modules/openlmis-web/src/main/resources/messages_pt.properties @@ -1223,4 +1223,5 @@ label.geographic.levels=Níveis Geográficos error.duplicate.geographic.level.code=Duplicate Código Geográfico Nível error.duplicate.facility.operator.code=Duplicar Facilidade Código de operador label.facility.operators=Operadores de instalações -error.duplicate.facility.type.code=Duplicar Tipo de Instalação Código \ No newline at end of file +error.duplicate.facility.type=Duplicar Tipo de Instalação +label.facility.types=Tipos de Instalação \ No newline at end of file From 6412c4097b7e3527ccbcbd49606c201f57f8f187 Mon Sep 17 00:00:00 2001 From: joshzamor Date: Tue, 8 Jul 2014 11:44:11 -0700 Subject: [PATCH 29/80] OA-34, refactored facility type operations out of facility mapper and repository. Removed tests that relied on seed data to operate. Split functionality of finding a facility type by code into two, one that explicitly returns null when none are found and one that throws a specific exception. --- .../core/repository/FacilityRepository.java | 29 ++++++------ .../repository/FacilityTypeRepository.java | 36 ++++++++++++++- .../repository/mapper/FacilityMapper.java | 22 +++------ .../repository/mapper/FacilityTypeMapper.java | 14 ++++-- .../core/service/FacilityService.java | 8 +++- .../core/service/ProgramProductService.java | 10 ++--- .../repository/FacilityRepositoryTest.java | 19 +++++--- .../repository/mapper/FacilityMapperIT.java | 45 +++---------------- .../mapper/FacilityTypeMapperIT.java | 20 ++++++++- .../service/ProgramProductServiceTest.java | 10 +++-- 10 files changed, 120 insertions(+), 93 deletions(-) diff --git a/modules/core/src/main/java/org/openlmis/core/repository/FacilityRepository.java b/modules/core/src/main/java/org/openlmis/core/repository/FacilityRepository.java index 38a0b780b5..e1e814d12e 100644 --- a/modules/core/src/main/java/org/openlmis/core/repository/FacilityRepository.java +++ b/modules/core/src/main/java/org/openlmis/core/repository/FacilityRepository.java @@ -33,18 +33,28 @@ @NoArgsConstructor public class FacilityRepository { - @Autowired private FacilityMapper mapper; + private FacilityTypeRepository facilityTypeRepository; + @Autowired private CommaSeparator commaSeparator; - @Autowired private GeographicZoneRepository geographicZoneRepository; - @Autowired private FacilityOperatorRepository facilityOperatorRepository; + @Autowired + public FacilityRepository(FacilityMapper facilityMapper, + FacilityTypeRepository facilityTypeRepository, + GeographicZoneRepository geographicZoneRepository, + FacilityOperatorRepository facilityOperatorRepository) { + this.mapper = facilityMapper; + this.facilityTypeRepository = facilityTypeRepository; + this.geographicZoneRepository = geographicZoneRepository; + this.facilityOperatorRepository = facilityOperatorRepository; + } + public List getAll() { return mapper.getAll(); } @@ -96,7 +106,7 @@ private void validateAndSetFacilityType(Facility facility) { throw new DataException("error.reference.data.facility.type.missing"); String facilityTypeCode = facilityType.getCode(); - FacilityType existingFacilityType = mapper.getFacilityTypeForCode(facilityTypeCode); + FacilityType existingFacilityType = facilityTypeRepository.getByCode(facilityTypeCode); if (existingFacilityType == null) throw new DataException("error.reference.data.invalid.facility.type"); @@ -117,10 +127,6 @@ private void validateAndSetFacilityOperatedBy(Facility facility) { facility.setOperatedBy(facilityOperatorRepository.getById(operatedById)); } - public List getAllTypes() { - return mapper.getAllTypes(); - } - public Facility getHomeFacility(Long userId) { return mapper.getHomeFacility(userId); } @@ -160,13 +166,6 @@ public Facility getHomeFacilityForRights(Long userId, Right... rights) { return mapper.getHomeFacilityWithRights(userId, commaSeparateRightNames(rights)); } - public FacilityType getFacilityTypeByCode(FacilityType facilityType) { - facilityType = mapper.getFacilityTypeForCode(facilityType.getCode()); - if (facilityType == null) { - throw new DataException("error.facility.type.code.invalid"); - } - return facilityType; - } public Facility getByCode(String code) { return mapper.getByCode(code); diff --git a/modules/core/src/main/java/org/openlmis/core/repository/FacilityTypeRepository.java b/modules/core/src/main/java/org/openlmis/core/repository/FacilityTypeRepository.java index 9fc4e74c22..af85753711 100644 --- a/modules/core/src/main/java/org/openlmis/core/repository/FacilityTypeRepository.java +++ b/modules/core/src/main/java/org/openlmis/core/repository/FacilityTypeRepository.java @@ -11,6 +11,7 @@ package org.openlmis.core.repository; import lombok.NoArgsConstructor; +import org.openlmis.core.domain.Facility; import org.openlmis.core.domain.FacilityType; import org.openlmis.core.exception.DataException; import org.openlmis.core.repository.mapper.FacilityTypeMapper; @@ -19,12 +20,17 @@ import org.springframework.dao.DuplicateKeyException; import org.springframework.stereotype.Repository; +import java.util.List; + /** * Repository for managing persistence of {@link org.openlmis.core.domain.FacilityType} entities and related operations. */ @Repository @NoArgsConstructor public class FacilityTypeRepository { + + public static final String ERROR_FACILITY_TYPE_CODE_INVALID = "error.facility.type.code.invalid"; + private FacilityTypeMapper mapper; @Autowired @@ -42,6 +48,26 @@ public FacilityType getByCode(String code) { return mapper.getByCode(code); } + /** + * Gets a FacilityType by it's associated code, will throw an exception if no such code exists. + * @param code the code to find by + * @return the FacilityType with the given code + * @throws DataException if no such code exists. + */ + public FacilityType getByCodeOrThrowException(String code) throws DataException { + FacilityType facType = getByCode(code); + if(facType == null) throw new DataException(ERROR_FACILITY_TYPE_CODE_INVALID); + return facType; + } + + /** + * Gets all persisted FacilityType entities ordered by display order (ascending) and then name. + * @return all FacilityType entities. + */ + public List getAll() { + return mapper.getAll(); + } + /** * Saves the given FacilityType to persistent storage. * @param facilityType the FacilityType to save. @@ -60,4 +86,12 @@ public void save(FacilityType facilityType) { throw new DataException("error.incorrect.length", dive); } } -} + + public FacilityType getFacilityTypeByCode(FacilityType facilityType) { + facilityType = mapper.getByCode(facilityType.getCode()); + if (facilityType == null) { + throw new DataException("error.facility.type.code.invalid"); + } + return facilityType; + } +} \ No newline at end of file diff --git a/modules/core/src/main/java/org/openlmis/core/repository/mapper/FacilityMapper.java b/modules/core/src/main/java/org/openlmis/core/repository/mapper/FacilityMapper.java index 5f2994bc91..13c6c1ef0d 100644 --- a/modules/core/src/main/java/org/openlmis/core/repository/mapper/FacilityMapper.java +++ b/modules/core/src/main/java/org/openlmis/core/repository/mapper/FacilityMapper.java @@ -12,7 +12,6 @@ import org.apache.ibatis.annotations.*; import org.openlmis.core.domain.Facility; -import org.openlmis.core.domain.FacilityType; import org.springframework.stereotype.Repository; import java.util.Date; @@ -53,12 +52,6 @@ public interface FacilityMapper { @Results(value = {@Result(property = "id", column = "facilityId")}) Facility getHomeFacility(Long userId); - @Select("SELECT * FROM facility_types ORDER BY displayOrder NULLS LAST, LOWER(name)") - List getAllTypes(); - - @Select("SELECT * FROM facility_types WHERE id = #{id}") - public FacilityType getFacilityTypeById(Long id); - @Select("SELECT id FROM facility_operators WHERE LOWER(code) = LOWER(#{code})") Long getOperatedByIdForCode(String code); @@ -67,7 +60,7 @@ public interface FacilityMapper { @Result(property = "geographicZone", column = "geographicZoneId", javaType = Long.class, one = @One(select = "org.openlmis.core.repository.mapper.GeographicZoneMapper.getWithParentById")), @Result(property = "facilityType", column = "typeId", javaType = Long.class, - one = @One(select = "getFacilityTypeById")), + one = @One(select = "org.openlmis.core.repository.mapper.FacilityTypeMapper.getById")), @Result(property = "operatedBy", column = "operatedById", javaType = Long.class, one = @One(select = "org.openlmis.core.repository.mapper.FacilityOperatorMapper.getById")) }) @@ -82,7 +75,7 @@ public interface FacilityMapper { @Result(property = "geographicZone", column = "geographicZoneId", javaType = Long.class, one = @One(select = "org.openlmis.core.repository.mapper.GeographicZoneMapper.getWithParentById")), @Result(property = "facilityType", column = "typeId", javaType = Long.class, - one = @One(select = "getFacilityTypeById")), + one = @One(select = "org.openlmis.core.repository.mapper.FacilityTypeMapper.getById")), @Result(property = "operatedBy", column = "operatedById", javaType = Long.class, one = @One(select = "org.openlmis.core.repository.mapper.FacilityOperatorMapper.getById")), @Result(property = "supportedPrograms", column = "id", javaType = List.class, @@ -104,9 +97,6 @@ public interface FacilityMapper { "comment = #{comment}, enabled = #{enabled}, modifiedBy = #{modifiedBy}, modifiedDate = (COALESCE(#{modifiedDate}, NOW())) WHERE id=#{id}") void update(Facility facility); - @Select("SELECT * FROM facility_types WHERE LOWER(code) = LOWER(#{code})") - FacilityType getFacilityTypeForCode(String facilityTypeCode); - @Update({"UPDATE facilities SET enabled = #{enabled}, active=#{active}, " + "modifiedBy=#{modifiedBy}, modifiedDate = NOW() WHERE id =#{id}"}) void updateEnabledAndActiveFor(Facility facility); @@ -127,7 +117,7 @@ public interface FacilityMapper { @Results(value = { @Result(property = "geographicZone.id", column = "geographicZoneId"), @Result(property = "facilityType", column = "typeId", javaType = Long.class, - one = @One(select = "getFacilityTypeById")), + one = @One(select = "org.openlmis.core.repository.mapper.FacilityTypeMapper.getById")), @Result(property = "operatedBy", column = "operatedById", javaType = Long.class, one = @One(select = "org.openlmis.core.repository.mapper.FacilityOperatorMapper.getById")) }) @@ -152,7 +142,7 @@ List searchFacilitiesByCodeOrNameAndVirtualFacilityFlag(@Param("search "WHERE U.id = #{userId} AND RR.rightName = ANY(#{commaSeparatedRights}::VARCHAR[]) AND RA.supervisoryNodeId IS NULL"}) @Results(value = { @Result(property = "geographicZone.id", column = "geographicZoneId"), - @Result(property = "facilityType", column = "typeId", javaType = Long.class, one = @One(select = "getFacilityTypeById")), + @Result(property = "facilityType", column = "typeId", javaType = Long.class, one = @One(select = "org.openlmis.core.repository.mapper.FacilityTypeMapper.getById")), @Result(property = "operatedBy", column = "operatedById", javaType = Long.class, one = @One(select = "org.openlmis.core.repository.mapper.FacilityOperatorMapper.getById")) }) Facility getHomeFacilityWithRights(@Param("userId") Long userId, @@ -164,7 +154,7 @@ Facility getHomeFacilityWithRights(@Param("userId") Long userId, @Results(value = { @Result(property = "geographicZone.id", column = "geographicZoneId"), @Result(property = "facilityType", column = "typeId", javaType = Long.class, - one = @One(select = "getFacilityTypeById")), + one = @One(select = "org.openlmis.core.repository.mapper.FacilityTypeMapper.getById")), @Result(property = "operatedBy", column = "operatedById", javaType = Long.class, one = @One(select = "org.openlmis.core.repository.mapper.FacilityOperatorMapper.getById")) }) @@ -201,7 +191,7 @@ List getAllInDeliveryZoneFor(@Param("deliveryZoneId") Long deliveryZon @Results(value = { @Result(property = "geographicZone.id", column = "geographicZoneId"), @Result(property = "facilityType", column = "typeId", javaType = Long.class, - one = @One(select = "getFacilityTypeById")) + one = @One(select = "org.openlmis.core.repository.mapper.FacilityTypeMapper.getById")) }) List getChildFacilities(Facility facility); diff --git a/modules/core/src/main/java/org/openlmis/core/repository/mapper/FacilityTypeMapper.java b/modules/core/src/main/java/org/openlmis/core/repository/mapper/FacilityTypeMapper.java index 0dc5181ab1..26a54fd235 100644 --- a/modules/core/src/main/java/org/openlmis/core/repository/mapper/FacilityTypeMapper.java +++ b/modules/core/src/main/java/org/openlmis/core/repository/mapper/FacilityTypeMapper.java @@ -16,11 +16,16 @@ import org.openlmis.core.domain.FacilityType; import org.springframework.stereotype.Repository; +import java.util.List; + @Repository public interface FacilityTypeMapper { @Select("SELECT * FROM facility_types WHERE LOWER(code) = LOWER(#{lower})") - public FacilityType getByCode(String code); + FacilityType getByCode(String code); + + @Select("SELECT * FROM facility_types WHERE id = #{id}") + public FacilityType getById(Long id); @Insert({"INSERT INTO facility_types (code" , ", name" @@ -47,7 +52,7 @@ public interface FacilityTypeMapper { , ", #{modifiedBy}" , ", NOW()" , ")"}) - public void insert(FacilityType facilityType); + void insert(FacilityType facilityType); @Update({"UPDATE facility_types SET code = #{code}" , ", name = #{name}" @@ -60,5 +65,8 @@ public interface FacilityTypeMapper { , ", modifiedBy = #{modifiedBy}" , ", modifiedDate = NOW()" , "WHERE id = #{id}"}) - public void update(FacilityType facilityType); + void update(FacilityType facilityType); + + @Select("SELECT * FROM facility_types ORDER BY displayOrder NULLS LAST, LOWER(name)") + List getAll(); } diff --git a/modules/core/src/main/java/org/openlmis/core/service/FacilityService.java b/modules/core/src/main/java/org/openlmis/core/service/FacilityService.java index 8a31dffd40..9649893858 100644 --- a/modules/core/src/main/java/org/openlmis/core/service/FacilityService.java +++ b/modules/core/src/main/java/org/openlmis/core/service/FacilityService.java @@ -21,6 +21,7 @@ import org.openlmis.core.dto.FacilityFeedDTO; import org.openlmis.core.exception.DataException; import org.openlmis.core.repository.FacilityRepository; +import org.openlmis.core.repository.FacilityTypeRepository; import org.openlmis.core.repository.GeographicZoneRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -47,6 +48,9 @@ public class FacilityService { @Autowired private FacilityRepository facilityRepository; + @Autowired + private FacilityTypeRepository facilityTypeRepository; + @Autowired private ProgramSupportedService programSupportedService; @@ -75,7 +79,7 @@ public List getAll() { } public List getAllTypes() { - return facilityRepository.getAllTypes(); + return facilityTypeRepository.getAll(); } public List getAllZones() { @@ -158,7 +162,7 @@ public List getForUserAndRights(Long userId, Right... rights) { } public FacilityType getFacilityTypeByCode(FacilityType facilityType) { - return facilityRepository.getFacilityTypeByCode(facilityType); + return facilityTypeRepository.getByCodeOrThrowException(facilityType.getCode()); } public Facility getByCode(Facility facility) { diff --git a/modules/core/src/main/java/org/openlmis/core/service/ProgramProductService.java b/modules/core/src/main/java/org/openlmis/core/service/ProgramProductService.java index 7e9baa0e3d..cd349dce77 100644 --- a/modules/core/src/main/java/org/openlmis/core/service/ProgramProductService.java +++ b/modules/core/src/main/java/org/openlmis/core/service/ProgramProductService.java @@ -20,11 +20,11 @@ import org.openlmis.core.domain.ProgramProduct; import org.openlmis.core.domain.ProgramProductPrice; import org.openlmis.core.exception.DataException; -import org.openlmis.core.repository.FacilityRepository; +import org.openlmis.core.repository.FacilityTypeRepository; import org.openlmis.core.repository.ProgramProductRepository; import org.openlmis.core.repository.ProgramRepository; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; import java.util.List; @@ -38,7 +38,7 @@ * Exposes the services for handling ProgramProduct entity. */ -@Component +@Service @NoArgsConstructor public class ProgramProductService { @@ -55,7 +55,7 @@ public class ProgramProductService { private ProgramRepository programRepository; @Autowired - private FacilityRepository facilityRepository; + private FacilityTypeRepository facilityTypeRepository; @Autowired private ProductCategoryService categoryService; @@ -124,7 +124,7 @@ public List getByProductCode(String productCode) { public List getProgramProductsBy(String programCode, String facilityTypeCode) { FacilityType facilityType = new FacilityType(); if ((facilityTypeCode = trimToNull(facilityTypeCode)) != null) { - facilityType = facilityRepository.getFacilityTypeByCode(new FacilityType(facilityTypeCode)); + facilityType = facilityTypeRepository.getByCodeOrThrowException(facilityTypeCode); } return programProductRepository.getProgramProductsBy(programRepository.getIdByCode(trimToEmpty(programCode)), facilityType.getCode()); diff --git a/modules/core/src/test/java/org/openlmis/core/repository/FacilityRepositoryTest.java b/modules/core/src/test/java/org/openlmis/core/repository/FacilityRepositoryTest.java index fecb1e8d04..70e03bfc97 100644 --- a/modules/core/src/test/java/org/openlmis/core/repository/FacilityRepositoryTest.java +++ b/modules/core/src/test/java/org/openlmis/core/repository/FacilityRepositoryTest.java @@ -58,15 +58,14 @@ public class FacilityRepositoryTest { private FacilityMapper mapper; @Mock - private FacilityOperatorRepository facilityOperatorRepository; + private FacilityTypeRepository facilityTypeRepository; @Mock - private GeographicZoneRepository geographicZoneRepository; + private FacilityOperatorRepository facilityOperatorRepository; @Mock - private CommaSeparator commaSeparator; + private GeographicZoneRepository geographicZoneRepository; - @InjectMocks private FacilityRepository repository; private DateTime now; @@ -74,6 +73,11 @@ public class FacilityRepositoryTest { @Before public void setUp() { + repository = new FacilityRepository(mapper, + facilityTypeRepository, + geographicZoneRepository, + facilityOperatorRepository); + mockStatic(DateTime.class); now = new DateTime(2012, 10, 10, 8, 0); when(DateTime.now()).thenReturn(now); @@ -82,7 +86,8 @@ public void setUp() { geographicZone.setLevel(defaultGeographicLevel); when(geographicZoneRepository.getByCode(GEOGRAPHIC_ZONE_CODE)).thenReturn(geographicZone); when(geographicZoneRepository.getLowestGeographicLevel()).thenReturn(4); - when(mapper.getFacilityTypeForCode(FacilityBuilder.FACILITY_TYPE_CODE)).thenReturn(new FacilityType(FACILITY_TYPE_ID)); + when(facilityTypeRepository.getByCode(FacilityBuilder.FACILITY_TYPE_CODE)) + .thenReturn(new FacilityType(FACILITY_TYPE_ID)); } @Test @@ -169,7 +174,7 @@ public void shouldSetFacilityOperatorIdWhenCodeIsValid() throws Exception { public void shouldRaiseInvalidReferenceDataFacilityTypeError() throws Exception { Facility facility = make(a(defaultFacility)); facility.getFacilityType().setCode("invalid code"); - when(mapper.getFacilityTypeForCode("invalid code")).thenReturn(null); + when(facilityTypeRepository.getByCodeOrThrowException("invalid code")).thenReturn(null); expectedEx.expect(dataExceptionMatcher("error.reference.data.invalid.facility.type")); @@ -192,7 +197,7 @@ public void shouldSetFacilityTypeIdWhenCodeIsValid() throws Exception { facility.getFacilityType().setCode("valid code"); FacilityType facilityType = new FacilityType("code"); facilityType.setId(1L); - when(mapper.getFacilityTypeForCode("valid code")).thenReturn(facilityType); + when(facilityTypeRepository.getByCode("valid code")).thenReturn(facilityType); repository.save(facility); assertThat(facility.getFacilityType().getId(), is(1L)); diff --git a/modules/core/src/test/java/org/openlmis/core/repository/mapper/FacilityMapperIT.java b/modules/core/src/test/java/org/openlmis/core/repository/mapper/FacilityMapperIT.java index edcdee7f11..43497ec82d 100644 --- a/modules/core/src/test/java/org/openlmis/core/repository/mapper/FacilityMapperIT.java +++ b/modules/core/src/test/java/org/openlmis/core/repository/mapper/FacilityMapperIT.java @@ -60,6 +60,9 @@ public class FacilityMapperIT { @Autowired FacilityMapper mapper; + @Autowired + FacilityTypeMapper facilityTypeMapper; + @Autowired RequisitionGroupMapper requisitionGroupMapper; @@ -123,25 +126,6 @@ public void shouldFetchAllFacilitiesAvailable() throws Exception { assertEquals(facilities.get(1).getCode(), trz002.getCode()); } - @Test - public void shouldGetAllFacilityTypes() throws Exception { - List facilityTypes = mapper.getAllTypes(); - - assertThat(facilityTypes.size(), is(11)); - FacilityType facilityType = facilityTypes.get(0); - assertThat(facilityType.getCode(), is("lvl3_hospital")); - assertThat(facilityType.getName(), is("Lvl3 Hospital")); - assertThat(facilityType.getDescription(), is("State Hospital")); - assertThat(facilityType.getLevelId(), is(nullValue())); - assertThat(facilityType.getNominalMaxMonth(), is(3)); - assertThat(facilityType.getNominalEop(), is(0.5)); - assertThat(facilityType.isActive(), is(true)); - - for (int index = 0; index < facilityTypes.size(); index++) { - assertThat(facilityTypes.get(index).getDisplayOrder(), is(index + 1)); - } - } - @Test public void shouldGetAllParentFacilitiesByModifiedDate() { Facility facility1 = make(a(defaultFacility)); @@ -291,25 +275,6 @@ public void shouldReturnFacilityOperatorIdForCode() { assertThat(id, is(nullValue())); } - @Test - public void shouldReturnFacilityTypeForCode() { - FacilityType facilityType = mapper.getFacilityTypeForCode(FACILITY_TYPE_CODE); - assertThat(facilityType.getId(), is(1L)); - - facilityType = mapper.getFacilityTypeForCode("InValid"); - assertThat(facilityType, is(nullValue())); - } - - @Test - public void shouldReturnFacilityTypeById() { - FacilityType facilityTypeWithId = mapper.getFacilityTypeForCode(FACILITY_TYPE_CODE); - - FacilityType facilityType = mapper.getFacilityTypeById(facilityTypeWithId.getId()); - assertThat(facilityType, is(notNullValue())); - assertThat(facilityType.getId(), is(facilityTypeWithId.getId())); - assertThat(facilityType.getCode(), is(FACILITY_TYPE_CODE)); - } - @Test public void shouldUpdateEnabledAndActiveForAFacility() throws Exception { Facility facility = make(a(defaultFacility)); @@ -529,7 +494,7 @@ public void shouldGetFacilityByCode() throws Exception { GeographicZone zone1 = new GeographicZone(1000L, "Z1", "Z1", level, zone0); geographicZoneMapper.insert(zone1); - List allTypes = mapper.getAllTypes(); + List allTypes = facilityTypeMapper.getAll(); FacilityType facilityType = allTypes.get(1); Facility facility = insertFacility("CODE123", facilityType, zone1, null); @@ -882,7 +847,7 @@ public void shouldUpdateOnlyTypeAndGeoZoneInVirtualFacilities() throws Exception GeographicZone zone2 = new GeographicZone(2000L, "Z2", "Z2", level, zone1); geographicZoneMapper.insert(zone2); - List allTypes = mapper.getAllTypes(); + List allTypes = facilityTypeMapper.getAll(); FacilityType facilityType1 = allTypes.get(1); FacilityType facilityType2 = allTypes.get(2); diff --git a/modules/core/src/test/java/org/openlmis/core/repository/mapper/FacilityTypeMapperIT.java b/modules/core/src/test/java/org/openlmis/core/repository/mapper/FacilityTypeMapperIT.java index d5614c8a93..c9e6ff1b48 100644 --- a/modules/core/src/test/java/org/openlmis/core/repository/mapper/FacilityTypeMapperIT.java +++ b/modules/core/src/test/java/org/openlmis/core/repository/mapper/FacilityTypeMapperIT.java @@ -14,6 +14,7 @@ import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; +import org.openlmis.core.domain.Facility; import org.openlmis.core.domain.FacilityType; import org.openlmis.db.categories.IntegrationTests; import org.springframework.beans.factory.annotation.Autowired; @@ -22,10 +23,13 @@ import org.springframework.test.context.transaction.TransactionConfiguration; import org.springframework.transaction.annotation.Transactional; -import static org.hamcrest.CoreMatchers.nullValue; +import java.util.List; + import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.Matchers.greaterThan; @Category(IntegrationTests.class) @ContextConfiguration(locations = "classpath:test-applicationContext-core.xml") @@ -53,6 +57,14 @@ public void setup() { mapper.insert(facType); } + @Test + public void shouldGetById() { + FacilityType persistentFacType = mapper.getByCode(facType.getCode()); + FacilityType facTypeFoundById = mapper.getById(persistentFacType.getId()); + assertThat(facTypeFoundById, notNullValue()); + assertThat(facTypeFoundById, is(persistentFacType)); + } + @Test public void shouldUpdateByCodeCaseInsensitive() { // test get by code @@ -67,6 +79,12 @@ public void shouldUpdateByCodeCaseInsensitive() { assertThat(updatedFacType, is(retFacType)); } + @Test + public void shouldGetAll() { + List all = mapper.getAll(); + assertThat(all.size(), greaterThan(0)); + } + @Test public void shouldReturnNullIfGetByCodeIsNull() { assertThat(mapper.getByCode(null), nullValue()); diff --git a/modules/core/src/test/java/org/openlmis/core/service/ProgramProductServiceTest.java b/modules/core/src/test/java/org/openlmis/core/service/ProgramProductServiceTest.java index 9bd01f1b34..8568be7686 100644 --- a/modules/core/src/test/java/org/openlmis/core/service/ProgramProductServiceTest.java +++ b/modules/core/src/test/java/org/openlmis/core/service/ProgramProductServiceTest.java @@ -29,6 +29,7 @@ import org.openlmis.core.domain.ProgramProductPrice; import org.openlmis.core.exception.DataException; import org.openlmis.core.repository.FacilityRepository; +import org.openlmis.core.repository.FacilityTypeRepository; import org.openlmis.core.repository.ProgramProductRepository; import org.openlmis.core.repository.ProgramRepository; import org.openlmis.db.categories.UnitTests; @@ -65,6 +66,9 @@ public class ProgramProductServiceTest { @Mock private FacilityRepository facilityRepository; + @Mock + private FacilityTypeRepository facilityTypeRepository; + @Mock private ProductCategoryService categoryService; @@ -301,13 +305,13 @@ public void shouldGetAllProgramProductsByProgramCodeAndFacilityTypeCode() { List expectedProgramProducts = new ArrayList<>(); when(programRepository.getIdByCode("P1")).thenReturn(10L); FacilityType warehouse = new FacilityType("warehouse"); - when(facilityRepository.getFacilityTypeByCode(warehouse)).thenReturn(warehouse); + when(facilityTypeRepository.getByCodeOrThrowException(warehouse.getCode())).thenReturn(warehouse); when(programProductRepository.getProgramProductsBy(10L, "warehouse")).thenReturn(expectedProgramProducts); List programProducts = programProductService.getProgramProductsBy(" P1", " warehouse"); assertThat(programProducts, is(expectedProgramProducts)); - verify(facilityRepository).getFacilityTypeByCode(warehouse); + verify(facilityTypeRepository).getByCodeOrThrowException(warehouse.getCode()); verify(programRepository).getIdByCode("P1"); verify(programProductRepository).getProgramProductsBy(10L, "warehouse"); } @@ -322,7 +326,7 @@ public void shouldGetAllProgramProductsByProgramCodeForNullFacilityTypeCode() { List programProducts = programProductService.getProgramProductsBy("P1", null); assertThat(programProducts, is(expectedProgramProducts)); - verify(facilityRepository, never()).getFacilityTypeByCode(any(FacilityType.class)); + verify(facilityTypeRepository, never()).getByCodeOrThrowException(null); verify(programRepository).getIdByCode("P1"); verify(programProductRepository).getProgramProductsBy(10L, null); } From 3e294fea684cd0b8383655d1645f94cee63c93c7 Mon Sep 17 00:00:00 2001 From: joshzamor Date: Tue, 8 Jul 2014 12:30:03 -0700 Subject: [PATCH 30/80] OA-35, fixed issue where created/modified fields were not being set on insert and update. --- .../mapper/FacilityOperatorMapper.java | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/modules/core/src/main/java/org/openlmis/core/repository/mapper/FacilityOperatorMapper.java b/modules/core/src/main/java/org/openlmis/core/repository/mapper/FacilityOperatorMapper.java index d9425888ba..86f434d4c8 100644 --- a/modules/core/src/main/java/org/openlmis/core/repository/mapper/FacilityOperatorMapper.java +++ b/modules/core/src/main/java/org/openlmis/core/repository/mapper/FacilityOperatorMapper.java @@ -21,10 +21,28 @@ @Repository public interface FacilityOperatorMapper { - @Insert("INSERT INTO facility_operators (code, text, displayOrder) VALUES (#{code}, #{text}, #{displayOrder})") + @Insert({"INSERT INTO facility_operators (code" + , ", text" + , ", displayOrder" + , ", createdBy" + , ", createdDate" + , ", modifiedBy" + , ", modifiedDate" + , ") VALUES (#{code}" + , ", #{text}" + , ", #{displayOrder}" + , ", #{createdBy}" + , ", NOW()" + , ", #{modifiedBy}" + , ", NOW() )"}) void insert(FacilityOperator facilityOperator); - @Update("UPDATE facility_operators SET code=#{code}, text=#{text}, displayOrder=#{displayOrder} WHERE id=#{id}") + @Update({"UPDATE facility_operators SET code = #{code}" + , ", text = #{text}" + , ", displayOrder = #{displayOrder}" + , ", modifiedBy = #{modifiedBy}" + , ", modifiedDate = NOW()" + , "WHERE id = #{id}"}) void update(FacilityOperator facilityOperator); @Select("SELECT * FROM facility_operators WHERE LOWER(code) = LOWER(#{code})") From 33cefae831aac0d957f4de84efc07e5de8d9071c Mon Sep 17 00:00:00 2001 From: joshzamor Date: Tue, 8 Jul 2014 14:29:26 -0700 Subject: [PATCH 31/80] OA-46, added mapper operations as well as an integration test to support insert, update and find by code for regimen category entities. --- .../openlmis/core/domain/RegimenCategory.java | 9 +++++- .../mapper/RegimenCategoryMapper.java | 29 +++++++++++++++++++ .../mapper/RegimenCategoryMapperIT.java | 23 +++++++++++++++ 3 files changed, 60 insertions(+), 1 deletion(-) diff --git a/modules/core/src/main/java/org/openlmis/core/domain/RegimenCategory.java b/modules/core/src/main/java/org/openlmis/core/domain/RegimenCategory.java index 8bbd1b57d5..d0e1d92aa0 100644 --- a/modules/core/src/main/java/org/openlmis/core/domain/RegimenCategory.java +++ b/modules/core/src/main/java/org/openlmis/core/domain/RegimenCategory.java @@ -14,6 +14,8 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; +import org.openlmis.upload.Importable; +import org.openlmis.upload.annotation.ImportField; /** * RegimenCategory represents category for a regimen (for eg. Adult or Child) @@ -22,10 +24,15 @@ @AllArgsConstructor @NoArgsConstructor @EqualsAndHashCode(callSuper = false) -public class RegimenCategory extends BaseModel { +public class RegimenCategory extends BaseModel implements Importable { + @ImportField(name="Regimen Category Code", mandatory=true) private String code; + + @ImportField(name="Name", mandatory=true) private String name; + + @ImportField(name="Display Order", mandatory=true) private Integer displayOrder; } diff --git a/modules/core/src/main/java/org/openlmis/core/repository/mapper/RegimenCategoryMapper.java b/modules/core/src/main/java/org/openlmis/core/repository/mapper/RegimenCategoryMapper.java index fda06b5bfc..700b8b5f42 100644 --- a/modules/core/src/main/java/org/openlmis/core/repository/mapper/RegimenCategoryMapper.java +++ b/modules/core/src/main/java/org/openlmis/core/repository/mapper/RegimenCategoryMapper.java @@ -10,7 +10,9 @@ package org.openlmis.core.repository.mapper; +import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.Update; import org.openlmis.core.domain.RegimenCategory; import org.springframework.stereotype.Repository; @@ -27,4 +29,31 @@ public interface RegimenCategoryMapper { @Select({"SELECT * FROM regimen_categories WHERE id = #{id}"}) RegimenCategory getById(Long id); + + @Select("SELECT * FROM regimen_categories WHERE LOWER(code) = LOWER(#{code})") + RegimenCategory getByCode(String code); + + @Insert({"INSERT INTO regimen_categories (code" + , ", name" + , ", displayOrder" + , ", createdBy" + , ", createdDate" + , ", modifiedBy" + , ", modifiedDate" + , ") VALUES ( #{code}" + , ", #{name}" + , ", #{displayOrder}" + , ", #{createdBy}" + , ", NOW()" + , ", #{modifiedBy}" + , ", NOW())"}) + void insert(RegimenCategory regimenCategory); + + @Update({"UPDATE regimen_categories SET code = #{code}" + , ", name = #{name}" + , ", displayOrder = #{displayOrder}" + , ", modifiedBy = #{modifiedBy}" + , ", modifiedDate = NOW()" + , "WHERE id = #{id}"}) + void update(RegimenCategory regimenCategory); } diff --git a/modules/core/src/test/java/org/openlmis/core/repository/mapper/RegimenCategoryMapperIT.java b/modules/core/src/test/java/org/openlmis/core/repository/mapper/RegimenCategoryMapperIT.java index 45ca0a15ce..61b801d828 100644 --- a/modules/core/src/test/java/org/openlmis/core/repository/mapper/RegimenCategoryMapperIT.java +++ b/modules/core/src/test/java/org/openlmis/core/repository/mapper/RegimenCategoryMapperIT.java @@ -24,6 +24,7 @@ import java.util.List; import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; import static org.junit.Assert.assertThat; @Category(IntegrationTests.class) @@ -48,4 +49,26 @@ public void shouldGetRegimenById() { RegimenCategory adultCategory = regimenCategoryMapper.getById(1L); assertThat(adultCategory.getCode(), is("ADULTS")); } + + + @Test + public void shouldInsertAndUpdateWithCaseInsensitiveCode() { + RegimenCategory regCat = new RegimenCategory(); + regCat.setCode("somecode"); + regCat.setName("someName"); + regCat.setDisplayOrder(1); + + // insert and test case insensitive get by code + regimenCategoryMapper.insert(regCat); + RegimenCategory retRegCat = regimenCategoryMapper.getByCode("SOMECODE"); + assertThat(retRegCat, notNullValue()); + assertThat(retRegCat, is(regCat)); + + // update something and test it + retRegCat.setName("some other name"); + retRegCat.setDisplayOrder(2); + regimenCategoryMapper.update(retRegCat); + RegimenCategory updatedRegCat = regimenCategoryMapper.getByCode("SOMECODE"); + assertThat(updatedRegCat, is(retRegCat)); + } } From d092b752996d80a62b0e3bdb924956cca608a931 Mon Sep 17 00:00:00 2001 From: joshzamor Date: Tue, 8 Jul 2014 14:49:39 -0700 Subject: [PATCH 32/80] OA-46, added regimen category repository and unit tests for finding by code and saving regimen categories. --- .../repository/RegimenCategoryRepository.java | 60 ++++++++++++++++ .../RegimenCategoryRepositoryTest.java | 68 +++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 modules/core/src/main/java/org/openlmis/core/repository/RegimenCategoryRepository.java create mode 100644 modules/core/src/test/java/org/openlmis/core/repository/RegimenCategoryRepositoryTest.java diff --git a/modules/core/src/main/java/org/openlmis/core/repository/RegimenCategoryRepository.java b/modules/core/src/main/java/org/openlmis/core/repository/RegimenCategoryRepository.java new file mode 100644 index 0000000000..5eda0b43f8 --- /dev/null +++ b/modules/core/src/main/java/org/openlmis/core/repository/RegimenCategoryRepository.java @@ -0,0 +1,60 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2013 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + *   + * 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 Affero General Public License for more details. + * You should have received a copy of the GNU Affero General Public License along with this program.  If not, see http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  + */ + +package org.openlmis.core.repository; + +import lombok.NoArgsConstructor; +import org.openlmis.core.domain.RegimenCategory; +import org.openlmis.core.exception.DataException; +import org.openlmis.core.repository.mapper.RegimenCategoryMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.stereotype.Repository; + +@Repository +@NoArgsConstructor +public class RegimenCategoryRepository { + private RegimenCategoryMapper mapper; + + @Autowired + public RegimenCategoryRepository(RegimenCategoryMapper regimenCategoryMapper) { + this.mapper = regimenCategoryMapper; + } + + /** + * Finds a stored RegimenCategory with the given code. + * @param code the code to find by, case insensitive + * @return the RegimenCategory with the given code, or null if no such code exists. + */ + public RegimenCategory getByCode(String code) { + if(code == null) return null; + return mapper.getByCode(code); + } + + /** + * Saves the given RegimenCategory entity if possible. + * @param regimenCategory the RegimenCategory to save. + * @throws java.lang.NullPointerException if regimenCategory is null. + * @throws org.openlmis.core.exception.DataException if unable to save. + */ + public void save(RegimenCategory regimenCategory) { + if(regimenCategory == null) throw new NullPointerException("RegimenCategory argument is null"); + + try { + if(regimenCategory.hasId()) mapper.update(regimenCategory); + else mapper.insert(regimenCategory); + } catch (DuplicateKeyException dke) { + throw new DataException("error.duplicate.regimen.category", dke); + } catch(DataIntegrityViolationException dive) { + throw new DataException("error.incorrect.length", dive); + } + } +} diff --git a/modules/core/src/test/java/org/openlmis/core/repository/RegimenCategoryRepositoryTest.java b/modules/core/src/test/java/org/openlmis/core/repository/RegimenCategoryRepositoryTest.java new file mode 100644 index 0000000000..c640b0154a --- /dev/null +++ b/modules/core/src/test/java/org/openlmis/core/repository/RegimenCategoryRepositoryTest.java @@ -0,0 +1,68 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2013 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + *   + * 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 Affero General Public License for more details. + * You should have received a copy of the GNU Affero General Public License along with this program.  If not, see http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  + */ + +package org.openlmis.core.repository; + +import org.junit.Before; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.openlmis.core.domain.RegimenCategory; +import org.openlmis.core.repository.mapper.RegimenCategoryMapper; +import org.openlmis.db.categories.UnitTests; + +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@Category(UnitTests.class) +@RunWith(MockitoJUnitRunner.class) +public class RegimenCategoryRepositoryTest { + + @Mock + private RegimenCategoryMapper mapper; + + private RegimenCategoryRepository repo; + + @Before + public void setup() { + repo = new RegimenCategoryRepository(mapper); + } + + @Test + public void shouldReturnNullIfGetByCodeGivenNull() { + assertThat(repo.getByCode(null), nullValue()); + } + + @Test + public void shouldCallInsertOnSaveWithoutId() { + RegimenCategory regCat = mock(RegimenCategory.class); + when(regCat.hasId()).thenReturn(false); + repo.save(regCat); + verify(mapper).insert(regCat); + } + + @Test + public void shouldCallUpdateOnSaveWithId() { + RegimenCategory regCat = mock(RegimenCategory.class); + when(regCat.hasId()).thenReturn(true); + repo.save(regCat); + verify(mapper).update(regCat); + } + + @Test(expected = NullPointerException.class) + public void shouldThrowNullPointerExceptionOnSaveNull() { + repo.save(null); + } +} From 61022dcfa881043411849c91fbce4703271a40ee Mon Sep 17 00:00:00 2001 From: joshzamor Date: Tue, 8 Jul 2014 15:05:26 -0700 Subject: [PATCH 33/80] OA-46, added regimen category persistence handler as well as translated messages. --- .../RegimenCategoryPersistenceHandler.java | 43 +++++++++++++++++++ .../src/main/resources/applicationContext.xml | 9 ++++ .../src/main/resources/messages_en.properties | 6 ++- .../src/main/resources/messages_es.properties | 4 +- .../src/main/resources/messages_pt.properties | 4 +- 5 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 modules/core/src/main/java/org/openlmis/core/upload/RegimenCategoryPersistenceHandler.java diff --git a/modules/core/src/main/java/org/openlmis/core/upload/RegimenCategoryPersistenceHandler.java b/modules/core/src/main/java/org/openlmis/core/upload/RegimenCategoryPersistenceHandler.java new file mode 100644 index 0000000000..9f65989c31 --- /dev/null +++ b/modules/core/src/main/java/org/openlmis/core/upload/RegimenCategoryPersistenceHandler.java @@ -0,0 +1,43 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2013 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + *   + * 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 Affero General Public License for more details. + * You should have received a copy of the GNU Affero General Public License along with this program.  If not, see http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  + */ + +package org.openlmis.core.upload; + +import org.openlmis.core.domain.BaseModel; +import org.openlmis.core.domain.RegimenCategory; +import org.openlmis.core.repository.RegimenCategoryRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class RegimenCategoryPersistenceHandler extends AbstractModelPersistenceHandler { + private RegimenCategoryRepository repo; + + @Autowired + public RegimenCategoryPersistenceHandler(RegimenCategoryRepository regimenCategoryRepository) { + this.repo = regimenCategoryRepository; + } + + @Override + protected BaseModel getExisting(BaseModel record) { + RegimenCategory regCat = (RegimenCategory) record; + return repo.getByCode(regCat.getCode()); + } + + @Override + protected void save(BaseModel record) { + repo.save((RegimenCategory) record); + } + + @Override + public String getMessageKey() { + return "error.duplicate.regimen.category"; + } +} diff --git a/modules/openlmis-web/src/main/resources/applicationContext.xml b/modules/openlmis-web/src/main/resources/applicationContext.xml index 8b994f3e3f..42479d7f29 100644 --- a/modules/openlmis-web/src/main/resources/applicationContext.xml +++ b/modules/openlmis-web/src/main/resources/applicationContext.xml @@ -333,6 +333,15 @@ + + + + + org.openlmis.core.domain.RegimenCategory + + + + diff --git a/modules/openlmis-web/src/main/resources/messages_en.properties b/modules/openlmis-web/src/main/resources/messages_en.properties index 66c4abf5bf..925f2c55d4 100644 --- a/modules/openlmis-web/src/main/resources/messages_en.properties +++ b/modules/openlmis-web/src/main/resources/messages_en.properties @@ -1247,4 +1247,8 @@ label.program.and.schedules = Program and Schedules facilities.added.successfully = Facilities added successfully add.selected.facilities = Add selected facilities label.facility.operators = Facility Operators -label.facility.types = Facility Types \ No newline at end of file +label.facility.types = Facility Types + + +error.duplicate.regimen.category = Duplicate Regimen Category +label.regimen.categories = Regimen Categories \ No newline at end of file diff --git a/modules/openlmis-web/src/main/resources/messages_es.properties b/modules/openlmis-web/src/main/resources/messages_es.properties index 87256b4ec1..9b00e4cc6b 100644 --- a/modules/openlmis-web/src/main/resources/messages_es.properties +++ b/modules/openlmis-web/src/main/resources/messages_es.properties @@ -1224,4 +1224,6 @@ error.duplicate.geographic.level.code=Duplicate Code Nivel Geographic error.duplicate.facility.operator.code=Duplicar Facilidad Código Operador label.facility.operators=Operadores de Instalaciones error.duplicate.facility.type=Duplicar Tipo de Instalaciones -label.facility.types=Tipos de Instalaciones \ No newline at end of file +label.facility.types=Tipos de Instalaciones +error.duplicate.regimen.category=Duplicar Régimen Categoría +label.regimen.categories=Régimen Categorías \ No newline at end of file diff --git a/modules/openlmis-web/src/main/resources/messages_pt.properties b/modules/openlmis-web/src/main/resources/messages_pt.properties index aa1e13b906..83d7ed72df 100644 --- a/modules/openlmis-web/src/main/resources/messages_pt.properties +++ b/modules/openlmis-web/src/main/resources/messages_pt.properties @@ -1224,4 +1224,6 @@ error.duplicate.geographic.level.code=Duplicate Código Geográfico Nível error.duplicate.facility.operator.code=Duplicar Facilidade Código de operador label.facility.operators=Operadores de instalações error.duplicate.facility.type=Duplicar Tipo de Instalação -label.facility.types=Tipos de Instalação \ No newline at end of file +label.facility.types=Tipos de Instalação +error.duplicate.regimen.category=Duplicar Regime Categoria +label.regimen.categories=Regime Categorias \ No newline at end of file From 6413a63c9cf5ca3ccc3a4a04183b0da056994b66 Mon Sep 17 00:00:00 2001 From: joshzamor Date: Tue, 8 Jul 2014 15:59:34 -0700 Subject: [PATCH 34/80] OA-46, refactored regimen categories out of regimen repository. --- .../repository/RegimenCategoryRepository.java | 10 +++++++++ .../core/repository/RegimenRepository.java | 4 ---- .../openlmis/core/service/RegimenService.java | 6 +++++- .../RegimenCategoryRepositoryTest.java | 6 ++++++ .../repository/RegimenRepositoryTest.java | 14 ------------- .../mapper/RegimenCategoryMapperIT.java | 21 +++++++++++++++++++ .../core/service/RegimenServiceTest.java | 8 +++++-- 7 files changed, 48 insertions(+), 21 deletions(-) diff --git a/modules/core/src/main/java/org/openlmis/core/repository/RegimenCategoryRepository.java b/modules/core/src/main/java/org/openlmis/core/repository/RegimenCategoryRepository.java index 5eda0b43f8..5faa875d12 100644 --- a/modules/core/src/main/java/org/openlmis/core/repository/RegimenCategoryRepository.java +++ b/modules/core/src/main/java/org/openlmis/core/repository/RegimenCategoryRepository.java @@ -19,6 +19,8 @@ import org.springframework.dao.DuplicateKeyException; import org.springframework.stereotype.Repository; +import java.util.List; + @Repository @NoArgsConstructor public class RegimenCategoryRepository { @@ -29,6 +31,14 @@ public RegimenCategoryRepository(RegimenCategoryMapper regimenCategoryMapper) { this.mapper = regimenCategoryMapper; } + /** + * Gets a list of all stored RegimenCategory entities. + * @return an sorted list by display order and then name, ascending. + */ + public List getAll() { + return mapper.getAll(); + } + /** * Finds a stored RegimenCategory with the given code. * @param code the code to find by, case insensitive diff --git a/modules/core/src/main/java/org/openlmis/core/repository/RegimenRepository.java b/modules/core/src/main/java/org/openlmis/core/repository/RegimenRepository.java index 6359af8dc2..faededc069 100644 --- a/modules/core/src/main/java/org/openlmis/core/repository/RegimenRepository.java +++ b/modules/core/src/main/java/org/openlmis/core/repository/RegimenRepository.java @@ -36,10 +36,6 @@ public List getByProgram(Long programId) { return mapper.getByProgram(programId); } - public List getAllRegimenCategories() { - return regimenCategoryMapper.getAll(); - } - public void save(List regimens, Long userId) { for (Regimen regimen : regimens) { regimen.setModifiedBy(userId); diff --git a/modules/core/src/main/java/org/openlmis/core/service/RegimenService.java b/modules/core/src/main/java/org/openlmis/core/service/RegimenService.java index 16f6e90a6c..974a5c3607 100644 --- a/modules/core/src/main/java/org/openlmis/core/service/RegimenService.java +++ b/modules/core/src/main/java/org/openlmis/core/service/RegimenService.java @@ -12,6 +12,7 @@ import org.openlmis.core.domain.Regimen; import org.openlmis.core.domain.RegimenCategory; +import org.openlmis.core.repository.RegimenCategoryRepository; import org.openlmis.core.repository.RegimenRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -28,6 +29,9 @@ public class RegimenService { @Autowired RegimenRepository repository; + @Autowired + private RegimenCategoryRepository regimenCategoryRepository; + @Autowired ProgramService programService; @@ -40,7 +44,7 @@ public List getByProgram(Long programId) { } public List getAllRegimenCategories() { - return repository.getAllRegimenCategories(); + return regimenCategoryRepository.getAll(); } } diff --git a/modules/core/src/test/java/org/openlmis/core/repository/RegimenCategoryRepositoryTest.java b/modules/core/src/test/java/org/openlmis/core/repository/RegimenCategoryRepositoryTest.java index c640b0154a..018bc45f9e 100644 --- a/modules/core/src/test/java/org/openlmis/core/repository/RegimenCategoryRepositoryTest.java +++ b/modules/core/src/test/java/org/openlmis/core/repository/RegimenCategoryRepositoryTest.java @@ -65,4 +65,10 @@ public void shouldCallUpdateOnSaveWithId() { public void shouldThrowNullPointerExceptionOnSaveNull() { repo.save(null); } + + @Test + public void shouldUseGetAllInMapper() { + repo.getAll(); + verify(mapper).getAll(); + } } diff --git a/modules/core/src/test/java/org/openlmis/core/repository/RegimenRepositoryTest.java b/modules/core/src/test/java/org/openlmis/core/repository/RegimenRepositoryTest.java index 2e6357588e..a47a9eadb1 100644 --- a/modules/core/src/test/java/org/openlmis/core/repository/RegimenRepositoryTest.java +++ b/modules/core/src/test/java/org/openlmis/core/repository/RegimenRepositoryTest.java @@ -17,7 +17,6 @@ import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import org.openlmis.core.domain.Regimen; -import org.openlmis.core.domain.RegimenCategory; import org.openlmis.core.repository.mapper.RegimenCategoryMapper; import org.openlmis.core.repository.mapper.RegimenMapper; import org.openlmis.db.categories.UnitTests; @@ -27,7 +26,6 @@ import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; -import static org.mockito.Matchers.isNotNull; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -93,16 +91,4 @@ public void shouldGetRegimenByProgram() { assertThat(regimens, is(expectedRegimens)); verify(mapper).getByProgram(programId); } - - @Test - public void shouldGetAllRegimenCategories() { - List expectedRegimenCategories = new ArrayList<>(); - when(regimenCategoryMapper.getAll()).thenReturn(expectedRegimenCategories); - - List regimenCategories = repository.getAllRegimenCategories(); - - assertThat(regimenCategories, is(expectedRegimenCategories)); - verify(regimenCategoryMapper).getAll(); - } - } diff --git a/modules/core/src/test/java/org/openlmis/core/repository/mapper/RegimenCategoryMapperIT.java b/modules/core/src/test/java/org/openlmis/core/repository/mapper/RegimenCategoryMapperIT.java index 61b801d828..9aa1b36008 100644 --- a/modules/core/src/test/java/org/openlmis/core/repository/mapper/RegimenCategoryMapperIT.java +++ b/modules/core/src/test/java/org/openlmis/core/repository/mapper/RegimenCategoryMapperIT.java @@ -21,10 +21,13 @@ import org.springframework.test.context.transaction.TransactionConfiguration; import org.springframework.transaction.annotation.Transactional; +import java.util.Collections; +import java.util.Comparator; import java.util.List; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.Matchers.greaterThan; import static org.junit.Assert.assertThat; @Category(IntegrationTests.class) @@ -44,6 +47,24 @@ public void shouldGetAllRegimenCategories() { assertThat(regimenCategories.get(0).getCode(), is("ADULTS")); } + @Test + public void shouldGetAllRegimenCategoriesSorted() { + List all = regimenCategoryMapper.getAll(); + assertThat(all.size(), greaterThan(1)); + + // manually sort by display order and then name + Collections.sort(all, new Comparator() { + public int compare(RegimenCategory o1, RegimenCategory o2) { + int onDispOrd = o1.getDisplayOrder().compareTo(o2.getDisplayOrder()); + if(onDispOrd != 0) return onDispOrd; + return o1.getName().compareTo(o2.getName()); + } + }); + + List allFromDb = regimenCategoryMapper.getAll(); + assertThat(allFromDb, is(all)); + } + @Test public void shouldGetRegimenById() { RegimenCategory adultCategory = regimenCategoryMapper.getById(1L); diff --git a/modules/core/src/test/java/org/openlmis/core/service/RegimenServiceTest.java b/modules/core/src/test/java/org/openlmis/core/service/RegimenServiceTest.java index 77831a1e59..5b9af7d1be 100644 --- a/modules/core/src/test/java/org/openlmis/core/service/RegimenServiceTest.java +++ b/modules/core/src/test/java/org/openlmis/core/service/RegimenServiceTest.java @@ -18,6 +18,7 @@ import org.mockito.runners.MockitoJUnitRunner; import org.openlmis.core.domain.Regimen; import org.openlmis.core.domain.RegimenCategory; +import org.openlmis.core.repository.RegimenCategoryRepository; import org.openlmis.core.repository.RegimenRepository; import java.util.ArrayList; @@ -37,6 +38,9 @@ public class RegimenServiceTest { @Mock ProgramService programService; + @Mock + private RegimenCategoryRepository regimenCategoryRepository; + @InjectMocks RegimenService service; @@ -72,12 +76,12 @@ public void shouldGetRegimensByProgram() { @Test public void shouldGetAllRegimenCategories() { List expectedRegimenCategories = new ArrayList<>(); - when(repository.getAllRegimenCategories()).thenReturn(expectedRegimenCategories); + when(regimenCategoryRepository.getAll()).thenReturn(expectedRegimenCategories); List regimenCategories = service.getAllRegimenCategories(); assertThat(regimenCategories, is(expectedRegimenCategories)); - verify(repository).getAllRegimenCategories(); + verify(regimenCategoryRepository).getAll(); } } From 05ccd1486a10efb252adf00dbf5c5111b3c09212 Mon Sep 17 00:00:00 2001 From: joshzamor Date: Tue, 5 Aug 2014 15:39:00 -0700 Subject: [PATCH 35/80] Hot fix to complete import configuration bug with validate methods of dosage units and product forms returning bools which were then automagically JSONized. This has been converted to using runtime exceptions (unfortunatly) for now as it the existing pattern. --- .../java/org/openlmis/core/domain/DosageUnit.java | 9 ++++++--- .../java/org/openlmis/core/domain/ProductForm.java | 13 +++++++++---- .../main/java/org/openlmis/core/domain/Program.java | 1 + .../core/repository/DosageUnitRepository.java | 2 +- .../core/repository/ProductFormRepository.java | 4 ++-- 5 files changed, 19 insertions(+), 10 deletions(-) diff --git a/modules/core/src/main/java/org/openlmis/core/domain/DosageUnit.java b/modules/core/src/main/java/org/openlmis/core/domain/DosageUnit.java index 068787dd66..a5d421f307 100644 --- a/modules/core/src/main/java/org/openlmis/core/domain/DosageUnit.java +++ b/modules/core/src/main/java/org/openlmis/core/domain/DosageUnit.java @@ -14,6 +14,7 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; +import org.openlmis.core.exception.DataException; import org.openlmis.upload.Importable; import org.openlmis.upload.annotation.ImportField; @@ -33,9 +34,11 @@ public class DosageUnit extends BaseModel implements Importable { /** * Validation method for an instantiated DosageUnit. A valid dosage unit has a code and a display order. - * @return true if this dosage unit is a defined well (valid), false otherwise. + * @throws DataException if this dosage unit is not defined well. */ - public boolean isValid() { - return code != null && code.length() > 0 && displayOrder > 0; + public void isValid() { + if (code == null + || code.length() == 0 + || displayOrder <= 0 ) throw new DataException("error.reference.data.missing"); } } diff --git a/modules/core/src/main/java/org/openlmis/core/domain/ProductForm.java b/modules/core/src/main/java/org/openlmis/core/domain/ProductForm.java index bb8c64981f..fbf69190cf 100644 --- a/modules/core/src/main/java/org/openlmis/core/domain/ProductForm.java +++ b/modules/core/src/main/java/org/openlmis/core/domain/ProductForm.java @@ -13,6 +13,7 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; +import org.openlmis.core.exception.DataException; import org.openlmis.upload.Importable; import org.openlmis.upload.annotation.ImportField; @@ -29,9 +30,13 @@ public class ProductForm extends BaseModel implements Importable { @ImportField(name="Display Order", mandatory=true) private Integer displayOrder; - public boolean isValid() { - return code != null - && code.length() > 0 - && displayOrder != null; + /** + * Validates this product form. + * @throws DataException if this object is not well-formed. + */ + public void isValid() { + if (code == null + || code.length() == 0 + || displayOrder == null) throw new DataException("error.reference.data.missing"); } } diff --git a/modules/core/src/main/java/org/openlmis/core/domain/Program.java b/modules/core/src/main/java/org/openlmis/core/domain/Program.java index 1973ba767b..1fbd8db075 100644 --- a/modules/core/src/main/java/org/openlmis/core/domain/Program.java +++ b/modules/core/src/main/java/org/openlmis/core/domain/Program.java @@ -14,6 +14,7 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; +import org.codehaus.jackson.annotate.JsonIgnore; import org.codehaus.jackson.map.annotate.JsonSerialize; import org.openlmis.upload.Importable; import org.openlmis.upload.annotation.ImportField; diff --git a/modules/core/src/main/java/org/openlmis/core/repository/DosageUnitRepository.java b/modules/core/src/main/java/org/openlmis/core/repository/DosageUnitRepository.java index 1191699368..7d0e6e7d15 100644 --- a/modules/core/src/main/java/org/openlmis/core/repository/DosageUnitRepository.java +++ b/modules/core/src/main/java/org/openlmis/core/repository/DosageUnitRepository.java @@ -39,7 +39,7 @@ public DosageUnit getExisting(DosageUnit du) { } public void insert(DosageUnit du) { - if(du.isValid() == false) throw new DataException("error.reference.data.missing"); + du.isValid(); if(getByCode(du.getCode()) != null) throw new DataException("error.duplicate.dosage.unit.code"); try { diff --git a/modules/core/src/main/java/org/openlmis/core/repository/ProductFormRepository.java b/modules/core/src/main/java/org/openlmis/core/repository/ProductFormRepository.java index 1275b165af..4a57dd4dac 100644 --- a/modules/core/src/main/java/org/openlmis/core/repository/ProductFormRepository.java +++ b/modules/core/src/main/java/org/openlmis/core/repository/ProductFormRepository.java @@ -44,7 +44,7 @@ public class ProductFormRepository { * @throws DataException if entity is invalid or already exists. */ public void insert(ProductForm pf) { - if(pf.isValid() == false) throw new DataException("error.reference.data.missing"); + pf.isValid(); if(getByCode(pf.getCode()) != null) throw new DataException("error.duplicate.dosage.unit.code"); try { @@ -60,7 +60,7 @@ public void insert(ProductForm pf) { * @throws DataException if entity is invalid. */ public void update(ProductForm pf) { - if(pf.isValid() == false) throw new DataException("error.reference.data.missing"); + pf.isValid(); try { pfMapper.update(pf); From b7852147e8111452d8252bbb93c0f5d87f30ebe2 Mon Sep 17 00:00:00 2001 From: joshzamor Date: Wed, 6 Aug 2014 13:26:03 -0700 Subject: [PATCH 36/80] Adding google translated french --- .../src/main/resources/messages_fr.properties | 1200 +++++++++++++++++ 1 file changed, 1200 insertions(+) create mode 100644 modules/openlmis-web/src/main/resources/messages_fr.properties diff --git a/modules/openlmis-web/src/main/resources/messages_fr.properties b/modules/openlmis-web/src/main/resources/messages_fr.properties new file mode 100644 index 0000000000..1a4cf80e75 --- /dev/null +++ b/modules/openlmis-web/src/main/resources/messages_fr.properties @@ -0,0 +1,1200 @@ +link.clear = Clair +label.child.coverage.total.vaccination = Vaccination totale +label.comment = Commentaire +error.agent.deleted = ASC ne peut pas être mis à jour car il a été supprimé +header.global.active = Mondiale active +report.template.error.reading.file = Erreur rapport de lecture fichier modèle +user.needs.to.enter.dependent.field = L'utilisateur doit entrer '' {0} '' pour calculer '' {1} '' +error.no.requisition.group = Le programme sélectionné ne dispose pas d'un programme associé pour créer une R & R, S'il vous plaît contactez l'administrateur à résoudre ce +right.upload = Admin - Envois +label.drop.off.facility = Déposez installation +msg.rnr.already.converted.to.order = Certains de la demande (s) ont déjà été convertis à l'ordre, s'il vous plaît essayer à nouveau. +label.column.label = étiquette de colonne +label.active = Actif +report.type.html = HTML +message.downloading.in.progress = Ne cliquez pas sur le bouton de retour, fermez la fenêtre du navigateur, ou hors ligne +link.download.csv = Télécharger CSV +label.coverage.rate = Taux de couverture +period.end.date = Date de fin (jj / mm / aaaa) +error.login.password = S'il vous plaît, entrez votre mot de passe +header.strength = Force +link.reports = Rapports +msg.rnr.approved.success = R & R a approuvé avec succès! +create.facility.coldStorageGrossCapacity = Stockage froid capacité brute +label.problem.last.visit = Y at-il eu un problème avec le réfrigérateur depuis la dernière visite? +button.remove = Supprimer +msg.rnr.not.started = Pas encore commencé +indicator.column.new.patient.count = Fa +label.associated.facility = Installation associée +label.approve.requisitions = Approuver réquisitions +regimens.saved.successfully = Schémas enregistré avec succès +label.number.low.alarms = Nombre d'événements de faible alarme depuis votre dernière visite +error.invalid.date.format = Utilisez le format de la date: MM / AAAA +description.column.product = Nom principal du produit +label.product.contraceptive.couple.years.of.protection = Couple-années de protection contraceptive +header.epi.use = EPI Information sur les actions +label.product.alternate.name = Autre nom de produit +label.order.PACKED = Comble +placeholder.description.optional = Description (facultatif) +create.role.mixed.warning = Remarque: Les différents rôles ne peuvent pas être un mélange de ces types +header.epi.use.startingStock = Stock premier jour du mois +error.period.without.schedule = Période ne peut être sauvé sans son annexe mère. +error.reference.data.invalid.product.group = Données de référence valides »Groupe de produits» +label.refrigerator.working.correctly = Était-ce fonctionne correctement lorsque vous avez quitté? (2-8 ° C) +label.status = Statut +view.label = Voir +label.select.zone = --Select Zone-- +right.fulfillment.manage.pod = Ordre - Gérer POD +option.value.facility.name = Nom de l'établissement +label.product.record.last.updated = fiche du produit Dernière mise à jour +create.facility.electricityAvailable = Électricité disponible à l'installation? +label.select.geographic.zone = --Select Géographique Zone-- +placeholder.supervisory.node.search = Entrez le nom de nœud de surveillance +delete.facility.program.confirm = Programme soutenu "{0}" sera retiré de cette facilité. +button.authorize = Autoriser +create.user.homeRoles.delete.warning = Tous les rôles affectés à ce programme pour l'utilisateur seront supprimés +msg.pod.submit.success = Preuve de livraison soumis avec succès +label.delivery.zone.warehouses = Entrepôts de Zone de livraison +link.select.facility = (Sélectionner l'établissement) +label.refrigerator.temperature = Quelle a été la température du réfrigérateur quand vous êtes arrivé? +delete.facility.program.header = Suppression d'un programme soutenu +report.template.name.already.exists = Rapport avec le même nom existe déjà +message.setProgramStartDate = Définir la date de début du programme +label.new.patient.count = Nouveau comte patients +label.coverage.mif = MIF 15-49 ans +label.verified.by = Vérifié par +create.user.homeFacility.roles = Rôles Accueil des installations +button.use.all.calculated = Remettre tout aux normes ISA calculées +error.period.without.start.date = Période ne peut être sauvé sans sa date de début. +rnr.already.approved = R & R a déjà approuvé +error.delivery.zone.program.combination = Zone et la combinaison de programme est déjà sélectionné +error.invalid.regimen = Régime incorrectes ont été trouvées +period.header.startDate = Date de début +error.drop.off.facility.not.present = Déposer le code de l'établissement n'est pas présent +label.coverage.adult = Couverture des adultes +label.latitude.value = La valeur doit être numérique et à portée -999,99999 à 999,99999 (5 décimales) +error.duplicate.product.group.code = Dupliquer le produit Code de groupe +label.missing.values = S'il vous plaît remplir toutes les valeurs requises +create.facility.removeProgram = et les heures; +placeholder.reenter.password = Répéter le nouveau mot de passe +header.configure.order.number = Configurez numéro de commande +message.user.updated.success = Utilisateur "{0} {1}" a été mis à jour avec succès +formula.column.cost = V * T +indicator.column.quantity.requested = J +label.unavailable.funds = Par des fonds d'indemnités journalières ou de carburant ne sont pas disponibles +label.product.form = forme de produit +message.no.distribution.initiated = Aucune distribution en cache +formula.column.quantity.dispensed = A + B (+/-) D - E +label.delivery.zone.members = Zone de livraison Membres +error.duplicate.product.form.code = Dupliquer le produit Code de formulaire trouvé +error.regimens.none.selected = Au moins une colonne doit être vérifié +label.ideal.quantity = Idéal Quantité +upload.file.empty = Le fichier est vide +message.requisition.group.created.success = Demande Groupe "{0}" créé avec succès +right.create.requisition = Réquisition - Créer +create.role.reportRights = les droits de rôle +msg.no.records.found = Pas de documents trouvés +placeholder.quantity = Quantité +label.gas.problem = Fuite de gaz ou un problème de canalisation de gaz +file.invalid.position = Numéro de position ne peut être vide ou zéro pour un champ inclus +header.quantity.ordered = Quantité commandée +indicator.column.quantity.dispensed = C +password.reset.email.body = Salut {0} {1}, \ n \ n \ n Votre nom d'utilisateur est: {2} \ n \ n \ n S'il vous plaît utilisez le lien pour réinitialiser le mot de passe. {3} \ n \ n \ n Merci +label.manage.distribution = Gérer une distribution +label.facility.count = Installations comptent +create.facility.name = Nom de l'établissement +label.requisition.group = Groupes réquisition +label.product.special.transport.instructions = Instructions de transport spéciaux +label.select.type = --Select Type-- +link.edit = Modifier +label.supervised.Facility = Mes installations supervisées +label.column.source.user.input = Entrée utilisateur +create.user.firstName = Prénom +label.visible = Visible +schedule.without.name = Annexe ne peut être sauvé sans son nom. +label.drop.down.select = - Sélectionnez - +msg.no.matches.found = Pas de résultat +label.observations = Observations +error.duplicate.delivery.zone.program = Dupliquer le code de la zone de livraison et le code du programme de combinaison trouvée +label.rnr.status = R & R Etat +error.program.not.push = Le programme ne prend pas en charge le mécanisme «push» +report.template.error.file.missing = Fichier modèle est obligatoire pour la création de rapport +create.user.deleteAdminRoles = Vous avez choisi de supprimer ce rôle, vous êtes sûr? +message.role.updated.success = "{0}" mise à jour avec succès +create.facility.programType = Type de programme +right.manage.distribution = Allocation - Gérer la distribution +report.name.label = Nom du rapport +error.select.roles = S'il vous plaît sélectionner rôles +error.schedule.not.found = Planifiez pas trouvé +message.arithmeticValidation.status = Quantité totale consommée (C) et le stock disponible (E) sont configurés comme entrées de l'utilisateur. Validation des entrées (C = A + B +/- D-E) est +label.facility.operators = exploitants d'installations +label.distribution.legend.synchronized = Synchronisé +right.view.requisition = Réquisition - Voir +create.facility.comments = Commentaires +error.upload.network.server.down = Téléchargez échoué: Réseau / Erreur de serveur +error.period.invalid.dates = Période Date de fin ne peut pas être antérieure à Date de début. +message.no.facility.available = Programme "{0}" n'est pas supportée par un établissement dans la zone de livraison "{1}" +schedule.add.period = Ajouter Période +create.facility.setFacilityActive = Voulez-vous mettre installation comme actif? +error.invalid.agent.code = Code de l'agent invalide +link.orders = Ordres +user.password.reset.token.invalid = Ce lien n'est plus valide +msg.one.match = 1 correspondance trouvée pour '{0}' +message.success.pod.updated = POD mis à jour avec succès +label.category = Catégorie +indicator.column.reason.for.requested.quantity = W +label.not.recorded = NR +label.requisition.group.member = Membres Demande Groupe +delete.refrigerator.readings.header = Supprimer Réfrigérateur +button.add = Ajouter +label.isa.formula.modal.minimum.value = Valeur minimale +create.user.role = Rôle +header.supervisory.node.name = Nom de nœud de surveillance +description.column.dispensing.unit = Unité de distribution de ce produit +label.initiate.requisition = Initier rapport et de la réquisition +label.product.store.at.room.temperature = Entreposer à la température ambiante +email.sent = Courriel envoyé +label.no.losses.adjustment = Il n'y a pas de pertes et ajustements +label.distribution.deliveryZone = Zone de Livraison +header.pack.size = Taille du paquet +error.supervisory.node.not.top.node = Superviser nœud n'est pas le nœud Haut +header.parent.node = nœud parent +option.value.geo.zone.parent = Géographique parent de la zone +message.reset.password = Réinitialiser le mot de passe +upload.record.error = {0} dans la notice n ° "{1}" +error.duplicate.requisition.group.code = Dupliquer Demande Code de groupe trouvé +error.supervisory.node.invalid = Invalide de surveillance Code Node +label.no.category = Non catégorisé +header.packed.date = Emballé date +placeholder.code = Code +error.duplicate.facility.type = Dupliquer le type d'établissement +message.success.agent.created = ASC créé avec succès +label.isa.header = Valeurs ISA pour +enable.user.confirm = Utilisateur "{0} {1}" sera activée dans le système +label.product.forms = Formes du produit +error.duplicate.requisition.group.program.schedule = Dupliquer le code de groupe de commande et de code de programme Regroupement trouvé +button.retry = Refaire +label.adjustment.quantity = quantité de réglage +formula.column.stock.in.hand = A + B (+/-) D-C +right.manage.user = Administrateur - Gérer les utilisateurs +error.schedule.code.exist = Une annexe au présent code existe déjà +message.user.created.success.email.sent = Utilisateur "{0} {1}" a été créé avec succès, lien de mot de passe a été envoyé sur l'adresse email enregistrée +button.apply.nr.all = Appliquer NR à tous les domaines +warehouse.header = Entrepôt +label.coverage.children = La couverture des enfants +create.facility.electronicDAR = Facilité a DAR électronique? +label.record.data = Enregistrement des données +header.shipped.date = Date d'expédition +menu.header.supervisory.nodes = Nœuds de surveillance +error.restapi.invalid.order = Numéro de commande non valide +label.period.end.date = Fin de la période Date de +label.product.type = Type de produit +label.average.wastage.rate = Facteur gaspillage +shipment.file.configuration.success = configuration de fichier de l'expédition enregistrée avec succès! +label.coverage.health.center = Centre de santé +label.delivery.zones = Zones de livraison +label.coverage.opened.vials.wastage.rate = Ouvert flacons gaspillage taux +email.check.message = S'il vous plaît vérifier votre e-mail et cliquez sur le lien du mot de passe de réinitialisation. +description.column.remarks = Toutes remarques supplémentaires +label.add.facility.approved.products = Ajouter article (s) pour {0} à {1} +add.selected.facilities = Ajouter des installations sélectionnées +link.order.file.template = Afin fichier +error.duplicate.code.requisition.group = Dupliquer Demande Code du groupe +indicator.column.dispensing.unit = U +regimen.reporting.patients.remarks = Remarques +programProduct.invalid.current.price = Prix ​​non valide par paquet +label.search.user = Recherche utilisateur +header.ship.date = Date d'expédition +create.facility.coldStorageCapacity.value = La valeur doit être numérique et moins de 9999.9999 +label.no.facility.selected = Aucune installation sélectionnée +label.rnr.type.suffix = Régulier / urgence suffixe (R / E) +label.facility.name = Nom de l'établissement +msg.records.found = {0} résultat (s) trouvé +error.duplicate.role = Rôle Duplicate trouvé +create.report.reportName = Nom du rapport +label.product.group = Groupe de produits +msg.rnr.submitted.success = R & R soumis avec succès! +label.product.prices.program = Prix ​​des produits par programme +msg.rnr.current.period.already.submitted = R & R pour la période actuelle a déjà présenté +button.test.calculation = Calcul de test +menu.header.facilities = Aménagements +link.admin.configure = Configurez +label.visit.date = Date de visite +report.template.extra.properties = Biens non identifié trouvé pour le paramètre "{0}" +error.invalid.pack.size = Pack de taille non valide +message.no.product.groups = Aucun produit n'a été ajouté +label.coverage.opened.vials = Les flacons ouverts +invalid.quantity.shipped = Quantité expédiée ne peut pas être plus de 8 caractères +label.program = Programme +msg.rnr.authorized.success = R & R autorisé avec succès! +label.latitude = Latitude +label.date = Date +search.facility.header = Moteur de recherche +error.page.not.found = Page non trouvée +product.code.invalid = Code produit valide +create.facility.basicInformation = Informations de base +label.requisition = Rapport et de la réquisition pour +label.requisition.facilityType = Facility type: +budget.file.configuration.success = configuration du fichier de budget sauvegardé avec succès! +msg.rnr.previous.pending = R & R précédent attente +button.view.load.amount = Voir quantités de charge +label.initiated.distributions = Distributions initiés +create.facility.internetConnection = Établissement a une connexion Internet? +budget.start.date.invalid = Période indéterminée pour date de début {0} pour l'installation {1}, {2} programme en nombre record {3} +label.map.delivery.zone.program.schedules = Carte des zones de livraison des horaires du programme +label.supervisory.nodes = Les nœuds de surveillance +budget.allocated.budget.invalid = Montant du budget non valide {0} trouvée dans nombre record {1} +label.products = Produits +error.enter.number.only = S'il vous plaît entrer le numéro que +label.information.systems = Systèmes d'information +error.rnr.required.fields.missing = S'il vous plaît remplir les champs en surbrillance sur la forme de R & R avant de soumettre +facility.exists.for.program.in.multiple.zones = Code site existe pour le même programme dans plusieurs zones de livraison +placeholder.email = Email +msg.rnr.converted.to.order = La demande (s) ont été convertis avec succès à l'ordre +placeholder.type.comment = Tapez votre commentaire ici +formula.column.max.stock.quantity = P * MaxMonthsStock +header.allocatedBudget = Budget alloué +error.shipment.file = Erreur envoi de fichiers +header.product.name = Nom du produit +create.role.description = Description de rôle +button.apply.filters = Appliquer +message.product.updated.success = Produit "{0}" correctement mis à jour +create.facility.otherInformation = Autres informations +create.user.lastName = Nom de famille +error.regimen.null.label = étiquettes de colonne ne peuvent pas être vide +label.isa.doses.of = doses de +program.facility.rights = Programme et des installations à base de droit +header.replaced.product.code = Code du produit remplacé +link.requisition.view = Voir +label.egp.fault = Électrique / gaz / essence faute +label.no.products = Pas de produits. +label.total.shipped.packs = Total des Packs expédiés +error.schedule.not.exists = Horaire code n'existe pas +template.header.label = Étiquette +create.role.assignRights = Droits cédés +placeholder.select.product.group = Group-- produit --Select +message.facility.created.success = Installation "{0}" créé avec succès +header.max.months.of.stock = Max mois de stock +indicator.column.max.stock.quantity = H +label.product.pack.width = Paquet de largeur (cm) +error.duplicate.serial.number = Dupliquer Identifiant / Numéro de série +label.overridden.isa = Surcharge de ISA +create.facility.gln = GLN +right.approve.requisition = Réquisition - Approuver +period.start.date = Date de début (jj / mm / aaaa) +placeholder.select.product = - Sélectionnez un produit - +label.sync = Sync +label.sequence.code = Code Sequence +delete.refrigerator.readings.confirm = Êtes-vous sûr de vouloir effacer le réfrigérateur? +option.value.program = Programme +message.no.refrigerators.added = Pas de réfrigérateurs ajoutés +error.facility.allocation.product.save = Erreur de sauvegarde des produits de répartition de l'installation pour le programme +label.product.store.refrigerated = Conserver au réfrigérateur +regimen.reporting.patients.initiated.treatment = Nombre de patients à être initié le traitement +label.select.all = Tous +supported.programs.invalid = Date de début est un must pour le programme actif +create.user.officePhone = Téléphone au bureau +label.inactive = Inactif +right.manage.supply.line = Admin - Gérer Supply Lines +error.rnr.authorization = R & R déjà autorisé +label.coverage.full = La couverture complète +create.facility.activateFacility = Activer l'installation +header.parameter = Paramètre +option.value.supervisory.node = Noeud de surveillance +error.quantity.consumed.negative = Quantité totale consommée est calculée à être négatif, s'il vous plaît valider les entrées +right.manage.geo.zone = Admin - Gérer zone géographique +label.date.range = Date de gamme +label.visit.information = Visitez Info / Observations +formula.column.amc = (N / M + Ng-1 / M + ... Ng- (g-1) / M) / g +header.epi.use.distributed = Distribué +label.select.program.supported = --Select Programme Supported-- +formula.column.total = A + B +label.operator.error = erreur de l'opérateur +label.children.age.group.zero.eleven.months = 0-11 mois +header.program.code = Le code du programme +label.facility.enabled = Activé +label.other = Autre +label.request = Demande +label.data.type = Type de données +error.geo.zone.not.at.lowest.level = Code géographique de la zone doit être au niveau administratif le plus bas dans la hiérarchie +label.change.password = Changer le mot de passe +label.name = Nom +button.search = Recherche +disable.facility.confirm = "{0}" / "{1}" sera désactivé sur le système. +error.redundant.warehouse = Entrepôt redondante spécifiée +error.user.needs.to.enter.requested.quantity.reason = Si '' {0} '' s'affiche, puis '' {1} '' doit également être affichée +message.no.facility.selected = Aucune installation sélectionnée +error.duplicate.delivery.zone.member = Code de la zone de livraison en double et le code de combinaison membres trouvés +msg.budget.not.allocated = Non attribué +no.program.mapped.for.delivery.zone = Pas de programme (s) mappé pour les zones de livraison +budget.allocated.invalid = Montant du budget non valide {0} trouvée dans nombre record {1} +create.user.allocationRoles = Zones de livraison +error.upload.invalid.header = "Têtes non valides dans le fichier de téléchargement: {0}" +message.arithmeticValidation.toggle = Tournez {0} +label.product.manufacturer = Fabricant +label.losses.adjustments = Pertes et ajustements +msg.enter.product.code.name = Entrez le code de produit ou nom +create.facility.select.facilityType = Installation --Select type-- +placeholder.geographic.zone.search = Entrez le nom de zone géographique +message.supervisory.node.updated.success = Noeud de surveillance "{0}" mise à jour avec succès +label.number.high.alarms = Nombre d'événements de grande alarme depuis votre dernière visite +error.endDate = Date de fin doit être supérieure à Date de début +header.user.verified = Vérifié +report.type.csv = CSV +label.all = Tous +label.distribution.synchronization.progress = Synchronisez en cours ... +create.facility.serviceDeliveryPoint = point de prestation de service? +label.include = Inclure +msg.delete.facility.approved.product.confirmation = Produit "{0}" sera supprimé "{1}" et cession "{2}" +label.coverage.students.not.mif = Les étudiants non MIF +label.celcius.symbol = C +label.select.facility = --Select Facility-- +label.isa.formula.modal = Formule ISA pour +label.vehicle.id = id de véhicules +create.user.jobTitle = Titre du poste +message.supply.line.created.success = La ligne d'alimentation créé avec succès +label.warehouse.load.amount = montant de la charge d'entrepôt +indicator.column.calculated.order.quantity = Je +message.downloading.app.data = Téléchargement des données d'application ... +create.report.addNew = Ajouter un nouveau rapport +error.duplicate.product.code.program.code = Dupliquer entrée pour le code de produit et la combinaison Code de programme trouvé +label.order.RELEASED = Libéré +error.program.schedules.not.done = Mark Les horaires de tous les programmes que «Terminé» avant d'enregistrer la forme +label.newPeriod = Nouvelle période +header.active.at.program = Active au programme +label.population = Population +label.product.pack.length = Paquet de longueur (cm) +right.manage.facility.approved.products = Admin - Gérer les produits des installations Approuvé +link.logout = Déconnexion +label.refrigerators = Réfrigérateurs +label.price.per.pack = Prix ​​par paquet +header.epi.use.loss = Perte +label.coverage.workers.not.mif = Les travailleurs non MIF +create.user.delete.warning = Tous les rôles assignés à ce programme et le noeud pour l'utilisateur seront supprimés +enable.user.header = Permettre à l'utilisateur +label.product.pack.weight = Paquet de poids (cm) +message.schedule.updated.success = "{0}" mise à jour avec succès +product.category.name.duplicate = Dupliquer Catégorie Nom trouvé +label.adult.tetanus.first = 1ère dose tétanos +error.reference.data.invalid.facility.type = Invalid données de référence »Type d'installation" +create.role.mixedWarning = (Rôles individuels ne peuvent pas être un mélange des deux types) +link.product.other.information = Autres informations +label.refrigerator.serial.number = Identifiant / numéro de série +header.user.email = Email +label.rnr.add.non.full.supply = Ajouter le produit de l'offre non complet (s) +message.sync.failure.retry = Certains de ces équipements n'ont pas réussi à synchroniser. S'il vous plaît vérifier votre connexion Internet et réessayez. +programProduct.product.program.invalid = Produit non valide et la combinaison de programme +error.authentication.failed = Échec de l'authentification +error.duplicate.regimen.category = Dupliquer Régime Catégorie +user.userName.invalid = Format non valide, espaces non autorisés dans le nom d'utilisateur +period.header.totalDays = Nombre total de jours +error.duplicate.facility.code = Dupliquer Facility Code +error.duplicate.program.code = Dupliquer un code de programmation +disable.user.header = Désactiver l'utilisateur +configure.rnr.header = Configurer R & amp; Modèle R +label.apply.nr.all = Appliquer NR à tous les domaines +label.zone.total = Total Zone +label.fulfillment.based.rights = Fulfillment fondée sur les droits +report.template.error.file.invalid = Fichier téléchargé n'est pas valide +label.sign.in = Se connecter +create.facility.phone = Téléphone +msg.unable.connect.server = Impossible de se connecter au serveur +error.password.mismatch = Les mots de passe ne correspondent pas +button.show = Afficher +label.losses.total = Total +description.column.product.code = Identifiant unique pour chaque produit +option.distribution.viewLoadAmount = Voir quantité de chargement de l'entrepôt +create.role.programBasedRole = Programme et le rôle en établissement +label.product.code = Code produit / Nom: +placeholder.select.product.form = Form-- produit --Select +option.value.requisition.group = groupe de réquisition +error.rnr.template.not.defined = S'il vous plaît contacter admin pour définir modèle de R & R pour ce programme +header.epi.use.endingStock = Stock à la fin du mois +label.coverage.complete.vaccinated = Complètement vaccinés enfants (doses) +label.product.groups = Groupes de produits +right.manage.supervisory.node = Admin - Gérer nœud de surveillance +button.approve = Approuver +error.incorrect.length = Longueur des données incorrectes +link.administration = Administration +link.requisitions.create.authorize = Créer / Autoriser +error.duplicate.email = Adresse e-mail en double +label.full.supply = Offre complète +link.next = Suivant +label.total.cost.full.supply = Coût total pour le plein de produits d'alimentation: +error.quantity.received.invalid = Quantité reçue ne peut être vide +error.pod.already.submitted = Preuve de livraison déjà soumis +error.duplicate.programNode.combination = Programme et la combinaison de noeud est déjà sélectionné +label.requisition.convertOrder = Convertir réquisitions à la commande +label.facility.ftp.details = détails FTP Facility +label.display.order = L'ordre d'affichage +link.pod.manage = Gérer POD +delete.facility.header = Supprimer établissement +label.view.data = Voir les données +label.adult.group.vaccinations = Groupe tétanos Vaccinations (doses) +label.distribution.legend.not.started = Non commencé +missing.mandatory = Les données manquantes obligatoires dans le champ: {0} {1} {2} +label.product = Produit +error.enter.numeric.value = S'il vous plaît entrez la valeur numérique +error.invalid.product.code = {0} code produit valide +label.refrigerator.brand = Marque +error.number.of.line.items.mismatch = Les produits ne correspondent pas avec réquisition +label.dispensing.units.for.new.patients = Unités de distribution pour les nouveaux patients +placeholder.password = Mot de passe +message.distribution.already.exists = Cette distribution a déjà été initiée par "{0}" à "{1}". Voulez-vous continuer? +label.receivedBy = Reçu par +sync.distribution.header = Sync distribution +label.search.supervisory.node = Rechercher noeud de surveillance +formula.column.normalised.consumption = C * 30 / ((M * 30) -X) + (F * (Pas de doses par mois / doses par unité de distribution)) +header.quantity.returned = Quantité retournée +error.duplicate.delivery.zone = Zone de livraison en double code trouvé +incorrect.file.format = Format de fichier incorrect, le nom de colonne manquant +label.regimens = Schéma (s) +placeholder.search = Recherche ... +label.geographic.zone = Zone géographique +option.distribution.inputFacilityData = données de l'installation d'entrée +user.email.incorrect = S'il vous plaît fournir une adresse email valide +error.agent.not.virtual = Agent n'est pas une installation virtuelle +msg.rnr.save.success = R & R enregistré avec succès! +label.product.special.storage.instructions = Précautions particulières de conservation +label.product.hazardous = Hasardeux +indicator.column.amc = P +label.myFacility = Mon installation +label.confirmed.by = Confirmé par +missing.value = S'il vous plaît remplir cette valeur +label.page.of = Page {0} de +report.type.pdf = PDF +order.ftpComment.incorrect.login = Invalid credentials +error.login.username = S'il vous plaît entrer votre nom d'utilisateur +description.column.max.stock.quantity = Stock maximale calculée sur la base de consommation et max mois de stock.This est quantifié en unités de distribution +label.order.no = L'ordonnance n ° +label.dosage.units = Unités posologiques +label.receivedDate = Date de réception +comments.header = Commentaires +error.reference.data.parent.facility.virtual = installation de parent ne peut pas être installation virtuelle +label.total.cost.full.supply.items = Coût total pour les articles d'alimentation complet +label.facilities.synced = Installations synchronisés +too.many.results.found = Trop de résultats trouvés. S'il vous plaît affiner votre recherche. +label.coverage.males = Hommes +error.column.should.be.visible.if.user.input = Si '' {0} '' est entrée d'utilisateur, il doit être visible +order.ftpComment.ftpcredential.missing = FTP à manque +schedule.code.invalid = Code de programmation invalide +option.value.product = Produit +error.requisition.not.submitted = Réquisition pas encore soumis +label.vials.unit = (Flacons / unités) +button.reset.password = Réinitialiser le mot de passe +error.duplicate.geographic.zone.code = Dupliquer Code géographique Zone +create.user.primaryNotificationMethod = Méthode de notification primaire +error.duplicate.program.product.combination = Dupliquer entrée pour le code de produit et la combinaison Code de programme trouvé +label.delivered.quantity = Livré Quantité +label.select.parent.zone = --Select Zone-- mère +label.coverage = Couverture +label.column.source.calculated = Calculé +create.role.addNew = Ajouter un nouveau rôle +message.requisition.group.updated.success = Demande Groupe "{0}" correctement mis à jour +right.view.report = Rapports - Voir +account.created.email.subject = Compte créé: Reset Password +label.facility.reportingPeriod = Période de déclaration +message.error.page = Quelque chose s'est mal passé! S'il vous plaît contactez l'administrateur +disable.user.confirm = Utilisateur "{0} {1}" sera désactivé +header.product.basic.information = Informations de base +error.interdependent.fields.can.not.be.calculated = Domaines interdépendants ('' {0} '', '' {1} '') ne peuvent pas être de type Calculé en même temps +label.product.category = Catégorie de produit +label.date.modified = Date de modification +error.duplicate.user.name = Dupliquer nom d'utilisateur +create.facility.operatedBy = Exploité par +label.order.list = Liste des commandes +error.period.start.date.less.than.last.period.end.date = Date de début du délai de est plus petite que de la période précédente End Date +error.permission.denied = L'utilisateur n'a pas l'autorisation +label.order.status = Suivi de commande +error.requisition.not.initiated = Réquisition pas encore lancé +order.number.configure.success = Afin configuration du numéro enregistré avec succès +label.to = À +label.product.carton.length = longueur de Carton (cm) +menu.header.roles = Rôles +label.distribution.synchronization.status = Statut de synchronisation +message.distribution.created.success = Les données pour le sélectionné {0}, {1}, {2} a été téléchargé +header.order.date = Date de la commande +placeholder.facility.search = Entrez le code de l'établissement ou le nom +msg.no.rnr.awaiting.authorization = Pas de demandes en attente d'autorisation +error.duplicate.facility.approved.product = Installation déjà produit approuvé. +label.add.losses.adjustments = Ajouter un nouveau Perte / Ajustement +of.record.no = du n ° d'enregistrement +label.coverage.community = Communauté +label.reason.for.no.visit = Pourquoi personne ne visiter? +create.role.roleName = Nom du rôle +label.product.tracer = Traceur +enter.emailInfo = S'il vous plaît entrer votre e-mail ou nom d'utilisateur +error.invalid.hierarchy = Hiérarchie invalide +create.facility.description = Description de l'établissement +header.facility.type = Type +label.new.regimen = Nouveau Régime +label.isa.formula = Formule ISA +label.total = Total +sync.distribution.confirm = Vous avez choisi de synchroniser les données pour les installations terminées, S'il vous plaît confirmer +msg.select.atleast.one.rnr = S'il vous plaît sélectionner au moins une Demande de Conversion de l'Ordre. +button.cancel = Annuler +label.product.pack.per.carton = Paquet par carton +description.column.total = Total de début équilibre et la quantité reçue +label.collapse.all = Réduire tout +label.facility.type = Facility type +button.disable = Désactiver +link.admin.configure.edi = Fichier EDI +header.dispensing.unit = Unité de distribution +button.edit = Modifier +right.manage.role = Administrateur - Gérer les rôles +header.full.supply = Totalité des crédits +label.product.alternate.product.code = Code produit alternatif +button.save = Sauver +header.requisition.group.name = Réquisition nom du groupe +label.coverage.outreach = Sensibilisation +indicator.column.cost = Q +label.calculated.isa = ISA calculé +regimen.reporting.patients.on.treatment = Nombre de patients sous traitement +label.product.generic.name = Nom de produit générique +error.period.without.name = Période ne peut être sauvé sans son Nom. +create.facility.electronicSCC = Facilité a CSC électronique? +search.geo.zone.header = Rechercher zone géographique +placeholder.select.product.dosageUnit = --Select Dosage Unit-- +label.facility.types = Types d'installations +right.configure.rnr = Admin - Configurer les modèles de réquisition +header.change.roleType = Changer le type de rôle +link.go.online = Allez en ligne +label.select.none = Aucun +label.deliveredBy = Livré par +label.facilities.already.synced = Équipements déjà synchronisés +create.role.adminRole = Admin et opérations générales rôle +label.longitude = Longitude +user.is.disabled = L'utilisateur est désactivé. Mot de passe ne peut pas être remis à zéro +label.admin.configure.system.settings = Configurer les paramètres du système +error.facility.type.code.invalid = Invalid type d'établissement +button.convert.to.order = Convertir en commande +label.total.cost.non.full.supply.items = Coût total pour les articles d'approvisionnement non complet +label.zone = Zone +option.value.facility.code = Code site +label.or = OU +label.on = ON +label.allocation.program.rights = droits fondés programme d'allocation +error.select.delivery.zone = S'il vous plaît sélectionner une zone de livraison +label.change.facility = Changement installation +header.geographic.zone = Zone géographique +label.column.include.headers = En-têtes de colonne +label.total.tetanus = Tétanos total +header.period.start.date = Dernière date de début +label.no = Aucun +error.rnr.already.initiated = R & R déjà lancé +create.report.success = Rapport créé avec succès +link.previous = Précédent +create.facility.suppliesOthers = Fonds fournit les autres? +error.reference.data.invalid.dosage.unit = «Unité de dosage» des données de référence valides +template.save.success = Modèle sauvegardé avec succès! +label.request.program.rights = Droits fondés demande programme +label.product.packSize = Pack de taille +msg.user.restore.success = Utilisateur "{0} {1}" a été activée +label.product.alternate.pack.size = Taille de l'emballage alternatif +create.user.employeeId = Employee ID +create.facility.mandatoryStartDate = Date de début est obligatoire +label.reporting.fields = Champs de rapport +create.user.deleteAdminRoleHeader = Supprimer rôle administrateur +header.epi.use.doses = EPI Stock (doses) +header.order.number = Numéro de commande +label.order.file.prefix = fichier de commande préfixe: +msg.product.added = Produits ajoutés avec succès +label.search.products = Rechercher un produit +right.fulfillment.fill.shipment = Ordre - Remplir envoi +label.child.coverage.second.total = Total +right.authorize.requisition = Réquisition - Autoriser +label.facility.export.orders = Facilité exporte commandes? +button.submit = Soumettre +forgot.password.email.subject = Mot de passe oublié +label.reporting.right.view = Voir - +link.requisition.approve = Approuver +form.error = Il ya des erreurs dans le formulaire. S'il vous plaît de les résoudre. +label.no.transport = Transport non disponible +msg.rnr.approved.without.supervisor = Il n'y a pas superviseur chargé d'examiner et d'approuver cette R & R, S'il vous plaît contactez l'administrateur +error.restapi.delivery.already.confirmed = Livraison a déjà confirmé +link.product.programs.associated = Programmes connexes +label.isa.will.need = seront +label.allocate = Allouer +right.manage.facility = Administrateur - Gérer équipements +label.date.submitted = Date de soumission +label.program.products = Programme produit +create.facility.geographicalInformation = D'information géographique +error.duplicate.dosage.unit.code = La duplication de code unité posologique trouvé +label.coverage.pregnant.women = Femmes enceintes +message.role.created.success = "{0}" créé avec succès +menu.header.users = Utilisateurs +regimen.reporting.dataType.numeric = Numérique +label.non.full.supply.products = L'approvisionnement en produits non complet (art) +label.full.supply.products = Full Product Supply (s) +integer.value = La valeur doit être un nombre entier +label.coverage.females = Femmes +placeholder.select.category = - Choisir une catégorie - +period.header.numOfMonths = Nombre de mois +label.back.schedules = Retour aux annexes +label.facilities.synchronization.failed = Sync n'a pas +create.user.userName = Nom d'utilisateur +email.sent.message = Email envoyé! +order.ftpComment.permission.denied = Autorisations inappropriées +label.admin.configure.edi.file = Configurez EDI Format de fichier +create.facility.code = Code de l'installation +report.template.error.file.type = Fichier téléchargé ne pas avoir le type requis (jrxml) +label.order.RECEIVED = Reçu +schedule.without.code = Annexe ne peut être sauvé sans son code. +label.add.members = Ajouter des membres +link.home = Maison +create.facility.contactInformation = Coordonnées +messages.commentBox.noComment = Aucun commentaire n'a encore été ajouté. +label.from = À partir de +error.stock.on.hand.negative = Le stock disponible est calculé comme étant négative, s'il vous plaît valider les entrées +error.requisition.group.not.exist = Demande Groupe n'existe pas +label.in = dans +header.parent = Mère +invalid.product.codes = Codes de produit non valide {0} +label.title = Titre +msg.no.period.available = Aucune période courant défini. S'il vous plaît contactez l'administrateur. +header.name = Nom +label.what.problems = Quels sont les problèmes? +template.header.display = Affichage +header.unit.of.issue = Unité de distribution +label.position = Position +error.negative.shipped.quantity = Quantité livrée ne peut être négative +label.requisition.list = Liste de réquisition +period.number.of.months = Nombre de mois: +create.user.invalid.userName = Format non valide, espaces non autorisés dans le nom d'utilisateur +header.supervisory.node.edit = Modifier noeud de surveillance +header.pod.manage = Gérer une preuve de livraison +error.reference.data.missing = Les données manquantes / non valide de référence +msg.error.occurred = Une erreur s'est produite +label.coverage.adults = Couverture des adultes +user.email.not.found = Aucune adresse e-mail enregistrée pour votre compte. Contact Admin +description.column.cost = Le coût total du produit. Ce sera zéro si le prix n'est pas défini +message.no.rnr.pending.approval = Pas de R & R en attente d'approbation +label.was.facility.visited = A {0} visité dans {1}? +enable.facility.success = "{0}" / "{1}" est maintenant activé +label.currency.symbol = $ +message.no.order = Pas de réquisitions ont été libérés en ordre encore +label.facility.code.name = Facilité nom de code +message.facility.updated.success = Installation "{0}" mise à jour avec succès +label.data.field = champ de données +label.isa.maximum.value = ISA valeur maximale +rnr.approval.not.allowed = Approbation pas autorisé +label.period.start.date = Période Date de début +button.enable = Permettre +label.distribution.legend.partial.complete = Partiellement rempli +create.facility.goLiveDate = Aller jour en direct +error.duplicate.supply.line = L'entrée en double pour Supply Line trouvé +user.username.incorrect = S'il vous plaît fournir un nom d'utilisateur valide +label.logistics.management = Gestion de la logistique +user.login.error = Le nom d'utilisateur ou mot de passe sont incorrects. S'il vous plaît essayez de nouveau. +error.agent.already.registered = Agent déjà enregistré +label.distribution.legend.cannot.synchronize = Vous ne pouvez pas synchroniser +report.template.error.file.empty = Fichier téléchargé est vide +menu.header.geographic.zones = Zones géographiques +label.isa.population.of = La quantité calculée pour une population d' +error.duplicate.program.supported = Facilité a déjà été cartographiée au programme +label.select.node = --Select Node-- +password.reset.success = Mot de passe a été réinitialisé avec succès +header.field = Domaine +link.requisitions.convertToOrder = Convertir à la commande +placeholder.node.search = Entrez le nom de nœud +label.new.refrigerator = Nouveau réfrigérateur +label.emergency.order.point = Urgence point de commande +header.facility.active = Actif +header.parameter.value = Valeur +error.minimum.greater.than.maximum = Valeur maximale ne peut être inférieure à la valeur minimum +file.duplicate.position = Les numéros de poste ne peuvent pas avoir des valeurs en double +label.product.flammable = Inflammable +message.success.agent.updated = ASC mise à jour avec succès +label.basicInformation = Informations de base +error.reason.required = S'il vous plaît entrer une raison +msg.email.notification.body = Cher {0}, {1} Ceci est pour vous informer que {2} a terminé son rapport et de la réquisition pour la période {3} et nécessite votre attention. S'il vous plaît vous connecter en tant {4} à traiter. {5} {6} {7} Je vous remercie. +label.coverage.students = Étudiants +label.doses.per.patient.per.year = Doses par patient et par an +label.noProgramLeft = --no Programme Left-- +msg.roles.delivery.zone.deletion = Tous les rôles assignés à cette Zone de livraison pour l'utilisateur seront supprimés +error.multiple.pages = Les erreurs constatées sur les pages {0} +label.child.coverage.first.total = Total +header.facility.approved.products = produits des installations approuvées +description.column.period.normalized.consumption = Quantité totale consommée en période après ajustement pour les jours de rupture de stock. Ceci est quantifié en unités de distribution +upload.incorrect.file = Fichier incorrect +right.manage.program.product = Admin - Gérer programme produit +requisition.template.header = Rapport Configurer et de la réquisition modèle +incorrect.data.type = Type de données erroné dans le champ: {0} {1} {2} +label.summary = Résumé +search.viewHere = Voir ici +label.facility.edit = Modifier établissement +indicator.column.skip = S +msg.rnr.authorized.without.supervisor = Il n'y a pas superviseur chargé d'examiner et d'approuver cette R & R, S'il vous plaît contactez l'administrateur +label.product.pack.height = Paquet de hauteur (cm) +error.reference.data.invalid.product.form = Données de référence valides »Formulaire de produit» +label.budget.file.format = Budget fichier +header.user.userName = Nom d'utilisateur +header.facility.code = Code de l'installation +label.none.assigned = --None Assigned-- +create.facility.altitude = Altitude +label.order.IN_ROUTE = Dans itinéraire +label.shipment.file.format = fichier envoi +label.by = Par: +create.user.reportingRoles = Rôles rapports +label.submitted.by = Soumis par +label.product.manufacturer.bar.code = Fabricant code à barres +label.confirm.delete.distribution = Êtes-vous sûr de vouloir supprimer cette distribution? Toutes les données qui n'ont pas été synchronisés avec le serveur sera perdue. +label.thermostat.setting = Le réglage du thermostat +button.yes = Oui +budget.program.code.invalid = Code de programme non valide {0} trouvée dans nombre record {1} +header.products = Produits +error.restapi.quantity.approved.negative = Quantité approuvée invalide +label.product.strength = Force +button.ok = Dáccord +error.duplicate.code.supervisory.node = Dupliquer Surveillance code Node +error.facility.data.already.synced = données des installations synchroniser déjà +header.rights = Droits +upload.incorrect.file.format = Format de fichier incorrect. S'il vous plaît télécharger {0} données dans un fichier «csv». +header.facility.enabled = Activé +header.epi.use.received = Reçu +edit.role.edit = Modifier rôle +facility.feed.invalid.url = URL valide pour l'alimentation de l'installation +user.email.invalid = Format d'adresse non valide Email +button.initiate.distribution = Initier distribution +msg.question.confirmation = Êtes-vous sûr? S'il vous plaît confirmer. +link.manage = Gérer +button.no = Aucun +label.product.fullSupply = Offre complète +message.no.requisitions.for.conversion = Aucune demande pour être converties en commandes +label.coverage.workers = Ouvriers +message.distribution.already.cached = Les données pour le sélectionné {0}, {1}, {2} est déjà en cache +message.geo.zone.created.success = Zone géographique "{0}" créé avec succès +no.reports.message = Pas de rapports +label.product.cartons.per.pallet = Carton par palette +label.program.supported.facilities = Les programmes soutenus par les installations +error.identify.schedule = Erreur Identifier annexe +right.configure.edi = Admin - Configurer EDI +password.reset = Mot de passe Réinitialiser +error.rnr.previous.not.filled = S'il vous plaît finir tout de R & R de la période précédente (s) +header.product.edit = Modifier le produit +label.clear.search = Effacer la recherche +button.proceed = Procéder +label.pod.products = Produit (s) +schedules.header = Horaires +label.no.programs.left = --no Programme Left-- +error.regimens.not.done = Marquez tous les régimes comme «fait» avant d'enregistrer le formulaire +description.column.normalized.consumption = Quantité totale consommée dans un mois, après ajustement pour les jours de rupture de stock. Ceci est quantifié en unités de distribution +label.total.cost.non.full.supply = Coût total pour le plein des produits non Alimentation: +label.product.dispensingUnit = Unité de distribution +msg.cost.exceeds.budget = Le coût total dépasse le budget alloué +disable.facility.success = "{0}" / "{1}" est maintenant désactivé +label.order.file.format = fichier de commande +label.facility.operatedBy = Exploité par +error.program.type.not.supported.requisitions = Programme type non-prise en charge pour les demandes +label.refrigerator.model = Modèle +button.add.new.row = #ERROR! +header.product.code = Code de produit +label.change.facility.type = --change Installation type-- +app.title = Ouvrez-SIGL +label.facility.closed = Le centre de santé a été fermé au moment de la visite +error.role.without.name = Rôle ne peut pas être créé sans nom. +header.epi.use.total = Total +link.requisitions = Réquisitions +reset.password.message = S'il vous plaît indiquer votre adresse e-mail ou nom d'utilisateur et nous vous enverrons un lien de réinitialisation de mot sur votre email id enregistré. +error.facility.requisition.group.mapping.exists = cartographie de réquisitionner Groupe de Fonds existe déjà +link.distributions = Distributions +link.modify.isa = Modifier ISA valeurs +description.column.price = Prix ​​par paquet. Sa valeur par défaut à zéro si non spécifiés. +template.change = (Change) +create.user.select.warehouse = S'il vous plaît sélectionner un entrepôt +button.done = Terminé +error.period.start.date = Date de début du délai de est plus petite que la date courante +error.role.without.rights = Rôle ne peut pas être créée sans droits qui lui sont assignées. +button.close = Proche +placeholder.filter.products = Filtrer les produits +error.duplicate.facility = Installation "{0}" est déjà ajoutée +create.facility.enableFacility = Activer installation +create.facility.fax = Fax +create.report.description = Description +error.arithmetically.invalid = Les entrées sont arithmétiquement invalide, s'il vous plaît vérifier à nouveau +msg.password.change.success = Mot de passe modifié avec succès! +label.product.alternate.MoH.bar.code = Autre ministère de la Santé code à barres +error.rnr.submission = R & R a déjà présenté +error.mandatory.fields.missing = Manquant champs obligatoires +label.map.program.schedule = Carte Groupe réquisition de programmes et horaire +msg.no.matches = Aucun résultat trouvé pour '{0}' +create.facility.programs = Programmes +placeholder.geo.zone.search = Entrez le nom / nom du parent +label.isa.monthly.restock.amount = Montant de réapprovisionnement mensuel = {min, max} +label.facility.approved.products = Produits établissement approuvé +period.total.days = Nombre de jours: +label.product.GTIN = GTIN +label.column.header = en-tête de la colonne +label.delete.distribution.header = Supprimer la distribution +label.doses.per.month = De doses par mois +error.rnr.validation = R & R a des erreurs, s'il vous plaît de les corriger pour continuer. +msg.user.disable.success = Utilisateur "{0} {1}" a été désactivé +error.requisition.group.not.exists = Demande Code de groupe n'existe pas +create.role.roleType = Type de rôle +label.order.date.time = Afin Date / Heure +description.column.skip = Passer produit +label.not.part.of.program = Le centre de santé ne fait pas partie du programme DLS +right.manage.regimen.template = Admin - Gérer Régime modèle +message.facility.approved.product.updated.success = "{0}" mise à jour avec succès +allocated.budget.header = Coût de R & R +label.program.and.schedules = Programmes et horaires +header.user.name = Nom +label.burner.problem = problème de brûleur +placeholder.username = Nom d'utilisateur +placeholder.new.password = Nouveau mot de passe +create.user.homeFacility = Accueil Facilité d'utilisation +upload.file.successful = Fichier téléchargé avec succès. «Nombre de dossiers traités: {0}". +error.duplicate.product.code = Dupliquer Code produit +indicator.column.stock.out.days = X +error.unknown.product = Code produit inconnu +error.duplicate.order = Numéro de commande déjà traités +label.periods = Période (s) +label.product.dosesPerDispensingUnit = Doses par unité de distribution +label.catchment.population = population du bassin versant +label.distribution.initiated = Répartition déjà lancé +description.column.stock.in.hand = Comptage physique actuel du stock disponible. Ceci est quantifié en unités de distribution +label.isa.formula.modal.formula.variables = variables de formule +msg.loading = Chargement en cours ... +label.spoiled.quantity = Spoiled Quantité +error.duplicate.employee.id = Id employé en double +button.set.filters = Définissez des filtres +label.product.carton.width = largeur de Carton (cm) +menu.header.schedules = Horaires +header.user.active = Actif +option.value.geo.zone = Zone géographique +menu.header.supply.lines = Supply Lines +label.product.light.sensitive = Sensible à la lumière +description.column.quantity.approved = Quantité finale approuvée. Ceci est quantifié en unités de distribution +error.active.invalid = Actif devrait être Vrai / Faux +button.use.calculated = Utilisez calculé +label.product.carton.height = hauteur de Carton (cm) +create.facility.programsSupported = Programmes pris en charge +supervisor.user.not.found = Contrôleur nom d'utilisateur n'est pas présent dans le système +label.who.ratio = Cible pourcentage de la population +right.upload.report = Admin - Rapport de téléchargement +create.facility.active = Actif? +indicator.column.total = Y +error.facility.code.invalid = Code de l'installation non valide +option.value.supervisory.node.parent = Surveillance parent du noeud +label.description = Description +header.quantity.approved = Quantité approuvée +welcome.to.message = Bienvenue à +placeholder.name = Nom +indicator.column.stock.in.hand = E +header.unit.of.measure = Unité de mesure +label.admin.configure.regimenTemplate = Modèle régime +label.notes = Remarques +label.direct.delivery = La livraison directe +message.apply.nr = Cette action effacera toutes les données sur le formulaire et définissez tous les domaines de NR, Voulez-vous continuer? +indicator.column.product.code = O +label.authorized.by = Autorisé par +label.product.manufacturer.product.code = code produit du fabricant +create.user.selectProgram = S'il vous plaît sélectionner un programme +error.geo.level.invalid = Invalide Code géographique de niveau +error.supplying.facility.already.assigned = Noeud de surveillance et le programme a déjà un établissement fournisseur qui lui est attribué. +create.user.warehouse.roles = Rôles de l'entrepôt +label.is.verified = Est vérifiée? +formula.column.losses.adjustments = D1 + D2 + D3 ... DN +upload.select.file = S'il vous plaît sélectionner un fichier à télécharger +label.epi.inventory = EPI Inventaire +error.duplicate.facility.operator.code = Code de l'opérateur de l'installation en double +placeholder.parent.node.search = Entrez le nom de nœud parent +error.reference.data.invalid.program = «Code du programme 'des données de référence valides +right.manage.report = Rapports - Gérer +label.select.geographic.level = Level-- géographique --Select +reports.header = Rapports +order.file.template.saved.success = configuration de fichier de commandes enregistrée avec succès! +budget.allocated.negative = Budgétaire négatif +file.mandatory.columns.not.included = Colonnes obligatoires doivent être inclus +message.no.products.mapped = Aucun produit n'a mis en correspondance avec le programme sélectionné +label.period = Période +error.drop.off.facility.not.defined = Déposez installation ne sont pas définis pour les programmes avec livraison directe comme faux +indicator.column.losses.adjustments = Ré +button.delete = Effacer +in.record.no = dans le numéro d'enregistrement +label.select.schedule = Schedule-- --Select +label.geographic.levels = Niveaux géographiques +label.regimen.categories = La posologie des catégories +program.header = Programme +error.duplicate.requisition.group.program.combination = Dupliquer le code de groupe de commande et de code de programme Regroupement trouvé +error.invalid.received.quantity = Quantité reçue invalide +create.facility.activeFacility = Installation active? +link.admin.configure.programProductISA = Programme ISA produit +label.epi.inventory.header = EPI Inventaire: Livraison & Information sur les actions +error.one.page = Les erreurs trouvées sur 1 page +error.invalid.returned.quantity = Quantité retournée invalide +label.select.program = --Select Program-- +create.facility.coldStorageNetCapacity = Stockage froid capacité nette +label.geographic.zones = Zones géographiques +label.column.source.reference.data = Données de référence +label.children.age.group.twelve.twenty.three.months = 12-23 mois +label.coverage.other.not.mif = Autres pas MIF +template.programName = Programme: +indicator.column.quantity.approved = K +column.do.not.match = Colonnes ne correspondent pas aux têtes: {0} {1} {2} {3} +error.duplicate.supervisory.node = Dupliquer nœud Surveillance trouvé +label.total.returned.packs = Total des Packs retournés +label.confirm.action = Confirmez action +budget.facility.code.invalid = Code de l'établissement non valide {0} trouvée dans nombre record {1} +label.product.dosageUnit = Dosage unitaire +label.noneAssigned = --None Assigned-- +label.dont.know = Je ne sais pas +label.isa.minimum.value = Valeur minimale ISA +deliveryZone.code.invalid = Livraison invalide Code de zone +header.level = Niveau +periods.header = Périodes +label.child.vaccination.doses = vaccination des enfants (doses) +message.period.deleted.success = Période supprimé avec succès +label.allocated.budget = Budget alloué +error.select.program = S'il vous plaît sélectionner un programme +header.supply.line.edit = Modifier la ligne d'alimentation +message.no.facility.synced = Aucune installation de la zone, le programme et la période choisie est prêt à être synchro +report.type.xls = XLS +label.order.number.prefix = Numéro de commande préfixe +msg.please.select.program.facility.type = S'il vous plaît sélectionner le programme et par type d'établissement pour voir la liste des produits +error.period.without.end.date = Période ne peut être sauvé sans sa date de fin. +right.system.settings = Paramètres système - Admin +label.regimen.category = Catégorie +link.admin.system.settings = Paramètres système +label.product.expected.shelf.life = Durée de vie prévue (mois) +header.cost = Coût +error.authorisation = Vous n'êtes pas autorisé à afficher la page demandée. +confirm.roleType.change = Si vous changez le type, toutes les sélections de type de courant seront supprimés? +msg.rnr.arithmeticValidation.turned.on = Pour effectuer la validation arithmétique, le système attribue ses valeurs par défaut pour A, B et D si elles ne sont pas disponibles pour l'entrée de l'utilisateur +programProductPrice.invalid.price.per.dosage = Prix ​​invalide par unité de dosage +right.convert.to.order = Réquisition - Convertir à la commande +label.order.READY_TO_PACK = Prêt à emballer +indicator.column.product = R +header.geo.zone.add.new = Ajouter nouvelle zone géographique +template.configure = Configurez +label.select.deliveryZone = --Select Livraison Zone-- +link.admin.upload = Télécharger +label.proof.of.delivery.for = Preuve de la livraison pour {0} +create.user.email = Email +create.user.supervisoryRoles = Rôles de surveillance +label.search.supply.line = Recherche en ligne offre +incorrect.date.format = Format de date incorrect dans le champ: {0} {1} {2} +header.template.type = Type +message.facility.approved.product.deleted.success = Produit "{0}" supprimé avec succès +header.packs.to.ship = Packs Ship +label.product.archived = Archivé +message.period.added.success = Période ajouté avec succès +label.non.full.supply = L'approvisionnement non complet +label.supplying.depot = Fourniture Depot +header.view.requisitions = Voir réquisitions +message.product.null = Le produit est nul +header.proof.of.delivery = Preuve de livraison +error.reference.data.invalid.geo.zone.code = Données de référence valides »Code géographique Zone ' +create.role.selectRightWarning = S'il vous plaît sélectionner au moins un droit pour le rôle +programProduct.program.invalid.code = Invalid Code de programme +message.downloading.success = données de l'application a été mise en cache +error.rnr.not.found = Réquisitionner Introuvable +description.column.amc = La consommation mensuelle moyenne, pour les trois derniers mois. Ceci est quantifié en unités de distribution +description.column.reason.for.requested.quantity = Explication de la demande pour une quantité autre que la quantité calculée de l'ordre +error.upload.missing.mandatory.columns = "Missing colonnes obligatoires dans le fichier de téléchargement: {0}" +right.manage.products = Administrateur - Gérer les produits +create.facility.addNew = Ajouter nouvelle installation +create.facility.goDownDate = Aller en bas la date +description.column.packs.to.ship = Nombre de paquets à être expédiés en fonction de la taille du paquet et l'application des règles d'arrondi +error.cost.negative = Les frais de port ne peut pas être négatif +facilityType.invalid = Invalid type d'établissement +label.product.controlled.substance = Substance contrôlée +program.code.invalid = Code de programme non valide +label.longitude.value = La valeur doit être numérique et à portée -999,99999 à 999,99999 (5 décimales) +description.column.stock.out.days = Nombre total de facilité jours était en rupture de stock +error.no.program.mapped.requisition.group = Pas de programme (s) mappé pour Demande Groupe +error.rnr.operation.unauthorized = L'utilisateur n'a pas l'autorisation +label.search.product = Rechercher un produit +label.yes = Oui +error.supervisory.node.parent.invalid = Parent non valide code Node +requisition.type.emergency = Urgence +msg.no.product.in.order = Aucun produit dans cet ordre +description.column.quantity.received = Quantité totale reçue en dernière période. Ceci est quantifié en unités de distribution +period.schedule.header = Annexe +header.quantity.shipped = Quantité livrée +label.programs = Programmes +template.configured = Configuré +message.facility.type.approved.products.added.successfully = {0} article (s) ajouté avec succès +header.product.add.new = Ajouter un produit +header.requisition.group.edit = Modifier le groupe de réquisition +msg.many.matches = {0} résultats trouvés pour '{1}' +header.supervisory.node.add.new = Ajouter noeud de surveillance +create.facility.startDate = Date de début +label.epi.use = EPI utilisation +error.duplicate.requisition.group.member = Dupliquer trouvé Demande Groupe de membres +header.min.months.of.stock = Mois min d'actions +filter.header = Filtre +error.facility.program.mapping.exists = Facilité a déjà été cartographiée au programme +formula.column.period.normalised.consumption = N * M +label.facility.maximumStock = Niveau de stock maximum +indicator.column.packs.to.ship = V +header.quantity.received = Quantité reçue +create.user.selectRoles = S'il vous plaît sélectionner rôles +message.downloading.failed = téléchargement de données de l'application n'a pas +description.column.expiration.date = Date d'expiration (MM / AAAA) +error.upload.header.missing = "En-tête de la colonne ({0}) est manquant." +upload.label.file = Dossier +error.period.exist.for.schedule = Nom de période existe déjà pour ce programme +msg.status.completed = REMPLI +error.approval.not.allowed = Approbation pas autorisé +label.select.period = --Select Period-- +budget.invalid.date.format = Format de date non valide {0} trouvée dans nombre record {1} +user.data.length.incorrect = Mauvais type de données ou la longueur +create.user.cellPhone = Téléphone cellulaire +label.supply.lines = Supply Lines +label.buffer.stock.level = Tampon niveau de stock +label.total.received.packs = Total des Packs reçus +label.adult.tetanus.second.fifth = Tétanos deuxième à cinquième dose +error.saving.order.configuration = Erreur dans la configuration du numéro d'ordre de l'économie +error.select.value = S'il vous plaît sélectionner cette valeur +link.shipment.file.template = Envoi de fichiers +button.add.new = Ajouter un nouveau +label.product.description = Description +upload.select.type = S'il vous plaît sélectionner un type de transfert +template.header.source = Source +placeholder.search.product = Recherche de produit ... +create.user.label = Ajouter un utilisateur +formula.column.calculated.order.quantity = H - E +description.column.losses.adjustments = Tous les types de perd / ajustements réalisés à l'installation +create.facility.select.operatedBy = Installation --Select exploité by-- +error.reference.data.invalid.product = Données de référence valides »Catégorie de produit» +indicator.column.period.normalized.consumption = Z +label.order.TRANSFER_FAILED = Échec du transfert +message.supply.line.updated.success = La ligne d'alimentation correctement mis à jour +label.product.active = Produit actif +link.admin.configure.requisitionTemplate = R & amp; Modèle R +indicator.column.quantity.received = B +label.facility = Facilité +error.requisition.not.exist = N'existe pas réquisition. S'il vous plaît lancer. +error.supervisory.node.parent.not.exist = N'existe pas de surveillance Node Parent +msg.sign.in = S'il vous plaît Connectez-vous avec votre nouveau mot de passe. +label.product.Approved.by.WHO = Approuvé par l'OMS +right.manage.requisition.group = Admin - Gérer Demande Groupe +msg.no.rnr.found = Pas de réquisitions trouvé +error.unknown.order = Numéro de commande inconnu +label.supplying.facility = Établissement fournisseur +order.ftpComment.supplyline.missing = Ligne une absence d'alimentation +error.reference.data.facility.type.missing = Manquant obligatoire des données de référence »Type d'installation" +indicator.column.expiration.date = Sol +label.program.product = Configurer ISA du produit +label.coverage.target.Group = Groupe cible +header.product.primary.name = nom principal de produit +period.header.name = Nom +label.product.name = Nom du produit +error.facility.inoperative = Installation ne fonctionne pas +description.column.beginning.balance = Stock dans la main de la période précédente. Ceci est quantifié en unités de distribution +description.column.quantity.requested = Dérogation demandée quantity.This calculées est quantifié en unités de distribution +button.sign.in = Se connecter +label.app.header = Systèmes d'information de gestion logistique +order.ftpComment.connection.refused = Impossible de se connecter +link.orders.view = Voire des commandes +menu.header.requisition.groups = groupes de réquisition +message.product.created.success = Produit "{0}" créé avec succès +message.isa.save.success = ISA enregistré avec succès +label.total.rnr.cost = Total R & amp; R Coût: +label.expand.all = Agrandir tout +create.user.deleteRoles = Supprimer rôles +header.parameter.description = Description +label.bad.weather = Route impraticable temps ou mauvais +create.facility.enabled = Est facilité activé? +requisition.type.regular = Ordinaire +error.duplicate.geographic.level.code = Dupliquer Code géographique de niveau +label.default.category = Autre +button.edit.formula = Modifier la formule +confirm.programRole.deletion = Tous les programmes et les rôles association pour cette installation sera supprimé. +msg.pod.save.success = POD enregistré avec succès! +edit.user.label = Modifier l'utilisateur +placeholder.add.role = #ERROR! +facilities.added.successfully = Equipements ajoutés avec succès +button.create = Créer +error.duplicate.product.category = Dupliquer Catégorie de produit +option.value.all = Tous +error.duplicate.regimen.code = Vous ne pouvez pas ajouter le code de traitement en double pour un même programme +link.admin.manage = Gérer +label.search.requisition.group = Recherche groupe de réquisition +link.update.pod = Mise à jour POD +message.geo.zone.updated.success = Zone géographique "{0}" correctement mis à jour +msg.email.notification.subject = Votre action est requise +description.column.quantity.dispensed = Quantité distribuée / consommé dans la dernière période de déclaration. Ceci est quantifié en unités de distribution +create.user.selectSupervisoryNode = S'il vous plaît sélectionner un noeud de contrôle +indicator.column.remarks = L +message.loggedInAs = Connecté en tant que +indicator.column.price = T +header.geo.zone.edit = Modifier zone géographique +label.distribution.legend.not.complete = Terminé +label.delivery.zone = Zone de Livraison +header.requisition.group.add.new = Ajouter un groupe de réquisition +warehouse.code.invalid = Code Entrepôt invalide +header.notes = Remarques +label.supervisory.node = Noeud de surveillance +label.members = Membres +placeholder.search.by.status = Recherche par statut ... +message.dateChangeConfirmMessage = Facilité personnel soumettra dos en raison de R & R de ce programme, à partir de cette date. +error.direct.delivery.drop.off.facility.combination.incorrect = Mauvaise combinaison de livraison directe et déposer installation +error.duplicate.program.product = Dupliquer programme produit +label.isa.per.month = par mois +placeholder.user.search = Entrez le nom d'utilisateur, nom, prénom ou e-mail +header.code = Code +create.role.adminRights = les droits d'exploitation d'administrateur et générales +create.user.restrictLogin = Restreindre connecter? +error.program.configuration.missing = Configuration du programme manquant +label.requisition.facilityName = Nom de l'établissement +error.reference.data.invalid.operated.by = Données de référence non valide »Gérée par ' +create.user.order.fulfillment.roles = Commandez Rôles Fulfillment +create.facility.altitude.value = La valeur doit être numérique et à portée -9999.9999 to 9999.9999 (4 décimales) +create.user.adminRoles = Admin et Opérations générales Rôles +error.geo.zone.parent.invalid = Invalide la zone géographique Code Parent +label.requisition.type = Type +create.facility.addressLine2 = Adresse ligne 2 +indicator.column.beginning.balance = A +create.facility.addressLine1 = Adresse ligne 1 +roles.header = Rôles +error.enabled.false = Installation handicapés avec un statut actif trouvé +msg.status.initiated = INITIATIVE +label.configure.order.number = Numéro de commande +option.value.facility = Facilité +label.off = OFF +label.product.fullName = nom complet du produit +create.report.uploadFile = Télécharger un fichier +message.supervisory.node.created.success = Noeud de surveillance "{0}" créé avec succès +sending.label = Envoi en cours ... +indicator.column.normalized.consumption = N +label.total.cost = Coût total +placeholder.explanation = Explication +right.manage.schedule = Administrateur - Gérer les annexes +disable.facility.header = Installation Désactiver +unexpected.exception = Oops, quelque chose s'est mal passé. S'il vous plaît réessayer plus tard +description.column.calculated.order.quantity = Actual Quantité nécessaire après déduction des actions en main. Ceci est quantifié en unités de distribution +msg.rnr.submitted.without.supervisor = Il n'y a pas de surveillance noeud pour traiter la R & R en outre, S'il vous plaît contactez l'administrateur +label.not.applicable = Pas applicable +error.program.products.not.done = Mark Les produits tout programme que «Terminé» avant d'enregistrer la forme +message.no.search.results = Aucun résultat de recherche +button.print = Imprimer +label.select.warehouse = --Select Warehouse-- +description.column.new.patient.count = De nouvelles données de patients +error.password.invalid = Mot de passe est invalide. Mot de passe doit être comprise entre 8 et 16 caractères, ne devrait pas contenir d'espaces et contenir au moins 1 chiffre. +facility.approved.product.does.not.exist = Facilité produit approuvé n'existe pas. +report.template.parameter.display.name.missing = nom d'affichage est manquant pour le paramètre "{0}" +regimen.reporting.patients.stopped.treatment = Nombre de patients ont arrêté le traitement +regimen.reporting.dataType.text = Texte +header.lastModified = Dernière mise à jour +message.schedule.created.success = "{0}" créé avec succès +select.value = S'il vous plaît sélectionner une valeur +formula.column.packs.to.ship = Règles K / U + arrondissement +right.view.order = Ordre - Voir +message.facility.synced.successfully = {0} synchronisation avec succès +label.product.packRoundingThreshold = Pack de seuil d'arrondi +link.forgot.password = Mot de passe oublié? +create.role.reporting.rights = Rapports sur les droits +button.restore = Restaurer +error.number.only = Chiffres seulement +header.supply.line.add.new = Ajouter une ligne d'alimentation +error.invalid.requisition.id = RequisitionID invalide +msg.roles.fulfillment.delete.warning = Tous les rôles assignés à cet utilisateur pour cet entrepôt seront supprimés +label.existing.quantity = Quantité existant +error.correct.highlighted = S'il vous plaît corriger les champs indiqués avant de soumettre +period.header.endDate = Date de fin +upload.label.type = Type de chargement +label.product.roundToZero = Ronde à zéro +msg.status.synced = Synchronisés +search.threeCharacters = 3 caractères minimum From 9efc0a83f7bc5d7c988f9c1dbe843aefb1a2191f Mon Sep 17 00:00:00 2001 From: joshzamor Date: Wed, 6 Aug 2014 15:49:44 -0700 Subject: [PATCH 37/80] Adding to default configuration that French is available --- modules/openlmis-web/src/main/resources/default.properties | 2 +- modules/openlmis-web/src/main/resources/messages.properties | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/openlmis-web/src/main/resources/default.properties b/modules/openlmis-web/src/main/resources/default.properties index 625da53e32..4ba94bac65 100644 --- a/modules/openlmis-web/src/main/resources/default.properties +++ b/modules/openlmis-web/src/main/resources/default.properties @@ -85,4 +85,4 @@ openlmis.conn.pool.statementCacheNumDeferredCloseThreads = 0 openlmis.conn.pool.testConnectionOnCheckin = false openlmis.conn.pool.unreturnedConnectionTimeout = 0 -locales.supported = en, pt, es +locales.supported = en, pt, es, fr diff --git a/modules/openlmis-web/src/main/resources/messages.properties b/modules/openlmis-web/src/main/resources/messages.properties index 2a800c014c..35e833533a 100644 --- a/modules/openlmis-web/src/main/resources/messages.properties +++ b/modules/openlmis-web/src/main/resources/messages.properties @@ -11,4 +11,5 @@ label.locale.en = English label.locale.pt = portugu\u00EAs label.locale.es = espa\u00F1ol +label.locale.fr = Français From 6afa25cd5657e91271317e6e59f10b68f0bd45dc Mon Sep 17 00:00:00 2001 From: joshzamor Date: Tue, 19 Aug 2014 18:22:01 -0700 Subject: [PATCH 38/80] OA-83, added openlmis report template and rough draft of stock imbalance report --- .../OpenLMIS-StockImbalancesByFacility.jrxml | 217 ++++++++++++++++++ reports/OpenLMIS-Template_Landscape.jrxml | 48 ++++ 2 files changed, 265 insertions(+) create mode 100644 reports/OpenLMIS-StockImbalancesByFacility.jrxml create mode 100644 reports/OpenLMIS-Template_Landscape.jrxml diff --git a/reports/OpenLMIS-StockImbalancesByFacility.jrxml b/reports/OpenLMIS-StockImbalancesByFacility.jrxml new file mode 100644 index 0000000000..a122e7fc03 --- /dev/null +++ b/reports/OpenLMIS-StockImbalancesByFacility.jrxml @@ -0,0 +1,217 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + rli.OverstockPoint THEN 'Over stocked' + ELSE 'OK' + END AS StockStatus +FROM geographic_zones +INNER JOIN facilities ON geographic_zones.id = facilities.geographiczoneid +INNER JOIN requisitions ON facilities.id = requisitions.facilityid +INNER JOIN processing_periods ON requisitions.periodid = processing_periods.id +INNER JOIN requisition_line_items ON requisitions.id = requisition_line_items.rnrid +INNER JOIN (SELECT id + , stockinhand / amc AS MonthsOnHand + , maxmonthsofstock * (1 + CAST($P{OverstockPercentage} as integer) / 100) AS OverstockPoint + , maxMonthsOfStock * (1 - CAST($P{UnderstockPercentage} as integer) / 100) as UnderstockPoint + FROM requisition_line_items) AS rli ON requisition_line_items.id=rli.id +INNER JOIN products ON requisition_line_items.productcode = products.code +INNER JOIN programs ON requisitions.programid = programs.id +WHERE geographic_zones.name=$P{DistrictName} + AND geographic_zones.levelid=3 + AND programs.name=$P{Program} + AND CAST($P{Date} as date) Between processing_periods.startdate And processing_periods.enddate + AND products.fullsupply=True + AND requisition_line_items.skipped=False +ORDER BY facilities.name, products.primaryname;]]> + + + + + + + + + + + + + + + + + + + + <band height="79" splitType="Stretch"> + <staticText> + <reportElement x="180" y="0" width="400" height="40" uuid="11f3a3e7-8367-499f-b5d5-83231cc79499"/> + <textElement textAlignment="Center" verticalAlignment="Middle"> + <font size="16"/> + </textElement> + <text><![CDATA[Stock Imbalance By Facility]]></text> + </staticText> + <textField pattern="MMMMM dd, yyyy"> + <reportElement x="650" y="0" width="100" height="20" uuid="bb0866e5-88b1-4b47-bc0e-29c11d11fa61"/> + <textElement textAlignment="Right"/> + <textFieldExpression><![CDATA[new java.util.Date()]]></textFieldExpression> + </textField> + <textField pattern="h:mm a z"> + <reportElement x="650" y="20" width="100" height="20" uuid="8ad6326f-b839-44be-835c-136c4a51a73e"/> + <textElement textAlignment="Right"/> + <textFieldExpression><![CDATA[new java.util.Date()]]></textFieldExpression> + </textField> + <textField> + <reportElement x="180" y="40" width="400" height="39" uuid="943a3320-b921-4ebb-8274-a767336ea133"/> + <textElement textAlignment="Center" verticalAlignment="Middle"/> + <textFieldExpression><![CDATA["For " + $P{Program} + " program, for requisitions from the month of " + $P{Date}]]></textFieldExpression> + </textField> + </band> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/reports/OpenLMIS-Template_Landscape.jrxml b/reports/OpenLMIS-Template_Landscape.jrxml new file mode 100644 index 0000000000..eaf92a3744 --- /dev/null +++ b/reports/OpenLMIS-Template_Landscape.jrxml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + <band height="79" splitType="Stretch"> + <staticText> + <reportElement x="180" y="0" width="400" height="79" uuid="11f3a3e7-8367-499f-b5d5-83231cc79499"/> + <textElement textAlignment="Center" verticalAlignment="Middle"/> + <text><![CDATA[OpenLMIS Title]]></text> + </staticText> + <textField pattern="MMMMM dd, yyyy"> + <reportElement x="650" y="0" width="100" height="20" uuid="bb0866e5-88b1-4b47-bc0e-29c11d11fa61"/> + <textElement textAlignment="Right"/> + <textFieldExpression><![CDATA[new java.util.Date()]]></textFieldExpression> + </textField> + <textField pattern="h:mm a z"> + <reportElement x="650" y="20" width="100" height="20" uuid="8ad6326f-b839-44be-835c-136c4a51a73e"/> + <textElement textAlignment="Right"/> + <textFieldExpression><![CDATA[new java.util.Date()]]></textFieldExpression> + </textField> + </band> + + + + + + + + + + + From 3e34dad3c92fd28f3f05ab9f0e33993e32e5c470 Mon Sep 17 00:00:00 2001 From: joshzamor Date: Wed, 20 Aug 2014 15:22:06 -0700 Subject: [PATCH 39/80] OA-86, added basic report Order Fill Rate by Facility --- .../OpenLMIS-OrderFillRateByFacility.jrxml | 248 ++++++++++++++++++ reports/OpenLMIS-Template_Landscape.jrxml | 11 +- 2 files changed, 257 insertions(+), 2 deletions(-) create mode 100644 reports/OpenLMIS-OrderFillRateByFacility.jrxml diff --git a/reports/OpenLMIS-OrderFillRateByFacility.jrxml b/reports/OpenLMIS-OrderFillRateByFacility.jrxml new file mode 100644 index 0000000000..0cb45e7fea --- /dev/null +++ b/reports/OpenLMIS-OrderFillRateByFacility.jrxml @@ -0,0 +1,248 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <band height="79" splitType="Stretch"> + <staticText> + <reportElement x="180" y="0" width="400" height="40" uuid="11f3a3e7-8367-499f-b5d5-83231cc79499"/> + <textElement textAlignment="Center" verticalAlignment="Middle"> + <font size="16"/> + </textElement> + <text><![CDATA[Order Fill Rate By Facility]]></text> + </staticText> + <textField pattern="MMMMM dd, yyyy"> + <reportElement x="650" y="0" width="100" height="20" uuid="bb0866e5-88b1-4b47-bc0e-29c11d11fa61"/> + <textElement textAlignment="Right"/> + <textFieldExpression><![CDATA[new java.util.Date()]]></textFieldExpression> + </textField> + <textField pattern="h:mm a z"> + <reportElement x="650" y="20" width="100" height="20" uuid="8ad6326f-b839-44be-835c-136c4a51a73e"/> + <textElement textAlignment="Right"/> + <textFieldExpression><![CDATA[new java.util.Date()]]></textFieldExpression> + </textField> + <textField> + <reportElement x="180" y="40" width="400" height="39" uuid="1c2f7b0c-34ac-44d1-92d8-e47de375a8c6"/> + <textElement textAlignment="Center" verticalAlignment="Top"/> + <textFieldExpression><![CDATA["For " + $F{programname} + " program, for the period " + $F{periodname}]]></textFieldExpression> + </textField> + </band> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/reports/OpenLMIS-Template_Landscape.jrxml b/reports/OpenLMIS-Template_Landscape.jrxml index eaf92a3744..bd50aaed5f 100644 --- a/reports/OpenLMIS-Template_Landscape.jrxml +++ b/reports/OpenLMIS-Template_Landscape.jrxml @@ -20,8 +20,10 @@ <band height="79" splitType="Stretch"> <staticText> - <reportElement x="180" y="0" width="400" height="79" uuid="11f3a3e7-8367-499f-b5d5-83231cc79499"/> - <textElement textAlignment="Center" verticalAlignment="Middle"/> + <reportElement x="180" y="0" width="400" height="40" uuid="11f3a3e7-8367-499f-b5d5-83231cc79499"/> + <textElement textAlignment="Center" verticalAlignment="Middle"> + <font size="16"/> + </textElement> <text><![CDATA[OpenLMIS Title]]></text> </staticText> <textField pattern="MMMMM dd, yyyy"> @@ -34,6 +36,11 @@ <textElement textAlignment="Right"/> <textFieldExpression><![CDATA[new java.util.Date()]]></textFieldExpression> </textField> + <textField> + <reportElement x="180" y="40" width="400" height="39" uuid="1c2f7b0c-34ac-44d1-92d8-e47de375a8c6"/> + <textElement textAlignment="Center" verticalAlignment="Middle"/> + <textFieldExpression><![CDATA["Subtitle"]]></textFieldExpression> + </textField> </band> From 20437fe0aa36e807ced7baaa05cc1109445e06cf Mon Sep 17 00:00:00 2001 From: joshzamor Date: Wed, 20 Aug 2014 15:22:45 -0700 Subject: [PATCH 40/80] OA-83, updated stock imbalance report format to use a horizontal line on the column headers --- .../OpenLMIS-StockImbalancesByFacility.jrxml | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/reports/OpenLMIS-StockImbalancesByFacility.jrxml b/reports/OpenLMIS-StockImbalancesByFacility.jrxml index a122e7fc03..128bc6b019 100644 --- a/reports/OpenLMIS-StockImbalancesByFacility.jrxml +++ b/reports/OpenLMIS-StockImbalancesByFacility.jrxml @@ -13,23 +13,23 @@ - + - + - + - + - + - + - + - + @@ -129,7 +129,7 @@ ORDER BY facilities.name, products.primaryname;]]> - + @@ -139,7 +139,7 @@ ORDER BY facilities.name, products.primaryname;]]> - + @@ -149,7 +149,7 @@ ORDER BY facilities.name, products.primaryname;]]> - + @@ -159,7 +159,7 @@ ORDER BY facilities.name, products.primaryname;]]> - + @@ -169,21 +169,24 @@ ORDER BY facilities.name, products.primaryname;]]> - + - + + + + - + From 1eb8d5fd937d4e66becbb9c326576057e5bf1669 Mon Sep 17 00:00:00 2001 From: joshzamor Date: Wed, 20 Aug 2014 15:39:27 -0700 Subject: [PATCH 41/80] OA-86, removed page header from report to remove extra whitespace consistent with the stock imbalance report. --- reports/OpenLMIS-OrderFillRateByFacility.jrxml | 3 --- 1 file changed, 3 deletions(-) diff --git a/reports/OpenLMIS-OrderFillRateByFacility.jrxml b/reports/OpenLMIS-OrderFillRateByFacility.jrxml index 0cb45e7fea..5f472a6b65 100644 --- a/reports/OpenLMIS-OrderFillRateByFacility.jrxml +++ b/reports/OpenLMIS-OrderFillRateByFacility.jrxml @@ -124,9 +124,6 @@ ORDER BY facilities.name - - - From f15ce71b6913576850253964912b39d70439e35d Mon Sep 17 00:00:00 2001 From: joshzamor Date: Fri, 22 Aug 2014 17:30:24 -0700 Subject: [PATCH 42/80] OA-114, modified add button to use the length of the non fully supply product list rather than the column visibility of requested quantity for being available or not. --- .../create/create-non-full-supply-controller.js | 4 ++-- .../create/create-requisition-controller.js | 11 ++++++----- .../main/webapp/public/js/shared/services/services.js | 2 +- .../rnr/partials/create/rnr-non-full-supply.html | 2 +- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/modules/openlmis-web/src/main/webapp/public/js/rnr/controller/create/create-non-full-supply-controller.js b/modules/openlmis-web/src/main/webapp/public/js/rnr/controller/create/create-non-full-supply-controller.js index 0ca28fe733..f6c1041b55 100644 --- a/modules/openlmis-web/src/main/webapp/public/js/rnr/controller/create/create-non-full-supply-controller.js +++ b/modules/openlmis-web/src/main/webapp/public/js/rnr/controller/create/create-non-full-supply-controller.js @@ -9,7 +9,7 @@ */ function CreateNonFullSupplyController($scope, messageService) { - var map = _.map($scope.facilityApprovedProducts, function (facilitySupportedProduct) { + var map = _.map($scope.facilityApprovedNFSProducts, function (facilitySupportedProduct) { return facilitySupportedProduct.programProduct.productCategory; }); @@ -97,7 +97,7 @@ function CreateNonFullSupplyController($scope, messageService) { _.pluck($scope.addedNonFullSupplyProducts, 'productCode') .concat(_.pluck($scope.rnr.nonFullSupplyLineItems, 'productCode')); if ($scope.nonFullSupplyProductCategory !== undefined) { - $scope.nonFullSupplyProductsToDisplay = $.grep($scope.facilityApprovedProducts, function (facilityApprovedProduct) { + $scope.nonFullSupplyProductsToDisplay = $.grep($scope.facilityApprovedNFSProducts, function (facilityApprovedProduct) { return $.inArray(facilityApprovedProduct.programProduct.product.code, addedNonFullSupplyProductList) == -1 && $.inArray(facilityApprovedProduct.programProduct.productCategory.name, [$scope.nonFullSupplyProductCategory.name]) === 0; }); diff --git a/modules/openlmis-web/src/main/webapp/public/js/rnr/controller/create/create-requisition-controller.js b/modules/openlmis-web/src/main/webapp/public/js/rnr/controller/create/create-requisition-controller.js index 7043da5c1d..b4514dfd32 100644 --- a/modules/openlmis-web/src/main/webapp/public/js/rnr/controller/create/create-requisition-controller.js +++ b/modules/openlmis-web/src/main/webapp/public/js/rnr/controller/create/create-requisition-controller.js @@ -8,7 +8,7 @@ * You should have received a copy of the GNU Affero General Public License along with this program.  If not, see http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  */ -function CreateRequisitionController($scope, requisitionData, pageSize, rnrColumns, lossesAndAdjustmentsTypes, facilityApprovedProducts, requisitionRights, regimenTemplate, $location, Requisitions, $routeParams, $dialog, requisitionService, $q) { +function CreateRequisitionController($scope, requisitionData, pageSize, rnrColumns, lossesAndAdjustmentsTypes, facilityApprovedNFSProducts, requisitionRights, regimenTemplate, $location, Requisitions, $routeParams, $dialog, requisitionService, $q) { var NON_FULL_SUPPLY = 'nonFullSupply'; var FULL_SUPPLY = 'fullSupply'; @@ -19,7 +19,7 @@ function CreateRequisitionController($scope, requisitionData, pageSize, rnrColum resetCostsIfNull(); $scope.lossesAndAdjustmentTypes = lossesAndAdjustmentsTypes; - $scope.facilityApprovedProducts = facilityApprovedProducts; + $scope.facilityApprovedNFSProducts = facilityApprovedNFSProducts; $scope.visibleColumns = requisitionService.getMappedVisibleColumns(rnrColumns, RegularRnrLineItem.frozenColumns, ['quantityApproved']); @@ -28,7 +28,7 @@ function CreateRequisitionController($scope, requisitionData, pageSize, rnrColum $scope.requisitionRights = requisitionRights; $scope.regimenColumns = regimenTemplate ? regimenTemplate.columns : []; $scope.visibleRegimenColumns = _.where($scope.regimenColumns, {'visible': true}); - $scope.addNonFullSupplyLineItemButtonShown = _.findWhere($scope.programRnrColumnList, {'name': 'quantityRequested'}); + $scope.addNonFullSupplyLineItemButtonShown = facilityApprovedNFSProducts.length > 0; $scope.errorPages = {fullSupply: [], nonFullSupply: []}; $scope.regimenCount = $scope.rnr.regimenLineItems.length; @@ -307,10 +307,11 @@ CreateRequisitionController.resolve = { return deferred.promise; }, - facilityApprovedProducts: function ($q, $timeout, $route, FacilityApprovedProducts) { + facilityApprovedNFSProducts: function ($q, $timeout, $route, FacilityApprovedNonFullSupplyProducts) { var deferred = $q.defer(); $timeout(function () { - FacilityApprovedProducts.get({facilityId: $route.current.params.facility, programId: $route.current.params.program}, + FacilityApprovedNonFullSupplyProducts.get({facilityId: $route.current.params.facility + , programId: $route.current.params.program}, function (data) { deferred.resolve(data.nonFullSupplyProducts); }, {}); diff --git a/modules/openlmis-web/src/main/webapp/public/js/shared/services/services.js b/modules/openlmis-web/src/main/webapp/public/js/shared/services/services.js index fdef03a921..7a3758ad8e 100644 --- a/modules/openlmis-web/src/main/webapp/public/js/shared/services/services.js +++ b/modules/openlmis-web/src/main/webapp/public/js/shared/services/services.js @@ -149,7 +149,7 @@ services.factory('ForgotPassword', function ($resource) { return $resource('/forgot-password.json', {}, {}); }); -services.factory('FacilityApprovedProducts', function ($resource) { +services.factory('FacilityApprovedNonFullSupplyProducts', function ($resource) { return $resource('/facilityApprovedProducts/facility/:facilityId/program/:programId/nonFullSupply.json', {}, {}); }); diff --git a/modules/openlmis-web/src/main/webapp/public/pages/logistics/rnr/partials/create/rnr-non-full-supply.html b/modules/openlmis-web/src/main/webapp/public/pages/logistics/rnr/partials/create/rnr-non-full-supply.html index f1943f6b1f..27891cb5ea 100644 --- a/modules/openlmis-web/src/main/webapp/public/pages/logistics/rnr/partials/create/rnr-non-full-supply.html +++ b/modules/openlmis-web/src/main/webapp/public/pages/logistics/rnr/partials/create/rnr-non-full-supply.html @@ -27,7 +27,7 @@
+ ng-disabled="formDisabled || !(addNonFullSupplyLineItemButtonShown)" openlmis-message="button.add"/>
From ee3f95b3d1668edb5e32f8819491192df1a644e4 Mon Sep 17 00:00:00 2001 From: joshzamor Date: Mon, 25 Aug 2014 21:04:16 -0700 Subject: [PATCH 43/80] OA-114, changed UI logic to always show requested quantity and reason for on non full supply products --- .../js/rnr/services/requisition-service.js | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/modules/openlmis-web/src/main/webapp/public/js/rnr/services/requisition-service.js b/modules/openlmis-web/src/main/webapp/public/js/rnr/services/requisition-service.js index 3ce72c152c..af3f727afa 100644 --- a/modules/openlmis-web/src/main/webapp/public/js/rnr/services/requisition-service.js +++ b/modules/openlmis-web/src/main/webapp/public/js/rnr/services/requisition-service.js @@ -101,16 +101,28 @@ services.factory('requisitionService', function (messageService) { return 'scrollable'; }); + var nfsColumns = {fixed: [], scrollable: []}; // non-full supply columns + nfsColumns.fixed = _.filter(fullSupplyVisibleColumns.fixed, function (column) { + return _.contains(['product', 'productCode'], column.name); + }); + + nfsColumns.scrollable = _.filter(fullSupplyVisibleColumns.scrollable, function (column) { + return _.contains(RegularRnrLineItem.visibleForNonFullSupplyColumns, column.name); + }); + + // find columns needed for non-full supply products: requested quantity and the reason for the request + // these are needed/displayed regardless of how the R&R form is setup - ie all non-full supply product requests + // always have a requested quantity and the associated reason for the request + // ensure these are in the list of scrollable columns. + var nfsReqQuantityColumns = _.filter(rnrColumns, function (column) { + return _.contains(['quantityRequested', 'reasonForRequestedQuantity'], column.name); + }); + if(nfsReqQuantityColumns.length == 0) throw new Error('Requested Quantity column(s) not found'); + nfsColumns.scrollable = _.union(nfsColumns.scrollable, nfsReqQuantityColumns); + return { fullSupply: fullSupplyVisibleColumns, - nonFullSupply: { - fixed: _.filter(fullSupplyVisibleColumns.fixed, function (column) { - return _.contains(['product', 'productCode'], column.name); - }), - scrollable: _.filter(fullSupplyVisibleColumns.scrollable, function (column) { - return _.contains(RegularRnrLineItem.visibleForNonFullSupplyColumns, column.name); - }) - } + nonFullSupply: nfsColumns }; } From e0fb9325316a71e3e66cf65c6817e012f802f46c Mon Sep 17 00:00:00 2001 From: joshzamor Date: Mon, 25 Aug 2014 21:41:32 -0700 Subject: [PATCH 44/80] OA-114, fixed JS error from requested column visible. --- .../main/webapp/public/js/rnr/model/regular-rnr-line-item.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/openlmis-web/src/main/webapp/public/js/rnr/model/regular-rnr-line-item.js b/modules/openlmis-web/src/main/webapp/public/js/rnr/model/regular-rnr-line-item.js index 6ad687d3a4..a3793349b0 100644 --- a/modules/openlmis-web/src/main/webapp/public/js/rnr/model/regular-rnr-line-item.js +++ b/modules/openlmis-web/src/main/webapp/public/js/rnr/model/regular-rnr-line-item.js @@ -308,7 +308,7 @@ var RegularRnrLineItem = base2.Base.extend({ }, validateRequiredFieldsForNonFullSupply: function () { - if (_.findWhere(this.programRnrColumnList, {name: 'quantityRequested'}).visible) { + if (_.findWhere(this.programRnrColumnList, {name: 'quantityRequested'})) { return !(isUndefined(this.quantityRequested) || isUndefined(this.reasonForRequestedQuantity)); } return false; From 436458b048595f1306202034f7b759500cb14443 Mon Sep 17 00:00:00 2001 From: joshzamor Date: Wed, 3 Sep 2014 10:41:04 -0700 Subject: [PATCH 45/80] OA-114, adding requested quantity columns for test --- .../specs/rnr/services/requisition-service-test.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/modules/openlmis-web/src/test/javascript/specs/rnr/services/requisition-service-test.js b/modules/openlmis-web/src/test/javascript/specs/rnr/services/requisition-service-test.js index 297262b6dc..111fed9ae3 100644 --- a/modules/openlmis-web/src/test/javascript/specs/rnr/services/requisition-service-test.js +++ b/modules/openlmis-web/src/test/javascript/specs/rnr/services/requisition-service-test.js @@ -19,12 +19,14 @@ describe("requisitionService", function () { {"id": 2, "name": "product", "position": 2, "source": {"description": "Reference Data", "name": "REFERENCE", "code": "R"}, "sourceConfigurable": false, "label": "Product", "formula": "", "indicator": "R", "used": true, "visible": true, "mandatory": true, "description": "Primary name of the product", "formulaValidationRequired": true}, {"id": 3, "name": "dispensingUnit", "position": 3, "source": {"description": "Reference Data", "name": "REFERENCE", "code": "R"}, "sourceConfigurable": false, "label": "Unit/Unit of Issue", "formula": "", "indicator": "U", "used": true, "visible": true, "mandatory": false, "description": "Dispensing unit for this product", "formulaValidationRequired": true}, {"id": 4, "name": "beginningBalance", "position": 4, "source": {"description": "User Input", "name": "USER_INPUT", "code": "U"}, "sourceConfigurable": false, "label": "Beginning Balance", "formula": "", "indicator": "A", "used": true, "visible": true, "mandatory": false, "description": "Stock in hand of previous period.This is quantified in dispensing units", "formulaValidationRequired": true}, - {"id": 7, "name": "lossesAndAdjustments", "position": 7, "source": {"description": "User Input", "name": "USER_INPUT", "code": "U"}, "sourceConfigurable": false, "label": "Total Losses / Adjustments", "formula": "D1 + D2+D3...DN", "indicator": "D", "used": true, "visible": false, "mandatory": false, "description": "All kind of looses/adjustments made at the facility", "formulaValidationRequired": true} + {"id": 7, "name": "lossesAndAdjustments", "position": 7, "source": {"description": "User Input", "name": "USER_INPUT", "code": "U"}, "sourceConfigurable": false, "label": "Total Losses / Adjustments", "formula": "D1 + D2+D3...DN", "indicator": "D", "used": true, "visible": false, "mandatory": false, "description": "All kind of looses/adjustments made at the facility", "formulaValidationRequired": true}, + {"id": 8, "name": "quantityRequested", "label":"Requested Quantity", "visible":true, "position":8, "source": {"description": "User Input", "name": "USER_INPUT", "code": "U"}, "sourceConfigurable": false, "formula": "","indicator": "Q", "used": true, "mandatory": false, "description": "Requested Quantity", "formulaValidationRequired": true, "configuredOption": null}, + {"id": 9, "name": "reasonForRequestedQuantity", "label":"Requested Quantity Explanation", "visible": true, "position": 19, "source": {"description": "User Input", "name": "USER_INPUT", "code": "U"}, "sourceConfigurable": false, "formula": "", "indicator": "T","used": true, "mandatory": false, "description": "Reason for Requested Quantity", "formulaValidationRequired":true, "configuredOption": null} ]; var visibleFullScrollableColumns = [columns[3], columns[4]]; var visibleFullFixedColumns = [columns[0], columns[1], columns[2]]; - var visibleNonFullScrollableColumns = [columns[3]]; + var visibleNonFullScrollableColumns = [columns[3], columns[8], columns[9]]; var visibleNonFullFixedColumns = [columns[1], columns[2]]; beforeEach(inject(function ($location, $routeParams, $rootScope, _requisitionService_, _messageService_) { @@ -126,7 +128,7 @@ describe("requisitionService", function () { it('should skip column from map', function () { var mappedColumns = requisitionService.getMappedVisibleColumns(columns, ['skipped', 'productCode', 'product'], ['beginningBalance']); - expect(mappedColumns.fullSupply.scrollable.length).toEqual(1); + expect(mappedColumns.fullSupply.scrollable.length).toEqual(3); }); }); \ No newline at end of file From 34b5a690ca98b6e4edee91c3df091a1a7284f42f Mon Sep 17 00:00:00 2001 From: joshzamor Date: Wed, 3 Sep 2014 11:11:11 -0700 Subject: [PATCH 46/80] Added karma dependencies to project build --- modules/openlmis-web/package.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/openlmis-web/package.json b/modules/openlmis-web/package.json index 4edeb817b0..018af6bf13 100644 --- a/modules/openlmis-web/package.json +++ b/modules/openlmis-web/package.json @@ -9,6 +9,10 @@ "grunt-contrib-watch": "~0.5.3", "grunt-contrib-uglify": "~0.2.4", "matchdep": "~0.1.2", - "grunt": "~0.4.1" + "grunt": "~0.4.1", + "karma": "~0.12.19", + "karma-coverage": "~0.2.5", + "karma-jasmine": "~0.1.5", + "karma-firefox-launcher": "~0.1.3" } } From 366cad404cf5bb9dafe001315e432392da1a4bb4 Mon Sep 17 00:00:00 2001 From: joshzamor Date: Wed, 3 Sep 2014 12:32:04 -0700 Subject: [PATCH 47/80] Adding html reporter to Karma --- modules/openlmis-web/karma.config.js | 7 ++++++- modules/openlmis-web/package.json | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/modules/openlmis-web/karma.config.js b/modules/openlmis-web/karma.config.js index 29896ebf9e..99f2971345 100644 --- a/modules/openlmis-web/karma.config.js +++ b/modules/openlmis-web/karma.config.js @@ -51,18 +51,23 @@ module.exports = function (config) { plugins: [ 'karma-jasmine', 'karma-coverage', + 'karma-htmlfile-reporter', 'karma-firefox-launcher' ], // test results reporter to use // possible values: 'dots', 'progress', 'junit', 'growl', 'coverage' - reporters: ['progress', 'coverage'], + reporters: ['progress', 'coverage', 'html'], coverageReporter: { type: 'html', dir: '../../../test/coverage/' }, + htmlReporter: { + outputFile: 'tests/units.html' + }, + preprocessors: { 'js/**/*.js': ['coverage'] }, diff --git a/modules/openlmis-web/package.json b/modules/openlmis-web/package.json index 018af6bf13..18395b1614 100644 --- a/modules/openlmis-web/package.json +++ b/modules/openlmis-web/package.json @@ -12,6 +12,7 @@ "grunt": "~0.4.1", "karma": "~0.12.19", "karma-coverage": "~0.2.5", + "karma-htmlfile-reporter": "~0.1", "karma-jasmine": "~0.1.5", "karma-firefox-launcher": "~0.1.3" } From a9270c58595d1119234e71a8220e1039d114bd8e Mon Sep 17 00:00:00 2001 From: joshzamor Date: Wed, 3 Sep 2014 12:32:04 -0700 Subject: [PATCH 48/80] Adding html reporter to Karma --- modules/openlmis-web/karma.config.js | 7 ++++++- modules/openlmis-web/package.json | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/modules/openlmis-web/karma.config.js b/modules/openlmis-web/karma.config.js index 29896ebf9e..8ebaf2dc69 100644 --- a/modules/openlmis-web/karma.config.js +++ b/modules/openlmis-web/karma.config.js @@ -51,18 +51,23 @@ module.exports = function (config) { plugins: [ 'karma-jasmine', 'karma-coverage', + 'karma-htmlfile-reporter', 'karma-firefox-launcher' ], // test results reporter to use // possible values: 'dots', 'progress', 'junit', 'growl', 'coverage' - reporters: ['progress', 'coverage'], + reporters: ['progress', 'coverage', 'html'], coverageReporter: { type: 'html', dir: '../../../test/coverage/' }, + htmlReporter: { + outputFile: '../../../test/karma/units.html' + }, + preprocessors: { 'js/**/*.js': ['coverage'] }, diff --git a/modules/openlmis-web/package.json b/modules/openlmis-web/package.json index 018af6bf13..18395b1614 100644 --- a/modules/openlmis-web/package.json +++ b/modules/openlmis-web/package.json @@ -12,6 +12,7 @@ "grunt": "~0.4.1", "karma": "~0.12.19", "karma-coverage": "~0.2.5", + "karma-htmlfile-reporter": "~0.1", "karma-jasmine": "~0.1.5", "karma-firefox-launcher": "~0.1.3" } From b7fc533ccd756e12943d8cdd9e49314c280a21c0 Mon Sep 17 00:00:00 2001 From: joshzamor Date: Wed, 3 Sep 2014 14:00:36 -0700 Subject: [PATCH 49/80] OA-114, fixing tests for requisition-service --- .../create/create-requisition-controller.js | 11 ++++--- .../js/rnr/services/requisition-service.js | 2 +- .../approve/approve-rnr-controller-test.js | 2 ++ .../create-non-full-supply-controller-test.js | 4 +-- .../create-requisition-controller-test.js | 32 +++++++++---------- .../rnr/services/requisition-service-test.js | 19 +++++++---- 6 files changed, 39 insertions(+), 31 deletions(-) diff --git a/modules/openlmis-web/src/main/webapp/public/js/rnr/controller/create/create-requisition-controller.js b/modules/openlmis-web/src/main/webapp/public/js/rnr/controller/create/create-requisition-controller.js index b4514dfd32..2d0f9544c5 100644 --- a/modules/openlmis-web/src/main/webapp/public/js/rnr/controller/create/create-requisition-controller.js +++ b/modules/openlmis-web/src/main/webapp/public/js/rnr/controller/create/create-requisition-controller.js @@ -310,11 +310,12 @@ CreateRequisitionController.resolve = { facilityApprovedNFSProducts: function ($q, $timeout, $route, FacilityApprovedNonFullSupplyProducts) { var deferred = $q.defer(); $timeout(function () { - FacilityApprovedNonFullSupplyProducts.get({facilityId: $route.current.params.facility - , programId: $route.current.params.program}, - function (data) { - deferred.resolve(data.nonFullSupplyProducts); - }, {}); + FacilityApprovedNonFullSupplyProducts.get( + {facilityId: $route.current.params.facility, programId: $route.current.params.program}, + function (data) { + deferred.resolve(data.nonFullSupplyProducts); + }, + {} ); }, 100); return deferred.promise; }, diff --git a/modules/openlmis-web/src/main/webapp/public/js/rnr/services/requisition-service.js b/modules/openlmis-web/src/main/webapp/public/js/rnr/services/requisition-service.js index af3f727afa..2a703976de 100644 --- a/modules/openlmis-web/src/main/webapp/public/js/rnr/services/requisition-service.js +++ b/modules/openlmis-web/src/main/webapp/public/js/rnr/services/requisition-service.js @@ -117,7 +117,7 @@ services.factory('requisitionService', function (messageService) { var nfsReqQuantityColumns = _.filter(rnrColumns, function (column) { return _.contains(['quantityRequested', 'reasonForRequestedQuantity'], column.name); }); - if(nfsReqQuantityColumns.length == 0) throw new Error('Requested Quantity column(s) not found'); + if(nfsReqQuantityColumns.length === 0) throw new Error('Requested Quantity column(s) not found'); nfsColumns.scrollable = _.union(nfsColumns.scrollable, nfsReqQuantityColumns); return { diff --git a/modules/openlmis-web/src/test/javascript/specs/rnr/controller/approve/approve-rnr-controller-test.js b/modules/openlmis-web/src/test/javascript/specs/rnr/controller/approve/approve-rnr-controller-test.js index 7c1a6fcbfe..8a9419b7a8 100644 --- a/modules/openlmis-web/src/test/javascript/specs/rnr/controller/approve/approve-rnr-controller-test.js +++ b/modules/openlmis-web/src/test/javascript/specs/rnr/controller/approve/approve-rnr-controller-test.js @@ -38,6 +38,8 @@ describe('Approve Requisition controller', function () { scope.approvalForm = {}; programRnrColumnList = [ {'name': 'ProductCode', 'label': 'Product Code', 'visible': true}, + {'name': 'quantityRequested'}, + {'name': 'reasonForRequestedQuantity'}, {'name': 'quantityApproved', 'label': 'quantity approved', 'visible': true}, {'name': 'remarks', 'label': 'remarks', 'visible': true} ]; diff --git a/modules/openlmis-web/src/test/javascript/specs/rnr/controller/create/create-non-full-supply-controller-test.js b/modules/openlmis-web/src/test/javascript/specs/rnr/controller/create/create-non-full-supply-controller-test.js index 36447141f0..6b27583a49 100644 --- a/modules/openlmis-web/src/test/javascript/specs/rnr/controller/create/create-non-full-supply-controller-test.js +++ b/modules/openlmis-web/src/test/javascript/specs/rnr/controller/create/create-non-full-supply-controller-test.js @@ -56,7 +56,7 @@ describe('CreateNonFullSupplyController', function () { $rootScope.fixToolBar = function () { }; - scope.facilityApprovedProducts = facilityApprovedProducts; + scope.facilityApprovedNFSProducts = facilityApprovedProducts; ctrl = controller(CreateNonFullSupplyController, {$scope: scope, $location: location, $routeParams: routeParams, localStorageService: localStorageService}); @@ -67,7 +67,7 @@ describe('CreateNonFullSupplyController', function () { }); it('should display non full supply addition modal window', function () { - scope.facilityApprovedProducts = []; + scope.facilityApprovedNFSProducts = []; spyOn(scope, 'resetNonFullSupplyModal'); scope.showAddNonFullSupplyModal(); diff --git a/modules/openlmis-web/src/test/javascript/specs/rnr/controller/create/create-requisition-controller-test.js b/modules/openlmis-web/src/test/javascript/specs/rnr/controller/create/create-requisition-controller-test.js index f607ce8aa3..255e585f56 100644 --- a/modules/openlmis-web/src/test/javascript/specs/rnr/controller/create/create-requisition-controller-test.js +++ b/modules/openlmis-web/src/test/javascript/specs/rnr/controller/create/create-requisition-controller-test.js @@ -11,7 +11,7 @@ describe('CreateRequisitionController', function () { var scope, rootScope, ctrl, httpBackend, location, routeParams, controller, localStorageService, mockedRequisition, rnrColumns, regimenColumnList, pageSize, - lossesAndAdjustmentTypes, facilityApprovedProducts, requisitionRights, rnrLineItem, messageService, regimenTemplate, requisitionService, categoryList, requisitions; + lossesAndAdjustmentTypes, facilityApprovedNFSProducts, requisitionRights, rnrLineItem, messageService, regimenTemplate, requisitionService, categoryList, requisitions; beforeEach(module('openlmis')); beforeEach(module('ui.bootstrap.dialog')); @@ -55,7 +55,9 @@ describe('CreateRequisitionController', function () { }; rnrColumns = [ - {"testField": "test"} + {"testField": "test"}, + {name: "requestedQuantity"}, + {name: "reasonForRequestedQuantity"} ]; regimenColumnList = [ @@ -84,13 +86,13 @@ describe('CreateRequisitionController', function () { var facilityApprovedProduct4 = {"programProduct": {"product": product4}}; var facilityApprovedProduct5 = {"programProduct": {"product": product5}}; - facilityApprovedProducts = [facilityApprovedProduct1, facilityApprovedProduct2, facilityApprovedProduct3, facilityApprovedProduct4, facilityApprovedProduct5]; + facilityApprovedNFSProducts = [facilityApprovedProduct1, facilityApprovedProduct2, facilityApprovedProduct3, facilityApprovedProduct4, facilityApprovedProduct5]; httpBackend.when('GET', '/rnr/1/columns.json').respond(rnrColumns); httpBackend.when('GET', '/programId/1/regimenColumns.json').respond(regimenColumnList); httpBackend.when('GET', '/reference-data/currency.json').respond({"currency": "$"}); httpBackend.when('GET', '/requisitions/lossAndAdjustments/reference-data.json').respond(200, lossesAndAdjustmentTypes); - httpBackend.when('GET', '/facilityApprovedProducts/facility/1/program/1/nonFullSupply.json').respond(200, {"nonFullSupplyProducts": facilityApprovedProducts}); + httpBackend.when('GET', '/facilityApprovedProducts/facility/1/program/1/nonFullSupply.json').respond(200, {"nonFullSupplyProducts": facilityApprovedNFSProducts}); $rootScope.fixToolBar = function () { }; @@ -105,7 +107,7 @@ describe('CreateRequisitionController', function () { ctrl = controller(CreateRequisitionController, {$scope: scope, $location: location, requisitionData: {rnr: mockedRequisition}, rnrColumns: rnrColumns, regimenTemplate: regimenTemplate, currency: '$', lossesAndAdjustmentsTypes: lossesAndAdjustmentTypes, - facilityApprovedProducts: facilityApprovedProducts, requisitionRights: requisitionRights, $routeParams: routeParams, + facilityApprovedNFSProducts: facilityApprovedNFSProducts, requisitionRights: requisitionRights, $routeParams: routeParams, $rootScope: rootScope, localStorageService: localStorageService, pageSize: pageSize}); })); @@ -136,9 +138,7 @@ describe('CreateRequisitionController', function () { }); it('should get list of Rnr Columns for program', function () { - expect([ - {"testField": "test"} - ]).toEqual(scope.programRnrColumnList); + expect(rnrColumns).toEqual(scope.programRnrColumnList); }); it('should get lossesAndAdjustments types', function () { @@ -146,7 +146,7 @@ describe('CreateRequisitionController', function () { }); it('should get facility approved products', function () { - expect(facilityApprovedProducts).toEqual(scope.facilityApprovedProducts); + expect(facilityApprovedNFSProducts).toEqual(scope.facilityApprovedNFSProducts); }); it('should set visible columns for regimen', function () { @@ -395,8 +395,8 @@ describe('CreateRequisitionController', function () { it('should not set disable flag if rnr is initiated and user has create right', function () { var rnr = {id: "rnrId", fullSupplyLineItems: [], regimenLineItems: [], status: "INITIATED"}; - ctrl = controller(CreateRequisitionController, {$scope: scope, $location: location, requisitionData: {rnr: rnr}, rnrColumns: [], regimenTemplate: regimenTemplate, - currency: '$', pageSize: pageSize, lossesAndAdjustmentsTypes: lossesAndAdjustmentTypes, facilityApprovedProducts: facilityApprovedProducts, + ctrl = controller(CreateRequisitionController, {$scope: scope, $location: location, requisitionData: {rnr: rnr}, rnrColumns: rnrColumns, regimenTemplate: regimenTemplate, + currency: '$', pageSize: pageSize, lossesAndAdjustmentsTypes: lossesAndAdjustmentTypes, facilityApprovedNFSProducts: facilityApprovedNFSProducts, requisitionRights: requisitionRights, $routeParams: routeParams, $rootScope: rootScope, localStorageService: localStorageService}); expect(scope.formDisabled).toEqual(false); @@ -405,8 +405,8 @@ describe('CreateRequisitionController', function () { it('should not set disable flag if rnr is submitted and user have authorize right', function () { var rnr = {id: "rnrId", fullSupplyLineItems: [], regimenLineItems: [], status: "SUBMITTED"}; - ctrl = controller(CreateRequisitionController, {$scope: scope, $location: location, requisitionData: {rnr: rnr}, rnrColumns: [], regimenTemplate: regimenTemplate, - currency: '$', pageSize: pageSize, lossesAndAdjustmentsTypes: lossesAndAdjustmentTypes, facilityApprovedProducts: facilityApprovedProducts, + ctrl = controller(CreateRequisitionController, {$scope: scope, $location: location, requisitionData: {rnr: rnr}, rnrColumns: rnrColumns, regimenTemplate: regimenTemplate, + currency: '$', pageSize: pageSize, lossesAndAdjustmentsTypes: lossesAndAdjustmentTypes, facilityApprovedNFSProducts: facilityApprovedNFSProducts, requisitionRights: requisitionRights, $routeParams: routeParams, $rootScope: rootScope, localStorageService: localStorageService}); expect(scope.formDisabled).toEqual(false); @@ -415,8 +415,8 @@ describe('CreateRequisitionController', function () { it('should set disable flag if rnr is not initiated/submitted', function () { var rnr = {id: "rnrId", fullSupplyLineItems: [], regimenLineItems: [], status: "some random status"}; - ctrl = controller(CreateRequisitionController, {$scope: scope, $location: location, requisitionData: {rnr: rnr}, rnrColumns: [], regimenTemplate: regimenTemplate, - currency: '$', pageSize: pageSize, lossesAndAdjustmentsTypes: lossesAndAdjustmentTypes, facilityApprovedProducts: facilityApprovedProducts, + ctrl = controller(CreateRequisitionController, {$scope: scope, $location: location, requisitionData: {rnr: rnr}, rnrColumns: rnrColumns, regimenTemplate: regimenTemplate, + currency: '$', pageSize: pageSize, lossesAndAdjustmentsTypes: lossesAndAdjustmentTypes, facilityApprovedNFSProducts: facilityApprovedNFSProducts, requisitionRights: requisitionRights, $routeParams: routeParams, $rootScope: rootScope, localStorageService: localStorageService}); expect(scope.formDisabled).toEqual(true); @@ -425,7 +425,7 @@ describe('CreateRequisitionController', function () { it('should make rnr in scope as Rnr Instance', function () { var spyRnr = spyOn(window, 'Rnr').andCallThrough(); ctrl = controller(CreateRequisitionController, {$scope: scope, $location: location, requisitionData: {rnr: mockedRequisition, numberOfMonths: 5}, rnrColumns: rnrColumns, regimenTemplate: regimenTemplate, - currency: '$', pageSize: pageSize, lossesAndAdjustmentsTypes: lossesAndAdjustmentTypes, facilityApprovedProducts: facilityApprovedProducts, + currency: '$', pageSize: pageSize, lossesAndAdjustmentsTypes: lossesAndAdjustmentTypes, facilityApprovedNFSProducts: facilityApprovedNFSProducts, requisitionRights: requisitionRights, $routeParams: routeParams, $rootScope: rootScope, localStorageService: localStorageService}); expect(scope.rnr instanceof Rnr).toBeTruthy(); diff --git a/modules/openlmis-web/src/test/javascript/specs/rnr/services/requisition-service-test.js b/modules/openlmis-web/src/test/javascript/specs/rnr/services/requisition-service-test.js index 111fed9ae3..387248bc7a 100644 --- a/modules/openlmis-web/src/test/javascript/specs/rnr/services/requisition-service-test.js +++ b/modules/openlmis-web/src/test/javascript/specs/rnr/services/requisition-service-test.js @@ -20,14 +20,14 @@ describe("requisitionService", function () { {"id": 3, "name": "dispensingUnit", "position": 3, "source": {"description": "Reference Data", "name": "REFERENCE", "code": "R"}, "sourceConfigurable": false, "label": "Unit/Unit of Issue", "formula": "", "indicator": "U", "used": true, "visible": true, "mandatory": false, "description": "Dispensing unit for this product", "formulaValidationRequired": true}, {"id": 4, "name": "beginningBalance", "position": 4, "source": {"description": "User Input", "name": "USER_INPUT", "code": "U"}, "sourceConfigurable": false, "label": "Beginning Balance", "formula": "", "indicator": "A", "used": true, "visible": true, "mandatory": false, "description": "Stock in hand of previous period.This is quantified in dispensing units", "formulaValidationRequired": true}, {"id": 7, "name": "lossesAndAdjustments", "position": 7, "source": {"description": "User Input", "name": "USER_INPUT", "code": "U"}, "sourceConfigurable": false, "label": "Total Losses / Adjustments", "formula": "D1 + D2+D3...DN", "indicator": "D", "used": true, "visible": false, "mandatory": false, "description": "All kind of looses/adjustments made at the facility", "formulaValidationRequired": true}, - {"id": 8, "name": "quantityRequested", "label":"Requested Quantity", "visible":true, "position":8, "source": {"description": "User Input", "name": "USER_INPUT", "code": "U"}, "sourceConfigurable": false, "formula": "","indicator": "Q", "used": true, "mandatory": false, "description": "Requested Quantity", "formulaValidationRequired": true, "configuredOption": null}, - {"id": 9, "name": "reasonForRequestedQuantity", "label":"Requested Quantity Explanation", "visible": true, "position": 19, "source": {"description": "User Input", "name": "USER_INPUT", "code": "U"}, "sourceConfigurable": false, "formula": "", "indicator": "T","used": true, "mandatory": false, "description": "Reason for Requested Quantity", "formulaValidationRequired":true, "configuredOption": null} + {"id": 8, "name": "quantityRequested", "label":"Requested Quantity", "visible": false, "position":8, "source": {"description": "User Input", "name": "USER_INPUT", "code": "U"}, "sourceConfigurable": false, "formula": "","indicator": "Q", "used": true, "mandatory": false, "description": "Requested Quantity", "formulaValidationRequired": true, "configuredOption": null}, + {"id": 9, "name": "reasonForRequestedQuantity", "label":"Requested Quantity Explanation", "visible": false, "position": 19, "source": {"description": "User Input", "name": "USER_INPUT", "code": "U"}, "sourceConfigurable": false, "formula": "", "indicator": "T","used": true, "mandatory": false, "description": "Reason for Requested Quantity", "formulaValidationRequired":true, "configuredOption": null} ]; - var visibleFullScrollableColumns = [columns[3], columns[4]]; - var visibleFullFixedColumns = [columns[0], columns[1], columns[2]]; + var visibleFullScrollableColumns = [ columns[3], columns[4] ]; + var visibleFullFixedColumns = [ columns[0], columns[1], columns[2] ]; - var visibleNonFullScrollableColumns = [columns[3], columns[8], columns[9]]; - var visibleNonFullFixedColumns = [columns[1], columns[2]]; + var visibleNonFullScrollableColumns = [ columns[3], columns[6], columns[7] ]; + var visibleNonFullFixedColumns = [ columns[1], columns[2] ]; beforeEach(inject(function ($location, $routeParams, $rootScope, _requisitionService_, _messageService_) { location = $location; @@ -128,7 +128,12 @@ describe("requisitionService", function () { it('should skip column from map', function () { var mappedColumns = requisitionService.getMappedVisibleColumns(columns, ['skipped', 'productCode', 'product'], ['beginningBalance']); - expect(mappedColumns.fullSupply.scrollable.length).toEqual(3); + expect(mappedColumns.fullSupply.scrollable.length).toEqual(1); }); + it('should error if requested quantity column not found', function () { + expect(function() { requisitionService.getMappedVisibleColumns([], [], []) } ) + .toThrow(new Error("Requested Quantity column(s) not found")); + }) + }); \ No newline at end of file From 231158509da748aac536b5af6e6998101eab539e Mon Sep 17 00:00:00 2001 From: joshzamor Date: Mon, 8 Sep 2014 17:26:43 -0700 Subject: [PATCH 50/80] OA-125, added very basic SQL select to report and logging in service to print it. --- .../org/openlmis/reporting/service/TemplateService.java | 9 ++++++++- .../openlmis/reporting/service/TemplateServiceTest.java | 1 - reports/OpenLMIS-OrderFillRateByFacility.jrxml | 1 + 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/modules/reporting/src/main/java/org/openlmis/reporting/service/TemplateService.java b/modules/reporting/src/main/java/org/openlmis/reporting/service/TemplateService.java index 9dc77b8f03..b9890a13e2 100644 --- a/modules/reporting/src/main/java/org/openlmis/reporting/service/TemplateService.java +++ b/modules/reporting/src/main/java/org/openlmis/reporting/service/TemplateService.java @@ -14,6 +14,7 @@ import net.sf.jasperreports.engine.JRParameter; import net.sf.jasperreports.engine.JasperCompileManager; import net.sf.jasperreports.engine.JasperReport; +import org.apache.log4j.Logger; import org.openlmis.core.domain.Right; import org.openlmis.core.exception.DataException; import org.openlmis.core.service.MessageService; @@ -49,6 +50,8 @@ public class TemplateService { public static final String PDF_VIEW = "pdf"; public static final String USER_ID_PARAM = "userId"; + private static Logger logger = Logger.getLogger(TemplateService.class); + @Autowired TemplateRepository repository; @@ -120,13 +123,17 @@ private void validateFile(Template template, MultipartFile file) { private TemplateParameter createParameter(Long createdBy, JRParameter jrParameter) { String[] propertyNames = jrParameter.getPropertiesMap().getPropertyNames(); - if (propertyNames.length > 1) { + if (propertyNames.length > 2) { throw new DataException(messageService.message("report.template.extra.properties", jrParameter.getName())); } String displayName = jrParameter.getPropertiesMap().getProperty("displayName"); if (isBlank(displayName)) { throw new DataException(messageService.message("report.template.parameter.display.name.missing", jrParameter.getName())); } + String sqlForSelect = jrParameter.getPropertiesMap().getProperty("sql"); + if(isBlank(sqlForSelect) == false) + logger.info("SQL from report parameter: " + sqlForSelect); + TemplateParameter templateParameter = new TemplateParameter(); templateParameter.setName(jrParameter.getName()); templateParameter.setDisplayName(displayName); diff --git a/modules/reporting/src/test/java/org/openlmis/reporting/service/TemplateServiceTest.java b/modules/reporting/src/test/java/org/openlmis/reporting/service/TemplateServiceTest.java index 716e9b0850..837afc1af2 100644 --- a/modules/reporting/src/test/java/org/openlmis/reporting/service/TemplateServiceTest.java +++ b/modules/reporting/src/test/java/org/openlmis/reporting/service/TemplateServiceTest.java @@ -151,7 +151,6 @@ public void shouldThrowErrorIfDisplayNameOfParameterIsMissing() throws Exception verify(repository, never()).insertWithParameters(template); } - @Test public void shouldThrowErrorIfThereAreExtraParameterProperties() throws Exception { expectedException.expect(DataException.class); expectedException.expectMessage("Error Message"); diff --git a/reports/OpenLMIS-OrderFillRateByFacility.jrxml b/reports/OpenLMIS-OrderFillRateByFacility.jrxml index 5f472a6b65..5bc48eb590 100644 --- a/reports/OpenLMIS-OrderFillRateByFacility.jrxml +++ b/reports/OpenLMIS-OrderFillRateByFacility.jrxml @@ -13,6 +13,7 @@ + From 7ec486891bc739dbc9e6acc6194fb4e1a9bfa940 Mon Sep 17 00:00:00 2001 From: joshzamor Date: Mon, 8 Sep 2014 17:28:05 -0700 Subject: [PATCH 51/80] Changing FTP mode from active to passive --- .../shipment/src/main/resources/applicationContext-shipment.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/shipment/src/main/resources/applicationContext-shipment.xml b/modules/shipment/src/main/resources/applicationContext-shipment.xml index 5745f6128c..0d5abdc474 100644 --- a/modules/shipment/src/main/resources/applicationContext-shipment.xml +++ b/modules/shipment/src/main/resources/applicationContext-shipment.xml @@ -43,7 +43,7 @@ - + From bfddddfafa45b2cef03796a6720af69bcdd2b11b Mon Sep 17 00:00:00 2001 From: joshzamor Date: Mon, 15 Sep 2014 16:52:12 -0700 Subject: [PATCH 52/80] OA-125, switching import of testing frameworks moving ham crest to be before JUnit to avoid namespace collisions within those projects. --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index d3ae478c6c..dcec3cd624 100644 --- a/build.gradle +++ b/build.gradle @@ -54,9 +54,9 @@ subprojects { 'commons-io:commons-io:2.4' - testCompile 'junit:junit:4.8.2', + testCompile 'org.hamcrest:hamcrest-all:1.3', 'org.mockito:mockito-all:1.9.5', - 'org.hamcrest:hamcrest-all:1.3', + 'junit:junit:4.8.2', 'cglib:cglib:2.2.2', 'org.powermock:powermock-mockito-release-full:1.5', 'com.natpryce:make-it-easy:3.1.0' From d821452a766f8299f2b2050853edd70a2e6c68f3 Mon Sep 17 00:00:00 2001 From: joshzamor Date: Mon, 15 Sep 2014 17:04:01 -0700 Subject: [PATCH 53/80] OA-125, connecting select sql to DB Connected select sql from report through to storing in DB. Added ability to template mapper to execute arbitrary sql. --- .../V144__alter_template_parameters.sql | 11 ++++ .../src/main/resources/openlmis_logging.xml | 5 ++ .../reporting/model/TemplateParameter.java | 2 + .../repository/mapper/TemplateMapper.java | 36 ++++++++++++- .../reporting/service/TemplateService.java | 19 +++++-- .../repository/mapper/TemplateMapperIT.java | 52 +++++++++++++++++-- 6 files changed, 114 insertions(+), 11 deletions(-) create mode 100644 modules/db/src/main/resources/db/migration/V144__alter_template_parameters.sql diff --git a/modules/db/src/main/resources/db/migration/V144__alter_template_parameters.sql b/modules/db/src/main/resources/db/migration/V144__alter_template_parameters.sql new file mode 100644 index 0000000000..3612aa81e9 --- /dev/null +++ b/modules/db/src/main/resources/db/migration/V144__alter_template_parameters.sql @@ -0,0 +1,11 @@ +-- +-- This program is part of the OpenLMIS logistics management information system platform software. +-- Copyright © 2013 VillageReach +-- +-- This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +--   +-- 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 Affero General Public License for more details. +-- You should have received a copy of the GNU Affero General Public License along with this program.  If not, see http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  +-- + +ALTER TABLE template_parameters ADD COLUMN selectSql TEXT; diff --git a/modules/openlmis-web/src/main/resources/openlmis_logging.xml b/modules/openlmis-web/src/main/resources/openlmis_logging.xml index ae815b262c..da348dd11d 100644 --- a/modules/openlmis-web/src/main/resources/openlmis_logging.xml +++ b/modules/openlmis-web/src/main/resources/openlmis_logging.xml @@ -55,6 +55,11 @@ + + + + + diff --git a/modules/reporting/src/main/java/org/openlmis/reporting/model/TemplateParameter.java b/modules/reporting/src/main/java/org/openlmis/reporting/model/TemplateParameter.java index 30782c488e..d34dbb9e54 100644 --- a/modules/reporting/src/main/java/org/openlmis/reporting/model/TemplateParameter.java +++ b/modules/reporting/src/main/java/org/openlmis/reporting/model/TemplateParameter.java @@ -51,6 +51,8 @@ public class TemplateParameter extends BaseModel { private String dataType; + private String selectSql; + private String description; public Object getParsedValueOf(String value) throws ParseException { diff --git a/modules/reporting/src/main/java/org/openlmis/reporting/repository/mapper/TemplateMapper.java b/modules/reporting/src/main/java/org/openlmis/reporting/repository/mapper/TemplateMapper.java index 8e43919466..e6a370bb44 100644 --- a/modules/reporting/src/main/java/org/openlmis/reporting/repository/mapper/TemplateMapper.java +++ b/modules/reporting/src/main/java/org/openlmis/reporting/repository/mapper/TemplateMapper.java @@ -57,11 +57,43 @@ public interface TemplateMapper { "INNER JOIN role_assignments ra ON ra.roleId = rr.roleId WHERE ra.userId = #{userId}"}) List