private static TransformResultInfo DoTransform(XElement template)
        {
            Log.LogThis("Starting invoice transform", eloglevel.info);
            Log.LogThis(string.Format("Using invoice template: Id: {0} Name: {1}", template.Attribute("Id").Value, template.Attribute("Name").Value), eloglevel.info);
            Log.LogThis(string.Format("Loading original invoice file(s) from: {0}", template.Attribute("SourceFolder").Value), eloglevel.info);

            List<string> filelist = Directory.GetFiles(template.Attribute("SourceFolder").Value).OfType<string>().ToList();
            Log.LogThis(string.Format("Invocie files found: {0}", string.Join(",", filelist.Select(Path.GetFileName).ToArray())), eloglevel.info);

            _templateTransformFields = template.Descendants("TemplateTransform").FirstOrDefault();
            _selectedTemplate = template;
            _selectedTemplate.Element("InvoiceNumbersToUpdate").RemoveAll();

            var eachesConversionElement = _selectedTemplate.Element("EachesConversion");

            _useEachesConversion = (eachesConversionElement != null && eachesConversionElement.Attribute("enabled") != null && eachesConversionElement.Attribute("enabled").Value == "1");
            _eachesConverionTag = _useEachesConversion && eachesConversionElement.Attribute("tag") != null ? eachesConversionElement.Attribute("tag").Value : "";

            var transformResultInfo = new TransformResultInfo();
            var newSplitLine = new ArrayList();

            var fileInvoices = new Dictionary<string, List<string[]>>();

            foreach (string file in filelist)
            {
                Log.LogThis(string.Format("Processing file: {0}", file), eloglevel.info);
                fileInvoices.Add(file, new List<string[]>());

                transformResultInfo.NumberOfFilesProcessed += 1;

                int newFieldPos = 0;
                XElement invoiceNoTemplateField = null;
                XElement invoiceDateTemplateField = null;

                var sr = new StreamReader(file);

                if (_selectedTemplate.Attribute("HasHeaderRecord").Value == "1")
                {
                    sr.ReadLine(); //advance past the header line
                }

                while (!sr.EndOfStream)
                {
                    var csvParser = new CsvParser(_selectedTemplate.Attribute("Delimiter").Value[0], '\0');

                    var splitOriginalLine = (string[])csvParser.CSVParser(sr.ReadLine()).ToArray(typeof(string));

                    if (splitOriginalLine.Count() == 0)
                        continue;

                    newSplitLine.Clear();

                    if (_selectedTemplate.Attribute("HasMasterRecord").Value == "1" && IsMasterRow(splitOriginalLine))
                    {
                        invoiceNoTemplateField = _selectedTemplate.Descendants("MasterRow").Descendants("Field").FirstOrDefault(x => x.Attribute("FieldNameId").Value == "1");
                        _invoiceNumber = splitOriginalLine[int.Parse(invoiceNoTemplateField.Descendants("Delimited").First().Attribute("Position").Value)];

                        invoiceDateTemplateField = _selectedTemplate.Descendants("MasterRow").Descendants("Field").FirstOrDefault(x => x.Attribute("FieldNameId").Value == "2");
                        _invoiceDate = splitOriginalLine[int.Parse(invoiceDateTemplateField.Descendants("Delimited").First().Attribute("Position").Value)];
                    }
                    else if (IsDetailRow(splitOriginalLine))
                    {
                        if (_selectedTemplate.Attribute("HasMasterRecord").Value == "1")
                        {
                            newSplitLine.Add(_invoiceNumber);
                            SetNewFieldPosition(invoiceNoTemplateField, ref newFieldPos);

                            newSplitLine.Add(_invoiceDate);
                            SetNewFieldPosition(invoiceDateTemplateField, ref newFieldPos);
                        }

                        foreach (XElement templateField in _selectedTemplate.Element("DetailFields").Descendants("Field"))
                        {
                            string fieldValue = splitOriginalLine[int.Parse(templateField.Descendants("Delimited").First().Attribute("Position").Value)];

                            if (templateField.Attribute("DirectiveId") != null &&
                                !string.IsNullOrEmpty(templateField.Attribute("DirectiveId").Value))
                            {
                                Log.LogThis("Alternative processing directive found for fieldNameId " + templateField.Attribute("FieldNameId").Value + ", directiveId " + templateField.Attribute("DirectiveId").Value, eloglevel.info);

                                var fieldDirective = _selectedTemplate.Element("Directives").Descendants("Directive").FirstOrDefault(directive => directive.Attribute("Id").Value == templateField.Attribute("DirectiveId").Value);
                                var sourceFieldConditionValue = splitOriginalLine[int.Parse(fieldDirective.Element("Condition").Attribute("ConditionFieldPosition").Value)];

                                Log.LogThis("DirectiveId " + fieldDirective.Attribute("Id").Value + ": Name: " + fieldDirective.Attribute("Name").Value, eloglevel.info);

                                decimal result = decimal.Parse(fieldValue);

                                if (sourceFieldConditionValue == fieldDirective.Element("Condition").Attribute("ConditionValue").Value)
                                {
                                    var operand1 = decimal.Parse(splitOriginalLine[int.Parse(fieldDirective.Element("Calculation").Element("Operand1").Attribute("sourceFieldPosition").Value)]);
                                    var operand2 = decimal.Parse(splitOriginalLine[int.Parse(fieldDirective.Element("Calculation").Element("Operand2").Attribute("sourceFieldPosition").Value)]);
                                    var op = fieldDirective.Element("Calculation").Element("Operator").Attribute("OpType").Value;

                                    switch (op)
                                    {
                                        case "division":
                                            result = operand1 / (operand2 == 0 ? 1 : operand2);
                                            Log.LogThis("Performing directive: " + operand1 + " / " + (operand2 == 0 ? 1 : operand2) + " = " + result, eloglevel.info);
                                            break;
                                        case "multiply":
                                            result = operand1 * operand2;
                                            break;
                                        case "add":
                                            result = operand1 + operand2;
                                            break;
                                        case "minus":
                                            result = operand1 - operand2;
                                            break;
                                        default:
                                            break;
                                    }

                                    fieldValue = result.ToString("F4");
                                }
                            }

                            newSplitLine.Add(fieldValue);
                            SetNewFieldPosition(templateField, ref newFieldPos);
                        }

                        //if (!IgnoreNegativeAmountLines(new[] { "UnitCost", "TotalCost" }, newSplitLine))
                            fileInvoices[file].Add(newSplitLine.OfType<string>().ToArray()); //build a list of all the detail lines
                    }
                    else if (IsSummaryRow(splitOriginalLine))
                    {
                        //ignore summary lines
                    }
                }
                sr.Close();

                transformResultInfo.NumberOfInvoiceLinesProcessed += fileInvoices[file].Count;
                ArchiveProcessedInvoiceFile(file);
            }

            var allDetailLines = fileInvoices.Values.SelectMany(l => l).ToList();
            if (allDetailLines.Count > 0)
            {
                foreach (var inv in allDetailLines.Select(invLine => invLine[0]).Distinct().ToList())
                    _selectedTemplate.Element("InvoiceNumbersToUpdate").Add(new XElement("InvoiceNumber", inv));

                CreateFixedLengthFieldsAndTransformDetials(fileInvoices);

                SaveTransformedInvoiceFile(fileInvoices);
            }

            return transformResultInfo;
        }
        public static TransformResultInfo DoTransform(List<XElement> templates)
        {
            var transformResultInfo = new TransformResultInfo();

            foreach (var template in templates)
            {
                transformResultInfo.NumberOfInvoiceLinesProcessed += DoTransform(template).NumberOfInvoiceLinesProcessed;
            }

            SaveTemplates();
            return transformResultInfo;
        }