/**
   * Batch Fetch processor.
   *
   * <p>Fetches a Batch result
   *
   * <p>{@sample.xml ../../../doc/avalara-connector.xml.sample avalara:fetch-batch-file}
   *
   * @param batchId The numerical identifier of the BatchFile.
   * @return The {@link Map<String,BatchFileFetchResult>}
   * @throws AvalaraRuntimeException
   */
  @Processor
  public Map<String, BatchFileFetchResult> fetchBatchFile(String batchId) {
    // This Request is needed to retrieve the batch file ids. The actual content cannot be retrieved
    // at once.
    FetchRequest batchFetchRequest = new FetchRequest();
    batchFetchRequest.setFields("Files");
    batchFetchRequest.setFilters("BatchId=" + batchId);
    BatchFetchResult batchFetchResult =
        apiClient.sendBatchRequestToAvalara(BatchRequestType.BatchFetch, batchFetchRequest);
    Map<String, BatchFileFetchResult> resultHashMap = new HashMap<String, BatchFileFetchResult>();
    // This is in order to be able to return result and error file to the caller.
    for (BatchFile bf : batchFetchResult.getBatches().getBatch().get(0).getFiles().getBatchFile()) {
      if (bf.getName().equalsIgnoreCase("Result")) {
        FetchRequest fetchRequest = new FetchRequest();
        fetchRequest.setFields("*,Content");
        fetchRequest.setFilters("BatchFileId=" + bf.getBatchFileId());
        resultHashMap.put(
            "result",
            (BatchFileFetchResult)
                apiClient.sendBatchRequestToAvalara(BatchRequestType.BatchFileFetch, fetchRequest));
      } else if (bf.getName().equalsIgnoreCase("Error")) {
        FetchRequest fetchRequest = new FetchRequest();
        fetchRequest.setFields("*,Content");
        fetchRequest.setFilters("BatchFileId=" + bf.getBatchFileId());
        resultHashMap.put(
            "error",
            (BatchFileFetchResult)
                apiClient.sendBatchRequestToAvalara(BatchRequestType.BatchFileFetch, fetchRequest));
      }
    }

    return resultHashMap;
  }
  /**
   * Post Tax processor
   *
   * <p>{@sample.xml ../../../doc/avalara-connector.xml.sample avalara:post-tax}
   *
   * @param docId The original document's type, such as Sales Invoice or Purchase Invoice.
   * @param companyCode Client application company reference code. If docId is specified, this is
   *     not needed.
   * @param docType The document type specifies the category of the document and affects how the
   *     document is treated after a tax calculation; see {@link AvalaraDocumentType} for more
   *     information about the specific document types.
   * @param docCode The internal reference code used by the client application.
   * @param docDate The date on the invoice, purchase order, etc
   * @param totalAmount The total amount (not including tax) for the document. This is used for
   *     verification and reconciliation. This should be the <b>TotalAmount</b> returned by {@link
   *     GetTaxResult} when tax was calculated for this document; otherwise the web service will
   *     return an error. The string represents a {@link BigDecimal}
   * @param totalTax The total tax for the document. This is used for verification and
   *     reconciliation. This should be the <b>TotalTax</b> returned by {@link GetTaxResult} when
   *     tax was calculated for this document; otherwise the web service will return an error. The
   *     string represents a {@link BigDecimal}
   * @param commit The commit value. This has been defaulted to false. If this has been set to true
   *     AvaTax will commit the document on this call. Seller's system who wants to Post and Commit
   *     the document on one call should use this flag.
   * @param newDocCode The new document code value.
   * @return The {@link PostTaxResult}
   * @throws AvalaraRuntimeException
   */
  @Processor
  public PostTaxResult postTax(
      @Optional String docId,
      String companyCode,
      AvalaraDocumentType docType,
      @Optional String docCode,
      XMLGregorianCalendar docDate,
      String totalAmount,
      String totalTax,
      @Default("false") boolean commit,
      @Optional String newDocCode) {
    BigDecimal totalAmountDecimal = totalAmount == null ? null : new BigDecimal(totalAmount);
    BigDecimal totalTaxDecimal = totalTax == null ? null : new BigDecimal(totalTax);

    return (PostTaxResult)
        apiClient.sendTaxRequestToAvalara(
            TaxRequestType.PostTax,
            mom.unmap(
                new MapBuilder()
                    .with("docId", docId)
                    .with("companyCode", companyCode)
                    .with("docType", docType.toDocumentType())
                    .with("docCode", docCode)
                    .with("docDate", docDate)
                    .with("totalAmount", totalAmountDecimal)
                    .with("totalTax", totalTaxDecimal)
                    .with("commit", commit)
                    .with("newDocCode", newDocCode)
                    .build(),
                PostTaxRequest.class));
  }
 /**
  * Has the Batch Processing finished
  *
  * <p>Fetches a Batch result
  *
  * <p>{@sample.xml ../../../doc/avalara-connector.xml.sample avalara:is-batch-finished}
  *
  * @param batchId The numerical identifier of the BatchFile.
  * @return a boolean representing if the Batch finished or not
  * @throws AvalaraRuntimeException
  */
 @Processor
 public boolean isBatchFinished(String batchId) {
   final FetchRequest batchFetchRequest = new FetchRequest();
   batchFetchRequest.setFilters("BatchId=" + batchId);
   BatchFetchResult batchFetchResult =
       apiClient.sendBatchRequestToAvalara(BatchRequestType.BatchFetch, batchFetchRequest);
   if (batchFetchResult.getBatches().getBatch().size() == 0
       || (batchFetchResult.getBatches().getBatch().get(0).getRecordCount()
           > batchFetchResult.getBatches().getBatch().get(0).getCurrentRecord())) {
     return false;
   } else {
     return true;
   }
 }
  /**
   * Validate Address processor.
   *
   * <p>This operation validates the supplied address, returning canonical form and additional
   * delivery details if successfully validated.
   *
   * <p>{@sample.xml ../../../doc/avalara-connector.xml.sample avalara:validate-address}
   *
   * @param line1 Address line 1
   * @param line2 Address line 2
   * @param line3 Address line 3
   * @param city City name. Required, when PostalCode is not specified.
   * @param region State or region name. Requirad, when PostalCode is not specified.
   * @param country Country code
   * @param postalCode Postal or ZIP code. Required, when City and Region are not specified
   * @param addressCode the address code.
   * @param taxRegionId The tax region id.
   * @param latitude Latitude.
   * @param longitude Longitude.
   * @param textCase The casing to apply to the validated address(es).
   * @param coordinates True, if you want in the result a not empty latitud and longitude.
   * @param taxability True, if you want the valid taxRegionId in the result.
   * @param date Date.
   * @return The {@link ValidateResult}
   * @throws AvalaraRuntimeException
   */
  @Processor
  public ValidateResult validateAddress(
      String line1,
      @Optional String line2,
      @Optional String line3,
      @Optional String city,
      @Optional String region,
      @Optional String country,
      @Optional String postalCode,
      @Optional String addressCode,
      Integer taxRegionId,
      @Optional String latitude,
      @Optional String longitude,
      @Default("DEFAULT") TextCaseType textCase,
      @Default("false") boolean coordinates,
      @Default("false") boolean taxability,
      XMLGregorianCalendar date) {
    BaseAddress address = new BaseAddress();
    address.setAddressCode(addressCode);
    address.setCity(city);
    address.setCountry(country);
    address.setLatitude(latitude);
    address.setLine1(line1);
    address.setLine2(line2);
    address.setLine3(line3);
    address.setLongitude(longitude);
    address.setPostalCode(postalCode);
    address.setRegion(region);
    address.setTaxRegionId(taxRegionId);

    return apiClient.sendAddressRequestToAvalara(
        AddressRequestType.Validate,
        (ValidateRequest)
            mom.unmap(
                new MapBuilder()
                    .with("address", address)
                    .with("textCase", textCase.toAvalaraTextCase())
                    .with("coordinates", coordinates)
                    .with("taxability", taxability)
                    .with("date", date)
                    .build(),
                ValidateRequest.class));
  }
 /**
  * Cancel tax, indicating the document that should be canceled and the reason for the operation.
  *
  * <p>{@sample.xml ../../../doc/avalara-connector.xml.sample avalara:cancel-tax}
  *
  * @param docId The original document's type, such as Sales Invoice or Purchase Invoice.
  * @param companyCode Client application company reference code. If docId is specified, this is
  *     not needed.
  * @param docType The document type specifies the category of the document and affects how the
  *     document is treated after a tax calculation; see {@link AvalaraDocumentType} for more
  *     information about the specific document types.
  * @param docCode The internal reference code used by the client application.
  * @param cancelCode A code indicating the reason the document is getting canceled.
  * @return The {@link CancelTaxResult}
  * @throws AvalaraRuntimeException
  */
 @Processor
 public CancelTaxResult cancelTax(
     @Optional String docId,
     String companyCode,
     AvalaraDocumentType docType,
     @Optional String docCode,
     CancelCodeType cancelCode) {
   return (CancelTaxResult)
       apiClient.sendTaxRequestToAvalara(
           TaxRequestType.CancelTax,
           mom.unmap(
               new MapBuilder()
                   .with("docId", docId)
                   .with("companyCode", companyCode)
                   .with("docType", docType.toDocumentType())
                   .with("docCode", docCode)
                   .with("cancelCode", cancelCode.toAvalaraCancelCode())
                   .build(),
               CancelTaxRequest.class));
 }
 /**
  * Get Tax History processor
  *
  * <p>{@sample.xml ../../../doc/avalara-connector.xml.sample avalara:get-tax-history}
  *
  * @param docId The original document's type, such as Sales Invoice or Purchase Invoice.
  * @param companyCode Client application company reference code. If docId is specified, this is
  *     not needed.
  * @param docType The document type specifies the category of the document and affects how the
  *     document is treated after a tax calculation; see {@link AvalaraDocumentType} for more
  *     information about the specific document types.
  * @param docCode The internal reference code used by the client application.
  * @param detailLevel Specifies the level of detail to return. See {@link DetailLevelType}.
  * @return The {@link GetTaxHistoryResult}
  * @throws AvalaraRuntimeException
  */
 @Processor
 public GetTaxHistoryResult getTaxHistory(
     @Optional String docId,
     String companyCode,
     AvalaraDocumentType docType,
     @Optional String docCode,
     DetailLevelType detailLevel) {
   return (GetTaxHistoryResult)
       apiClient.sendTaxRequestToAvalara(
           TaxRequestType.GetTaxHistory,
           mom.unmap(
               new MapBuilder()
                   .with("docId", docId)
                   .with("companyCode", companyCode)
                   .with("docType", docType.toDocumentType())
                   .with("docCode", docCode)
                   .with("detailLevel", detailLevel.toAvalaraDetailLevel())
                   .build(),
               GetTaxHistoryRequest.class));
 }
  /**
   * Batch Save processor.
   *
   * <p>Saves a Batch. Note that you should pass-in your console credentials for this method,
   * instead of the API ones
   *
   * <p>{@sample.xml ../../../doc/avalara-connector.xml.sample avalara:save-batch}
   *
   * @param batchType The kind of records to be imported.
   * @param companyId The id of the company. (Need to be retrived from address bar in Avalara after
   *     hitting the Organization Tab)
   * @param content The content of this import, usually a csv file.
   * @param batchName The name of the batch.
   * @return The {@link BatchSaveResult}
   * @throws AvalaraRuntimeException
   */
  @Processor
  public BatchSaveResult saveBatch(
      BatchType batchType, int companyId, String content, @Optional String batchName) {

    final BatchFile batchFile = new BatchFile();
    batchFile.setContent(content.getBytes()); // cxf takes care of base64 encoding
    batchFile.setContentType("application/csv");
    batchFile.setName(
        batchName + ".csv"); // Must set extension, or Avalara will complain about missing ext
    final ArrayOfBatchFile arrayOfBatchFile = new ArrayOfBatchFile();
    arrayOfBatchFile.getBatchFile().add(batchFile);

    // Batch object to contain the file.
    // Have tested batchfile as well but all examples from Avalara is using this one.
    final Batch batch = new Batch();
    batch.setName(batchName);
    batch.setBatchTypeId(batchType.value());
    batch.setCompanyId(companyId);
    batch.setFiles(arrayOfBatchFile);
    return apiClient.sendBatchRequestToAvalara(BatchRequestType.BatchSave, batch);
  }
 @ConnectionIdentifier
 public String getConnectionIdentifier() {
   return apiClient.getConnectionIdentifier();
 }
 /**
  * Ping Avalara to test connectivity and version of the service.
  *
  * <p>{@sample.xml ../../../doc/avalara-connector.xml.sample avalara:ping}
  *
  * @param message Ping Message
  * @return The {@link PingResult}
  * @throws AvalaraRuntimeException
  */
 @Processor
 public PingResult ping(@Default("Ping") String message) {
   Validate.notNull(message);
   return apiClient.sendTaxRequestToAvalara(TaxRequestType.Ping, message);
 }
  /**
   * Adjust Tax processor.
   *
   * <p>The Get Tax operation calculates tax for one or more invoiced items and displays details
   * describing the calculation of tax for each line item.
   *
   * <p>{@sample.xml ../../../doc/avalara-connector.xml.sample avalara:adjust-tax}
   *
   * @param companyCode Client application company reference code
   * @param adjustmentReason The reason for this tax adjustment.
   * @param adjustmentDescription A description of a tax adjustment.
   * @param docType The document type specifies the category of the document and affects how the
   *     document is treated after a tax calculation; see {@link AvalaraDocumentType} for more
   *     information about the specific document types.
   * @param docCode The internal reference code used by the client application.
   * @param docDate Date of invoice, purchase order, etc.
   * @param salespersonCode The client application salesperson reference code.
   * @param customerCode Client application customer reference code
   * @param customerUsageType Client application customer or usage type. CustomerUsageType
   *     determines the exempt status of the transaction based on the exemption tax rules for the
   *     jurisdictions involved. CustomerUsageType may also be set at the line item level.
   *     <p>The standard values for the CustomerUsageType (A-L).<br>
   *     A Federal Government<br>
   *     B State/Local Govt.<br>
   *     C Tribal Government<br>
   *     D Foreign Diplomat<br>
   *     E Charitable Organization<br>
   *     F Religious/Education<br>
   *     G Resale<br>
   *     H Agricultural Production<br>
   *     I Industrial Prod/Mfg.<br>
   *     J Direct Pay Permit<br>
   *     K Direct Mail<br>
   *     L - Other
   * @param discount The discount amount to apply to the document. The string represents a {@link
   *     BigDecimal}.
   * @param purchaseOrderNo Purchase order identifier. PurchaseOrderNo is required for single use
   *     exemption certificates to match the order and invoice with the certificate.
   * @param exemptionNo Exemption number used for this transaction
   * @param originCode Code that refers one of the address of the baseAddress collection. It has to
   *     be the same code of one of the address's addressCode. It represents the origin address.
   * @param destinationCode Code that refers one of the address of the baseAddress collection. It
   *     has to be the same code of one of the address's addressCode. It represents the destination
   *     address.
   * @param baseAddresses Collection of physical addresses that will be referred to as the
   *     destination or origination of 1 or more invoice line entries
   * @param lines Collection of invoice lines requiring tax calculation
   * @param detailLevel Specifies the level of tax detail to return
   * @param referenceCode For returns (see {@link AvalaraDocumentType}), refers to the {@link
   *     AdjustTaxRequest} of the original invoice.
   * @param locationCode Location Code value. It is Also referred to as a Store Location, Outlet Id,
   *     or Outlet code is a number assigned by the State which identifies a Store location. Some
   *     state returns require taxes are broken out separately for Store Locations.
   * @param commit Commit flag. If Commit is set to true, tax for the transaction is saved, posted
   *     and committed as tax document.
   * @param batchCode The batchCode value.
   * @param taxOverride Indicates to apply tax override to the document.
   * @param currencyCode It is 3 character ISO 4217 currency code.
   * @param serviceMode This is only supported by AvaLocal servers. It provides the ability to
   *     controls whether tax is calculated locally or remotely when using an AvaLocal server. The
   *     default is Automatic which calculates locally unless remote is necessary for non-local
   *     addresses.
   * @param paymentDate The date on which payment was made.
   * @param exchangeRate The exchange rate value. The string represents a {@link BigDecimal}
   * @param exchangeRateEffDate The exchange rate effective date value.
   * @return The {@link AdjustTaxResult}
   * @throws AvalaraRuntimeException
   */
  @Processor
  public AdjustTaxResult adjustTax(
      int adjustmentReason,
      String adjustmentDescription,
      String companyCode,
      AvalaraDocumentType docType,
      @Optional String docCode,
      XMLGregorianCalendar docDate,
      @Optional String salespersonCode,
      String customerCode,
      @Optional String customerUsageType,
      String discount,
      @Optional String purchaseOrderNo,
      @Optional String exemptionNo,
      String originCode,
      String destinationCode,
      List<Map<String, Object>> baseAddresses,
      List<Map<String, Object>> lines,
      DetailLevelType detailLevel,
      @Optional String referenceCode,
      @Optional String locationCode,
      @Default("false") boolean commit,
      @Optional String batchCode,
      @Optional Map<String, Object> taxOverride,
      @Optional String currencyCode,
      @Default("AUTOMATIC") ServiceModeType serviceMode,
      XMLGregorianCalendar paymentDate,
      String exchangeRate,
      XMLGregorianCalendar exchangeRateEffDate) {
    BigDecimal discountDecimal = discount == null ? null : new BigDecimal(discount);
    BigDecimal exchangeRateDecimal = exchangeRate == null ? null : new BigDecimal(exchangeRate);

    Map<String, Object> addresses = null;
    if (baseAddresses != null && !baseAddresses.isEmpty()) {
      addresses = new HashMap<String, Object>();
      addresses.put("baseAddress", baseAddresses);
    }

    Map<String, Object> mapLines = null;
    if (lines != null && !lines.isEmpty()) {
      mapLines = new HashMap<String, Object>();
      mapLines.put("line", lines);
    }
    GetTaxRequest getTaxRequest =
        (GetTaxRequest)
            mom.unmap(
                new MapBuilder()
                    .with("companyCode", companyCode)
                    .with("docType", docType.toDocumentType())
                    .with("docCode", docCode)
                    .with("docDate", docDate)
                    .with("salespersonCode", salespersonCode)
                    .with("customerCode", customerCode)
                    .with("customerUsageType", customerUsageType)
                    .with("discount", discountDecimal)
                    .with("purchaseOrderNo", purchaseOrderNo)
                    .with("exemptionNo", exemptionNo)
                    .with("originCode", originCode)
                    .with("destinationCode", destinationCode)
                    .with("addresses", addresses)
                    .with("lines", mapLines)
                    .with("detailLevel", detailLevel.toAvalaraDetailLevel())
                    .with("referenceCode", referenceCode)
                    .with("locationCode", locationCode)
                    .with("commit", commit)
                    .with("batchCode", batchCode)
                    .with("taxOverride", taxOverride)
                    .with("currencyCode", currencyCode)
                    .with("serviceMode", serviceMode.toAvalaraServiceMode())
                    .with("paymentDate", paymentDate)
                    .with("exchangeRate", exchangeRateDecimal)
                    .with("exchangeRateEffDate", exchangeRateEffDate)
                    .build(),
                GetTaxRequest.class);

    return apiClient.sendTaxRequestToAvalara(
        TaxRequestType.AdjustTax,
        mom.unmap(
            new MapBuilder()
                .with("adjustmentReason", adjustmentReason)
                .with("adjustmentDescription", adjustmentDescription)
                .with("getTaxRequest", getTaxRequest)
                .build(),
            AdjustTaxRequest.class));
  }