From f543cdac57c87af682e862b9161515e5d6806e5f Mon Sep 17 00:00:00 2001 From: jstaerk Date: Wed, 21 Aug 2024 14:16:29 +0200 Subject: [PATCH] working on invoiceimporter as new zugferdimporter basis --- .../ZUGFeRD/ZUGFeRDImporter.java | 555 +++++++++++++++--- .../ZUGFeRD/ZUGFeRDInvoiceImporter.java | 419 ------------- 2 files changed, 484 insertions(+), 490 deletions(-) diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDImporter.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDImporter.java index 2029e854..c15ced7a 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDImporter.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDImporter.java @@ -20,6 +20,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; +import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.*; @@ -76,6 +77,10 @@ public class ZUGFeRDImporter { */ private Document document; private Integer version; + Invoice importedInvoice=null; + private boolean recalcPrice = false; + private boolean ignoreCalculationErrors = false; + private ArrayList fileAttachments=new ArrayList<>(); protected ZUGFeRDImporter() { @@ -218,6 +223,13 @@ public class ZUGFeRDImporter { final ByteArrayInputStream is = new ByteArrayInputStream(rawXML); /// is.skip(guessBOMSize(is)); document = builder.parse(is); + try { + extractInto(importedInvoice); + } catch (XPathExpressionException e) { + throw new RuntimeException(e); + } catch (ParseException e) { + throw new RuntimeException(e); + } } @@ -261,21 +273,441 @@ public class ZUGFeRDImporter { } + + + public void fromXML(String XML) { + try { + containsMeta = true; + setRawXML(XML.getBytes(StandardCharsets.UTF_8)); + } catch (IOException e) { + LOGGER.error(e.getMessage(), e); + } + } + + + /*** + * This will parse a XML into the given invoice object + * @param zpp the invoice to be altered + * @return the parsed invoice object + * @throws XPathExpressionException if xpath could not be evaluated + * @throws ParseException if the grand total of the parsed invoice could not be replicated with the new invoice + */ + public Invoice extractInto(Invoice zpp) throws XPathExpressionException, ParseException { + + String number = ""; + String typeCode = null; + /* + * dummywerte sind derzeit noch setDueDate setIssueDate setDeliveryDate + * setSender setRecipient setnumber bspw. due date + * //ExchangedDocument//IssueDateTime//DateTimeString : due date optional + */ + XPathFactory xpathFact = XPathFactory.newInstance(); + XPath xpath = xpathFact.newXPath(); + XPathExpression xpr = xpath.compile("//*[local-name()=\"SellerTradeParty\"]|//*[local-name()=\"AccountingSupplierParty\"]/*"); + NodeList SellerNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); + + xpr = xpath.compile("//*[local-name()=\"BuyerTradeParty\"]|//*[local-name()=\"AccountingCustomerParty\"]/*"); + NodeList BuyerNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); + xpr = xpath.compile("//*[local-name()=\"ExchangedDocument\"]|//*[local-name()=\"HeaderExchangedDocument\"]"); + NodeList ExchangedDocumentNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); + + xpr = xpath.compile("//*[local-name()=\"GrandTotalAmount\"]|//*[local-name()=\"PayableAmount\"]"); + BigDecimal expectedGrandTotal = null; + NodeList totalNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); + if (totalNodes.getLength() > 0) { + expectedGrandTotal = new BigDecimal(totalNodes.item(0).getTextContent()); + } + + Date issueDate = null; + Date dueDate = null; + Date deliveryDate = null; + String despatchAdviceReferencedDocument = null; + for (int i = 0; i < ExchangedDocumentNodes.getLength(); i++) { + + // nodes.item(i).getTextContent())) { + Node exchangedDocumentNode = ExchangedDocumentNodes.item(i); + NodeList exchangedDocumentChilds = exchangedDocumentNode.getChildNodes(); + for (int documentChildIndex = 0; documentChildIndex < exchangedDocumentChilds.getLength(); documentChildIndex++) { + Node item = exchangedDocumentChilds.item(documentChildIndex); + if ((item.getLocalName() != null) && (item.getLocalName().equals("ID"))) { + number = item.getTextContent(); + } + if ((item.getLocalName() != null) && (item.getLocalName().equals("TypeCode"))) { + typeCode = item.getTextContent(); + } + if ((item.getLocalName() != null) && (item.getLocalName().equals("IssueDateTime"))) { + NodeList issueDateTimeChilds = item.getChildNodes(); + for (int issueDateChildIndex = 0; issueDateChildIndex < issueDateTimeChilds.getLength(); issueDateChildIndex++) { + if ((issueDateTimeChilds.item(issueDateChildIndex).getLocalName() != null) + && (issueDateTimeChilds.item(issueDateChildIndex).getLocalName().equals("DateTimeString"))) { + issueDate = new SimpleDateFormat("yyyyMMdd").parse(issueDateTimeChilds.item(issueDateChildIndex).getTextContent()); + } + } + } + } + } + String rootNode = extractString("local-name(/*)"); + if (rootNode.equals("Invoice")) { + // UBL... + number = extractString("//*[local-name()=\"Invoice\"]/*[local-name()=\"ID\"]").trim(); + issueDate = new SimpleDateFormat("yyyy-MM-dd").parse(extractString("//*[local-name()=\"Invoice\"]/*[local-name()=\"IssueDate\"]").trim()); + String dueDt = extractString("//*[local-name()=\"Invoice\"]/*[local-name()=\"DueDate\"]").trim(); + if (dueDt.length() > 0) { + dueDate = new SimpleDateFormat("yyyy-MM-dd").parse(dueDt); + } + String deliveryDt = extractString("//*[local-name()=\"Delivery\"]/*[local-name()=\"ActualDeliveryDate\"]").trim(); + if (deliveryDt.length() > 0) { + deliveryDate = new SimpleDateFormat("yyyy-MM-dd").parse(deliveryDt); + } + } + xpr = xpath.compile("//*[local-name()=\"ApplicableHeaderTradeDelivery\"]"); + NodeList headerTradeDeliveryNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); + + for (int i = 0; i < headerTradeDeliveryNodes.getLength(); i++) { + // nodes.item(i).getTextContent())) { + Node headerTradeDeliveryNode = headerTradeDeliveryNodes.item(i); + NodeList headerTradeDeliveryChilds = headerTradeDeliveryNode.getChildNodes(); + for (int deliveryChildIndex = 0; deliveryChildIndex < headerTradeDeliveryChilds.getLength(); deliveryChildIndex++) { + if (headerTradeDeliveryChilds.item(deliveryChildIndex).getLocalName() != null) { + if (headerTradeDeliveryChilds.item(deliveryChildIndex).getLocalName().equals("ActualDeliverySupplyChainEvent")) { + NodeList actualDeliveryChilds = headerTradeDeliveryChilds.item(deliveryChildIndex).getChildNodes(); + for (int actualDeliveryChildIndex = 0; actualDeliveryChildIndex < actualDeliveryChilds.getLength(); actualDeliveryChildIndex++) { + if ((actualDeliveryChilds.item(actualDeliveryChildIndex).getLocalName() != null) + && (actualDeliveryChilds.item(actualDeliveryChildIndex).getLocalName().equals("OccurrenceDateTime"))) { + NodeList occurenceChilds = actualDeliveryChilds.item(actualDeliveryChildIndex).getChildNodes(); + for (int occurenceChildIndex = 0; occurenceChildIndex < occurenceChilds.getLength(); occurenceChildIndex++) { + if ((occurenceChilds.item(occurenceChildIndex).getLocalName() != null) + && (occurenceChilds.item(occurenceChildIndex).getLocalName().equals("DateTimeString"))) { + deliveryDate = new SimpleDateFormat("yyyyMMdd").parse(occurenceChilds.item(occurenceChildIndex).getTextContent()); + } + } + } + } + } + + if (headerTradeDeliveryChilds.item(deliveryChildIndex).getLocalName().equals("DespatchAdviceReferencedDocument")) { + NodeList despatchAdviceChilds = headerTradeDeliveryChilds.item(deliveryChildIndex).getChildNodes(); + for (int despatchAdviceChildIndex = 0; despatchAdviceChildIndex < despatchAdviceChilds.getLength(); despatchAdviceChildIndex++) { + if (despatchAdviceChilds.item(despatchAdviceChildIndex).getLocalName() != null + && despatchAdviceChilds.item(despatchAdviceChildIndex).getLocalName().equals("IssuerAssignedID")) { + despatchAdviceReferencedDocument = despatchAdviceChilds.item(despatchAdviceChildIndex).getTextContent(); + } + } + } + } + } + } + + xpr = xpath.compile("//*[local-name()=\"ApplicableHeaderTradeAgreement\"]"); + NodeList headerTradeAgreementNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); + String buyerOrderIssuerAssignedID = null; + String sellerOrderIssuerAssignedID = null; + for (int i = 0; i < headerTradeAgreementNodes.getLength(); i++) { + // nodes.item(i).getTextContent())) { + Node headerTradeAgreementNode = headerTradeAgreementNodes.item(i); + NodeList headerTradeAgreementChilds = headerTradeAgreementNode.getChildNodes(); + for (int agreementChildIndex = 0; agreementChildIndex < headerTradeAgreementChilds.getLength(); agreementChildIndex++) { + if (headerTradeAgreementChilds.item(agreementChildIndex).getLocalName() != null) { + if (headerTradeAgreementChilds.item(agreementChildIndex).getLocalName().equals("BuyerOrderReferencedDocument")) { + NodeList buyerOrderChilds = headerTradeAgreementChilds.item(agreementChildIndex).getChildNodes(); + for (int buyerOrderChildIndex = 0; buyerOrderChildIndex < buyerOrderChilds.getLength(); buyerOrderChildIndex++) { + if ((buyerOrderChilds.item(buyerOrderChildIndex).getLocalName() != null) + && (buyerOrderChilds.item(buyerOrderChildIndex).getLocalName().equals("IssuerAssignedID"))) { + buyerOrderIssuerAssignedID = buyerOrderChilds.item(buyerOrderChildIndex).getTextContent(); + } + } + } + + if (headerTradeAgreementChilds.item(agreementChildIndex).getLocalName().equals("SellerOrderReferencedDocument")) { + NodeList sellerOrderChilds = headerTradeAgreementChilds.item(agreementChildIndex).getChildNodes(); + for (int sellerOrderChildIndex = 0; sellerOrderChildIndex < sellerOrderChilds.getLength(); sellerOrderChildIndex++) { + if ((sellerOrderChilds.item(sellerOrderChildIndex).getLocalName() != null) + && (sellerOrderChilds.item(sellerOrderChildIndex).getLocalName().equals("IssuerAssignedID"))) { + sellerOrderIssuerAssignedID = sellerOrderChilds.item(sellerOrderChildIndex).getTextContent(); + } + } + } + } + } + + } + + + xpr = xpath.compile("//*[local-name()=\"ApplicableHeaderTradeSettlement\"]|//*[local-name()=\"ApplicableSupplyChainTradeSettlement\"]"); + NodeList headerTradeSettlementNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); + List bankDetails = new ArrayList<>(); + + for (int i = 0; i < headerTradeSettlementNodes.getLength(); i++) { + // nodes.item(i).getTextContent())) { + Node headerTradeSettlementNode = headerTradeSettlementNodes.item(i); + NodeList headerTradeSettlementChilds = headerTradeSettlementNode.getChildNodes(); + for (int settlementChildIndex = 0; settlementChildIndex < headerTradeSettlementChilds.getLength(); settlementChildIndex++) { + if ((headerTradeSettlementChilds.item(settlementChildIndex).getLocalName() != null) + && (headerTradeSettlementChilds.item(settlementChildIndex).getLocalName().equals("SpecifiedTradePaymentTerms"))) { + NodeList paymentTermChilds = headerTradeSettlementChilds.item(settlementChildIndex).getChildNodes(); + for (int paymentTermChildIndex = 0; paymentTermChildIndex < paymentTermChilds.getLength(); paymentTermChildIndex++) { + if ((paymentTermChilds.item(paymentTermChildIndex).getLocalName() != null) && (paymentTermChilds.item(paymentTermChildIndex).getLocalName().equals("DueDateDateTime"))) { + NodeList dueDateChilds = paymentTermChilds.item(paymentTermChildIndex).getChildNodes(); + for (int dueDateChildIndex = 0; dueDateChildIndex < dueDateChilds.getLength(); dueDateChildIndex++) { + if ((dueDateChilds.item(dueDateChildIndex).getLocalName() != null) && (dueDateChilds.item(dueDateChildIndex).getLocalName().equals("DateTimeString"))) { + dueDate = new SimpleDateFormat("yyyyMMdd").parse(dueDateChilds.item(dueDateChildIndex).getTextContent()); + } + } + } + } + } + + if ((headerTradeSettlementChilds.item(settlementChildIndex).getLocalName() != null) + && (headerTradeSettlementChilds.item(settlementChildIndex).getLocalName().equals("SpecifiedTradeSettlementPaymentMeans"))) { + NodeList paymentMeansChilds = headerTradeSettlementChilds.item(settlementChildIndex).getChildNodes(); + for (int paymentMeansChildIndex = 0; paymentMeansChildIndex < paymentMeansChilds.getLength(); paymentMeansChildIndex++) { + String IBAN = null, BIC = null; + if ((paymentMeansChilds.item(paymentMeansChildIndex).getLocalName() != null) && (paymentMeansChilds.item(paymentMeansChildIndex).getLocalName().equals("PayeePartyCreditorFinancialAccount"))) { + NodeList accountChilds = paymentMeansChilds.item(paymentMeansChildIndex).getChildNodes(); + for (int accountChildIndex = 0; accountChildIndex < accountChilds.getLength(); accountChildIndex++) { + if ((accountChilds.item(accountChildIndex).getLocalName() != null) && (accountChilds.item(accountChildIndex).getLocalName().equals("IBANID"))) {//CII + IBAN = accountChilds.item(accountChildIndex).getTextContent(); + } + } + } + if ((paymentMeansChilds.item(paymentMeansChildIndex).getLocalName() != null) && (paymentMeansChilds.item(paymentMeansChildIndex).getLocalName().equals("PayeePartyCreditorFinancialInstitution"))) { + NodeList accountChilds = paymentMeansChilds.item(paymentMeansChildIndex).getChildNodes(); + for (int accountChildIndex = 0; accountChildIndex < accountChilds.getLength(); accountChildIndex++) { + if ((accountChilds.item(accountChildIndex).getLocalName() != null) && (accountChilds.item(accountChildIndex).getLocalName().equals("BICID"))) {//CII + BIC = accountChilds.item(accountChildIndex).getTextContent(); + } + } + } + if (IBAN != null) { + BankDetails bd = new BankDetails(IBAN); + if (BIC != null) { + bd.setBIC(BIC); + } + bankDetails.add(bd); + } + } + } + } + } + + + xpr = xpath.compile("//*[local-name()=\"PaymentMeans\"]"); //UBL only + NodeList paymentMeansNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); + + for (int i = 0; i < paymentMeansNodes.getLength(); i++) { + // nodes.item(i).getTextContent())) { + Node paymentMeansNode = paymentMeansNodes.item(i); + NodeList paymentMeansChilds = paymentMeansNode.getChildNodes(); + for (int meansChildIndex = 0; meansChildIndex < paymentMeansChilds.getLength(); meansChildIndex++) { + if ((paymentMeansChilds.item(meansChildIndex).getLocalName() != null) + && (paymentMeansChilds.item(meansChildIndex).getLocalName().equals("PayeeFinancialAccount"))) { + NodeList paymentTermChilds = paymentMeansChilds.item(meansChildIndex).getChildNodes(); + for (int paymentTermChildIndex = 0; paymentTermChildIndex < paymentTermChilds.getLength(); paymentTermChildIndex++) { + if ((paymentTermChilds.item(paymentTermChildIndex).getLocalName() != null) && (paymentTermChilds.item(paymentTermChildIndex).getLocalName().equals("ID"))) { + String IBAN = paymentTermChilds.item(paymentTermChildIndex).getTextContent(); + if (IBAN != null) { + BankDetails bd = new BankDetails(IBAN); + bankDetails.add(bd); + } + } + } + } + } + } + + zpp.setDueDate(dueDate).setDeliveryDate(deliveryDate).setIssueDate(issueDate).setSender(new TradeParty(SellerNodes)).setRecipient(new TradeParty(BuyerNodes)).setNumber(number).setDocumentCode(typeCode); + bankDetails.forEach(bankDetail -> zpp.getSender().addBankDetails(bankDetail)); + + if (buyerOrderIssuerAssignedID != null) { + zpp.setBuyerOrderReferencedDocumentID(buyerOrderIssuerAssignedID); + } + if (sellerOrderIssuerAssignedID != null) { + zpp.setSellerOrderReferencedDocumentID(sellerOrderIssuerAssignedID); + } + if (despatchAdviceReferencedDocument != null) { + zpp.setDespatchAdviceReferencedDocumentID(despatchAdviceReferencedDocument); + } + + zpp.setOwnOrganisationName(extractString("//*[local-name()=\"SellerTradeParty\"]/*[local-name()=\"Name\"]|//*[local-name()=\"AccountingSupplierParty\"]/*[local-name()=\"Party\"]/*[local-name()=\"PartyName\"]").trim()); + + xpr = xpath.compile("//*[local-name()=\"BuyerReference\"]"); + String buyerReference = null; + totalNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); + if (totalNodes.getLength() > 0) { + buyerReference = totalNodes.item(0).getTextContent(); + } + if (buyerReference != null) { + zpp.setReferenceNumber(buyerReference); + } + + xpr = xpath.compile("//*[local-name()=\"IncludedSupplyChainTradeLineItem\"]|//*[local-name()=\"InvoiceLine\"]"); + NodeList nodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); + + if (nodes.getLength() != 0) { + for (int i = 0; i < nodes.getLength(); i++) { + + Node currentItemNode = nodes.item(i); + Item it = new Item(currentItemNode.getChildNodes(), recalcPrice); + zpp.addItem(it); + + } + + // now handling base64 encoded attachments AttachmentBinaryObject=CII, EmbeddedDocumentBinaryObject=UBL + xpr = xpath.compile("//*[local-name()=\"AttachmentBinaryObject\"]|//*[local-name()=\"EmbeddedDocumentBinaryObject\"]"); + NodeList attachmentNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); + for (int i = 0; i < attachmentNodes.getLength(); i++) { + FileAttachment fa=new FileAttachment(attachmentNodes.item(i).getAttributes().getNamedItem("filename").getNodeValue(),attachmentNodes.item(i).getAttributes().getNamedItem("mimeCode").getNodeValue(),"Data", Base64.getDecoder().decode(attachmentNodes.item(i).getTextContent())); + fileAttachments.add(fa); + // filename = "Aufmass.png" mimeCode = "image/png" + //EmbeddedDocumentBinaryObject cbc:EmbeddedDocumentBinaryObject mimeCode="image/png" filename="Aufmass.png" + } + + // item level charges+allowances are not yet handled but a lower item price will + // be read, + // so the invoice remains arithmetically correct + // -> parse document level charges+allowances + xpr = xpath.compile("//*[local-name()=\"SpecifiedTradeAllowanceCharge\"]"); + NodeList chargeNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); + for (int i = 0; i < chargeNodes.getLength(); i++) { + NodeList chargeNodeChilds = chargeNodes.item(i).getChildNodes(); + boolean isCharge = true; + String chargeAmount = null; + String reason = null; + String reasonCode = null; + String taxPercent = null; + for (int chargeChildIndex = 0; chargeChildIndex < chargeNodeChilds.getLength(); chargeChildIndex++) { + String chargeChildName = chargeNodeChilds.item(chargeChildIndex).getLocalName(); + if (chargeChildName != null) { + + if (chargeChildName.equals("ChargeIndicator")) { + NodeList indicatorChilds = chargeNodeChilds.item(chargeChildIndex).getChildNodes(); + for (int indicatorChildIndex = 0; indicatorChildIndex < indicatorChilds.getLength(); indicatorChildIndex++) { + if ((indicatorChilds.item(indicatorChildIndex).getLocalName() != null) + && (indicatorChilds.item(indicatorChildIndex).getLocalName().equals("Indicator"))) { + isCharge = indicatorChilds.item(indicatorChildIndex).getTextContent().equalsIgnoreCase("true"); + } + } + } else if (chargeChildName.equals("ActualAmount")) { + chargeAmount = chargeNodeChilds.item(chargeChildIndex).getTextContent(); + } else if (chargeChildName.equals("Reason")) { + reason = chargeNodeChilds.item(chargeChildIndex).getTextContent(); + } else if (chargeChildName.equals("ReasonCode")) { + reasonCode = chargeNodeChilds.item(chargeChildIndex).getTextContent(); + } else if (chargeChildName.equals("CategoryTradeTax")) { + NodeList taxChilds = chargeNodeChilds.item(chargeChildIndex).getChildNodes(); + for (int taxChildIndex = 0; taxChildIndex < taxChilds.getLength(); taxChildIndex++) { + String taxItemName = taxChilds.item(taxChildIndex).getLocalName(); + if ((taxItemName != null) && (taxItemName.equals("RateApplicablePercent") || taxItemName.equals("ApplicablePercent"))) { + taxPercent = taxChilds.item(taxChildIndex).getTextContent(); + } + } + } + } + } + + if (isCharge) { + Charge c = new Charge(new BigDecimal(chargeAmount)); + if (reason != null) { + c.setReason(reason); + } + if (reasonCode != null) { + c.setReasonCode(reasonCode); + } + if (taxPercent != null) { + c.setTaxPercent(new BigDecimal(taxPercent)); + } + zpp.addCharge(c); + } else { + Allowance a = new Allowance(new BigDecimal(chargeAmount)); + if (reason != null) { + a.setReason(reason); + } + if (reasonCode != null) { + a.setReasonCode(reasonCode); + } + if (taxPercent != null) { + a.setTaxPercent(new BigDecimal(taxPercent)); + } + zpp.addAllowance(a); + } + + } + + TransactionCalculator tc = new TransactionCalculator(zpp); + String expectedStringTotalGross = tc.getGrandTotal().toPlainString(); + EStandard whichType; + try { + whichType = getStandard(); + } catch (Exception e) { + throw new ParseException("Could not find out if it's an invoice, order, or delivery advice", 0); + + } + + if ((whichType != EStandard.despatchadvice) + && ((!expectedStringTotalGross.equals(XMLTools.nDigitFormat(expectedGrandTotal, 2))) + && (!ignoreCalculationErrors))) { + throw new ParseException( + "Could not reproduce the invoice, this could mean that it could not be read properly", 0); + } + } + return zpp; + + } + + /*** + * + * @return the file attachments embedded in XML (using base64) decoded as byte array, + * @see for PDF embedded files in FX use getFileAttachmentsPDF() + */ + public List getFileAttachmentsXML() { + return fileAttachments; + } + + /*** + * This will parse a XML into a invoice object + * + * @return the parsed invoice object + * @throws XPathExpressionException if internal xpath expressions were wrong + * @throws ParseException if the grand total of the parsed invoice could not be replicated with the new invoice + */ + public Invoice extractInvoice() throws XPathExpressionException, ParseException { + Invoice i = new Invoice(); + return extractInto(i); + + + } + + /*** + * have the item prices be determined from the line total. + * That's a workaround for some invoices which just put 0 as item price + */ + public void doRecalculateItemPricesFromLineTotals() { + recalcPrice = true; + } + + /*** + * do not raise ParseExceptions even if the reproduced invoice total does not match the given value + */ + public void doIgnoreCalculationErrors() { + ignoreCalculationErrors = true; + } + + //////////////////////////////////// + /** * @return the reference (purpose) the sender specified for this invoice */ public String getForeignReference() { - String result = extractString("//*[local-name() = 'ApplicableHeaderTradeSettlement']/*[local-name() = 'PaymentReference']"); - if (result == null || result.isEmpty()) { - result = extractString("//*[local-name() = 'ApplicableSupplyChainTradeSettlement']/*[local-name() = 'PaymentReference']"); - } - return result; + + return importedInvoice.getNumber(); } /** * @return the ZUGFeRD Profile */ public String getZUGFeRDProfil() { + importedInvoice.getProfile + @todo String guideline = extractString("//*[local-name() = 'GuidelineSpecifiedDocumentContextParameter']//*[local-name() = 'ID']"); if (guideline.contains("xrechnung")) { return "XRECHNUNG"; @@ -299,21 +731,6 @@ public class ZUGFeRDImporter { } } - /** - * @return the Invoice Currency Code - */ - public String getInvoiceCurrencyCode() { - try { - if (getVersion() == 1) { - return extractString("//*[local-name() = 'ApplicableSupplyChainTradeSettlement']//*[local-name() = 'InvoiceCurrencyCode']"); - } else { - return extractString("//*[local-name() = 'ApplicableHeaderTradeSettlement']//*[local-name() = 'InvoiceCurrencyCode']"); - } - } catch (final Exception e) { - // Exception was already logged - return ""; - } - } /** * @return the IssuerAssigned ID @@ -336,57 +753,6 @@ public class ZUGFeRDImporter { return extractIssuerAssignedID("ContractReferencedDocument"); } - private String extractIssuerAssignedID(String propertyName) { - try { - if (getVersion() == 1) { - return extractString("//*[local-name() = '" + propertyName + "']//*[local-name() = 'ID']"); - } else { - return extractString("//*[local-name() = '" + propertyName + "']//*[local-name() = 'IssuerAssignedID']"); - } - } catch (final Exception e) { - // Exception was already logged - return ""; - } - } - - /** - * @return the BuyerTradeParty ID - */ - public String getBuyerTradePartyID() { - return extractString("//*[local-name() = 'BuyerTradeParty']//*[local-name() = 'ID']"); - } - - /** - * @return the Issue Date() - */ - public String getIssueDate() { - try { - if (getVersion() == 1) { - return extractString("//*[local-name() = 'HeaderExchangedDocument']//*[local-name() = 'IssueDateTime']//*[local-name() = 'DateTimeString']"); - } else { - return extractString("//*[local-name() = 'ExchangedDocument']//*[local-name() = 'IssueDateTime']//*[local-name() = 'DateTimeString']"); - } - } catch (final Exception e) { - // Exception was already logged - return ""; - } - } - - public Date getDetailedDeliveryPeriodFrom() { - final String toParse = extractString( - "//*[local-name() = 'ApplicableHeaderTradeSettlement']" + - "//*[local-name() = 'BillingSpecifiedPeriod']" + - "//*[local-name() = 'StartDateTime']//*[local-name() = 'DateTimeString']"); - return tryDate(toParse); - } - - public Date getDetailedDeliveryPeriodTo() { - final String toParse = extractString( - "//*[local-name() = 'ApplicableHeaderTradeSettlement']" + - "//*[local-name() = 'BillingSpecifiedPeriod']" + - "//*[local-name() = 'EndDateTime']//*[local-name() = 'DateTimeString']"); - return tryDate(toParse); - } /** * @return the TaxBasisTotalAmount @@ -647,6 +1013,53 @@ public class ZUGFeRDImporter { } + //////////////////// + + /** + * @return the Invoice Currency Code + */ + public String getInvoiceCurrencyCode() { + return importedInvoice.getCurrency(); + } + + + private String extractIssuerAssignedID(String propertyName) { + try { + if (getVersion() == 1) { + return extractString("//*[local-name() = '" + propertyName + "']//*[local-name() = 'ID']"); + } else { + return extractString("//*[local-name() = '" + propertyName + "']//*[local-name() = 'IssuerAssignedID']"); + } + } catch (final Exception e) { + // Exception was already logged + return ""; + } + } + + /** + * @return the BuyerTradeParty ID + */ + public String getBuyerTradePartyID() { + return importedInvoice.getRecipient().getID(); + } + + /** + * @return the Issue Date() + */ + public String getIssueDate() { + SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd"); + return sdf.format(importedInvoice.getIssueDate()); + } + + public Date getDetailedDeliveryPeriodFrom() { + return importedInvoice.getDetailedDeliveryPeriodFrom(); + } + + public Date getDetailedDeliveryPeriodTo() { + return importedInvoice.getDetailedDeliveryPeriodTo(); + } + + public HashMap getAdditionalData() { return additionalXMLs; } diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java index 4a28fd44..bcc56b3c 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java @@ -25,9 +25,6 @@ import org.w3c.dom.NodeList; public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter { private static final Logger LOGGER = LoggerFactory.getLogger(ZUGFeRDInvoiceImporter.class.getCanonicalName()); // log - private boolean recalcPrice = false; - private boolean ignoreCalculationErrors = false; - private ArrayList fileAttachments=new ArrayList<>(); public ZUGFeRDInvoiceImporter() { super(); @@ -41,420 +38,4 @@ public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter { super(stream); } - public void fromXML(String XML) { - try { - containsMeta = true; - setRawXML(XML.getBytes(StandardCharsets.UTF_8)); - } catch (IOException e) { - LOGGER.error(e.getMessage(), e); - } - } - - - /*** - * This will parse a XML into the given invoice object - * @param zpp the invoice to be altered - * @return the parsed invoice object - * @throws XPathExpressionException if xpath could not be evaluated - * @throws ParseException if the grand total of the parsed invoice could not be replicated with the new invoice - */ - public Invoice extractInto(Invoice zpp) throws XPathExpressionException, ParseException { - - String number = ""; - String typeCode = null; - /* - * dummywerte sind derzeit noch setDueDate setIssueDate setDeliveryDate - * setSender setRecipient setnumber bspw. due date - * //ExchangedDocument//IssueDateTime//DateTimeString : due date optional - */ - XPathFactory xpathFact = XPathFactory.newInstance(); - XPath xpath = xpathFact.newXPath(); - XPathExpression xpr = xpath.compile("//*[local-name()=\"SellerTradeParty\"]|//*[local-name()=\"AccountingSupplierParty\"]/*"); - NodeList SellerNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); - - xpr = xpath.compile("//*[local-name()=\"BuyerTradeParty\"]|//*[local-name()=\"AccountingCustomerParty\"]/*"); - NodeList BuyerNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); - xpr = xpath.compile("//*[local-name()=\"ExchangedDocument\"]|//*[local-name()=\"HeaderExchangedDocument\"]"); - NodeList ExchangedDocumentNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); - - xpr = xpath.compile("//*[local-name()=\"GrandTotalAmount\"]|//*[local-name()=\"PayableAmount\"]"); - BigDecimal expectedGrandTotal = null; - NodeList totalNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); - if (totalNodes.getLength() > 0) { - expectedGrandTotal = new BigDecimal(totalNodes.item(0).getTextContent()); - } - - Date issueDate = null; - Date dueDate = null; - Date deliveryDate = null; - String despatchAdviceReferencedDocument = null; - for (int i = 0; i < ExchangedDocumentNodes.getLength(); i++) { - - // nodes.item(i).getTextContent())) { - Node exchangedDocumentNode = ExchangedDocumentNodes.item(i); - NodeList exchangedDocumentChilds = exchangedDocumentNode.getChildNodes(); - for (int documentChildIndex = 0; documentChildIndex < exchangedDocumentChilds.getLength(); documentChildIndex++) { - Node item = exchangedDocumentChilds.item(documentChildIndex); - if ((item.getLocalName() != null) && (item.getLocalName().equals("ID"))) { - number = item.getTextContent(); - } - if ((item.getLocalName() != null) && (item.getLocalName().equals("TypeCode"))) { - typeCode = item.getTextContent(); - } - if ((item.getLocalName() != null) && (item.getLocalName().equals("IssueDateTime"))) { - NodeList issueDateTimeChilds = item.getChildNodes(); - for (int issueDateChildIndex = 0; issueDateChildIndex < issueDateTimeChilds.getLength(); issueDateChildIndex++) { - if ((issueDateTimeChilds.item(issueDateChildIndex).getLocalName() != null) - && (issueDateTimeChilds.item(issueDateChildIndex).getLocalName().equals("DateTimeString"))) { - issueDate = new SimpleDateFormat("yyyyMMdd").parse(issueDateTimeChilds.item(issueDateChildIndex).getTextContent()); - } - } - } - } - } - String rootNode = extractString("local-name(/*)"); - if (rootNode.equals("Invoice")) { - // UBL... - number = extractString("//*[local-name()=\"Invoice\"]/*[local-name()=\"ID\"]").trim(); - issueDate = new SimpleDateFormat("yyyy-MM-dd").parse(extractString("//*[local-name()=\"Invoice\"]/*[local-name()=\"IssueDate\"]").trim()); - String dueDt = extractString("//*[local-name()=\"Invoice\"]/*[local-name()=\"DueDate\"]").trim(); - if (dueDt.length() > 0) { - dueDate = new SimpleDateFormat("yyyy-MM-dd").parse(dueDt); - } - String deliveryDt = extractString("//*[local-name()=\"Delivery\"]/*[local-name()=\"ActualDeliveryDate\"]").trim(); - if (deliveryDt.length() > 0) { - deliveryDate = new SimpleDateFormat("yyyy-MM-dd").parse(deliveryDt); - } - } - xpr = xpath.compile("//*[local-name()=\"ApplicableHeaderTradeDelivery\"]"); - NodeList headerTradeDeliveryNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); - - for (int i = 0; i < headerTradeDeliveryNodes.getLength(); i++) { - // nodes.item(i).getTextContent())) { - Node headerTradeDeliveryNode = headerTradeDeliveryNodes.item(i); - NodeList headerTradeDeliveryChilds = headerTradeDeliveryNode.getChildNodes(); - for (int deliveryChildIndex = 0; deliveryChildIndex < headerTradeDeliveryChilds.getLength(); deliveryChildIndex++) { - if (headerTradeDeliveryChilds.item(deliveryChildIndex).getLocalName() != null) { - if (headerTradeDeliveryChilds.item(deliveryChildIndex).getLocalName().equals("ActualDeliverySupplyChainEvent")) { - NodeList actualDeliveryChilds = headerTradeDeliveryChilds.item(deliveryChildIndex).getChildNodes(); - for (int actualDeliveryChildIndex = 0; actualDeliveryChildIndex < actualDeliveryChilds.getLength(); actualDeliveryChildIndex++) { - if ((actualDeliveryChilds.item(actualDeliveryChildIndex).getLocalName() != null) - && (actualDeliveryChilds.item(actualDeliveryChildIndex).getLocalName().equals("OccurrenceDateTime"))) { - NodeList occurenceChilds = actualDeliveryChilds.item(actualDeliveryChildIndex).getChildNodes(); - for (int occurenceChildIndex = 0; occurenceChildIndex < occurenceChilds.getLength(); occurenceChildIndex++) { - if ((occurenceChilds.item(occurenceChildIndex).getLocalName() != null) - && (occurenceChilds.item(occurenceChildIndex).getLocalName().equals("DateTimeString"))) { - deliveryDate = new SimpleDateFormat("yyyyMMdd").parse(occurenceChilds.item(occurenceChildIndex).getTextContent()); - } - } - } - } - } - - if (headerTradeDeliveryChilds.item(deliveryChildIndex).getLocalName().equals("DespatchAdviceReferencedDocument")) { - NodeList despatchAdviceChilds = headerTradeDeliveryChilds.item(deliveryChildIndex).getChildNodes(); - for (int despatchAdviceChildIndex = 0; despatchAdviceChildIndex < despatchAdviceChilds.getLength(); despatchAdviceChildIndex++) { - if (despatchAdviceChilds.item(despatchAdviceChildIndex).getLocalName() != null - && despatchAdviceChilds.item(despatchAdviceChildIndex).getLocalName().equals("IssuerAssignedID")) { - despatchAdviceReferencedDocument = despatchAdviceChilds.item(despatchAdviceChildIndex).getTextContent(); - } - } - } - } - } - } - - xpr = xpath.compile("//*[local-name()=\"ApplicableHeaderTradeAgreement\"]"); - NodeList headerTradeAgreementNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); - String buyerOrderIssuerAssignedID = null; - String sellerOrderIssuerAssignedID = null; - for (int i = 0; i < headerTradeAgreementNodes.getLength(); i++) { - // nodes.item(i).getTextContent())) { - Node headerTradeAgreementNode = headerTradeAgreementNodes.item(i); - NodeList headerTradeAgreementChilds = headerTradeAgreementNode.getChildNodes(); - for (int agreementChildIndex = 0; agreementChildIndex < headerTradeAgreementChilds.getLength(); agreementChildIndex++) { - if (headerTradeAgreementChilds.item(agreementChildIndex).getLocalName() != null) { - if (headerTradeAgreementChilds.item(agreementChildIndex).getLocalName().equals("BuyerOrderReferencedDocument")) { - NodeList buyerOrderChilds = headerTradeAgreementChilds.item(agreementChildIndex).getChildNodes(); - for (int buyerOrderChildIndex = 0; buyerOrderChildIndex < buyerOrderChilds.getLength(); buyerOrderChildIndex++) { - if ((buyerOrderChilds.item(buyerOrderChildIndex).getLocalName() != null) - && (buyerOrderChilds.item(buyerOrderChildIndex).getLocalName().equals("IssuerAssignedID"))) { - buyerOrderIssuerAssignedID = buyerOrderChilds.item(buyerOrderChildIndex).getTextContent(); - } - } - } - - if (headerTradeAgreementChilds.item(agreementChildIndex).getLocalName().equals("SellerOrderReferencedDocument")) { - NodeList sellerOrderChilds = headerTradeAgreementChilds.item(agreementChildIndex).getChildNodes(); - for (int sellerOrderChildIndex = 0; sellerOrderChildIndex < sellerOrderChilds.getLength(); sellerOrderChildIndex++) { - if ((sellerOrderChilds.item(sellerOrderChildIndex).getLocalName() != null) - && (sellerOrderChilds.item(sellerOrderChildIndex).getLocalName().equals("IssuerAssignedID"))) { - sellerOrderIssuerAssignedID = sellerOrderChilds.item(sellerOrderChildIndex).getTextContent(); - } - } - } - } - } - - } - - - xpr = xpath.compile("//*[local-name()=\"ApplicableHeaderTradeSettlement\"]|//*[local-name()=\"ApplicableSupplyChainTradeSettlement\"]"); - NodeList headerTradeSettlementNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); - List bankDetails = new ArrayList<>(); - - for (int i = 0; i < headerTradeSettlementNodes.getLength(); i++) { - // nodes.item(i).getTextContent())) { - Node headerTradeSettlementNode = headerTradeSettlementNodes.item(i); - NodeList headerTradeSettlementChilds = headerTradeSettlementNode.getChildNodes(); - for (int settlementChildIndex = 0; settlementChildIndex < headerTradeSettlementChilds.getLength(); settlementChildIndex++) { - if ((headerTradeSettlementChilds.item(settlementChildIndex).getLocalName() != null) - && (headerTradeSettlementChilds.item(settlementChildIndex).getLocalName().equals("SpecifiedTradePaymentTerms"))) { - NodeList paymentTermChilds = headerTradeSettlementChilds.item(settlementChildIndex).getChildNodes(); - for (int paymentTermChildIndex = 0; paymentTermChildIndex < paymentTermChilds.getLength(); paymentTermChildIndex++) { - if ((paymentTermChilds.item(paymentTermChildIndex).getLocalName() != null) && (paymentTermChilds.item(paymentTermChildIndex).getLocalName().equals("DueDateDateTime"))) { - NodeList dueDateChilds = paymentTermChilds.item(paymentTermChildIndex).getChildNodes(); - for (int dueDateChildIndex = 0; dueDateChildIndex < dueDateChilds.getLength(); dueDateChildIndex++) { - if ((dueDateChilds.item(dueDateChildIndex).getLocalName() != null) && (dueDateChilds.item(dueDateChildIndex).getLocalName().equals("DateTimeString"))) { - dueDate = new SimpleDateFormat("yyyyMMdd").parse(dueDateChilds.item(dueDateChildIndex).getTextContent()); - } - } - } - } - } - - if ((headerTradeSettlementChilds.item(settlementChildIndex).getLocalName() != null) - && (headerTradeSettlementChilds.item(settlementChildIndex).getLocalName().equals("SpecifiedTradeSettlementPaymentMeans"))) { - NodeList paymentMeansChilds = headerTradeSettlementChilds.item(settlementChildIndex).getChildNodes(); - for (int paymentMeansChildIndex = 0; paymentMeansChildIndex < paymentMeansChilds.getLength(); paymentMeansChildIndex++) { - String IBAN = null, BIC = null; - if ((paymentMeansChilds.item(paymentMeansChildIndex).getLocalName() != null) && (paymentMeansChilds.item(paymentMeansChildIndex).getLocalName().equals("PayeePartyCreditorFinancialAccount"))) { - NodeList accountChilds = paymentMeansChilds.item(paymentMeansChildIndex).getChildNodes(); - for (int accountChildIndex = 0; accountChildIndex < accountChilds.getLength(); accountChildIndex++) { - if ((accountChilds.item(accountChildIndex).getLocalName() != null) && (accountChilds.item(accountChildIndex).getLocalName().equals("IBANID"))) {//CII - IBAN = accountChilds.item(accountChildIndex).getTextContent(); - } - } - } - if ((paymentMeansChilds.item(paymentMeansChildIndex).getLocalName() != null) && (paymentMeansChilds.item(paymentMeansChildIndex).getLocalName().equals("PayeePartyCreditorFinancialInstitution"))) { - NodeList accountChilds = paymentMeansChilds.item(paymentMeansChildIndex).getChildNodes(); - for (int accountChildIndex = 0; accountChildIndex < accountChilds.getLength(); accountChildIndex++) { - if ((accountChilds.item(accountChildIndex).getLocalName() != null) && (accountChilds.item(accountChildIndex).getLocalName().equals("BICID"))) {//CII - BIC = accountChilds.item(accountChildIndex).getTextContent(); - } - } - } - if (IBAN != null) { - BankDetails bd = new BankDetails(IBAN); - if (BIC != null) { - bd.setBIC(BIC); - } - bankDetails.add(bd); - } - } - } - } - } - - - xpr = xpath.compile("//*[local-name()=\"PaymentMeans\"]"); //UBL only - NodeList paymentMeansNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); - - for (int i = 0; i < paymentMeansNodes.getLength(); i++) { - // nodes.item(i).getTextContent())) { - Node paymentMeansNode = paymentMeansNodes.item(i); - NodeList paymentMeansChilds = paymentMeansNode.getChildNodes(); - for (int meansChildIndex = 0; meansChildIndex < paymentMeansChilds.getLength(); meansChildIndex++) { - if ((paymentMeansChilds.item(meansChildIndex).getLocalName() != null) - && (paymentMeansChilds.item(meansChildIndex).getLocalName().equals("PayeeFinancialAccount"))) { - NodeList paymentTermChilds = paymentMeansChilds.item(meansChildIndex).getChildNodes(); - for (int paymentTermChildIndex = 0; paymentTermChildIndex < paymentTermChilds.getLength(); paymentTermChildIndex++) { - if ((paymentTermChilds.item(paymentTermChildIndex).getLocalName() != null) && (paymentTermChilds.item(paymentTermChildIndex).getLocalName().equals("ID"))) { - String IBAN = paymentTermChilds.item(paymentTermChildIndex).getTextContent(); - if (IBAN != null) { - BankDetails bd = new BankDetails(IBAN); - bankDetails.add(bd); - } - } - } - } - } - } - - zpp.setDueDate(dueDate).setDeliveryDate(deliveryDate).setIssueDate(issueDate).setSender(new TradeParty(SellerNodes)).setRecipient(new TradeParty(BuyerNodes)).setNumber(number).setDocumentCode(typeCode); - bankDetails.forEach(bankDetail -> zpp.getSender().addBankDetails(bankDetail)); - - if (buyerOrderIssuerAssignedID != null) { - zpp.setBuyerOrderReferencedDocumentID(buyerOrderIssuerAssignedID); - } - if (sellerOrderIssuerAssignedID != null) { - zpp.setSellerOrderReferencedDocumentID(sellerOrderIssuerAssignedID); - } - if (despatchAdviceReferencedDocument != null) { - zpp.setDespatchAdviceReferencedDocumentID(despatchAdviceReferencedDocument); - } - - zpp.setOwnOrganisationName(extractString("//*[local-name()=\"SellerTradeParty\"]/*[local-name()=\"Name\"]|//*[local-name()=\"AccountingSupplierParty\"]/*[local-name()=\"Party\"]/*[local-name()=\"PartyName\"]").trim()); - - xpr = xpath.compile("//*[local-name()=\"BuyerReference\"]"); - String buyerReference = null; - totalNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); - if (totalNodes.getLength() > 0) { - buyerReference = totalNodes.item(0).getTextContent(); - } - if (buyerReference != null) { - zpp.setReferenceNumber(buyerReference); - } - - xpr = xpath.compile("//*[local-name()=\"IncludedSupplyChainTradeLineItem\"]|//*[local-name()=\"InvoiceLine\"]"); - NodeList nodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); - - if (nodes.getLength() != 0) { - for (int i = 0; i < nodes.getLength(); i++) { - - Node currentItemNode = nodes.item(i); - Item it = new Item(currentItemNode.getChildNodes(), recalcPrice); - zpp.addItem(it); - - } - - // now handling base64 encoded attachments AttachmentBinaryObject=CII, EmbeddedDocumentBinaryObject=UBL - xpr = xpath.compile("//*[local-name()=\"AttachmentBinaryObject\"]|//*[local-name()=\"EmbeddedDocumentBinaryObject\"]"); - NodeList attachmentNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); - for (int i = 0; i < attachmentNodes.getLength(); i++) { - FileAttachment fa=new FileAttachment(attachmentNodes.item(i).getAttributes().getNamedItem("filename").getNodeValue(),attachmentNodes.item(i).getAttributes().getNamedItem("mimeCode").getNodeValue(),"Data", Base64.getDecoder().decode(attachmentNodes.item(i).getTextContent())); - fileAttachments.add(fa); - // filename = "Aufmass.png" mimeCode = "image/png" - //EmbeddedDocumentBinaryObject cbc:EmbeddedDocumentBinaryObject mimeCode="image/png" filename="Aufmass.png" - } - - // item level charges+allowances are not yet handled but a lower item price will - // be read, - // so the invoice remains arithmetically correct - // -> parse document level charges+allowances - xpr = xpath.compile("//*[local-name()=\"SpecifiedTradeAllowanceCharge\"]"); - NodeList chargeNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); - for (int i = 0; i < chargeNodes.getLength(); i++) { - NodeList chargeNodeChilds = chargeNodes.item(i).getChildNodes(); - boolean isCharge = true; - String chargeAmount = null; - String reason = null; - String reasonCode = null; - String taxPercent = null; - for (int chargeChildIndex = 0; chargeChildIndex < chargeNodeChilds.getLength(); chargeChildIndex++) { - String chargeChildName = chargeNodeChilds.item(chargeChildIndex).getLocalName(); - if (chargeChildName != null) { - - if (chargeChildName.equals("ChargeIndicator")) { - NodeList indicatorChilds = chargeNodeChilds.item(chargeChildIndex).getChildNodes(); - for (int indicatorChildIndex = 0; indicatorChildIndex < indicatorChilds.getLength(); indicatorChildIndex++) { - if ((indicatorChilds.item(indicatorChildIndex).getLocalName() != null) - && (indicatorChilds.item(indicatorChildIndex).getLocalName().equals("Indicator"))) { - isCharge = indicatorChilds.item(indicatorChildIndex).getTextContent().equalsIgnoreCase("true"); - } - } - } else if (chargeChildName.equals("ActualAmount")) { - chargeAmount = chargeNodeChilds.item(chargeChildIndex).getTextContent(); - } else if (chargeChildName.equals("Reason")) { - reason = chargeNodeChilds.item(chargeChildIndex).getTextContent(); - } else if (chargeChildName.equals("ReasonCode")) { - reasonCode = chargeNodeChilds.item(chargeChildIndex).getTextContent(); - } else if (chargeChildName.equals("CategoryTradeTax")) { - NodeList taxChilds = chargeNodeChilds.item(chargeChildIndex).getChildNodes(); - for (int taxChildIndex = 0; taxChildIndex < taxChilds.getLength(); taxChildIndex++) { - String taxItemName = taxChilds.item(taxChildIndex).getLocalName(); - if ((taxItemName != null) && (taxItemName.equals("RateApplicablePercent") || taxItemName.equals("ApplicablePercent"))) { - taxPercent = taxChilds.item(taxChildIndex).getTextContent(); - } - } - } - } - } - - if (isCharge) { - Charge c = new Charge(new BigDecimal(chargeAmount)); - if (reason != null) { - c.setReason(reason); - } - if (reasonCode != null) { - c.setReasonCode(reasonCode); - } - if (taxPercent != null) { - c.setTaxPercent(new BigDecimal(taxPercent)); - } - zpp.addCharge(c); - } else { - Allowance a = new Allowance(new BigDecimal(chargeAmount)); - if (reason != null) { - a.setReason(reason); - } - if (reasonCode != null) { - a.setReasonCode(reasonCode); - } - if (taxPercent != null) { - a.setTaxPercent(new BigDecimal(taxPercent)); - } - zpp.addAllowance(a); - } - - } - - TransactionCalculator tc = new TransactionCalculator(zpp); - String expectedStringTotalGross = tc.getGrandTotal().toPlainString(); - EStandard whichType; - try { - whichType = getStandard(); - } catch (Exception e) { - throw new ParseException("Could not find out if it's an invoice, order, or delivery advice", 0); - - } - - if ((whichType != EStandard.despatchadvice) - && ((!expectedStringTotalGross.equals(XMLTools.nDigitFormat(expectedGrandTotal, 2))) - && (!ignoreCalculationErrors))) { - throw new ParseException( - "Could not reproduce the invoice, this could mean that it could not be read properly", 0); - } - } - return zpp; - - } - - /*** - * - * @return the file attachments embedded in XML (using base64) decoded as byte array, - * @see for PDF embedded files in FX use getFileAttachmentsPDF() - */ - public List getFileAttachmentsXML() { - return fileAttachments; - } - - /*** - * This will parse a XML into a invoice object - * - * @return the parsed invoice object - * @throws XPathExpressionException if internal xpath expressions were wrong - * @throws ParseException if the grand total of the parsed invoice could not be replicated with the new invoice - */ - public Invoice extractInvoice() throws XPathExpressionException, ParseException { - Invoice i = new Invoice(); - return extractInto(i); - - - } - - /*** - * have the item prices be determined from the line total. - * That's a workaround for some invoices which just put 0 as item price - */ - public void doRecalculateItemPricesFromLineTotals() { - recalcPrice = true; - } - - /*** - * do not raise ParseExceptions even if the reproduced invoice total does not match the given value - */ - public void doIgnoreCalculationErrors() { - ignoreCalculationErrors = true; - } }