zoho schnittstelle

This commit is contained in:
Ralph Soika 2025-08-29 09:43:53 +02:00
parent fe23ba4050
commit fbf5bf2962
5 changed files with 194 additions and 71 deletions

View file

@ -158,7 +158,7 @@ public class ZohoAPIService {
* @return
* @throws PluginException
*/
public ZohoInvoice findInvoiceIDByInvoiceNumber(String invoiceNumber, String accessToken)
public ZohoInvoice findInvoiceIDByInvoiceNumber(String invoiceNumber, String accessToken, boolean debug)
throws PluginException {
// Print JSON to console for verification
@ -179,9 +179,11 @@ public class ZohoAPIService {
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 (debug) {
logger.info("├── Zoho Response:");
logger.info("│ ├── Status Code: " + response.statusCode());
logger.info("│ ├── 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: "
@ -212,7 +214,7 @@ public class ZohoAPIService {
* @param invoice
* @throws PluginException
*/
public ZohoInvoice exportInvoice(ItemCollection invoice, String contactID, String accessToken)
public ZohoInvoice exportInvoice(ItemCollection invoice, String contactID, String accessToken, boolean debug)
throws PluginException {
logger.info("├── Zoho Export Invoice " + invoice.getItemValueString("invoice.number") + "...");
@ -225,9 +227,10 @@ public class ZohoAPIService {
String requestBody = jsonb.toJson(zohoInvoice);
// Print JSON to console for verification
logger.fine("├── Zoho Invoice JSON Request...");
logger.fine("│ ├── " + requestBody);
if (debug) {
logger.info("├── Zoho Invoice JSON Request...");
logger.info("│ ├── " + requestBody);
}
// https://www.zohoapis.com/books/v3/invoices?organization_id=10234695'
String uri = zohoOAuthManager.getBaseURI() + "invoices?organization_id="
+ zohoOAuthManager.getOrganization();
@ -241,9 +244,10 @@ public class ZohoAPIService {
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
// Print response details to console
logger.fine("├── Zoho Response:");
logger.fine("│ ├── Status Code: " + response.statusCode());
if (debug) {
logger.info("├── Zoho Response:");
logger.info("│ ├── Status Code: " + response.statusCode());
}
if (response.statusCode() >= 300) {
throw new PluginException(this.getClass().getName(), "Zoho API error",
"Failed to create invoice. Status code: " + response.statusCode() + ", Response: "
@ -252,13 +256,14 @@ public class ZohoAPIService {
// Erfolgreiche Erstellung
String bodyContent = response.body();
logger.fine("│ ├── Response Body: " + bodyContent);
if (debug) {
logger.info("│ ├── Response Body: " + bodyContent);
}
System.out.print(bodyContent);
ZohoInvoiceCreateResponse createResponse = jsonb.fromJson(bodyContent, ZohoInvoiceCreateResponse.class);
if (createResponse != null) {
logger.fine("│ ├── OK - customerID=" + createResponse.getInvoice().getCustomerId());
logger.info("│ ├── OK - customerID=" + createResponse.getInvoice().getCustomerId());
logger.info("│ ├── OK - invoiceID=" + createResponse.getInvoice().getInvoiceId());
zohoInvoice = createResponse.getInvoice();
@ -286,7 +291,7 @@ public class ZohoAPIService {
* @throws PluginException
*/
public ZohoCreditnote exportCreditnote(ItemCollection invoice, String contactID,
String accessToken) throws PluginException {
String accessToken, boolean debug) throws PluginException {
logger.info("├── Zoho Export creditnote " + invoice.getItemValueString("invoice.number") + "...");
String currencyID = lookupCurrency(invoice.getItemValueString("invoice.currency"), accessToken);
@ -298,7 +303,7 @@ public class ZohoAPIService {
ItemCollection invoiceRef = documentService.load(invoiceRefID);
if (invoiceRef != null) {
invoiceZoho = findInvoiceIDByInvoiceNumber(invoiceRef.getItemValueString("invoice.number"),
accessToken);
accessToken, debug);
logger.info("├── Zoho fond invoice zoho ref: " + invoiceZoho.getInvoiceId());
}
@ -309,8 +314,10 @@ public class ZohoAPIService {
String requestBody = jsonb.toJson(zohoCreditnote);
// Print JSON to console for verification
logger.fine("├── Zoho CreditNote JSON Request...");
logger.fine("│ ├── " + requestBody);
if (debug) {
logger.info("├── Zoho CreditNote JSON Request...");
logger.info("│ ├── " + requestBody);
}
// https://www.zohoapis.com/books/v3/creditnotes?organization_id=10234695'
String uri = zohoOAuthManager.getBaseURI() + "creditnotes?organization_id="
@ -329,8 +336,10 @@ public class ZohoAPIService {
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
// Print response details to console
logger.fine("├── Zoho Response:");
logger.fine("│ ├── Status Code: " + response.statusCode());
if (debug) {
logger.info("├── Zoho Response:");
logger.info("│ ├── Status Code: " + response.statusCode());
}
if (response.statusCode() >= 300) {
throw new PluginException(this.getClass().getName(), "Zoho API error",
@ -340,8 +349,9 @@ public class ZohoAPIService {
// Erfolgreiche Erstellung
String bodyContent = response.body();
logger.fine("│ ├── Response Body: " + bodyContent);
if (debug) {
logger.info("│ ├── Response Body: " + bodyContent);
}
System.out.print(bodyContent);
ZohoCreditnoteCreateResponse createResponse = jsonb.fromJson(bodyContent,
ZohoCreditnoteCreateResponse.class);
@ -373,7 +383,7 @@ public class ZohoAPIService {
* @throws PluginException
*/
public ZohoBill exportBill(ItemCollection invoice, String contactID, String accountID,
String accessToken) throws PluginException {
String accessToken, boolean debug) throws PluginException {
logger.info("├── Zoho Export Bill " + invoice.getItemValueString("invoice.number") + "...");
String currencyID = lookupCurrency(invoice.getItemValueString("invoice.currency"), accessToken);
@ -385,9 +395,10 @@ public class ZohoAPIService {
String requestBody = jsonb.toJson(zohoBill);
// Print JSON to console for verification
logger.fine("├── Zoho Bill JSON Request...");
logger.fine("│ ├── " + requestBody);
if (debug) {
logger.info("├── Zoho Bill JSON Request...");
logger.info("│ ├── " + requestBody);
}
// https://www.zohoapis.com/books/v3/expenses?organization_id=10234695'
String uri = zohoOAuthManager.getBaseURI() + "bills?organization_id=" + zohoOAuthManager.getOrganization();
logger.fine("│ ├── uri= " + uri);
@ -400,9 +411,10 @@ public class ZohoAPIService {
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
// Print response details to console
logger.fine("├── Zoho Response:");
logger.fine("│ ├── Status Code: " + response.statusCode());
if (debug) {
logger.info("├── Zoho Response:");
logger.info("│ ├── Status Code: " + response.statusCode());
}
if (response.statusCode() >= 300) {
throw new PluginException(this.getClass().getName(), "Zoho API error",
"Failed to create expense. Status code: " + response.statusCode() + ", Response: "
@ -411,8 +423,9 @@ public class ZohoAPIService {
// Erfolgreiche Erstellung
String bodyContent = response.body();
logger.fine("│ ├── Response Body: " + bodyContent);
if (debug) {
logger.info("│ ├── Response Body: " + bodyContent);
}
System.out.print(bodyContent);
ZohoBillCreateResponse createResponse = jsonb.fromJson(bodyContent, ZohoBillCreateResponse.class);
@ -443,7 +456,7 @@ public class ZohoAPIService {
* @throws PluginException
*/
public ZohoVendorCredit exportVendorCredit(ItemCollection invoice, String contactID, String accountID,
String accessToken) throws PluginException {
String accessToken, boolean debug) throws PluginException {
logger.info("├── Zoho Export VendorCredit " + invoice.getItemValueString("invoice.number") + "...");
String currencyID = lookupCurrency(invoice.getItemValueString("invoice.currency"), accessToken);
@ -456,9 +469,10 @@ public class ZohoAPIService {
String requestBody = jsonb.toJson(zohoVendorCredit);
// Print JSON to console for verification
logger.fine("├── Zoho VendorCredit JSON Request...");
logger.fine("│ ├── " + requestBody);
if (debug) {
logger.info("├── Zoho VendorCredit JSON Request...");
logger.info("│ ├── " + requestBody);
}
// https://www.zohoapis.com/books/v3/expenses?organization_id=10234695'
String uri = zohoOAuthManager.getBaseURI() + "vendorcredits?organization_id="
+ zohoOAuthManager.getOrganization();
@ -472,9 +486,10 @@ public class ZohoAPIService {
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
// Print response details to console
logger.fine("├── Zoho Response:");
logger.fine("│ ├── Status Code: " + response.statusCode());
if (debug) {
logger.info("├── Zoho Response:");
logger.info("│ ├── Status Code: " + response.statusCode());
}
if (response.statusCode() >= 300) {
throw new PluginException(this.getClass().getName(), "Zoho API error",
"Failed to create expense. Status code: " + response.statusCode() + ", Response: "
@ -483,8 +498,9 @@ public class ZohoAPIService {
// Erfolgreiche Erstellung
String bodyContent = response.body();
logger.fine("│ ├── Response Body: " + bodyContent);
if (debug) {
logger.info("│ ├── Response Body: " + bodyContent);
}
System.out.print(bodyContent);
ZohoVendorCreditCreateResponse createResponse = jsonb.fromJson(bodyContent,
ZohoVendorCreditCreateResponse.class);
@ -712,12 +728,12 @@ public class ZohoAPIService {
// List<ZohoInvoiceItem> 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);
// 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;
}
@ -906,8 +922,10 @@ public class ZohoAPIService {
String boundary = "----WebKitFormBoundary" + System.currentTimeMillis();
// Build multipart request body
// Ersetzt alle Punkte außer dem letzten (vor der Dateierweiterung)
String cleanFileName = file.getName().replaceAll("\\.(?=.*\\.)", "_");
byte[] header = ("--" + boundary + "\r\n"
+ "Content-Disposition: form-data; name=\"attachment\"; filename=\"" + file.getName() + "\"\r\n"
+ "Content-Disposition: form-data; name=\"attachment\"; filename=\"" + cleanFileName + "\"\r\n"
+ "Content-Type: " + file.getContentType() + "\r\n\r\n").getBytes(StandardCharsets.UTF_8);
byte[] footer = ("\r\n--" + boundary + "--\r\n").getBytes(StandardCharsets.UTF_8);

View file

@ -21,6 +21,16 @@ import jakarta.inject.Inject;
/**
* This adapter exports the invoice data to zoho
*
* To activate debug add the following event definition
*
* <pre>
* {@code
<zoho name="EXPORT">
<debug>true</debug>
</zoho>
* }
* </pre>
*
* @version 1.0
* @author rsoika
@ -31,6 +41,7 @@ public class ZohoExportAdapter implements SignalAdapter {
public static final String ENV_ZOHO_EXPORT_MAX_ATTACHMENT_SIZE = "zoho.export.maxattachmentsize";
private static Logger logger = Logger.getLogger(ZohoExportAdapter.class.getName());
private boolean debug = false;
@Inject
ZohoAPIService zohoAPIService;
@ -55,7 +66,19 @@ public class ZohoExportAdapter implements SignalAdapter {
@Override
public ItemCollection execute(ItemCollection document, ItemCollection event)
throws AdapterException {
// read debug flag
try {
List<ItemCollection> zohoDefinitions = workflowService.evalWorkflowResultXML(event, "zoho",
"EXPORT", document, false);
if (zohoDefinitions != null && zohoDefinitions.size() > 0) {
ItemCollection zohoDefinition = zohoDefinitions.get(0);
debug = zohoDefinition.getItemValueBoolean("debug");
}
} catch (PluginException ee) {
// no op
}
try {
// get access token
String accessToken = zohoOAuthManager.getValidAccessToken();
@ -88,7 +111,8 @@ public class ZohoExportAdapter implements SignalAdapter {
* @param accessToken
* @throws PluginException
*/
private void exportRechnungsausgang(ItemCollection document, String accessToken) throws PluginException {
private void exportRechnungsausgang(ItemCollection document, String accessToken)
throws PluginException {
// find currencyID
String currencyID = zohoAPIService.lookupCurrency(document.getItemValueString("invoice.currency"), accessToken);
@ -102,7 +126,7 @@ public class ZohoExportAdapter implements SignalAdapter {
if (document.getItemValueDouble("invoice.total") < 0) {
// create credit note!
logger.info("├── CreditNote Export");
ZohoCreditnote zohoCreditnote = zohoAPIService.exportCreditnote(document, contactID, accessToken);
ZohoCreditnote zohoCreditnote = zohoAPIService.exportCreditnote(document, contactID, accessToken, debug);
// Attache Files....
List<FileData> files = null;
@ -120,7 +144,7 @@ public class ZohoExportAdapter implements SignalAdapter {
}
} else {
logger.info("├── Invoice Export");
ZohoInvoice zohoInvoice = zohoAPIService.exportInvoice(document, contactID, accessToken);
ZohoInvoice zohoInvoice = zohoAPIService.exportInvoice(document, contactID, accessToken, debug);
// Attache Files....
List<FileData> files = null;
@ -147,7 +171,8 @@ public class ZohoExportAdapter implements SignalAdapter {
* @param accessToken
* @throws PluginException
*/
private void exportRechnungseingang(ItemCollection document, String accessToken) throws PluginException {
private void exportRechnungseingang(ItemCollection document, String accessToken)
throws PluginException {
String currencyID = zohoAPIService.lookupCurrency(document.getItemValueString("invoice.currency"), accessToken);
@ -162,7 +187,7 @@ public class ZohoExportAdapter implements SignalAdapter {
// Gutschrift
logger.info("├── Vendor Credit Export");
ZohoVendorCredit zohoVendorCredit = zohoAPIService.exportVendorCredit(document, contactID, accountID,
accessToken);
accessToken, debug);
// Attache Files....
List<FileData> files = null;
@ -180,7 +205,7 @@ public class ZohoExportAdapter implements SignalAdapter {
}
} else {
logger.info("├── Bill Export");
ZohoBill zohoBill = zohoAPIService.exportBill(document, contactID, accountID, accessToken);
ZohoBill zohoBill = zohoAPIService.exportBill(document, contactID, accountID, accessToken, debug);
// Attache Files....
List<FileData> files = null;

View file

@ -0,0 +1,71 @@
{
"creditnote_number": "494",
"currency_id": "6273937000000091141",
"customer_id": "6273937000000192143",
"date": "2025-07-30",
"exchange_rate": 0.0,
"line_items": [
{
"description": "Air Freight (Door to Airport)",
"name": "IM-DWC-2507-003",
"quantity": 1,
"rate": 3936.0
},
{
"description": "DAP Costs",
"name": "IM-DWC-2507-003",
"quantity": 1,
"rate": 3800.0
},
{
"description": "Trip cancellation, Truck waiting charges",
"name": "IM-DWC-2507-003",
"quantity": 1,
"rate": 1000.0
},
{
"description": "Transport (DIP - Dubai Hills)",
"name": "IM-DWC-2507-003",
"quantity": 1,
"rate": 550.0
},
{
"description": "Empty Box disposal",
"name": "IM-DWC-2507-003",
"quantity": 1,
"rate": 250.0
},
{
"description": "Warehouse Handling - IN",
"name": "IM-DWC-2507-003",
"quantity": 1,
"rate": 250.0
},
{
"description": "Warehouse Handling - OUT",
"name": "IM-DWC-2507-003",
"quantity": 1,
"rate": 250.0
},
{
"description": "Storage Fee",
"name": "IM-DWC-2507-003",
"quantity": 1,
"rate": 175.0
},
{
"description": "Customs Duty",
"name": "IM-DWC-2507-003",
"quantity": 1,
"rate": 18976.0
},
{
"description": "",
"name": "IM-DWC-2507-003",
"quantity": 1,
"rate": -43277.05
}
],
"place_of_supply": "DU",
"total": 43277.05
}

View file

@ -1234,6 +1234,11 @@ Wiedervorlage wird um 7 bzw. 5 Tage erhöht</bpmn2:text>
<imixs:value><![CDATA[{space:?:member}]]></imixs:value>
</imixs:item>
<imixs:item name="keyaddreadfields" type="xs:string"/>
<imixs:item name="txtactivityresult" type="xs:string">
<imixs:value><![CDATA[<zoho name="EXPORT">
<debug>false</debug>
</zoho>]]></imixs:value>
</imixs:item>
</bpmn2:extensionElements>
<bpmn2:documentation id="documentation_oH5GRw"/>
<bpmn2:dataOutput id="dataOutput_k0h11Q" name="Signal_2_Output"/>
@ -2140,7 +2145,7 @@ result.isValid=true;
<imixs:value><![CDATA[2]]></imixs:value>
</imixs:item>
<imixs:item name="keypublicresult" type="xs:string">
<imixs:value><![CDATA[0]]></imixs:value>
<imixs:value><![CDATA[1]]></imixs:value>
</imixs:item>
<imixs:item name="keylogdateformat" type="xs:string">
<imixs:value><![CDATA[2]]></imixs:value>
@ -2162,11 +2167,19 @@ result.isValid=true;
<imixs:value><![CDATA[0]]></imixs:value>
</imixs:item>
<imixs:item name="rtfresultlog" type="xs:string">
<imixs:value><![CDATA[Retry Zoho Export]]></imixs:value>
<imixs:value><![CDATA[Skip Zoho Export]]></imixs:value>
</imixs:item>
<imixs:item name="keyupdateacl" type="xs:boolean">
<imixs:value>false</imixs:value>
</imixs:item>
<imixs:item name="txtbusinessrule" type="xs:string">
<imixs:value><![CDATA[var result={};
result.isValid=true;
if (workitem.getItemValueString('txtcomment')==='') {
result.isValid=false;
result.errorMessage='Please enter a commment.';
}]]></imixs:value>
</imixs:item>
</bpmn2:extensionElements>
<bpmn2:documentation id="documentation_nyLjtg"><![CDATA[Zoho export skipped ]]></bpmn2:documentation>
<bpmn2:incoming>sequenceFlow_ZTMZUg</bpmn2:incoming>
@ -2785,8 +2798,8 @@ result.isValid=true;
<di:waypoint x="1067.0" y="595.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_R5jeGg" id="BPMNEdge_3PxrMA" sourceElement="BPMNShape_UoB4Vw" targetElement="BPMNShape_ExclusiveGateway_1">
<di:waypoint x="1103.0" y="596.0"/>
<di:waypoint x="1142.0" y="596.0"/>
<di:waypoint x="1103.0" y="595.0"/>
<di:waypoint x="1142.0" y="595.0"/>
<di:waypoint x="1142.0" y="317.0"/>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>

View file

@ -1551,14 +1551,12 @@ result.isValid=true;
<imixs:item name="keyupdateacl" type="xs:boolean">
<imixs:value>false</imixs:value>
</imixs:item>
<imixs:item name="txtbusinessrule" type="CDATA">
<imixs:item name="txtbusinessrule" type="xs:string">
<imixs:value><![CDATA[var result={};
var refField="txtcomment";
result.isValid=true;
if ( ( workitem.get(refField) == null || ''==workitem.get(refField)[0]) ) {
if (workitem.getItemValueString('txtcomment')==='') {
result.isValid=false;
result.errorMessage='Bitte geben Sie einen Kommentar ein.';
result.errorMessage='Please enter a commment.';
}]]></imixs:value>
</imixs:item>
</bpmn2:extensionElements>
@ -3034,14 +3032,12 @@ result.isValid=true;
<imixs:item name="keyupdateacl" type="xs:boolean">
<imixs:value>false</imixs:value>
</imixs:item>
<imixs:item name="txtbusinessrule" type="CDATA">
<imixs:item name="txtbusinessrule" type="xs:string">
<imixs:value><![CDATA[var result={};
var refField="txtcomment";
result.isValid=true;
if ( ( workitem.get(refField) == null || ''==workitem.get(refField)[0]) ) {
if (workitem.getItemValueString('txtcomment')==='') {
result.isValid=false;
result.errorMessage='Bitte geben Sie einen Kommentar ein.';
result.errorMessage='Please enter a commment.';
}]]></imixs:value>
</imixs:item>
</bpmn2:extensionElements>