Introduction to Zoho Books API
Zoho Books is a comprehensive accounting software that provides APIs for managing invoices, contacts, items, payments, and other financial data. This guide covers building a complete Java client for Zoho Books API integration.
Key Features
- Invoice Management - Create, read, update invoices
- Contact Management - Customer and vendor management
- Item Management - Product and service catalog
- Payment Processing - Payment recording and tracking
- Organization Management - Multi-organization support
- Report Generation - Financial reports and analytics
Dependencies and Setup
Maven Configuration
<properties>
<okhttp.version>4.12.0</okhttp.version>
<jackson.version>2.15.2</jackson.version>
<oauth2.version>0.1.0</oauth2.version>
</properties>
<dependencies>
<!-- HTTP Client -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>${okhttp.version}</version>
</dependency>
<!-- JSON Processing -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
<!-- OAuth2 Support -->
<dependency>
<groupId>com.github.scribejava</groupId>
<artifactId>scribejava-core</artifactId>
<version>8.3.3</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.7</version>
</dependency>
<!-- Testing -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.9.3</version>
<scope>test</scope>
</dependency>
</dependencies>
Core Zoho Books Client
Main Zoho Books Client
package com.zohobooks.client;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
public class ZohoBooksClient {
private static final Logger logger = LoggerFactory.getLogger(ZohoBooksClient.class);
private final String baseUrl;
private final String organizationId;
private final String accessToken;
private final OkHttpClient httpClient;
private final ObjectMapper objectMapper;
public ZohoBooksClient(String accessToken, String organizationId) {
this("https://books.zoho.com/api/v3", accessToken, organizationId);
}
public ZohoBooksClient(String baseUrl, String accessToken, String organizationId) {
this.baseUrl = baseUrl;
this.accessToken = accessToken;
this.organizationId = organizationId;
this.objectMapper = new ObjectMapper();
this.httpClient = new OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.build();
}
/**
* Execute API request with authentication
*/
private Response executeRequest(Request request) throws ZohoBooksException {
try {
Response response = httpClient.newCall(request).execute();
if (!response.isSuccessful()) {
handleErrorResponse(response);
}
return response;
} catch (IOException e) {
throw new ZohoBooksException("API request failed", e);
}
}
/**
* Handle error responses from Zoho Books API
*/
private void handleErrorResponse(Response response) throws IOException, ZohoBooksException {
String errorBody = response.body().string();
logger.error("Zoho Books API error: {} - {}", response.code(), errorBody);
try {
ErrorResponse errorResponse = objectMapper.readValue(errorBody, ErrorResponse.class);
throw new ZohoBooksException(
errorResponse.getMessage() != null ? errorResponse.getMessage() : "API Error",
response.code(),
errorResponse.getCode()
);
} catch (Exception e) {
throw new ZohoBooksException("HTTP " + response.code() + ": " + errorBody, response.code());
}
}
/**
* Create authenticated request builder
*/
private Request.Builder createAuthenticatedRequest(String endpoint) {
HttpUrl url = HttpUrl.parse(baseUrl + endpoint)
.newBuilder()
.addQueryParameter("organization_id", organizationId)
.build();
return new Request.Builder()
.url(url)
.addHeader("Authorization", "Zoho-oauthtoken " + accessToken)
.addHeader("Content-Type", "application/json");
}
/**
* GET request
*/
public <T> T get(String endpoint, Map<String, String> params, Class<T> responseType) throws ZohoBooksException {
try {
HttpUrl.Builder urlBuilder = HttpUrl.parse(baseUrl + endpoint).newBuilder()
.addQueryParameter("organization_id", organizationId);
if (params != null) {
for (Map.Entry<String, String> param : params.entrySet()) {
urlBuilder.addQueryParameter(param.getKey(), param.getValue());
}
}
Request request = new Request.Builder()
.url(urlBuilder.build())
.addHeader("Authorization", "Zoho-oauthtoken " + accessToken)
.get()
.build();
try (Response response = executeRequest(request)) {
String responseBody = response.body().string();
return objectMapper.readValue(responseBody, responseType);
}
} catch (IOException e) {
throw new ZohoBooksException("GET request failed: " + endpoint, e);
}
}
/**
* POST request
*/
public <T> T post(String endpoint, Object data, Class<T> responseType) throws ZohoBooksException {
return post(endpoint, data, null, responseType);
}
public <T> T post(String endpoint, Object data, Map<String, String> params, Class<T> responseType) throws ZohoBooksException {
try {
String jsonData = objectMapper.writeValueAsString(data);
HttpUrl.Builder urlBuilder = HttpUrl.parse(baseUrl + endpoint).newBuilder()
.addQueryParameter("organization_id", organizationId);
if (params != null) {
for (Map.Entry<String, String> param : params.entrySet()) {
urlBuilder.addQueryParameter(param.getKey(), param.getValue());
}
}
RequestBody body = RequestBody.create(jsonData, MediaType.parse("application/json"));
Request request = new Request.Builder()
.url(urlBuilder.build())
.addHeader("Authorization", "Zoho-oauthtoken " + accessToken)
.post(body)
.build();
try (Response response = executeRequest(request)) {
String responseBody = response.body().string();
return objectMapper.readValue(responseBody, responseType);
}
} catch (IOException e) {
throw new ZohoBooksException("POST request failed: " + endpoint, e);
}
}
/**
* PUT request
*/
public <T> T put(String endpoint, Object data, Class<T> responseType) throws ZohoBooksException {
return put(endpoint, data, null, responseType);
}
public <T> T put(String endpoint, Object data, Map<String, String> params, Class<T> responseType) throws ZohoBooksException {
try {
String jsonData = objectMapper.writeValueAsString(data);
HttpUrl.Builder urlBuilder = HttpUrl.parse(baseUrl + endpoint).newBuilder()
.addQueryParameter("organization_id", organizationId);
if (params != null) {
for (Map.Entry<String, String> param : params.entrySet()) {
urlBuilder.addQueryParameter(param.getKey(), param.getValue());
}
}
RequestBody body = RequestBody.create(jsonData, MediaType.parse("application/json"));
Request request = new Request.Builder()
.url(urlBuilder.build())
.addHeader("Authorization", "Zoho-oauthtoken " + accessToken)
.put(body)
.build();
try (Response response = executeRequest(request)) {
String responseBody = response.body().string();
return objectMapper.readValue(responseBody, responseType);
}
} catch (IOException e) {
throw new ZohoBooksException("PUT request failed: " + endpoint, e);
}
}
/**
* DELETE request
*/
public <T> T delete(String endpoint, Class<T> responseType) throws ZohoBooksException {
return delete(endpoint, null, responseType);
}
public <T> T delete(String endpoint, Map<String, String> params, Class<T> responseType) throws ZohoBooksException {
try {
HttpUrl.Builder urlBuilder = HttpUrl.parse(baseUrl + endpoint).newBuilder()
.addQueryParameter("organization_id", organizationId);
if (params != null) {
for (Map.Entry<String, String> param : params.entrySet()) {
urlBuilder.addQueryParameter(param.getKey(), param.getValue());
}
}
Request request = new Request.Builder()
.url(urlBuilder.build())
.addHeader("Authorization", "Zoho-oauthtoken " + accessToken)
.delete()
.build();
try (Response response = executeRequest(request)) {
String responseBody = response.body().string();
return objectMapper.readValue(responseBody, responseType);
}
} catch (IOException e) {
throw new ZohoBooksException("DELETE request failed: " + endpoint, e);
}
}
/**
* Get list with pagination
*/
public <T> PaginatedResponse<T> getList(String endpoint, Map<String, String> params, Class<T> itemType) throws ZohoBooksException {
try {
String responseBody = getRawResponse(endpoint, params);
return objectMapper.readValue(responseBody,
objectMapper.getTypeFactory().constructParametricType(PaginatedResponse.class, itemType));
} catch (IOException e) {
throw new ZohoBooksException("Failed to get list: " + endpoint, e);
}
}
/**
* Get raw response as string
*/
public String getRawResponse(String endpoint, Map<String, String> params) throws ZohoBooksException {
try {
HttpUrl.Builder urlBuilder = HttpUrl.parse(baseUrl + endpoint).newBuilder()
.addQueryParameter("organization_id", organizationId);
if (params != null) {
for (Map.Entry<String, String> param : params.entrySet()) {
urlBuilder.addQueryParameter(param.getKey(), param.getValue());
}
}
Request request = new Request.Builder()
.url(urlBuilder.build())
.addHeader("Authorization", "Zoho-oauthtoken " + accessToken)
.get()
.build();
try (Response response = executeRequest(request)) {
return response.body().string();
}
} catch (IOException e) {
throw new ZohoBooksException("Failed to get raw response: " + endpoint, e);
}
}
// Custom exception
public static class ZohoBooksException extends Exception {
private final int statusCode;
private final String errorCode;
public ZohoBooksException(String message) {
super(message);
this.statusCode = 0;
this.errorCode = null;
}
public ZohoBooksException(String message, Throwable cause) {
super(message, cause);
this.statusCode = 0;
this.errorCode = null;
}
public ZohoBooksException(String message, int statusCode) {
super(message);
this.statusCode = statusCode;
this.errorCode = null;
}
public ZohoBooksException(String message, int statusCode, String errorCode) {
super(message);
this.statusCode = statusCode;
this.errorCode = errorCode;
}
public int getStatusCode() {
return statusCode;
}
public String getErrorCode() {
return errorCode;
}
}
}
Data Models
Core Data Classes
```java
package com.zohobooks.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;
// Base Response Models
@JsonIgnoreProperties(ignoreUnknown = true)
class BaseResponse {
private int code;
private String message;
// Getters and setters
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; }
}
@JsonIgnoreProperties(ignoreUnknown = true)
class PaginatedResponse {
private int code;
private String message;
private PageContext pageContext;
private List data;
// Getters and setters
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 PageContext getPageContext() { return pageContext; }
public void setPageContext(PageContext pageContext) { this.pageContext = pageContext; }
public List<T> getData() { return data; }
public void setData(List<T> data) { this.data = data; }
}
@JsonIgnoreProperties(ignoreUnknown = true)
class PageContext {
private int page;
private int perPage;
private boolean hasMorePage;
private String reportName;
private String appliedFilter;
private String sortColumn;
private String sortOrder;
// Getters and setters
public int getPage() { return page; }
public void setPage(int page) { this.page = page; }
public int getPerPage() { return perPage; }
public void setPerPage(int perPage) { this.perPage = perPage; }
public boolean isHasMorePage() { return hasMorePage; }
public void setHasMorePage(boolean hasMorePage) { this.hasMorePage = hasMorePage; }
public String getReportName() { return reportName; }
public void setReportName(String reportName) { this.reportName = reportName; }
public String getAppliedFilter() { return appliedFilter; }
public void setAppliedFilter(String appliedFilter) { this.appliedFilter = appliedFilter; }
public String getSortColumn() { return sortColumn; }
public void setSortColumn(String sortColumn) { this.sortColumn = sortColumn; }
public String getSortOrder() { return sortOrder; }
public void setSortOrder(String sortOrder) { this.sortOrder = sortOrder; }
}
@JsonIgnoreProperties(ignoreUnknown = true)
class ErrorResponse {
private int code;
private String message;
private Map details;
// Getters and setters
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 Map<String, Object> getDetails() { return details; }
public void setDetails(Map<String, Object> details) { this.details = details; }
}
// Organization Models
@JsonIgnoreProperties(ignoreUnknown = true)
class Organization {
private String organizationId;
private String name;
private String contactName;
private String email;
private String languageCode;
private String locale;
private String timeZone;
private String currencyCode;
private String currencySymbol;
private String currencyFormat;
private int decimalPlaces;
private int yearEndMonth;
private String dateFormat;
private String fieldSeparator;
private String industryType;
private String industrySize;
private String companyIdLabel;
private String companyIdValue;
private String taxIdLabel;
private String taxIdValue;
private Address address;
private String orgAddress;
private String remitToAddress;
private String phone;
private String fax;
private String website;
private String fiscalYearStartMonth;
private boolean isOrgActive;
private boolean isDefaultOrg;
// Getters and setters
public String getOrganizationId() { return organizationId; }
public void setOrganizationId(String organizationId) { this.organizationId = organizationId; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getContactName() { return contactName; }
public void setContactName(String contactName) { this.contactName = contactName; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getLanguageCode() { return languageCode; }
public void setLanguageCode(String languageCode) { this.languageCode = languageCode; }
public String getLocale() { return locale; }
public void setLocale(String locale) { this.locale = locale; }
public String getTimeZone() { return timeZone; }
public void setTimeZone(String timeZone) { this.timeZone = timeZone; }
public String getCurrencyCode() { return currencyCode; }
public void setCurrencyCode(String currencyCode) { this.currencyCode = currencyCode; }
public String getCurrencySymbol() { return currencySymbol; }
public void setCurrencySymbol(String currencySymbol) { this.currencySymbol = currencySymbol; }
public String getCurrencyFormat() { return currencyFormat; }
public void setCurrencyFormat(String currencyFormat) { this.currencyFormat = currencyFormat; }
public int getDecimalPlaces() { return decimalPlaces; }
public void setDecimalPlaces(int decimalPlaces) { this.decimalPlaces = decimalPlaces; }
public int getYearEndMonth() { return yearEndMonth; }
public void setYearEndMonth(int yearEndMonth) { this.yearEndMonth = yearEndMonth; }
public String getDateFormat() { return dateFormat; }
public void setDateFormat(String dateFormat) { this.dateFormat = dateFormat; }
public String getFieldSeparator() { return fieldSeparator; }
public void setFieldSeparator(String fieldSeparator) { this.fieldSeparator = fieldSeparator; }
public String getIndustryType() { return industryType; }
public void setIndustryType(String industryType) { this.industryType = industryType; }
public String getIndustrySize() { return industrySize; }
public void setIndustrySize(String industrySize) { this.industrySize = industrySize; }
public String getCompanyIdLabel() { return companyIdLabel; }
public void setCompanyIdLabel(String companyIdLabel) { this.companyIdLabel = companyIdLabel; }
public String getCompanyIdValue() { return companyIdValue; }
public void setCompanyIdValue(String companyIdValue) { this.companyIdValue = companyIdValue; }
public String getTaxIdLabel() { return taxIdLabel; }
public void setTaxIdLabel(String taxIdLabel) { this.taxIdLabel = taxIdLabel; }
public String getTaxIdValue() { return taxIdValue; }
public void setTaxIdValue(String taxIdValue) { this.taxIdValue = taxIdValue; }
public Address getAddress() { return address; }
public void setAddress(Address address) { this.address = address; }
public String getOrgAddress() { return orgAddress; }
public void setOrgAddress(String orgAddress) { this.orgAddress = orgAddress; }
public String getRemitToAddress() { return remitToAddress; }
public void setRemitToAddress(String remitToAddress) { this.remitToAddress = remitToAddress; }
public String getPhone() { return phone; }
public void setPhone(String phone) { this.phone = phone; }
public String getFax() { return fax; }
public void setFax(String fax) { this.fax = fax; }
public String getWebsite() { return website; }
public void setWebsite(String website) { this.website = website; }
public String getFiscalYearStartMonth() { return fiscalYearStartMonth; }
public void setFiscalYearStartMonth(String fiscalYearStartMonth) { this.fiscalYearStartMonth = fiscalYearStartMonth; }
public boolean isOrgActive() { return isOrgActive; }
public void setOrgActive(boolean orgActive) { isOrgActive = orgActive; }
public boolean isDefaultOrg() { return isDefaultOrg; }
public void setDefaultOrg(boolean defaultOrg) { isDefaultOrg = defaultOrg; }
}
// Contact Models
@JsonIgnoreProperties(ignoreUnknown = true)
class Contact {
private String contactId;
private String contactName;
private String companyName;
private boolean hasTransaction;
private String contactType;
private boolean isCrmCustomer;
private String primaryContactId;
private String paymentTerms;
private int paymentTermsLabel;
private String currencyId;
private String currencyCode;
private String currencySymbol;
private double outstandingReceivableAmount;
private double outstandingReceivableAmountBcy;
private double outstandingPayableAmount;
private double outstandingPayableAmountBcy;
private double unusedCreditsReceivableAmount;
private double unusedCreditsReceivableAmountBcy;
private double unusedCreditsPayableAmount;
private double unusedCreditsPayableAmountBcy;
private String status;
private boolean paymentReminderEnabled;
private String customFields;
private String billingAddress;
private String shippingAddress;
private List contactPersons;
private String defaultTaxes;
private String notes;
private String createdAt;
private String lastModifiedAt;
// Getters and setters
public String getContactId() { return contactId; }
public void setContactId(String contactId) { this.contactId = contactId; }
public String getContactName() { return contactName; }
public void setContactName(String contactName) { this.contactName = contactName; }
public String getCompanyName() { return companyName; }
public void setCompanyName(String companyName) { this.companyName = companyName; }
public boolean isHasTransaction() { return hasTransaction; }
public void setHasTransaction(boolean hasTransaction) { this.hasTransaction = hasTransaction; }
public String getContactType() { return contactType; }
public void setContactType(String contactType) { this.contactType = contactType; }
public boolean isCrmCustomer() { return isCrmCustomer; }
public void setCrmCustomer(boolean crmCustomer) { isCrmCustomer = crmCustomer; }
public String getPrimaryContactId() { return primaryContactId; }
public void setPrimaryContactId(String primaryContactId) { this.primaryContactId = primaryContactId; }
public String getPaymentTerms() { return paymentTerms; }
public void setPaymentTerms(String paymentTerms) { this.paymentTerms = paymentTerms; }
public int getPaymentTermsLabel() { return paymentTermsLabel; }
public void setPaymentTermsLabel(int paymentTermsLabel) { this.paymentTermsLabel = paymentTermsLabel; }
public String getCurrencyId() { return currencyId; }
public void setCurrencyId(String currencyId) { this.currencyId = currencyId; }
public String getCurrencyCode() { return currencyCode; }
public void setCurrencyCode(String currencyCode) { this.currencyCode = currencyCode; }
public String getCurrencySymbol() { return currencySymbol; }
public void setCurrencySymbol(String currencySymbol) { this.currencySymbol = currencySymbol; }
public double getOutstandingReceivableAmount() { return outstandingReceivableAmount; }
public void setOutstandingReceivableAmount(double outstandingReceivableAmount) { this.outstandingReceivableAmount = outstandingReceivableAmount; }
public double getOutstandingReceivableAmountBcy() { return outstandingReceivableAmountBcy; }
public void setOutstandingReceivableAmountBcy(double outstandingReceivableAmountBcy) { this.outstandingReceivableAmountBcy = outstandingReceivableAmountBcy; }
public double getOutstandingPayableAmount() { return outstandingPayableAmount; }
public void setOutstandingPayableAmount(double outstandingPayableAmount) { this.outstandingPayableAmount = outstandingPayableAmount; }
public double getOutstandingPayableAmountBcy() { return outstandingPayableAmountBcy; }
public void setOutstandingPayableAmountBcy(double outstandingPayableAmountBcy) { this.outstandingPayableAmountBcy = outstandingPayableAmountBcy; }
public double getUnusedCreditsReceivableAmount() { return unusedCreditsReceivableAmount; }
public void setUnusedCreditsReceivableAmount(double unusedCreditsReceivableAmount) { this.unusedCreditsReceivableAmount = unusedCreditsReceivableAmount; }
public double getUnusedCreditsReceivableAmountBcy() { return unusedCreditsReceivableAmountBcy; }
public void setUnusedCreditsReceivableAmountBcy(double unusedCreditsReceivableAmountBcy) { this.unusedCreditsReceivableAmountBcy = unusedCreditsReceivableAmountBcy; }
public double getUnusedCreditsPayableAmount() { return unusedCreditsPayableAmount; }
public void setUnusedCreditsPayableAmount(double unusedCreditsPayableAmount) { this.unusedCreditsPayableAmount = unusedCreditsPayableAmount; }
public double getUnusedCreditsPayableAmountBcy() { return unusedCreditsPayableAmountBcy; }
public void setUnusedCreditsPayableAmountBcy(double unusedCreditsPayableAmountBcy) { this.unusedCreditsPayableAmountBcy = unusedCreditsPayableAmountBcy; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public boolean isPaymentReminderEnabled() { return paymentReminderEnabled; }
public void setPaymentReminderEnabled(boolean paymentReminderEnabled) { this.paymentReminderEnabled = paymentReminderEnabled; }
public String getCustomFields() { return customFields; }
public void setCustomFields(String customFields) { this.customFields = customFields; }
public String getBillingAddress() { return billingAddress; }
public void setBillingAddress(String billingAddress) { this.billingAddress = billingAddress; }
public String getShippingAddress() { return shippingAddress; }
public void setShippingAddress(String shippingAddress) { this.shippingAddress = shippingAddress; }
public List<ContactPerson> getContactPersons() { return contactPersons; }
public void setContactPersons(List<ContactPerson> contactPersons) { this.contactPersons = contactPersons; }
public String getDefaultTaxes() { return defaultTaxes; }
public void setDefaultTaxes(String defaultTaxes) { this.defaultTaxes = defaultTaxes; }
public String getNotes() { return notes; }
public void setNotes(String notes) { this.notes = notes; }
public String getCreatedAt() { return createdAt; }
public void setCreatedAt(String createdAt) { this.createdAt = createdAt; }
public String getLastModifiedAt() { return lastModifiedAt; }
public void setLastModifiedAt(String lastModifiedAt) { this.lastModifiedAt = lastModifiedAt; }
}
@JsonIgnoreProperties(ignoreUnknown = true)
class ContactPerson {
private String contactPersonId;
private String salutation;
private String firstName;
private String lastName;
private String email;
private String phone;
private String mobile;
private boolean isPrimaryContact;
// Getters and setters
public String getContactPersonId() { return contactPersonId; }
public void setContactPersonId(String contactPersonId) { this.contactPersonId = contactPersonId; }
public String getSalutation() { return salutation; }
public void setSalutation(String salutation) { this.salutation = salutation; }
public String getFirstName() { return firstName; }
public void setFirstName(String firstName) { this.firstName = firstName; }
public String getLastName() { return lastName; }
public void setLastName(String lastName) { this.lastName = lastName; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getPhone() { return phone; }
public void setPhone(String phone) { this.phone = phone; }
public String getMobile() { return mobile; }
public void setMobile(String mobile) { this.mobile = mobile; }
public boolean isPrimaryContact() { return isPrimaryContact; }
public void setPrimaryContact(boolean primaryContact) { isPrimaryContact = primaryContact; }
}
// Invoice Models
@JsonIgnoreProperties(ignoreUnknown = true)
class Invoice {
private String invoiceId;
private String customerId;
private String customerName;
private String contactPersons;
private String invoiceNumber;
private String referenceNumber;
private String placeOfSupply;
private String gstTreatment;
private String gstNo;
private String date;
private String dueDate;
private String dueDays;
private String paymentTerms;
private int paymentTermsLabel;
private String currencyId;
private String currencyCode;
private String currencySymbol;
private double exchangeRate;
private double discount;
private boolean isDiscountBeforeTax;
private String discountType;
private String entityDiscount;
private String itemDiscount;
private String salespersonId;
private String salespersonName;
private String shippingCharge;
private String adjustment;
private String adjustmentDescription;
private double subTotal;
private double taxTotal;
private double total;
private double totalInWords;
private double balance;
private boolean hasAttachment;
private double pricePrecision;
private String paymentOptions;
private boolean allowPartialPayments;
private String customFields;
private String templateId;
private String templateName;
private String notes;
private String terms;
private String shippingAddress;
private String billingAddress;
private String templateType;
private String createdTime;
private String lastModifiedTime;
private String status;
private String invoiceUrl;
private List lineItems;
private List taxes;
private List payments;
private List customFieldHash;
// Getters and setters
public String getInvoiceId() { return invoiceId; }
public void setInvoiceId(String invoiceId) { this.invoiceId = invoiceId; }
public String getCustomerId() { return customerId; }
public void setCustomerId(String customerId) { this.customerId = customerId; }
public String getCustomerName() { return customerName; }
public void setCustomerName(String customerName) { this.customerName = customerName; }
public String getContactPersons() { return contactPersons; }
public void setContactPersons(String contactPersons) { this.contactPersons = contactPersons; }
public String getInvoiceNumber() { return invoiceNumber; }
public void setInvoiceNumber(String invoiceNumber) { this.invoiceNumber = invoiceNumber; }
public String getReferenceNumber() { return referenceNumber; }
public void setReferenceNumber(String referenceNumber) { this.referenceNumber = referenceNumber; }
public String getPlaceOfSupply() { return placeOfSupply; }
public void setPlaceOfSupply(String placeOfSupply) { this.placeOfSupply = placeOfSupply; }
public String getGstTreatment() { return gstTreatment; }
public void setGstTreatment(String gstTreatment) { this.gstTreatment = gstTreatment; }
public String getGstNo() { return gstNo; }
public void setGstNo(String gstNo) { this.gstNo = gstNo; }
public String getDate() { return date; }
public void setDate(String date) { this.date = date; }
public String getDueDate() { return dueDate; }
public void setDueDate(String dueDate) { this.dueDate = dueDate; }
public String getDueDays() { return dueDays; }
public void setDueDays(String dueDays) { this.dueDays = dueDays; }
public String getPaymentTerms() { return paymentTerms; }
public void setPaymentTerms(String paymentTerms) { this.paymentTerms = paymentTerms; }
public int getPaymentTermsLabel() { return paymentTermsLabel; }
public void setPaymentTermsLabel(int paymentTermsLabel) { this.paymentTermsLabel = paymentTermsLabel; }
public String getCurrencyId() { return currencyId; }
public void setCurrencyId(String currencyId) { this.currencyId = currencyId; }
public String getCurrencyCode() { return currencyCode; }
public void setCurrencyCode(String currencyCode) { this.currencyCode = currencyCode; }
public String getCurrencySymbol() { return currencySymbol; }
public void setCurrencySymbol(String currencySymbol) { this.currencySymbol = currencySymbol; }
public double getExchangeRate() { return exchangeRate; }
public void setExchangeRate(double exchangeRate) { this.exchangeRate = exchangeRate; }
public double getDiscount() { return discount; }
public void setDiscount(double discount) { this.discount = discount; }
public boolean isDiscountBeforeTax() { return isDiscountBeforeTax; }
public void setDiscountBeforeTax(boolean discountBeforeTax) { isDiscountBeforeTax = discountBeforeTax; }
public String getDiscountType() { return discountType; }
public void setDiscountType(String discountType) { this.discountType = discountType; }
public String getEntityDiscount() { return entityDiscount; }
public void setEntityDiscount(String entityDiscount) { this.entityDiscount = entityDiscount; }
public String getItemDiscount() { return itemDiscount; }
public void setItemDiscount(String itemDiscount) { this.itemDiscount = itemDiscount; }
public String getSalespersonId() { return salespersonId; }
public void setSalespersonId(String salespersonId) { this.salespersonId = salespersonId; }
public String getSalespersonName() { return salespersonName; }
public void setSalespersonName(String salespersonName) { this.salespersonName = salespersonName; }
public String getShippingCharge() { return shippingCharge; }
public void setShippingCharge(String shippingCharge) { this.shippingCharge = shippingCharge; }
public String getAdjustment() { return adjustment; }
public void setAdjustment(String adjustment) { this.adjustment = adjustment; }
public String getAdjustmentDescription() { return adjustmentDescription; }
public void setAdjustmentDescription(String adjustmentDescription) { this.adjustmentDescription = adjustmentDescription; }
public double getSubTotal() { return subTotal; }
public void setSubTotal(double subTotal) { this.subTotal = subTotal; }
public double getTaxTotal() { return taxTotal; }
public void setTaxTotal(double taxTotal) { this.taxTotal = taxTotal; }
public double getTotal() { return total; }
public void setTotal(double total) { this.total = total; }
public double getTotalInWords() { return totalInWords; }
public void setTotalInWords(double totalInWords) { this.totalInWords = totalInWords; }
public double getBalance() { return balance; }
public void setBalance(double balance) { this.balance = balance; }
public boolean isHasAttachment() { return hasAttachment; }
public void setHasAttachment(boolean hasAttachment) { this.hasAttachment = hasAttachment; }
public double getPricePrecision() { return pricePrecision; }
public void setPricePrecision(double pricePrecision) { this.pricePrecision = pricePrecision; }
Java Observability, Logging Intelligence & AI-Driven Monitoring (APM, Tracing, Logs & Anomaly Detection)
https://macronepal.com/blog/beyond-metrics-observing-serverless-and-traditional-java-applications-with-thundra-apm/
Explains using Thundra APM to observe both serverless and traditional Java applications by combining tracing, metrics, and logs into a unified observability platform for faster debugging and performance insights.
https://macronepal.com/blog/dynatrace-oneagent-in-java-2/
Explains Dynatrace OneAgent for Java, which automatically instruments JVM applications to capture metrics, traces, and logs, enabling full-stack monitoring and root-cause analysis with minimal configuration.
https://macronepal.com/blog/lightstep-java-sdk-distributed-tracing-and-observability-implementation/
Explains Lightstep Java SDK for distributed tracing, helping developers track requests across microservices and identify latency issues using OpenTelemetry-based observability.
https://macronepal.com/blog/honeycomb-io-beeline-for-java-complete-guide-2/
Explains Honeycomb Beeline for Java, which provides high-cardinality observability and deep query capabilities to understand complex system behavior and debug distributed systems efficiently.
https://macronepal.com/blog/lumigo-for-serverless-in-java-complete-distributed-tracing-guide-2/
Explains Lumigo for Java serverless applications, offering automatic distributed tracing, log correlation, and error tracking to simplify debugging in cloud-native environments. (Lumigo Docs)
https://macronepal.com/blog/from-noise-to-signals-implementing-log-anomaly-detection-in-java-applications/
Explains how to detect anomalies in Java logs using behavioral patterns and machine learning techniques to separate meaningful incidents from noisy log data and improve incident response.
https://macronepal.com/blog/ai-powered-log-analysis-in-java-from-reactive-debugging-to-proactive-insights/
Explains AI-driven log analysis for Java applications, shifting from manual debugging to predictive insights that identify issues early and improve system reliability using intelligent log processing.
https://macronepal.com/blog/titliel-java-logging-best-practices/
Explains best practices for Java logging, focusing on structured logs, proper log levels, performance optimization, and ensuring logs are useful for debugging and observability systems.
https://macronepal.com/blog/seeking-a-loguru-for-java-the-quest-for-elegant-and-simple-logging/
Explains the search for simpler, more elegant logging frameworks in Java, comparing modern logging approaches that aim to reduce complexity while improving readability and developer experience.