Import Xero sales invoice flat-file

Feature complete and tested
This commit is contained in:
Bobbie Hodgetts
2020-01-31 11:32:54 +00:00
committed by GitHub
parent aea82da897
commit 88159d4cc3
34 changed files with 1166 additions and 358 deletions

View File

@@ -24,7 +24,6 @@ namespace BealeEngineering.Accounts
string conString = ConfigurationManager.ConnectionStrings["BealeEngSQLDb"].ToString();
var inst = new Core.Test.Autoexec(conString);
inst.Start();
}
}
}

View File

@@ -42,6 +42,7 @@
</Reference>
<Reference Include="Microsoft.VisualBasic" />
<Reference Include="System" />
<Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System.Core" />
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
@@ -50,6 +51,7 @@
<HintPath>..\packages\System.Threading.Tasks.Extensions.4.5.2\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll</HintPath>
</Reference>
<Reference Include="System.Transactions" />
<Reference Include="System.Web" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
@@ -58,38 +60,44 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Data\Database\Client\PurchaseOrderAllocationCreate.cs" />
<Compile Include="Data\Database\Client\PurchaseOrderGet.cs" />
<Compile Include="Data\Database\Client\PurchaseOrderAllocationGet.cs" />
<Compile Include="Data\Database\Sale\InvoiceGet.cs" />
<Compile Include="Data\Database\Sale\InvoiceHeaderGet.cs" />
<Compile Include="Data\Xero\SaleInvoiceGet.cs" />
<Compile Include="Data\Database\Client\CreatePurchaseOrderAllocation.cs" />
<Compile Include="Data\Database\Client\ReadPurchaseOrder.cs" />
<Compile Include="Data\Database\Client\ReadPurchaseOrderAllocation.cs" />
<Compile Include="Data\Database\Contact\UpdateContact.cs" />
<Compile Include="Data\Database\Sale\CreateInvoice.cs" />
<Compile Include="Data\Database\Sale\UpdateInvoice.cs" />
<Compile Include="Model\Contact\Address.cs" />
<Compile Include="Data\Database\Contact\CreateContact.cs" />
<Compile Include="Data\Database\Sale\ReadInvoice.cs" />
<Compile Include="Data\Database\Sale\ReadInvoiceHeader.cs" />
<Compile Include="Data\Xero\ReadXeroInvoiceFlatFile.cs" />
<Compile Include="Logic\Client\PurchaseOrderAutoAllocate.cs" />
<Compile Include="Data\Database\Connection.cs" />
<Compile Include="Data\Database\Contact\ContactHeaderGet.cs" />
<Compile Include="Data\Database\Client\PurchaseOrderHeaderGet.cs" />
<Compile Include="Logic\Import\wipXeroInvoiceFlatFile.cs" />
<Compile Include="Data\Database\Contact\ReadContact.cs" />
<Compile Include="Data\Database\Client\ReadPurchaseOrderHeader.cs" />
<Compile Include="Logic\Import\XeroInvoiceFlatFile.cs" />
<Compile Include="Logic\Utilities\CSVGetRFC4180Compliant.cs" />
<Compile Include="Logic\Utilities\Reflection.cs" />
<Compile Include="Logic\Utilities\StringCheck.cs" />
<Compile Include="Model\Client\PurchaseOrder.cs" />
<Compile Include="Model\Client\PurchaseOrderHeader.cs" />
<Compile Include="Model\Client\PurchaseOrderLine.cs" />
<Compile Include="Model\Contact\ContactHeader.cs" />
<Compile Include="Model\Contact\Contact.cs" />
<Compile Include="Model\Import\XeroInvoiceFlatFile.cs" />
<Compile Include="Model\Import\XeroInvoiceFlatFileDTO.cs" />
<Compile Include="Model\Sale\Invoice.cs" />
<Compile Include="Model\Sale\InvoiceHeader.cs" />
<Compile Include="Model\Sale\InvoiceLine.cs" />
<Compile Include="Model\ValidateModel.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Test\Autoexec.cs" />
<Compile Include="Test\Client\PurchaseOrder.cs" />
<Compile Include="Test\Contact\Contact.cs" />
<Compile Include="Test\Import\ImportFlatfile.cs" />
<Compile Include="Test\Sales\Invoice.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="Data\Xero\FlatFile\" />
<Folder Include="Service\" />
<Folder Include="Logic\Sale\" />
<Folder Include="Service\Contact\" />
<Folder Include="UI\" />
</ItemGroup>
<ItemGroup>

View File

@@ -8,9 +8,9 @@ using System.Transactions;
namespace BealeEngineering.Core.Data.Database.Client
{
public class PurchaseOrderAllocationCreate : Connection
public class CreatePurchaseOrderAllocation : Connection
{
public PurchaseOrderAllocationCreate(string sqlConnectionString) : base(sqlConnectionString)
public CreatePurchaseOrderAllocation(string sqlConnectionString) : base(sqlConnectionString)
{
}

View File

@@ -9,9 +9,9 @@ using System.Threading.Tasks;
namespace BealeEngineering.Core.Data.Database.Client
{
public class PurchaseOrderGet : PurchaseOrderHeaderGet
public class ReadPurchaseOrder : ReadPurchaseOrderHeader
{
public PurchaseOrderGet(string sqlConnectionString) : base(sqlConnectionString)
public ReadPurchaseOrder(string sqlConnectionString) : base(sqlConnectionString)
{
}
@@ -73,7 +73,7 @@ namespace BealeEngineering.Core.Data.Database.Client
var orderDictionary = new Dictionary<int, Model.Client.PurchaseOrder>();
var orderList = conn.Query<Model.Client.PurchaseOrder, Model.Client.PurchaseOrderLine, Model.Client.PurchaseOrder>
var orderList = conn.Query<Model.Client.PurchaseOrder, Model.Client.PurchaseOrder.PurchaseOrderLine, Model.Client.PurchaseOrder>
(
sqlString,
(order, orderDetail) =>
@@ -83,7 +83,7 @@ namespace BealeEngineering.Core.Data.Database.Client
if (!orderDictionary.TryGetValue(order.ClientPurchaseOrderID, out orderEntry))
{
orderEntry = order;
orderEntry.OrderLineList = new List<Model.Client.PurchaseOrderLine>();
orderEntry.OrderLineList = new List<Model.Client.PurchaseOrder.PurchaseOrderLine>();
orderDictionary.Add(orderEntry.ClientPurchaseOrderID, orderEntry);
}

View File

@@ -7,9 +7,9 @@ using System.Threading.Tasks;
namespace BealeEngineering.Core.Data.Database.Client
{
public class PurchaseOrderAllocationGet : Connection
public class ReadPurchaseOrderAllocation : Connection
{
public PurchaseOrderAllocationGet(string sqlConnectionString) : base(sqlConnectionString)
public ReadPurchaseOrderAllocation(string sqlConnectionString) : base(sqlConnectionString)
{
}

View File

@@ -8,9 +8,9 @@ using Dapper;
namespace BealeEngineering.Core.Data.Database.Client
{
public class PurchaseOrderHeaderGet : Connection
public class ReadPurchaseOrderHeader : Connection
{
public PurchaseOrderHeaderGet(string sqlConnectionString) : base(sqlConnectionString)
public ReadPurchaseOrderHeader(string sqlConnectionString) : base(sqlConnectionString)
{
}

View File

@@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Dapper;
using System.Data.SqlClient;
namespace BealeEngineering.Core.Data.Database
{

View File

@@ -1,160 +0,0 @@
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BealeEngineering.Core.Data.Database.Contact
{
public class ContactHeaderGet : Connection
{
public ContactHeaderGet(string sqlConnectionString) : base(sqlConnectionString)
{
}
private Dictionary<int, Model.Contact.ContactHeader> cacheContact = new Dictionary<int, Model.Contact.ContactHeader>();
public int NumberRequested = 0;
public int NumberRetrived = 0;
public int NumberRetrivedUnique = 0;
public int NumberFailed = 0;
public Model.Contact.ContactHeader ById(int contactId, bool cacheClear = false)
{
var contactIdList = new List<int>();
contactIdList.Add(contactId);
var resultList = ByIdList(contactIdList, cacheClear);
if (cacheContact.ContainsKey(contactId))
{
return cacheContact[contactId];
}
else
{
return null;
}
}
public Dictionary<int, Model.Contact.ContactHeader> ByIdList(List<int> contactIdList, bool cacheClear = false)
{
ClearStats();
// check list for values
if (!contactIdList.Any())
{
return null;
}
if (cacheClear)
{
cacheContact = new Dictionary<int, Model.Contact.ContactHeader>();
}
// build list of contactIds to lookup from db
var idLookupList = new List<int>();
foreach (int contactId in contactIdList)
{
if (!cacheContact.ContainsKey(contactId))
{
idLookupList.Add(contactId);
}
}
// query db and add to cache
if (idLookupList.Any())
{
//build sql string
string sqlString = null;
foreach (int id in idLookupList)
{
int count = 0;
if (count == 0)
{
sqlString = " WHERE ContactId=@contactId" + count;
}
else
{
sqlString = sqlString + " OR ContactId=@contactId" + count;
}
count = count + 1;
}
sqlString = @"
SELECT
ContactID
,ContactName
,EmailAddress
FROM Contact
" + sqlString;
// run query
using (SqlConnection conn = new SqlConnection(sqlConnectionString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(sqlString, conn))
{
foreach (int id in idLookupList)
{
int count = 0;
cmd.Parameters.AddWithValue("@contactId" + count, id);
count = count + 1;
}
using (SqlDataReader reader = cmd.ExecuteReader())
{
if (!reader.HasRows)
{
// do something
}
else
{
while (reader.Read())
{
// load data into cache
var contact = new Model.Contact.ContactHeader();
contact.ContactId = reader.GetInt32(0);
contact.ContactName = reader.GetString(1);
contact.EmailAddress = reader.GetString(2);
if (cacheContact.ContainsKey(contact.ContactId))
{
cacheContact.Remove(contact.ContactId);
}
cacheContact.Add(contact.ContactId, contact);
}
}
}
}
}
}
// build and return list
var returnList = new Dictionary<int, Model.Contact.ContactHeader>();
foreach (var contactId in contactIdList)
{
NumberRequested = NumberRequested + 1;
if (cacheContact.ContainsKey(contactId))
{
if (!returnList.ContainsKey(contactId))
{
returnList.Add(contactId, cacheContact[contactId]);
}
}
else
{
NumberFailed = NumberFailed + 1;
}
}
NumberRetrived = NumberRequested - NumberFailed;
NumberRetrivedUnique = returnList.Count();
return returnList;
}
private void ClearStats()
{
NumberRequested = 0;
NumberRetrived = 0;
NumberRetrivedUnique = 0;
NumberFailed = 0;
}
}
}

View File

@@ -0,0 +1,29 @@
using Dapper;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Transactions;
namespace BealeEngineering.Core.Data.Database.Contact
{
public class CreateContact : Connection
{
public CreateContact(string sqlConnectionString) : base(sqlConnectionString)
{
}
public void ByContact(Model.Contact.Contact contact)
{
var list = new List<Model.Contact.Contact> { contact };
ByContactList(list);
}
public void ByContactList(List<Model.Contact.Contact> contactList)
{
var updateContact = new UpdateContact(sqlConnectionString);
updateContact.ByContactList(contactList, true);
}
}
}

View File

@@ -0,0 +1,191 @@
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using Dapper;
namespace BealeEngineering.Core.Data.Database.Contact
{
public class ReadContact : Connection
{
public ReadContact(string sqlConnectionString) : base(sqlConnectionString)
{
CacheInnit();
}
private Dictionary<int, Model.Contact.Contact> cacheId;
private Dictionary<string, int> cacheName;
public Model.Contact.Contact ByContactId(int contactId, bool cacheClear = false)
{
var contactIdList = new List<int>();
contactIdList.Add(contactId);
var resultList = ByContactId(contactIdList, cacheClear);
if (resultList == null || !resultList.Any())
{ return null; }
else
{ return resultList[0]; }
}
public List<Model.Contact.Contact> ByContactId(List<int> contactIdList, bool cacheClear = false)
{
if (cacheClear)
{ CacheInnit(); }
if (!contactIdList.Any())
{ return null; }
// find items not in cache
var dbQuery = new List<int>();
foreach (var id in contactIdList)
{
if (!cacheId.ContainsKey(id))
{ dbQuery.Add(id); }
}
// populate cache with values from db
if (dbQuery.Any())
{
string whereString = @"
AND ContactID IN @contactID";
var parameters = new DynamicParameters();
parameters.Add("@contactID", dbQuery);
Execute(whereString, parameters);
}
// query chache for return
var returnList = new List<Model.Contact.Contact>();
foreach (var id in contactIdList)
{
if (cacheId.ContainsKey(id))
{ returnList.Add(cacheId[id]); }
}
if (returnList.Any())
{ return returnList; }
else
{ return null; }
}
/// <summary>
/// Get contact by unique contact name.
/// </summary>
/// <param name="contactName">Unique contact name</param>
/// <param name="cacheClear">Force database read</param>
/// <returns>Contact Header information</returns>
public Model.Contact.Contact ByContactName(string contactName, bool cacheClear = false)
{
var contactNameList = new List<string>();
contactNameList.Add(contactName);
var resultList = ByContactName(contactNameList, cacheClear);
if (resultList == null || !resultList.Any())
{ return null; }
else
{ return resultList[0]; }
}
public List<Model.Contact.Contact> ByContactName(List<string> contactNameList, bool cacheClear = false)
{
if (cacheClear)
{ CacheInnit(); }
if (!contactNameList.Any())
{ return null; }
// find items not in cache
List<string> dbQuery = new List<string>();
foreach (var name in contactNameList)
{
if (!cacheName.ContainsKey(name))
{ dbQuery.Add(name); }
}
// populate cache with values from db
if (dbQuery.Any())
{
string whereString = @"
AND ContactName IN @contactName";
var parameters = new DynamicParameters();
parameters.Add("@contactName", dbQuery);
Execute(whereString, parameters);
}
// query chache for return
var returnList = new List<Model.Contact.Contact>();
foreach (var name in contactNameList)
{
if (cacheName.ContainsKey(name))
{ returnList.Add(cacheId[cacheName[name]]); }
}
if (returnList.Any())
{ return returnList; }
else
{ return null; }
}
private void CacheAdd(Model.Contact.Contact contact)
{
if (cacheId.ContainsKey(contact.ContactId))
{ CacheRemove(contact); }
cacheId.Add(contact.ContactId, contact);
cacheName.Add(contact.ContactName, contact.ContactId);
}
private void CacheInnit()
{
cacheId = new Dictionary<int, Model.Contact.Contact>();
cacheName = new Dictionary<string, int>();
}
private void CacheRemove(Model.Contact.Contact contact)
{
cacheId.Remove(contact.ContactId);
cacheName.Remove(contact.ContactName);
}
private void Execute(string sqlWhere, DynamicParameters parameters)
{
var contactList = new List<Model.Contact.Contact>();
string sqlString = @"
SELECT
[ContactID]
,[ContactName]
,[EmailAddress]
,[PrimaryPersonFirstName]
,[PrimaryPersonSurname]
,[PrimaryPersonEmail]
,[POAddressLine1] AS AddressLine1
,[POAddressLine2] AS AddressLine2
,[POAddressLine3] AS AddressLine3
,[POAddressLine4] AS AddressLine4
,[POAddressCity] AS City
,[POAddressRegion] AS Region
,[POAddressPostalCode] AS PostalCode
,[POAddressCountry] AS Country
FROM Contact
WHERE 1=1" + sqlWhere;
// make the call
using (SqlConnection conn = new SqlConnection(sqlConnectionString))
{
conn.Open();
contactList = conn.Query<Model.Contact.Contact, Model.Contact.Address, Model.Contact.Contact>
(
sqlString,
(contact, address) =>
{
contact.PostalAddress = address;
return contact;
},
parameters,
splitOn: "AddressLine1")
.Distinct()
.ToList();
}
foreach (var contact in contactList)
{
CacheAdd(contact);
}
}
}
}

View File

@@ -0,0 +1,157 @@
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Transactions;
using Dapper;
namespace BealeEngineering.Core.Data.Database.Contact
{
public class UpdateContact : Connection
{
public UpdateContact(string sqlConnectionString) : base(sqlConnectionString)
{
}
public void ByContact(Model.Contact.Contact contact, bool insertNew = false)
{
var list = new List<Model.Contact.Contact> { contact };
ByContactList(list, insertNew);
}
public void ByContactList(List<Model.Contact.Contact> contactList, bool insertNew = false)
{
// check the list
CheckList(ref contactList);
using (TransactionScope scope = new TransactionScope())
using (SqlConnection conn = new SqlConnection(sqlConnectionString))
{
conn.Open();
foreach (var contact in contactList)
{
// build the sql string
string sqlString = @"
UPDATE Contact
SET
[ContactName] = @contactName
,[EmailAddress] = @emailAddress
,[PrimaryPersonFirstName] = @primaryPersonFirstName
,[PrimaryPersonSurname] = @primaryPersonSurname
,[PrimaryPersonEmail] = @primaryPersonEmail
,[POAddressLine1] = @poAddressLine1
,[POAddressLine2] = @poAddressLine2
,[POAddressLine3] = @poAddressLine3
,[POAddressLine4] = @poAddressLine4
,[POAddressCity] = @poAddressCity
,[POAddressRegion] = @poAddressRegion
,[POAddressPostalCode] = @poAddressPostalCode
,[POAddressCountry] = @poAddressCountry
WHERE ContactName = @contactName";
if (insertNew)
{
sqlString = sqlString + @"
IF @@ROWCOUNT = 0
INSERT INTO Contact(
[ContactName]
,[EmailAddress]
,[PrimaryPersonFirstName]
,[PrimaryPersonSurname]
,[PrimaryPersonEmail]
,[POAddressLine1]
,[POAddressLine2]
,[POAddressLine3]
,[POAddressLine4]
,[POAddressCity]
,[POAddressRegion]
,[POAddressPostalCode]
,[POAddressCountry]
)
VALUES(
@contactName
,@emailAddress
,@primaryPersonFirstName
,@primaryPersonSurname
,@primaryPersonEmail
,@poAddressLine1
,@poAddressLine2
,@poAddressLine3
,@poAddressLine4
,@poAddressCity
,@poAddressRegion
,@poAddressPostalCode
,@poAddressCountry
)";
}
using (SqlCommand cmd = new SqlCommand(sqlString, conn))
{
cmd.Parameters.AddWithValue("@contactName", contact.ContactName);
if (!string.IsNullOrWhiteSpace(contact.EmailAddress))
{ cmd.Parameters.AddWithValue("@emailAddress", contact.EmailAddress); }
else { cmd.Parameters.AddWithValue("@emailAddress", DBNull.Value); }
cmd.Parameters.AddWithValue("@primaryPersonFirstName", DBNull.Value);
cmd.Parameters.AddWithValue("@primaryPersonSurname", DBNull.Value);
cmd.Parameters.AddWithValue("@primaryPersonEmail", DBNull.Value);
if (contact.PostalAddressIsSet)
{
if (!string.IsNullOrWhiteSpace(contact.PostalAddress.AddressLine1))
{ cmd.Parameters.AddWithValue("@poAddressLine1", contact.PostalAddress.AddressLine1); }
else { cmd.Parameters.AddWithValue("@poAddressLine1", DBNull.Value); }
if (!string.IsNullOrWhiteSpace(contact.PostalAddress.AddressLine2))
{ cmd.Parameters.AddWithValue("@poAddressLine2", contact.PostalAddress.AddressLine2); }
else { cmd.Parameters.AddWithValue("@poAddressLine2", DBNull.Value); }
if (!string.IsNullOrWhiteSpace(contact.PostalAddress.AddressLine3))
{ cmd.Parameters.AddWithValue("@poAddressLine3", contact.PostalAddress.AddressLine3); }
else { cmd.Parameters.AddWithValue("@poAddressLine3", DBNull.Value); }
if (!string.IsNullOrWhiteSpace(contact.PostalAddress.AddressLine4))
{ cmd.Parameters.AddWithValue("@poAddressLine4", contact.PostalAddress.AddressLine4); }
else { cmd.Parameters.AddWithValue("@poAddressLine4", DBNull.Value); }
if (!string.IsNullOrWhiteSpace(contact.PostalAddress.City))
{ cmd.Parameters.AddWithValue("@poAddressCity", contact.PostalAddress.City); }
else { cmd.Parameters.AddWithValue("@poAddressCity", DBNull.Value); }
if (!string.IsNullOrWhiteSpace(contact.PostalAddress.Region))
{ cmd.Parameters.AddWithValue("@poAddressRegion", contact.PostalAddress.Region); }
else { cmd.Parameters.AddWithValue("@poAddressRegion", DBNull.Value); }
if (!string.IsNullOrWhiteSpace(contact.PostalAddress.PostalCode))
{ cmd.Parameters.AddWithValue("@poAddressPostalCode", contact.PostalAddress.PostalCode); }
else { cmd.Parameters.AddWithValue("@poAddressPostalCode", DBNull.Value); }
if (!string.IsNullOrWhiteSpace(contact.PostalAddress.Country))
{ cmd.Parameters.AddWithValue("@poAddressCountry", contact.PostalAddress.Country); }
else { cmd.Parameters.AddWithValue("@poAddressCountry", DBNull.Value); }
}
else
{
cmd.Parameters.AddWithValue("@poAddressLine1", DBNull.Value);
cmd.Parameters.AddWithValue("@poAddressLine2", DBNull.Value);
cmd.Parameters.AddWithValue("@poAddressLine3", DBNull.Value);
cmd.Parameters.AddWithValue("@poAddressLine4", DBNull.Value);
cmd.Parameters.AddWithValue("@poAddressCity", DBNull.Value);
cmd.Parameters.AddWithValue("@poAddressRegion", DBNull.Value);
cmd.Parameters.AddWithValue("@poAddressPostalCode", DBNull.Value);
cmd.Parameters.AddWithValue("@poAddressCountry", DBNull.Value);
}
int effected = (int)cmd.ExecuteNonQuery();
}
}
scope.Complete();
}
}
private void CheckList(ref List<Model.Contact.Contact> contactList)
{
var tempDic = new Dictionary<string, int>();
foreach(var item in contactList)
{
if (tempDic.ContainsKey(item.ContactName))
{ throw new Exception("Duplicate contact name in list."); }
else { tempDic.Add(item.ContactName, 0); }
if (!item.IsValid())
{ throw new Exception("Validation check failed: " + item.ValidationResults[0]); }
}
}
}
}

View File

@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BealeEngineering.Core.Data.Database.Sale
{
public class CreateInvoice : Connection
{
public CreateInvoice(string sqlConnectionString) : base(sqlConnectionString)
{
}
public void ByInvoice(Model.Sale.Invoice invoice)
{
var invoiceList = new List<Model.Sale.Invoice> { invoice };
ByInvoiceList(invoiceList);
}
public void ByInvoiceList(List<Model.Sale.Invoice> invoiceList)
{
var updateInvoice = new UpdateInvoice(sqlConnectionString);
updateInvoice.ByInvoiceList(invoiceList, true);
}
}
}

View File

@@ -8,9 +8,9 @@ using System.Threading.Tasks;
namespace BealeEngineering.Core.Data.Database.Sale
{
public class InvoiceGet : InvoiceHeaderGet
public class ReadInvoice : ReadInvoiceHeader
{
public InvoiceGet(string sqlConnectionString) : base(sqlConnectionString)
public ReadInvoice(string sqlConnectionString) : base(sqlConnectionString)
{
}
@@ -45,14 +45,15 @@ namespace BealeEngineering.Core.Data.Database.Sale
var parameters = new DynamicParameters();
string sqlString = @"
SELECT SaleInvoice.SaleInvoiceID
,SaleInvoice.ContactID
,SaleInvoice.SaleInvoiceNumber
,SaleInvoice.InvoiceDate
,Contact.ContactName
,SaleInvoice.DateDue
,SaleInvoice.Reference
,SaleInvoice.CurrencyCode
,SaleInvoice.InvoiceTotal
,SaleInvoice.TaxTotal
,SaleInvoice.IsCreditNote
,SaleInvoiceLine.SaleInvoiceLineID
,SaleInvoiceLine.SaleInvoiceID
,SaleInvoiceLine.LineNumber
@@ -66,6 +67,7 @@ namespace BealeEngineering.Core.Data.Database.Sale
,SaleInvoiceLine.TaxAmount
,SaleInvoiceLine.LineAmount
FROM SaleInvoice
INNER JOIN Contact ON SaleInvoice.ContactID = Contact.ContactID
LEFT OUTER JOIN SaleInvoiceLine ON SaleInvoice.SaleInvoiceID = SaleInvoiceLine.SaleInvoiceID";
AddSqlWhereString(ref sqlString, ref parameters);
@@ -82,7 +84,7 @@ namespace BealeEngineering.Core.Data.Database.Sale
var invoiceDictionary = new Dictionary<int, Model.Sale.Invoice>();
var invoiceList = conn.Query<Model.Sale.Invoice, Model.Sale.InvoiceLine, Model.Sale.Invoice>
var invoiceList = conn.Query<Model.Sale.Invoice, Model.Sale.Invoice.InvoiceLine, Model.Sale.Invoice>
(
sqlString,
(invoice, invoiceDetail) =>
@@ -92,7 +94,7 @@ namespace BealeEngineering.Core.Data.Database.Sale
if (!invoiceDictionary.TryGetValue(invoice.SaleInvoiceID, out invoiceEntry))
{
invoiceEntry = invoice;
invoiceEntry.InvoiceLineList = new List<Model.Sale.InvoiceLine>();
invoiceEntry.InvoiceLineList = new List<Model.Sale.Invoice.InvoiceLine>();
invoiceDictionary.Add(invoiceEntry.SaleInvoiceID, invoiceEntry);
}

View File

@@ -8,9 +8,9 @@ using System.Threading.Tasks;
namespace BealeEngineering.Core.Data.Database.Sale
{
public class InvoiceHeaderGet : Connection
public class ReadInvoiceHeader : Connection
{
public InvoiceHeaderGet(string sqlConnectionString) : base(sqlConnectionString)
public ReadInvoiceHeader(string sqlConnectionString) : base(sqlConnectionString)
{
}
@@ -130,17 +130,18 @@ namespace BealeEngineering.Core.Data.Database.Sale
// build the sql string and dapper parameters
var parameters = new DynamicParameters();
string sqlString = @"
SELECT SaleInvoiceID
,ContactID
,SaleInvoiceNumber
,InvoiceDate
,DateDue
,Reference
,CurrencyCode
,InvoiceTotal
,TaxTotal
,IsCreditNote
FROM SaleInvoice";
SELECT SaleInvoice.SaleInvoiceID
,SaleInvoice.SaleInvoiceNumber
,SaleInvoice.InvoiceDate
,SaleInvoice.DateDue
,SaleInvoice.Reference
,SaleInvoice.CurrencyCode
,SaleInvoice.InvoiceTotal
,SaleInvoice.TaxTotal
,SaleInvoice.IsCreditNote
,Contact.ContactName
FROM SaleInvoice
INNER JOIN Contact ON SaleInvoice.ContactID = Contact.ContactID";
AddSqlWhereString(ref sqlString, ref parameters);

View File

@@ -0,0 +1,201 @@
using Dapper;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Transactions;
namespace BealeEngineering.Core.Data.Database.Sale
{
public class UpdateInvoice : Connection
{
public UpdateInvoice(string sqlConnectionString) : base(sqlConnectionString)
{
}
public void ByInvoice(Model.Sale.Invoice invoice, bool insertNew = false)
{
var invoiceList = new List<Model.Sale.Invoice> { invoice };
ByInvoiceList(invoiceList, insertNew);
}
public void ByInvoiceList(List<Model.Sale.Invoice> invoiceList, bool insertNew = false)
{
// check the list
CheckList(ref invoiceList);
using (TransactionScope scope = new TransactionScope())
using (SqlConnection conn = new SqlConnection(sqlConnectionString))
{
conn.Open();
// get list of id from db
var dic = new Dictionary<string, int>();
string sqlString = @"
SELECT SaleInvoiceNumber, SaleInvoiceID
FROM saleInvoice
WHERE SaleInvoiceNumber IN @saleInvoiceNumber;";
var parameters = new DynamicParameters();
parameters.Add("@saleInvoiceNumber", invoiceList.Select(o => o.SaleInvoiceNumber).ToList());
using (var reader = conn.ExecuteReader(sqlString, parameters))
{
while (reader.Read())
{
if (!dic.ContainsKey(reader.GetString(0)))
{
dic.Add(reader.GetString(0), reader.GetInt32(1));
}
}
}
foreach (var invoice in invoiceList)
{
int saleInvoiceId = 0;
if (dic.ContainsKey(invoice.SaleInvoiceNumber))
{
saleInvoiceId = dic[invoice.SaleInvoiceNumber];
sqlString = @"
UPDATE SaleInvoice
SET
[ContactID] = (SELECT ContactID FROM Contact WHERE ContactName = @contactName)
,[SaleInvoiceNumber] = @saleInvoiceNumber
,[InvoiceDate] = @invoiceDate
,[DateDue] = @dateDue
,[Reference] = @reference
,[CurrencyCode] = @currencyCode
,[InvoiceTotal] = @invoiceTotal
,[TaxTotal] = @taxTotal
,[IsCreditNote] = @isCreditNote
WHERE SaleInvoiceID = @saleInvoiceId;
DELETE FROM SaleInvoiceLine WHERE SaleInvoiceID = @saleInvoiceId;";
}
else if (insertNew)
{
sqlString = @"
INSERT INTO SaleInvoice(
[ContactID]
,[SaleInvoiceNumber]
,[InvoiceDate]
,[DateDue]
,[Reference]
,[CurrencyCode]
,[InvoiceTotal]
,[TaxTotal]
,[IsCreditNote]
)
OUTPUT INSERTED.SaleInvoiceID
VALUES(
(SELECT ContactID FROM Contact WHERE ContactName = @contactName)
@saleInvoiceNumber
@invoiceDate
@dateDue
@reference
@currencyCode
@invoiceTotal
@taxTotal
@isCreditNote
)";
}
else
{
continue;
}
using (SqlCommand cmd = new SqlCommand(sqlString, conn))
{
cmd.Parameters.AddWithValue("@contactName", invoice.ContactName);
cmd.Parameters.AddWithValue("@saleInvoiceNumber", invoice.SaleInvoiceNumber);
cmd.Parameters.AddWithValue("@invoiceDate", invoice.InvoiceDate);
if (invoice.DueDate == null) { cmd.Parameters.AddWithValue("@dateDue", DBNull.Value); }
else { cmd.Parameters.AddWithValue("@dateDue", invoice.DueDate); }
cmd.Parameters.AddWithValue("@reference", invoice.Reference);
cmd.Parameters.AddWithValue("@currencyCode", invoice.CurrencyCode);
cmd.Parameters.AddWithValue("@invoiceTotal", invoice.InvoiceTotal);
cmd.Parameters.AddWithValue("@taxTotal", invoice.TaxTotal);
cmd.Parameters.AddWithValue("@isCreditNote", invoice.IsCreditNote);
if (saleInvoiceId == 0)
{
saleInvoiceId = (int)cmd.ExecuteScalar();
}
else
{
cmd.Parameters.AddWithValue("@saleInvoiceId", saleInvoiceId);
int effected = cmd.ExecuteNonQuery();
}
}
// deal with lines
foreach (var line in invoice.InvoiceLineList)
{
sqlString = @"
INSERT INTO SaleInvoiceLine(
[SaleInvoiceID]
,[LineNumber]
,[InventoryItemCode]
,[Description]
,[Quantity]
,[UnitAmount]
,[Discount]
,[AccountCode]
,[TaxType]
,[TaxAmount]
,[LineAmount]
)
VALUES(
@saleInvoiceID
,@lineNumber
,@inventoryItemCode
,@description
,@quantity
,@unitAmount
,@discount
,@accountCode
,@taxType
,@taxAmount
,@lineAmount
)";
using (SqlCommand cmd = new SqlCommand(sqlString, conn))
{
cmd.Parameters.AddWithValue("@saleInvoiceID", saleInvoiceId);
cmd.Parameters.AddWithValue("@lineNumber", line.LineNumber);
if (string.IsNullOrWhiteSpace(line.InventoryItemCode)) { cmd.Parameters.AddWithValue("@inventoryItemCode", DBNull.Value); }
else { cmd.Parameters.AddWithValue("@inventoryItemCode", line.InventoryItemCode); }
if (string.IsNullOrWhiteSpace(line.Description)) { cmd.Parameters.AddWithValue("@description", DBNull.Value); }
else { cmd.Parameters.AddWithValue("@description", line.Description); }
cmd.Parameters.AddWithValue("@quantity", line.Quantity);
cmd.Parameters.AddWithValue("@unitAmount", line.UnitAmount);
if (line.Discount == null) { cmd.Parameters.AddWithValue("@discount", DBNull.Value); }
else { cmd.Parameters.AddWithValue("@discount", line.Discount); }
if (string.IsNullOrWhiteSpace(line.AccountCode)) { cmd.Parameters.AddWithValue("@accountCode", DBNull.Value); }
else { cmd.Parameters.AddWithValue("@accountCode", line.AccountCode); }
if (string.IsNullOrWhiteSpace(line.TaxType)) { cmd.Parameters.AddWithValue("@taxType", DBNull.Value); }
else { cmd.Parameters.AddWithValue("@taxType", line.TaxType); }
cmd.Parameters.AddWithValue("@taxAmount", line.TaxAmount);
cmd.Parameters.AddWithValue("@lineAmount", line.LineAmount);
cmd.ExecuteNonQuery();
}
}
}
scope.Complete();
}
}
private void CheckList(ref List<Model.Sale.Invoice> invoiceList)
{
var tempDic = new Dictionary<string, int>();
foreach (var item in invoiceList)
{
if (tempDic.ContainsKey(item.SaleInvoiceNumber))
{ throw new Exception("Duplicate contact name in list."); }
else { tempDic.Add(item.SaleInvoiceNumber, 0); }
if (!item.IsValid())
{ throw new Exception("Validation check failed: " + item.ValidationResults[0]); }
}
}
}
}

View File

@@ -10,9 +10,9 @@ using System.Threading.Tasks;
namespace BealeEngineering.Core.Data.Xero.FlatFile
{
public class ImportInvoice
public class ReadXeroInvoiceFlatFile
{
public ImportInvoice()
public ReadXeroInvoiceFlatFile()
{
FileInputPath =
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)
@@ -31,7 +31,7 @@ namespace BealeEngineering.Core.Data.Xero.FlatFile
/// </summary>
/// <param name="filePath"></param>
/// <returns>Dictionary, Invoice numbers against data.</returns>
public Dictionary<string, Model.Import.XeroInvoiceFlatFile> ByFilePath(string filePath)
public List<Model.Import.XeroInvoiceFlatFile> ByFilePath(string filePath)
{
/* So here's the rub. Any field in a CSV doc that has a double quote wihtin, must be enclosed by double quotes and
* the double quote within must be 'escaped' by a double quote.
@@ -64,7 +64,7 @@ namespace BealeEngineering.Core.Data.Xero.FlatFile
dto = csv.GetRecords<Model.Import.XeroInvoiceFlatFileDTO>().ToList();
}
return ConvertFlatDTO(ref dto);
return ConvertFlatDTO(ref dto).Values.ToList();
}
private Dictionary<string, Model.Import.XeroInvoiceFlatFile> ConvertFlatDTO(ref List<Model.Import.XeroInvoiceFlatFileDTO> flatData)

View File

@@ -27,7 +27,7 @@ namespace BealeEngineering.Core.Logic.Client
Init();
// get list of unallocated invoices
var poAlloInstance = new Data.Database.Client.PurchaseOrderAllocationGet(SqlConnectionString);
var poAlloInstance = new Data.Database.Client.ReadPurchaseOrderAllocation(SqlConnectionString);
var invoiceIdList = poAlloInstance.GetUnallocatedInvoiceId();
// nothing to allocate
@@ -42,7 +42,7 @@ namespace BealeEngineering.Core.Logic.Client
}
// get invoice header info
var invoiceInst = new Data.Database.Sale.InvoiceHeaderGet(SqlConnectionString);
var invoiceInst = new Data.Database.Sale.ReadInvoiceHeader(SqlConnectionString);
var invoiceList = invoiceInst.GetBySaleInvoiceId(invoiceIdList);
// create lookup list for matching
@@ -72,7 +72,7 @@ namespace BealeEngineering.Core.Logic.Client
//get client POs from db that match reference list
var referenceList = new List<string>();
foreach(var item in lookupList) { referenceList.Add(item.Item2); }
var poInstance = new Data.Database.Client.PurchaseOrderGet(SqlConnectionString);
var poInstance = new Data.Database.Client.ReadPurchaseOrder(SqlConnectionString);
poInstance.Reference = referenceList.Distinct().ToList();
poInstance.ReturnIsClosed = true; // <--------------------------------------------------change to false after
var clientPoList = poInstance.GetByFilters();
@@ -90,7 +90,7 @@ namespace BealeEngineering.Core.Logic.Client
// match contact ID
foreach (var po in clientPoList)
{
if (po.ContactID == lookupList[i].Item1.ContactID)
if (po.Contact.ContactName == lookupList[i].Item1.ContactName)
{
// match PO reference
string reference = lookupList[i].Item2;
@@ -143,7 +143,7 @@ namespace BealeEngineering.Core.Logic.Client
// update db table
if (invoiceIdToPoLineId.Count > 0)
{
var instance2 = new Data.Database.Client.PurchaseOrderAllocationCreate(SqlConnectionString);
var instance2 = new Data.Database.Client.CreatePurchaseOrderAllocation(SqlConnectionString);
InvoiceMatched = instance2.ByDictionary(invoiceIdToPoLineId);
}
IsComplete = true;

View File

@@ -0,0 +1,136 @@
using CsvHelper;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualBasic.FileIO;
namespace BealeEngineering.Core.Logic.Import
{
public class XeroInvoiceFlatFile
{
private List<Model.Import.XeroInvoiceFlatFile> XeroInvoiceData { get; set; }
private List<Model.Sale.Invoice> DbInvoiceData { get; set; }
public XeroInvoiceFlatFile(string sqlConnectionString)
{
SqlConnectionString = sqlConnectionString;
}
private string SqlConnectionString { get; set; }
public void ByFilePath(string filePath, bool updateContacts = true)
{
// get xero data
var data = new Data.Xero.FlatFile.ReadXeroInvoiceFlatFile();
XeroInvoiceData = data.ByFilePath(filePath);
// update db contacts
UpdateContacts();
// populate/map xero data to invoice model list
MapInvoiceDetails();
//check
if (XeroInvoiceData.Count != DbInvoiceData.Count)
{
throw new Exception("Something went wrong while mapping the data.");
}
// send list to database (it will get validated in data layer)
if (DbInvoiceData.Any())
{
var updateInvoice = new Data.Database.Sale.UpdateInvoice(SqlConnectionString);
updateInvoice.ByInvoiceList(DbInvoiceData);
}
}
/// <summary>
/// Get a dictionary of contacts, referenced by unique 'Contact Name'. Any contacts not found in db are
/// automatically added.
/// </summary>
/// <returns></returns>
private void UpdateContacts()
{
var dicContacts = new Dictionary<string, Model.Contact.Contact>();
for (var i = 0; i < XeroInvoiceData.Count; i++)
{
if (!dicContacts.ContainsKey(XeroInvoiceData[0].ContactName))
{
dicContacts.Add(XeroInvoiceData[0].ContactName, MapContactDetails(XeroInvoiceData[0]));
}
}
if (dicContacts.Any())
{
var updateContact = new Data.Database.Contact.UpdateContact(SqlConnectionString);
updateContact.ByContactList(dicContacts.Values.ToList());
}
}
private Model.Contact.Contact MapContactDetails(Model.Import.XeroInvoiceFlatFile xeroData)
{
var contact = new Model.Contact.Contact();
contact.ContactName = xeroData.ContactName;
if (string.IsNullOrWhiteSpace(xeroData.EmailAddress)) { contact.EmailAddress = null; }
else { contact.EmailAddress = xeroData.EmailAddress; }
var address = new Model.Contact.Address();
address.AddressLine1 = xeroData.POAddressLine1;
address.AddressLine2 = xeroData.POAddressLine2;
address.AddressLine3 = xeroData.POAddressLine3;
address.AddressLine4 = xeroData.POAddressLine4;
address.City = xeroData.POCity;
address.Country = xeroData.POCountry;
address.PostalCode = xeroData.POPostalCode;
address.Region = xeroData.PORegion;
if (address.IsValid()) { contact.PostalAddress = address; }
return contact;
}
private void MapInvoiceDetails()
{
DbInvoiceData = new List<Model.Sale.Invoice>();
for (var i = 0; i < XeroInvoiceData.Count; i++)
{
if (XeroInvoiceData[i] == null) { throw new NullReferenceException(); }
var invoice = new Model.Sale.Invoice();
invoice.ContactName = XeroInvoiceData[i].ContactName;
invoice.CurrencyCode = XeroInvoiceData[i].Currency;
invoice.DueDate = XeroInvoiceData[i].DueDate;
invoice.InvoiceDate = XeroInvoiceData[i].InvoiceDate;
invoice.InvoiceTotal = XeroInvoiceData[i].Total;
if (XeroInvoiceData[i].Type == "Sales invoice") { invoice.IsCreditNote = false; }
else if (XeroInvoiceData[i].Type == "Sales credit note") { invoice.IsCreditNote = true; }
else { throw new FormatException("Unknow value '" + XeroInvoiceData[i].Type + "' found in 'Type' field"); }
invoice.Reference = XeroInvoiceData[i].Reference;
invoice.SaleInvoiceNumber = XeroInvoiceData[i].InvoiceNumber;
invoice.TaxTotal = XeroInvoiceData[i].TaxTotal;
invoice.InvoiceLineList = new List<Model.Sale.Invoice.InvoiceLine>();
for (int j = 0; j < XeroInvoiceData[i].LineItems.Count; j++)
{
if (XeroInvoiceData[i].LineItems[j] == null) { throw new NullReferenceException(); }
var invoiceLine = new Model.Sale.Invoice.InvoiceLine();
invoiceLine.LineNumber = j + 1;
invoiceLine.AccountCode = XeroInvoiceData[i].LineItems[j].AccountCode;
invoiceLine.Description = XeroInvoiceData[i].LineItems[j].Description;
invoiceLine.Discount = XeroInvoiceData[i].LineItems[j].Discount;
invoiceLine.InventoryItemCode = XeroInvoiceData[i].LineItems[j].InventoryItemCode;
invoiceLine.Quantity = XeroInvoiceData[i].LineItems[j].Quantity;
invoiceLine.TaxAmount = XeroInvoiceData[i].LineItems[j].TaxAmount;
invoiceLine.TaxType = XeroInvoiceData[i].LineItems[j].TaxType;
invoiceLine.UnitAmount = XeroInvoiceData[i].LineItems[j].UnitAmount;
//check, as line amount is same as calculated in model
if (invoiceLine.LineAmount != XeroInvoiceData[i].LineItems[j].LineAmount)
{
throw new Exception("Imported line total does not equal caluclated.");
}
invoice.InvoiceLineList.Add(invoiceLine);
}
DbInvoiceData.Add(invoice);
}
}
}
}

View File

@@ -1,54 +0,0 @@
using CsvHelper;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualBasic.FileIO;
namespace BealeEngineering.Core.Logic.Import
{
public class wipXeroInvoiceFlatFile
{
public wipXeroInvoiceFlatFile(string sqlConnectionString)
{
SqlConnectionString = sqlConnectionString;
}
private string SqlConnectionString { get; set; }
public void ByFilePath(string filePath)
{
// get model list
//// get db invoices
//var saleInvInst = new Data.Database.Sale.InvoiceGet(SqlConnectionString);
//saleInvInst.InvoiceNumber = invDictionary.Keys.ToList();
//var dataInvList = saleInvInst.GetByFilters();
// compare
// update modified records
// insert new records <--------- only insert approved invoices
// delete records (is this possible??) <------- i think so, include deleted and voided in flatfile??
}
}
}

View File

@@ -15,6 +15,15 @@ namespace BealeEngineering.Core.Model.Client
else { return OrderLineList.Count; }
}
}
public List<Client.PurchaseOrderLine> OrderLineList { get; set; }
public List<PurchaseOrderLine> OrderLineList { get; set; }
public class PurchaseOrderLine
{
public int ClientPurchaseOrderLineID { get; set; }
public int ClientPurchaseOrderID { get; set; }
public int LineNumber { get; set; }
public int ProjectJobID { get; set; }
public string Description { get; set; }
public decimal LineNetAmount { get; set; }
}
}
}

View File

@@ -11,28 +11,10 @@ namespace BealeEngineering.Core.Model.Client
{
public int ClientPurchaseOrderID { get; set; }
public DateTime PurchaseOrderDate { get; set; }
public int ContactID { get; set; }
public Model.Contact.Contact Contact { get; set; }
public string ClientReference { get; set; }
public string RequestorEmail { get; set; }
public decimal OrderTotal { get; set; }
public bool IsClosed { get; set; }
//sealed PurchaseOrder CreatePurchaseOrder()
//{
// PurchaseOrder destObject = new PurchaseOrder();
// this.CopyProperties(destObject); // inside a class you want to copy from
// return destObject;
// Reflection.CopyProperties(this, destObject); // Same as above but directly calling the function
// TestClass srcClass = new TestClass();
// TestStruct destStruct = new TestStruct();
// srcClass.CopyProperties(destStruct); // using the extension directly on a object
// Reflection.CopyProperties(srcClass, destObject); // Same as above but directly calling the function
// //so on and so forth.... your imagination is the limits :D
// return srcClass;
//}
}
}

View File

@@ -1,16 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BealeEngineering.Core.Model.Client
{
public class PurchaseOrderLine
{
public int ClientPurchaseOrderLineID { get; set; }
public int ClientPurchaseOrderID { get; set; }
public int LineNumber { get; set; }
public int ProjectJobID { get; set; }
public string Description { get; set; }
public decimal LineNetAmount { get; set; }
}
}

View File

@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BealeEngineering.Core.Model.Contact
{
public class Address : ValidateModel
{
[Required(AllowEmptyStrings = false), StringLength(100)]
public string AddressLine1 { get; set; }
[StringLength(100)]
public string AddressLine2 { get; set; }
[StringLength(100)]
public string AddressLine3 { get; set; }
[StringLength(100)]
public string AddressLine4 { get; set; }
[Required(AllowEmptyStrings = false), StringLength(100)]
public string City { get; set; }
[StringLength(100)]
public string Region { get; set; }
[Required(AllowEmptyStrings = false), StringLength(50)]
public string PostalCode { get; set; }
[StringLength(100)]
public string Country { get; set; }
}
}

View File

@@ -0,0 +1,76 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BealeEngineering.Core.Model.Contact
{
public class Contact : ValidateModel
{
public int ContactId { get; set; }
[Required(AllowEmptyStrings = false), StringLength(100)]
public string ContactName { get; set; }
[StringLength(150), EmailAddress]
public string EmailAddress { get; set; }
public string PrimaryPersonFirstName { get; set; }
public string PrimaryPersonSurname { get; set; }
public string PrimaryPersonEmail { get; set; }
public Address PostalAddress { get; set; }
public bool PostalAddressIsSet
{
get
{
if(PostalAddress == null) { return false; }
else { return true; }
}
}
public override IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (ValidationResults == null) { ValidationResults = new List<ValidationResult>(); }
if (PostalAddressIsSet)
{
//var lkdfj = Validator.TryValidateObject(this.PostalAddress,
//new ValidationContext(this.PostalAddress, null, null) { MemberName = "PostalAddress" },
//ValidationResults);
//// this doesn't
//Contact contact = validationContext as contact;
//if (contact != null)
//{
// // ValidationResult(" Error Message ", " MemberNames " )
// yield return new ValidationResult("No Department and Employees information", new string[] { "DepartmentList & EmployeeList" });
//}
//// this works!!!!!!!!!!
var context = new ValidationContext(this.PostalAddress);
var results = new List<ValidationResult>();
if (!Validator.TryValidateObject(this.PostalAddress, context, results))
{
ValidationResults.AddRange(results);
}
//if (!PostalAddress.IsValid())
//{
// // for some reason, when an invalid property is found, it is
// // added to the list twice.
// // It works, so no point in trying to fix
// ValidationResults.AddRange(PostalAddress.ValidationResults);
//}
}
return ValidationResults;
}
}
}

View File

@@ -1,15 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BealeEngineering.Core.Model.Contact
{
public class ContactHeader
{
public int ContactId { get; set; }
public string ContactName { get; set; }
public string EmailAddress { get; set; }
}
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@@ -8,6 +9,87 @@ namespace BealeEngineering.Core.Model.Sale
{
public class Invoice : InvoiceHeader
{
public List<Model.Sale.InvoiceLine> InvoiceLineList { get; set; }
public List<InvoiceLine> InvoiceLineList { get; set; } = new List<InvoiceLine>();
public bool InvoiceLineListIsSet
{
get
{
if (InvoiceLineList == null || !InvoiceLineList.Any()) { return false; }
else { return true; }
}
}
public class InvoiceLine
{
[Required(), Range(0, 255)]
public int LineNumber { get; set; }
[Required(), StringLength(50)]
public string InventoryItemCode { get; set; }
[StringLength(500)]
public string Description { get; set; }
[Required(), Range(0, 255)]
public decimal Quantity { get; set; }
[Required()]
public decimal UnitAmount { get; set; }
[Range(1, 100)]
public int? Discount { get; set; }
[Required(), StringLength(10)]
public string AccountCode { get; set; }
[Required(), StringLength(50)]
public string TaxType { get; set; }
[Required()]
public decimal TaxAmount { get; set; }
/// <summary>
/// Line amount is Tax Exclusive
/// </summary>
public decimal LineAmount
{
get
{
int discount;
if (Discount == null || Discount == 0) { discount = 100; }
else { discount = (int)Discount; }
return decimal.Round(((Quantity * UnitAmount) * (discount / 100m)),2, MidpointRounding.AwayFromZero);
}
}
}
public override IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
base.Validate(validationContext);
// additional validation
if (!InvoiceLineListIsSet)
{
var result = new ValidationResult("Invoice must have at least one line.");
ValidationResults.Add(result);
}
else
{
decimal lineTotal = 0;
decimal lineTaxTotal = 0;
foreach (var line in InvoiceLineList)
{
// checks on each line
if (line.Quantity <= 0)
{
var result = new ValidationResult("Quantity must be greater than zero.");
ValidationResults.Add(result);
}
lineTotal = lineTotal + line.LineAmount ;
lineTaxTotal = lineTaxTotal + line.TaxAmount;
}
if (lineTotal + lineTaxTotal != InvoiceTotal)
{
var result = new ValidationResult("Line total is not equal to Invoice Total.");
ValidationResults.Add(result);
}
if (lineTaxTotal != TaxTotal)
{
var result = new ValidationResult("Line tax total is not equal to Invoice Tax Total.");
ValidationResults.Add(result);
}
}
return ValidationResults;
}
}
}

View File

@@ -1,22 +1,48 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BealeEngineering.Core.Model.Sale
{
public class InvoiceHeader
public class InvoiceHeader : ValidateModel
{
public int SaleInvoiceID { get; set; }
public int ContactID { get; set; }
[Required(AllowEmptyStrings = false)]
public string ContactName { get; set; }
[Required(AllowEmptyStrings = false), StringLength(50)]
public string SaleInvoiceNumber { get; set; }
[Required()]
public DateTime InvoiceDate { get; set; }
public DateTime DueDate { get; set; }
public DateTime? DueDate { get; set; }
[StringLength(50)]
public string Reference { get; set; }
[Required(AllowEmptyStrings = false)]
[StringLength(3, MinimumLength = 3)]
public string CurrencyCode { get; set; }
[Required()]
public decimal InvoiceTotal { get; set;}
[Required()]
public decimal TaxTotal { get; set; }
[Required()]
public bool IsCreditNote { get; set; }
public override IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
base.Validate(validationContext);
if (IsCreditNote && InvoiceTotal > 0)
{
var result = new ValidationResult("Credit note total cannot be greater than zero.");
ValidationResults.Add(result);
}
if (!IsCreditNote && InvoiceTotal < 0)
{
var result = new ValidationResult("Invoice total cannot be less than zero.");
ValidationResults.Add(result);
}
return ValidationResults;
}
}
}

View File

@@ -1,23 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BealeEngineering.Core.Model.Sale
{
public class InvoiceLine
{
public int SaleInvoiceLineID { get; set; }
public int SaleInvoiceID { get; set; }
public string InventoryItemCode { get; set; }
public string Description { get; set; }
public int Quantity { get; set; }
public decimal UnitAmount { get; set; }
public int Discount { get; set; }
public string AccountCode { get; set; }
public string TaxType { get; set; }
public decimal TaxAmount { get; set; }
public decimal LineAmount { get; set; }
}
}

View File

@@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BealeEngineering.Core.Model
{
public abstract class ValidateModel : IValidatableObject
{
public List<ValidationResult> ValidationResults { get; protected set; }
public bool IsValid()
{
ValidationResults = new List<ValidationResult>();
var validationContext = new ValidationContext(this);
if (Validator.TryValidateObject(validationContext.ObjectInstance, validationContext, ValidationResults, true))
{ return true; }
else
{ return false; }
}
/// <summary>
/// Standard checks on class properties. Use 'override' if you wish to add additional checks in a derived classes
/// </summary>
/// <param name="validationContext"></param>
/// <returns>List<ValidationResult></returns>
public virtual IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (ValidationResults == null) { ValidationResults = new List<ValidationResult>(); }
return ValidationResults;
}
}
}

View File

@@ -11,21 +11,28 @@ namespace BealeEngineering.Core.Test
public Autoexec(string sqlConnectionString)
{
SqlConnectionString = sqlConnectionString;
// enter method to start here
Import();
}
private string SqlConnectionString { get; set; }
public void Start()
public void Invoice()
{
// thing you want to run from the form button
//var inst = new Core.Test.Sales.Invoice(SqlConnectionString);
//inst.GetInvoice();
//var inst2 = new Test.Client.PurchaseOrder(SqlConnectionString);
//inst2.AllocateInvoicesToPurchaseOrders();
var inst3 = new Test.Import.ImportFlatfile();
inst3.Go();
var inst = new Core.Test.Sales.Invoice(SqlConnectionString);
}
public void Contact()
{
var inst = new Test.Contact.Contact(SqlConnectionString);
}
public void Import()
{
var inst = new Test.Import.ImportFlatfile(SqlConnectionString);
}
public void ValidateModel()
{
var inst = new Test.Sales.Invoice(SqlConnectionString);
}
}
}

View File

@@ -21,7 +21,7 @@ namespace BealeEngineering.Core.Test.Client
}
public void GetPurchaseOrderById()
{
var inst = new Core.Data.Database.Client.PurchaseOrderGet(SqlConnectionString);
var inst = new Core.Data.Database.Client.ReadPurchaseOrder(SqlConnectionString);
var newList = inst.GetByClientPurchaseOrderId(PurchaseOrderIdList);
}
public void AllocateInvoicesToPurchaseOrders()

View File

@@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BealeEngineering.Core.Test.Contact
{
public class Contact
{
public Contact(string sqlConnectionString)
{
SqlConnectionString = sqlConnectionString;
ContactIdList = new List<int>
{
23, 18, 5
};
EmailAddressList = new List<string>
{
"phil.kennerley@gardiners.uk.com",
"patricia.snook@severntrent.co.uk",
"david.lane@severntrent.co.uk"
};
// put method to run here
ValidateContact();
}
private string SqlConnectionString { get; set; }
public List<int> ContactIdList { get; set; }
public List<string> EmailAddressList { get; set; }
public void ReadContactHeader()
{
var inst = new Data.Database.Contact.ReadContact(SqlConnectionString);
//var result = inst.ByContactId(ContactIdList);
//var result = inst.ByEmailAddress(EmailAddressList);
}
public void ValidateContact()
{
var inst = new Data.Database.Contact.ReadContact(SqlConnectionString);
var result = inst.ByContactName("Ben Broughton");
result.PostalAddress.AddressLine1 = " ";
bool isValid = result.IsValid();
var errorList = result.ValidationResults;
}
}
}

View File

@@ -8,14 +8,23 @@ namespace BealeEngineering.Core.Test.Import
{
public class ImportFlatfile
{
public void Go()
public ImportFlatfile(string sqlConnectionString)
{
Go(sqlConnectionString);
}
public void Go(string sqlConnectionString)
{
string fileInputPath =
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)
+ @"\Dropbox\Beale Engineering Services Ltd\BE Accounts\Xero-Export-Invoices.csv";
var inst = new Data.Xero.FlatFile.ImportInvoice();
var lkdsjflsd = inst.ByFilePath(fileInputPath);
var inst = new Logic.Import.XeroInvoiceFlatFile(sqlConnectionString);
inst.ByFilePath(fileInputPath);
return;
var inst2 = new Data.Xero.FlatFile.ReadXeroInvoiceFlatFile();
var lkdsjflsd = inst2.ByFilePath(fileInputPath);
}
}
}

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel.DataAnnotations;
namespace BealeEngineering.Core.Test.Sales
{
@@ -12,14 +13,34 @@ namespace BealeEngineering.Core.Test.Sales
{
SqlConnectionString = sqlConnectionString;
SaleInvoiceIdList = new List<int> { 131, 481, 105, 324 };
// add method you want to start here
ValidateInvoice();
}
public Model.Sale.InvoiceHeader InvoiceHeader {get; set;}
private string SqlConnectionString { get; set; }
public List<int> SaleInvoiceIdList { get; set; }
public void GetInvoice()
{
var InvInst = new Data.Database.Sale.InvoiceGet(SqlConnectionString);
var InvInst = new Data.Database.Sale.ReadInvoice(SqlConnectionString);
var lkdjflsk = InvInst.GetBySaleInvoiceId(SaleInvoiceIdList);
}
public void ValidateInvoice()
{
var readContact = new Data.Database.Contact.ReadContact(SqlConnectionString);
var contact = readContact.ByContactId(1);
var readInvoice = new Data.Database.Sale.ReadInvoice(SqlConnectionString);
var invoices = readInvoice.GetBySaleInvoiceId(new List<int> { 1 });
invoices[0].CurrencyCode = "EURR";
bool isValid = invoices[0].IsValid();
var errorList = invoices[0].ValidationResults;
}
}
}