package entity.prescription;
import op.OPDE;
import op.tools.SYSTools;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.swing.*;
import java.awt.*;
import java.util.Locale;
/**
* Created by IntelliJ IDEA.
* User: tloehr
* Date: 15.12.11
* Time: 15:24
* To change this template use File | Settings | File Templates.
*/
public class MedPackageTools {
public static final String GROESSE[] = {"N1", "N2", "N3", "AP", "OP"};
public static ListCellRenderer getMedPackungRenderer() {
return (jList, o, i, b, b1) -> {
String text;
if (o == null) {
text = SYSTools.toHTML("<i>Keine Auswahl</i>");
} else if (o instanceof MedPackage) {
MedPackage aPackage = (MedPackage) o;
text = toPrettyString(aPackage);
} else {
text = o.toString();
}
return new DefaultListCellRenderer().getListCellRendererComponent(jList, text, i, b, b1);
};
}
public static String toPrettyString(MedPackage aPackage) {
String text = SYSTools.formatBigDecimal(aPackage.getContent()) + " " + TradeFormTools.getPackUnit(aPackage.getTradeForm()) + ", " + GROESSE[aPackage.getSize()] + ", ";
text += "PZN: " + aPackage.getPzn();
return text;
}
/**
* Checks if a PZN is valid and id not already in use.
* Testet ob eine neue PZN gültig ist. Also ob sie 7 oder 8 Zeichen lang ist. Führende 'ß' Zeichen (kommt bei den Barcodes vor)
* werden abgeschnitten. Und es wird anhand der Datenbank geprüft, ob die PZN noch frei ist oder nicht.
*
* @param pzn die geprüfte und bereinigte PZN. <code>null</code> bei falscher oder belegter PZN.
* @param ignoreMe lässt die betreffende Packung bei der Suche ausser acht. Null, wenn nicht gewünscht.
* @return gibt die PZN zurück, wenn sie gültig ist. NULL sonst.
*/
public static String checkNewPZN(String pzn, MedPackage ignoreMe) {
// pzn = parsePZN(pzn);
if (pzn != null) {
// PZN's darfs nur einmal geben. Gibts die hier schon ?
// Dann ist die Packung falsch.
EntityManager em = OPDE.createEM();
String jpql = "SELECT m FROM MedPackage m WHERE m.pzn = :pzn " + (ignoreMe != null ? " AND m <> :medPackage " : "");
Query query = em.createQuery(jpql);
query.setParameter("pzn", pzn);
if (ignoreMe != null) {
query.setParameter("medPackage", ignoreMe);
}
if (!query.getResultList().isEmpty()) {
pzn = null;
}
em.close();
}
return pzn;
}
/**
* This method checks if a given string represents a valid german PZN, which may (as of 2013) have a length
* of 7 or 8 chars. It must also be conform to the checksum algorithm defined by SecurPharm.
* <p>
* Extension for the <b>austrian</b> PZN system. In austria the PZN as a fixed size of 7 (well, 6 + the checkdigit).
* It is integrated into an EAN13 of a special structure.
* <p>
* All the barcode scanners that came across added a "ß" at the front of the scanned number. So this
* has to be removed if present.
*
* @param pzn the string to be checked
* @return the cleaned and checked string. PZN7's are always added up to PZN8's (by puttin a zero to the head). If the PZN was invalid you will only get NULL.
*/
public static String parsePZN(String pzn) throws NumberFormatException {
pzn = pzn.trim();
pzn = pzn.replaceAll("[^\\d]", "");
String countrycode = Locale.getDefault().getCountry().toLowerCase();
if (!countrycode.matches("de|at|ch"))
countrycode = "de";
if (countrycode.equals("de")) {
if (pzn.matches("^\\d{7,8}")) {
// pzn = (pzn.startsWith("ß") ? pzn.substring(1) : pzn);
// this is only temporarily until the PZN7's are gone completely. it may take some years.
if (pzn.length() == 7) {
pzn = "0" + pzn;
}
if (!isPZNValid(pzn, getMOD11Checksum(pzn))) {
pzn = null;
}
} else {
throw new NumberFormatException("error.pzn.de");
}
} else if (countrycode.equals("at")) {
// the austrian PZNs are also checked by teh MOD11 procedure.
if (pzn.matches("^\\d{7}")) {
// this is only temporarily until the PZN7's are gone completely. it may take some years.
if (pzn.length() == 7) {
pzn = "0" + pzn;
}
if (!isPZNValid(pzn, getMOD11Checksum(pzn))) {
throw new NumberFormatException("error.pzn.at");
}
} else if (pzn.matches("^\\d{13}")) { // when the EAN Code is used, the checkdigit of the PZN is not available anymore. It is unimportant anyways, because EAN13 makes sure, that there are no typos.
if (pzn.startsWith("908888")) { // 90 is austria, 8888 is the pharma code for 'ARGE Pharma'
pzn = pzn.substring(6, 12);
pzn += Integer.toString(getMOD11Checksum(pzn + "0")); // calculate a proper MOD11 checksum. the "0" is a dummy checksum with is ignored by MOD11 anyways
// this is only temporarily until the PZN7's are gone completely. it may take some years.
if (pzn.length() == 7) {
pzn = "0" + pzn;
}
} else {
throw new NumberFormatException("error.pzn.at");
}
} else {
throw new NumberFormatException("error.pzn.at");
}
} else if (countrycode.equals("ch")) {
if (pzn.matches("^\\d{13}")) {
if (pzn.startsWith("7680") && isPZNValid(pzn, getSwissmedicChecksum(pzn.substring(0, 12)))) { // GS1-CH prefix for pharmaceuticals
pzn = pzn.substring(4, 12); // we only need the pharma part of the number. the table can handle only 8 digits anyways.
} else {
throw new NumberFormatException("error.pzn.ch");
}
} else {
// https://github.com/tloehr/Offene-Pflege.de/issues/33
throw new NumberFormatException("error.pzn.ch");
}
}
return pzn;
}
/**
* checks the validity of a given PZN according to the algorithm defined by SecurPharm
* <br/>
* <br/>
* <img src="http://www.offene-pflege.de/images/javadoc/pzn-checksum-calculation.png">
* <br/>
* <br/>
* <i>This picture has been taken from the german PZN8 document. It is copyrighted to
* Informationsstelle für Arzneispezialitäten - IFA GmbH</i>
*
* @return guess
*/
private static boolean isPZNValid(String pzn, int calculatedChecksum) {
int givenChecksum = Integer.parseInt(String.valueOf(pzn.charAt(pzn.length() - 1)));
if (calculatedChecksum != givenChecksum) {
OPDE.debug("PZN is NOT valid");
}
return calculatedChecksum == givenChecksum;
}
/**
* http://www.gs1.ch/docs/default-source/gs1-system-document/genspecs/genspec-kapitel9.pdf?sfvrsn=2
* Page 21
*
* @param pzn
* @return
*/
public static int getSwissmedicChecksum(String pzn) {
if (pzn.isEmpty()) return -1;
OPDE.debug(pzn);
int[] digits = new int[pzn.length()];
int partsum = 0;
for (int c = 0; c < pzn.length(); c++) {
digits[c] = Integer.parseInt(String.valueOf(pzn.charAt(c)));
partsum += digits[c] * (c % 2 == 0 ? 1 : 3);
}
// next complete decade to the partsum
int nextDecade = (partsum / 10 + 1) * 10;
return nextDecade - partsum;
}
/**
* checks the validity of a given PZN according to the algorithm defined by GS1-CH and Swissmedic
* <i>This picture has been taken from the german PZN8 document. It is copyrighted to
* Informationsstelle für Arzneispezialitäten - IFA GmbH</i>
*
* @return guess
*/
public static int getMOD11Checksum(String pzn) {
if (pzn.isEmpty()) return -1;
// OPDE.debug(pzn);
int[] digits = new int[pzn.length() - 1];
for (int c = 0; c < pzn.length() - 1; c++) {
digits[c] = Integer.parseInt(String.valueOf(pzn.charAt(c)));
}
int weightedSum = 0;
int w = 7;
for (int c = digits.length - 1; c >= 0; c--) {
weightedSum += digits[c] * w;
w--;
}
int calculatedChecksum = weightedSum % 11;
return calculatedChecksum;
}
}