diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/ZohoAPIService.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/ZohoAPIService.java
index d456877..83f74cd 100644
--- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/ZohoAPIService.java
+++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/ZohoAPIService.java
@@ -20,6 +20,7 @@ import java.util.stream.Collectors;
import org.imixs.workflow.FileData;
import org.imixs.workflow.ItemCollection;
+import org.imixs.workflow.engine.DocumentService;
import org.imixs.workflow.exceptions.PluginException;
import com.alexanderlogistics.InvoiceUtil;
@@ -33,6 +34,7 @@ import com.alexanderlogistics.zoho.dto.ZohoCreditnote;
import com.alexanderlogistics.zoho.dto.ZohoCreditnoteCreateResponse;
import com.alexanderlogistics.zoho.dto.ZohoInvoice;
import com.alexanderlogistics.zoho.dto.ZohoInvoiceCreateResponse;
+import com.alexanderlogistics.zoho.dto.ZohoInvoiceFindResponse;
import com.alexanderlogistics.zoho.dto.ZohoInvoiceItem;
import com.alexanderlogistics.zoho.dto.ZohoTax;
import com.alexanderlogistics.zoho.dto.ZohoTaxListResponse;
@@ -75,6 +77,9 @@ public class ZohoAPIService {
@Inject
ZohoOAuthManager zohoOAuthManager;
+ @Inject
+ DocumentService documentService;
+
@PostConstruct
void init() {
this.httpClient = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build();
@@ -144,6 +149,61 @@ public class ZohoAPIService {
}
+ /**
+ * This finder method returns the invoice ID to a given invoice.number
+ * The method returns null if no invoice with this nubmer exists
+ *
+ * @param companyName
+ * @param accessToken
+ * @return
+ * @throws PluginException
+ */
+ public ZohoInvoice findInvoiceIDByInvoiceNumber(String invoiceNumber, String accessToken)
+ throws PluginException {
+
+ // Print JSON to console for verification
+ logger.info("├── Zoho lookup invoice '" + invoiceNumber + "'...");
+ try {
+ // https://www.zohoapis.com/books/v3/contacts?organization_id=10234695&company_name=xxx'
+ String uri = zohoOAuthManager.getBaseURI() + "invoices?organization_id="
+ + zohoOAuthManager.getOrganization() + "&invoice_number="
+ + URLEncoder.encode(invoiceNumber, StandardCharsets.UTF_8);
+ logger.fine("│ ├── uri= " + uri);
+ logger.fine("│ ├── accessToken=" + accessToken);
+ HttpRequest request = HttpRequest.newBuilder().uri(URI.create(uri))
+ .header("Authorization", "Zoho-oauthtoken " + accessToken)
+ .header("Content-Type", "application/json").GET().build();
+
+ HttpResponse response;
+
+ response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
+
+ // Print response details to console
+ logger.fine("├── Zoho Response:");
+ logger.fine("│ ├── Status Code: " + response.statusCode());
+ logger.fine("│ ├── Response Body: " + response.body());
+ if (response.statusCode() >= 300) {
+ throw new PluginException(this.getClass().getName(), "Zoho API error",
+ "Failed to find invoice - Status code: " + response.statusCode() + ", Response: "
+ + response.body());
+ }
+
+ ZohoInvoiceFindResponse invoicesResponse = jsonb.fromJson(response.body(), ZohoInvoiceFindResponse.class);
+
+ if (invoicesResponse != null && invoicesResponse.getInvoices() != null
+ && invoicesResponse.getInvoices().size() > 0) {
+ // return 1 entry
+ ZohoInvoice invoice = invoicesResponse.getInvoices().get(0);
+ return invoice;
+ }
+ } catch (IOException | InterruptedException e) {
+ throw new PluginException(this.getClass().getName(), "Zoho API error",
+ "Failed to find Contacts: " + e.getMessage());
+ }
+ return null;
+
+ }
+
/**
* Exports an invocie document
*
@@ -231,8 +291,19 @@ public class ZohoAPIService {
String currencyID = lookupCurrency(invoice.getItemValueString("invoice.currency"), accessToken);
+ // Lookup die Invoice ID in zOho
+
+ ZohoInvoice invoiceZoho = null;
+ String invoiceRefID = invoice.getItemValueString("invoice.ref");
+ ItemCollection invoiceRef = documentService.load(invoiceRefID);
+ if (invoiceRef != null) {
+ invoiceZoho = findInvoiceIDByInvoiceNumber(invoiceRef.getItemValueString("invoice.number"),
+ accessToken);
+ logger.info("├── Zoho fond invoice zoho ref: " + invoiceZoho.getInvoiceId());
+ }
+
// Konvertiere ItemCollection zu ZohoInvoiceDTO
- ZohoCreditnote zohoCreditnote = convertToZohoCreditnote(invoice, contactID, currencyID);
+ ZohoCreditnote zohoCreditnote = convertToZohoCreditnote(invoice, contactID, currencyID, invoiceZoho);
try {
String requestBody = jsonb.toJson(zohoCreditnote);
@@ -244,6 +315,10 @@ public class ZohoAPIService {
// https://www.zohoapis.com/books/v3/creditnotes?organization_id=10234695'
String uri = zohoOAuthManager.getBaseURI() + "creditnotes?organization_id="
+ zohoOAuthManager.getOrganization();
+
+ if (zohoCreditnote != null) {
+ uri = uri + "&invoice_id=" + invoiceZoho.getInvoiceId();
+ }
logger.fine("│ ├── uri= " + uri);
logger.fine("│ ├── accessToken=" + accessToken);
HttpRequest request = HttpRequest.newBuilder().uri(URI.create(uri))
@@ -565,14 +640,15 @@ public class ZohoAPIService {
* @return
* @throws PluginException
*/
- private ZohoCreditnote convertToZohoCreditnote(ItemCollection invoice, String contactID, String currencyID)
+ private ZohoCreditnote convertToZohoCreditnote(ItemCollection invoice, String contactID, String currencyID,
+ ZohoInvoice zohoInvoice)
throws PluginException {
ZohoCreditnote zohoCreditnote = new ZohoCreditnote();
// Basisinformationen
zohoCreditnote.setCreditNoteNumber(invoice.getItemValueString("invoice.number"));
- zohoCreditnote.setReferenceNumber(invoice.getItemValueString("invoice.number"));
+
zohoCreditnote.setDate(
LocalDate.ofInstant(invoice.getItemValueDate("invoice.date").toInstant(), ZoneId.systemDefault()));
// zohoCreditnote.setDueDate(
@@ -586,6 +662,19 @@ public class ZohoAPIService {
zohoCreditnote.setCurrencyId(currencyID);
}
+ // do we have an invoice?
+ if (zohoInvoice != null) {
+ String placeOfSupply = zohoInvoice.getPlaceOfSupply();
+ if (placeOfSupply == null) {
+ placeOfSupply = "DU";
+ }
+ logger.info("│ ├── set place of supply=" + placeOfSupply);
+ zohoCreditnote.setPlaceOfSupply(placeOfSupply);
+ // logger.info("....assign invoice");
+ // ZohoCreditnoteInvoiceItem zcii = new ZohoCreditnoteInvoiceItem(zohoInvoice);
+ // zohoCreditnote.setInvoices(java.util.Arrays.asList(zcii));
+ }
+
// convert total by rate - negativer betrag!
zohoCreditnote.setTotal(Math.abs(invoice.getItemValueDouble("invoice.total")));
// set exchange Rate
@@ -606,6 +695,30 @@ public class ZohoAPIService {
}).collect(Collectors.toList());
zohoCreditnote.setLineItems(lineItems);
+
+ // add line items...
+ // changes 24.06.2025 - Keine einzelPositionen mehr an Zoho senden, da diese von
+ // Cargosoft unbrauchbar sind.
+ // List positionen = InvoiceUtil.explodeChildList(invoice);
+ // List lineItems = positionen.stream().map(item -> {
+ // ItemCollection itemCol = (ItemCollection) item;
+ // ZohoInvoiceItem lineItem = new ZohoInvoiceItem();
+ // lineItem.setName(itemCol.getItemValueString("datev.text"));
+ // lineItem.setDescription(itemCol.getItemValueString("billingtext"));
+ // lineItem.setRate(itemCol.getItemValueDouble("datev.umsatz"));
+ // lineItem.setQuantity(1);
+ // return lineItem;
+ // }).collect(Collectors.toList());
+ // List lineItems = new ArrayList<>();
+
+ // create only one single item!
+ ZohoInvoiceItem lineItem = new ZohoInvoiceItem();
+ lineItem.setName(invoice.getItemValueString("invoice.text"));
+ lineItem.setDescription("");
+ lineItem.setRate(invoice.getItemValueDouble("invoice.total"));
+ lineItem.setQuantity(1);
+ lineItems.add(lineItem);
+
return zohoCreditnote;
}
diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/dto/ZohoCreditnote.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/dto/ZohoCreditnote.java
index 8f80001..1b708ee 100644
--- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/dto/ZohoCreditnote.java
+++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/dto/ZohoCreditnote.java
@@ -25,12 +25,18 @@ public class ZohoCreditnote {
@JsonbProperty("reference_number")
private String referenceNumber;
+ @JsonbProperty("place_of_supply")
+ private String placeOfSupply;
+
@JsonbProperty("line_items")
private List lineItems;
@JsonbProperty("exchange_rate")
private double exchangeRate;
+ @JsonbProperty("invoices")
+ private List invoices;
+
private double total;
// Getter und Setter
@@ -106,4 +112,20 @@ public class ZohoCreditnote {
public void setReferenceNumber(String referenceNumber) {
this.referenceNumber = referenceNumber;
}
+
+ public List getInvoices() {
+ return invoices;
+ }
+
+ public void setInvoices(List invoices) {
+ this.invoices = invoices;
+ }
+
+ public String getPlaceOfSupply() {
+ return placeOfSupply;
+ }
+
+ public void setPlaceOfSupply(String placeOfSupply) {
+ this.placeOfSupply = placeOfSupply;
+ }
}
diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/dto/ZohoCreditnoteInvoiceItem.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/dto/ZohoCreditnoteInvoiceItem.java
new file mode 100644
index 0000000..12e7030
--- /dev/null
+++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/dto/ZohoCreditnoteInvoiceItem.java
@@ -0,0 +1,49 @@
+package com.alexanderlogistics.zoho.dto;
+
+import jakarta.json.bind.annotation.JsonbProperty;
+
+/**
+ * Referenz von Invoices in einer CreditNote
+ */
+public class ZohoCreditnoteInvoiceItem {
+
+ @JsonbProperty("invoice_id")
+ private String invoiceId;
+
+ @JsonbProperty("invoice_number")
+ private String invoiceNumber;
+
+ private double amount;
+
+ public ZohoCreditnoteInvoiceItem(ZohoInvoice invoice) {
+ this.invoiceId = invoice.getInvoiceId();
+ this.invoiceNumber = invoice.getInvoiceNumber();
+ this.amount = invoice.getTotal();
+ }
+
+ // Getter und Setter
+ public String getInvoiceId() {
+ return invoiceId;
+ }
+
+ public void setInvoiceId(String invoiceId) {
+ this.invoiceId = invoiceId;
+ }
+
+ public String getInvoiceNumber() {
+ return invoiceNumber;
+ }
+
+ public void setInvoiceNumber(String invoiceNumber) {
+ this.invoiceNumber = invoiceNumber;
+ }
+
+ public double getAmount() {
+ return amount;
+ }
+
+ public void setAmount(double amount) {
+ this.amount = amount;
+ }
+
+}
diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/dto/ZohoInvoice.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/dto/ZohoInvoice.java
index d4b0650..c656787 100644
--- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/dto/ZohoInvoice.java
+++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/dto/ZohoInvoice.java
@@ -33,6 +33,9 @@ public class ZohoInvoice {
@JsonbProperty("exchange_rate")
private double exchangeRate;
+ @JsonbProperty("place_of_supply")
+ private String placeOfSupply;
+
private double total;
// Getter und Setter
@@ -108,4 +111,12 @@ public class ZohoInvoice {
public void setExchangeRate(double exchangeRate) {
this.exchangeRate = exchangeRate;
}
+
+ public String getPlaceOfSupply() {
+ return placeOfSupply;
+ }
+
+ public void setPlaceOfSupply(String placeOfSupply) {
+ this.placeOfSupply = placeOfSupply;
+ }
}
diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/dto/ZohoInvoiceFindResponse.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/dto/ZohoInvoiceFindResponse.java
new file mode 100644
index 0000000..876947b
--- /dev/null
+++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/dto/ZohoInvoiceFindResponse.java
@@ -0,0 +1,41 @@
+package com.alexanderlogistics.zoho.dto;
+
+import java.util.List;
+
+import jakarta.json.bind.annotation.JsonbProperty;
+
+public class ZohoInvoiceFindResponse {
+ @JsonbProperty("code")
+ private int code;
+
+ @JsonbProperty("message")
+ private String message;
+
+ @JsonbProperty("invoices")
+ private List invoices;
+
+ // Getter und Setter
+ public int getCode() {
+ return code;
+ }
+
+ public void setCode(int code) {
+ this.code = code;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ public List getInvoices() {
+ return invoices;
+ }
+
+ public void setInvoices(List invoices) {
+ this.invoices = invoices;
+ }
+}
diff --git a/workflow/dwc/rechnungsausgang-dwc-1.0.6.bpmn b/workflow/dwc/rechnungsausgang-dwc-1.0.6.bpmn
new file mode 100644
index 0000000..838e588
--- /dev/null
+++ b/workflow/dwc/rechnungsausgang-dwc-1.0.6.bpmn
@@ -0,0 +1,2687 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+$workflowstatus
+
+]]>
+
+
+ In case you have already made the payment, please disregard this notice. If you require any information regarding your account, please do not hesitate to contact us.
+Thank you for your cooperation.
+
+Kind regards
Alexander Global Logistics GmbH
+
+