private async Task _removeCapitalTracking(ProductForWareshousingCreationDto product, Guid storageId, Guid warehousingId)
        {
            var productStorages = await _context.ProductStorages.Where(ps => ps.StorageId == storageId && ps.ProductId == product.Id).ToListAsync();

            foreach (var productStorage in productStorages)
            {
                if (productStorage.Inventory >= product.InputAmount || setting.IsAllowNegativeInventoryBill)
                {
                    var    capitalPriceTrackings = JsonConvert.DeserializeObject <List <CapitalPriceTrackingDto> >(productStorage.CapitalPriceTrackings);
                    int    index          = capitalPriceTrackings.FindIndex(c => c.WarehousingId == warehousingId);
                    double deltaInventory = -1 * product.InputAmount;

                    var result = ChangeCapitalPriceTrackingsResultHelper.ChangeCapitalPriceTrackings(index,
                                                                                                     deltaInventory, product.InputPrice, capitalPriceTrackings, true);

                    productStorage.Inventory             = result.Inventory;
                    productStorage.CapitalPrice          = result.CapitalPrice;
                    productStorage.CapitalPriceTrackings = result.CapitalPriceTrackingsJson;

                    // remove detail inventory
                    var oldProductProductionList = (productStorage.ProductProductionDateList == null) ? new List <ProductProductionDateDto>() : JsonConvert.DeserializeObject <List <ProductProductionDateDto> >(productStorage.ProductProductionDateList);
                    var detailInputAmountList    = product.DetailInputAmountList;
                    foreach (var detailInputAmount in detailInputAmountList)
                    {
                        if (detailInputAmount.InputAmount > product.InputAmount)
                        {
                            throw new Exception("Detail InputAmount is greater than product InputAmount");
                        }

                        var oldProductProduction = oldProductProductionList.SingleOrDefault(p => p.ProductionWeekYear == detailInputAmount.ProductionWeekYear);
                        if (oldProductProduction != null)
                        {
                            oldProductProduction.Inventory -= detailInputAmount.InputAmount;
                        }
                        else
                        {
                            oldProductProductionList.Add(new ProductProductionDateDto()
                            {
                                ProductionWeekYear = detailInputAmount.ProductionWeekYear,
                                ProductionDate     = DateTimeHelper.ConvertWeekYearToDateTime(detailInputAmount.ProductionWeekYear),
                                Inventory          = detailInputAmount.InputAmount
                            });
                        }
                    }
                    productStorage.ProductProductionDateList = JsonConvert.SerializeObject(oldProductProductionList);
                }
                else
                {
                    throw new Exception("change_warehousing_make_negative_inventory");
                }
                _context.ProductStorages.Update(productStorage);
            }
        }
        new public async Task <Guid> EditAsync(Guid id, WarehousingForCreationDto updationDto)
        {
            var warehousing = await _entity.SingleOrDefaultAsync(w => w.Id == id);

            if (warehousing == null)
            {
                throw new Exception("Can not find warehousing with id = " + id);
            }
            if (!warehousing.IsActive)
            {
                throw new Exception("The destroyed warehousing can not be edited");
            }

            var oldProducts    = JsonConvert.DeserializeObject <List <ProductForWareshousingCreationDto> >(warehousing.ProductList);
            var editedProducts = updationDto.ProductList;

            double productMoney = 0;

            // when a old product is removed in new product list, we remove inventory
            foreach (var oldProduct in oldProducts)
            {
                var editedProduct = editedProducts.SingleOrDefault(p => p.Id == oldProduct.Id);
                if (editedProduct == null)
                {
                    await _removeCapitalTracking(oldProduct, updationDto.StorageId, id);
                }
            }

            foreach (var editedProduct in editedProducts)
            {
                // calculate product money
                productMoney += editedProduct.InputAmount * editedProduct.InputPrice;

                /* when not finding edited product on old product list, let add a new cappital tracking into
                 * ProductStorage of edited product */

                var oldProduct = oldProducts.SingleOrDefault(p => p.Id == editedProduct.Id);
                if (oldProduct == null)
                {
                    var productStorage = await _context.ProductStorages.SingleOrDefaultAsync(p => p.ProductId == editedProduct.Id &&
                                                                                             p.StorageId == warehousing.StorageId);

                    _addCapitalTracking(editedProduct, updationDto.StorageId, id, productStorage);
                }
                else
                {
                    var productStorage = await _context.ProductStorages.SingleOrDefaultAsync(p => p.ProductId == editedProduct.Id &&
                                                                                             p.StorageId == warehousing.StorageId);

                    // if we find a old product is same with edited product, we need to to update ProductProductionDate
                    if (editedProduct.DetailInputAmountList != null && editedProduct.DetailInputAmountList.Count > 0)
                    {
                        _updateInventoryDetail(editedProduct, oldProduct, productStorage);
                    }

                    // if we find old product is same with edited product, we need to update capital tracking of product storage


                    var capitalPriceTrackings = JsonConvert.DeserializeObject <List <CapitalPriceTrackingDto> >(productStorage.CapitalPriceTrackings);
                    int index = capitalPriceTrackings.FindIndex(c => c.WarehousingId == id);
                    if (index == -1) // hot fix, this shit. duplicate warehousing when create cause error on destroy, it remove all warehousing
                    {
                        var lastestRecord = capitalPriceTrackings[capitalPriceTrackings.Count - 1];

                        CapitalPriceTrackingDto capitalPriceTrackingDto = new CapitalPriceTrackingDto()
                        {
                            WarehousingId = id,
                            BillId        = Guid.Empty,
                            InventoryId   = Guid.Empty,
                            Amount        = editedProduct.InputAmount,
                            Inventory     = lastestRecord.Inventory + editedProduct.InputAmount,
                            CapitalPrice  = ProductStorageHelper.CalculateCapitalPrice(
                                lastestRecord.Inventory,
                                lastestRecord.CapitalPrice,
                                editedProduct.InputAmount,
                                editedProduct.InputPrice
                                )
                        };
                        capitalPriceTrackings.Add(capitalPriceTrackingDto);

                        index = capitalPriceTrackings.Count - 1;
                    }


                    double deltaInventory = editedProduct.InputAmount - oldProduct.InputAmount;

                    var result = ChangeCapitalPriceTrackingsResultHelper.ChangeCapitalPriceTrackings(index,
                                                                                                     deltaInventory, editedProduct.InputPrice, capitalPriceTrackings, false);

                    if (!setting.IsAllowNegativeInventoryBill && result.Inventory < 0)
                    {
                        throw new Exception("change_warehousing_make_negative_inventory");
                    }
                    // remove tracking record
                    capitalPriceTrackings.RemoveAt(index);
                    // save new information
                    productStorage.Inventory             = result.Inventory;
                    productStorage.CapitalPrice          = result.CapitalPrice;
                    productStorage.CapitalPriceTrackings = result.CapitalPriceTrackingsJson;

                    _context.Update(productStorage);
                }
            }

            foreach (PropertyInfo propertyInfo in updationDto.GetType().GetProperties())
            {
                if (warehousing.GetType().GetProperty(propertyInfo.Name) != null &&
                    propertyInfo.Name != "ProductList" &&
                    propertyInfo.Name != "SupplierBillList" &&
                    propertyInfo.Name != "InputDate"
                    )
                {
                    warehousing.GetType().GetProperty(propertyInfo.Name).SetValue(warehousing, propertyInfo.GetValue(updationDto, null));
                }
            }

            warehousing.SupplierBillList = JsonConvert.SerializeObject(updationDto.SupplierBillList);
            warehousing.UpdatedDateTime  = DateTime.Now;

            var user = await _userManager.GetUserAsync(_httpContextAccessor.HttpContext.User);

            warehousing.UpdatedUserId = user.Id;
            warehousing.ProductList   = JsonConvert.SerializeObject(updationDto.ProductList);


            warehousing.ProductMoney = productMoney;
            warehousing.SummaryMoney = warehousing.ProductMoney + warehousing.TaxMoney;
            warehousing.DebtMoney    = warehousing.SummaryMoney - updationDto.PaymentMoney;

            _entity.Update(warehousing);

            var created = await _context.SaveChangesAsync();

            if (created < 1)
            {
                throw new InvalidOperationException("Database context could not create Warehousing.");
            }

            return(warehousing.Id);
        }