zoho attachments

This commit is contained in:
Ralph Soika 2025-05-02 14:27:02 +02:00
parent f30cc8e8fe
commit be064fb965
6 changed files with 661 additions and 286 deletions

View file

@ -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

View 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."
}
}
}

View file

@ -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);
}
}
} }

View file

@ -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;
}
} }

View file

@ -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;
} }

View file

@ -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;
}
}