public UpdateClientBillingResult UpdateClientBilling(UpdateClientBillingCommand command)
        {
            if (command.Period == default)
            {
                throw new Exception("Missing parameter: Period");
            }

            if (command.Period.Day != 1)
            {
                throw new Exception("Period must be the first day of the month.");
            }

            using (var conn = NewConnection())
            {
                conn.Open();

                DateTime now = DateTime.Now;

                DateTime sd = command.Period;
                DateTime ed = command.Period.AddMonths(1);

                var toolDataClean = new WriteToolDataCleanProcess(new WriteToolDataCleanConfig {
                    Connection = conn, Context = "ProcessRepository.UpdateClientBilling", StartDate = sd, EndDate = ed, ClientID = command.ClientID
                });
                var toolData = new WriteToolDataProcess(WriteToolDataConfig.Create(conn, "ProcessRepository.UpdateClientBilling", sd, command.ClientID, 0, Cost.GetToolCosts(sd, 0)));

                var roomDataClean = new WriteRoomDataCleanProcess(new WriteRoomDataCleanConfig {
                    Connection = conn, Context = "ProcessRepository.UpdateClientBilling", StartDate = sd, EndDate = ed, ClientID = command.ClientID
                });
                var roomData = new WriteRoomDataProcess(new WriteRoomDataConfig {
                    Connection = conn, Context = "ProcessRepository.UpdateClientBilling", Period = sd, ClientID = command.ClientID, RoomID = 0
                });

                var pr1 = toolDataClean.Start();
                var pr2 = roomDataClean.Start();

                var pr3 = toolData.Start();
                var pr4 = roomData.Start();

                bool temp = DateTime.Now.FirstOfMonth() == command.Period;

                var step1 = new BillingDataProcessStep1(new Step1Config {
                    Connection = conn, Context = "ProcessRepository.UpdateClientBilling", Period = command.Period, Now = now, ClientID = command.ClientID, IsTemp = temp
                });

                var pr5 = step1.PopulateToolBilling();
                var pr6 = step1.PopulateRoomBilling();

                PopulateSubsidyBillingResult pr7 = null;

                if (!temp)
                {
                    var step4 = new BillingDataProcessStep4Subsidy(new Step4Config {
                        Connection = conn, Context = "ProcessRepository.UpdateClientBilling", Period = command.Period, ClientID = command.ClientID
                    });
                    pr7 = step4.PopulateSubsidyBilling();
                }

                var result = new UpdateClientBillingResult
                {
                    WriteToolDataCleanProcessResult     = pr1,
                    WriteRoomDataCleanProcessResult     = pr2,
                    WriteToolDataProcessResult          = pr3,
                    WriteRoomDataProcessResult          = pr4,
                    PopulateToolBillingProcessResult    = pr5,
                    PopulateRoomBillingProcessResult    = pr6,
                    PopulateSubsidyBillingProcessResult = pr7
                };

                conn.Close();

                return(result);
            }
        }
        public IEnumerable <string> UpdateBilling(UpdateBillingArgs args)
        {
            // updates all billing
            using (var conn = NewConnection())
            {
                conn.Open();

                DateTime startTime = DateTime.Now;

                var result = new List <string>
                {
                    $"Started UpdateBilling at {startTime:yyyy-MM-dd HH:mm:ss}",
                    $"Period: {string.Join(", ", args.Periods.Select(x => x.ToString("yyyy-MM-dd")))}",
                    $"ClientID: {args.ClientID}",
                    $"BillingCategory: {args.BillingCategory}"
                };

                Stopwatch sw;
                DateTime  sd, ed;

                foreach (var p in args.Periods)
                {
                    if (p == default)
                    {
                        throw new Exception("Missing parameter: Period");
                    }

                    if (p.Day != 1)
                    {
                        throw new Exception("Period must be the first day of the month.");
                    }

                    var temp = Utility.IsCurrentPeriod(p);

                    sd = p;
                    ed = p.AddMonths(1);

                    var populateSubsidy = false;

                    sw = new Stopwatch();

                    DateTime now = DateTime.Now;

                    var step1 = new BillingDataProcessStep1(new Step1Config {
                        Connection = conn, Context = "ProcessRepository.UpdateBilling", Period = p, Now = now, ClientID = args.ClientID, IsTemp = temp
                    });

                    if (args.BillingCategory.HasFlag(BillingCategory.Tool))
                    {
                        var toolDataClean = new WriteToolDataCleanProcess(new WriteToolDataCleanConfig {
                            Connection = conn, Context = "ProcessRepository.UpdateBilling", StartDate = sd, EndDate = ed, ClientID = args.ClientID
                        });
                        var toolData = new WriteToolDataProcess(WriteToolDataConfig.Create(conn, "ProcessRepository.UpdateBilling", p, args.ClientID, 0, Cost.GetToolCosts(p, 0)));

                        sw.Restart();
                        toolDataClean.Start();
                        result.Add(string.Format("Completed ToolDataClean in {0}", sw.Elapsed));

                        sw.Restart();
                        toolData.Start();
                        result.Add(string.Format("Completed ToolData in {0}", sw.Elapsed));

                        sw.Restart();
                        step1.PopulateToolBilling();
                        result.Add(string.Format("Completed ToolBilling in {0}", sw.Elapsed));

                        populateSubsidy = true;
                    }

                    if (args.BillingCategory.HasFlag(BillingCategory.Room))
                    {
                        var roomDataClean = new WriteRoomDataCleanProcess(new WriteRoomDataCleanConfig {
                            Connection = conn, Context = "ProcessRepository.UpdateBilling", StartDate = sd, EndDate = ed, ClientID = args.ClientID, RoomID = 0
                        });
                        var roomData = new WriteRoomDataProcess(new WriteRoomDataConfig {
                            Connection = conn, Context = "ProcessRepository.UpdateBilling", Period = p, ClientID = args.ClientID, RoomID = 0
                        });

                        sw.Restart();
                        roomDataClean.Start();
                        result.Add(string.Format("Completed RoomDataClean in {0}", sw.Elapsed));

                        sw.Restart();
                        roomData.Start();
                        result.Add(string.Format("Completed RoomData in {0}", sw.Elapsed));

                        sw.Restart();
                        step1.PopulateRoomBilling();
                        result.Add(string.Format("Completed RoomBilling in {0}", sw.Elapsed));

                        populateSubsidy = true;
                    }

                    if (args.BillingCategory.HasFlag(BillingCategory.Store))
                    {
                        var storeDataClean = new WriteStoreDataCleanProcess(new WriteStoreDataCleanConfig {
                            Connection = conn, Context = "ProcessRepository.UpdateBilling", StartDate = sd, EndDate = ed, ClientID = args.ClientID
                        });
                        var storeData = new WriteStoreDataProcess(new WriteStoreDataConfig {
                            Connection = conn, Context = "ProcessRepository.UpdateBilling", Period = p, ClientID = args.ClientID, ItemID = 0
                        });

                        sw.Restart();
                        storeDataClean.Start();
                        result.Add(string.Format("Completed StoreDataClean in {0}", sw.Elapsed));

                        sw.Restart();
                        storeData.Start();
                        result.Add(string.Format("Completed StoreData in {0}", sw.Elapsed));

                        sw.Restart();
                        step1.PopulateStoreBilling();
                        result.Add(string.Format("Completed StoreBilling in {0}", sw.Elapsed));
                    }

                    if (!temp && populateSubsidy)
                    {
                        sw.Restart();
                        var step4 = new BillingDataProcessStep4Subsidy(new Step4Config {
                            Connection = conn, Context = "ProcessRepository.UpdateBilling", Period = p, ClientID = args.ClientID
                        });

                        step4.PopulateSubsidyBilling();

                        result.Add(string.Format("Completed SubsidyBilling in {0}", sw.Elapsed));
                    }

                    sw.Stop();
                }

                result.Add(string.Format("Completed at {0:yyyy-MM-dd HH:mm:ss}, time taken: {1}", DateTime.Now, DateTime.Now - startTime));

                conn.Close();

                return(result);
            }
        }