600 lines
No EOL
25 KiB
Java
600 lines
No EOL
25 KiB
Java
package com.alexanderlogistics.einvoice;
|
|
|
|
import java.io.ByteArrayInputStream;
|
|
import java.io.FileNotFoundException;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.math.BigDecimal;
|
|
import java.math.RoundingMode;
|
|
import java.text.NumberFormat;
|
|
import java.text.SimpleDateFormat;
|
|
import java.util.Collection;
|
|
import java.util.Date;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
import java.util.logging.Logger;
|
|
|
|
import javax.xml.parsers.DocumentBuilder;
|
|
import javax.xml.parsers.DocumentBuilderFactory;
|
|
import javax.xml.parsers.ParserConfigurationException;
|
|
import javax.xml.transform.TransformerException;
|
|
|
|
import org.imixs.archive.core.SnapshotService;
|
|
import org.imixs.einvoice.EInvoiceFormatException;
|
|
import org.imixs.einvoice.EInvoiceModel;
|
|
import org.imixs.einvoice.EInvoiceModelFactory;
|
|
import org.imixs.einvoice.EInvoiceModelKSeF;
|
|
import org.imixs.einvoice.EInvoiceNS;
|
|
import org.imixs.einvoice.TradeLineItem;
|
|
import org.imixs.einvoice.TradeParty;
|
|
import org.imixs.workflow.FileData;
|
|
import org.imixs.workflow.ItemCollection;
|
|
import org.imixs.workflow.SignalAdapter;
|
|
import org.imixs.workflow.engine.DocumentService;
|
|
import org.imixs.workflow.engine.WorkflowService;
|
|
import org.imixs.workflow.exceptions.AdapterException;
|
|
import org.imixs.workflow.exceptions.PluginException;
|
|
import org.imixs.workflow.exceptions.QueryException;
|
|
import org.imixs.workflow.util.XMLParser;
|
|
import org.w3c.dom.Document;
|
|
import org.w3c.dom.Element;
|
|
import org.xml.sax.InputSource;
|
|
import org.xml.sax.SAXException;
|
|
|
|
import com.alexanderlogistics.BusinessPartnerService;
|
|
import com.alexanderlogistics.InvoiceService;
|
|
import com.alexanderlogistics.InvoiceUtil;
|
|
import com.alexanderlogistics.xml.CargosoftXMLInvoiceImportService;
|
|
|
|
import jakarta.inject.Inject;
|
|
|
|
/**
|
|
* The KSeFAdapter converts a Cargsoft outbound invoice into an KSeF XML
|
|
* e-invoice and sends the xml file to the polish KSeF API
|
|
*
|
|
* The adapter can be configured by the model:
|
|
*
|
|
* <pre>
|
|
* {@code
|
|
<ksef name="create">
|
|
<textblock>textblock-ref</textblock>
|
|
<template>filename</template>
|
|
<debug>true</debug>
|
|
</ksef>
|
|
|
|
}
|
|
* </pre>
|
|
*
|
|
* <p>
|
|
* NIP: The adapter needs the NIP (vat-id). If we do not find the partner.vat in
|
|
* the business object, we do a lookup on the D-Cargosoft object and try the vat
|
|
* id from there. This is because the partner.vat is a field which was not
|
|
* defined before.
|
|
*
|
|
* <p>
|
|
* Wenn Debug = true gibt der Adapter alle einzelschritte auf der console aus
|
|
* und erzeugt zusätzlich die factur-x xml und txt dateien.
|
|
*
|
|
*
|
|
*
|
|
* @version 1.0
|
|
* @author rsoika
|
|
*/
|
|
public class KSeFAdapterDeprecated implements SignalAdapter {
|
|
|
|
final String TYPE_TEXTBLOCK = "textblock";
|
|
public static final String DOCUMENT_ERROR = "DOCUMENT_ERROR";
|
|
public static final String CONFIG_ERROR = "CONFIG_ERROR";
|
|
public static final String API_ERROR = "API_ERROR";
|
|
public static final String BUSINESSPARTNER_ERROR = "BUSINESSPARTNER_ERROR";
|
|
public static final String LINE_ITEMS_PROPERTY = "invoice.items";
|
|
private static Logger logger = Logger.getLogger(EInvoiceAdapter.class.getName());
|
|
|
|
public static SimpleDateFormat dateFormatter = new SimpleDateFormat("dd.MM.yyyy", Locale.GERMAN);
|
|
public static NumberFormat numberFormat = NumberFormat.getInstance(Locale.GERMANY);
|
|
boolean debug = false;
|
|
|
|
@Inject
|
|
WorkflowService workflowService;
|
|
|
|
@Inject
|
|
DocumentService documentService;
|
|
|
|
@Inject
|
|
SnapshotService snapshotService;
|
|
|
|
@Inject
|
|
BusinessPartnerService businessPartnerService;
|
|
|
|
@Inject
|
|
InvoiceService invoiceService;
|
|
|
|
/**
|
|
* This method
|
|
*
|
|
* @throws PluginException
|
|
*/
|
|
@Override
|
|
public ItemCollection execute(ItemCollection workitem, ItemCollection event)
|
|
throws AdapterException, PluginException {
|
|
|
|
logger.info("├── 🔜 Convert Invoice to KSeF...");
|
|
// read configuration....
|
|
ItemCollection eInvoiceConfig = workflowService.evalWorkflowResult(event, "ksef",
|
|
workitem,
|
|
false);
|
|
if (eInvoiceConfig == null || !eInvoiceConfig.hasItem("CREATE")) {
|
|
throw new PluginException(EInvoiceAdapter.class.getSimpleName(), CONFIG_ERROR,
|
|
"missing e-invoice/ksef configuration in model event - please check model configuration");
|
|
}
|
|
ItemCollection ksefCreateDefinition = XMLParser.parseItemStructure(eInvoiceConfig.getItemValueString("CREATE"));
|
|
try {
|
|
|
|
// Load the e-invoice template....
|
|
FileData xmlFileData = loadXMLTemplate(workitem, ksefCreateDefinition);
|
|
|
|
updateEInvoice(xmlFileData, workitem);
|
|
|
|
// append XML document
|
|
logger.info("│ ├── attach KSeF e-invoice...");
|
|
workitem.addFileData(xmlFileData);
|
|
|
|
} catch (PluginException e) {
|
|
throw new AdapterException(e);
|
|
}
|
|
|
|
return workitem;
|
|
}
|
|
|
|
/**
|
|
* This method updates an e-invoice template with the data stored in the
|
|
* workitem.
|
|
*
|
|
* First the method loads an EInvoiceModel based on the provided XML Template
|
|
* and than updates the e-invoice data based on the items stored in the given
|
|
* workitem.
|
|
*
|
|
* @param workitem
|
|
* @throws PluginException
|
|
*/
|
|
public void updateEInvoice(FileData fileDataXMLTemplate, ItemCollection workitem) throws PluginException {
|
|
|
|
try {
|
|
EInvoiceModel model = EInvoiceModelFactory.read(new ByteArrayInputStream(fileDataXMLTemplate.getContent()));
|
|
|
|
model.setId(workitem.getItemValueString("invoice.number"));
|
|
|
|
// date
|
|
model.setIssueDateTime(workitem.getItemValueLocalDate("invoice.date"));
|
|
|
|
// Set Performance Date
|
|
if (workitem.getItemValueDate("invoice.performancedate") == null) {
|
|
// hilfs code um das invoice.performancedate nachträglich zu parsen
|
|
syncPerformanceDate(workitem);
|
|
}
|
|
((EInvoiceModelKSeF) model)
|
|
.setPerformanceDateTime(workitem.getItemValueLocalDate("invoice.performancedate"));
|
|
|
|
// Update Addresses
|
|
TradeParty billingAddress = buildAddress(workitem, "buyer", model);
|
|
model.setTradeParty(billingAddress);
|
|
|
|
// set Tax Type based on vatID - as a result taxType is 1=Poland 2=EU 3=Other
|
|
((EInvoiceModelKSeF) model).setTaxType(workitem.getItemValueString("partner.vat"));
|
|
workitem.setItemValue("invoice.tax.type", ((EInvoiceModelKSeF) model).getTaxType());
|
|
|
|
// Update Invoice Type (RodzajFaktury) -> VAT | KOR
|
|
Element elementFa = model.findOrCreateChildNode(model.getRoot(), EInvoiceNS.KSEF, "Fa");
|
|
|
|
if (isKorrekturRechnung(workitem)) {
|
|
((EInvoiceModelKSeF) model).setRodzajFaktury("KOR");
|
|
logger.info("│ ├── invoice type=KOR");
|
|
|
|
// Set correction data (DaneFaKorygowanej) - required for KOR invoices
|
|
Element daneFaKorygowanej = model.findOrCreateChildNodeAfter(
|
|
elementFa, EInvoiceNS.KSEF, "DaneFaKorygowanej", "RodzajFaktury");
|
|
|
|
Date correctionInvoiceDate = workitem.getItemValueDate("invoice.date");
|
|
// try to lookup original invoice by invoice.CorrectionInvoiceNumber
|
|
ItemCollection correctionInvoice = invoiceService
|
|
.findOutboundInvoiceByNumber(workitem.getItemValueString("invoice.CorrectionInvoiceNumber"));
|
|
if (correctionInvoice != null) {
|
|
correctionInvoiceDate = correctionInvoice.getItemValueDate("invoice.date");
|
|
logger.info("│ ├── found original invoice, date: " + correctionInvoiceDate);
|
|
} else {
|
|
logger.info("│ ├── ⚠️ original invoice not found, using current date as fallback");
|
|
}
|
|
// Original invoice date (DataWystFaKorygowanej) - required
|
|
if (correctionInvoiceDate != null) {
|
|
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
|
|
String sCorrectionDate = formatter.format(correctionInvoiceDate);
|
|
model.updateElementValue(daneFaKorygowanej, EInvoiceNS.KSEF,
|
|
"DataWystFaKorygowanej", sCorrectionDate);
|
|
}
|
|
|
|
// Original invoice number (NrFaKorygowanej) - required
|
|
model.updateElementValue(daneFaKorygowanej, EInvoiceNS.KSEF,
|
|
"NrFaKorygowanej", workitem.getItemValueString("invoice.CorrectionInvoiceNumber"));
|
|
|
|
// NrKSeFN = 1 means the original invoice was issued outside KSeF
|
|
model.updateElementValue(daneFaKorygowanej, EInvoiceNS.KSEF, "NrKSeFN", "1");
|
|
|
|
} else {
|
|
// normale rechnung
|
|
((EInvoiceModelKSeF) model).setRodzajFaktury("VAT");
|
|
logger.info("│ ├── invoice type=VAT");
|
|
}
|
|
|
|
// set currency
|
|
model.updateElementValue(elementFa, EInvoiceNS.KSEF, "KodWaluty",
|
|
workitem.getItemValueString("invoice.currency"));
|
|
|
|
// Update Invoice Items
|
|
// Here we compute the pos number independent if the _childitems hold a posnum.
|
|
// This is because a new imported cargosoft invoice does not provide numPos
|
|
// items in the child list. So it is important to generate the pos numbers here
|
|
// one by one
|
|
List<ItemCollection> invoiceItems = InvoiceUtil.explodeChildList(workitem, "_childitems");
|
|
double lineTotalAmount = 0.00;
|
|
int pos = 1;
|
|
for (ItemCollection invoiceItem : invoiceItems) {
|
|
TradeLineItem tradeLineItem = buildTradeLineItem(invoiceItem, pos);
|
|
model.setTradeLineItem(tradeLineItem);
|
|
lineTotalAmount = lineTotalAmount + tradeLineItem.getTotal();
|
|
pos++;
|
|
}
|
|
|
|
// Summenbildung
|
|
/*
|
|
* Field mapping:
|
|
* <ul>
|
|
* <li>taxType "1" (Poland): P_13_1 (domestic VAT)</li>
|
|
* <li>taxType "2" (EU): P_13_6_2 (intra-community delivery, 0%)</li>
|
|
* <li>taxType "3" (Non-EU): P_13_6_3 (export, 0%)</li>
|
|
* </ul>
|
|
*/
|
|
model.setNetTotalAmount(workitem.getItemValueDouble("invoice.total.net"));
|
|
|
|
// ?? has no function
|
|
model.setTaxRate(workitem.getItemValueDouble("invoice.total.tax"));
|
|
|
|
// Tax
|
|
/*
|
|
* P_14_1
|
|
*
|
|
* This call also generate P_14_1W bei fremdwährung
|
|
*/
|
|
model.setTaxTotalAmount(InvoiceUtil.round(workitem.getItemValueDouble("invoice.total")
|
|
- workitem.getItemValueDouble("invoice.total.net")));
|
|
/*
|
|
* If we have Foreign Currency we need to set P_14_1W in case we are tax type 1
|
|
*/
|
|
if (!"PLN".equals(workitem.getItemValueString("invoice.currency"))) {
|
|
if ("1".equals(((EInvoiceModelKSeF) model).getTaxType())) {
|
|
// compute rate
|
|
double rate = workitem.getItemValueDouble("invoice.rate");
|
|
logger.info("│ ├── rate=" + rate);
|
|
double totalTax = InvoiceUtil.round(workitem.getItemValueDouble("invoice.total.tax"));
|
|
logger.info("│ ├── total.tax=" + totalTax);
|
|
BigDecimal value = BigDecimal.valueOf(totalTax)
|
|
.divide(BigDecimal.valueOf(rate), 10, RoundingMode.HALF_UP);
|
|
// P_14_1W must come directly after P_14_1
|
|
logger.info("│ ├── P_14_1W=" + value);
|
|
Element element = model.findOrCreateChildNodeAfter(elementFa, EInvoiceNS.KSEF, "P_14_1W", "P_14_1");
|
|
element.setTextContent(value.setScale(2, RoundingMode.HALF_UP).toPlainString());
|
|
}
|
|
|
|
}
|
|
|
|
// Brutto
|
|
/*
|
|
* P_15
|
|
*/
|
|
model.setGrandTotalAmount(workitem.getItemValueDouble("invoice.total"));
|
|
|
|
/*
|
|
* KursWalutyZ
|
|
*
|
|
* Für Rechnungen in Fremdwährung obligatorisch. Anzuwenden ist der NBP-
|
|
* Durchschnittskurs des Tages vor Entstehen der Steuerpflicht (Art. 31a
|
|
* UStG-PL).
|
|
* Dezimalformat mit Punkt.
|
|
*/
|
|
String currency = workitem.getItemValueString("invoice.currency");
|
|
if (!currency.isBlank() && !"PLN".equals(currency)) {
|
|
Element p15 = model.findChildNode(elementFa, EInvoiceNS.KSEF, "P_15");
|
|
if (p15 != null) {
|
|
Double rate = workitem.getItemValueDouble("invoice.rate");
|
|
logger.info("│ ├── set KursWalutyZ = " + rate);
|
|
Element element = model.findOrCreateChildNodeAfter(elementFa, EInvoiceNS.KSEF, "KursWalutyZ",
|
|
"P_15");
|
|
element.setTextContent(rate.toString());
|
|
}
|
|
}
|
|
|
|
// finally set the due date at the end of the XML tree
|
|
model.setDueDateTime(workitem.getItemValueLocalDate("invoice.duedate"));
|
|
|
|
/*
|
|
* finally update the template file
|
|
*/
|
|
fileDataXMLTemplate.setContent(model.getContent());
|
|
|
|
} catch (FileNotFoundException | EInvoiceFormatException | TransformerException e) {
|
|
throw new PluginException(this.getClass().getName(), DOCUMENT_ERROR, e.getMessage(), e);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Dies ist eine Hilfsmethode die nachträglich das invoice.performancedate aus
|
|
* dem Cargosoft XML ausliest
|
|
*
|
|
* @param workitem
|
|
* @throws PluginException
|
|
*/
|
|
private void syncPerformanceDate(ItemCollection workitem) throws PluginException {
|
|
|
|
if (!workitem.hasItem("invoice.performancedate")) {
|
|
try {
|
|
logger.info("----Resync cargosoft performancedate....");
|
|
DocumentBuilder documentBuilder;
|
|
// hole die XML Datei
|
|
FileData cargoXML = snapshotService.getWorkItemFile(workitem.getUniqueID(),
|
|
workitem.getItemValueString("cargosoft.import.filename"));
|
|
if (cargoXML != null) {
|
|
InputStream inputStream = new ByteArrayInputStream(cargoXML.getContent());
|
|
InputSource inputSource = new InputSource(inputStream);
|
|
|
|
documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
|
|
Document doc = documentBuilder.parse(inputSource);
|
|
|
|
// Performance Date
|
|
CargosoftXMLInvoiceImportService.readXMLValue(doc,
|
|
"/Invoices/Invoice/InvoiceHeader/PerformanceDate",
|
|
workitem, "invoice.performancedate",
|
|
Date.class);
|
|
|
|
// falls keines gefunden wurde nehmen wir das invoice date
|
|
if (workitem.getItemValueDate("invoice.performancedate") == null) {
|
|
workitem.setItemValue("invoice.performancedate", workitem.getItemValueDate("invoice.date"));
|
|
}
|
|
}
|
|
} catch (ParserConfigurationException | SAXException | IOException e) {
|
|
throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(), "XML_ERROR",
|
|
e.getMessage());
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gibt True zurück wenn es eine korrektur rechnung ist
|
|
*
|
|
* InvoiceHeader/Correction=true
|
|
* InvoiceHeader/CorrectionInvoiceNumber !empty
|
|
*
|
|
*
|
|
* @param workitem
|
|
* @return
|
|
*/
|
|
private boolean isKorrekturRechnung(ItemCollection workitem) {
|
|
return ("true".equals(workitem.getItemValueString("invoice.correction"))
|
|
&& !workitem.getItemValueString("invoice.CorrectionInvoiceNumber").isEmpty());
|
|
}
|
|
|
|
/**
|
|
* Erstellt ein TradeParty Objekt aus einer Liste von Adresszeilen.
|
|
*
|
|
* @param addressLines Liste der Adresszeilen
|
|
* @param type Der Typ der TradeParty
|
|
* @return TradeParty
|
|
*/
|
|
public TradeParty buildAddress(ItemCollection workitem, String type, EInvoiceModel model) throws PluginException {
|
|
|
|
String partnerID = workitem.getItemValueString("partner.id");
|
|
if (partnerID == null || partnerID.isEmpty()) {
|
|
throw new PluginException(this.getClass().getName(), DOCUMENT_ERROR, "Missing partnerID");
|
|
}
|
|
|
|
ItemCollection businessPartner = businessPartnerService.getBusinessPartnerByID(partnerID);
|
|
if (businessPartner == null) {
|
|
throw new PluginException(this.getClass().getName(), DOCUMENT_ERROR,
|
|
"Business Partner ID '" + partnerID + "' does not exist");
|
|
}
|
|
|
|
/**
|
|
* Migration:
|
|
* If the business partner does not yet have the item "partner.vat" we try here
|
|
* to resync this information from the cargosoftkreditor object. This is needed
|
|
* because the partner.vat was not initially defined by the synch processor and
|
|
* so many partner objects do not yet provide this information.
|
|
*/
|
|
if (businessPartner.getItemValueString("partner.vat").isEmpty()) {
|
|
logger.info("│ ├── BusinessPartner " + partnerID + " does not provide vat-id - try to migrate....");
|
|
String dNumber = businessPartner.getItemValueString("dbtr.number");
|
|
if (!dNumber.isEmpty()) {
|
|
try {
|
|
String query = "(type:cargosoftkreditor) AND (name:" + dNumber + ")";
|
|
List<ItemCollection> result;
|
|
result = documentService.find(query, 1, 0);
|
|
if (result != null && result.size() > 0) {
|
|
ItemCollection creditorData = result.get(0);
|
|
String vatID = creditorData.getItemValueString("_vendor_vat_registration_id");
|
|
logger.info("│ ├── synchronize VAT Registrytion ID: " + vatID);
|
|
// update business partner
|
|
businessPartner.setItemValue("partner.vat", vatID);
|
|
documentService.saveByNewTransaction(businessPartner);
|
|
}
|
|
} catch (Exception e) {
|
|
// should not happen!
|
|
logger.info("│ ├── ⚠️ Failed to sync BusinessPartner : " + e.getMessage());
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
TradeParty tradeParty = new TradeParty(type);
|
|
|
|
// Name ist immer die erste Zeile
|
|
tradeParty.setName(businessPartner.getItemValueString("partner.name"));
|
|
tradeParty.setCountryId(businessPartner.getItemValueString("partner.country"));
|
|
tradeParty.setCityName(businessPartner.getItemValueString("partner.city"));
|
|
tradeParty.setPostcodeCode(businessPartner.getItemValueString("partner.zip"));
|
|
tradeParty.setStreetAddress(businessPartner.getItemValueString("partner.address"));
|
|
|
|
if ("buyer".equals(type)) {
|
|
// if we do NOT have a partner.vat we can not upload the invoice!
|
|
String partnerVAT = businessPartner.getItemValueString("partner.vat");
|
|
if (partnerVAT.isBlank()) {
|
|
// Just a warning
|
|
logger.warning("│ ├── ⚠️ Business Partner ID '" + partnerID + "' does not contain a VAT ID !");
|
|
// throw new PluginException(this.getClass().getName(), BUSINESSPARTNER_ERROR,
|
|
// "Business Partner ID '" + partnerID + "' does not contain a VAT ID !");
|
|
}
|
|
workitem.setItemValue("partner.vat", partnerVAT);
|
|
tradeParty.setVatNumber(partnerVAT);
|
|
// Update or create NrKlienta element directly under Podmiot2
|
|
Element podmiot2 = model.findOrCreateChildNode(model.getRoot(), EInvoiceNS.KSEF, "Podmiot2");
|
|
model.updateElementValue(podmiot2, EInvoiceNS.KSEF, "NrKlienta",
|
|
businessPartner.getItemValueString("dbtr.number"));
|
|
|
|
}
|
|
|
|
return tradeParty;
|
|
}
|
|
|
|
/**
|
|
* Parses the data list of an invoice line and returns a TradeLineItem object.
|
|
* The order of the list items must be exactly!
|
|
*
|
|
* # 16.04.2026
|
|
* Laut Herrn Grzegorz Grzelczyk dürfen in den Rechnungspositionen nur netto
|
|
* werte eingetragen sein. Siehe buildTradeLineItem()
|
|
*
|
|
* Des weiteren soll anstatt datev.text (pos nummer) der original billing text
|
|
* ausgewiesen werden
|
|
*/
|
|
private TradeLineItem buildTradeLineItem(ItemCollection orderItem, int pos) {
|
|
if (orderItem == null) {
|
|
return null;
|
|
}
|
|
|
|
// TradeLineItem tradeLineItem = new
|
|
// TradeLineItem(orderItem.getItemValueString("numpos"));
|
|
// We do not trust the item 'numPos' here because it can be empty for new
|
|
// invoices!
|
|
TradeLineItem tradeLineItem = new TradeLineItem(pos + "");
|
|
|
|
// Herrn Grzegorz Grzelczyk möchte hier BillingText
|
|
// tradeLineItem.setName(orderItem.getItemValueString("datev.text"));
|
|
// tradeLineItem.setName(orderItem.getItemValueString("billingtext"));
|
|
// Herr Grzelczyk will das alle billing texte aus der liste, verkettet
|
|
// ausgegeben werden.
|
|
List<String> billingTexts = orderItem.getItemValue("billingtext");
|
|
String billingTextName = billingTexts == null
|
|
? ""
|
|
: String.join(", ", billingTexts);
|
|
final int MAX_LENGTH = 512;
|
|
if (billingTextName.length() > MAX_LENGTH) {
|
|
billingTextName = billingTextName.substring(0, MAX_LENGTH);
|
|
}
|
|
tradeLineItem.setName(billingTextName);
|
|
|
|
tradeLineItem.setQuantity(1);
|
|
|
|
double vat = orderItem.getItemValueDouble("datev.vatrate"); // 23.00
|
|
double netto = orderItem.getItemValueDouble("datev.umsatz"); // 970.00
|
|
|
|
// # 16.04.2026
|
|
// Der total bezieht sich laut Herrn Grzegorz Grzelczyk ausschließlich auf den
|
|
// Nettowert x Menge.
|
|
// Der Brutto Wert ist hier falsch! Da die Menge immer 1 ist ist das beide male
|
|
// der selbe wert
|
|
// WRONG: tradeLineItem.setTotal(brutto);
|
|
|
|
// double brutto = netto * (1 + (vat / 100));
|
|
// double steuer = brutto - netto;
|
|
tradeLineItem.setTaxRate(vat);
|
|
tradeLineItem.setNetPrice(netto);
|
|
tradeLineItem.setTotal(netto); // menge x netto
|
|
|
|
return tradeLineItem;
|
|
}
|
|
|
|
/**
|
|
* This method loads a text-block for a specified ref and appends the named
|
|
* fileData object of this document.
|
|
*
|
|
* @param document
|
|
* @throws PluginException
|
|
*/
|
|
|
|
private FileData loadXMLTemplate(ItemCollection workitem, ItemCollection config)
|
|
throws PluginException {
|
|
|
|
String textblock = config.getItemValueString("textblock");
|
|
String template = config.getItemValueString("template");
|
|
String sourceName = config.getItemValueString("source");
|
|
|
|
try {
|
|
debug = Boolean.parseBoolean(config.getItemValueString("debug"));
|
|
} catch (Exception e) {
|
|
}
|
|
String targetName = "ksef.xml";
|
|
|
|
// adapt text....
|
|
sourceName = workflowService.adaptText(sourceName, workitem);
|
|
|
|
if ((template == null || template.isEmpty()) || (textblock == null || textblock.isEmpty())) {
|
|
throw new PluginException(EInvoiceAdapter.class.getSimpleName(),
|
|
CONFIG_ERROR,
|
|
"invalid e-invoice configuration in model event - textblock/template reference not defined!");
|
|
}
|
|
|
|
// load the text block
|
|
FileData fileData = loadTextBlockFileData(textblock, template);
|
|
|
|
// do we found the document?
|
|
if (fileData == null) {
|
|
throw new PluginException(EInvoiceAdapter.class.getSimpleName(),
|
|
CONFIG_ERROR,
|
|
"invalid e-invoice configuration in model event - textblock/template: " + textblock + "/" + template
|
|
+ " not found!");
|
|
}
|
|
fileData.setName(targetName);
|
|
|
|
return fileData;
|
|
}
|
|
|
|
/**
|
|
* This method returns a text-block ItemCollection for a specified name.
|
|
*
|
|
* @param name in attribute txtname
|
|
*
|
|
*
|
|
*/
|
|
public FileData loadTextBlockFileData(String name, String fileName) {
|
|
ItemCollection textBlockItemCollection = null;
|
|
|
|
// load text-block by name....
|
|
String sQuery = "(type:\"" + TYPE_TEXTBLOCK + "\" AND txtname:\"" + name + "\")";
|
|
Collection<ItemCollection> col;
|
|
try {
|
|
// find the textblock...
|
|
col = documentService.find(sQuery, 1, 0);
|
|
if (col.size() > 0) {
|
|
textBlockItemCollection = col.iterator().next();
|
|
// fetch the fileData...
|
|
return snapshotService.getWorkItemFile(textBlockItemCollection.getUniqueID(), fileName);
|
|
|
|
} else {
|
|
logger.warning("Missing text-block : '" + name + "'");
|
|
}
|
|
} catch (QueryException e) {
|
|
logger.warning("getTextBlock - invalid query: " + e.getMessage());
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
} |