/*
* Copyright (C) 2014 GG-Net GmbH - Oliver Günther
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.ggnet.dwoss.redtape;
import eu.ggnet.dwoss.rules.DocumentType;
import eu.ggnet.dwoss.util.DateFormats;
import eu.ggnet.dwoss.util.FileJacket;
import eu.ggnet.dwoss.util.UserInfoException;
import eu.ggnet.dwoss.redtape.entity.Dossier;
import eu.ggnet.dwoss.redtape.entity.Position;
import eu.ggnet.dwoss.redtape.entity.Document;
import eu.ggnet.lucidcalc.CBorder;
import eu.ggnet.lucidcalc.CFormat;
import eu.ggnet.lucidcalc.CSheet;
import eu.ggnet.lucidcalc.CCalcDocument;
import eu.ggnet.lucidcalc.TempCalcDocument;
import eu.ggnet.lucidcalc.SFormulaAction;
import eu.ggnet.lucidcalc.STableModelList;
import eu.ggnet.lucidcalc.STableColumn;
import eu.ggnet.lucidcalc.STable;
import java.io.*;
import java.net.URL;
import java.util.*;
import javax.ejb.Stateless;
import javax.enterprise.inject.Instance;
import javax.inject.Inject;
import javax.mail.util.ByteArrayDataSource;
import javax.persistence.EntityManager;
import org.apache.commons.io.IOUtils;
import org.apache.commons.mail.*;
import org.slf4j.*;
import net.sf.jasperreports.engine.*;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
import eu.ggnet.dwoss.configuration.GlobalConfig;
import eu.ggnet.dwoss.customer.api.CustomerService;
import eu.ggnet.dwoss.customer.api.UiCustomer;
import eu.ggnet.lucidcalc.jexcel.JExcelLucidCalcWriter;
import eu.ggnet.dwoss.mandator.api.DocumentViewType;
import eu.ggnet.dwoss.mandator.api.FreeDocumentTemplateParameter;
import eu.ggnet.dwoss.mandator.api.service.DocumentService;
import eu.ggnet.dwoss.mandator.api.value.Mandator;
import eu.ggnet.dwoss.mandator.api.value.partial.MailDocumentParameter;
import eu.ggnet.dwoss.mandator.api.value.partial.MandatorMailAttachment;
import eu.ggnet.dwoss.redtape.assist.RedTapes;
import eu.ggnet.dwoss.redtape.eao.DocumentEao;
import eu.ggnet.dwoss.redtape.eao.DossierEao;
import eu.ggnet.dwoss.redtape.entity.Document.Flag;
import eu.ggnet.dwoss.redtape.format.DocumentFormater;
import eu.ggnet.dwoss.uniqueunit.assist.UniqueUnits;
import eu.ggnet.dwoss.uniqueunit.eao.UniqueUnitEao;
import eu.ggnet.dwoss.uniqueunit.entity.PriceType;
import eu.ggnet.dwoss.uniqueunit.entity.UniqueUnit;
import static eu.ggnet.lucidcalc.CFormat.FontStyle.BOLD_ITALIC;
import static eu.ggnet.lucidcalc.CFormat.FontStyle.ITALIC;
import static eu.ggnet.lucidcalc.CFormat.HorizontalAlignment.*;
import static eu.ggnet.lucidcalc.CFormat.Representation.*;
import static eu.ggnet.lucidcalc.SUtil.SR;
import static eu.ggnet.dwoss.redtape.DocumentSupporterOperation.TemplateParameter.*;
import static java.awt.Color.*;
/**
* Supporting Operations for Documents, mostly printing or mailing.
* <p/>
* @author oliver.guenther
*/
@Stateless
public class DocumentSupporterOperation implements DocumentSupporter {
@Inject
private Instance<DocumentService> documentService;
/**
* Contains all Paramters which a Template may support, see also {@link FreeDocumentTemplateParameter}.
*/
public static class TemplateParameter {
public final static String REF_ID = "REF_ID";
public final static String CUSTOMER_ID = "CUSTOMER_ID";
public static final String IDENTIFIER_TYPE = "IDENTIFIER_TYPE";
public static final String IDENTIFIER_ID = "IDENTIFIER_ID";
public static final String INVOICE_ADDRESS = "INVOICE_ADDRESS";
public static final String SHIPPING_ADDRESS = "SHIPPING_ADDRESS";
public static final String PAYMENT_TEXT = "PAYMENT_TEXT";
public static final String SUM_NETTO = "SUM_NETTO";
public static final String SUM_BRUTTO = "SUM_BRUTTO";
public static final String SUM_TAX = "SUM_TAX";
public static final String TAX = "TAX";
public static final String COMPANY_LOGO = "COMPANY_LOGO";
public static final String COMPANY = "COMPANY";
public static final String FOOTER = "FOOTER";
public static final String TERMS1 = "TERMS1";
public static final String TERMS2 = "TERMS2";
public static final String ACTUAL = "ACTUAL";
public static final String PERFOMANCE_ON = "PERFOMANCE_ON";
}
private static final Logger L = LoggerFactory.getLogger(DocumentSupporterOperation.class);
@Inject
@RedTapes
private EntityManager redTapeEm;
@Inject
@UniqueUnits
private EntityManager uuEm;
@Inject
private Mandator mandator;
@Inject
private CustomerService customerService;
/**
* Creates a JasperPrint for the Document.
*
* @param document the document
* @return a JasperPrint
*/
@Override
public JasperPrint render(Document document, DocumentViewType viewType) {
return jasper(document, viewType);
}
/**
* This method send document to the e-Mail address that is in the customer set.
* <p/>
* @param document This is the Document that will be send.
* @throws UserInfoException if the sending of the Mail is not successful.
* @throws RuntimeException if problems exist in the JasperExporter
*/
@Override
public void mail(Document document, DocumentViewType jtype) throws UserInfoException, RuntimeException {
UiCustomer customer = customerService.asUiCustomer(document.getDossier().getCustomerId());
String customerMailAddress = customerService.asCustomerMetaData(document.getDossier().getCustomerId()).getEmail();
if ( customerMailAddress == null ) {
throw new UserInfoException("Kunde hat keine E-Mail Hinterlegt! Senden einer E-Mail ist nicht Möglich!");
}
String doctype = (jtype == DocumentViewType.DEFAULT ? document.getType().getName() : jtype.getName());
try (InputStream is = mandator.getMailDocumentTemplate().openStream();
InputStreamReader templateReader = new InputStreamReader(is)) {
String text = new MailDocumentParameter(customer.toTitleNameLine(), doctype).eval(IOUtils.toString(templateReader));
MultiPartEmail email = mandator.prepareDirectMail();
email.setCharset("UTF-8");
email.addTo(customerMailAddress);
email.setSubject(document.getType().getName() + " | " + document.getDossier().getIdentifier());
email.setMsg(text + mandator.getDefaultMailSignature());
email.attach(
new ByteArrayDataSource(JasperExportManager.exportReportToPdf(jasper(document, jtype)), "application/pdf"),
"Dokument.pdf", "Das ist das Dokument zu Ihrem Aufrag als PDF.");
for (MandatorMailAttachment mma : mandator.getDefaultMailAttachment()) {
email.attach(mma.getAttachmentData(), mma.getAttachmentName(), mma.getAttachmentDescription());
}
email.send();
} catch (EmailException ex) {
L.error("Error on Mail sending", ex);
throw new UserInfoException("Das senden der Mail war nicht erfolgreich!\n" + ex.getMessage());
} catch (IOException | JRException e) {
throw new RuntimeException(e);
}
}
/**
* Sets the Flags {@link Flag#CUSTOMER_BRIEFED} and {@link Flag#CUSTOMER_EXACTLY_BRIEFED} at the document.
* Also appends this change at the DocumentHistory.
*
* @param detached the document
* @return the updated document
*/
@Override
public Dossier briefed(Document detached, String arranger) {
L.info("Setting briefed at {}", DocumentFormater.toSimpleLine(detached));
Document document = new DocumentEao(redTapeEm).findById(detached.getId());
document.getDossier().fetchEager();
document.add(Document.Flag.CUSTOMER_BRIEFED);
document.add(Document.Flag.CUSTOMER_EXACTLY_BRIEFED);
document.getHistory().setComment(document.getHistory().getComment() + ", Kunde wurde durch " + arranger + " informiert.");
return document.getDossier();
}
@Override
public FileJacket toXls(String identifier) {
DossierEao dossierEao = new DossierEao(redTapeEm);
UniqueUnitEao uniqueUnitEao = new UniqueUnitEao(uuEm);
List<Dossier> dossiers = dossierEao.findByIdentifier(identifier);
if ( dossiers.isEmpty() ) return null;
Map<String, List<Object[]>> datas = new HashMap<>();
for (Dossier dossier : dossiers) {
for (Document document : dossier.getActiveDocuments()) {
List<Object[]> rows = new ArrayList<>();
datas.put(
dossier.getIdentifier() + "_" + document.getType().getName() + "_" + (document.getIdentifier() == null ? "" : document.getIdentifier()),
rows);
for (Position pos : document.getPositions().values()) {
if ( pos.getUniqueUnitId() > 0 ) {
UniqueUnit uu = uniqueUnitEao.findById(pos.getUniqueUnitId());
rows.add(new Object[]{
pos.getType().getName(),
pos.getAmount(),
pos.getName(),
pos.getPrice(),
pos.getAfterTaxPrice(),
DateFormats.ISO.format(uu.getMfgDate()),
uu.getProduct().getPrice(PriceType.MANUFACTURER_COST)
});
} else {
rows.add(new Object[]{
pos.getType().getName(),
pos.getAmount(),
pos.getName(),
pos.getPrice(),
pos.getAfterTaxPrice(),
null,
null
});
}
}
}
}
STable template = new STable();
CFormat euro = new CFormat(RIGHT, CURRENCY_EURO);
CFormat date = new CFormat(CENTER, SHORT_DATE);
CFormat percent = new CFormat(ITALIC, BLUE, null, null, null, PERCENT_FLOAT);
template.setHeadlineFormat(new CFormat(BOLD_ITALIC, BLACK, WHITE, CENTER, new CBorder(BLACK)));
template.add(new STableColumn("Type", 7))
.add(new STableColumn("Menge", 10))
.add(new STableColumn("Name", 30))
.add(new STableColumn("Preis", 15, euro))
.add(new STableColumn("Preis inc. Mwst", 15, euro))
.add(new STableColumn("MfgDate", 13, date))
.add(new STableColumn("CostPrice", 12, euro))
.add(new STableColumn("%Cost", 12, percent).setAction(new SFormulaAction(SR(3), "/", SR(6))));
CCalcDocument document = new TempCalcDocument();
for (Map.Entry<String, List<Object[]>> entry : datas.entrySet()) {
CSheet sheet = new CSheet(entry.getKey());
STable table = new STable(template);
table.setModel(new STableModelList(entry.getValue()));
sheet.addBelow(table);
document.add(sheet);
}
FileJacket fj = new FileJacket("Dossiers", ".xls", new JExcelLucidCalcWriter().write(document));
return fj;
}
/**
* Creates a JasperPrint for the Document.
*
* @param document the document
* @return a JasperPrint
*/
private JasperPrint jasper(Document document, DocumentViewType viewType) {
URL url = mandator.getDocumentIntermix().getTemplate(viewType) != null
? mandator.getDocumentIntermix().getTemplate(viewType)
: DocumentSupporterOperation.class.getResource(viewType.getFileName());
try (InputStream inputStream = url.openStream()) {
JasperReport jasperReport = JasperCompileManager.compileReport(inputStream);
JasperPrint result = JasperFillManager.fillReport(jasperReport, toTemplateParameters(document, viewType), toNormalizedDataSource(document));
return result;
} catch (JRException | IOException e) {
throw new RuntimeException(e);
}
}
private JRDataSource toNormalizedDataSource(Document document) {
ArrayList<Position> positions = new ArrayList<>();
for (Position position : document.getPositions().values()) {
Position p = position;
String description = position.getDescription().replace("•", " "); // Possible unprintable Character.
p.setDescription(description);
positions.add(p);
}
return new JRBeanCollectionDataSource(positions);
}
private Map<String, Object> toTemplateParameters(Document document, DocumentViewType viewType) {
double nettoPrice = 0;
double bruttoPrice = 0;
for (Position position : document.getPositions().values()) {
nettoPrice += position.getPrice() * position.getAmount();
bruttoPrice += position.getAfterTaxPrice() * position.getAmount();
}
// Setting Defaults.
Map<String, Object> reportParameter = new HashMap<>();
Dossier dossier = document.getDossier();
reportParameter.put(REF_ID, "K" + dossier.getCustomerId() + (dossier.getIdentifier() == null ? "-" : dossier.getIdentifier()));
reportParameter.put(CUSTOMER_ID, dossier.getCustomerId());
reportParameter.put(IDENTIFIER_TYPE, document.getType().getName());
reportParameter.put(IDENTIFIER_ID, document.getIdentifier() == null ? (dossier.getIdentifier() == null ? "-" : dossier.getIdentifier()) : document.getIdentifier());
// The two \n are a workaround for Windows/Remote Client. Otherwise the last line of an address is not shown.
reportParameter.put(INVOICE_ADDRESS, document.getInvoiceAddress().getDescription() + "\n");
reportParameter.put(SHIPPING_ADDRESS, document.getShippingAddress().getDescription() + "\n");
reportParameter.put(SUM_NETTO, nettoPrice);
reportParameter.put(SUM_BRUTTO, bruttoPrice);
reportParameter.put(SUM_TAX, bruttoPrice - nettoPrice);
reportParameter.put(TAX, GlobalConfig.TAX * 100);
reportParameter.put(ACTUAL, document.getActual());
reportParameter.put(COMPANY, mandator.getCompany().toSingleLine());
reportParameter.put(COMPANY_LOGO, mandator.getCompany().getLogo());
reportParameter.put(FOOTER, mandator.getDocumentIntermix().getFooter() + "\n");
reportParameter.put(PERFOMANCE_ON, document.getActual());
reportParameter.put(PAYMENT_TEXT, "");
for (FreeDocumentTemplateParameter parameter : FreeDocumentTemplateParameter.values()) {
reportParameter.put(parameter.name(), mandator.getDocumentIntermix().getFreeTexts(parameter, viewType, document.getType()));
}
if ( documentService.isAmbiguous() || documentService.isUnsatisfied() ) {
//default
if ( document.getType() == DocumentType.ORDER ) reportParameter.put(PAYMENT_TEXT, dossier.getPaymentMethod().getOrderText());
if ( document.getType() == DocumentType.INVOICE ) reportParameter.put(PAYMENT_TEXT, dossier.getPaymentMethod().getInvoiceText(0));
} else {
reportParameter.put(PAYMENT_TEXT, documentService.get().paymentInstructionText(document.getType(), dossier.getPaymentMethod()));
}
// Depending dates in PERFORMANCE_ON, like a CreditMemo depends on the date of the invoice.
List<Document> invoices = dossier.getActiveDocuments(DocumentType.INVOICE);
if ( !invoices.isEmpty() ) {
reportParameter.put(PERFOMANCE_ON, invoices.get(0).getActual());
}
if ( viewType != null && viewType != DocumentViewType.DEFAULT ) reportParameter.put(IDENTIFIER_TYPE, viewType.getDocumentTitle());
// TODO: Should be somethere else, but keep it here for now.
else if ( document.getType() == DocumentType.ORDER ) reportParameter.put(IDENTIFIER_TYPE, "Auftragsbestätigung/Proformarechnung");
reportParameter.put(JRParameter.REPORT_LOCALE, Locale.GERMANY);
return reportParameter;
}
}