/// <summary> /// Whether user can claim the reward /// </summary> /// <param name="db"></param> /// <param name="rewardsId"></param> /// <param name="registrationId"></param> /// <returns></returns> public static bool CanClaim(NancyBlackDatabase db, int rewardsId, int registrationId) { var reg = db.GetById <AffiliateRegistration>(registrationId); var rewards = db.GetById <AffiliateReward>(rewardsId); var stat = AffiliateReward.GetRewardStats(db, reg); return(AffiliateReward.CanClaim(db, rewards, reg, stat)); }
internal static void ProcessReceiptCreation(NancyBlackDatabase db, Receipt obj) { // When payment receipt is created, create accounting entry db.Transaction(() => { var saleorder = db.GetById <SaleOrder>(obj.SaleOrderId); var paymentlog = db.GetById <PaymentLog>(obj.PaymentLogId); if (saleorder == null || paymentlog == null) { // bogus receipt throw new InvalidOperationException("Invalid Receipt was created"); } // Ensures all sale order logic has been ran // if the sale order was created before new system change if (saleorder.__createdAt < TaxSystemEpoch) { saleorder.UpdateSaleOrder(AdminModule.ReadSiteSettings(), db, false); } // Receipt will create 2 entries // 1) PaymentSource account increases, with total amount // TODO: Mapping from PaymentSource to Account AccountingEntry entry1 = new AccountingEntry(); entry1.TransactionDate = paymentlog.__createdAt; entry1.TransactionType = "income"; entry1.DebtorLoanerName = "Customer"; entry1.IncreaseAccount = paymentlog.PaymentSource; entry1.IncreaseAmount = saleorder.TotalAmount; entry1.SaleOrderId = saleorder.Id; db.UpsertRecord(entry1); if (saleorder.TotalTax > 0) { // 2) paid tax is decreased // (ภาษีขาย ทำให้ภาษีซื้อลดลง, ภาษีซื้อ บันทึกไว้ตอน InventoryInbound) AccountingEntry entry2 = new AccountingEntry(); entry2.TransactionDate = paymentlog.__createdAt; entry2.TransactionType = "expense"; entry2.DebtorLoanerName = "Tax"; entry2.DecreaseAccount = "Paid Tax"; entry2.DecreaseAmount = saleorder.TotalTax * -1; entry2.SaleOrderId = saleorder.Id; db.UpsertRecord(entry2); } }); }
private AngularChart GetLaptopSoldChart(List <SaleOrder> paidSaleOrders, NancyBlackDatabase db) { var chart = new AngularChart() { Title = "Sold Laptop", Labels = new List <string>(), EnumType = AngularChartType.Pie, IsLegend = true, Data = new List <dynamic>() }; var lookupLaptopId = new ConcurrentDictionary <int, int>(); // count sold laptop paidSaleOrders.AsParallel().ForAll((so => { // find laptop from item's url var laptop = so.ItemsDetail.Where(item => item.Url.StartsWith("/products/laptops")).FirstOrDefault(); // find laptop in another type (archive-laptops) if (laptop == null) { laptop = so.ItemsDetail.Where(item => item.Url.StartsWith("/products/archive-laptops")).FirstOrDefault(); } // count sold laptop when found one if (laptop != null) { var newQty = (int)laptop.Attributes.Qty; lookupLaptopId.AddOrUpdate(laptop.Id, newQty, (laptopId, existQty) => existQty + newQty); } })); var soldLaptopsData = new ConcurrentBag <dynamic>(); // group.Key = laptop's id and group.Value = laptop sold's count lookupLaptopId.AsParallel().ForAll((group => { var laptop = db.GetById <Product>(group.Key); soldLaptopsData.Add(new { Id = group.Key, Title = laptop.Title, Quantity = group.Value, Price = laptop.Price }); })); foreach (var laptop in soldLaptopsData.OrderBy(product => product.Price)) { chart.Labels.Add(laptop.Title); chart.Data.Add(laptop.Quantity); } return(chart); }
/// <summary> /// Get Content which match the same Tag /// </summary> /// <param name="db"></param> /// <param name="url"></param> /// <returns></returns> public static IEnumerable <object> GetContentByTag(NancyBlackDatabase db, Tag tag, int skip, int take) { var tags = db.Query <Tag>() .Where(rec => rec.Url == tag.Url && rec.Type == tag.Type && rec.Name == tag.Name); foreach (var rec in tags) { yield return(db.GetById(rec.Type, rec.ContentId)); } }
/// <summary> /// Refresh access token for given user id /// </summary> public static void RefreshTokenIfRequired(NancyBlackDatabase db, dynamic siteSettings, int userId) { var user = db.GetById <NcbUser>(userId); if (user == null) { throw new ArgumentException("User is not valid"); } GoogleAccountModule.RefreshTokenIfRequired(db, siteSettings, user); }
public IEnumerable <RMAItem> GetRMAItems(NancyBlackDatabase db) { if (this.RMAItemsId == null) { yield break; } foreach (var itemId in this.RMAItemsId) { yield return(db.GetById <RMAItem>(itemId)); } }
/// <summary> /// Discovers down line of current user /// </summary> /// <param name="reg"></param> /// <param name="maxLevel"></param> /// <returns></returns> public static IEnumerable <DownLineInformation> DiscoverDownLine(NancyBlackDatabase db, string topCode, int maxLevel = 2) { var cache = MemoryCache.Default["AffiliateCache"] as ILookup <string, dynamic>; if (cache == null) { cache = db.Query <AffiliateRegistration>() .AsEnumerable().ToLookup(r => r.RefererAffiliateCode, r => (new { AffiliateName = r.AffiliateName, NcbUserId = r.NcbUserId, AffiliateCode = r.AffiliateCode }) as dynamic); MemoryCache.Default.Add("AffiliateCache", cache, DateTimeOffset.Now.AddMinutes(10)); } Queue <string> referer = new Queue <string>(); referer.Enqueue(topCode); int currentLevel = 1; while (referer.Count > 0) { var current = referer.Dequeue(); var downline = cache[current]; foreach (var item in downline) { var user = db.GetById <NcbUser>((int)item.NcbUserId); if (user == null) { continue; } yield return(new DownLineInformation() { level = currentLevel, name = (string)item.AffiliateName, facebookId = user.FacebookAppScopedId, parent = current, RegistrationDate = user.__createdAt }); referer.Enqueue((string)item.AffiliateCode); } currentLevel++; if (currentLevel > maxLevel) { yield break; } } }
public void Static_Delete() { var temp = Path.GetTempFileName(); SQLite.SQLiteConnection conn = new SQLite.SQLiteConnection(temp, true); NancyBlackDatabase db = new NancyBlackDatabase(conn); var instance = db.UpsertRecord(new TestClass()); db.DeleteRecord(instance); var output = db.GetById<TestClass>(instance.Id); Assert.IsTrue(output == null, "DELETE was not success"); conn.Dispose(); File.Delete(temp); }
public static void SetPackingStatus(NancyBlackDatabase db, SaleOrder so) { // deduct stock of all product // first, we group the product id to minimize selects db.Transaction(() => { var products = from item in so.Items group item by item into g select g; foreach (var productIdGroup in products) { var product = db.GetById <Product>(productIdGroup.Key); product.Stock = product.Stock - productIdGroup.Count(); db.UpsertRecord <Product>(product); } }); }
public void AddItem(NancyBlackDatabase db, dynamic currentSite, int itemId) { var list = this.Items.ToList(); list.Add(itemId); this.Items = list.ToArray(); var newItem = db.GetById <Product>(itemId); var existItem = this.ItemsDetail.Where(p => p.Id == itemId && p.Title == newItem.Title && p.Price == newItem.Price).FirstOrDefault(); if (existItem == null) { JObject attr = newItem.Attributes; if (attr == null) { attr = new JObject(); newItem.Attributes = attr; } attr["Qty"] = 1; newItem.ContentParts = null; this.ItemsDetail.Add(newItem); } else { JObject attr = existItem.Attributes; attr["Qty"] = attr.Value <int>("Qty") + 1; } if (newItem.IsPromotionPrice) { var discount = this.ItemsDetail.Where(p => p.Url == "/dummy/dummy").FirstOrDefault(); if (discount != null) { discount.Price += newItem.CurrentPrice - newItem.Price; } } this.TotalAmount = this.TotalAmount - (this.ShippingFee + this.ShippingInsuranceFee + this.PaymentFee); this.TotalAmount += newItem.CurrentPrice; this.SetAllFee(currentSite); this.TotalAmount += this.ShippingFee + this.ShippingInsuranceFee + this.PaymentFee; db.UpsertRecord <SaleOrder>(this); }
/// <summary> /// Creates the click tracking from current context /// </summary> public static void CreateFromContext(NancyContext ctx, NancyBlackDatabase db, AffiliateRegistration reg) { var owner = db.GetById <NcbUser>(reg.NcbUserId); var record = new AffiliateShareClick(); record.UserId = ctx.Items["userid"] as string; // wont track own click if (record.UserId == owner.Guid.ToString()) { return; } record.IPAddress = ctx.Request.UserHostAddress; record.Url = ctx.Request.Url.Path + "?" + ctx.Request.Url.Query; record.AffiliateRegistrationId = reg.Id; db.UpsertRecord(record); }
public static AffiliateRegistration ApplyAffiliate(NancyBlackDatabase db, int userId, string refererCode = null) { AffiliateRegistration reg = null; // whether user already registered var existing = db.Query <AffiliateRegistration>() .Where(r => r.NcbUserId == userId) .FirstOrDefault(); // dont replace existing code if (existing == null) { reg = new AffiliateRegistration(); reg.NcbUserId = userId; reg.Commission = 0.01M; // start at 1 percent // automatic code var bytes = Encoding.ASCII.GetBytes(userId.ToString()); reg.AffiliateCode = Crc32.ComputeChecksumString(bytes); var user = db.GetById <NcbUser>(userId); if (user.Profile != null && user.Profile.first_name != null) { reg.AffiliateName = user.Profile.first_name; } if (reg.AffiliateName == null) { reg.AffiliateName = "SQUAD51#" + userId; } reg.RefererAffiliateCode = refererCode; db.UpsertRecord(reg); return(reg); } else { return(existing); } }
/// <summary> /// /// </summary> /// <param name="context"></param> /// <param name="user"></param> /// <param name="newProfile"></param> public void UpdateProfile(NancyBlackDatabase siteDb, int userId, dynamic newProfile) { var user = siteDb.GetById <NcbUser>(userId); user.Profile = newProfile; if (user.Profile != null) { siteDb.UpsertRecord <NcbUser>(user); // refresh the cache after update var key = "User-" + user.Guid; MemoryCache.Default.Remove(key); MemoryCache.Default.Add(key, user, new CacheItemPolicy() { SlidingExpiration = TimeSpan.FromMinutes(15) }); } }
public void AddItem(NancyBlackDatabase db, dynamic currentSite, int itemId, int qty = 1, bool save = true) { if (this.ItemsDetail == null) { this.ItemsDetail = new List <Product>(); } var existItem = this.ItemsDetail.Where(p => p.Id == itemId).FirstOrDefault(); if (existItem == null) { var newItem = db.GetById <Product>(itemId); JObject attr = newItem.Attributes; if (attr == null) { attr = new JObject(); newItem.Attributes = attr; } attr["Qty"] = qty; newItem.ContentParts = null; newItem.MetaDescription = null; newItem.MetaKeywords = null; newItem.Layout = null; newItem.PromotionReferenceDate = DateTime.Now; this.ItemsDetail.Add(newItem); } else { JObject attr = existItem.Attributes; attr["Qty"] = attr.Value <int>("Qty") + qty; } this.UpdateSaleOrder(currentSite, db, save); }
internal static void ProcessReceiptCreation(NancyBlackDatabase db, Receipt obj) { // When payment receipt is created, create accounting entry db.Transaction(() => { var saleorder = db.GetById <SaleOrder>(obj.SaleOrderId); var paymentlog = db.GetById <PaymentLog>(obj.PaymentLogId); if (paymentlog.Amount <= 0) { return; // perhaps it is an error } if (saleorder == null || paymentlog == null) { // bogus receipt throw new InvalidOperationException("Invalid Receipt was created"); } var currentSite = saleorder.SiteSettings; if (currentSite == null) { currentSite = AdminModule.ReadSiteSettings(); } // Ensures all sale order logic has been ran // if the sale order was created before new system change if (saleorder.__createdAt < TaxSystemEpoch) { saleorder.UpdateSaleOrder(currentSite, db, false); } // Receipt will create 4 entries // 1) PaymentSource account increases, with amount paid // TODO: Mapping from PaymentSource to Account AccountingEntry entry1 = new AccountingEntry(); entry1.TransactionDate = paymentlog.__createdAt; entry1.TransactionType = "income"; entry1.DebtorLoanerName = "Customer"; entry1.IncreaseAccount = paymentlog.PaymentSource; entry1.IncreaseAmount = paymentlog.Amount; entry1.SaleOrderId = saleorder.Id; db.UpsertRecord(entry1); // 2) Sales Tax Calculation { AccountingEntry taxExtry = new AccountingEntry(); taxExtry.TransactionDate = paymentlog.__createdAt; taxExtry.TransactionType = "taxcredit"; taxExtry.DebtorLoanerName = "Tax"; taxExtry.DecreaseAccount = "Tax Credit"; taxExtry.SaleOrderId = saleorder.Id; if (currentSite.commerce.billing.vattype == "addvat") { var tax = paymentlog.Amount * ((100 + (Decimal)currentSite.commerce.billing.vatpercent) / 100); taxExtry.DecreaseAmount = tax * -1; } if (currentSite.commerce.billing.vattype == "includevat") { var tax = paymentlog.Amount * ((Decimal)currentSite.commerce.billing.vatpercent / 100); taxExtry.DecreaseAmount = tax * -1; } db.UpsertRecord(taxExtry); } // 3) Payment Fee if (paymentlog.Fee > 0) { AccountingEntry feeEntry = new AccountingEntry(); feeEntry.TransactionDate = paymentlog.__createdAt; feeEntry.TransactionType = "buy"; feeEntry.DebtorLoanerName = paymentlog.PaymentSource; feeEntry.IncreaseAccount = "Payment Fee - " + paymentlog.PaymentSource; feeEntry.IncreaseAmount = paymentlog.Fee; feeEntry.SaleOrderId = saleorder.Id; db.UpsertRecord(feeEntry); } // 4) Receivable from the Sale Order { // existing receivable of this sale order var existingReceivable = db.Query <AccountingEntry>().Where(e => e.SaleOrderId == saleorder.Id && e.IncreaseAccount == "Receivable").ToList(); // see if we have any receivable of this sale order // if we had, we have to deduct it if (existingReceivable.Count > 0) { AccountingEntry deductReceivableEntry = new AccountingEntry(); deductReceivableEntry.TransactionDate = paymentlog.__createdAt; deductReceivableEntry.TransactionType = "arpayment"; deductReceivableEntry.DebtorLoanerName = "Receivable From Sales"; deductReceivableEntry.DecreaseAccount = "Receivable"; deductReceivableEntry.DecreaseAmount = paymentlog.Amount; deductReceivableEntry.SaleOrderId = saleorder.Id; db.UpsertRecord(deductReceivableEntry); } else { // this maybe the first payment, see if all amount has been paid // see all payment log of this sale order // we only query payments up to currently processing payment log // so that when we re var payments = db.Query <PaymentLog>().Where(l => l.SaleOrderId == saleorder.Id && l.Id <= paymentlog.Id).ToList(); var remaining = saleorder.TotalAmount - payments.Sum(p => p.Amount); if (remaining > 0) { // this is partial payment - create new receivable AccountingEntry receivableEntry = new AccountingEntry(); receivableEntry.TransactionDate = paymentlog.__createdAt; receivableEntry.TransactionType = "newaccount"; receivableEntry.DebtorLoanerName = "Receivable From Sales"; receivableEntry.IncreaseAccount = "Receivable"; receivableEntry.IncreaseAmount = remaining; receivableEntry.SaleOrderId = saleorder.Id; db.UpsertRecord(receivableEntry); } // this is full payment in one go, no need for receivable } } }); }
/// <summary> /// /// </summary> /// <param name="db"></param> public void UpdateSaleOrder(dynamic currentSite, NancyBlackDatabase db, bool save = true) { // Update Total this.TotalAmount = 0; // Total without discount decimal totalWithoutDiscount = 0; // snapshot the products into this sale order // so that if there is a change in product info later // we still have the one that customer sees var oldItemsDetail = this.ItemsDetail; this.ItemsDetail = new List <Product>(); //lookupItemDetail is used for provent duplication var lookupItemDetail = new Dictionary <int, Product>(); foreach (var item in this.Items) { var product = db.GetById <Product>(item); // in case some product no longer exist if (product == null) { var previousProduct = oldItemsDetail.Where(p => p.Id == item).FirstOrDefault(); this.ItemsDetail.Add(previousProduct); continue; } product.ContentParts = null; // check for duplication if (lookupItemDetail.ContainsKey(product.Id)) { var existProduct = lookupItemDetail[product.Id]; JObject attr = existProduct.Attributes; attr["Qty"] = attr.Value <int>("Qty") + 1; } else { JObject attr = product.Attributes; if (attr == null) { attr = new JObject(); product.Attributes = attr; } attr["Qty"] = 1; this.ItemsDetail.Add(product); lookupItemDetail.Add(product.Id, product); } this.TotalAmount += product.CurrentPrice; totalWithoutDiscount += product.Price; } // insert discount when there are some item with discount price (all in one discount) if (this.TotalAmount != totalWithoutDiscount) { // find all negative prices var totalNegativePrices = this.ItemsDetail.Where(i => i.CurrentPrice < 0).Sum(i => i.CurrentPrice); var attr = new JObject(); attr.Add("Qty", 1); var discount = new Product() { Title = "Discount", Price = this.TotalAmount - totalWithoutDiscount, Url = "/dummy/dummy", Attributes = attr }; this.ItemsDetail.Add(discount); this.TotalDiscount = (discount.Price + totalNegativePrices) * -1; this.TotalWithoutDiscount = totalWithoutDiscount + (totalNegativePrices * -1); } this.SetAllFee(currentSite); this.TotalAmount += this.ShippingFee + this.ShippingInsuranceFee + this.PaymentFee; this.TotalAmount = Math.Round(this.TotalAmount, 2, MidpointRounding.AwayFromZero); // TAX Calculation if (currentSite.commerce.billing.vattype == "addvat") { this.TotalAmountWithoutTax = this.TotalAmount; this.TotalTax = this.TotalAmountWithoutTax * (100 + (int)currentSite.commerce.billing.vatpercent) / 100; this.TotalAmount = this.TotalAmountWithoutTax + this.TotalTax; } if (currentSite.commerce.billing.vattype == "includevat") { this.TotalAmountWithoutTax = this.TotalAmount * 100 / (100 + (int)currentSite.commerce.billing.vatpercent); this.TotalTax = this.TotalAmount - this.TotalAmountWithoutTax; } if (!string.IsNullOrEmpty(this.Currency)) { JObject rate = CommerceAdminModule.ExchangeRate; decimal want = (decimal)rate.Property(this.Currency).Value; decimal home = (decimal)rate.Property("THB").Value; this.CurrencyConversionRate = want / home; Func <decimal, decimal> toWant = (decimal input) => input * this.CurrencyConversionRate * 1.03m; foreach (Product current in this.ItemsDetail) { current.Price = toWant(current.Price); current.DiscountPrice = toWant(current.DiscountPrice); } this.ShippingFee = toWant(this.ShippingFee); this.ShippingInsuranceFee = toWant(this.ShippingInsuranceFee); this.PaymentFee = toWant(this.PaymentFee); this.TotalAmount = toWant(this.TotalAmount); } this.SiteSettings = currentSite; if (save == false) { return; // Just update the details for calculation } db.Transaction(() => { // need to insert to get ID db.UpsertRecord <SaleOrder>(this); this.SaleOrderIdentifier = string.Format(CultureInfo.InvariantCulture, "SO{0:yyyyMMdd}-{1:000000}", this.__createdAt, this.Id); // save the SO ID again db.UpsertRecord <SaleOrder>(this); }); }
public static void HandlePayment(NancyBlackDatabase db, PaymentLog log, DateTime paidWhen) { // ensure only one thread is processing this so lock (BaseModule.GetLockObject(log.SaleOrderIdentifier)) { // find the sale order var so = db.Query <SaleOrder>() .Where(row => row.SaleOrderIdentifier == log.SaleOrderIdentifier) .FirstOrDefault(); bool isPaymentReceived = false; JArray exceptions = new JArray(); if (so == null) { exceptions.Add(JObject.FromObject(new { type = "Wrong SO Number", description = "Wrong SO Number" })); goto EndPayment; } log.SaleOrderId = so.Id; log.PaymentDate = paidWhen; // check duplicated payment log (sometime we got double request from PaySbuy) if (log.PaymentSource == PaymentMethod.PaySbuy && !log.IsErrorCode) { var jsonStr = ((JObject)log.FormResponse).ToString(); var duplicatedRequests = db.QueryAsJObject("PaymentLog", "FormResponse eq '" + jsonStr + "'").ToList(); if (duplicatedRequests.Count > 0) { exceptions.Add(JObject.FromObject(new { type = "Duplicated Request", description = string.Format( "Duplicated with Id: {0}", duplicatedRequests.First().Value <int>("Id")) })); goto EndPayment; } } // Wrong Payment Status if (so.PaymentStatus == PaymentStatus.PaymentReceived) { so.IsDuplicatePayment = true; exceptions.Add(JObject.FromObject(new { type = "Wrong Status", description = string.Format( "Current paymentlog status of SO is: {0}", PaymentStatus.DuplicatePayment) })); } // Error code received if (log.IsErrorCode) { so.PaymentStatus = PaymentStatus.WaitingForPayment; exceptions.Add(JObject.FromObject(new { type = "Error Code", description = "Error Code Received from Payment Processor: " + log.ResponseCode })); goto EndPayment; } // after this line will never be run until EndPayment when IsErrorCode == true if (so.PaymentStatus != PaymentStatus.PaymentReceived && log.Amount != so.TotalAmount) { log.IsPaymentSuccess = true; so.PaymentStatus = PaymentStatus.Deposit; so.PaymentReceivedDate = DateTime.Now; // Need to use this to manage queue exceptions.Add(JObject.FromObject(new { type = "Split Payment", description = string.Format( "Expects: {0} amount from SO, payment is {1}", so.TotalAmount, log.Amount) })); var paymentlogs = db.Query <PaymentLog>() .Where(p => p.SaleOrderIdentifier == so.SaleOrderIdentifier); var splitPaymentLogs = (from sPLog in paymentlogs where sPLog.IsErrorCode == false select sPLog).ToList(); isPaymentReceived = so.TotalAmount <= splitPaymentLogs.Sum(splog => splog.Amount) + log.Amount; } if (exceptions.Count == 0 || isPaymentReceived) { log.IsPaymentSuccess = true; so.PaymentStatus = PaymentStatus.PaymentReceived; so.PaymentReceivedDate = DateTime.Now; } EndPayment: log.Exception = exceptions; db.UpsertRecord <PaymentLog>(log); CommerceModule.PaymentOccured(so, db); if (log.IsPaymentSuccess) { // Set Receipt number var rc = db.UpsertRecord <Receipt>(new Receipt() { SaleOrderId = so.Id, PaymentLogId = log.Id }); rc.SetIdentifier(); db.UpsertRecord(rc); CommerceModule.PaymentSuccess(so, db); } db.UpsertRecord <SaleOrder>(so); // reset the one time code used foreach (var item in so.ItemsDetail) { if (item.Url.StartsWith("/promotions/code")) { if (item.Attributes.onetime != null) { var product = db.GetById <Product>(item.Id); product.Url = product.Url.Replace("/promotions/code", "/promotions/code/archive-onetime"); db.UpsertRecord(product); } } } // Automate change status to WaitingForOrder for add item to PO if (exceptions.Count == 0 || isPaymentReceived) { if (so.Status == SaleOrderStatus.Confirmed) { so.Status = SaleOrderStatus.WaitingForOrder; db.UpsertRecord <SaleOrder>(so); } } } }
/// <summary> /// Claim the rewards /// </summary> /// <param name="db"></param> /// <param name="rewardsId"></param> /// <param name="registrationId"></param> /// <returns></returns> public static AffiliateRewardsClaim ClaimReward(NancyBlackDatabase db, int rewardsId, int registrationId) { var canClaim = AffiliateReward.CanClaim(db, rewardsId, registrationId); if (canClaim == false) { return(null); } AffiliateReward rewards; var reg = db.GetById <AffiliateRegistration>(registrationId); rewards = db.GetById <AffiliateReward>(rewardsId); if (rewards.MaxPerUser > 0) { lock (BaseModule.GetLockObject("RewardClaim-Reg-" + registrationId)) { var totalClaimedByUser = db.Query <AffiliateRewardsClaim>() .Where(c => c.AffiliateRewardsId == rewards.Id && c.AffiliateRegistrationId == registrationId).Count(); if (totalClaimedByUser >= rewards.MaxPerUser) { return(null); } } } if (rewards.TotalQuota > 0) { lock (BaseModule.GetLockObject("RewardClaim-" + rewardsId)) { var totalClaimed = db.Query <AffiliateRewardsClaim>().Where(c => c.AffiliateRewardsId == rewards.Id).Count(); rewards.RemainingQuota = rewards.TotalQuota - totalClaimed; db.UpsertRecord(rewards); } } if (rewards.IsRewardsClaimable == false) { return(null); } if (rewards.IsCodeDiscount || rewards.IsFreeGiftInSaleOrder) { var until = DateTime.MaxValue.Ticks; if (rewards.CodeDicountExpiryInDays != null) { until = DateTime.Now.AddDays(rewards.CodeDicountExpiryInDays.Value).Ticks; } if (rewards.CodeDiscountExpiryDate != null) { until = rewards.CodeDiscountExpiryDate.Value.Ticks; } AffiliateRewardsClaim claim = null; db.Transaction(() => { // free gift also gets created as code Product p = new Product(); if (rewards.IsCodeDiscount) { p.Price = rewards.CodeDiscountAmount * -1; p.Attributes = new { rewardId = rewards.Id, description = rewards.Title + ", ราคาก่อนส่วนลดขั้นต่ำ: " + rewards.MinimumPurchaseAmount, min = rewards.MinimumPurchaseAmount, onetime = true, until = until, discount = rewards.CodeDiscountAmount, affiliateName = reg.AffiliateName, require = rewards.RequiredProductIds, }; } if (rewards.IsFreeGiftInSaleOrder) { p.DiscountPrice = 0; p.Price = rewards.CodeDiscountAmount; p.PromotionEndDate = new DateTime(until); p.MasterProductId = rewards.RewardsProductId; p.IsVariation = true; p.Attributes = new { rewardId = rewards.Id, description = rewards.Title + ", ราคาก่อนส่วนลดขั้นต่ำ: " + rewards.MinimumPurchaseAmount, min = rewards.MinimumPurchaseAmount, onetime = true, until = until, discount = rewards.CodeDiscountAmount, isfreeproduct = 1, affiliateName = reg.AffiliateName, require = rewards.RequiredProductIds, }; } db.UpsertRecord(p); var code = hashids.Encode(p.Id, reg.Id); p.Url = "/promotions/code/" + code; p.Title = "Affiliate Discount: " + code; if (rewards.IsFreeGiftInSaleOrder) { p.Title = "GIFT ITEM:" + rewards.Title; } db.UpsertRecord(p); claim = new AffiliateRewardsClaim(); claim.AffiliateRegistrationId = reg.Id; claim.NcbUserId = reg.NcbUserId; claim.AffiliateCode = reg.AffiliateCode; claim.DiscountCode = code; claim.RewardsName = rewards.Title; claim.AffiliateRewardsId = rewards.Id; claim.ProductId = p.Id; claim.CouponAttributes = p.Attributes; db.UpsertRecord(claim); }); return(claim); } { var claim = new AffiliateRewardsClaim(); claim.AffiliateRegistrationId = reg.Id; claim.NcbUserId = reg.NcbUserId; claim.AffiliateCode = reg.AffiliateCode; claim.RewardsName = rewards.Title; claim.AffiliateRewardsId = rewards.Id; claim.ProductId = rewards.RewardsProductId; db.UpsertRecord(claim); return(claim); } }
public void Static_Update() { var temp = Path.GetTempFileName(); SQLite.SQLiteConnection conn = new SQLite.SQLiteConnection(temp, true); NancyBlackDatabase db = new NancyBlackDatabase(conn); var instance = db.UpsertRecord(new TestClass()); instance.Name = "Test Update"; db.UpsertRecord(instance); var check = db.GetById<TestClass>(instance.Id); Assert.IsTrue(check.Name == "Test Update", "UPDATE was not success"); conn.Dispose(); File.Delete(temp); }
/// <summary> /// Submit purchase invoice and create neccessary accounting entries /// </summary> /// <param name="pi"></param> public static void SubmitPurchaseInvoice(PurchaseInvoice pi, NancyBlackDatabase db) { List <AccountingEntry> toAdd = new List <AccountingEntry>(); var inventoryEntry = new AccountingEntry(); inventoryEntry.TransactionDate = pi.PurchasedDate; inventoryEntry.IncreaseAccount = "Inventory"; inventoryEntry.IncreaseAmount = pi.TotalProductValue; inventoryEntry.DecreaseAccount = pi.PaidByAccount; inventoryEntry.DecreaseAmount = pi.TotalProductValue * -1; inventoryEntry.DocumentNumber = pi.SupplierInvoiceNumber; inventoryEntry.DebtorLoanerName = db.GetById <Supplier>(pi.SupplierId).Name; inventoryEntry.Notes = ""; foreach (var item in pi.Items) { inventoryEntry.Notes += item.Qty + "x" + db.GetById <Product>(item.ProductId).Title + "\r\n"; } toAdd.Add(inventoryEntry); var taxCredit = new AccountingEntry(); taxCredit.TransactionDate = pi.PurchasedDate; taxCredit.IncreaseAccount = "Tax Credit"; taxCredit.IncreaseAmount = pi.Tax; taxCredit.DecreaseAccount = pi.PaidByAccount; taxCredit.DecreaseAmount = pi.Tax * -1; taxCredit.DocumentNumber = pi.SupplierInvoiceNumber; taxCredit.DebtorLoanerName = "Tax"; taxCredit.Notes = "Tax Credit from Invoice: " + pi.SupplierInvoiceNumber + ", " + inventoryEntry.DebtorLoanerName; toAdd.Add(taxCredit); if (pi.AdditionalCost != 0) { var addCost = new AccountingEntry(); addCost.TransactionDate = pi.PurchasedDate; addCost.IncreaseAccount = "Expense"; addCost.IncreaseAmount = pi.AdditionalCost; addCost.DecreaseAccount = pi.PaidByAccount; addCost.DecreaseAmount = pi.AdditionalCost * -1; addCost.DocumentNumber = pi.SupplierInvoiceNumber; addCost.DebtorLoanerName = inventoryEntry.DebtorLoanerName; addCost.Notes = "Additional cost of buying from: " + pi.SupplierInvoiceNumber; toAdd.Add(addCost); } if (pi.Shipping != 0) { var shipping = new AccountingEntry(); shipping.TransactionDate = pi.PurchasedDate; shipping.IncreaseAccount = "Expense"; shipping.IncreaseAmount = pi.Shipping; shipping.DecreaseAccount = pi.PaidByAccount; shipping.DecreaseAmount = pi.Shipping * -1; shipping.DocumentNumber = pi.SupplierInvoiceNumber; shipping.DebtorLoanerName = inventoryEntry.DebtorLoanerName; shipping.Notes = "Shipping cost of buying from: " + pi.SupplierInvoiceNumber; if (!string.IsNullOrEmpty(pi.ShippingInvoiceNumber)) { shipping.DocumentNumber = pi.ShippingInvoiceNumber; } toAdd.Add(shipping); } foreach (var item in toAdd) { item.__createdAt = DateTime.Now; item.__updatedAt = DateTime.Now; } if (pi.IsConsignment) { inventoryEntry.DecreaseAccount = "Payable"; inventoryEntry.DueDate = pi.ConsignmentDueDate; if (pi.TaxEffectiveDate == default(DateTime)) { toAdd.Remove(taxCredit); } else { taxCredit.TransactionDate = pi.TaxEffectiveDate; } } db.Connection.InsertAll(toAdd); }
/// <summary> /// Updates the sale order /// </summary> /// <param name="db"></param> public void UpdateSaleOrder(dynamic currentSite, NancyBlackDatabase db, bool save = true) { // if we dont have site settings, use the one provided // otherwise use the remembered one if (this.SiteSettings == null) { this.SiteSettings = currentSite; } else { currentSite = this.SiteSettings; } // Update Total this.TotalAmount = 0; // Total without discount decimal totalWithoutDiscount = 0; // New Logic 2018 - we will primariry use itemsdetail // if it already exists - so that admin can add/remove items // freely and customer still sees the old prices //lookupItemDetail is used for prevent duplication var lookupItemDetail = new Dictionary <int, Product>(); Action <int> addnewProduct = (item) => { var product = db.GetById <Product>(item); if (product == null) { return; // id does not exists } product.ContentParts = null; product.MetaDescription = null; product.MetaKeywords = null; product.Layout = null; product.PromotionReferenceDate = DateTime.Today; // check for duplication if (lookupItemDetail.ContainsKey(product.Id)) { var existProduct = lookupItemDetail[product.Id]; JObject attr = existProduct.Attributes; attr["Qty"] = attr.Value <int>("Qty") + 1; } else { JObject attr = product.Attributes; if (attr == null) { attr = new JObject(); product.Attributes = attr; } attr["Qty"] = 1; this.ItemsDetail.Add(product); lookupItemDetail.Add(product.Id, product); } this.TotalAmount += product.CurrentPrice; if (product.Price > 0) { totalWithoutDiscount += product.Price; } }; // generate itemsdetail list from items list if not exists if (this.ItemsDetail == null || this.ItemsDetail.Count == 0) { this.ItemsDetail = new List <Product>(); foreach (var item in this.Items) { addnewProduct(item); } } else { HashSet <int> processedProductId = new HashSet <int>(); // otherwise - the items list is being generated from the itemsdetail list var newItemsList = new List <int>(); foreach (var item in this.ItemsDetail) { processedProductId.Add(item.Id); if (item.Url == "/dummy/dummy") { continue; } if (item.Attributes["Qty"] == null) { continue; } else { for (int i = 0; i < (int)item.Attributes["Qty"]; i++) { // suppose to throw because we cannot verify product price due to cannot specify PromotionReferenceDate if (item.PromotionReferenceDate == default(DateTime)) { item.PromotionReferenceDate = this.__createdAt; } newItemsList.Add(item.Id); this.TotalAmount += item.CurrentPrice; if (item.CurrentPrice > item.Price) // this is not discount { totalWithoutDiscount += item.CurrentPrice; } else if (item.CurrentPrice < 0 && item.CurrentPrice == item.Price) { // do nothing } else { totalWithoutDiscount += item.Price; } } } } foreach (var id in this.Items) { if (processedProductId.Contains(id) == false) { addnewProduct(id); newItemsList.Add(id); } } this.Items = newItemsList.ToArray(); } Func <Product, int> getSortOrder = (p) => { var orderList = new string[] { "/laptops/", "/monitor/", "/calibrate/", "/cpu/", "/gpu/", "/ram/", "/m2/", "/hdd/", "/wifi/", "/keyboard/", "/thermal/", "/os/" }; if (p.Url.Contains("/promotion")) { return(int.MaxValue); } for (int i = 0; i < orderList.Length; i++) { if (p.Url.IndexOf(orderList[i]) > 0) { return(i); } } return(int.MaxValue); }; this.ItemsDetail = this.ItemsDetail.OrderBy(p => getSortOrder(p)).ToList(); // remove the discount item var discountItem = this.ItemsDetail.Where(i => i.Url == "/dummy/dummy").FirstOrDefault(); if (discountItem != null) { this.ItemsDetail.Remove(discountItem); } // insert discount when there are some item with discount price (all in one discount) if (this.TotalAmount != totalWithoutDiscount) { // find all negative prices var totalNegativePrices = this.ItemsDetail.Where(i => i.CurrentPrice < 0).Sum(i => i.CurrentPrice); var attr = new JObject(); attr.Add("Qty", 1); if (this.TotalAmount > totalWithoutDiscount) { // discount is more than older amount // this can happen with item that has price = 0 discountItem = new Product() { Title = "Discount", Price = 0, Url = "/dummy/dummy", Attributes = attr }; } else { discountItem = new Product() { Title = "Discount", Price = this.TotalAmount - (totalWithoutDiscount + totalNegativePrices), Url = "/dummy/dummy", Attributes = attr }; } this.ItemsDetail.Add(discountItem); this.TotalDiscount = (discountItem.Price); this.TotalWithoutDiscount = totalWithoutDiscount; } else { this.TotalDiscount = 0; this.TotalWithoutDiscount = this.TotalAmount; } this.SetAllFee(); this.TotalAmount += this.ShippingFee + this.ShippingInsuranceFee + this.PaymentFee; this.TotalAmount = Math.Round(this.TotalAmount, 2, MidpointRounding.AwayFromZero); // TAX Calculation if (currentSite.commerce.billing.vattype == "addvat") { this.TotalAmountWithoutTax = this.TotalAmount; this.TotalTax = this.TotalAmountWithoutTax * (100 + (int)currentSite.commerce.billing.vatpercent) / 100; this.TotalAmount = this.TotalAmountWithoutTax + this.TotalTax; } if (currentSite.commerce.billing.vattype == "includevat") { this.TotalAmountWithoutTax = this.TotalAmount * 100 / (100 + (int)currentSite.commerce.billing.vatpercent); this.TotalTax = this.TotalAmount - this.TotalAmountWithoutTax; } if (save == false) { return; // Just update the details for calculation } db.Transaction(() => { // need to insert to get ID db.UpsertRecord <SaleOrder>(this); this.SaleOrderIdentifier = string.Format(CultureInfo.InvariantCulture, "SO{0:yyyyMMdd}-{1:000000}", this.__createdAt, this.Id); // save the SO ID again db.UpsertRecord <SaleOrder>(this); }); }