zoho attachments
This commit is contained in:
parent
f30cc8e8fe
commit
be064fb965
6 changed files with 661 additions and 286 deletions
|
|
@ -13,19 +13,16 @@ client ID= 1000.NLTM2KUEHI696MSLGEIANATVGGX7IN
|
||||||
client secret= ee9ba49f16e77c64abb89cea13542d2326d8d03ce8
|
client secret= ee9ba49f16e77c64abb89cea13542d2326d8d03ce8
|
||||||
```
|
```
|
||||||
|
|
||||||
|
# API Dokumentation
|
||||||
|
|
||||||
|
- API Docu: https://www.zoho.com/books/api/v3/introduction/
|
||||||
|
- Authentication: https://www.zoho.com/books/api/v3/oauth/
|
||||||
|
|
||||||
# Testumgebung
|
# Testumgebung
|
||||||
|
|
||||||
https://books.zoho.eu/app/20105697367
|
https://books.zoho.eu/app/20105697367
|
||||||
https://accounts.zoho.eu/home#
|
https://accounts.zoho.eu/home#
|
||||||
|
|
||||||
API Docu:
|
|
||||||
|
|
||||||
https://www.zoho.com/books/api/v3/introduction/
|
|
||||||
|
|
||||||
Authentication:
|
|
||||||
|
|
||||||
https://www.zoho.com/books/api/v3/oauth/
|
|
||||||
|
|
||||||
Base API URI= https://accounts.zoho.eu/
|
Base API URI= https://accounts.zoho.eu/
|
||||||
|
|
||||||
## 1. Client Einrichten
|
## 1. Client Einrichten
|
||||||
|
|
|
||||||
273
doc/zoho/invoice-create-example.json
Normal file
273
doc/zoho/invoice-create-example.json
Normal file
|
|
@ -0,0 +1,273 @@
|
||||||
|
{
|
||||||
|
"code": 0,
|
||||||
|
"message": "The invoice has been created.",
|
||||||
|
"invoice": {
|
||||||
|
"invoice_id": "777249000000082026",
|
||||||
|
"invoice_number": "INV-000018",
|
||||||
|
"date": "2024-04-03",
|
||||||
|
"due_date": "2024-05-03",
|
||||||
|
"offline_created_date_with_time": "",
|
||||||
|
"customer_id": "777249000000075016",
|
||||||
|
"contact_number": "CUS-00005",
|
||||||
|
"customer_name": "Action Agenciamento de Cargas LTDA",
|
||||||
|
"customer_custom_fields": [],
|
||||||
|
"customer_custom_field_hash": {},
|
||||||
|
"email": "",
|
||||||
|
"currency_id": "777249000000000071",
|
||||||
|
"invoice_source": "Api",
|
||||||
|
"currency_code": "EUR",
|
||||||
|
"currency_symbol": "€",
|
||||||
|
"currency_name_formatted": "EUR- Euro",
|
||||||
|
"status": "draft",
|
||||||
|
"unprocessed_payment_amount": 0.00,
|
||||||
|
"custom_fields": [],
|
||||||
|
"custom_field_hash": {},
|
||||||
|
"recurring_invoice_id": "",
|
||||||
|
"is_last_child_invoice": false,
|
||||||
|
"payment_mode": "",
|
||||||
|
"bank_account_id": "",
|
||||||
|
"payment_terms": 30,
|
||||||
|
"payment_terms_label": "Net 30",
|
||||||
|
"early_payment_discount_percentage": 0.00,
|
||||||
|
"early_payment_discount_due_days": "",
|
||||||
|
"early_payment_discount_amount": 0.00,
|
||||||
|
"payment_reminder_enabled": true,
|
||||||
|
"payment_made": 0.00,
|
||||||
|
"zcrm_potential_id": "",
|
||||||
|
"zcrm_potential_name": "",
|
||||||
|
"reference_number": "",
|
||||||
|
"preceding_invoice_id": "",
|
||||||
|
"is_early_payment_discount_applicable": false,
|
||||||
|
"lock_details": {},
|
||||||
|
"is_progress_invoice": false,
|
||||||
|
"can_show_kit_return": false,
|
||||||
|
"is_kit_partial_return": false,
|
||||||
|
"line_items": [
|
||||||
|
{
|
||||||
|
"line_item_id": "777249000000082036",
|
||||||
|
"item_id": "",
|
||||||
|
"item_order": 1,
|
||||||
|
"name": "R IM-GCA-2403-110",
|
||||||
|
"internal_name": "",
|
||||||
|
"description": "",
|
||||||
|
"discount_account_id": "",
|
||||||
|
"discount_account_name": "",
|
||||||
|
"unit": "",
|
||||||
|
"quantity": 1.00,
|
||||||
|
"discount_amount": 0.00,
|
||||||
|
"discount": 0.00,
|
||||||
|
"discounts": [],
|
||||||
|
"bcy_rate": 1995.00,
|
||||||
|
"rate": 1995.00,
|
||||||
|
"account_id": "777249000000000376",
|
||||||
|
"account_name": "Sales",
|
||||||
|
"header_id": "",
|
||||||
|
"header_name": "",
|
||||||
|
"pricebook_id": "",
|
||||||
|
"tax_id": "",
|
||||||
|
"tax_name": "",
|
||||||
|
"tax_type": "tax",
|
||||||
|
"tax_percentage": 0,
|
||||||
|
"item_total": 1995.00,
|
||||||
|
"item_custom_fields": [],
|
||||||
|
"pricing_scheme": "unit",
|
||||||
|
"tags": [],
|
||||||
|
"documents": [],
|
||||||
|
"line_item_taxes": [],
|
||||||
|
"bill_id": "",
|
||||||
|
"bill_item_id": "",
|
||||||
|
"project_id": "",
|
||||||
|
"time_entry_ids": [],
|
||||||
|
"expense_id": "",
|
||||||
|
"item_type": "",
|
||||||
|
"expense_receipt_name": "",
|
||||||
|
"sales_rate": "",
|
||||||
|
"purchase_rate": "",
|
||||||
|
"salesorder_item_id": "",
|
||||||
|
"cost_amount": 0,
|
||||||
|
"markup_percent": 0,
|
||||||
|
"mapped_items": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total_retention_amount": 0.00,
|
||||||
|
"retention_items": [],
|
||||||
|
"retention_override_preference": "no_override",
|
||||||
|
"exchange_rate": 1.00,
|
||||||
|
"is_autobill_enabled": false,
|
||||||
|
"inprocess_transaction_present": false,
|
||||||
|
"allow_partial_payments": false,
|
||||||
|
"price_precision": 2,
|
||||||
|
"sub_total": 1995.00,
|
||||||
|
"tax_total": 0.00,
|
||||||
|
"discount_total": 0.00,
|
||||||
|
"discount_percent": 0.00,
|
||||||
|
"discount": 0.00,
|
||||||
|
"discount_applied_on_amount": 0.00,
|
||||||
|
"discount_type": "item_level",
|
||||||
|
"discount_account_id": "",
|
||||||
|
"discount_account_name": "",
|
||||||
|
"is_discount_before_tax": true,
|
||||||
|
"adjustment": 0.00,
|
||||||
|
"adjustment_description": "",
|
||||||
|
"shipping_charge_tax_id": "",
|
||||||
|
"shipping_charge_tax_name": "",
|
||||||
|
"shipping_charge_tax_type": "",
|
||||||
|
"shipping_charge_tax_percentage": "",
|
||||||
|
"shipping_charge_tax_exemption_id": "",
|
||||||
|
"shipping_charge_tax_exemption_code": "",
|
||||||
|
"shipping_charge_tax": "",
|
||||||
|
"bcy_shipping_charge_tax": "",
|
||||||
|
"shipping_charge_exclusive_of_tax": 0.000,
|
||||||
|
"shipping_charge_inclusive_of_tax": 0.000,
|
||||||
|
"shipping_charge_tax_formatted": "",
|
||||||
|
"shipping_charge_exclusive_of_tax_formatted": "€0,00",
|
||||||
|
"shipping_charge_inclusive_of_tax_formatted": "€0,00",
|
||||||
|
"shipping_charge_account_id": "",
|
||||||
|
"shipping_charge_account_name": "",
|
||||||
|
"shipping_charge": 0.00,
|
||||||
|
"bcy_shipping_charge": 0.00,
|
||||||
|
"bcy_adjustment": 0.00,
|
||||||
|
"bcy_sub_total": 1995.00,
|
||||||
|
"bcy_discount_total": 0.00,
|
||||||
|
"bcy_tax_total": 0.00,
|
||||||
|
"bcy_total": 1995.000,
|
||||||
|
"total": 1995.00,
|
||||||
|
"balance": 1995.00,
|
||||||
|
"write_off_amount": 0.00,
|
||||||
|
"roundoff_value": 0.00,
|
||||||
|
"transaction_rounding_type": "no_rounding",
|
||||||
|
"is_inclusive_tax": false,
|
||||||
|
"sub_total_inclusive_of_tax": 0.00,
|
||||||
|
"contact_category": "",
|
||||||
|
"tax_rounding": "entity_level",
|
||||||
|
"taxes": [],
|
||||||
|
"tds_calculation_type": "tds_item_level",
|
||||||
|
"can_send_invoice_sms": true,
|
||||||
|
"payment_expected_date": "",
|
||||||
|
"payment_discount": 0.00,
|
||||||
|
"stop_reminder_until_payment_expected_date": false,
|
||||||
|
"last_payment_date": "",
|
||||||
|
"ach_supported": false,
|
||||||
|
"ach_payment_initiated": false,
|
||||||
|
"payment_options": {
|
||||||
|
"payment_gateways": []
|
||||||
|
},
|
||||||
|
"reader_offline_payment_initiated": false,
|
||||||
|
"contact_persons": [],
|
||||||
|
"contact_persons_associated": [],
|
||||||
|
"bank_account_details": {
|
||||||
|
"bank_account_id": "777249000000000376",
|
||||||
|
"bank_account_name": "Sales",
|
||||||
|
"bank_account_number": "",
|
||||||
|
"is_active": false,
|
||||||
|
"iban_number": "",
|
||||||
|
"swift_code": ""
|
||||||
|
},
|
||||||
|
"attachment_name": "",
|
||||||
|
"documents": [],
|
||||||
|
"computation_type": "basic",
|
||||||
|
"deliverychallans": [],
|
||||||
|
"merchant_id": "",
|
||||||
|
"merchant_name": "",
|
||||||
|
"ecomm_operator_id": "",
|
||||||
|
"ecomm_operator_name": "",
|
||||||
|
"salesorder_id": "",
|
||||||
|
"salesorder_number": "",
|
||||||
|
"salesorders": [],
|
||||||
|
"shipping_bills": [],
|
||||||
|
"contact_persons_details": [],
|
||||||
|
"contact": {
|
||||||
|
"customer_balance": 0.00,
|
||||||
|
"credit_limit": 0.00,
|
||||||
|
"unused_customer_credits": 0.00,
|
||||||
|
"is_credit_limit_migration_completed": true
|
||||||
|
},
|
||||||
|
"salesperson_id": "",
|
||||||
|
"salesperson_name": "",
|
||||||
|
"is_emailed": false,
|
||||||
|
"reminders_sent": 0,
|
||||||
|
"last_reminder_sent_date": "",
|
||||||
|
"next_reminder_date_formatted": "",
|
||||||
|
"is_viewed_by_client": false,
|
||||||
|
"client_viewed_time": "",
|
||||||
|
"submitter_id": "",
|
||||||
|
"approver_id": "",
|
||||||
|
"submitted_date": "",
|
||||||
|
"submitted_by": "",
|
||||||
|
"submitted_by_name": "",
|
||||||
|
"submitted_by_email": "",
|
||||||
|
"submitted_by_photo_url": "",
|
||||||
|
"template_id": "777249000000000103",
|
||||||
|
"template_name": "Standard Template",
|
||||||
|
"template_type": "standard",
|
||||||
|
"notes": "Thanks for your business.",
|
||||||
|
"terms": "",
|
||||||
|
"billing_address": {
|
||||||
|
"street": "",
|
||||||
|
"address": "",
|
||||||
|
"street2": "",
|
||||||
|
"city": "",
|
||||||
|
"state": "",
|
||||||
|
"zip": "",
|
||||||
|
"country": "",
|
||||||
|
"fax": "",
|
||||||
|
"phone": "",
|
||||||
|
"attention": ""
|
||||||
|
},
|
||||||
|
"shipping_address": {
|
||||||
|
"street": "",
|
||||||
|
"address": "",
|
||||||
|
"street2": "",
|
||||||
|
"city": "",
|
||||||
|
"state": "",
|
||||||
|
"zip": "",
|
||||||
|
"country": "",
|
||||||
|
"fax": "",
|
||||||
|
"phone": "",
|
||||||
|
"attention": ""
|
||||||
|
},
|
||||||
|
"invoice_url": "https://zohosecurepay.eu/books/imixs/secure?CInvoiceID=2-64277fbd3cd8bc868d9e57c3085f4f428b1f66a1d5e3f3a32fc159e6879f623863183b0ac4651a08fdcce7621ddf18bf61770563d9fd2832f987dda624d4f11579820a40c3f55870 ",
|
||||||
|
"subject_content": "",
|
||||||
|
"can_send_in_mail": false,
|
||||||
|
"created_time": "2025-05-02T10:23:49+0200",
|
||||||
|
"last_modified_time": "2025-05-02T10:23:49+0200",
|
||||||
|
"created_date": "2025-05-02",
|
||||||
|
"created_by_id": "777249000000055227",
|
||||||
|
"created_by_name": "Ralph Soika",
|
||||||
|
"last_modified_by_id": "777249000000055227",
|
||||||
|
"page_width": "8.27in",
|
||||||
|
"page_height": "11.69in",
|
||||||
|
"orientation": "portrait",
|
||||||
|
"is_backorder": "",
|
||||||
|
"sales_channel": "direct_sales",
|
||||||
|
"color_code": "",
|
||||||
|
"current_sub_status_id": "",
|
||||||
|
"current_sub_status": "draft",
|
||||||
|
"sub_statuses": [],
|
||||||
|
"estimate_id": "",
|
||||||
|
"is_client_review_settings_enabled": false,
|
||||||
|
"unused_retainer_payments": 0.00,
|
||||||
|
"credits_applied": 0.00,
|
||||||
|
"tax_amount_withheld": 0.00,
|
||||||
|
"schedule_time": "",
|
||||||
|
"customer_default_billing_address": {
|
||||||
|
"zip": "",
|
||||||
|
"country": "",
|
||||||
|
"address": "",
|
||||||
|
"city": "",
|
||||||
|
"phone": "",
|
||||||
|
"street2": "",
|
||||||
|
"state": "",
|
||||||
|
"fax": "",
|
||||||
|
"state_code": ""
|
||||||
|
},
|
||||||
|
"includes_package_tracking_info": false,
|
||||||
|
"approvers_list": [],
|
||||||
|
"qr_code": {
|
||||||
|
"qr_source": "invoice_url",
|
||||||
|
"is_qr_enabled": false,
|
||||||
|
"qr_value": "",
|
||||||
|
"qr_description": "Scan the QR code to view the configured information."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,11 +8,15 @@ import java.net.http.HttpRequest;
|
||||||
import java.net.http.HttpResponse;
|
import java.net.http.HttpResponse;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.imixs.workflow.FileData;
|
||||||
import org.imixs.workflow.ItemCollection;
|
import org.imixs.workflow.ItemCollection;
|
||||||
import org.imixs.workflow.exceptions.PluginException;
|
import org.imixs.workflow.exceptions.PluginException;
|
||||||
|
|
||||||
|
|
@ -21,6 +25,7 @@ import com.alexanderlogistics.zoho.dto.ZohoContact;
|
||||||
import com.alexanderlogistics.zoho.dto.ZohoContactCreateResponse;
|
import com.alexanderlogistics.zoho.dto.ZohoContactCreateResponse;
|
||||||
import com.alexanderlogistics.zoho.dto.ZohoContactFindResponse;
|
import com.alexanderlogistics.zoho.dto.ZohoContactFindResponse;
|
||||||
import com.alexanderlogistics.zoho.dto.ZohoInvoice;
|
import com.alexanderlogistics.zoho.dto.ZohoInvoice;
|
||||||
|
import com.alexanderlogistics.zoho.dto.ZohoInvoiceCreateResponse;
|
||||||
import com.alexanderlogistics.zoho.dto.ZohoInvoiceItem;
|
import com.alexanderlogistics.zoho.dto.ZohoInvoiceItem;
|
||||||
|
|
||||||
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
|
|
@ -60,10 +65,11 @@ public class ZohoAPIService {
|
||||||
this.httpClient = HttpClient.newBuilder()
|
this.httpClient = HttpClient.newBuilder()
|
||||||
.connectTimeout(Duration.ofSeconds(10))
|
.connectTimeout(Duration.ofSeconds(10))
|
||||||
.build();
|
.build();
|
||||||
// this.jsonb = JsonbBuilder.create();
|
|
||||||
this.jsonb = JsonbBuilder.create(new JsonbConfig()
|
|
||||||
.withDateFormat("yyyy-MM-dd", Locale.getDefault()));
|
|
||||||
|
|
||||||
|
JsonbConfig config = new JsonbConfig()
|
||||||
|
.withDateFormat("yyyy-MM-dd", Locale.getDefault())
|
||||||
|
.setProperty("jsonb.fail-on-unknown-properties", false); // Ignoriert unbekannte Felder
|
||||||
|
this.jsonb = JsonbBuilder.create(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String findContactIDByCompanyName(String companyName, String accessToken) throws PluginException {
|
public String findContactIDByCompanyName(String companyName, String accessToken) throws PluginException {
|
||||||
|
|
@ -125,7 +131,7 @@ public class ZohoAPIService {
|
||||||
* @param invoice
|
* @param invoice
|
||||||
* @throws PluginException
|
* @throws PluginException
|
||||||
*/
|
*/
|
||||||
public void exportInvoice(ItemCollection invoice) throws PluginException {
|
public ZohoInvoice exportInvoice(ItemCollection invoice, List<FileData> files) throws PluginException {
|
||||||
String accessToken = zohoOAuthManager.getValidAccessToken();
|
String accessToken = zohoOAuthManager.getValidAccessToken();
|
||||||
logger.info("├── Zoho Export Invoice " + invoice.getItemValueString("invoice.number") + "...");
|
logger.info("├── Zoho Export Invoice " + invoice.getItemValueString("invoice.number") + "...");
|
||||||
|
|
||||||
|
|
@ -166,7 +172,6 @@ public class ZohoAPIService {
|
||||||
// Print response details to console
|
// Print response details to console
|
||||||
logger.fine("├── Zoho Response:");
|
logger.fine("├── Zoho Response:");
|
||||||
logger.fine("│ ├── Status Code: " + response.statusCode());
|
logger.fine("│ ├── Status Code: " + response.statusCode());
|
||||||
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",
|
||||||
|
|
@ -174,13 +179,40 @@ public class ZohoAPIService {
|
||||||
", Response: " + response.body());
|
", Response: " + response.body());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Erfolgreiche Verarbeitung hier
|
// Erfolgreiche Erstellung
|
||||||
// Optional: Response auswerten
|
String bodyContent = response.body();
|
||||||
|
logger.fine("│ ├── Response Body: " + bodyContent);
|
||||||
|
|
||||||
|
System.out.print(bodyContent);
|
||||||
|
ZohoInvoiceCreateResponse createResponse = jsonb.fromJson(bodyContent,
|
||||||
|
ZohoInvoiceCreateResponse.class);
|
||||||
|
|
||||||
|
logger.fine("│ ├── OK - customerID=" + createResponse.getInvoice().getCustomerId());
|
||||||
|
logger.info("│ ├── OK - invoiceID=" + createResponse.getInvoice().getInvoiceId());
|
||||||
|
|
||||||
|
if (createResponse != null) {
|
||||||
|
zohoInvoice = createResponse.getInvoice();
|
||||||
|
|
||||||
|
// now add attachments (optional)
|
||||||
|
if (files != null) {
|
||||||
|
for (FileData file : files) {
|
||||||
|
attacheFileData(zohoInvoice.getInvoiceId(), file, "invoices",
|
||||||
|
accessToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
logger.info("│ ├── Invoice Export successful.");
|
logger.info("│ ├── Invoice Export successful.");
|
||||||
|
return zohoInvoice;
|
||||||
|
} else {
|
||||||
|
throw new PluginException(this.getClass().getName(), "Zoho API error",
|
||||||
|
"Failed to export invoice to Zoho: unable to resolve response");
|
||||||
|
}
|
||||||
|
|
||||||
} catch (IOException | InterruptedException 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.getMessage(), e);
|
"Failed to export invoice to Zoho: " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -223,7 +255,8 @@ public class ZohoAPIService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Erfolgreiche Erstellung
|
// Erfolgreiche Erstellung
|
||||||
ZohoContactCreateResponse createResponse = jsonb.fromJson(response.body(), ZohoContactCreateResponse.class);
|
ZohoContactCreateResponse createResponse = jsonb.fromJson(response.body(),
|
||||||
|
ZohoContactCreateResponse.class);
|
||||||
if (createResponse != null) {
|
if (createResponse != null) {
|
||||||
logger.info("│ ├── OK - contactID=" + createResponse.getContact().getContactId());
|
logger.info("│ ├── OK - contactID=" + createResponse.getContact().getContactId());
|
||||||
return createResponse.getContact();
|
return createResponse.getContact();
|
||||||
|
|
@ -246,8 +279,16 @@ public class ZohoAPIService {
|
||||||
// Basisinformationen
|
// Basisinformationen
|
||||||
logger.fine("invoice.date=" + invoice.getItemValueDate("invoice.date"));
|
logger.fine("invoice.date=" + invoice.getItemValueDate("invoice.date"));
|
||||||
logger.fine("invoice.duedate=" + invoice.getItemValueDate("invoice.duedate"));
|
logger.fine("invoice.duedate=" + invoice.getItemValueDate("invoice.duedate"));
|
||||||
zohoInvoice.setDate(invoice.getItemValueDate("invoice.date"));
|
|
||||||
zohoInvoice.setDueDate(invoice.getItemValueDate("invoice.duedate"));
|
// LocalDate.ofInstant(date.toInstant(), ZoneId.systemDefault())
|
||||||
|
zohoInvoice.setDate(
|
||||||
|
LocalDate.ofInstant(invoice.getItemValueDate("invoice.date").toInstant(),
|
||||||
|
ZoneId.systemDefault()));
|
||||||
|
|
||||||
|
zohoInvoice.setDueDate(
|
||||||
|
LocalDate.ofInstant(invoice.getItemValueDate("invoice.duedate").toInstant(),
|
||||||
|
ZoneId.systemDefault()));
|
||||||
|
|
||||||
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);
|
||||||
|
|
@ -268,4 +309,69 @@ public class ZohoAPIService {
|
||||||
zohoInvoice.setLineItems(lineItems);
|
zohoInvoice.setLineItems(lineItems);
|
||||||
return zohoInvoice;
|
return zohoInvoice;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method adds an attachment to an invoice
|
||||||
|
*
|
||||||
|
* @param invoiceId The Zoho invoice ID
|
||||||
|
* @param file The FileData object containing the attachment
|
||||||
|
* @param uriPattern The API endpoint pattern (e.g., "invoices")
|
||||||
|
* @param accessToken The OAuth access token
|
||||||
|
* @throws PluginException If the API call fails
|
||||||
|
*/
|
||||||
|
public void attacheFileData(String invoiceId, FileData file, String uriPattern, String accessToken)
|
||||||
|
throws PluginException {
|
||||||
|
String uri = ZOHO_API_BASE_URL + uriPattern + "/" + invoiceId + "/attachment" +
|
||||||
|
"?organization_id=" + zohoOAuthManager.getOrganization();
|
||||||
|
|
||||||
|
logger.info("│ ├── add attachment '" + file.getName() + "'...");
|
||||||
|
logger.fine("│ ├── ...uri: " + uri + "...");
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Generate a unique boundary
|
||||||
|
String boundary = "----WebKitFormBoundary" + System.currentTimeMillis();
|
||||||
|
|
||||||
|
// Build multipart request body
|
||||||
|
byte[] header = ("--" + boundary + "\r\n" +
|
||||||
|
"Content-Disposition: form-data; name=\"attachment\"; filename=\""
|
||||||
|
+ file.getName() + "\"\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);
|
||||||
|
|
||||||
|
logger.fine("│ ├── content size=" + file.getContent().length);
|
||||||
|
HttpRequest.BodyPublisher bodyPublisher = HttpRequest.BodyPublishers.ofByteArrays(
|
||||||
|
Arrays.asList(header, file.getContent(), footer));
|
||||||
|
|
||||||
|
// Create and send the request
|
||||||
|
HttpRequest request = HttpRequest.newBuilder()
|
||||||
|
.uri(URI.create(uri))
|
||||||
|
.header("Authorization", "Zoho-oauthtoken " + accessToken)
|
||||||
|
.header("Content-Type", "multipart/form-data; boundary=" + boundary)
|
||||||
|
.POST(bodyPublisher)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
HttpResponse<String> response = httpClient.send(
|
||||||
|
request,
|
||||||
|
HttpResponse.BodyHandlers.ofString());
|
||||||
|
|
||||||
|
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 add attachment. Status code: " + response.statusCode() +
|
||||||
|
", Response: " + response.body());
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("│ ├── OK - Attachment added successfully");
|
||||||
|
|
||||||
|
} catch (IOException | InterruptedException e) {
|
||||||
|
throw new PluginException(this.getClass().getName(), "Zoho API error",
|
||||||
|
"Failed to add attachment: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import org.imixs.workflow.engine.WorkflowService;
|
||||||
import org.imixs.workflow.exceptions.AdapterException;
|
import org.imixs.workflow.exceptions.AdapterException;
|
||||||
import org.imixs.workflow.exceptions.PluginException;
|
import org.imixs.workflow.exceptions.PluginException;
|
||||||
|
|
||||||
import com.alexanderlogistics.InvoicePlugin;
|
import com.alexanderlogistics.zoho.dto.ZohoInvoice;
|
||||||
|
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
|
|
||||||
|
|
@ -52,78 +52,19 @@ public class ZohoExportAdapter implements SignalAdapter {
|
||||||
throws AdapterException, PluginException {
|
throws AdapterException, PluginException {
|
||||||
logger.info("├── Invoice Export");
|
logger.info("├── Invoice Export");
|
||||||
|
|
||||||
|
// Load attachments
|
||||||
|
List<FileData> files = null;
|
||||||
|
ItemCollection snapshot = snapshotService.findSnapshot(document);
|
||||||
|
|
||||||
|
if (snapshot != null) {
|
||||||
|
files = snapshot.getFileData();
|
||||||
|
}
|
||||||
|
|
||||||
// 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);
|
||||||
|
|
||||||
zohoAPIService.exportInvoice(document);
|
ZohoInvoice zohoInvoice = zohoAPIService.exportInvoice(document, files);
|
||||||
|
|
||||||
return document;
|
return document;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This method lookups the origin snapshot data and add the content of the $file
|
|
||||||
* to the export document
|
|
||||||
* <p>
|
|
||||||
* Change 14.10.2021 - Wir senden maximal 5MB and Daten, da die Cargosoft
|
|
||||||
* Schnittstelle größere Datensätze mit großen Dateianhängen einfach verschluckt
|
|
||||||
* ohne eine Fehlermeldugn zu liefern.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param document
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private ItemCollection loadFileDataFromSnapshot(ItemCollection document) {
|
|
||||||
ItemCollection origin = null;
|
|
||||||
ItemCollection result = (ItemCollection) document.clone();
|
|
||||||
|
|
||||||
// find the origin document
|
|
||||||
List<String> refs = document.getItemValue(WorkflowService.UNIQUEIDREF);
|
|
||||||
for (String ref : refs) {
|
|
||||||
origin = workflowService.getWorkItem(ref);
|
|
||||||
// "Rechnungseingang".equals(origin.getWorkflowGroup())
|
|
||||||
if (InvoicePlugin.isCargoRechnung(origin)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
origin = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we should have found the origin...
|
|
||||||
if (origin != null) {
|
|
||||||
ItemCollection snapshot = snapshotService.findSnapshot(origin);
|
|
||||||
// we should have found a snapshot...
|
|
||||||
if (snapshot != null) {
|
|
||||||
List<FileData> fileDataList = snapshot.getFileData();
|
|
||||||
// transfer origin file content
|
|
||||||
result.removeItem("$file");
|
|
||||||
long currentAttachmentSize = 0;
|
|
||||||
// we do only accept .pdf files and a maximum size of 10MB
|
|
||||||
for (FileData filedata : fileDataList) {
|
|
||||||
if (filedata.getName().toLowerCase().endsWith(".pdf")) {
|
|
||||||
// test if the size of the file is below the
|
|
||||||
// "cargosoft.export.ftp.maxattachmentsize"
|
|
||||||
if ((currentAttachmentSize + filedata.getContent().length) > maxAttachmentSize) {
|
|
||||||
long maxSizeInMB = maxAttachmentSize / 1024 / 1024;
|
|
||||||
String message = "Attachment '" + filedata.getName()
|
|
||||||
+ "' can not be exported to cargosoft - max file size exeeded (" + maxSizeInMB
|
|
||||||
+ "MB)!";
|
|
||||||
logger.warning(message);
|
|
||||||
// add comment
|
|
||||||
document.setItemValue("txtComment", message);
|
|
||||||
} else {
|
|
||||||
result.addFileData(filedata);
|
|
||||||
currentAttachmentSize = currentAttachmentSize + filedata.getContent().length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logger.warning("...did not found snapshot for origin workitem " + origin.getUniqueID());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logger.warning("...did not found origin workitem " + document.getUniqueID());
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,22 +1,44 @@
|
||||||
package com.alexanderlogistics.zoho.dto;
|
package com.alexanderlogistics.zoho.dto;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.time.LocalDate;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import jakarta.json.bind.annotation.JsonbDateFormat;
|
||||||
import jakarta.json.bind.annotation.JsonbProperty;
|
import jakarta.json.bind.annotation.JsonbProperty;
|
||||||
|
|
||||||
public class ZohoInvoice {
|
public class ZohoInvoice {
|
||||||
|
|
||||||
|
@JsonbProperty("invoice_id")
|
||||||
|
private String invoiceId;
|
||||||
|
|
||||||
|
@JsonbProperty("customer_id")
|
||||||
private String customerId;
|
private String customerId;
|
||||||
private Date date;
|
|
||||||
private Date dueDate;
|
@JsonbDateFormat("yyyy-MM-dd")
|
||||||
|
private LocalDate date;
|
||||||
|
|
||||||
|
@JsonbProperty("due_date")
|
||||||
|
@JsonbDateFormat("yyyy-MM-dd")
|
||||||
|
private LocalDate dueDate;
|
||||||
|
|
||||||
|
@JsonbProperty("invoice_number")
|
||||||
private String invoiceNumber;
|
private String invoiceNumber;
|
||||||
|
|
||||||
|
@JsonbProperty("line_items")
|
||||||
private List<ZohoInvoiceItem> lineItems;
|
private List<ZohoInvoiceItem> lineItems;
|
||||||
|
|
||||||
private double total;
|
private double total;
|
||||||
|
|
||||||
// Getter und Setter
|
// Getter und Setter
|
||||||
|
|
||||||
@JsonbProperty("customer_id")
|
public String getInvoiceId() {
|
||||||
|
return invoiceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInvoiceId(String invoiceId) {
|
||||||
|
this.invoiceId = invoiceId;
|
||||||
|
}
|
||||||
|
|
||||||
public String getCustomerId() {
|
public String getCustomerId() {
|
||||||
return customerId;
|
return customerId;
|
||||||
}
|
}
|
||||||
|
|
@ -25,24 +47,22 @@ public class ZohoInvoice {
|
||||||
this.customerId = customerId;
|
this.customerId = customerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Date getDate() {
|
public LocalDate getDate() {
|
||||||
return date;
|
return date;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDate(Date date) {
|
public void setDate(LocalDate date) {
|
||||||
this.date = date;
|
this.date = date;
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonbProperty("due_date")
|
public LocalDate getDueDate() {
|
||||||
public Date getDueDate() {
|
|
||||||
return dueDate;
|
return dueDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDueDate(Date duedate) {
|
public void setDueDate(LocalDate duedate) {
|
||||||
this.dueDate = duedate;
|
this.dueDate = duedate;
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonbProperty("invoice_number")
|
|
||||||
public String getInvoiceNumber() {
|
public String getInvoiceNumber() {
|
||||||
return invoiceNumber;
|
return invoiceNumber;
|
||||||
}
|
}
|
||||||
|
|
@ -51,7 +71,6 @@ public class ZohoInvoice {
|
||||||
this.invoiceNumber = invoiceNumber;
|
this.invoiceNumber = invoiceNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonbProperty("line_items")
|
|
||||||
public List<ZohoInvoiceItem> getLineItems() {
|
public List<ZohoInvoiceItem> getLineItems() {
|
||||||
return lineItems;
|
return lineItems;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
package com.alexanderlogistics.zoho.dto;
|
||||||
|
|
||||||
|
import jakarta.json.bind.annotation.JsonbProperty;
|
||||||
|
|
||||||
|
public class ZohoInvoiceCreateResponse {
|
||||||
|
@JsonbProperty("code")
|
||||||
|
private int code;
|
||||||
|
|
||||||
|
@JsonbProperty("message")
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
@JsonbProperty("invoice")
|
||||||
|
private ZohoInvoice invoice;
|
||||||
|
|
||||||
|
// 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 ZohoInvoice getInvoice() {
|
||||||
|
return invoice;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInvoice(ZohoInvoice invoice) {
|
||||||
|
this.invoice = invoice;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue