/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package entity.prescription; import entity.Ownable; import entity.files.SYSPRE2FILE; import entity.info.ResInfo; import entity.info.Resident; import entity.nursingprocess.Intervention; import entity.process.QProcess; import entity.process.QProcessElement; import entity.process.SYSPRE2PROCESS; import entity.system.Commontags; import entity.system.Users; import op.OPDE; import op.tools.SYSCalendar; import op.tools.SYSConst; import op.tools.SYSTools; import org.apache.commons.collections.Closure; import org.apache.commons.collections.CollectionUtils; import org.eclipse.persistence.annotations.OptimisticLocking; import org.eclipse.persistence.annotations.OptimisticLockingType; import org.joda.time.DateTime; import org.joda.time.Interval; import org.joda.time.LocalDate; import javax.persistence.*; import java.io.Serializable; import java.text.DateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.List; /** * OPDE kann verschiedene Arten von wiederkehrenden Terminen für die Anwendung einer ärztlichen Verordnung * speichern. Dazu sind zwei Entity Classes nötig. Verordnungen und VerordnungPlanung. * <ul> * <li><b>Verordnungen</b> enthält die Angaben über die Medikamente und Maßnahmen. Ärzte und Krankenhäuser, sowie Situations bei * Bedarfsmedikattion.</li> * <li><b>VerordnungPlanung</b> sind die Termine und die Dosis bzw. Häufigkeit in der ein Medikament oder eine Maßnahme angewendet werden soll. * Ebenso stehen hier drin, die maximale Tagesdosis einer Bedarfsmedikation. Zu jeder Verordnung können unterschiedliche Termin-Muster und Dosen * eingegeben werden. Für jede dieser Einträge gibt es ein Objekt aus dieser Klasse.</li> * </ul> * Folgende Terminarten können in OPDE formuliert werden: * <ul> * <li><b>Regelverordnungen</b>, die einem bestimmten chronologischen Muster folgen * <ul> * <li><b>täglich</b>, bzw. alle <i>n</i> Tage.</li> * <li><b>wöchentlich</b>, bzw. alle <i>n</i> Wochen, an unterschiedlichen Wochentagen.</li> * <li><b>monatlich</b>, bzw. alle <i>n</i> Monate, jeweils am <i>m.</i> Tag des Monats oder am <i>o.</i> Wochentag des Monats.</li> * <li>es kann ein Datum festgelegt werden, ab dem <b>die erste Anwendung</b> dieser Vergabe erfolgen soll. Dies steht normalerweise auf dem aktuellen Tagesdatum.</li> * <li>Die einzelnen Dosisangaben können für vordefinierte Tageszeiten oder für eine Uhrzeit eingegeben werden. * <ul> * <li>Nachts, früh morgens</li> * <li>Morgens</li> * <li>Mittags</li> * <li>Nachmittags</li> * <li>Abends</li> * <li>Nachts, spät abends</li> * <li>Uhrzeit</li> * </ul> * </li> * </ul> * </li> * <li><b>Bedarfsverordnungen</b>, die nur für die Anwendung in <b>bestimmten Situations</b> gedacht sind.</li> * </ul> * <h2>Beispiele</h2> * <h3>Bedarfsmedikation</h3> * <img src="http://www.offene-pflege.de/images/stories/opde/medi/prescription-bedarf1.png" /> * <ul> * <li><code><b>Verordnung</b>{verid=4658, anDatum=Thu Dec 22 15:54:14 CET 2011, abDatum=Fri Dec 31 23:59:59 CET 9999, bisPackEnde=false, verKennung=3580, bemerkung='', stellplan=false, attachedFiles=[], attachedVorgaenge=[], angesetztDurch=Löhr, Torsten [tloehr], abgesetztDurch=null, bewohner=[JH1], massnahme=entity.rest.Massnahmen[massID=140], darreichung=entity.rest.Darreichung[dafID=1336], situation=entity.rest.Situations[sitID=10], anKH=entity.rest.Hospital[khid=16], abKH=null, anArzt=entity.rest.GP[arztID=21], abArzt=null}</code></li> * <li><code><b>VerordnungPlanung</b>{bhppid=7403, nachtMo=0, morgens=0, mittags=0, nachmittags=0, abends=0, nachtAb=0, uhrzeitDosis=0, uhrzeit=null, maxAnzahl=1, maxEDosis=2, taeglich=1, woechentlich=0, monatlich=0, tagNum=0, mon=0, die=0, mit=0, don=0, fre=0, sam=0, son=0, lDatum=Thu Dec 22 15:55:05 CET 2011, uKennung='tloehr', prescription=Verordnung{verid=4658, ...}}</code></li> * </ul> * <p> * <h3>Regelverordnung mit sehr unterschiedlichen Dosierungen</h3> * <ul> * <li><img src="http://www.offene-pflege.de/images/stories/opde/medi/prescription-regel123.png" /><p/><code><b>Verordnung</b>{verid=4659, anDatum=Thu Dec 22 16:09:09 CET 2011, abDatum=Fri Dec 31 23:59:59 CET 9999, bisPackEnde=false, verKennung=3581, bemerkung='', stellplan=false, attachedFiles=[], attachedVorgaenge=[], angesetztDurch=Löhr, Torsten [tloehr], abgesetztDurch=null, bewohner=[JH1], massnahme=entity.rest.Massnahmen[massID=140], darreichung=entity.rest.Darreichung[dafID=1336], situation=null, anKH=null, abKH=null, anArzt=entity.rest.GP[arztID=1], abArzt=null}</code></li> * <li><img src="http://www.offene-pflege.de/images/stories/opde/medi/prescription-regel1.png" /><p/><code><b>VerordnungPlanung</b>{bhppid=7406, nachtMo=0, morgens=1, mittags=1, nachmittags=0, abends=1, nachtAb=0, uhrzeitDosis=0, uhrzeit=null, maxAnzahl=0, maxEDosis=0, taeglich=1, woechentlich=0, monatlich=0, tagNum=0, mon=0, die=0, mit=0, don=0, fre=0, sam=0, son=0, lDatum=Thu Dec 22 16:12:49 CET 2011, uKennung='tloehr', prescription=Verordnung{verid=4659, ...}}</code></li> * <li><img src="http://www.offene-pflege.de/images/stories/opde/medi/prescription-regel2.png" /><p/><code><b>VerordnungPlanung</b>{bhppid=7404, nachtMo=0, morgens=0, mittags=0, nachmittags=0, abends=0, nachtAb=0, uhrzeitDosis=2.5, uhrzeit=Thu Dec 22 22:00:00 CET 2011, maxAnzahl=0, maxEDosis=0, taeglich=0, woechentlich=1, monatlich=0, tagNum=0, mon=0, die=1, mit=0, don=0, fre=0, sam=1, son=0, lDatum=Thu Dec 22 16:10:52 CET 2011, uKennung='tloehr', prescription=Verordnung{verid=4659, ...}}</code></li> * <li><img src="http://www.offene-pflege.de/images/stories/opde/medi/prescription-regel3.png" /><p/><code><b>VerordnungPlanung</b>{bhppid=7405, nachtMo=0, morgens=0, mittags=0, nachmittags=0, abends=3, nachtAb=0, uhrzeitDosis=0, uhrzeit=null, maxAnzahl=0, maxEDosis=0, taeglich=0, woechentlich=0, monatlich=2, tagNum=0, mon=0, die=0, mit=0, don=0, fre=0, sam=1, son=0, lDatum=Thu Dec 22 16:11:49 CET 2011, uKennung='tloehr', prescription=Verordnung{verid=4659, ...}}</code></li> * </ul> * * @author tloehr */ @Entity @Table(name = "prescription") @OptimisticLocking(cascade = false, type = OptimisticLockingType.VERSION_COLUMN) public class Prescription extends Ownable implements Serializable, QProcessElement, Cloneable, Comparable<Prescription> { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "VerID") private Long id; @Version @Column(name = "version") private Long version; @Basic(optional = false) @Column(name = "AnDatum") @Temporal(TemporalType.TIMESTAMP) private Date from; @Basic(optional = false) @Column(name = "AbDatum") @Temporal(TemporalType.TIMESTAMP) private Date to; @Column(name = "BisPackEnde") private boolean toEndOfPackage; @Basic(optional = false) @Column(name = "VerKennung") private long relation; @Lob @Column(name = "Bemerkung") private String text; @Basic(optional = false) @Column(name = "Stellplan") private boolean showOnDailyPlan; // @Basic(optional = false) // @Column(name = "weightcontrol") // private boolean weightControl; // == // 1:N Relationen // == @OneToMany(orphanRemoval = true, cascade = CascadeType.ALL, mappedBy = "prescription") private List<SYSPRE2FILE> attachedFilesConnections; @OneToMany(orphanRemoval = true, cascade = CascadeType.ALL, mappedBy = "prescription") private List<SYSPRE2PROCESS> attachedProcessConnections; @OneToMany(orphanRemoval = true, cascade = CascadeType.ALL, mappedBy = "prescription") private List<PrescriptionSchedule> pSchedule; // these are the annotations for a prescription. currently only used for the MRE studies @OneToMany(orphanRemoval = true, cascade = CascadeType.ALL, mappedBy = "prescription") private Collection<ResInfo> annotations; // @OneToMany(cascade = CascadeType.ALL, mappedBy = "prescription") // private List<BHP> bhps; // == // N:1 Relationen // == @JoinColumn(name = "AnUKennung", referencedColumnName = "UKennung") @ManyToOne private Users userON; @JoinColumn(name = "AbUKennung", referencedColumnName = "UKennung") @ManyToOne private Users userOFF; @JoinColumn(name = "BWKennung", referencedColumnName = "BWKennung") @ManyToOne private Resident resident; @JoinColumn(name = "MassID", referencedColumnName = "MassID") @ManyToOne private Intervention intervention; @JoinColumn(name = "DafID", referencedColumnName = "DafID") @ManyToOne private TradeForm tradeform; @JoinColumn(name = "SitID", referencedColumnName = "SitID") @ManyToOne private Situations situation; @JoinColumn(name = "AnKHID", referencedColumnName = "KHID") @ManyToOne private Hospital hospitalON; @JoinColumn(name = "AbKHID", referencedColumnName = "KHID") @ManyToOne private Hospital hospitalOFF; @JoinColumn(name = "AnArztID", referencedColumnName = "ArztID") @ManyToOne private GP docON; @JoinColumn(name = "AbArztID", referencedColumnName = "ArztID") @ManyToOne private GP docOFF; @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST) @JoinTable(name = "prescription2tags", joinColumns = @JoinColumn(name = "prescid"), inverseJoinColumns = @JoinColumn(name = "ctagid")) private Collection<Commontags> commontags; public Prescription() { } public Prescription(Resident resident) { this.resident = resident; this.attachedFilesConnections = new ArrayList<SYSPRE2FILE>(); this.attachedProcessConnections = new ArrayList<SYSPRE2PROCESS>(); this.pSchedule = new ArrayList<PrescriptionSchedule>(); this.from = new Date(); this.to = SYSConst.DATE_UNTIL_FURTHER_NOTICE; this.userON = OPDE.getLogin().getUser(); this.commontags = new ArrayList<>(); this.annotations = new ArrayList<>(); // this.weightControl = false; } public Prescription(Date from, Date to, boolean toEndOfPackage, long relation, String text, boolean showOnDailyPlan, List<SYSPRE2FILE> attachedFilesConnections, List<SYSPRE2PROCESS> attachedProcessConnections, Users userON, Users userOFF, Resident resident, Intervention intervention, TradeForm tradeform, Situations situation, Hospital hospitalON, Hospital hospitalOFF, GP docON, GP docOFF) { this.from = from; this.to = to; this.toEndOfPackage = toEndOfPackage; this.relation = relation; this.text = SYSTools.tidy(text); this.showOnDailyPlan = showOnDailyPlan; this.attachedFilesConnections = attachedFilesConnections; this.attachedProcessConnections = attachedProcessConnections; this.userON = userON; this.userOFF = userOFF; this.resident = resident; this.intervention = intervention; this.tradeform = tradeform; this.situation = situation; this.hospitalON = hospitalON; this.hospitalOFF = hospitalOFF; this.docON = docON; this.docOFF = docOFF; this.pSchedule = new ArrayList<PrescriptionSchedule>(); this.commontags = new ArrayList<Commontags>(); this.annotations = new ArrayList<>(); } public Collection<Commontags> getCommontags() { return commontags; } public Collection<ResInfo> getAnnotations() { return annotations; } public Date getFrom() { return from; } public void setFrom(Date from) { this.from = from; } public Date getTo() { return to; } public void setTo(Date to) { this.to = to; } public Hospital getHospitalON() { return hospitalON; } public void setHospitalON(Hospital anKH) { this.hospitalON = anKH; } public Hospital getHospitalOFF() { return hospitalOFF; } public void setHospitalOFF(Hospital abKH) { this.hospitalOFF = abKH; } public GP getDocON() { return docON; } public void setDocON(GP anDoc) { this.docON = anDoc; } public GP getDocOFF() { return docOFF; } public void setDocOFF(GP abDoc) { this.docOFF = abDoc; } public boolean isUntilEndOfPackage() { return toEndOfPackage; } public void setUntilEndOfPackage(boolean tillEndOfPackage) { this.toEndOfPackage = tillEndOfPackage; } public long getRelation() { return relation; } public void setRelation(long verKennung) { this.relation = verKennung; } public String getText() { return SYSTools.catchNull(text); } public void setText(String bemerkung) { this.text = SYSTools.tidy(bemerkung); } public boolean isOnDailyPlan() { return showOnDailyPlan; } public void setShowOnDailyPlan(boolean show) { this.showOnDailyPlan = show; } public Users getUserOFF() { return userOFF; } public Situations getSituation() { return situation; } public boolean hasMed() { return tradeform != null; } public boolean shouldBeCalculated() { // TODO: distinction between the several UPR modes return hasMed() && resident.isCalcMediUPR1(); } public boolean isWeightControlled() { return hasMed() && tradeform.isWeightControlled(); } public void setSituation(Situations situation) { this.situation = situation; } public TradeForm getTradeForm() { return tradeform; } public void setTradeForm(TradeForm tradeform) { this.tradeform = tradeform; } public Intervention getIntervention() { return intervention; } public void setIntervention(Intervention massnahme) { this.intervention = massnahme; } public void setUserOFF(Users userOFF) { this.userOFF = userOFF; } public Users getUserON() { return userON; } public void setUserON(Users userON) { this.userON = userON; } @Override public Users getOwner() { return userON; } @Override public String getTitle() { return SYSTools.xx("nursingrecords.prescription") + ": " + PrescriptionTools.getShortDescriptionAsCompactText(this); } public Resident getResident() { return resident; } public void setResident(Resident resident) { this.resident = resident; } public boolean isClosed() { return new DateTime(to).isBeforeNow(); } public boolean isActiveOn(LocalDate day) { return new Interval(new DateTime(from), new DateTime(to)).overlaps(new Interval(day.toDateTimeAtStartOfDay(), SYSCalendar.eod(day))); } public boolean isLimited() { return to.before(SYSConst.DATE_UNTIL_FURTHER_NOTICE); } public boolean isOnDemand() { return situation != null; } public List<SYSPRE2FILE> getAttachedFilesConnections() { return attachedFilesConnections; } public List<SYSPRE2PROCESS> getAttachedProcessConnections() { return attachedProcessConnections; } public List<PrescriptionSchedule> getPrescriptionSchedule() { return pSchedule; } @Override public Users getUser() { return userON; } @Override public long getPITInMillis() { return from.getTime(); } @Override public ArrayList<QProcess> getAttachedProcesses() { ArrayList<QProcess> list = new ArrayList<QProcess>(); for (SYSPRE2PROCESS att : attachedProcessConnections) { list.add(att.getQProcess()); } return list; } @Override public String getContentAsHTML() { return PrescriptionTools.getPrescriptionAsHTML(this, false, false, true, false); } @Override public String getPITAsHTML() { String result = ""; DateFormat df = DateFormat.getDateInstance(); if (isClosed()) { result += "<table id=\"fonttext\" border=\"0\" cellspacing=\"0\">"; result += "<tr>"; result += "<td valign=\"top\"><b>" + df.format(from) + "</b></td>"; result += "<td valign=\"top\">»</td>"; result += "<td valign=\"top\"><b>" + df.format(to) + "</b></td>"; result += "</tr>\n"; result += "<tr>"; result += "<td valign=\"top\">" + userON.getFullname() + "</td>"; result += "<td valign=\"top\">»</td>"; result += "<td valign=\"top\">" + userOFF.getFullname() + "</td>"; result += "</tr>\n"; if (docON != null || docOFF != null) { result += "<tr>"; result += "<td valign=\"top\">" + GPTools.getFullName(docON) + "</td>"; result += "<td valign=\"top\">»</td>"; result += "<td valign=\"top\">" + GPTools.getFullName(docOFF) + "</td>"; result += "</tr>\n"; } if (hospitalON != null || hospitalOFF != null) { result += "<tr>"; result += "<td valign=\"top\">" + HospitalTools.getFullName(hospitalON) + "</td>"; result += "<td valign=\"top\">»</td>"; result += "<td valign=\"top\">" + HospitalTools.getFullName(hospitalOFF) + "</td>"; result += "</tr>\n"; } result += "</table>\n"; } else { if (to.before(SYSConst.DATE_UNTIL_FURTHER_NOTICE)) { result += SYSConst.html_bold(df.format(from)) + " » " + SYSConst.html_bold(df.format(to)); } else { result += SYSConst.html_bold(df.format(from)) + " »»"; } result += "<br/>" + userON.getFullname(); if (docON != null) { result += "<br/>"; result += GPTools.getFullName(docON); } if (hospitalON != null) { result += "<br/>"; result += HospitalTools.getFullName(hospitalON); } } result += "<br>" + "[" + id + "]"; return result; } @Override public long getID() { return id; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Prescription that = (Prescription) o; if (toEndOfPackage != that.toEndOfPackage) return false; if (showOnDailyPlan != that.showOnDailyPlan) return false; if (relation != that.relation) return false; if (docOFF != null ? !docOFF.equals(that.docOFF) : that.docOFF != null) return false; if (to != null ? !to.equals(that.to) : that.to != null) return false; if (hospitalOFF != null ? !hospitalOFF.equals(that.hospitalOFF) : that.hospitalOFF != null) return false; if (userOFF != null ? !userOFF.equals(that.userOFF) : that.userOFF != null) return false; if (docON != null ? !docON.equals(that.docON) : that.docON != null) return false; if (from != null ? !from.equals(that.from) : that.from != null) return false; if (hospitalON != null ? !hospitalON.equals(that.hospitalON) : that.hospitalON != null) return false; if (userON != null ? !userON.equals(that.userON) : that.userON != null) return false; if (attachedFilesConnections != null ? !attachedFilesConnections.equals(that.attachedFilesConnections) : that.attachedFilesConnections != null) return false; if (attachedProcessConnections != null ? !attachedProcessConnections.equals(that.attachedProcessConnections) : that.attachedProcessConnections != null) return false; if (text != null ? !text.equals(that.text) : that.text != null) return false; if (intervention != null ? !intervention.equals(that.intervention) : that.intervention != null) return false; if (resident != null ? !resident.equals(that.resident) : that.resident != null) return false; if (situation != null ? !situation.equals(that.situation) : that.situation != null) return false; if (tradeform != null ? !tradeform.equals(that.tradeform) : that.tradeform != null) return false; if (id != null ? !id.equals(that.id) : that.id != null) return false; if (version != null ? !version.equals(that.version) : that.version != null) return false; return true; } @Override public int hashCode() { int result = id != null ? id.hashCode() : 0; result = 31 * result + (version != null ? version.hashCode() : 0); result = 31 * result + (from != null ? from.hashCode() : 0); result = 31 * result + (to != null ? to.hashCode() : 0); result = 31 * result + (toEndOfPackage ? 1 : 0); result = 31 * result + (int) (relation ^ (relation >>> 32)); result = 31 * result + (text != null ? text.hashCode() : 0); result = 31 * result + (showOnDailyPlan ? 1 : 0); result = 31 * result + (attachedFilesConnections != null ? attachedFilesConnections.hashCode() : 0); result = 31 * result + (attachedProcessConnections != null ? attachedProcessConnections.hashCode() : 0); result = 31 * result + (userON != null ? userON.hashCode() : 0); result = 31 * result + (userOFF != null ? userOFF.hashCode() : 0); result = 31 * result + (resident != null ? resident.hashCode() : 0); result = 31 * result + (intervention != null ? intervention.hashCode() : 0); result = 31 * result + (tradeform != null ? tradeform.hashCode() : 0); result = 31 * result + (situation != null ? situation.hashCode() : 0); result = 31 * result + (hospitalON != null ? hospitalON.hashCode() : 0); result = 31 * result + (hospitalOFF != null ? hospitalOFF.hashCode() : 0); result = 31 * result + (docON != null ? docON.hashCode() : 0); result = 31 * result + (docOFF != null ? docOFF.hashCode() : 0); return result; } @Override public Prescription clone() { final Prescription prescriptionClone = new Prescription(from, to, toEndOfPackage, relation, text, showOnDailyPlan, attachedFilesConnections, attachedProcessConnections, OPDE.getLogin().getUser(), userOFF, resident, intervention, tradeform, situation, hospitalON, hospitalOFF, docON, docOFF); CollectionUtils.forAllDo(pSchedule, new Closure() { public void execute(Object o) { PrescriptionSchedule scheduleCopy = ((PrescriptionSchedule) o).createCopy(prescriptionClone); prescriptionClone.getPrescriptionSchedule().add(scheduleCopy); } }); for (Commontags ctag : commontags) { prescriptionClone.getCommontags().add(ctag); } for (ResInfo annotation : annotations) { ResInfo annotationClone = annotation.clone(); annotationClone.setPrescription(prescriptionClone); prescriptionClone.getAnnotations().add(annotationClone); } return prescriptionClone; } @Override public int compareTo(Prescription them) { // int result = ((Boolean) isClosed()).compareTo(them.isClosed()) * -1; int result = ((Boolean) isOnDemand()).compareTo(them.isOnDemand()) * -1; if (result == 0) { result = ((Boolean) hasMed()).compareTo(them.hasMed()); } if (result == 0) { String mytitle = hasMed() ? getTradeForm().getMedProduct().getText() : getIntervention().getBezeichnung(); String thattitle = hasMed() ? them.getTradeForm().getMedProduct().getText() : them.getIntervention().getBezeichnung(); result = mytitle.compareTo(thattitle); } if (result == 0) { result = new Long(relation).compareTo(them.getRelation()); } if (result == 0) { result = ((Boolean) isClosed()).compareTo(them.isClosed()); } if (result == 0) { result = from.compareTo(them.getFrom()) * -1; } return result; } @Override public String toString() { return "Prescription{" + "verid=" + id; } }