Sales invoice 'Status'

Added 'status' to sales invoice
This commit is contained in:
2020-02-05 21:18:39 +00:00
committed by GitHub
parent 0198d12549
commit 7210812458
10 changed files with 100 additions and 19 deletions

View File

@@ -135,7 +135,7 @@ namespace BealeEngineering.Accounts
{
string fileInputPath =
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)
+ @"\Dropbox\Beale Engineering Services Ltd\BE Accounts\Xero-Export-Invoices - Orig.csv";
+ @"\Dropbox\Beale Engineering Services Ltd\BE Accounts\Xero-Export-Invoices.csv";
string dialogText = "Importing file from location: " + Environment.NewLine + Environment.NewLine + fileInputPath;

View File

@@ -54,6 +54,7 @@ namespace BealeEngineering.Core.Data.Database.Sale
,SaleInvoice.InvoiceTotal
,SaleInvoice.TaxTotal
,SaleInvoice.IsCreditNote
,SaleInvoice.Status
,SaleInvoiceLine.SaleInvoiceLineID
,SaleInvoiceLine.SaleInvoiceID
,SaleInvoiceLine.LineNumber

View File

@@ -139,6 +139,7 @@ namespace BealeEngineering.Core.Data.Database.Sale
,SaleInvoice.InvoiceTotal
,SaleInvoice.TaxTotal
,SaleInvoice.IsCreditNote
,SaleInvoice.Status
,Contact.ContactName
FROM SaleInvoice
INNER JOIN Contact ON SaleInvoice.ContactID = Contact.ContactID";

View File

@@ -85,6 +85,7 @@ namespace BealeEngineering.Core.Data.Database.Sale
,[InvoiceTotal] = @invoiceTotal
,[TaxTotal] = @taxTotal
,[IsCreditNote] = @isCreditNote
,[Status] = @status
WHERE SaleInvoiceID = @saleInvoiceId;
DELETE FROM SaleInvoiceLine WHERE SaleInvoiceID = @saleInvoiceId;";
@@ -102,6 +103,7 @@ namespace BealeEngineering.Core.Data.Database.Sale
,[InvoiceTotal]
,[TaxTotal]
,[IsCreditNote]
,[Status]
)
OUTPUT INSERTED.SaleInvoiceID
VALUES(
@@ -114,6 +116,7 @@ namespace BealeEngineering.Core.Data.Database.Sale
,@invoiceTotal
,@taxTotal
,@isCreditNote
,@status
)";
}
else
@@ -134,6 +137,7 @@ namespace BealeEngineering.Core.Data.Database.Sale
cmd.Parameters.AddWithValue("@invoiceTotal", invoice.InvoiceTotal);
cmd.Parameters.AddWithValue("@taxTotal", invoice.TaxTotal);
cmd.Parameters.AddWithValue("@isCreditNote", invoice.IsCreditNote);
cmd.Parameters.AddWithValue("@status", invoice.Status);
if (saleInvoiceId == 0)
{

View File

@@ -8,6 +8,21 @@ namespace BealeEngineering.Core.Logic.Adapter
{
public class SaleInvoice
{
public SaleInvoice()
{
// ensure sale invoice hasn't changed
int propertyCount = new Model.Sale.Invoice().GetType().GetProperties().Count();
if (propertyCount != 14)
{
throw new Exception("Model.Import.XeroInvoiceFlatFile has changed, it's adapter class may need updating.");
}
propertyCount = new Model.Sale.Invoice.InvoiceLine().GetType().GetProperties().Count();
if (propertyCount != 10)
{
throw new Exception("Model.Import.XeroInvoiceFlatFile.LineItem has changed, it's adapter class may need updating.");
}
}
public Model.Sale.Invoice XeroInvoiceFlatFIle(Model.Import.XeroInvoiceFlatFile xeroInvoice)
{
var result = XeroInvoiceFlatFile(new List<Model.Import.XeroInvoiceFlatFile> { xeroInvoice });
@@ -33,6 +48,7 @@ namespace BealeEngineering.Core.Logic.Adapter
else { throw new FormatException("Unknow value '" + xeroInvoiceList[i].Type + "' found in 'Type' field"); }
invoice.Reference = xeroInvoiceList[i].Reference;
invoice.SaleInvoiceNumber = xeroInvoiceList[i].InvoiceNumber;
invoice.Status = xeroInvoiceList[i].Status;
invoice.TaxTotal = xeroInvoiceList[i].TaxTotal;
invoice.InvoiceLineList = new List<Model.Sale.Invoice.InvoiceLine>();

View File

@@ -8,6 +8,21 @@ namespace BealeEngineering.Core.Logic.Adapter
{
public class XeroInvoiceFlatFile
{
public XeroInvoiceFlatFile()
{
// ensure XeroInvoiceFlatFile hasn't changed
int propertyCount = new Model.Import.XeroInvoiceFlatFile().GetType().GetProperties().Count();
if (propertyCount != 32)
{
throw new Exception("Model.Import.XeroInvoiceFlatFile has changed, it's adapter class may need updating.");
}
propertyCount = new Model.Import.XeroInvoiceFlatFile.LineItem().GetType().GetProperties().Count();
if (propertyCount != 13)
{
throw new Exception("Model.Import.XeroInvoiceFlatFile.LineItem has changed, it's adapter class may need updating.");
}
}
public List<Model.Import.XeroInvoiceFlatFile> SaleInvoice(List<Model.Sale.Invoice> invoiceList)
{
if (invoiceList == null || !invoiceList.Any()) { return null; }
@@ -23,6 +38,7 @@ namespace BealeEngineering.Core.Logic.Adapter
xeroInvoice.InvoiceDate = invoiceList[i].InvoiceDate;
xeroInvoice.InvoiceNumber = invoiceList[i].SaleInvoiceNumber;
xeroInvoice.Reference = invoiceList[i].Reference;
xeroInvoice.Status = invoiceList[i].Status;
xeroInvoice.TaxTotal = invoiceList[i].TaxTotal;
xeroInvoice.Total = invoiceList[i].InvoiceTotal;
if (invoiceList[i].IsCreditNote) { xeroInvoice.Type = "Sales credit note"; }
@@ -59,19 +75,21 @@ namespace BealeEngineering.Core.Logic.Adapter
// ensure flat data is in invoice number order
var invDictionary = new Dictionary<string, int>();
string lastNumber = null;
string lastUniqueString = "";
foreach (var line in flatData)
{
if (line.InvoiceNumber != lastNumber)
// invoice number isn't unique, can be duplicated if one invoice is void/deleted
string uniqueString = line.InvoiceNumber + line.Status;
if (uniqueString != lastUniqueString)
{
lastNumber = line.InvoiceNumber;
if (invDictionary.ContainsKey(lastNumber))
lastUniqueString = uniqueString;
if (invDictionary.ContainsKey(lastUniqueString))
{
throw new Exception("Invoices are not grouped in CSV flatfile.");
}
else
{
invDictionary.Add(lastNumber, 0);
invDictionary.Add(lastUniqueString, 0);
}
}
}

View File

@@ -11,12 +11,13 @@ namespace BealeEngineering.Core.Logic.Adapter
public XeroInvoiceFlatFileDTO()
{
// ensure XeroInvoiceFlatFileDTO hasn't changed
int dtoPropertyCouny = new Model.Import.XeroInvoiceFlatFileDTO().GetType().GetProperties().Count();
if (dtoPropertyCouny != 44)
int propertyCount = new Model.Import.XeroInvoiceFlatFileDTO().GetType().GetProperties().Count();
if (propertyCount != 44)
{
throw new Exception("Model.Import.XeroInvoiceFlatFileDTO has changed, it's adapter class needs updating.");
throw new Exception("Model.Import.XeroInvoiceFlatFileDTO has changed, it's adapter class may need updating.");
}
}
public List<Model.Import.XeroInvoiceFlatFileDTO> XeroInvoiceFlatFile(List<Model.Import.XeroInvoiceFlatFile> invoices)
{
//throw new NotImplementedException();

View File

@@ -13,7 +13,7 @@ namespace BealeEngineering.Core.Logic.Import
this.sqlConnectionString = sqlConnectionString;
}
private List<Model.Import.XeroInvoiceFlatFile> XeroInvoiceData { get; set; }
private List<Model.Import.XeroInvoiceFlatFile> XeroFlatData { get; set; }
public int InvoicesCreated { get; private set; } = 0;
@@ -26,26 +26,41 @@ namespace BealeEngineering.Core.Logic.Import
public void ByFilePath(string filePath, bool updateContacts = true)
{
// get xero data
var data = new Data.Xero.FlatFile.ReadXeroInvoiceFlatFile();
XeroInvoiceData = data.ByFilePath(filePath);
var flatfileData = new Data.Xero.FlatFile.ReadXeroInvoiceFlatFile();
XeroFlatData = flatfileData.ByFilePath(filePath);
// check data
if (XeroFlatData == null || !XeroFlatData.Any()) { return; }
else
{
for (int i = 0; i < XeroFlatData.Count(); i++)
{
if (XeroFlatData[i].Status == "Deleted" || XeroFlatData[i].Status == "Voided")
{ throw new Exception("Deleted/Voided invoices found in dataset."); }
// xero flatfiles can have duplicate invoice numbers if one of the invoices is voided/deleted.
// Long story short; to avoid missing invoices from db dataset, any voided/deleted invoices need
// to be manually deleted on the database. While this has it's problems, it's more probable that
// the db side will be correct this way.
}
}
// update db contacts
UpdateContacts();
// populate/map xero data to invoice model list
var dbInvoiceData = new Logic.Adapter.SaleInvoice().XeroInvoiceFlatFile(XeroInvoiceData);
var xeroInvoiceList = new Logic.Adapter.SaleInvoice().XeroInvoiceFlatFile(XeroFlatData);
//check
if (dbInvoiceData == null ||( XeroInvoiceData.Count != dbInvoiceData.Count))
if (xeroInvoiceList == null ||( XeroFlatData.Count != xeroInvoiceList.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())
if (xeroInvoiceList.Any())
{
var updateInvoice = new Data.Database.Sale.UpdateInvoice(sqlConnectionString);
updateInvoice.ByInvoiceList(dbInvoiceData, true);
updateInvoice.ByInvoiceList(xeroInvoiceList, true);
InvoicesCreated = updateInvoice.RecordsCreated;
InvoicesUpdated = updateInvoice.RecordsUpdated;
@@ -65,11 +80,11 @@ namespace BealeEngineering.Core.Logic.Import
{
var contactAdapter = new Logic.Adapter.Contact();
var dicContacts = new Dictionary<string, Model.Contact.Contact>();
for (var i = 0; i < XeroInvoiceData.Count; i++)
for (var i = 0; i < XeroFlatData.Count; i++)
{
if (!dicContacts.ContainsKey(XeroInvoiceData[0].ContactName))
if (!dicContacts.ContainsKey(XeroFlatData[0].ContactName))
{
dicContacts.Add(XeroInvoiceData[0].ContactName, contactAdapter.XeroInvoiceFlatFile(XeroInvoiceData[0]));
dicContacts.Add(XeroFlatData[0].ContactName, contactAdapter.XeroInvoiceFlatFile(XeroFlatData[0]));
}
}
if (dicContacts.Any())

View File

@@ -10,6 +10,7 @@ namespace BealeEngineering.Core.Model.Sale
public class Invoice : InvoiceHeader
{
public List<InvoiceLine> InvoiceLineList { get; set; } = new List<InvoiceLine>();
public bool InvoiceLineListIsSet
{
get
@@ -18,26 +19,36 @@ namespace BealeEngineering.Core.Model.Sale
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>
@@ -52,6 +63,7 @@ namespace BealeEngineering.Core.Model.Sale
}
}
}
public override IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
base.Validate(validationContext);

View File

@@ -10,24 +10,37 @@ namespace BealeEngineering.Core.Model.Sale
public class InvoiceHeader : ValidateModel
{
public int SaleInvoiceID { 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; }
[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; }
[Required()]
public string Status { get; set; }
public override IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
base.Validate(validationContext);