Free cookie consent management tool by TermsFeed Policy Generator

source: branches/OaaS/HeuristicLab.Services.Optimization.Billing/BillingEngine/BillingEngine.cs @ 9645

Last change on this file since 9645 was 9645, checked in by spimming, 10 years ago

#1888:

  • Model classes implement new interface to track current state of entity
  • Mark EntityState property as 'not mapped'
  • Set hook to initialize entity as unchanged
  • Extension method for DbContext to apply changes only on modified entity
File size: 6.2 KB
Line 
1
2using System;
3using System.Collections.Generic;
4using System.Configuration;
5using System.Diagnostics;
6using System.Linq;
7using System.Threading;
8using HeuristicLab.Services.Optimization.Billing.Business;
9using HeuristicLab.Services.Optimization.Billing.Interfaces;
10using HeuristicLab.Services.Optimization.Billing.Model;
11namespace HeuristicLab.Services.Optimization.Billing.BillingEngine {
12  public class BillingEngine {
13    private bool stop;
14    private AutoResetEvent waitHandle;
15    private TimeSpan interval;
16    private DateTime currentDate;
17
18    private IOptimizationBilling billingService;
19    public IOptimizationBilling BillingService {
20      get {
21        if (billingService == null) {
22          billingService = new BillingService();
23        }
24        return billingService;
25      }
26      set { billingService = value; }
27    }
28
29    private IInvoiceFormattingEngine invoiceFormattingEngine;
30    public IInvoiceFormattingEngine InvoiceFormattingEngine {
31      get {
32        if (invoiceFormattingEngine == null) {
33          invoiceFormattingEngine = new PlainTextInvoiceFormattingEngine();
34        }
35        return invoiceFormattingEngine;
36      }
37      set { invoiceFormattingEngine = value; }
38    }
39
40    public BillingEngine() {
41      stop = false;
42      interval = TimeSpan.Parse(ConfigurationManager.AppSettings["BillingEngineInterval"]);
43      waitHandle = new AutoResetEvent(true);
44    }
45
46    public void StopBillingEngine() {
47      stop = true;
48      waitHandle.Set();
49    }
50
51    public void Run() {
52      while (!stop) {
53        try {
54          Trace.WriteLine("[BillingEngine] Start billing cycle");
55          currentDate = DateTime.Now.Date;
56          Trace.WriteLine(string.Format("[BillingEngine] Current date: {0}", currentDate));
57          IList<Order> activeOrders = BillingService.GetOrdersByState(OrderState.Active);
58          Trace.WriteLine(string.Format("[BillingEngine] Processing {0} active orders.", activeOrders.Count));
59          foreach (Order order in activeOrders) {
60            if (!order.NextBillableDay.HasValue) {
61              // An invoice has never been generated from this order
62              order.NextBillableDay = currentDate;
63            }
64
65            if (order.NextBillableDay >= currentDate) {
66              // TODO: Embed everything in a transaction
67
68              // Collect Usage Data
69              //IList<UsageRecord> usageRecrods = BillingService.GetUsageRecords(order.User.Name, order.LastBillableDay.Value, currentDate);
70
71              // Collect Invoice Data
72              Invoice invoice = new Invoice();
73              invoice.Order = order;
74              invoice.User = order.User;
75              invoice.Due = currentDate.AddMonths(1);
76              invoice.InvoiceDate = currentDate;
77              invoice.Status = InvoiceStatus.Open;
78              invoice.InvoiceLines = new List<InvoiceLine>();
79              foreach (OrderLine orderLine in order.OrderLines) {
80                InvoiceLine invoiceLine = new InvoiceLine();
81                invoiceLine.Invoice = invoice;
82                invoiceLine.Product = orderLine.Product;
83                invoiceLine.ProductPrice = orderLine.ProductPrice;
84                invoiceLine.Quantity = orderLine.Quantity;
85                invoice.InvoiceLines.Add(invoiceLine);
86              }
87
88              InvoiceLine usageCharges = new InvoiceLine();
89              usageCharges.Invoice = invoice;
90              usageCharges.Product = billingService.GetProductByName("Oaas Usage Charges").First();
91              usageCharges.ProductPrice = 999.99;
92              usageCharges.Quantity = 1;
93              invoice.InvoiceLines.Add(usageCharges);
94
95              // Bill Post Processing: Apply Discounts or Promotions
96              // add discounts, sum up items to sub-total, calc. tax -> total current charges
97              // maybe use windows workflow foundation for this task
98
99              // Invoice Formatting Engine: Generate Invoice
100              invoice.InvoiceDocument = InvoiceFormattingEngine.Generate(invoice);
101
102              UpdateBillingState(order);
103              billingService.UpdateOrder(order);
104              billingService.SaveInvoice(invoice);
105            }
106          }
107
108          Trace.WriteLine("[BillingEngine] Finished billing cycle");
109        }
110        catch (Exception e) {
111          Trace.WriteLine(string.Format("[BillingEngine] The following exception occured: {0}", e.ToString()));
112        }
113        waitHandle.WaitOne(interval);
114      }
115      waitHandle.Close();
116    }
117
118    private void UpdateBillingState(Order order) {
119      if (order.BillingType == BillingType.Pre) {
120        throw new Exception("This billing type is currently not supported: " + order.BillingType);
121      }
122
123      order.LastBillableDay = currentDate;
124      DateTime nextBillingDay = CalculateNextBillingDate(order);
125
126      if (order.ActiveUntil.HasValue && order.ActiveUntil == currentDate) {
127        order.State = OrderState.Finished;
128        order.NextBillableDay = null;
129      } else if (order.ActiveUntil.HasValue && order.ActiveUntil < nextBillingDay) {
130        order.NextBillableDay = order.ActiveUntil;
131      } else {
132        order.NextBillableDay = nextBillingDay;
133      }
134
135      order.EntityState = State.Modified;
136    }
137
138    private DateTime CalculateNextBillingDate(Order order) {
139      DateTime nextBillingDay = currentDate;
140
141      if (order.BillingPeriod == BillingPeriod.Weekly) {
142        nextBillingDay = Next(currentDate, DayOfWeek.Sunday);
143      } else if (order.BillingPeriod == BillingPeriod.Monthly) {
144        DateTime endOfNextMonth = new DateTime(
145          currentDate.AddMonths(1).Year,
146          currentDate.AddMonths(1).Month,
147          DateTime.DaysInMonth(currentDate.AddMonths(1).Year, currentDate.AddMonths(1).Month));
148        nextBillingDay = endOfNextMonth;
149      } else if (order.BillingPeriod == BillingPeriod.Yearly) {
150        nextBillingDay = new DateTime(currentDate.AddYears(1).Year, 12, 31);
151      }
152
153      return nextBillingDay;
154    }
155
156    private DateTime Next(DateTime from, DayOfWeek dayOfWeek) {
157      int start = (int)from.DayOfWeek;
158      int target = (int)dayOfWeek;
159      if (target <= start)
160        target += 7;
161      return from.AddDays(target - start);
162    }
163  }
164}
Note: See TracBrowser for help on using the repository browser.