zoho invoice export

This commit is contained in:
Ralph Soika 2025-04-30 11:25:08 +02:00
parent 72a10ddcf5
commit f30cc8e8fe
7 changed files with 388 additions and 98 deletions

View file

@ -0,0 +1,177 @@
{
"code": 0,
"message": "The contact has been added.",
"contact": {
"contact_id": "777249000000077029",
"contact_name": "HQ Recyclers Network e.K.",
"company_name": "HQ Recyclers Network e.K.",
"contact_number": "CUS-00008",
"contact_tax_information": "",
"first_name": "",
"last_name": "",
"designation": "",
"department": "",
"website": "",
"is_bcy_only_contact": true,
"is_credit_limit_migration_completed": true,
"language_code": "",
"language_code_formatted": "",
"contact_salutation": "",
"email": "",
"phone": "",
"mobile": "",
"invited_by": "",
"portal_status": "disabled",
"is_client_review_asked": false,
"has_transaction": false,
"contact_type": "customer",
"customer_sub_type": "business",
"owner_id": "",
"owner_name": "",
"source": "api",
"documents": [],
"twitter": "",
"facebook": "",
"is_crm_customer": false,
"is_linked_with_zohocrm": false,
"primary_contact_id": "",
"zcrm_account_id": "",
"zcrm_contact_id": "",
"crm_owner_id": "",
"payment_terms": 0,
"payment_terms_label": "Due on Receipt",
"payment_terms_id": "",
"credit_limit_exceeded_amount": 0.00,
"currency_id": "777249000000000071",
"currency_code": "EUR",
"currency_symbol": "€",
"price_precision": 2,
"exchange_rate": "",
"can_show_customer_ob": true,
"can_show_vendor_ob": true,
"opening_balance_amount": 0.00,
"opening_balance_amount_bcy": "",
"outstanding_ob_receivable_amount": 0.00,
"outstanding_ob_payable_amount": 0.00,
"outstanding_receivable_amount": 0.00,
"outstanding_receivable_amount_bcy": 0.00,
"outstanding_payable_amount": 0.00,
"outstanding_payable_amount_bcy": 0.00,
"unused_credits_receivable_amount": 0.00,
"unused_credits_receivable_amount_bcy": 0.00,
"unused_credits_payable_amount": 0.00,
"unused_credits_payable_amount_bcy": 0.00,
"unused_retainer_payments": 0.00,
"status": "active",
"payment_reminder_enabled": true,
"is_sms_enabled": true,
"is_consent_agreed": false,
"consent_date": "",
"is_client_review_settings_enabled": false,
"custom_fields": [],
"custom_field_hash": {},
"tax_reg_label": "",
"contact_category": "",
"sales_channel": "direct_sales",
"ach_supported": false,
"portal_receipt_count": 0,
"opening_balances": [],
"entity_address_id": "777249000000077035",
"billing_address": {
"address_id": "777249000000077031",
"attention": "",
"address": "",
"street2": "",
"city": "",
"state_code": "",
"state": "",
"zip": "",
"country": "",
"county": "",
"country_code": "",
"phone": "",
"fax": ""
},
"shipping_address": {
"address_id": "777249000000077033",
"attention": "",
"address": "",
"street2": "",
"city": "",
"state_code": "",
"state": "",
"zip": "",
"country": "",
"county": "",
"country_code": "",
"phone": "",
"fax": "",
"latitude": "",
"longitude": ""
},
"contact_persons": [],
"addresses": [],
"pricebook_id": "",
"pricebook_name": "",
"default_templates": {
"statement_template_id": "",
"statement_template_name": "",
"invoice_template_id": "",
"invoice_template_name": "",
"bill_template_id": "",
"bill_template_name": "",
"estimate_template_id": "",
"estimate_template_name": "",
"creditnote_template_id": "",
"creditnote_template_name": "",
"paymentthankyou_template_id": "",
"paymentthankyou_template_name": "",
"invoice_email_template_id": "",
"invoice_email_template_name": "",
"estimate_email_template_id": "",
"estimate_email_template_name": "",
"creditnote_email_template_id": "",
"creditnote_email_template_name": "",
"paymentthankyou_email_template_id": "",
"paymentthankyou_email_template_name": "",
"payment_remittance_email_template_id": "",
"payment_remittance_email_template_name": ""
},
"associated_with_square": false,
"cards": [],
"checks": [],
"bank_accounts": [],
"vpa_list": [],
"notes": "",
"created_time": "2025-04-30T11:14:42+0200",
"created_date": "2025-04-30",
"created_by_name": "Ralph Soika",
"last_modified_time": "2025-04-30T11:14:42+0200",
"tags": [],
"zohopeople_client_id": "",
"customer_currency_summaries": [
{
"currency_id": "777249000000000071",
"currency_code": "EUR",
"currency_symbol": "€",
"price_precision": 2,
"is_base_currency": true,
"currency_name_formatted": "EUR- Euro",
"outstanding_receivable_amount": 0.00,
"unused_credits_receivable_amount": 0.00
}
],
"is_valid_trn": false,
"trn_last_validated_time": "",
"trn_last_validated_time_formatted": "",
"approvers_list": [],
"submitted_date": "",
"submitted_by": "",
"submitted_by_name": "",
"submitted_by_email": "",
"submitted_by_photo_url": "",
"submitter_id": "",
"approver_id": "",
"integration_references": []
}
}

View file

@ -1,5 +1,6 @@
package com.alexanderlogistics.zoho; package com.alexanderlogistics.zoho;
import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.net.http.HttpClient; import java.net.http.HttpClient;
@ -17,7 +18,8 @@ import org.imixs.workflow.exceptions.PluginException;
import com.alexanderlogistics.InvoiceUtil; import com.alexanderlogistics.InvoiceUtil;
import com.alexanderlogistics.zoho.dto.ZohoContact; import com.alexanderlogistics.zoho.dto.ZohoContact;
import com.alexanderlogistics.zoho.dto.ZohoContactsResponse; import com.alexanderlogistics.zoho.dto.ZohoContactCreateResponse;
import com.alexanderlogistics.zoho.dto.ZohoContactFindResponse;
import com.alexanderlogistics.zoho.dto.ZohoInvoice; import com.alexanderlogistics.zoho.dto.ZohoInvoice;
import com.alexanderlogistics.zoho.dto.ZohoInvoiceItem; import com.alexanderlogistics.zoho.dto.ZohoInvoiceItem;
@ -67,13 +69,13 @@ public class ZohoAPIService {
public String findContactIDByCompanyName(String companyName, String accessToken) throws PluginException { public String findContactIDByCompanyName(String companyName, String accessToken) throws PluginException {
// Print JSON to console for verification // Print JSON to console for verification
logger.info("├── Zoho getcontact Request..."); logger.info("├── Zoho lookup contact '" + companyName + "'...");
try { try {
// https://www.zohoapis.com/books/v3/contacts?organization_id=10234695&company_name=xxx' // https://www.zohoapis.com/books/v3/contacts?organization_id=10234695&company_name=xxx'
String uri = ZOHO_API_BASE_URL + "contacts?organization_id=" String uri = ZOHO_API_BASE_URL + "contacts?organization_id="
+ zohoOAuthManager.getOrganization() + "&company_name=" + zohoOAuthManager.getOrganization() + "&company_name="
+ URLEncoder.encode(companyName, StandardCharsets.UTF_8); + URLEncoder.encode(companyName, StandardCharsets.UTF_8);
logger.info("│ ├── uri= " + uri); logger.fine("│ ├── uri= " + uri);
logger.fine("│ ├── accessToken=" + accessToken); logger.fine("│ ├── accessToken=" + accessToken);
HttpRequest request = HttpRequest.newBuilder() HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(uri)) .uri(URI.create(uri))
@ -86,23 +88,23 @@ public class ZohoAPIService {
request, HttpResponse.BodyHandlers.ofString()); request, HttpResponse.BodyHandlers.ofString());
// Print response details to console // Print response details to console
logger.info("├── Zoho Response:"); logger.fine("├── Zoho Response:");
logger.info("│ ├── Status Code: " + response.statusCode()); logger.fine("│ ├── Status Code: " + response.statusCode());
logger.info("│ ├── Response Body: " + response.body()); logger.fine("│ ├── Response Body: " + response.body());
if (response.statusCode() >= 300) { if (response.statusCode() >= 300) {
throw new PluginException(this.getClass().getName(), "Zoho API error", throw new PluginException(this.getClass().getName(), "Zoho API error",
"Failed to find Contacts - Status code: " + response.statusCode() + "Failed to find Contacts - Status code: " + response.statusCode() +
", Response: " + response.body()); ", Response: " + response.body());
} }
ZohoContactsResponse contactsResponse = jsonb.fromJson(response.body(), ZohoContactFindResponse contactsResponse = jsonb.fromJson(response.body(),
ZohoContactsResponse.class); ZohoContactFindResponse.class);
if (contactsResponse != null && contactsResponse.getContacts() != null) { if (contactsResponse != null && contactsResponse.getContacts() != null) {
// Durchsuche die Ergebnisse nach exakter Übereinstimmung // Durchsuche die Ergebnisse nach exakter Übereinstimmung
for (ZohoContact contact : contactsResponse.getContacts()) { for (ZohoContact contact : contactsResponse.getContacts()) {
if (companyName.equalsIgnoreCase(contact.getCompanyName())) { if (companyName.equalsIgnoreCase(contact.getCompanyName())) {
logger.info("│ ├── contactID= " + contact.getContactId());
return contact.getContactId(); return contact.getContactId();
} }
} }
@ -125,21 +127,31 @@ public class ZohoAPIService {
*/ */
public void exportInvoice(ItemCollection invoice) throws PluginException { public void exportInvoice(ItemCollection invoice) throws PluginException {
String accessToken = zohoOAuthManager.getValidAccessToken(); String accessToken = zohoOAuthManager.getValidAccessToken();
logger.info("├── Zoho Export Invoice " + invoice.getItemValueString("invoice.number") + "...");
// first lookup contact!
String contactID = "";
contactID = findContactIDByCompanyName(invoice.getItemValueString("dbtr.name"), accessToken);
if (contactID == null) {
logger.info("│ ├── contact not found");
ZohoContact contact = createContact(invoice.getItemValueString("dbtr.name"), accessToken);
contactID = contact.getContactId();
}
// Konvertiere ItemCollection zu ZohoInvoiceDTO // Konvertiere ItemCollection zu ZohoInvoiceDTO
ZohoInvoice zohoInvoice = convertToZohoInvoice(invoice, accessToken); ZohoInvoice zohoInvoice = convertToZohoInvoice(invoice, contactID, accessToken);
try { try {
String requestBody = jsonb.toJson(zohoInvoice); String requestBody = jsonb.toJson(zohoInvoice);
// Print JSON to console for verification // Print JSON to console for verification
logger.info("├── Zoho Invoice JSON Request..."); logger.fine("├── Zoho Invoice JSON Request...");
logger.info("│ ├── " + requestBody); logger.fine("│ ├── " + requestBody);
// https://www.zohoapis.com/books/v3/invoices?organization_id=10234695' // https://www.zohoapis.com/books/v3/invoices?organization_id=10234695'
String uri = ZOHO_API_BASE_URL + "invoices?organization_id=" String uri = ZOHO_API_BASE_URL + "invoices?organization_id="
+ zohoOAuthManager.getOrganization(); + zohoOAuthManager.getOrganization();
logger.info("│ ├── uri= " + uri); logger.fine("│ ├── uri= " + uri);
logger.fine("│ ├── accessToken=" + accessToken); logger.fine("│ ├── accessToken=" + accessToken);
HttpRequest request = HttpRequest.newBuilder() HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(uri)) .uri(URI.create(uri))
@ -152,9 +164,9 @@ public class ZohoAPIService {
request, HttpResponse.BodyHandlers.ofString()); request, HttpResponse.BodyHandlers.ofString());
// Print response details to console // Print response details to console
logger.info("├── Zoho Response:"); logger.fine("├── Zoho Response:");
logger.info("│ ├── Status Code: " + response.statusCode()); logger.fine("│ ├── Status Code: " + response.statusCode());
logger.info("│ ├── Response Body: " + response.body()); logger.fine("│ ├── Response Body: " + response.body());
if (response.statusCode() >= 300) { if (response.statusCode() >= 300) {
throw new PluginException(this.getClass().getName(), "Zoho API error", throw new PluginException(this.getClass().getName(), "Zoho API error",
@ -164,10 +176,10 @@ public class ZohoAPIService {
// Erfolgreiche Verarbeitung hier // Erfolgreiche Verarbeitung hier
// Optional: Response auswerten // Optional: Response auswerten
logger.info("│ ├── Invoice Export successful.");
} catch (Exception e) { } catch (IOException | InterruptedException e) {
throw new PluginException(this.getClass().getName(), "Zoho API error", throw new PluginException(this.getClass().getName(), "Zoho API error",
"Failed to export invoice to Zoho", e); "Failed to export invoice to Zoho: " + e.getMessage(), e);
} }
} }
@ -179,7 +191,7 @@ public class ZohoAPIService {
* @throws PluginException Wenn der API-Aufruf fehlschlägt * @throws PluginException Wenn der API-Aufruf fehlschlägt
*/ */
public ZohoContact createContact(String companyName, String accessToken) throws PluginException { public ZohoContact createContact(String companyName, String accessToken) throws PluginException {
logger.info("├── Zoho create contact '" + companyName + "'...");
try { try {
// Contact-DTO vorbereiten // Contact-DTO vorbereiten
ZohoContact newContact = new ZohoContact(); ZohoContact newContact = new ZohoContact();
@ -188,10 +200,7 @@ public class ZohoAPIService {
// JSON serialisieren // JSON serialisieren
String requestBody = jsonb.toJson(newContact); String requestBody = jsonb.toJson(newContact);
logger.fine("│ ├── " + requestBody);
logger.info("├── Zoho Contact Creation...");
logger.info("│ ├── " + requestBody);
String uri = ZOHO_API_BASE_URL + "contacts?organization_id=" String uri = ZOHO_API_BASE_URL + "contacts?organization_id="
+ zohoOAuthManager.getOrganization(); + zohoOAuthManager.getOrganization();
@ -204,61 +213,44 @@ public class ZohoAPIService {
HttpResponse<String> response = httpClient.send( HttpResponse<String> response = httpClient.send(
request, HttpResponse.BodyHandlers.ofString()); request, HttpResponse.BodyHandlers.ofString());
logger.fine("├── Zoho Response:");
logger.info("├── Zoho Response:"); logger.fine("│ ├── Status Code: " + response.statusCode());
logger.info("│ ├── Status Code: " + response.statusCode()); logger.fine("│ ├── Response Body: " + response.body());
logger.info("│ ├── Response Body: " + response.body()); if (response.statusCode() >= 300) {
if (response.statusCode() == 201) {
// Erfolgreiche Erstellung
ZohoContact createResponse = jsonb.fromJson(response.body(), ZohoContact.class);
if (createResponse != null) {
return createResponse;
}
}
throw new PluginException(this.getClass().getName(), "Zoho API error", throw new PluginException(this.getClass().getName(), "Zoho API error",
"Failed to create contact. Status code: " + response.statusCode() + "Failed to create invoice. Status code: " + response.statusCode() +
", Response: " + response.body()); ", Response: " + response.body());
}
} catch (Exception e) { // Erfolgreiche Erstellung
ZohoContactCreateResponse createResponse = jsonb.fromJson(response.body(), ZohoContactCreateResponse.class);
if (createResponse != null) {
logger.info("│ ├── OK - contactID=" + createResponse.getContact().getContactId());
return createResponse.getContact();
} else {
throw new PluginException(this.getClass().getName(), "Zoho API error", throw new PluginException(this.getClass().getName(), "Zoho API error",
"Failed to create contact", e); "Response could not be resolved!");
}
} }
private ZohoInvoice convertToZohoInvoice(ItemCollection invoice, String accessToken) throws PluginException { } catch (IOException | InterruptedException e) {
throw new PluginException(this.getClass().getName(), "Zoho API error",
"Failed to create contact: " + e.getMessage(), e);
}
// teste ob wir einen contact finden
String contactID = "";
try {
contactID = findContactIDByCompanyName(invoice.getItemValueString("dbtr.name"), accessToken);
if (contactID == null) {
ZohoContact contact = createContact(invoice.getItemValueString("dbtr.name"), accessToken);
contactID = contact.getContactId();
}
} catch (PluginException ef) {
logger.severe("Failed to find Contact by company name: " + ef.getMessage());
} }
private ZohoInvoice convertToZohoInvoice(ItemCollection invoice, String contactID, String accessToken)
throws PluginException {
ZohoInvoice zohoInvoice = new ZohoInvoice(); ZohoInvoice zohoInvoice = new ZohoInvoice();
// Basisinformationen // Basisinformationen
// zohoInvoice.setInvoiceNumber(invoice.getItemValueString("invoice.number")); logger.fine("invoice.date=" + invoice.getItemValueDate("invoice.date"));
logger.fine("invoice.duedate=" + invoice.getItemValueDate("invoice.duedate"));
logger.info("invoice.date=" + invoice.getItemValueDate("invoice.date"));
logger.info("invoice.duedate=" + invoice.getItemValueDate("invoice.duedate"));
zohoInvoice.setDate(invoice.getItemValueDate("invoice.date")); zohoInvoice.setDate(invoice.getItemValueDate("invoice.date"));
zohoInvoice.setDueDate(invoice.getItemValueDate("invoice.duedate")); zohoInvoice.setDueDate(invoice.getItemValueDate("invoice.duedate"));
zohoInvoice.setTotal(invoice.getItemValueDouble("invoice.total")); zohoInvoice.setTotal(invoice.getItemValueDouble("invoice.total"));
// Kunden-ID (muss angepasst werden je nachdem wie du sie speicherst) // Kunden-ID (muss angepasst werden je nachdem wie du sie speicherst)
zohoInvoice.setCustomerId(contactID); zohoInvoice.setCustomerId(contactID);
// zohoInvoice.setCustomerId("777249000000055569");
// Positionen (muss angepasst werden je nach Datenstruktur)
// List<ZohoInvoiceItemDTO> lineItems = new ArrayList<>();
List<ItemCollection> positionen = InvoiceUtil.explodeChildList(invoice); List<ItemCollection> positionen = InvoiceUtil.explodeChildList(invoice);
List<ZohoInvoiceItem> lineItems = positionen.stream() List<ZohoInvoiceItem> lineItems = positionen.stream()
@ -274,7 +266,6 @@ public class ZohoAPIService {
.collect(Collectors.toList()); .collect(Collectors.toList());
zohoInvoice.setLineItems(lineItems); zohoInvoice.setLineItems(lineItems);
return zohoInvoice; return zohoInvoice;
} }
} }

View file

@ -50,8 +50,7 @@ public class ZohoExportAdapter implements SignalAdapter {
@Override @Override
public ItemCollection execute(ItemCollection document, ItemCollection event) public ItemCollection execute(ItemCollection document, ItemCollection event)
throws AdapterException, PluginException { throws AdapterException, PluginException {
logger.info("├── Invoice Export");
logger.info("......starting export...");
// read the cargosoft export options // read the cargosoft export options
ItemCollection evalItemCollection = workflowService.evalWorkflowResult(event, "zoho", document, false); ItemCollection evalItemCollection = workflowService.evalWorkflowResult(event, "zoho", document, false);

View file

@ -63,7 +63,7 @@ public class ZohoOAuthManager {
*/ */
@Lock(LockType.READ) @Lock(LockType.READ)
public String getValidAccessToken() throws PluginException { public String getValidAccessToken() throws PluginException {
logger.info("├── get Zoho AccessTokens"); logger.fine("├── get Zoho AccessTokens");
ItemCollection zohoConfig = configService.loadConfiguration(ZohoController.ZOHO_CONFIGURATION, false); ItemCollection zohoConfig = configService.loadConfiguration(ZohoController.ZOHO_CONFIGURATION, false);
String accessToken = zohoConfig.getItemValueString("zoho.accessToken"); String accessToken = zohoConfig.getItemValueString("zoho.accessToken");
logger.fine("│ ├── accessToken=" + accessToken); logger.fine("│ ├── accessToken=" + accessToken);
@ -72,7 +72,7 @@ public class ZohoOAuthManager {
Date expireDate = zohoConfig.getItemValueDate("zoho.expires"); Date expireDate = zohoConfig.getItemValueDate("zoho.expires");
if (expireDate.getTime() < System.currentTimeMillis()) { if (expireDate.getTime() < System.currentTimeMillis()) {
logger.info("│ ├── Access Token expired (" + expireDate + ")"); logger.fine("│ ├── Access Token expired (" + expireDate + ")");
accessToken = refreshAccessToken(); accessToken = refreshAccessToken();
} }
@ -114,7 +114,7 @@ public class ZohoOAuthManager {
"&redirect_uri=" + URLEncoder.encode(redirectURI, StandardCharsets.UTF_8) + "&redirect_uri=" + URLEncoder.encode(redirectURI, StandardCharsets.UTF_8) +
"&grant_type=authorization_code"; "&grant_type=authorization_code";
logger.info("│ ├── Request URI=" + requestUrl); logger.fine("│ ├── Request URI=" + requestUrl);
HttpRequest request = HttpRequest.newBuilder() HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(requestUrl)) .uri(URI.create(requestUrl))
@ -126,10 +126,10 @@ public class ZohoOAuthManager {
response = httpClient.send(request, response = httpClient.send(request,
HttpResponse.BodyHandlers.ofString()); HttpResponse.BodyHandlers.ofString());
logger.info("│ ├── Response statusCode=" + response.statusCode()); logger.fine("│ ├── Response statusCode=" + response.statusCode());
if (response.statusCode() == 200) { if (response.statusCode() == 200) {
// 1. Response-Logging für Debugging // 1. Response-Logging für Debugging
logger.info("│ ├── Response Body=" + response.body()); logger.fine("│ ├── Response Body=" + response.body());
if (response.body().contains("error")) { if (response.body().contains("error")) {
throw new PluginException(ZohoOAuthManager.class.getSimpleName(), throw new PluginException(ZohoOAuthManager.class.getSimpleName(),
@ -176,7 +176,7 @@ public class ZohoOAuthManager {
+ +
"&grant_type=refresh_token"; "&grant_type=refresh_token";
logger.info("│ ├── Request URI=" + requestUrl); logger.fine("│ ├── Request URI=" + requestUrl);
HttpRequest request = HttpRequest.newBuilder() HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(requestUrl)) .uri(URI.create(requestUrl))
@ -189,11 +189,11 @@ public class ZohoOAuthManager {
response = httpClient.send(request, response = httpClient.send(request,
HttpResponse.BodyHandlers.ofString()); HttpResponse.BodyHandlers.ofString());
logger.info("│ ├── Response statusCode=" + response.statusCode()); logger.fine("│ ├── Response statusCode=" + response.statusCode());
if (response.statusCode() == 200) { if (response.statusCode() == 200) {
// 1. Response-Logging für Debugging // 1. Response-Logging für Debugging
logger.info("│ ├── Response Body=" + response.body()); logger.fine("│ ├── Response Body=" + response.body());
TokenResponse tokenResponse = jsonb.fromJson(response.body(), TokenResponse tokenResponse = jsonb.fromJson(response.body(),
TokenResponse.class); TokenResponse.class);
@ -221,33 +221,33 @@ public class ZohoOAuthManager {
String apiDomain = tokenResponse.getApi_domain(); String apiDomain = tokenResponse.getApi_domain();
if (apiDomain != null && !apiDomain.isEmpty()) { if (apiDomain != null && !apiDomain.isEmpty()) {
logger.info("│ ├── apiDomain=" + apiDomain); logger.fine("│ ├── apiDomain=" + apiDomain);
zohoConfig.setItemValue("zoho.apiDomain", apiDomain); zohoConfig.setItemValue("zoho.apiDomain", apiDomain);
} }
String scope = tokenResponse.getScope(); String scope = tokenResponse.getScope();
if (scope != null && !scope.isEmpty()) { if (scope != null && !scope.isEmpty()) {
logger.info("│ ├── scope=" + scope); logger.fine("│ ├── scope=" + scope);
zohoConfig.setItemValue("zoho.scope", scope); zohoConfig.setItemValue("zoho.scope", scope);
} }
String accessToken = tokenResponse.getAccess_token(); String accessToken = tokenResponse.getAccess_token();
if (accessToken != null && !accessToken.isEmpty()) { if (accessToken != null && !accessToken.isEmpty()) {
logger.info("│ ├── accessToken=" + accessToken); logger.fine("│ ├── accessToken=" + accessToken);
zohoConfig.setItemValue("zoho.accessToken", accessToken); zohoConfig.setItemValue("zoho.accessToken", accessToken);
} }
String refreshToken = tokenResponse.getRefresh_token(); String refreshToken = tokenResponse.getRefresh_token();
if (refreshToken != null && !refreshToken.isEmpty()) { if (refreshToken != null && !refreshToken.isEmpty()) {
logger.info("│ ├── refreshToken=" + refreshToken); logger.fine("│ ├── refreshToken=" + refreshToken);
zohoConfig.setItemValue("zoho.refreshToken", refreshToken); zohoConfig.setItemValue("zoho.refreshToken", refreshToken);
} }
logger.info("│ ├── expires_in=" + tokenResponse.getExpires_in()); logger.fine("│ ├── expires_in=" + tokenResponse.getExpires_in());
// Expires-Zeit in Millisekunden berechnen // Expires-Zeit in Millisekunden berechnen
long expiresInSeconds = tokenResponse.getExpires_in() - 60; long expiresInSeconds = tokenResponse.getExpires_in() - 60;
long currentTimeMillis = System.currentTimeMillis(); long currentTimeMillis = System.currentTimeMillis();
long expiresTimeMillis = currentTimeMillis + (expiresInSeconds * 1000); long expiresTimeMillis = currentTimeMillis + (expiresInSeconds * 1000);
// Expires-Zeit in ein Date-Objekt konvertieren // Expires-Zeit in ein Date-Objekt konvertieren
Date expiresDate = new Date(expiresTimeMillis); Date expiresDate = new Date(expiresTimeMillis);
logger.info("│ ├── expire date=" + expiresDate); logger.fine("│ ├── expire date=" + expiresDate);
zohoConfig.setItemValue("zoho.expires", expiresDate); zohoConfig.setItemValue("zoho.expires", expiresDate);
zohoConfig.setItemValue("message", "Access Token OK"); zohoConfig.setItemValue("message", "Access Token OK");
configService.save(zohoConfig); configService.save(zohoConfig);

View file

@ -0,0 +1,39 @@
package com.alexanderlogistics.zoho.dto;
import jakarta.json.bind.annotation.JsonbProperty;
public class ZohoContactCreateResponse {
@JsonbProperty("code")
private int code;
@JsonbProperty("message")
private String message;
@JsonbProperty("contact")
private ZohoContact contact;
// 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 ZohoContact getContact() {
return contact;
}
public void setContact(ZohoContact contact) {
this.contact = contact;
}
}

View file

@ -4,7 +4,7 @@ import java.util.List;
import jakarta.json.bind.annotation.JsonbProperty; import jakarta.json.bind.annotation.JsonbProperty;
public class ZohoContactsResponse { public class ZohoContactFindResponse {
@JsonbProperty("code") @JsonbProperty("code")
private int code; private int code;

View file

@ -131,6 +131,7 @@ th { font-weight: bold;}
<bpmn2:flowNodeRef>event_0zodvw</bpmn2:flowNodeRef> <bpmn2:flowNodeRef>event_0zodvw</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>textAnnotation_QBZ00w</bpmn2:flowNodeRef> <bpmn2:flowNodeRef>textAnnotation_QBZ00w</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>event_iUSXSg</bpmn2:flowNodeRef> <bpmn2:flowNodeRef>event_iUSXSg</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>event_07fo0g</bpmn2:flowNodeRef>
</bpmn2:lane> </bpmn2:lane>
<bpmn2:lane id="Lane_6" name="Bereich Team"> <bpmn2:lane id="Lane_6" name="Bereich Team">
<bpmn2:flowNodeRef>Task_1</bpmn2:flowNodeRef> <bpmn2:flowNodeRef>Task_1</bpmn2:flowNodeRef>
@ -283,6 +284,7 @@ Bei Fragen wenden Sie sich bitte an info@imixs.com
<bpmn2:outgoing>SequenceFlow_39</bpmn2:outgoing> <bpmn2:outgoing>SequenceFlow_39</bpmn2:outgoing>
<bpmn2:outgoing>SequenceFlow_22</bpmn2:outgoing> <bpmn2:outgoing>SequenceFlow_22</bpmn2:outgoing>
<bpmn2:incoming>sequenceFlow_OttgXA</bpmn2:incoming> <bpmn2:incoming>sequenceFlow_OttgXA</bpmn2:incoming>
<bpmn2:incoming>sequenceFlow_mdGzDQ</bpmn2:incoming>
</bpmn2:task> </bpmn2:task>
<bpmn2:dataObject id="DataObject_2" name="Invoice Form"> <bpmn2:dataObject id="DataObject_2" name="Invoice Form">
<bpmn2:documentation id="Documentation_8"><![CDATA[<?xml version="1.0"?> <bpmn2:documentation id="Documentation_8"><![CDATA[<?xml version="1.0"?>
@ -872,9 +874,9 @@ Betrag: <itemvalue>invoice.total</itemvalue> <itemvalue>invoice.currency</itemva
</bpmn2:extensionElements> </bpmn2:extensionElements>
<bpmn2:documentation id="Documentation_35"/> <bpmn2:documentation id="Documentation_35"/>
<bpmn2:outputSet id="OutputSet_4" name="Output Set 3"/> <bpmn2:outputSet id="OutputSet_4" name="Output Set 3"/>
<bpmn2:signalEventDefinition id="SignalEventDefinition_8"/>
<bpmn2:outgoing>sequenceFlow_zhcE7Q</bpmn2:outgoing> <bpmn2:outgoing>sequenceFlow_zhcE7Q</bpmn2:outgoing>
<bpmn2:outgoing>association_P36Fzw</bpmn2:outgoing> <bpmn2:outgoing>association_P36Fzw</bpmn2:outgoing>
<bpmn2:compensationEventDefinition id="compensationEventDefinition_M3jl8w"/>
</bpmn2:intermediateCatchEvent> </bpmn2:intermediateCatchEvent>
<bpmn2:task id="Task_8" imixs:processid="5910" name="Debt collection paid"> <bpmn2:task id="Task_8" imixs:processid="5910" name="Debt collection paid">
<bpmn2:extensionElements> <bpmn2:extensionElements>
@ -1305,7 +1307,7 @@ Wiedervorlage wird um 7 bzw. 5 Tage erhöht</bpmn2:text>
<imixs:value><![CDATA[2]]></imixs:value> <imixs:value><![CDATA[2]]></imixs:value>
</imixs:item> </imixs:item>
<imixs:item name="keypublicresult" type="xs:string"> <imixs:item name="keypublicresult" type="xs:string">
<imixs:value><![CDATA[0]]></imixs:value> <imixs:value><![CDATA[1]]></imixs:value>
</imixs:item> </imixs:item>
<imixs:item name="keylogdateformat" type="xs:string"> <imixs:item name="keylogdateformat" type="xs:string">
<imixs:value><![CDATA[2]]></imixs:value> <imixs:value><![CDATA[2]]></imixs:value>
@ -1329,16 +1331,6 @@ Wiedervorlage wird um 7 bzw. 5 Tage erhöht</bpmn2:text>
<imixs:item name="keyupdateacl" type="xs:boolean"> <imixs:item name="keyupdateacl" type="xs:boolean">
<imixs:value>false</imixs:value> <imixs:value>false</imixs:value>
</imixs:item> </imixs:item>
<imixs:item name="txtbusinessrule" type="xs:string">
<imixs:value><![CDATA[var result={};
var refField="dbtr.mail";
result.isValid=true;
if ( ( workitem.get(refField) == null || ''==workitem.get(refField)[0]) ) {
result.isValid=false;
result.errorMessage='No email address has been recorded for this vendor yet. Please update it in the Creditor/Debitor Management.';
}]]></imixs:value>
</imixs:item>
<imixs:item name="namaddreadaccess" type="xs:string"> <imixs:item name="namaddreadaccess" type="xs:string">
<imixs:value><![CDATA[{process:?:member}]]></imixs:value> <imixs:value><![CDATA[{process:?:member}]]></imixs:value>
<imixs:value><![CDATA[{space:?:member}]]></imixs:value> <imixs:value><![CDATA[{space:?:member}]]></imixs:value>
@ -1435,6 +1427,86 @@ result.isValid=true;
<bpmn2:association id="association_xVkeww" sourceRef="DataObject_2" targetRef="Task_1"> <bpmn2:association id="association_xVkeww" sourceRef="DataObject_2" targetRef="Task_1">
<bpmn2:documentation id="documentation_euQGuA"/> <bpmn2:documentation id="documentation_euQGuA"/>
</bpmn2:association> </bpmn2:association>
<bpmn2:intermediateCatchEvent id="event_07fo0g" imixs:activityid="400" name="ZOHO Test">
<bpmn2:extensionElements>
<imixs:item name="keylogtimeformat" type="xs:string">
<imixs:value><![CDATA[2]]></imixs:value>
</imixs:item>
<imixs:item name="keyarchive" type="xs:string">
<imixs:value><![CDATA[0]]></imixs:value>
</imixs:item>
<imixs:item name="txtmailsubject" type="xs:string">
<imixs:value/>
</imixs:item>
<imixs:item name="keyaccessmode" type="xs:string">
<imixs:value><![CDATA[0]]></imixs:value>
</imixs:item>
<imixs:item name="keyscheduledactivity" type="xs:string">
<imixs:value><![CDATA[0]]></imixs:value>
</imixs:item>
<imixs:item name="rtfmailbody" type="xs:string">
<imixs:value/>
</imixs:item>
<imixs:item name="keymailreceiverfields" type="xs:string"/>
<imixs:item name="keyversion" type="xs:string">
<imixs:value><![CDATA[0]]></imixs:value>
</imixs:item>
<imixs:item name="keyaddwritefields" type="xs:string">
<imixs:value><![CDATA[process.manager]]></imixs:value>
<imixs:value><![CDATA[process.team]]></imixs:value>
<imixs:value><![CDATA[space.manager]]></imixs:value>
</imixs:item>
<imixs:item name="numnextactivityid" type="xs:int">
<imixs:value><![CDATA[0]]></imixs:value>
</imixs:item>
<imixs:item name="keyfollowup" type="xs:string">
<imixs:value><![CDATA[2]]></imixs:value>
</imixs:item>
<imixs:item name="keypublicresult" type="xs:string">
<imixs:value><![CDATA[1]]></imixs:value>
</imixs:item>
<imixs:item name="keylogdateformat" type="xs:string">
<imixs:value><![CDATA[2]]></imixs:value>
</imixs:item>
<imixs:item name="keyownershipfields" type="xs:string"/>
<imixs:item name="numnextid" type="xs:int">
<imixs:value><![CDATA[5005]]></imixs:value>
</imixs:item>
<imixs:item name="txtnextprocesstree" type="xs:string">
<imixs:value/>
</imixs:item>
<imixs:item name="txtname" type="xs:string">
<imixs:value><![CDATA[Freigabe einholen]]></imixs:value>
</imixs:item>
<imixs:item name="keyownershipmode" type="xs:string">
<imixs:value><![CDATA[0]]></imixs:value>
</imixs:item>
<imixs:item name="rtfresultlog" type="xs:string">
<imixs:value><![CDATA[Zoho export]]></imixs:value>
</imixs:item>
<imixs:item name="keyupdateacl" type="xs:boolean">
<imixs:value>false</imixs:value>
</imixs:item>
<imixs:item name="namaddreadaccess" type="xs:string">
<imixs:value><![CDATA[{process:?:member}]]></imixs:value>
<imixs:value><![CDATA[{space:?:member}]]></imixs:value>
</imixs:item>
<imixs:item name="keyaddreadfields" type="xs:string"/>
</bpmn2:extensionElements>
<bpmn2:documentation id="documentation_WC7jtA"/>
<bpmn2:dataOutput id="dataOutput_B0vRwQ" name="Signal_2_Output"/>
<bpmn2:dataOutputAssociation id="dataOutputAssociation_VdUGcQ">
<bpmn2:sourceRef>DataOutput_1</bpmn2:sourceRef>
</bpmn2:dataOutputAssociation>
<bpmn2:outputSet id="outputSet_b1tCbA" name="Output Set 2">
<bpmn2:dataOutputRefs>DataOutput_1</bpmn2:dataOutputRefs>
</bpmn2:outputSet>
<bpmn2:signalEventDefinition id="signalEventDefinition_h50HrA" signalRef="signal_8"/>
<bpmn2:outgoing>sequenceFlow_mdGzDQ</bpmn2:outgoing>
</bpmn2:intermediateCatchEvent>
<bpmn2:sequenceFlow id="sequenceFlow_mdGzDQ" sourceRef="event_07fo0g" targetRef="Task_2">
<bpmn2:documentation id="documentation_NV1xfg"/>
</bpmn2:sequenceFlow>
</bpmn2:process> </bpmn2:process>
<bpmn2:process id="process_2" name="Default Process" processType="Public"> <bpmn2:process id="process_2" name="Default Process" processType="Public">
<bpmn2:documentation id="documentation_J67Rkw"/> <bpmn2:documentation id="documentation_J67Rkw"/>
@ -1800,14 +1872,14 @@ result.isValid=true;
<di:waypoint x="1317.0" y="215.0"/> <di:waypoint x="1317.0" y="215.0"/>
</bpmndi:BPMNEdge> </bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_zhcE7Q" id="BPMNEdge_oQt4SA" sourceElement="BPMNShape_IntermediateCatchEvent_8" targetElement="BPMNShape_Task_3"> <bpmndi:BPMNEdge bpmnElement="sequenceFlow_zhcE7Q" id="BPMNEdge_oQt4SA" sourceElement="BPMNShape_IntermediateCatchEvent_8" targetElement="BPMNShape_Task_3">
<di:waypoint x="690.0" y="818.0"/> <di:waypoint x="695.0" y="817.0"/>
<di:waypoint x="690.0" y="805.0"/> <di:waypoint x="695.0" y="805.0"/>
<di:waypoint x="710.0" y="805.0"/> <di:waypoint x="710.0" y="805.0"/>
<di:waypoint x="710.0" y="780.0"/> <di:waypoint x="710.0" y="780.0"/>
</bpmndi:BPMNEdge> </bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="association_P36Fzw" id="BPMNEdge_QtxjNQ" sourceElement="BPMNShape_IntermediateCatchEvent_8" targetElement="BPMNShape_TextAnnotation_3"> <bpmndi:BPMNEdge bpmnElement="association_P36Fzw" id="BPMNEdge_QtxjNQ" sourceElement="BPMNShape_IntermediateCatchEvent_8" targetElement="BPMNShape_TextAnnotation_3">
<di:waypoint x="678.0" y="840.0"/> <di:waypoint x="677.0" y="835.0"/>
<di:waypoint x="575.0" y="840.0"/> <di:waypoint x="575.0" y="835.0"/>
<di:waypoint x="575.0" y="920.0"/> <di:waypoint x="575.0" y="920.0"/>
</bpmndi:BPMNEdge> </bpmndi:BPMNEdge>
<bpmndi:BPMNShape bpmnElement="task_LqbtSQ" id="BPMNShape_PXTHfw"> <bpmndi:BPMNShape bpmnElement="task_LqbtSQ" id="BPMNShape_PXTHfw">
@ -1895,6 +1967,18 @@ result.isValid=true;
<di:waypoint x="648.0" y="755.0"/> <di:waypoint x="648.0" y="755.0"/>
<di:waypoint x="690.0" y="755.0"/> <di:waypoint x="690.0" y="755.0"/>
</bpmndi:BPMNEdge> </bpmndi:BPMNEdge>
<bpmndi:BPMNShape bpmnElement="event_07fo0g" id="BPMNShape_i8omwg">
<dc:Bounds height="36.0" width="36.0" x="797.0" y="397.0"/>
<bpmndi:BPMNLabel id="BPMNLabel_i9tvtw">
<dc:Bounds height="20.0" width="100.0" x="765.0" y="436.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_mdGzDQ" id="BPMNEdge_VUfhzg" sourceElement="BPMNShape_i8omwg" targetElement="BPMNShape_Task_5">
<di:waypoint x="815.0" y="397.0"/>
<di:waypoint x="815.0" y="364.0"/>
<di:waypoint x="775.0" y="364.0"/>
<di:waypoint x="775.0" y="330.0"/>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane> </bpmndi:BPMNPlane>
<bpmndi:BPMNLabelStyle id="BPMNLabelStyle_1"> <bpmndi:BPMNLabelStyle id="BPMNLabelStyle_1">
<dc:Font name="arial" size="9.0"/> <dc:Font name="arial" size="9.0"/>