diff --git a/Avalara.AvaTax.RestClient.sln b/Avalara.AvaTax.RestClient.sln new file mode 100644 index 0000000..5cc2f5c --- /dev/null +++ b/Avalara.AvaTax.RestClient.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.10.35004.147 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalara.AvaTax.RestClient", "src\Avalara.AvaTax.RestClient.csproj", "{F0EEC671-76CD-47C3-8671-FA178139B2A9}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalara.AvaTax.RestClient.Tests", "tests\Avalara.AvaTax.RestClient.Tests.csproj", "{88267F39-00E4-4D70-8E76-EA601E43CCA0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F0EEC671-76CD-47C3-8671-FA178139B2A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0EEC671-76CD-47C3-8671-FA178139B2A9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0EEC671-76CD-47C3-8671-FA178139B2A9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0EEC671-76CD-47C3-8671-FA178139B2A9}.Release|Any CPU.Build.0 = Release|Any CPU + {88267F39-00E4-4D70-8E76-EA601E43CCA0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {88267F39-00E4-4D70-8E76-EA601E43CCA0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {88267F39-00E4-4D70-8E76-EA601E43CCA0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {88267F39-00E4-4D70-8E76-EA601E43CCA0}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {EF80143E-E986-4C4B-9401-395AF44EF2FE} + EndGlobalSection +EndGlobal diff --git a/src/AvaTaxOfflineHelper.cs b/src/AvaTaxOfflineHelper.cs index 14f2806..42d1f1b 100644 --- a/src/AvaTaxOfflineHelper.cs +++ b/src/AvaTaxOfflineHelper.cs @@ -145,7 +145,8 @@ private static void WriteZipRateFile(TaxRateModel zipRate, string zip, string pa TextWriter writer = null; try { - Directory.GetAccessControl(path); + DirectoryInfo directory = new DirectoryInfo(path); + directory.GetAccessControl(); var content = JsonConvert.SerializeObject(zipRate); writer = new StreamWriter(Path.Combine(path, zip + ".json")); writer.Write(content); diff --git a/src/Avalara.AvaTax.RestClient.csproj b/src/Avalara.AvaTax.RestClient.csproj index 8a8c95c..ef9e425 100644 --- a/src/Avalara.AvaTax.RestClient.csproj +++ b/src/Avalara.AvaTax.RestClient.csproj @@ -1,12 +1,14 @@  - net20;net45;net461;net472;netstandard1.6;netstandard2.0 false + net20;net45;net461;net472;netstandard1.6;netstandard2.0;net6.0;net8.0 false true Avalara.AvaTax.RestClient.snk + + NETFRAMEWORK;TRACE;DEBUG;NET20 false @@ -15,8 +17,9 @@ NETFRAMEWORK;TRACE;RELEASE;NET20 true - - + + + NETFRAMEWORK;TRACE;DEBUG;PORTABLE;NET45 false @@ -25,8 +28,9 @@ NETFRAMEWORK;TRACE;RELEASE;PORTABLE;NET45 true + - + NETFRAMEWORK;TRACE;DEBUG;PORTABLE;NET461 true @@ -35,80 +39,85 @@ NETFRAMEWORK;TRACE;RELEASE;PORTABLE;NET461 true + - + NETFRAMEWORK;TRACE;DEBUG;PORTABLE;NET472 true - NETFRAMEWORK;TRACE;RELEASE;PORTABLE;NET472 true - + + + TRACE;DEBUG;PORTABLE;NETSTANDARD1_6 true - TRACE;RELEASE;PORTABLE;NETSTANDARD1_6 true + - + + TRACE;DEBUG;PORTABLE;NETSTANDARD2_0 true - - + TRACE;RELEASE;PORTABLE;NETSTANDARD2_0 true + + + TRACE;DEBUG;PORTABLE;NET6_0 + true + + + TRACE;RELEASE;PORTABLE;NET6_0 + true + + + + + + TRACE;DEBUG;PORTABLE;NET8_0 + true + + + TRACE;RELEASE;PORTABLE;NET8_0 + true + + + - + + - - - - Properties\GlobalAssemblyInfo.cs - - - - - - - Properties\GlobalAssemblyInfo.cs - - - - - - - Properties\GlobalAssemblyInfo.cs - - - - - - - Properties\GlobalAssemblyInfo.cs - - - - - - - - - - - - - + + + + + + + + Properties\GlobalAssemblyInfo.cs + + + + + + + + + + diff --git a/tests/Avalara.AvaTax.RestClient.Test.csproj b/tests/Avalara.AvaTax.RestClient.Test.csproj index 1864a8a..3f0176b 100644 --- a/tests/Avalara.AvaTax.RestClient.Test.csproj +++ b/tests/Avalara.AvaTax.RestClient.Test.csproj @@ -1,88 +1,79 @@  - net451;net45;net461;net472;netcoreapp2.2;netcoreapp3.1 + net451;net45;net461;net472;netcoreapp2.2.8;netcoreapp3.1;net6.0;net8.0 false - - - - - - - - - - - - + - - - - - + - - + + + + + + + TargetFramework=net20 + + - - - - - TargetFramework=net45 + + - - - - - TargetFramework=net461 + + - - - - - TargetFramework=net472 - - - - - - + + + TargetFramework=netstandard1.6 + + - - - - - TargetFramework=netstandard2.0 + + + + + + TargetFramework=net6.0 + + + + + + + + TargetFramework=net6.0 + + \ No newline at end of file diff --git a/tests/net6.0/BatchTests.cs b/tests/net6.0/BatchTests.cs new file mode 100644 index 0000000..dceff99 --- /dev/null +++ b/tests/net6.0/BatchTests.cs @@ -0,0 +1,198 @@ +using Avalara.AvaTax.RestClient; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Avalara.AvaTax.RestClient.Test.net60 +{ + [TestFixture] + public class BatchTests + { + public AvaTaxClient Client { get; set; } + public string CompanyCode { get; set; } + public CompanyModel TestCompany { get; set; } + + #region Setup / TearDown + /// + /// Create a company for use with these tests + /// + [SetUp] + public void Setup() + { + try + { + // Create a client and set up authentication + Client = new AvaTaxClient(typeof(TransactionTests).Name, + typeof(TransactionTests).GetTypeInfo().Assembly.ImageRuntimeVersion.ToString(), + Environment.MachineName, + AvaTaxEnvironment.Sandbox) + .WithSecurity(Environment.GetEnvironmentVariable("SANDBOX_USERNAME"), Environment.GetEnvironmentVariable("SANDBOX_PASSWORD")); + + // Verify that we can ping successfully + var pingResult = Client.Ping(); + + // Assert that ping succeeded + Assert.NotNull(pingResult, "Should be able to call Ping"); + Assert.True(pingResult.authenticated, "Environment variables should provide correct authentication"); + + // Create a basic company with nexus in the state of Washington + TestCompany = Client.CompanyInitialize(new CompanyInitializationModel() + { + city = "Bainbridge Island", + companyCode = Guid.NewGuid().ToString("N").Substring(0, 25), + country = "US", + email = "bob@example.org", + faxNumber = null, + firstName = "Bob", + lastName = "McExample", + line1 = "100 Ravine Lane", + mobileNumber = "206 555 1212", + phoneNumber = "206 555 1212", + postalCode = "98110", + region = "WA", + taxpayerIdNumber = "123456789", + name = "Bob's Hardware Store", + title = "Owner/CEO" + }); + + // Add a delay after creating a company + System.Threading.Thread.Sleep(6 * 1000); + + // Assert that company setup succeeded + Assert.NotNull(TestCompany, "Test company should be created"); + Assert.True(TestCompany.nexus.Count > 0, "Test company should have nexus"); + Assert.True(TestCompany.locations.Count > 0, "Test company should have locations"); + + // Shouldn't fail + } catch (Exception ex) + { + Assert.Fail("Exception in SetUp: " + ex); + } + } + + /// + /// Any cleanup required goes here + /// + [TearDown] + public void TearDown() + { + try + { + // Re-fetch the company + var company = Client.GetCompany(TestCompany.id, null); + + // Flag this company as inactive + company.isActive = false; + var disableResult = Client.UpdateCompany(company.id, company); + + // Assert that it succeeded + Assert.NotNull(disableResult, "Should have been able to update this company"); + Assert.False(disableResult.isActive, "Company should have been deactivated"); + + // Shouldn't fail + } catch (Exception ex) + { + Assert.Fail("Exception in TearDown: " + ex); + } + } + #endregion + + /// + /// + /// + [Test] + public async Task BatchesWorkflow() + { + // Raw batch CSV string. + const string transactionImport = @"ProcessCode,DocCode,DocType,DocDate,CompanyCode,CustomerCode,EntityUseCode,LineNo,TaxCode,TaxDate,ItemCode,Description,Qty,Amount,Discount,Ref1,Ref2,ExemptionNo,RevAcct,DestAddress,DestCity,DestRegion,DestPostalCode,DestCountry,OrigAddress,OrigCity,OrigRegion,OrigPostalCode,OrigCountry,LocationCode,SalesPersonCode,PurchaseOrderNo,CurrencyCode,ExchangeRate,ExchangeRateEffDate,PaymentDate,TaxIncluded,DestTaxRegion,OrigTaxRegion,Taxable,TaxType,TotalTax,CountryName,CountryCode,CountryRate,CountryTax,StateName,StateCode,StateRate,StateTax,CountyName,CountyCode,CountyRate,CountyTax,CityName,CityCode,CityRate,CityTax,Other1Name,Other1Code,Other1Rate,Other1Tax,Other2Name,Other2Code,Other2Rate,Other2Tax,Other3Name,Other3Code,Other3Rate,Other3Tax,Other4Name,Other4Code,Other4Rate,Other4Tax,ReferenceCode,BuyersVATNo +3,BS1323154187029MG10,2,16-Apr-14,,029MG10,,0000000001,SR060100,06-May-14,6500,REPAIRS & MAINTENANCE,,1980,,,0,,6500,119 N. 72nd St.,Omaha,NE,68114,,6923 MAPLE ST,OMAHA,NE,68104,,029,,,USD,,,,0,,,,,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +3,BS1323158772194036MAC1,2,04-Apr-14,,036MAC1,,0000000001,SC150158,06-May-14,6505,R&M HEAT / VENT / AIR COND,,322.26,,,0,,6505,1200 E. Mall Drive,Holland,OH,43528,,2875 CRANE WAY,NORTHWOOD,OH,43619,,036,,,USD,,,,0,,,,,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +3,BS1323159W206409036MLK,2,25-Mar-14,,036MLK,,0000000001,SR060100,06-May-14,6507,R&M OTHER,,449,,,31.43,,6507,1200 E. Mall Drive,Holland,OH,43528,,1214 JEFFERSON AVE,TOLEDO,OH,43604,,036,,,USD,,,,0,,,,,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +3,BS13231601596048MRP2,2,02-May-14,,048MRP2,,0000000001,SR060100,06-May-14,6520,FURNITURE REPAIRS,,60,,,0,,6520,2201 Hwy 75 North,Sherman,TX,75090,,P.O. BOX 125,POTTSBORO,TX,75076,,048,,,USD,,,,0,,,,,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +3,BS13231631591048MRP2,2,02-May-14,,048MRP2,,0000000001,SR060100,06-May-14,6520,FURNITURE REPAIRS,,58,,,0,,6520,2201 Hwy 75 North,Sherman,TX,75090,,P.O. BOX 125,POTTSBORO,TX,75076,,048,,,USD,,,,0,,,,,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +3,BS13231611594048MRP2,2,01-May-14,,048MRP2,,0000000001,SR060100,06-May-14,6520,FURNITURE REPAIRS,,65,,,0,,6520,2201 Hwy 75 North,Sherman,TX,75090,,P.O. BOX 125,POTTSBORO,TX,75076,,048,,,USD,,,,0,,,,,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +3,BS13231621593048MRP2,2,01-May-14,,048MRP2,,0000000001,SR060100,06-May-14,6520,FURNITURE REPAIRS,,75,,,0,,6520,2201 Hwy 75 North,Sherman,TX,75090,,P.O. BOX 125,POTTSBORO,TX,75076,,048,,,USD,,,,0,,,,,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +3,BS13231641587048MRP2,2,01-May-14,,048MRP2,,0000000001,SR060100,06-May-14,6520,FURNITURE REPAIRS,,60,,,0,,6520,2201 Hwy 75 North,Sherman,TX,75090,,P.O. BOX 125,POTTSBORO,TX,75076,,048,,,USD,,,,0,,,,,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +3,BS13231651582048MRP2,2,15-Mar-14,,048MRP2,,0000000001,SR060100,06-May-14,6520,FURNITURE REPAIRS,,47,,,0,,6520,2201 Hwy 75 North,Sherman,TX,75090,,P.O. BOX 125,POTTSBORO,TX,75076,,048,,,USD,,,,0,,,,,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"; + + // Let's Create the file portion of the batch request. + var batchFileModel = new BatchFileModel + { + name = "TransactionImport.csv", + content = Encoding.UTF8.GetBytes(transactionImport), + contentType = "text/csv", + fileExtension = "csv" + }; + + // Create the bare-minimum batch header info. + var batchRequest = new BatchModel + { + name = "AutomationTestBatch", + type = BatchType.TransactionImport, + files = new List { batchFileModel } + }; + + // Send the batch! + try + { + var batchResult = Client.CreateBatches(TestCompany.id, new List { batchRequest }); + Assert.NotNull(batchResult, "Batch not sent."); + Assert.True(batchResult.Count > 0, "No batches created."); + + // Check that the batch comes out of Waiting state using a linear backoff strategy. + var waiting = true; + BatchModel batchFetchResult = null; + for (var i = 1; i < 6; ++i) + { + await Task.Delay(i * 1000); + + batchFetchResult = Client.GetBatch(TestCompany.id, batchResult[0].id.Value); + Assert.NotNull(batchFetchResult, "Batch fetch unsuccessful."); + if (batchFetchResult.status.Value == BatchStatus.Waiting) continue; + waiting = false; + break; + } + Assert.True(waiting == false, $"Batch waiting too long. Check BatchId: {batchResult[0].id}"); + + // This batch is no longer in the Waiting state. Let's see it process. + var processing = true; + for (var i = 1; i < 11; ++i) + { + await Task.Delay(i * 1000); + + batchFetchResult = Client.GetBatch(TestCompany.id, batchResult[0].id.Value); + Assert.NotNull(batchFetchResult, "Batch fetch unsuccessful."); + if (batchFetchResult.status.Value == BatchStatus.Processing) continue; + processing = false; + break; + } + Assert.True(processing == false, $"Batch processing too long. Check BatchId: {batchResult[0].id}"); + + // This batch is done processing. + Assert.True((batchFetchResult.status.Value == BatchStatus.Errors || batchFetchResult.status.Value == BatchStatus.Completed), + $"BatchId: {batchResult[0].id} should either complete or error out."); + // Ensure that the number of records matches what we sent in. + Assert.AreEqual(9, batchFetchResult.currentRecord.Value); + + // Alright. Time to download the sent batch file. + var fileResult = Client.DownloadBatch(TestCompany.id, batchFetchResult.id.Value, batchFetchResult.files[0].id.Value); + Assert.NotNull(fileResult); + + // Compare what we got back with what we sent. + Assert.AreEqual(batchFetchResult.name + ".Input.CSV; filename*=UTF-8''" + batchFetchResult.name + ".Input.CSV", fileResult.Filename); + Assert.AreEqual(batchFileModel.content, fileResult.Data); + Assert.AreEqual(batchFileModel.contentType, fileResult.ContentType); + } catch (AvaTaxError e) + { + Assert.True(false, $"AvaTaxError: {e.error.error.details?[0].message}"); + } catch (Exception e) + { + Assert.True(false, $"Unknown Exception! {e.Message}"); + } + } + } +} diff --git a/tests/net6.0/CertificateTests.cs b/tests/net6.0/CertificateTests.cs new file mode 100644 index 0000000..be1739f --- /dev/null +++ b/tests/net6.0/CertificateTests.cs @@ -0,0 +1,144 @@ +using Avalara.AvaTax.RestClient; +using Newtonsoft.Json; +using NUnit.Framework; +using System; +using System.IO; +using System.Net; +using System.Reflection; + +namespace Avalara.AvaTax.RestClient.Test.net60 +{ + [TestFixture] + public class CertificateTests + { + public AvaTaxClient Client { get; set; } + public string CompanyCode { get; set; } + public int DefaultCompanyId { get; set; } + public CompanyModel TestCompany { get; set; } + + #region Setup / TearDown + /// + /// Create a company for use with these tests + /// + [SetUp] + public void Setup() + { + try { + // Create a client and set up authentication + Client = new AvaTaxClient(typeof(CertificateTests).Name, + typeof(CertificateTests).GetTypeInfo().Assembly.ImageRuntimeVersion.ToString(), + Environment.MachineName, + AvaTaxEnvironment.Sandbox) + .WithSecurity(Environment.GetEnvironmentVariable("SANDBOX_USERNAME"), Environment.GetEnvironmentVariable("SANDBOX_PASSWORD")); + + // Verify that we can ping successfully + var pingResult = Client.Ping(); + + // Assert that ping succeeded + Assert.NotNull(pingResult, "Should be able to call Ping"); + Assert.True(pingResult.authenticated, "Environment variables should provide correct authentication"); + + //Get the default company. + var defaultCompanyModel = Client.QueryCompanies(string.Empty, "isDefault EQ true", null, null, string.Empty).value[0]; + + DefaultCompanyId = defaultCompanyModel.id; + + // Create a basic company with nexus in the state of Washington + TestCompany = Client.CompanyInitialize(new CompanyInitializationModel() + { + city = "Bainbridge Island", + companyCode = Guid.NewGuid().ToString().Substring(0, 25), + country = "US", + email = "bob@example.org", + faxNumber = null, + firstName = "Bob", + lastName = "McExample", + line1 = "100 Ravine Lane", + mobileNumber = "206 555 1212", + phoneNumber = "206 555 1212", + postalCode = "98110", + region = "WA", + taxpayerIdNumber = "123456789", + name = "Bob's Greatest Popcorn", + title = "Owner/CEO" + }); + + // Add a delay + System.Threading.Thread.Sleep(6 * 1000); + + // Assert that company setup succeeded + Assert.NotNull(TestCompany, "Test company should be created"); + Assert.True(TestCompany.nexus.Count > 0, "Test company should have nexus"); + Assert.True(TestCompany.locations.Count > 0, "Test company should have locations"); + + // Shouldn't fail + } catch (Exception ex) { + Assert.Fail("Exception in SetUp: " + ex); + } + } + + + /// + /// Any cleanup required goes here + /// + [TearDown] + public void TearDown() + { + try { + + // Re-fetch the company + var company = Client.GetCompany(TestCompany.id, null); + + // Flag this company as inactive + company.isActive = false; + var disableResult = Client.UpdateCompany(company.id, company); + + // Assert that it succeeded + Assert.NotNull(disableResult, "Should have been able to update this company"); + Assert.False(disableResult.isActive, "Company should have been deactivated"); + + // Shouldn't fail + } catch (Exception ex) { + Assert.Fail("Exception in TearDown: " + ex); + } + } + #endregion + + /// + /// Tests the upload certificate image endpoint. + /// + [Test] + [Ignore("Ignore CertificateImageUploadTest")] + public void CertificateImageUploadTest() + { + //Get the cert number. The account needs to have CertCapture + //be provisioned, already have a certificate created, and the + //certificate needs to be valid. + var certs = Client.QueryCertificates(DefaultCompanyId, string.Empty, "valid EQ true", null, null, string.Empty).value; + var certId = certs[0].id.Value; + + //Get an image. + byte[] jpegByteArr = File.ReadAllBytes(Environment.GetEnvironmentVariable("TEST_IMAGE_PATH")); + + FileResult fileResult = new FileResult() + { + ContentType = "multipart/form-data", + Filename = "test_cert_image.jpg", + Data = jpegByteArr + }; + + //Send request. + var certUploadResult = Client.UploadCertificateImage(DefaultCompanyId, certId, fileResult); + + //Response should be "OK" + Assert.True(string.Equals(certUploadResult, "\"OK\"")); + + //Test download of image attachment. + var certAttachment = Client.DownloadCertificateImage(DefaultCompanyId, certId, null, CertificatePreviewType.Pdf); + Assert.NotNull(certAttachment); + Assert.True(string.Equals(certAttachment.ContentType, "application/pdf")); + Assert.True(certAttachment.Data.Length > 1000); + + } + } +} diff --git a/tests/net6.0/ErrorResultTests.cs b/tests/net6.0/ErrorResultTests.cs new file mode 100644 index 0000000..6626028 --- /dev/null +++ b/tests/net6.0/ErrorResultTests.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using Avalara.AvaTax.RestClient; +using NUnit.Framework; + +namespace Avalara.AvaTax.RestClient.Test.net60 +{ + [TestFixture] + public class ErrorResultTests : TestBase + { + [Test] + public async Task NonJsonErrorResult() + { + AvaTaxError avataxError = null; + try + { + // make a client that points to a server that will give a 404 for ping + var client = new AvaTaxClient(GetType().Name, + GetType().GetTypeInfo().Assembly.ImageRuntimeVersion.ToString(), + Environment.MachineName, new Uri("https://www.google.com")); + + var result = await client.PingAsync().ConfigureAwait(false); + } + catch (AvaTaxError e) + { + avataxError = e; + } + + Assert.NotNull(avataxError); + Assert.True(avataxError.error.error.message.Contains("the response is in an unexpected format")); + Assert.True(avataxError.error.error.details[0].description.ToLower().Contains("not found")); + } + + [Test] + public async Task JsonErrorResult() + { + AvaTaxError avataxError = null; + try + { + var result = await _client.ChangePasswordAsync(new PasswordChangeModel + { + }).ConfigureAwait(false); + } + catch (AvaTaxError e) + { + avataxError = e; + } + + Assert.NotNull(avataxError); + Assert.True(avataxError.error.error.message.Contains("Field oldPassword is required")); + } + } +} diff --git a/tests/net6.0/FreeTaxRatesTest.cs b/tests/net6.0/FreeTaxRatesTest.cs new file mode 100644 index 0000000..1b0ff67 --- /dev/null +++ b/tests/net6.0/FreeTaxRatesTest.cs @@ -0,0 +1,97 @@ +using Avalara.AvaTax.RestClient; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace Avalara.AvaTax.RestClient.Test.net60 +{ + [TestFixture] + class FreeTaxRatesTest + { + public AvaTaxClient _client; + + #region Setup / Teardown + /// + /// Create a company for use with these tests + /// + [SetUp] + public void Setup() + { + try { + // Create a client and set up authentication + _client = new AvaTaxClient(typeof(TransactionTests).Name, + typeof(TransactionTests).GetTypeInfo().Assembly.ImageRuntimeVersion.ToString(), + Environment.MachineName, + AvaTaxEnvironment.Sandbox) + .WithSecurity(Environment.GetEnvironmentVariable("SANDBOX_USERNAME"), Environment.GetEnvironmentVariable("SANDBOX_PASSWORD")); + } catch (Exception ex) { + Assert.Fail("Exception in SetUp: " + ex); + } + } + #endregion + + [Test] + public async Task FreeTaxRates() + { + // Call TaxRates for a few addresses and verify that the rates are nonzero + var tr = await _client.TaxRatesByAddressAsync("123 Main Street", null, null, "Irvine", "CA", "92615", "US"); + Assert.NotNull(tr); + Assert.True(tr.totalRate > 0.05m); + + // Washington + tr = await _client.TaxRatesByAddressAsync("522 Stadium Pl S", null, null, "Seattle", "WA", "98104", "US"); + Assert.NotNull(tr); + Assert.True(tr.totalRate > 0.05m); + + // By postal code for New York + tr = await _client.TaxRatesByPostalCodeAsync("US", "10010"); + Assert.NotNull(tr); + Assert.True(tr.totalRate > 0.05m); + } + + /// + /// Test the local rate by ZIP storage and retrieval. + /// + [Test] + [Ignore("This test will fail in Travis")] + public void StoreRatesByZipTest() + { + string path = Environment.GetEnvironmentVariable("ZIP_RATE_FILE_STORAGE_PATH"); + List zips = new List() { "12590", "98104" }; + + //Call the content caching helper. + AvaTaxOfflineHelper.StoreZipRateContent(_client, "US", zips, path); + + //Verify that the files were stored locally. + bool zipRateExists = AvaTaxOfflineHelper.VerifyLocalZipRateAvailable(zips[0], path); + Assert.True(zipRateExists); + zipRateExists = AvaTaxOfflineHelper.VerifyLocalZipRateAvailable(zips[1], path); + Assert.True(zipRateExists); + + //Verify that a bogus file does not exist locally. + zipRateExists = AvaTaxOfflineHelper.VerifyLocalZipRateAvailable("bogusZipFile.json", path); + Assert.False(zipRateExists); + + //Verify that the local file can be used for rate calculation. + var zipTaxRate = AvaTaxOfflineHelper.GetLocalTaxRateByZip(zips[1], path); + Assert.NotNull(zipTaxRate); + decimal result = 9.99m * zipTaxRate.totalRate.Value; + Assert.NotZero(result); + + //Test AvaTaxOfflineHelper Exception handling. + path = @"n:\someBadPath"; + try { + AvaTaxOfflineHelper.StoreZipRateContent(_client, "US", zips, path); + } catch (AvaTaxOfflineHelperException exc) { +#if PORTABLE + Assert.True(string.Equals(exc.InnerException.Message, "Could not find a part of the path 'n:\\someBadPath\\12590.json'.")); +#else + Assert.True(string.Equals(exc.Message, "An error occurred retrieving or storing the rate content. Please see inner exception for details.")); +#endif + } + } + } +} diff --git a/tests/net6.0/HttpClientTransactionTests.cs b/tests/net6.0/HttpClientTransactionTests.cs new file mode 100644 index 0000000..80af650 --- /dev/null +++ b/tests/net6.0/HttpClientTransactionTests.cs @@ -0,0 +1,204 @@ +using Avalara.AvaTax.RestClient; +using Newtonsoft.Json; +using NUnit.Framework; +using System; +using System.Net; +using System.Reflection; + +namespace Avalara.AvaTax.RestClient.Test.net60 +{ + [TestFixture] + public class HttpClientTransactionTests + { + public AvaTaxClient Client { get; set; } + public string CompanyCode { get; set; } + public CompanyModel TestCompany { get; set; } + + #region Setup / TearDown + /// + /// Create a company for use with these tests + /// + [SetUp] + public void Setup() + { + try + { + var httpClient = new System.Net.Http.HttpClient() { Timeout = TimeSpan.FromMinutes(20) }; + // Create a client and set up authentication + Client = new AvaTaxClient(httpClient, typeof(HttpClientTransactionTests).Name, + typeof(HttpClientTransactionTests).GetTypeInfo().Assembly.ImageRuntimeVersion.ToString(), + Environment.MachineName, + AvaTaxEnvironment.Sandbox) + .WithSecurity(Environment.GetEnvironmentVariable("SANDBOX_USERNAME"), Environment.GetEnvironmentVariable("SANDBOX_PASSWORD")); + + // Verify that we can ping successfully + var pingResult = Client.Ping(); + + // Assert that ping succeeded + Assert.NotNull(pingResult, "Should be able to call Ping"); + Assert.True(pingResult.authenticated, "Environment variables should provide correct authentication"); + + // Create a basic company with nexus in the state of Washington + TestCompany = Client.CompanyInitialize(new CompanyInitializationModel() + { + city = "Bainbridge Island", + companyCode = Guid.NewGuid().ToString().Substring(0, 25), + country = "US", + email = "bob@example.org", + faxNumber = null, + firstName = "Bob", + lastName = "McExample", + line1 = "100 Ravine Lane", + mobileNumber = "206 555 1212", + phoneNumber = "206 555 1212", + postalCode = "98110", + region = "WA", + taxpayerIdNumber = "123456789", + name = "Bob's Greatest Popcorn", + title = "Owner/CEO" + }); + + // Add a delay + System.Threading.Thread.Sleep(6 * 1000); + + // Assert that company setup succeeded + Assert.NotNull(TestCompany, "Test company should be created"); + Assert.True(TestCompany.nexus.Count > 0, "Test company should have nexus"); + Assert.True(TestCompany.locations.Count > 0, "Test company should have locations"); + + // Shouldn't fail + } catch (Exception ex) + { + Assert.Fail("Exception in SetUp: " + ex); + } + } + + + /// + /// Any cleanup required goes here + /// + [TearDown] + public void TearDown() + { + try + { + + // Re-fetch the company + var company = Client.GetCompany(TestCompany.id, null); + + // Flag this company as inactive + company.isActive = false; + var disableResult = Client.UpdateCompany(company.id, company); + + // Assert that it succeeded + Assert.NotNull(disableResult, "Should have been able to update this company"); + Assert.False(disableResult.isActive, "Company should have been deactivated"); + + // Shouldn't fail + } catch (Exception ex) + { + Assert.Fail("Exception in TearDown: " + ex); + } + } + #endregion + + /// + /// To debug this application, call app must be called with args[0] as username and args[1] as password + /// + [Test] + public void TransactionWorkflow() + { + Client.CallCompleted += Client_CallCompleted; + var tfn = System.IO.Path.GetTempFileName(); + Client.LogToFile(tfn); + + // Execute a transaction + var transaction = new TransactionBuilder(Client, TestCompany.companyCode, DocumentType.SalesInvoice, "ABC") + .WithAddress(TransactionAddressType.SingleLocation, "521 S Weller St", null, null, "Seattle", "WA", + "98104", "US") + .WithLine(100.0m, 1, "P0000000") + .WithLine(200m) + .WithExemptLine(50m, "NT") + .WithLineReference("Special Line Reference!", "Also this!") + .Create(); + + // Verify that the call was captured and logged + Assert.NotNull(lastEvent); + Assert.True(String.Equals(lastEvent.HttpVerb, "POST", StringComparison.CurrentCultureIgnoreCase)); + Assert.True(String.Equals(lastEvent.RequestUri.ToString(), "https://sandbox-rest.avatax.com/api/v2/transactions/create", StringComparison.CurrentCultureIgnoreCase)); + Assert.AreEqual(lastEvent.Code, HttpStatusCode.Created); + + // Verify that the log file was created + Assert.True(System.IO.File.Exists(tfn)); + + // Ensure this transaction was created, and has three lines, and has some tax + Assert.NotNull(transaction, "Transaction should have been created"); + Assert.True(transaction.totalTax > 0.0m, "Transaction should have had some tax"); + Assert.True(transaction.lines.Count == 3, "Transaction should have three lines"); + Assert.True(transaction.lines[2].ref1.Contains("Reference!"), "Line3 should have had a Ref1."); + + // Now commit that transaction + var commitResult = Client.CommitTransaction(TestCompany.companyCode, transaction.code, null, null, new CommitTransactionModel() { commit = true }); + + // Ensure that this transaction was committed + Assert.NotNull(commitResult, "Should have been able to call CommitTransaction"); + Assert.True(commitResult.status == DocumentStatus.Committed, "Transaction should have been committed"); + + // Now void the transaction + var voidResult = Client.VoidTransaction(TestCompany.companyCode, transaction.code, null, null, new VoidTransactionModel() + { + code = VoidReasonCode.DocVoided + }); + + // Ensure that the transaction was voided + Assert.NotNull(voidResult, "Should have been able to call VoidTransactoin"); + Assert.True(voidResult.status == DocumentStatus.Cancelled, "Transaction should have been voided"); + } + + private AvaTaxCallEventArgs lastEvent = null; + private void Client_CallCompleted(object sender, EventArgs e) + { + lastEvent = e as AvaTaxCallEventArgs; + } + + [Test] + + public void TaxOverrideExample() + { + // Create base transaction. + var builder = new TransactionBuilder(Client, TestCompany.companyCode, DocumentType.SalesInvoice, + "TaxOverrideCustomerCode") + .WithAddress(TransactionAddressType.SingleLocation, "521 S Weller St", null, null, "Seattle", "WA", + "98104", "US") + .WithLine(100.0m, 1, "P0000000") + .WithLine(200m); + + var transaction = builder.Create(); + + // Ensure this transaction was created. + Assert.NotNull(transaction, "Transaction should have been created"); + + // Add Line-level TaxOverride. + var overrideTransaction = builder + .WithLineTaxOverride(TaxOverrideType.TaxAmount, "Tax Override Reason", 1) + .Create(); + + // Ensure this transaction was created. + Assert.NotNull(overrideTransaction, "Transaction should have been created"); + + // Compare the two transactions. + Assert.AreEqual(overrideTransaction.totalTaxCalculated, transaction.totalTaxCalculated, "Total Tax Calculated should be the same."); + Assert.True(overrideTransaction.totalTax < transaction.totalTax, "Total Tax should not be the same. Overridden transaction should be smaller."); + + // Compare the transaction lines. + var overrideLine = overrideTransaction.lines[1]; + var line = transaction.lines[1]; + Assert.AreEqual(overrideLine.isItemTaxable, line.isItemTaxable); + Assert.AreEqual(overrideLine.taxCalculated, line.taxCalculated); + Assert.AreEqual(overrideLine.lineAmount, line.lineAmount); + Assert.AreEqual(1, overrideLine.tax); + Assert.True(overrideLine.tax < line.tax); + Assert.AreEqual(TaxOverrideType.TaxAmount, overrideLine.taxOverrideType); + } + } +} diff --git a/tests/net6.0/NexusTests.cs b/tests/net6.0/NexusTests.cs new file mode 100644 index 0000000..39e3c81 --- /dev/null +++ b/tests/net6.0/NexusTests.cs @@ -0,0 +1,169 @@ +using Avalara.AvaTax.RestClient; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; + +namespace Avalara.AvaTax.RestClient.Test.net60 +{ + [TestFixture] + [Ignore("This test is not yet implemented")] + public class NexusTests + { + public AvaTaxClient Client { get; set; } + public string CompanyCode { get; set; } + public CompanyModel TestCompany { get; set; } + + #region Setup / TearDown + /// + /// Create a company for use with these tests + /// + [SetUp] + public void Setup() + { + try { + // Create a client and set up authentication + Client = new AvaTaxClient(typeof(TransactionTests).Name, + typeof(TransactionTests).GetTypeInfo().Assembly.ImageRuntimeVersion.ToString(), + Environment.MachineName, + AvaTaxEnvironment.Sandbox) + .WithSecurity(Environment.GetEnvironmentVariable("SANDBOX_USERNAME"), Environment.GetEnvironmentVariable("SANDBOX_PASSWORD")); + + // Verify that we can ping successfully + var pingResult = Client.Ping(); + + // Assert that ping succeeded + Assert.NotNull(pingResult, "Should be able to call Ping"); + Assert.True(pingResult.authenticated, "Environment variables should provide correct authentication"); + + // Create a basic company with nexus in the state of Washington + TestCompany = Client.CompanyInitialize(new CompanyInitializationModel() + { + city = "Bainbridge Island", + companyCode = Guid.NewGuid().ToString().Substring(0, 25), + country = "US", + email = "bob@example.org", + faxNumber = null, + firstName = "Bob", + lastName = "McExample", + line1 = "100 Ravine Lane", + mobileNumber = "206 555 1212", + phoneNumber = "206 555 1212", + postalCode = "98110", + region = "WA", + taxpayerIdNumber = "123456789", + name = "Bob's Greatest Popcorn", + title = "Owner/CEO" + }); + + // Assert that company setup succeeded + Assert.NotNull(TestCompany, "Test company should be created"); + Assert.True(TestCompany.nexus.Count > 0, "Test company should have nexus"); + Assert.True(TestCompany.locations.Count > 0, "Test company should have locations"); + + // Shouldn't fail + } catch (Exception ex) { + Assert.Fail("Exception in SetUp: " + ex.ToString()); + } + } + + + /// + /// Any cleanup required goes here + /// + [TearDown] + public void TearDown() + { + try { + + // Re-fetch the company + var company = Client.GetCompany(TestCompany.id, null); + + // Flag this company as inactive + company.isActive = false; + var disableResult = Client.UpdateCompany(company.id, company); + + // Assert that it succeeded + Assert.NotNull(disableResult, "Should have been able to update this company"); + Assert.False(disableResult.isActive, "Company should have been deactivated"); + + // Shouldn't fail + } catch (Exception ex) { + Assert.Fail("Exception in TearDown: " + ex.ToString()); + } + } + #endregion + + [Test] + public void CreateAndDeleteNexus() + { + var nexusModels = new List(); + + var stateNexus = new NexusModel + { + id = 0, + companyId = TestCompany.id, + country = "US", + region = "AL", + jurisTypeId = JurisTypeId.STA, + jurisCode = "01", + jurisName = "ALABAMA", + shortName = "AL", + signatureCode = "", + stateAssignedNo = "", + nexusTypeId = NexusTypeId.SalesOrSellersUseTax, + hasLocalNexus = true, + hasPermanentEstablishment = true, + effectiveDate = new DateTime(2008, 07, 01), + endDate = new DateTime(2019, 07, 01) + }; + + var cityNexus = new NexusModel + { + id = 0, + companyId = TestCompany.id, + country = "US", + region = "AL", + jurisTypeId = JurisTypeId.CIT, + jurisCode = "00124", + jurisName = "ABBEVILLE", + shortName = "ABBEVILLE", + signatureCode = "", + stateAssignedNo = "9356", + nexusTypeId = NexusTypeId.SalesTax, + hasLocalNexus = true, + hasPermanentEstablishment = false, + effectiveDate = new DateTime(2008, 07, 01), + endDate = new DateTime(2018, 07, 01) + }; + + nexusModels.Add(stateNexus); + nexusModels.Add(cityNexus); + + var nexusModelsAdded = Client.CreateNexus(TestCompany.id, new List { stateNexus, cityNexus }); + + // Get State nexus + NexusModel getALNexus = null; + try { + getALNexus = Client.GetNexus(TestCompany.id, nexusModelsAdded[0].id.Value, null); + } catch (Exception) { } + Assert.NotNull(getALNexus); + + var fetchedUSNexus = new List { getALNexus }; + + // Get City Nexus + NexusModel getCityNexus = null; + try { + getCityNexus = Client.GetNexus(TestCompany.id, nexusModelsAdded[1].id.Value, null); + } catch (Exception) { } + Assert.NotNull(getALNexus); + + fetchedUSNexus.Add(getCityNexus); + + // Delete Nexus + var errorResult = Client.DeleteNexus(TestCompany.id, nexusModelsAdded[1].id.Value, null); + Assert.NotNull(errorResult); + } + } +} \ No newline at end of file diff --git a/tests/net6.0/SetUpFixture.cs b/tests/net6.0/SetUpFixture.cs new file mode 100644 index 0000000..0f5ddb7 --- /dev/null +++ b/tests/net6.0/SetUpFixture.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using System.Runtime.Versioning; +using System.Text; +using System.Threading.Tasks; +using Avalara.AvaTax.RestClient; +using NUnit.Framework; + +namespace Avalara.AvaTax.RestClient.Test.net60 +{ + [SetUpFixture] + public class SetUpFixture + { + [OneTimeSetUp] + public void RunBeforeAnyTests() + { + var stringBuilder = new StringBuilder(); + + stringBuilder + .AppendLine($"Hosting Framework Runtime Version: {GetRuntimeFrameworkVersion(GetType().Assembly)}") + .AppendLine($"Hosting Framework Version: {GetFrameworkVersion(GetType().Assembly)}") + .AppendLine($"Target Framework Runtime Version: {GetRuntimeFrameworkVersion(typeof(AvaTaxClient).Assembly)}") + .AppendLine($"Target Framework Version: {GetFrameworkVersion(typeof(AvaTaxClient).Assembly)}"); + + TestContext.Progress.WriteLine(stringBuilder); + } + + private static string GetRuntimeFrameworkVersion(Assembly assembly) + { + var imageRuntimeVersion = assembly + .ImageRuntimeVersion; + + return imageRuntimeVersion; + } + + private static string GetFrameworkVersion(Assembly assembly) + { + var targetFrameAttribute = assembly.GetCustomAttributes(true) + .OfType().FirstOrDefault(); + if (targetFrameAttribute == null) + { + return ".NET 2, 3 or 3.5"; + } + + return targetFrameAttribute.FrameworkName; + } + } +} diff --git a/tests/net6.0/TestBase.cs b/tests/net6.0/TestBase.cs new file mode 100644 index 0000000..2c50898 --- /dev/null +++ b/tests/net6.0/TestBase.cs @@ -0,0 +1,103 @@ +using Avalara.AvaTax.RestClient; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Text; + +namespace Avalara.AvaTax.RestClient.Test.net60 +{ + public class TestBase + { + protected AvaTaxClient _client; + protected string _companyCode; + protected CompanyModel _testCompany; + + [SetUp] + public void Setup() + { + _client = null; + _testCompany = null; + try + { + // Create a client and set up authentication + _client = new AvaTaxClient(GetType().Name, + GetType().GetTypeInfo().Assembly.ImageRuntimeVersion.ToString(), + Environment.MachineName, + AvaTaxEnvironment.Sandbox) + .WithSecurity(Environment.GetEnvironmentVariable("SANDBOX_USERNAME"), Environment.GetEnvironmentVariable("SANDBOX_PASSWORD")); + + // Verify that we can ping successfully + var pingResult = _client.Ping(); + + // Assert that ping succeeded + Assert.NotNull(pingResult, "Should be able to call Ping"); + Assert.True(pingResult.authenticated, "Environment variables should provide correct authentication"); + + _companyCode = Guid.NewGuid().ToString("N").Substring(0, 25); + // Create a basic company with nexus in the state of Washington + _testCompany = _client.CompanyInitialize(new CompanyInitializationModel() + { + city = "Bainbridge Island", + companyCode = _companyCode, + country = "US", + email = "bob@example.org", + faxNumber = null, + firstName = "Bob", + lastName = "McExample", + line1 = "100 Ravine Lane", + mobileNumber = "206 555 1212", + phoneNumber = "206 555 1212", + postalCode = "98110", + region = "WA", + taxpayerIdNumber = "123456789", + name = "Bob's Hardware Store", + title = "Owner/CEO" + }); + + // Add a delay after creating a company + System.Threading.Thread.Sleep(6 * 1000); + + // Assert that company setup succeeded + Assert.NotNull(_testCompany, "Test company should be created"); + Assert.True(_testCompany.nexus.Count > 0, "Test company should have nexus"); + Assert.True(_testCompany.locations.Count > 0, "Test company should have locations"); + + // Shouldn't fail + } + catch (Exception ex) + { + Assert.Fail("Exception in SetUp: " + ex); + } + } + + + /// + /// Any cleanup required goes here + /// + [TearDown] + public void TearDown() + { + try + { + // Re-fetch the company + var company = _client.GetCompany(_testCompany.id, null); + + // Flag this company as inactive + company.isActive = false; + var disableResult = _client.UpdateCompany(company.id, company); + + // Assert that it succeeded + Assert.NotNull(disableResult, "Should have been able to update this company"); + Assert.False(disableResult.isActive, "Company should have been deactivated"); + + // Shouldn't fail + } + catch (Exception ex) + { + Assert.Fail("Exception in TearDown: " + ex); + } + } + } +} diff --git a/tests/net6.0/TransactionTests.cs b/tests/net6.0/TransactionTests.cs new file mode 100644 index 0000000..36a0e94 --- /dev/null +++ b/tests/net6.0/TransactionTests.cs @@ -0,0 +1,211 @@ +using Avalara.AvaTax.RestClient; +using Newtonsoft.Json; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Net; +using System.Reflection; + +namespace Avalara.AvaTax.RestClient.Test.net60 +{ + [TestFixture] + public class TransactionTests + { + public AvaTaxClient Client { get; set; } + public string CompanyCode { get; set; } + public CompanyModel TestCompany { get; set; } + + #region Setup / TearDown + /// + /// Create a company for use with these tests + /// + [SetUp] + public void Setup() + { + try + { + // Create a client and set up authentication + Client = new AvaTaxClient(typeof(TransactionTests).Name, + typeof(TransactionTests).GetTypeInfo().Assembly.ImageRuntimeVersion.ToString(), + Environment.MachineName, + AvaTaxEnvironment.Sandbox) + .WithSecurity(Environment.GetEnvironmentVariable("SANDBOX_USERNAME"), Environment.GetEnvironmentVariable("SANDBOX_PASSWORD")); + + // Verify that we can ping successfully + var pingResult = Client.Ping(); + + // Assert that ping succeeded + Assert.NotNull(pingResult, "Should be able to call Ping"); + Assert.True(pingResult.authenticated, "Environment variables should provide correct authentication"); + + // Create a basic company with nexus in the state of Washington + TestCompany = Client.CompanyInitialize(new CompanyInitializationModel() + { + city = "Bainbridge Island", + companyCode = Guid.NewGuid().ToString().Substring(0, 25), + country = "US", + email = "bob@example.org", + faxNumber = null, + firstName = "Bob", + lastName = "McExample", + line1 = "100 Ravine Lane", + mobileNumber = "206 555 1212", + phoneNumber = "206 555 1212", + postalCode = "98110", + region = "WA", + taxpayerIdNumber = "123456789", + name = "Bob's Greatest Popcorn", + title = "Owner/CEO" + }); + + // Add a delay + System.Threading.Thread.Sleep(6 * 1000); + + // Assert that company setup succeeded + Assert.NotNull(TestCompany, "Test company should be created"); + Assert.True(TestCompany.nexus.Count > 0, "Test company should have nexus"); + Assert.True(TestCompany.locations.Count > 0, "Test company should have locations"); + + // Shouldn't fail + } catch (Exception ex) + { + Assert.Fail("Exception in SetUp: " + ex); + } + } + + + /// + /// Any cleanup required goes here + /// + [TearDown] + public void TearDown() + { + try + { + + // Re-fetch the company + var company = Client.GetCompany(TestCompany.id, null); + + // Flag this company as inactive + company.isActive = false; + var disableResult = Client.UpdateCompany(company.id, company); + + // Assert that it succeeded + Assert.NotNull(disableResult, "Should have been able to update this company"); + Assert.False(disableResult.isActive, "Company should have been deactivated"); + + // Shouldn't fail + } catch (Exception ex) + { + Assert.Fail("Exception in TearDown: " + ex); + } + } + #endregion + + /// + /// To debug this application, call app must be called with args[0] as username and args[1] as password + /// + [Test] + public void TransactionWorkflow() + { + Client.CallCompleted += Client_CallCompleted; + var tfn = System.IO.Path.GetTempFileName(); + Client.LogToFile(tfn); + + // Execute a transaction + var transaction = new TransactionBuilder(Client, TestCompany.companyCode, DocumentType.SalesInvoice, "ABC") + .WithAddress(TransactionAddressType.SingleLocation, "521 S Weller St", null, null, "Seattle", "WA", + "98104", "US") + .WithLine(100.0m, 1, "P0000000") + .WithLine(200m) + .WithExemptLine(50m, "NT") + .WithLineReference("Special Line Reference!", "Also this!") + .Create(); + + // Verify that the call was captured and logged + Assert.NotNull(lastEvent); + Assert.True(String.Equals(lastEvent.HttpVerb, "POST", StringComparison.CurrentCultureIgnoreCase)); + Assert.True(String.Equals(lastEvent.RequestUri.ToString(), "https://sandbox-rest.avatax.com/api/v2/transactions/create", StringComparison.CurrentCultureIgnoreCase)); + Assert.AreEqual(lastEvent.Code, HttpStatusCode.Created); + + // Verify that the log file was created + Assert.True(System.IO.File.Exists(tfn)); + + // Ensure this transaction was created, and has three lines, and has some tax + Assert.NotNull(transaction, "Transaction should have been created"); + Assert.True(transaction.totalTax > 0.0m, "Transaction should have had some tax"); + Assert.True(transaction.lines.Count == 3, "Transaction should have three lines"); + Assert.True(transaction.lines[2].ref1.Contains("Reference!"), "Line3 should have had a Ref1."); + + // Now commit that transaction + var commitResult = Client.CommitTransaction(TestCompany.companyCode, transaction.code, null, null, new CommitTransactionModel() { commit = true }); + + // Ensure that this transaction was committed + Assert.NotNull(commitResult, "Should have been able to call CommitTransaction"); + Assert.True(commitResult.status == DocumentStatus.Committed, "Transaction should have been committed"); + + // Now void the transaction + var voidResult = Client.VoidTransaction(TestCompany.companyCode, transaction.code, null, null, new VoidTransactionModel() + { + code = VoidReasonCode.DocVoided + }); + + // Ensure that the transaction was voided + Assert.NotNull(voidResult, "Should have been able to call VoidTransactoin"); + Assert.True(voidResult.status == DocumentStatus.Cancelled, "Transaction should have been voided"); + } + + private AvaTaxCallEventArgs lastEvent = null; + private void Client_CallCompleted(object sender, EventArgs e) + { + lastEvent = e as AvaTaxCallEventArgs; + } + [Ignore("Ignore Override")] + [Test] + public void TaxOverrideExample() + { + // Create base transaction. + var builder = new TransactionBuilder(Client, TestCompany.companyCode, DocumentType.SalesInvoice, + "TaxOverrideCustomerCode") + .WithAddress(TransactionAddressType.SingleLocation, "521 S Weller St", null, null, "Seattle", "WA", + "98104", "US") + .WithLine(100.0m, 1, "P0000000") + .WithLine(200m); + + var transaction = builder.Create(); + + // Ensure this transaction was created. + Assert.NotNull(transaction, "Transaction should have been created"); + + var taxOverrideList = new List(); + var item = new TransactionLineTaxAmountByTaxTypeModel(); + item.taxTypeId = "123"; + item.taxAmount = 10; + taxOverrideList.Add(item); + // Add Line-level TaxOverride. + var overrideTransaction = builder + .WithLineTaxOverride(TaxOverrideType.TaxAmount, "Tax Override Reason", 1) + .WithLine(300m, 1) + .WithLineTaxOverride(TaxOverrideType.TaxAmountByTaxType, "Another reason", 10, null, taxOverrideList) + .Create(); + + + // Ensure this transaction was created. + Assert.NotNull(overrideTransaction, "Transaction should have been created"); + + // Compare the two transactions. + Assert.AreEqual(overrideTransaction.totalTaxCalculated, transaction.totalTaxCalculated, "Total Tax Calculated should be the same."); + Assert.True(overrideTransaction.totalTax < transaction.totalTax, "Total Tax should not be the same. Overridden transaction should be smaller."); + + // Compare the transaction lines. + var overrideLine = overrideTransaction.lines[1]; + var line = transaction.lines[1]; + Assert.AreEqual(overrideLine.isItemTaxable, line.isItemTaxable); + Assert.AreEqual(overrideLine.taxCalculated, line.taxCalculated); + Assert.AreEqual(overrideLine.lineAmount, line.lineAmount); + Assert.AreEqual(1, overrideLine.tax); + Assert.True(overrideLine.tax < line.tax); + Assert.AreEqual(TaxOverrideType.TaxAmount, overrideLine.taxOverrideType); + } + } +} diff --git a/tests/net8.0/BatchTests.cs b/tests/net8.0/BatchTests.cs new file mode 100644 index 0000000..9c74c53 --- /dev/null +++ b/tests/net8.0/BatchTests.cs @@ -0,0 +1,198 @@ +using Avalara.AvaTax.RestClient; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Avalara.AvaTax.RestClient.Test.net80 +{ + [TestFixture] + public class BatchTests + { + public AvaTaxClient Client { get; set; } + public string CompanyCode { get; set; } + public CompanyModel TestCompany { get; set; } + + #region Setup / TearDown + /// + /// Create a company for use with these tests + /// + [SetUp] + public void Setup() + { + try + { + // Create a client and set up authentication + Client = new AvaTaxClient(typeof(TransactionTests).Name, + typeof(TransactionTests).GetTypeInfo().Assembly.ImageRuntimeVersion.ToString(), + Environment.MachineName, + AvaTaxEnvironment.Sandbox) + .WithSecurity(Environment.GetEnvironmentVariable("SANDBOX_USERNAME"), Environment.GetEnvironmentVariable("SANDBOX_PASSWORD")); + + // Verify that we can ping successfully + var pingResult = Client.Ping(); + + // Assert that ping succeeded + Assert.NotNull(pingResult, "Should be able to call Ping"); + Assert.True(pingResult.authenticated, "Environment variables should provide correct authentication"); + + // Create a basic company with nexus in the state of Washington + TestCompany = Client.CompanyInitialize(new CompanyInitializationModel() + { + city = "Bainbridge Island", + companyCode = Guid.NewGuid().ToString("N").Substring(0, 25), + country = "US", + email = "bob@example.org", + faxNumber = null, + firstName = "Bob", + lastName = "McExample", + line1 = "100 Ravine Lane", + mobileNumber = "206 555 1212", + phoneNumber = "206 555 1212", + postalCode = "98110", + region = "WA", + taxpayerIdNumber = "123456789", + name = "Bob's Hardware Store", + title = "Owner/CEO" + }); + + // Add a delay after creating a company + System.Threading.Thread.Sleep(6 * 1000); + + // Assert that company setup succeeded + Assert.NotNull(TestCompany, "Test company should be created"); + Assert.True(TestCompany.nexus.Count > 0, "Test company should have nexus"); + Assert.True(TestCompany.locations.Count > 0, "Test company should have locations"); + + // Shouldn't fail + } catch (Exception ex) + { + Assert.Fail("Exception in SetUp: " + ex); + } + } + + /// + /// Any cleanup required goes here + /// + [TearDown] + public void TearDown() + { + try + { + // Re-fetch the company + var company = Client.GetCompany(TestCompany.id, null); + + // Flag this company as inactive + company.isActive = false; + var disableResult = Client.UpdateCompany(company.id, company); + + // Assert that it succeeded + Assert.NotNull(disableResult, "Should have been able to update this company"); + Assert.False(disableResult.isActive, "Company should have been deactivated"); + + // Shouldn't fail + } catch (Exception ex) + { + Assert.Fail("Exception in TearDown: " + ex); + } + } + #endregion + + /// + /// + /// + [Test] + public async Task BatchesWorkflow() + { + // Raw batch CSV string. + const string transactionImport = @"ProcessCode,DocCode,DocType,DocDate,CompanyCode,CustomerCode,EntityUseCode,LineNo,TaxCode,TaxDate,ItemCode,Description,Qty,Amount,Discount,Ref1,Ref2,ExemptionNo,RevAcct,DestAddress,DestCity,DestRegion,DestPostalCode,DestCountry,OrigAddress,OrigCity,OrigRegion,OrigPostalCode,OrigCountry,LocationCode,SalesPersonCode,PurchaseOrderNo,CurrencyCode,ExchangeRate,ExchangeRateEffDate,PaymentDate,TaxIncluded,DestTaxRegion,OrigTaxRegion,Taxable,TaxType,TotalTax,CountryName,CountryCode,CountryRate,CountryTax,StateName,StateCode,StateRate,StateTax,CountyName,CountyCode,CountyRate,CountyTax,CityName,CityCode,CityRate,CityTax,Other1Name,Other1Code,Other1Rate,Other1Tax,Other2Name,Other2Code,Other2Rate,Other2Tax,Other3Name,Other3Code,Other3Rate,Other3Tax,Other4Name,Other4Code,Other4Rate,Other4Tax,ReferenceCode,BuyersVATNo +3,BS1323154187029MG10,2,16-Apr-14,,029MG10,,0000000001,SR060100,06-May-14,6500,REPAIRS & MAINTENANCE,,1980,,,0,,6500,119 N. 72nd St.,Omaha,NE,68114,,6923 MAPLE ST,OMAHA,NE,68104,,029,,,USD,,,,0,,,,,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +3,BS1323158772194036MAC1,2,04-Apr-14,,036MAC1,,0000000001,SC150158,06-May-14,6505,R&M HEAT / VENT / AIR COND,,322.26,,,0,,6505,1200 E. Mall Drive,Holland,OH,43528,,2875 CRANE WAY,NORTHWOOD,OH,43619,,036,,,USD,,,,0,,,,,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +3,BS1323159W206409036MLK,2,25-Mar-14,,036MLK,,0000000001,SR060100,06-May-14,6507,R&M OTHER,,449,,,31.43,,6507,1200 E. Mall Drive,Holland,OH,43528,,1214 JEFFERSON AVE,TOLEDO,OH,43604,,036,,,USD,,,,0,,,,,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +3,BS13231601596048MRP2,2,02-May-14,,048MRP2,,0000000001,SR060100,06-May-14,6520,FURNITURE REPAIRS,,60,,,0,,6520,2201 Hwy 75 North,Sherman,TX,75090,,P.O. BOX 125,POTTSBORO,TX,75076,,048,,,USD,,,,0,,,,,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +3,BS13231631591048MRP2,2,02-May-14,,048MRP2,,0000000001,SR060100,06-May-14,6520,FURNITURE REPAIRS,,58,,,0,,6520,2201 Hwy 75 North,Sherman,TX,75090,,P.O. BOX 125,POTTSBORO,TX,75076,,048,,,USD,,,,0,,,,,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +3,BS13231611594048MRP2,2,01-May-14,,048MRP2,,0000000001,SR060100,06-May-14,6520,FURNITURE REPAIRS,,65,,,0,,6520,2201 Hwy 75 North,Sherman,TX,75090,,P.O. BOX 125,POTTSBORO,TX,75076,,048,,,USD,,,,0,,,,,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +3,BS13231621593048MRP2,2,01-May-14,,048MRP2,,0000000001,SR060100,06-May-14,6520,FURNITURE REPAIRS,,75,,,0,,6520,2201 Hwy 75 North,Sherman,TX,75090,,P.O. BOX 125,POTTSBORO,TX,75076,,048,,,USD,,,,0,,,,,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +3,BS13231641587048MRP2,2,01-May-14,,048MRP2,,0000000001,SR060100,06-May-14,6520,FURNITURE REPAIRS,,60,,,0,,6520,2201 Hwy 75 North,Sherman,TX,75090,,P.O. BOX 125,POTTSBORO,TX,75076,,048,,,USD,,,,0,,,,,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +3,BS13231651582048MRP2,2,15-Mar-14,,048MRP2,,0000000001,SR060100,06-May-14,6520,FURNITURE REPAIRS,,47,,,0,,6520,2201 Hwy 75 North,Sherman,TX,75090,,P.O. BOX 125,POTTSBORO,TX,75076,,048,,,USD,,,,0,,,,,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"; + + // Let's Create the file portion of the batch request. + var batchFileModel = new BatchFileModel + { + name = "TransactionImport.csv", + content = Encoding.UTF8.GetBytes(transactionImport), + contentType = "text/csv", + fileExtension = "csv" + }; + + // Create the bare-minimum batch header info. + var batchRequest = new BatchModel + { + name = "AutomationTestBatch", + type = BatchType.TransactionImport, + files = new List { batchFileModel } + }; + + // Send the batch! + try + { + var batchResult = Client.CreateBatches(TestCompany.id, new List { batchRequest }); + Assert.NotNull(batchResult, "Batch not sent."); + Assert.True(batchResult.Count > 0, "No batches created."); + + // Check that the batch comes out of Waiting state using a linear backoff strategy. + var waiting = true; + BatchModel batchFetchResult = null; + for (var i = 1; i < 6; ++i) + { + await Task.Delay(i * 1000); + + batchFetchResult = Client.GetBatch(TestCompany.id, batchResult[0].id.Value); + Assert.NotNull(batchFetchResult, "Batch fetch unsuccessful."); + if (batchFetchResult.status.Value == BatchStatus.Waiting) continue; + waiting = false; + break; + } + Assert.True(waiting == false, $"Batch waiting too long. Check BatchId: {batchResult[0].id}"); + + // This batch is no longer in the Waiting state. Let's see it process. + var processing = true; + for (var i = 1; i < 11; ++i) + { + await Task.Delay(i * 1000); + + batchFetchResult = Client.GetBatch(TestCompany.id, batchResult[0].id.Value); + Assert.NotNull(batchFetchResult, "Batch fetch unsuccessful."); + if (batchFetchResult.status.Value == BatchStatus.Processing) continue; + processing = false; + break; + } + Assert.True(processing == false, $"Batch processing too long. Check BatchId: {batchResult[0].id}"); + + // This batch is done processing. + Assert.True((batchFetchResult.status.Value == BatchStatus.Errors || batchFetchResult.status.Value == BatchStatus.Completed), + $"BatchId: {batchResult[0].id} should either complete or error out."); + // Ensure that the number of records matches what we sent in. + Assert.AreEqual(9, batchFetchResult.currentRecord.Value); + + // Alright. Time to download the sent batch file. + var fileResult = Client.DownloadBatch(TestCompany.id, batchFetchResult.id.Value, batchFetchResult.files[0].id.Value); + Assert.NotNull(fileResult); + + // Compare what we got back with what we sent. + Assert.AreEqual(batchFetchResult.name + ".Input.CSV; filename*=UTF-8''" + batchFetchResult.name + ".Input.CSV", fileResult.Filename); + Assert.AreEqual(batchFileModel.content, fileResult.Data); + Assert.AreEqual(batchFileModel.contentType, fileResult.ContentType); + } catch (AvaTaxError e) + { + Assert.True(false, $"AvaTaxError: {e.error.error.details?[0].message}"); + } catch (Exception e) + { + Assert.True(false, $"Unknown Exception! {e.Message}"); + } + } + } +} diff --git a/tests/net8.0/CertificateTests.cs b/tests/net8.0/CertificateTests.cs new file mode 100644 index 0000000..11915bb --- /dev/null +++ b/tests/net8.0/CertificateTests.cs @@ -0,0 +1,144 @@ +using Avalara.AvaTax.RestClient; +using Newtonsoft.Json; +using NUnit.Framework; +using System; +using System.IO; +using System.Net; +using System.Reflection; + +namespace Avalara.AvaTax.RestClient.Test.net80 +{ + [TestFixture] + public class CertificateTests + { + public AvaTaxClient Client { get; set; } + public string CompanyCode { get; set; } + public int DefaultCompanyId { get; set; } + public CompanyModel TestCompany { get; set; } + + #region Setup / TearDown + /// + /// Create a company for use with these tests + /// + [SetUp] + public void Setup() + { + try { + // Create a client and set up authentication + Client = new AvaTaxClient(typeof(CertificateTests).Name, + typeof(CertificateTests).GetTypeInfo().Assembly.ImageRuntimeVersion.ToString(), + Environment.MachineName, + AvaTaxEnvironment.Sandbox) + .WithSecurity(Environment.GetEnvironmentVariable("SANDBOX_USERNAME"), Environment.GetEnvironmentVariable("SANDBOX_PASSWORD")); + + // Verify that we can ping successfully + var pingResult = Client.Ping(); + + // Assert that ping succeeded + Assert.NotNull(pingResult, "Should be able to call Ping"); + Assert.True(pingResult.authenticated, "Environment variables should provide correct authentication"); + + //Get the default company. + var defaultCompanyModel = Client.QueryCompanies(string.Empty, "isDefault EQ true", null, null, string.Empty).value[0]; + + DefaultCompanyId = defaultCompanyModel.id; + + // Create a basic company with nexus in the state of Washington + TestCompany = Client.CompanyInitialize(new CompanyInitializationModel() + { + city = "Bainbridge Island", + companyCode = Guid.NewGuid().ToString().Substring(0, 25), + country = "US", + email = "bob@example.org", + faxNumber = null, + firstName = "Bob", + lastName = "McExample", + line1 = "100 Ravine Lane", + mobileNumber = "206 555 1212", + phoneNumber = "206 555 1212", + postalCode = "98110", + region = "WA", + taxpayerIdNumber = "123456789", + name = "Bob's Greatest Popcorn", + title = "Owner/CEO" + }); + + // Add a delay + System.Threading.Thread.Sleep(6 * 1000); + + // Assert that company setup succeeded + Assert.NotNull(TestCompany, "Test company should be created"); + Assert.True(TestCompany.nexus.Count > 0, "Test company should have nexus"); + Assert.True(TestCompany.locations.Count > 0, "Test company should have locations"); + + // Shouldn't fail + } catch (Exception ex) { + Assert.Fail("Exception in SetUp: " + ex); + } + } + + + /// + /// Any cleanup required goes here + /// + [TearDown] + public void TearDown() + { + try { + + // Re-fetch the company + var company = Client.GetCompany(TestCompany.id, null); + + // Flag this company as inactive + company.isActive = false; + var disableResult = Client.UpdateCompany(company.id, company); + + // Assert that it succeeded + Assert.NotNull(disableResult, "Should have been able to update this company"); + Assert.False(disableResult.isActive, "Company should have been deactivated"); + + // Shouldn't fail + } catch (Exception ex) { + Assert.Fail("Exception in TearDown: " + ex); + } + } + #endregion + + /// + /// Tests the upload certificate image endpoint. + /// + [Test] + [Ignore("Ignore CertificateImageUploadTest")] + public void CertificateImageUploadTest() + { + //Get the cert number. The account needs to have CertCapture + //be provisioned, already have a certificate created, and the + //certificate needs to be valid. + var certs = Client.QueryCertificates(DefaultCompanyId, string.Empty, "valid EQ true", null, null, string.Empty).value; + var certId = certs[0].id.Value; + + //Get an image. + byte[] jpegByteArr = File.ReadAllBytes(Environment.GetEnvironmentVariable("TEST_IMAGE_PATH")); + + FileResult fileResult = new FileResult() + { + ContentType = "multipart/form-data", + Filename = "test_cert_image.jpg", + Data = jpegByteArr + }; + + //Send request. + var certUploadResult = Client.UploadCertificateImage(DefaultCompanyId, certId, fileResult); + + //Response should be "OK" + Assert.True(string.Equals(certUploadResult, "\"OK\"")); + + //Test download of image attachment. + var certAttachment = Client.DownloadCertificateImage(DefaultCompanyId, certId, null, CertificatePreviewType.Pdf); + Assert.NotNull(certAttachment); + Assert.True(string.Equals(certAttachment.ContentType, "application/pdf")); + Assert.True(certAttachment.Data.Length > 1000); + + } + } +} diff --git a/tests/net8.0/ErrorResultTests.cs b/tests/net8.0/ErrorResultTests.cs new file mode 100644 index 0000000..41fd7af --- /dev/null +++ b/tests/net8.0/ErrorResultTests.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using Avalara.AvaTax.RestClient; +using NUnit.Framework; + +namespace Avalara.AvaTax.RestClient.Test.net80 +{ + [TestFixture] + public class ErrorResultTests : TestBase + { + [Test] + public async Task NonJsonErrorResult() + { + AvaTaxError avataxError = null; + try + { + // make a client that points to a server that will give a 404 for ping + var client = new AvaTaxClient(GetType().Name, + GetType().GetTypeInfo().Assembly.ImageRuntimeVersion.ToString(), + Environment.MachineName, new Uri("https://www.google.com")); + + var result = await client.PingAsync().ConfigureAwait(false); + } + catch (AvaTaxError e) + { + avataxError = e; + } + + Assert.NotNull(avataxError); + Assert.True(avataxError.error.error.message.Contains("the response is in an unexpected format")); + Assert.True(avataxError.error.error.details[0].description.ToLower().Contains("not found")); + } + + [Test] + public async Task JsonErrorResult() + { + AvaTaxError avataxError = null; + try + { + var result = await _client.ChangePasswordAsync(new PasswordChangeModel + { + }).ConfigureAwait(false); + } + catch (AvaTaxError e) + { + avataxError = e; + } + + Assert.NotNull(avataxError); + Assert.True(avataxError.error.error.message.Contains("Field oldPassword is required")); + } + } +} diff --git a/tests/net8.0/FreeTaxRatesTest.cs b/tests/net8.0/FreeTaxRatesTest.cs new file mode 100644 index 0000000..7302654 --- /dev/null +++ b/tests/net8.0/FreeTaxRatesTest.cs @@ -0,0 +1,97 @@ +using Avalara.AvaTax.RestClient; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace Avalara.AvaTax.RestClient.Test.net80 +{ + [TestFixture] + class FreeTaxRatesTest + { + public AvaTaxClient _client; + + #region Setup / Teardown + /// + /// Create a company for use with these tests + /// + [SetUp] + public void Setup() + { + try { + // Create a client and set up authentication + _client = new AvaTaxClient(typeof(TransactionTests).Name, + typeof(TransactionTests).GetTypeInfo().Assembly.ImageRuntimeVersion.ToString(), + Environment.MachineName, + AvaTaxEnvironment.Sandbox) + .WithSecurity(Environment.GetEnvironmentVariable("SANDBOX_USERNAME"), Environment.GetEnvironmentVariable("SANDBOX_PASSWORD")); + } catch (Exception ex) { + Assert.Fail("Exception in SetUp: " + ex); + } + } + #endregion + + [Test] + public async Task FreeTaxRates() + { + // Call TaxRates for a few addresses and verify that the rates are nonzero + var tr = await _client.TaxRatesByAddressAsync("123 Main Street", null, null, "Irvine", "CA", "92615", "US"); + Assert.NotNull(tr); + Assert.True(tr.totalRate > 0.05m); + + // Washington + tr = await _client.TaxRatesByAddressAsync("522 Stadium Pl S", null, null, "Seattle", "WA", "98104", "US"); + Assert.NotNull(tr); + Assert.True(tr.totalRate > 0.05m); + + // By postal code for New York + tr = await _client.TaxRatesByPostalCodeAsync("US", "10010"); + Assert.NotNull(tr); + Assert.True(tr.totalRate > 0.05m); + } + + /// + /// Test the local rate by ZIP storage and retrieval. + /// + [Test] + [Ignore("This test will fail in Travis")] + public void StoreRatesByZipTest() + { + string path = Environment.GetEnvironmentVariable("ZIP_RATE_FILE_STORAGE_PATH"); + List zips = new List() { "12590", "98104" }; + + //Call the content caching helper. + AvaTaxOfflineHelper.StoreZipRateContent(_client, "US", zips, path); + + //Verify that the files were stored locally. + bool zipRateExists = AvaTaxOfflineHelper.VerifyLocalZipRateAvailable(zips[0], path); + Assert.True(zipRateExists); + zipRateExists = AvaTaxOfflineHelper.VerifyLocalZipRateAvailable(zips[1], path); + Assert.True(zipRateExists); + + //Verify that a bogus file does not exist locally. + zipRateExists = AvaTaxOfflineHelper.VerifyLocalZipRateAvailable("bogusZipFile.json", path); + Assert.False(zipRateExists); + + //Verify that the local file can be used for rate calculation. + var zipTaxRate = AvaTaxOfflineHelper.GetLocalTaxRateByZip(zips[1], path); + Assert.NotNull(zipTaxRate); + decimal result = 9.99m * zipTaxRate.totalRate.Value; + Assert.NotZero(result); + + //Test AvaTaxOfflineHelper Exception handling. + path = @"n:\someBadPath"; + try { + AvaTaxOfflineHelper.StoreZipRateContent(_client, "US", zips, path); + } catch (AvaTaxOfflineHelperException exc) { +#if PORTABLE + Assert.True(string.Equals(exc.InnerException.Message, "Could not find a part of the path 'n:\\someBadPath\\12590.json'.")); +#else + Assert.True(string.Equals(exc.Message, "An error occurred retrieving or storing the rate content. Please see inner exception for details.")); +#endif + } + } + } +} diff --git a/tests/net8.0/HttpClientTransactionTests.cs b/tests/net8.0/HttpClientTransactionTests.cs new file mode 100644 index 0000000..c0c79e1 --- /dev/null +++ b/tests/net8.0/HttpClientTransactionTests.cs @@ -0,0 +1,204 @@ +using Avalara.AvaTax.RestClient; +using Newtonsoft.Json; +using NUnit.Framework; +using System; +using System.Net; +using System.Reflection; + +namespace Avalara.AvaTax.RestClient.Test.net80 +{ + [TestFixture] + public class HttpClientTransactionTests + { + public AvaTaxClient Client { get; set; } + public string CompanyCode { get; set; } + public CompanyModel TestCompany { get; set; } + + #region Setup / TearDown + /// + /// Create a company for use with these tests + /// + [SetUp] + public void Setup() + { + try + { + var httpClient = new System.Net.Http.HttpClient() { Timeout = TimeSpan.FromMinutes(20) }; + // Create a client and set up authentication + Client = new AvaTaxClient(httpClient, typeof(HttpClientTransactionTests).Name, + typeof(HttpClientTransactionTests).GetTypeInfo().Assembly.ImageRuntimeVersion.ToString(), + Environment.MachineName, + AvaTaxEnvironment.Sandbox) + .WithSecurity(Environment.GetEnvironmentVariable("SANDBOX_USERNAME"), Environment.GetEnvironmentVariable("SANDBOX_PASSWORD")); + + // Verify that we can ping successfully + var pingResult = Client.Ping(); + + // Assert that ping succeeded + Assert.NotNull(pingResult, "Should be able to call Ping"); + Assert.True(pingResult.authenticated, "Environment variables should provide correct authentication"); + + // Create a basic company with nexus in the state of Washington + TestCompany = Client.CompanyInitialize(new CompanyInitializationModel() + { + city = "Bainbridge Island", + companyCode = Guid.NewGuid().ToString().Substring(0, 25), + country = "US", + email = "bob@example.org", + faxNumber = null, + firstName = "Bob", + lastName = "McExample", + line1 = "100 Ravine Lane", + mobileNumber = "206 555 1212", + phoneNumber = "206 555 1212", + postalCode = "98110", + region = "WA", + taxpayerIdNumber = "123456789", + name = "Bob's Greatest Popcorn", + title = "Owner/CEO" + }); + + // Add a delay + System.Threading.Thread.Sleep(6 * 1000); + + // Assert that company setup succeeded + Assert.NotNull(TestCompany, "Test company should be created"); + Assert.True(TestCompany.nexus.Count > 0, "Test company should have nexus"); + Assert.True(TestCompany.locations.Count > 0, "Test company should have locations"); + + // Shouldn't fail + } catch (Exception ex) + { + Assert.Fail("Exception in SetUp: " + ex); + } + } + + + /// + /// Any cleanup required goes here + /// + [TearDown] + public void TearDown() + { + try + { + + // Re-fetch the company + var company = Client.GetCompany(TestCompany.id, null); + + // Flag this company as inactive + company.isActive = false; + var disableResult = Client.UpdateCompany(company.id, company); + + // Assert that it succeeded + Assert.NotNull(disableResult, "Should have been able to update this company"); + Assert.False(disableResult.isActive, "Company should have been deactivated"); + + // Shouldn't fail + } catch (Exception ex) + { + Assert.Fail("Exception in TearDown: " + ex); + } + } + #endregion + + /// + /// To debug this application, call app must be called with args[0] as username and args[1] as password + /// + [Test] + public void TransactionWorkflow() + { + Client.CallCompleted += Client_CallCompleted; + var tfn = System.IO.Path.GetTempFileName(); + Client.LogToFile(tfn); + + // Execute a transaction + var transaction = new TransactionBuilder(Client, TestCompany.companyCode, DocumentType.SalesInvoice, "ABC") + .WithAddress(TransactionAddressType.SingleLocation, "521 S Weller St", null, null, "Seattle", "WA", + "98104", "US") + .WithLine(100.0m, 1, "P0000000") + .WithLine(200m) + .WithExemptLine(50m, "NT") + .WithLineReference("Special Line Reference!", "Also this!") + .Create(); + + // Verify that the call was captured and logged + Assert.NotNull(lastEvent); + Assert.True(String.Equals(lastEvent.HttpVerb, "POST", StringComparison.CurrentCultureIgnoreCase)); + Assert.True(String.Equals(lastEvent.RequestUri.ToString(), "https://sandbox-rest.avatax.com/api/v2/transactions/create", StringComparison.CurrentCultureIgnoreCase)); + Assert.AreEqual(lastEvent.Code, HttpStatusCode.Created); + + // Verify that the log file was created + Assert.True(System.IO.File.Exists(tfn)); + + // Ensure this transaction was created, and has three lines, and has some tax + Assert.NotNull(transaction, "Transaction should have been created"); + Assert.True(transaction.totalTax > 0.0m, "Transaction should have had some tax"); + Assert.True(transaction.lines.Count == 3, "Transaction should have three lines"); + Assert.True(transaction.lines[2].ref1.Contains("Reference!"), "Line3 should have had a Ref1."); + + // Now commit that transaction + var commitResult = Client.CommitTransaction(TestCompany.companyCode, transaction.code, null, null, new CommitTransactionModel() { commit = true }); + + // Ensure that this transaction was committed + Assert.NotNull(commitResult, "Should have been able to call CommitTransaction"); + Assert.True(commitResult.status == DocumentStatus.Committed, "Transaction should have been committed"); + + // Now void the transaction + var voidResult = Client.VoidTransaction(TestCompany.companyCode, transaction.code, null, null, new VoidTransactionModel() + { + code = VoidReasonCode.DocVoided + }); + + // Ensure that the transaction was voided + Assert.NotNull(voidResult, "Should have been able to call VoidTransactoin"); + Assert.True(voidResult.status == DocumentStatus.Cancelled, "Transaction should have been voided"); + } + + private AvaTaxCallEventArgs lastEvent = null; + private void Client_CallCompleted(object sender, EventArgs e) + { + lastEvent = e as AvaTaxCallEventArgs; + } + + [Test] + + public void TaxOverrideExample() + { + // Create base transaction. + var builder = new TransactionBuilder(Client, TestCompany.companyCode, DocumentType.SalesInvoice, + "TaxOverrideCustomerCode") + .WithAddress(TransactionAddressType.SingleLocation, "521 S Weller St", null, null, "Seattle", "WA", + "98104", "US") + .WithLine(100.0m, 1, "P0000000") + .WithLine(200m); + + var transaction = builder.Create(); + + // Ensure this transaction was created. + Assert.NotNull(transaction, "Transaction should have been created"); + + // Add Line-level TaxOverride. + var overrideTransaction = builder + .WithLineTaxOverride(TaxOverrideType.TaxAmount, "Tax Override Reason", 1) + .Create(); + + // Ensure this transaction was created. + Assert.NotNull(overrideTransaction, "Transaction should have been created"); + + // Compare the two transactions. + Assert.AreEqual(overrideTransaction.totalTaxCalculated, transaction.totalTaxCalculated, "Total Tax Calculated should be the same."); + Assert.True(overrideTransaction.totalTax < transaction.totalTax, "Total Tax should not be the same. Overridden transaction should be smaller."); + + // Compare the transaction lines. + var overrideLine = overrideTransaction.lines[1]; + var line = transaction.lines[1]; + Assert.AreEqual(overrideLine.isItemTaxable, line.isItemTaxable); + Assert.AreEqual(overrideLine.taxCalculated, line.taxCalculated); + Assert.AreEqual(overrideLine.lineAmount, line.lineAmount); + Assert.AreEqual(1, overrideLine.tax); + Assert.True(overrideLine.tax < line.tax); + Assert.AreEqual(TaxOverrideType.TaxAmount, overrideLine.taxOverrideType); + } + } +} diff --git a/tests/net8.0/NexusTests.cs b/tests/net8.0/NexusTests.cs new file mode 100644 index 0000000..d144b07 --- /dev/null +++ b/tests/net8.0/NexusTests.cs @@ -0,0 +1,169 @@ +using Avalara.AvaTax.RestClient; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; + +namespace Avalara.AvaTax.RestClient.Test.net80 +{ + [TestFixture] + [Ignore("This test is not yet implemented")] + public class NexusTests + { + public AvaTaxClient Client { get; set; } + public string CompanyCode { get; set; } + public CompanyModel TestCompany { get; set; } + + #region Setup / TearDown + /// + /// Create a company for use with these tests + /// + [SetUp] + public void Setup() + { + try { + // Create a client and set up authentication + Client = new AvaTaxClient(typeof(TransactionTests).Name, + typeof(TransactionTests).GetTypeInfo().Assembly.ImageRuntimeVersion.ToString(), + Environment.MachineName, + AvaTaxEnvironment.Sandbox) + .WithSecurity(Environment.GetEnvironmentVariable("SANDBOX_USERNAME"), Environment.GetEnvironmentVariable("SANDBOX_PASSWORD")); + + // Verify that we can ping successfully + var pingResult = Client.Ping(); + + // Assert that ping succeeded + Assert.NotNull(pingResult, "Should be able to call Ping"); + Assert.True(pingResult.authenticated, "Environment variables should provide correct authentication"); + + // Create a basic company with nexus in the state of Washington + TestCompany = Client.CompanyInitialize(new CompanyInitializationModel() + { + city = "Bainbridge Island", + companyCode = Guid.NewGuid().ToString().Substring(0, 25), + country = "US", + email = "bob@example.org", + faxNumber = null, + firstName = "Bob", + lastName = "McExample", + line1 = "100 Ravine Lane", + mobileNumber = "206 555 1212", + phoneNumber = "206 555 1212", + postalCode = "98110", + region = "WA", + taxpayerIdNumber = "123456789", + name = "Bob's Greatest Popcorn", + title = "Owner/CEO" + }); + + // Assert that company setup succeeded + Assert.NotNull(TestCompany, "Test company should be created"); + Assert.True(TestCompany.nexus.Count > 0, "Test company should have nexus"); + Assert.True(TestCompany.locations.Count > 0, "Test company should have locations"); + + // Shouldn't fail + } catch (Exception ex) { + Assert.Fail("Exception in SetUp: " + ex.ToString()); + } + } + + + /// + /// Any cleanup required goes here + /// + [TearDown] + public void TearDown() + { + try { + + // Re-fetch the company + var company = Client.GetCompany(TestCompany.id, null); + + // Flag this company as inactive + company.isActive = false; + var disableResult = Client.UpdateCompany(company.id, company); + + // Assert that it succeeded + Assert.NotNull(disableResult, "Should have been able to update this company"); + Assert.False(disableResult.isActive, "Company should have been deactivated"); + + // Shouldn't fail + } catch (Exception ex) { + Assert.Fail("Exception in TearDown: " + ex.ToString()); + } + } + #endregion + + [Test] + public void CreateAndDeleteNexus() + { + var nexusModels = new List(); + + var stateNexus = new NexusModel + { + id = 0, + companyId = TestCompany.id, + country = "US", + region = "AL", + jurisTypeId = JurisTypeId.STA, + jurisCode = "01", + jurisName = "ALABAMA", + shortName = "AL", + signatureCode = "", + stateAssignedNo = "", + nexusTypeId = NexusTypeId.SalesOrSellersUseTax, + hasLocalNexus = true, + hasPermanentEstablishment = true, + effectiveDate = new DateTime(2008, 07, 01), + endDate = new DateTime(2019, 07, 01) + }; + + var cityNexus = new NexusModel + { + id = 0, + companyId = TestCompany.id, + country = "US", + region = "AL", + jurisTypeId = JurisTypeId.CIT, + jurisCode = "00124", + jurisName = "ABBEVILLE", + shortName = "ABBEVILLE", + signatureCode = "", + stateAssignedNo = "9356", + nexusTypeId = NexusTypeId.SalesTax, + hasLocalNexus = true, + hasPermanentEstablishment = false, + effectiveDate = new DateTime(2008, 07, 01), + endDate = new DateTime(2018, 07, 01) + }; + + nexusModels.Add(stateNexus); + nexusModels.Add(cityNexus); + + var nexusModelsAdded = Client.CreateNexus(TestCompany.id, new List { stateNexus, cityNexus }); + + // Get State nexus + NexusModel getALNexus = null; + try { + getALNexus = Client.GetNexus(TestCompany.id, nexusModelsAdded[0].id.Value, null); + } catch (Exception) { } + Assert.NotNull(getALNexus); + + var fetchedUSNexus = new List { getALNexus }; + + // Get City Nexus + NexusModel getCityNexus = null; + try { + getCityNexus = Client.GetNexus(TestCompany.id, nexusModelsAdded[1].id.Value, null); + } catch (Exception) { } + Assert.NotNull(getALNexus); + + fetchedUSNexus.Add(getCityNexus); + + // Delete Nexus + var errorResult = Client.DeleteNexus(TestCompany.id, nexusModelsAdded[1].id.Value, null); + Assert.NotNull(errorResult); + } + } +} \ No newline at end of file diff --git a/tests/net8.0/SetUpFixture.cs b/tests/net8.0/SetUpFixture.cs new file mode 100644 index 0000000..8586be1 --- /dev/null +++ b/tests/net8.0/SetUpFixture.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using System.Runtime.Versioning; +using System.Text; +using System.Threading.Tasks; +using Avalara.AvaTax.RestClient; +using NUnit.Framework; + +namespace Avalara.AvaTax.RestClient.Test.net80 +{ + [SetUpFixture] + public class SetUpFixture + { + [OneTimeSetUp] + public void RunBeforeAnyTests() + { + var stringBuilder = new StringBuilder(); + + stringBuilder + .AppendLine($"Hosting Framework Runtime Version: {GetRuntimeFrameworkVersion(GetType().Assembly)}") + .AppendLine($"Hosting Framework Version: {GetFrameworkVersion(GetType().Assembly)}") + .AppendLine($"Target Framework Runtime Version: {GetRuntimeFrameworkVersion(typeof(AvaTaxClient).Assembly)}") + .AppendLine($"Target Framework Version: {GetFrameworkVersion(typeof(AvaTaxClient).Assembly)}"); + + TestContext.Progress.WriteLine(stringBuilder); + } + + private static string GetRuntimeFrameworkVersion(Assembly assembly) + { + var imageRuntimeVersion = assembly + .ImageRuntimeVersion; + + return imageRuntimeVersion; + } + + private static string GetFrameworkVersion(Assembly assembly) + { + var targetFrameAttribute = assembly.GetCustomAttributes(true) + .OfType().FirstOrDefault(); + if (targetFrameAttribute == null) + { + return ".NET 2, 3 or 3.5"; + } + + return targetFrameAttribute.FrameworkName; + } + } +} diff --git a/tests/net8.0/TestBase.cs b/tests/net8.0/TestBase.cs new file mode 100644 index 0000000..332d184 --- /dev/null +++ b/tests/net8.0/TestBase.cs @@ -0,0 +1,103 @@ +using Avalara.AvaTax.RestClient; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Text; + +namespace Avalara.AvaTax.RestClient.Test.net80 +{ + public class TestBase + { + protected AvaTaxClient _client; + protected string _companyCode; + protected CompanyModel _testCompany; + + [SetUp] + public void Setup() + { + _client = null; + _testCompany = null; + try + { + // Create a client and set up authentication + _client = new AvaTaxClient(GetType().Name, + GetType().GetTypeInfo().Assembly.ImageRuntimeVersion.ToString(), + Environment.MachineName, + AvaTaxEnvironment.Sandbox) + .WithSecurity(Environment.GetEnvironmentVariable("SANDBOX_USERNAME"), Environment.GetEnvironmentVariable("SANDBOX_PASSWORD")); + + // Verify that we can ping successfully + var pingResult = _client.Ping(); + + // Assert that ping succeeded + Assert.NotNull(pingResult, "Should be able to call Ping"); + Assert.True(pingResult.authenticated, "Environment variables should provide correct authentication"); + + _companyCode = Guid.NewGuid().ToString("N").Substring(0, 25); + // Create a basic company with nexus in the state of Washington + _testCompany = _client.CompanyInitialize(new CompanyInitializationModel() + { + city = "Bainbridge Island", + companyCode = _companyCode, + country = "US", + email = "bob@example.org", + faxNumber = null, + firstName = "Bob", + lastName = "McExample", + line1 = "100 Ravine Lane", + mobileNumber = "206 555 1212", + phoneNumber = "206 555 1212", + postalCode = "98110", + region = "WA", + taxpayerIdNumber = "123456789", + name = "Bob's Hardware Store", + title = "Owner/CEO" + }); + + // Add a delay after creating a company + System.Threading.Thread.Sleep(6 * 1000); + + // Assert that company setup succeeded + Assert.NotNull(_testCompany, "Test company should be created"); + Assert.True(_testCompany.nexus.Count > 0, "Test company should have nexus"); + Assert.True(_testCompany.locations.Count > 0, "Test company should have locations"); + + // Shouldn't fail + } + catch (Exception ex) + { + Assert.Fail("Exception in SetUp: " + ex); + } + } + + + /// + /// Any cleanup required goes here + /// + [TearDown] + public void TearDown() + { + try + { + // Re-fetch the company + var company = _client.GetCompany(_testCompany.id, null); + + // Flag this company as inactive + company.isActive = false; + var disableResult = _client.UpdateCompany(company.id, company); + + // Assert that it succeeded + Assert.NotNull(disableResult, "Should have been able to update this company"); + Assert.False(disableResult.isActive, "Company should have been deactivated"); + + // Shouldn't fail + } + catch (Exception ex) + { + Assert.Fail("Exception in TearDown: " + ex); + } + } + } +} diff --git a/tests/net8.0/TransactionTests.cs b/tests/net8.0/TransactionTests.cs new file mode 100644 index 0000000..ab5ef12 --- /dev/null +++ b/tests/net8.0/TransactionTests.cs @@ -0,0 +1,211 @@ +using Avalara.AvaTax.RestClient; +using Newtonsoft.Json; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Net; +using System.Reflection; + +namespace Avalara.AvaTax.RestClient.Test.net80 +{ + [TestFixture] + public class TransactionTests + { + public AvaTaxClient Client { get; set; } + public string CompanyCode { get; set; } + public CompanyModel TestCompany { get; set; } + + #region Setup / TearDown + /// + /// Create a company for use with these tests + /// + [SetUp] + public void Setup() + { + try + { + // Create a client and set up authentication + Client = new AvaTaxClient(typeof(TransactionTests).Name, + typeof(TransactionTests).GetTypeInfo().Assembly.ImageRuntimeVersion.ToString(), + Environment.MachineName, + AvaTaxEnvironment.Sandbox) + .WithSecurity(Environment.GetEnvironmentVariable("SANDBOX_USERNAME"), Environment.GetEnvironmentVariable("SANDBOX_PASSWORD")); + + // Verify that we can ping successfully + var pingResult = Client.Ping(); + + // Assert that ping succeeded + Assert.NotNull(pingResult, "Should be able to call Ping"); + Assert.True(pingResult.authenticated, "Environment variables should provide correct authentication"); + + // Create a basic company with nexus in the state of Washington + TestCompany = Client.CompanyInitialize(new CompanyInitializationModel() + { + city = "Bainbridge Island", + companyCode = Guid.NewGuid().ToString().Substring(0, 25), + country = "US", + email = "bob@example.org", + faxNumber = null, + firstName = "Bob", + lastName = "McExample", + line1 = "100 Ravine Lane", + mobileNumber = "206 555 1212", + phoneNumber = "206 555 1212", + postalCode = "98110", + region = "WA", + taxpayerIdNumber = "123456789", + name = "Bob's Greatest Popcorn", + title = "Owner/CEO" + }); + + // Add a delay + System.Threading.Thread.Sleep(6 * 1000); + + // Assert that company setup succeeded + Assert.NotNull(TestCompany, "Test company should be created"); + Assert.True(TestCompany.nexus.Count > 0, "Test company should have nexus"); + Assert.True(TestCompany.locations.Count > 0, "Test company should have locations"); + + // Shouldn't fail + } catch (Exception ex) + { + Assert.Fail("Exception in SetUp: " + ex); + } + } + + + /// + /// Any cleanup required goes here + /// + [TearDown] + public void TearDown() + { + try + { + + // Re-fetch the company + var company = Client.GetCompany(TestCompany.id, null); + + // Flag this company as inactive + company.isActive = false; + var disableResult = Client.UpdateCompany(company.id, company); + + // Assert that it succeeded + Assert.NotNull(disableResult, "Should have been able to update this company"); + Assert.False(disableResult.isActive, "Company should have been deactivated"); + + // Shouldn't fail + } catch (Exception ex) + { + Assert.Fail("Exception in TearDown: " + ex); + } + } + #endregion + + /// + /// To debug this application, call app must be called with args[0] as username and args[1] as password + /// + [Test] + public void TransactionWorkflow() + { + Client.CallCompleted += Client_CallCompleted; + var tfn = System.IO.Path.GetTempFileName(); + Client.LogToFile(tfn); + + // Execute a transaction + var transaction = new TransactionBuilder(Client, TestCompany.companyCode, DocumentType.SalesInvoice, "ABC") + .WithAddress(TransactionAddressType.SingleLocation, "521 S Weller St", null, null, "Seattle", "WA", + "98104", "US") + .WithLine(100.0m, 1, "P0000000") + .WithLine(200m) + .WithExemptLine(50m, "NT") + .WithLineReference("Special Line Reference!", "Also this!") + .Create(); + + // Verify that the call was captured and logged + Assert.NotNull(lastEvent); + Assert.True(String.Equals(lastEvent.HttpVerb, "POST", StringComparison.CurrentCultureIgnoreCase)); + Assert.True(String.Equals(lastEvent.RequestUri.ToString(), "https://sandbox-rest.avatax.com/api/v2/transactions/create", StringComparison.CurrentCultureIgnoreCase)); + Assert.AreEqual(lastEvent.Code, HttpStatusCode.Created); + + // Verify that the log file was created + Assert.True(System.IO.File.Exists(tfn)); + + // Ensure this transaction was created, and has three lines, and has some tax + Assert.NotNull(transaction, "Transaction should have been created"); + Assert.True(transaction.totalTax > 0.0m, "Transaction should have had some tax"); + Assert.True(transaction.lines.Count == 3, "Transaction should have three lines"); + Assert.True(transaction.lines[2].ref1.Contains("Reference!"), "Line3 should have had a Ref1."); + + // Now commit that transaction + var commitResult = Client.CommitTransaction(TestCompany.companyCode, transaction.code, null, null, new CommitTransactionModel() { commit = true }); + + // Ensure that this transaction was committed + Assert.NotNull(commitResult, "Should have been able to call CommitTransaction"); + Assert.True(commitResult.status == DocumentStatus.Committed, "Transaction should have been committed"); + + // Now void the transaction + var voidResult = Client.VoidTransaction(TestCompany.companyCode, transaction.code, null, null, new VoidTransactionModel() + { + code = VoidReasonCode.DocVoided + }); + + // Ensure that the transaction was voided + Assert.NotNull(voidResult, "Should have been able to call VoidTransactoin"); + Assert.True(voidResult.status == DocumentStatus.Cancelled, "Transaction should have been voided"); + } + + private AvaTaxCallEventArgs lastEvent = null; + private void Client_CallCompleted(object sender, EventArgs e) + { + lastEvent = e as AvaTaxCallEventArgs; + } + [Ignore("Ignore Override")] + [Test] + public void TaxOverrideExample() + { + // Create base transaction. + var builder = new TransactionBuilder(Client, TestCompany.companyCode, DocumentType.SalesInvoice, + "TaxOverrideCustomerCode") + .WithAddress(TransactionAddressType.SingleLocation, "521 S Weller St", null, null, "Seattle", "WA", + "98104", "US") + .WithLine(100.0m, 1, "P0000000") + .WithLine(200m); + + var transaction = builder.Create(); + + // Ensure this transaction was created. + Assert.NotNull(transaction, "Transaction should have been created"); + + var taxOverrideList = new List(); + var item = new TransactionLineTaxAmountByTaxTypeModel(); + item.taxTypeId = "123"; + item.taxAmount = 10; + taxOverrideList.Add(item); + // Add Line-level TaxOverride. + var overrideTransaction = builder + .WithLineTaxOverride(TaxOverrideType.TaxAmount, "Tax Override Reason", 1) + .WithLine(300m, 1) + .WithLineTaxOverride(TaxOverrideType.TaxAmountByTaxType, "Another reason", 10, null, taxOverrideList) + .Create(); + + + // Ensure this transaction was created. + Assert.NotNull(overrideTransaction, "Transaction should have been created"); + + // Compare the two transactions. + Assert.AreEqual(overrideTransaction.totalTaxCalculated, transaction.totalTaxCalculated, "Total Tax Calculated should be the same."); + Assert.True(overrideTransaction.totalTax < transaction.totalTax, "Total Tax should not be the same. Overridden transaction should be smaller."); + + // Compare the transaction lines. + var overrideLine = overrideTransaction.lines[1]; + var line = transaction.lines[1]; + Assert.AreEqual(overrideLine.isItemTaxable, line.isItemTaxable); + Assert.AreEqual(overrideLine.taxCalculated, line.taxCalculated); + Assert.AreEqual(overrideLine.lineAmount, line.lineAmount); + Assert.AreEqual(1, overrideLine.tax); + Assert.True(overrideLine.tax < line.tax); + Assert.AreEqual(TaxOverrideType.TaxAmount, overrideLine.taxOverrideType); + } + } +}