Java tutorial
/* * Copyright 2015 Groupon, Inc * Copyright 2015 The Billing Project, LLC * * The Billing Project licenses this file to you under the Apache License, version 2.0 * (the "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package org.killbill.billing.plugin.avatax.dao; import java.io.IOException; import java.math.BigDecimal; import java.sql.Connection; import java.sql.SQLException; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import javax.annotation.Nullable; import javax.sql.DataSource; import org.joda.time.DateTime; import org.jooq.Configuration; import org.jooq.DSLContext; import org.jooq.TransactionalRunnable; import org.jooq.impl.DSL; import org.killbill.billing.invoice.api.InvoiceItem; import org.killbill.billing.plugin.avatax.client.model.CommonResponse; import org.killbill.billing.plugin.avatax.client.model.GetTaxResult; import org.killbill.billing.plugin.avatax.client.model.TaxRateResult; import org.killbill.billing.plugin.avatax.dao.gen.tables.records.AvataxResponsesRecord; import org.killbill.billing.plugin.avatax.dao.gen.tables.records.AvataxTaxCodesRecord; import org.killbill.billing.plugin.dao.PluginDao; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fasterxml.jackson.core.type.TypeReference; import com.google.common.base.Function; import com.google.common.base.Strings; import com.google.common.collect.Maps; import static org.killbill.billing.plugin.avatax.dao.gen.tables.AvataxResponses.AVATAX_RESPONSES; import static org.killbill.billing.plugin.avatax.dao.gen.tables.AvataxTaxCodes.AVATAX_TAX_CODES; public class AvaTaxDao extends PluginDao { private static final Logger logger = LoggerFactory.getLogger(AvaTaxDao.class); private static final String SUCCESS = CommonResponse.SeverityLevel.Success.name(); public AvaTaxDao(final DataSource dataSource) throws SQLException { super(dataSource); } public void setTaxCode(final String productName, @Nullable final String taxCode, final DateTime utcNow, final UUID kbTenantId) throws SQLException { execute(dataSource.getConnection(), new WithConnectionCallback<Void>() { @Override public Void withConnection(final Connection conn) throws SQLException { DSL.using(conn, dialect, settings).transaction(new TransactionalRunnable() { @Override public void run(final Configuration configuration) throws Exception { final DSLContext dslContext = DSL.using(configuration); dslContext.delete(AVATAX_TAX_CODES).where(AVATAX_TAX_CODES.PRODUCT_NAME.equal(productName)) .and(AVATAX_TAX_CODES.KB_TENANT_ID.equal(kbTenantId.toString())).execute(); if (taxCode != null) { dslContext .insertInto(AVATAX_TAX_CODES, AVATAX_TAX_CODES.PRODUCT_NAME, AVATAX_TAX_CODES.TAX_CODE, AVATAX_TAX_CODES.CREATED_DATE, AVATAX_TAX_CODES.KB_TENANT_ID) .values(productName, taxCode, toTimestamp(utcNow), kbTenantId.toString()) .execute(); } } }); return null; } }); } public List<AvataxTaxCodesRecord> getTaxCodes(final UUID kbTenantId) throws SQLException { return execute(dataSource.getConnection(), new WithConnectionCallback<List<AvataxTaxCodesRecord>>() { @Override public List<AvataxTaxCodesRecord> withConnection(final Connection conn) throws SQLException { return DSL.using(conn, dialect, settings).selectFrom(AVATAX_TAX_CODES) .where(AVATAX_TAX_CODES.KB_TENANT_ID.equal(kbTenantId.toString())) .orderBy(AVATAX_TAX_CODES.RECORD_ID.asc()).fetch(); } }); } public String getTaxCode(final String productName, final UUID kbTenantId) throws SQLException { final AvataxTaxCodesRecord record = execute(dataSource.getConnection(), new WithConnectionCallback<AvataxTaxCodesRecord>() { @Override public AvataxTaxCodesRecord withConnection(final Connection conn) throws SQLException { return DSL.using(conn, dialect, settings).selectFrom(AVATAX_TAX_CODES) .where(AVATAX_TAX_CODES.PRODUCT_NAME.equal(productName)) .and(AVATAX_TAX_CODES.KB_TENANT_ID.equal(kbTenantId.toString())) .orderBy(AVATAX_TAX_CODES.RECORD_ID.asc()).fetchAny(); } }); if (record == null) { return null; } else { return record.getTaxCode(); } } public void addResponse(final UUID kbAccountId, final UUID kbInvoiceId, final Map<UUID, Iterable<InvoiceItem>> kbInvoiceItems, final TaxRateResult taxRateResult, final DateTime utcNow, final UUID kbTenantId) throws SQLException { execute(dataSource.getConnection(), new WithConnectionCallback<Void>() { @Override public Void withConnection(final Connection conn) throws SQLException { DSL.using(conn, dialect, settings) .insertInto(AVATAX_RESPONSES, AVATAX_RESPONSES.KB_ACCOUNT_ID, AVATAX_RESPONSES.KB_INVOICE_ID, AVATAX_RESPONSES.KB_INVOICE_ITEM_IDS, AVATAX_RESPONSES.TOTAL_TAX, AVATAX_RESPONSES.RESULT_CODE, AVATAX_RESPONSES.CREATED_DATE, AVATAX_RESPONSES.KB_TENANT_ID) .values(kbAccountId.toString(), kbInvoiceId.toString(), kbInvoiceItemsIdsAsString(kbInvoiceItems), BigDecimal.valueOf(taxRateResult.totalRate), SUCCESS, toTimestamp(utcNow), kbTenantId.toString()) .execute(); return null; } }); } public void addResponse(final UUID kbAccountId, final UUID kbInvoiceId, final Map<UUID, Iterable<InvoiceItem>> kbInvoiceItems, final GetTaxResult taxResult, final DateTime utcNow, final UUID kbTenantId) throws SQLException { execute(dataSource.getConnection(), new WithConnectionCallback<Void>() { @Override public Void withConnection(final Connection conn) throws SQLException { DSL.using(conn, dialect, settings) .insertInto(AVATAX_RESPONSES, AVATAX_RESPONSES.KB_ACCOUNT_ID, AVATAX_RESPONSES.KB_INVOICE_ID, AVATAX_RESPONSES.KB_INVOICE_ITEM_IDS, AVATAX_RESPONSES.DOC_CODE, AVATAX_RESPONSES.DOC_DATE, AVATAX_RESPONSES.TIMESTAMP, AVATAX_RESPONSES.TOTAL_AMOUNT, AVATAX_RESPONSES.TOTAL_DISCOUNT, AVATAX_RESPONSES.TOTAL_EXEMPTION, AVATAX_RESPONSES.TOTAL_TAXABLE, AVATAX_RESPONSES.TOTAL_TAX, AVATAX_RESPONSES.TOTAL_TAX_CALCULATED, AVATAX_RESPONSES.TAX_DATE, AVATAX_RESPONSES.TAX_LINES, AVATAX_RESPONSES.TAX_SUMMARY, AVATAX_RESPONSES.TAX_ADDRESSES, AVATAX_RESPONSES.RESULT_CODE, AVATAX_RESPONSES.MESSAGES, AVATAX_RESPONSES.ADDITIONAL_DATA, AVATAX_RESPONSES.CREATED_DATE, AVATAX_RESPONSES.KB_TENANT_ID) .values(kbAccountId.toString(), kbInvoiceId.toString(), kbInvoiceItemsIdsAsString(kbInvoiceItems), taxResult.DocCode, toTimestamp(taxResult.DocDate), toTimestamp(taxResult.Timestamp), BigDecimal.valueOf(taxResult.TotalAmount), BigDecimal.valueOf(taxResult.TotalDiscount), BigDecimal.valueOf(taxResult.TotalExemption), BigDecimal.valueOf(taxResult.TotalTaxable), BigDecimal.valueOf(taxResult.TotalTax), BigDecimal.valueOf(taxResult.TotalTaxCalculated), toTimestamp(taxResult.TaxDate), asString(taxResult.TaxLines), asString(taxResult.TaxSummary), asString(taxResult.TaxAddresses), taxResult.ResultCode == null ? null : taxResult.ResultCode.name(), asString(taxResult.Messages), null, toTimestamp(utcNow), kbTenantId.toString()) .execute(); return null; } }); } // TODO Check committed docs only - but DocStatus isn't returned? public List<AvataxResponsesRecord> getSuccessfulResponses(final UUID invoiceId, final UUID kbTenantId) throws SQLException { return execute(dataSource.getConnection(), new WithConnectionCallback<List<AvataxResponsesRecord>>() { @Override public List<AvataxResponsesRecord> withConnection(final Connection conn) throws SQLException { return DSL.using(conn, dialect, settings).selectFrom(AVATAX_RESPONSES) .where(AVATAX_RESPONSES.KB_INVOICE_ID.equal(invoiceId.toString())) .and(AVATAX_RESPONSES.RESULT_CODE.equal(SUCCESS)) .and(AVATAX_RESPONSES.KB_TENANT_ID.equal(kbTenantId.toString())) .orderBy(AVATAX_RESPONSES.RECORD_ID.asc()).fetch(); } }); } /** * Retrieve all taxed items (successfully committed in AvaTax), along with the associated adjustments that have already * been taken into account. * * @param responses existing AvaTax responses * @return Mapping between taxed invoice item ids and associated adjustments (if any) */ public Map<UUID, Set<UUID>> getTaxedItemsWithAdjustments(final Iterable<AvataxResponsesRecord> responses) { final Map<UUID, Set<UUID>> kbInvoiceItemsIds = new HashMap<UUID, Set<UUID>>(); for (final AvataxResponsesRecord response : responses) { try { kbInvoiceItemsIdsFromString(response.getKbInvoiceItemIds(), kbInvoiceItemsIds); } catch (final IOException e) { logger.warn("Corrupted entry for response record_id {}: {}", response.getRecordId(), response.getKbInvoiceItemIds()); } } return kbInvoiceItemsIds; } private void kbInvoiceItemsIdsFromString(@Nullable final String kbInvoiceItemsIdsAsString, final Map<UUID, Set<UUID>> kbInvoiceItemsIds) throws IOException { if (Strings.emptyToNull(kbInvoiceItemsIdsAsString) != null) { final Map<UUID, Set<UUID>> kbInvoiceItemsIdsAsMap = objectMapper.readValue(kbInvoiceItemsIdsAsString, new TypeReference<Map<UUID, Set<UUID>>>() { }); for (final UUID kbInvoiceItemId : kbInvoiceItemsIdsAsMap.keySet()) { if (kbInvoiceItemsIds.get(kbInvoiceItemId) == null) { kbInvoiceItemsIds.put(kbInvoiceItemId, new HashSet<UUID>()); } kbInvoiceItemsIds.get(kbInvoiceItemId).addAll(kbInvoiceItemsIdsAsMap.get(kbInvoiceItemId)); } } } private String kbInvoiceItemsIdsAsString(final Map<UUID, Iterable<InvoiceItem>> kbInvoiceItems) throws SQLException { final Map<UUID, Set<UUID>> kbInvoiceItemsIds = Maps.<UUID, Iterable<InvoiceItem>, Set<UUID>>transformValues( kbInvoiceItems, new Function<Iterable<InvoiceItem>, Set<UUID>>() { @Override public Set<UUID> apply(final Iterable<InvoiceItem> invoiceItems) { final Set<UUID> invoiceItemIds = new HashSet<UUID>(); for (final InvoiceItem invoiceItem : invoiceItems) { invoiceItemIds.add(invoiceItem.getId()); } return invoiceItemIds; } }); return asString(kbInvoiceItemsIds); } }