/******************************************************************************* * * The authorship of this code and the accompanying materials is held by * medshare GmbH, Switzerland. All rights reserved. * http://medshare.net * * This code and the accompanying materials are made available under * the terms of the Eclipse Public License v1.0 * * Year of publication: 2012 * *******************************************************************************/ package net.medshare.connector.aerztekasse.invoice; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.Properties; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import net.medshare.connector.aerztekasse.MessagesAK; import net.medshare.connector.aerztekasse.data.AerztekasseSettings; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.DirectoryDialog; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.progress.IProgressService; import ch.elexis.TarmedRechnung.XMLExporter; import ch.elexis.core.data.activator.CoreHub; import ch.elexis.core.data.interfaces.IRnOutputter; import ch.elexis.core.ui.util.SWTHelper; import ch.elexis.data.Rechnung; import ch.elexis.data.RnStatus; import ch.rgw.tools.ExHandler; import ch.rgw.tools.Result; public class InvoiceOutputter extends XMLExporter { // public only because of net.medshare.connector.aerztekasse_test public AerztekasseSettings settings; public boolean transferState = false; private String responseState; private String responseError; private String outputDir; private String transmittedInvoices; private String failedInvoices; private int transmittedInvoicesCount = 0; private int failedInvoicesCount = 0; /** * Output und Übertragun für eine Liste von Rechnungen an die Ärztekasse. Die Rechnungen werden * jede einzeln mit doExport als XML gespeichert, gezippt und an die Ärztekasse übermittelt. Bei * erfolgreicher übermittlung wird der Status der Rechnung auf bezahlt gesetzt. * * @param type * desired mode (original, copy, storno) * @param rnn * a Collection of Rechnung - Objects to output */ @Override public Result<Rechnung> doOutput(final IRnOutputter.TYPE type, final Collection<Rechnung> rnn, Properties props){ final Result<Rechnung> ret = new Result<Rechnung>(); transmittedInvoices = ""; //$NON-NLS-1$ failedInvoices = ""; //$NON-NLS-1$ IProgressService progressService = PlatformUI.getWorkbench().getProgressService(); if (outputDir == null) { SWTHelper.SimpleDialog dlg = new SWTHelper.SimpleDialog(new SWTHelper.IControlProvider() { @Override public Control getControl(Composite parent){ return createSettingsControl(parent); } @Override public void beforeClosing(){ // Nothing } }); if (dlg.open() != Dialog.OK) { return ret; } } try { progressService.runInUI(PlatformUI.getWorkbench().getProgressService(), new IRunnableWithProgress() { @Override public void run(final IProgressMonitor monitor){ boolean abort = false; monitor.beginTask(MessagesAK.InvoiceOutputter_DoExport, rnn.size() * 3); File f = new File(outputDir); f.mkdirs(); // XML und ArrayList mit Rechnungsnamen aller Rechnungen erstellen ArrayList<String> fileNamesXml = new ArrayList<String>(rnn.size()); for (Rechnung rn : rnn) { String fileNameXml = rn.getNr(); String filePathXml = outputDir + fileNameXml + ".xml"; //$NON-NLS-1$ if (type == TYPE.STORNO) { fileNameXml += "_storno.xml"; //$NON-NLS-1$ } else { fileNameXml += ".xml"; //$NON-NLS-1$ } fileNamesXml.add(fileNameXml); if (doExport(rn, filePathXml, type, false) == null) { ret.add(Result.SEVERITY.ERROR, 1, MessagesAK.InvoiceOutputter_ErrorInInvoice + rn.getNr(), rn, true); abort = true; break; } monitor.worked(1); if (monitor.isCanceled()) { break; } } // XMLs Zippen wenn do Export aller Rechnung ok war // Name of the ZIP file String filePathZip = outputDir + "invoices.zip"; //$NON-NLS-1$ if (!abort) { try { // Delete the ZIP file if it already exists deleteFile(filePathZip); // Create the ZIP file ZipOutputStream out = new ZipOutputStream(new FileOutputStream(filePathZip)); // Fill ZIP file with XMLs for (String fileNameXml : fileNamesXml) { String filePathXml = outputDir + fileNameXml; // Create a buffer for reading the files byte[] buf = new byte[1024]; // Compress the file FileInputStream in = new FileInputStream(filePathXml); // Add ZIP entry to output stream. out.putNextEntry(new ZipEntry(fileNameXml)); // Transfer bytes from the file to the ZIP file int len; while ((len = in.read(buf)) > 0) { out.write(buf, 0, len); } // Complete the entry out.closeEntry(); in.close(); monitor.worked(1); if (monitor.isCanceled()) { break; } } // Complete the ZIP file out.finish(); out.flush(); out.close(); } catch (IOException e) { ExHandler.handle(e); abort = true; } } // ZIP File Übermitteln // Übermittlung an Ärztekasse und Parsen der Antwort String filePathAnswer = outputDir + "answer.html"; //$NON-NLS-1$ if (!abort) { deleteFile(filePathAnswer); if (doHttpPost(filePathZip, filePathAnswer)) { // Wenn übermittlung OK, Status für Rechnungen setzen if (transferState) { for (Rechnung rn : rnn) { if (type == TYPE.ORIG) { rn.setStatus(RnStatus.BEZAHLT); } transmittedInvoicesCount++; transmittedInvoices += MessagesAK.InvoiceOutputter_SuccessInvoiceNr + rn.getNr() + " : " //$NON-NLS-1$ + MessagesAK.InvoiceOutputter_NewState + " : " //$NON-NLS-1$ + RnStatus.getStatusText(rn.getStatus()); monitor.worked(1); if (monitor.isCanceled()) { break; } } } else { // Übermittlung an Ärztekasse fehlgeschlagen for (Rechnung rn : rnn) { rn.reject(RnStatus.REJECTCODE.REJECTED_BY_PEER, MessagesAK.InvoiceOutputter_TransmissionFailed + " " + responseState + "/" + responseError); //$NON-NLS-1$ //$NON-NLS-2$ failedInvoicesCount++; failedInvoices += MessagesAK.InvoiceOutputter_FailureInvoiceNr + rn.getNr() + " : " //$NON-NLS-1$ + " " + responseState + "/" + responseError; //$NON-NLS-1$ //$NON-NLS-2$ monitor.worked(1); if (monitor.isCanceled()) { break; } } } } else { // Http POST fehlgeschlagen, abbruch Iterator<Rechnung> ir = rnn.iterator(); Rechnung r = ir.next(); ret.add(Result.SEVERITY.ERROR, 1, MessagesAK.InvoiceOutputter_ErrorHttpPost, r, true); } } for (String fileNameXml : fileNamesXml) { deleteFileOnExit(outputDir + fileNameXml); } deleteFileOnExit(filePathZip); deleteFileOnExit(filePathAnswer); monitor.done(); } }, null); } catch (Exception ex) { ExHandler.handle(ex); ret.add(Result.SEVERITY.ERROR, 2, ex.getMessage(), null, true); } if (!ret.isOK()) { String errorStr = ""; //$NON-NLS-1$ for (Result<Rechnung>.msg errorMsg : ret.getMessages()) { errorStr += errorMsg.getText() + "\n"; //$NON-NLS-1$ } SWTHelper.alert(MessagesAK.InvoiceOutputter_ErrorInvoice + ret.get().getNr(), errorStr); } if (failedInvoicesCount > 0) { if ((transmittedInvoicesCount + failedInvoicesCount) < 10) { SWTHelper.showError(MessagesAK.InvoiceOutputter_TransmittedInvoicesTitle, MessagesAK.InvoiceOutputter_TransmittedInvoices + transmittedInvoices + failedInvoices); } else { SWTHelper.showError(MessagesAK.InvoiceOutputter_TransmittedInvoicesTitle, MessageFormat.format(MessagesAK.InvoiceOutputter_TransmisionAKFailure, failedInvoicesCount, transmittedInvoicesCount)); } } else { if (transmittedInvoicesCount < 10) { SWTHelper.showInfo(MessagesAK.InvoiceOutputter_TransmittedInvoicesTitle, MessagesAK.InvoiceOutputter_TransmittedInvoices + transmittedInvoices); } else { SWTHelper.showInfo(MessagesAK.InvoiceOutputter_TransmittedInvoicesTitle, MessageFormat.format(MessagesAK.InvoiceOutputter_TransmisionAKSuccess, transmittedInvoicesCount)); } } return ret; } @Override public String getDescription(){ return MessagesAK.InvoiceOutputter_TransmisionAK; } @Override public Control createSettingsControl(Object parent){ final Composite compParent = (Composite) parent; outputDir = System.getProperty("java.io.tmpdir") + "InvoiceOutput" + File.separator; //$NON-NLS-1$ //$NON-NLS-2$ Composite ret = new Composite(compParent, SWT.NONE); ret.setLayout(new GridLayout(2, false)); Label l = new Label(ret, SWT.NONE); l.setText(MessagesAK.InvoiceOutputter_InvoiceOutputDir); l.setLayoutData(SWTHelper.getFillGridData(2, true, 1, false)); final Text text = new Text(ret, SWT.READ_ONLY | SWT.BORDER); text.setLayoutData(SWTHelper.getFillGridData(1, true, 1, true)); Button b = new Button(ret, SWT.PUSH); b.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent e){ DirectoryDialog dd = new DirectoryDialog(compParent.getShell(), SWT.OPEN); dd.setFilterPath(outputDir); String tmpOutputDir = dd.open(); if (tmpOutputDir != null) { outputDir = tmpOutputDir.replace("\\", "/") + "/"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ text.setText(outputDir); } } }); b.setText(MessagesAK.InvoiceOutputter_ChangeDir); text.setText(outputDir); return ret; } /** * Übermittelt das übergebene Zip File an die Ärztekasse. Bei erfolgreicher Übermittlung wird * die Antwort geparst und gespeichert und es wird true zurückgegeben, sonst false. * * @param inFilePath * Pfad der Zip Datei * @param outFilePath * Pfad der HTML Respoonse Datei * @param ret * Resultat um allfällige Fehlermeldungen hinzuzufügen * @param rn * Rechnung welcher übermittelt wird * @return true bei erfolgreichem Post, sonst false */ public boolean doHttpPost(String inFilePath, String outFilepath){ // public only because of net.medshare.connector.aerztekasse_test boolean returnValue = false; try { transferState = false; responseState = ""; //$NON-NLS-1$ responseError = ""; //$NON-NLS-1$ if (settings == null) { settings = new AerztekasseSettings((CoreHub.actMandant)); } ClientHttpRequest post = new ClientHttpRequest(settings.getUrl()); post.setParameter("user", settings.getUsername()); //$NON-NLS-1$ post.setParameter("pwd", settings.getPassword()); //$NON-NLS-1$ post.setParameter("chkDoubleOK", "false"); //$NON-NLS-1$ //$NON-NLS-2$ post.setParameter("docs", "false"); //$NON-NLS-1$ //$NON-NLS-2$ post.setParameter("lang", "A"); //$NON-NLS-1$ //$NON-NLS-2$ post.setParameter("txtDocument", new File(inFilePath)); //$NON-NLS-1$ post.setParameter("btnTransfert", "true"); //$NON-NLS-1$ //$NON-NLS-2$ FileOutputStream fout = new FileOutputStream(outFilepath); OutputStreamWriter writer = new OutputStreamWriter(fout, "UTF-8"); //$NON-NLS-1$ BufferedReader input = new BufferedReader(new InputStreamReader(post.post(), "UTF-8")); //$NON-NLS-1$ String line; while ((line = input.readLine()) != null) { writer.append(line); writer.append(System.getProperty("line.separator")); //$NON-NLS-1$ parseHttpPostResponse(line); } writer.flush(); writer.close(); fout.flush(); fout.close(); returnValue = true; } catch (Exception e) {} return returnValue; } /** * Parser für die Http POST Antwort von der Ärztekasse. Wenn erfolgreoch, wird transferState auf * true gesetzt. Allfällige Fehlermeldungen werden gespeichert, Status in responseState und * Fehler in responseError. * * @param line */ private void parseHttpPostResponse(String line){ int startIndex = line.indexOf("ctl02__TextStatus"); //$NON-NLS-1$ if (startIndex > 0) { responseState = getResponse(line, startIndex); if (responseState.contains("ReadyToControl")) { //$NON-NLS-1$ transferState = true; } else { transferState = false; } } startIndex = line.indexOf("ctl02__TextError"); //$NON-NLS-1$ if (startIndex > 0) { responseError = getResponse(line, startIndex); } } /** * Gibt die entsprechende Meldung des gefunden Tilte, Status oder Errors zurück. ist keine * korrekte Meldung vorhanden, wird null zurückgegeben. * * @param line * @param startIndex * @return Meldung */ private String getResponse(String line, int startIndex){ startIndex = line.indexOf(">", startIndex); //$NON-NLS-1$ if (startIndex > 0) { int stopIndex = line.indexOf("<", startIndex); //$NON-NLS-1$ if (stopIndex > startIndex + 2) { return line.substring(startIndex + 3, stopIndex); } } return null; } /** * Löscht das angegebene File * * @param filename */ public static boolean deleteFile(String filename){ File f = new File(filename); boolean deleted = f.delete(); return deleted; } /** * Löscht das angegebene File beim Beenden von Elexis * * @param filename */ private void deleteFileOnExit(String filename){ File f = new File(filename); f.deleteOnExit(); } }