/// <summary>
        /// Extracts expense data from a block of text
        /// </summary>
        /// <param name="text">Text to process</param>
        /// <param name="cancellationToken">Default cancellation token</param>
        /// <returns></returns>
        public async Task <ExpenseResourceModel> ExtractExpenseAsync(string text, CancellationToken cancellationToken = default)
        {
            ExpenseResourceModel resourceModel = new ExpenseResourceModel();

            await Task.Run(() =>
            {
                if (string.IsNullOrEmpty(text))
                {
                    throw new EmailProcessingException("Text block can not be empty");
                }

                // Load the email text as html doc
                HtmlDocument htmlDocument = new HtmlDocument();
                htmlDocument.LoadHtml(text);

                // Detect tags that are not closed
                if (htmlDocument.ContainsUnclosedTags())
                {
                    throw new UnclosedTagException("Block of text has unclosed tag(s)");
                }

                // Extract data
                HtmlNode expenseNode = htmlDocument.DocumentNode.SelectSingleNode("//expense");
                if (expenseNode == null)
                {
                    throw new EmailProcessingException("Expense element does not exist or is invalid");
                }

                if (expenseNode.HasChildNodes)
                {
                    HtmlNode costCentre    = expenseNode.SelectSingleNode("//cost_centre");
                    HtmlNode total         = expenseNode.SelectSingleNode("//total");
                    HtmlNode paymentMethod = expenseNode.SelectSingleNode("//payment_method");

                    if (total == null)
                    {
                        throw new MissingElementException("Total node is required");
                    }

                    decimal totalValue = decimal.Parse(total.InnerText, CultureInfo.InvariantCulture);

                    resourceModel.CostCentre        = costCentre != null ? costCentre.InnerText : "UNKNOWN";
                    resourceModel.Total             = totalValue;
                    resourceModel.PaymentMethod     = paymentMethod.InnerText;
                    resourceModel.TotalExcludingGST = FinancialCalculations.CalculateTotalNetFromTotalGross(totalValue);
                    resourceModel.GSTValue          = FinancialCalculations.CalculateGSTValueFromTotalGross(totalValue);
                }
            }, cancellationToken);

            return(resourceModel);
        }
Example #2
0
        public async Task Service_Get_ExpenseResourceModel()
        {
            // Arrange
            string text = FakeDataHelper.EmailProcessing.CorrectExpenseTextBlock;

            // Act
            ExpenseResourceModel model = await EmailProcessingService.ExtractExpenseAsync(text);


            // Assert
            Assert.IsNotNull(model);
            Assert.AreEqual("DEV002", model.CostCentre);
            Assert.AreEqual(1024.01m, model.Total);
            Assert.AreEqual(890.44m, model.TotalExcludingGST);
            Assert.AreEqual(133.57m, model.GSTValue);
            Assert.AreEqual("personal card", model.PaymentMethod);
        }
Example #3
0
        public async Task Controller_Get_ExpenseResourceModel_No_CostCentre()
        {
            // Arrange
            string text = FakeDataHelper.EmailProcessing.MissingCostCentreExpenseTextBlock;

            // Act
            IActionResult result = await EmailProcessingController.Expense1_0(text);

            // Assert
            ExpenseResourceModel model = ((OkObjectResult)result).Value as ExpenseResourceModel;

            Assert.IsNotNull(model);
            Assert.AreEqual("UNKNOWN", model.CostCentre);
            Assert.AreEqual(1024.01m, model.Total);
            Assert.AreEqual(890.44m, model.TotalExcludingGST);
            Assert.AreEqual(133.57m, model.GSTValue);
            Assert.AreEqual("personal card", model.PaymentMethod);
        }