I am making a plugin that is supposed to take data from Quote Product entity.
By default, there is no discount in percent field, just discount amount. I added Discount% filed to the form (ad_discountpercent). Now I should calculate new price like this:
sum = (price - discount)*quantity*(100 - discount%)/100
I made a code for that. When i build this code there are no errors, but there are no changes in the CRM. I have also registered my plugin.
There are 3 fields manualdiscountamount , priceperunit , extendedamount that are Currency type fields. quantity is decimal and ad_discountpercent is integer. I have found a few methods for getting value from Currency, some are commented, some are not, because I don't know which one is proper.
One notice i checked fetch results, and they are as expected, so fetch is working fine. I compressed it in one line, because of the code length.
Here is the part of the code that should do the job. I have no information is there anything wrong with conversion, am I using proper way to set a new value?
I am new in this, so any comment would help me.
string fetch1 = #"<fetch count='50' ><entity name='quotedetail' ><attribute name='manualdiscountamount' /><attribute name='priceperunit' /><attribute name='ad_discountpercent' /><attribute name='quantity' /> <attribute name='extendedamount' /> <filter> <condition attribute='ad_discountpercent' operator='not-null'/> </filter > </ entity> </fetch>";
EntityCollection result = service.RetrieveMultiple(new FetchExpression(fetch1));
foreach (var c in result.Entities)
{
// int discountPer = (int)c.Attributes["ad_discountpercent"];
int discountPer = c.GetAttributeValue<int>("ad_discountpercent");
Money m = (Money)c.Attributes["priceperunit"];
decimal price = m.Value;
// decimal price = c.GetAttributeValue<decimal>("priceperunit");
Money m1 = (Money)c.Attributes["manualdiscountamount"];
decimal discount = m1.Value;
// decimal discount = c.GetAttributeValue<decimal>("manualdiscountamount");
//decimal discountPer = Convert.ToDecimal( c.Attributes["ad_discountpercent"]);
decimal quantity = Convert.ToDecimal(c.Attributes["quantity"]);
//decimal quantity = c.GetAttributeValue<decimal>("quantity");
decimal sum = (price - discount) * quantity * (100 - discountPer) / 100;
Money summTotal = new Money();
summTotal.Value = sum;
entity["extendedamount"] = summTotal;
service.Update(entity);
ExtendedAmount is an auto-calculated field based on priceperunit and quantity. So any updates to this field will be ignored by CRM.
Instead update the priceperunit and quantity to get the desired extendedamount.
entity["priceperunit"] = new Money((price - discount) * quantity * (100 - discountPer) / 100);
entity["quantity"] = 1;
service.Update(entity);
Is it possible that your query isn't returning any data, therefore your loop is never dropped into and your update never executes?
As previously suggested, take a look at how to debug a plugin via the plugin profiler.
Or, if you don't want to do that. Throw an temporary exception in your code after you do the RetrieveMultiple and in the message, add in the result.Entities.Count value to confirm how many records you're getting back.
Related
So I have a List<Product> that I need to loop and calculate some stuff. Every Product has propery Price and Tax. What I want to do is to have a method where I can loop throught the list and calculate the total price and the total tax, but I don't want to write to methods for this.
At the moment I have this:
private double calculateTotalPrice()
{
List<Product> temp = this.GetList();
double price = 0;
for(int i = 0; i < Count; i++)
{
price += temp[i].Price;
}
return price;
}
But with this method I need to have one method for calculating Price and another for Tax. Is it possible to change this method so that I can call it and at one moment calculate the Price, and next time I call it to calculate Tax? My hope is to do this without using If-statements but I'm not sure if this is possible.
Appreciate any help.
EDIT: Spelling
You can use an existing Sum method from System.Linq namespace without writing it by yourself
double price = temp.Sum(item => item.Price);
double tax = temp.Sum(item => item.Tax);
You can make use of the Aggregate LINQ extension method like below.
var totalPrice = temp.Aggregate((p1, p2) => p1.Price + p2.Price);
var totalTax = temp.Aggregate((t1, t2) => t1.Tax + t2.Tax);
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 years ago.
Improve this question
The question could be duplicate but my problem is totally different.
I have a one textbox and one label.
Example: label text is already exist with number like 450 Now I want to add number in textbox and the textbox value should be sum in 450 + 30 = 480 where 30 is textbox value.
what I have tried:
lbl_TotalAmount.Text = lbl_TotalAmount.Text + txt_DeliveryCharges.Text;
The above Result is: 45030
Another Code I Have tried:
double total = Convert.ToDouble(lbl_TotalAmount.Text);
double charges = Convert.ToDouble(txt_DeliveryCharges.Text);
double sum = total + charges;
lbl_TotalAmount.Text = String.Format(sum.ToString("0.00"));
The above code is going to sum every value that I put in the textbox and also when I remove one word the number is sum every time.
Please give me best solution I tried many solution to solve this but unable to do this.
Sorry for bad English.
The above code is going to sum every value that I put in the textbox and also when I remove one word the number is sum every time.
That's most likely because you're calling your code inside the TextChanged or the KeyPress events of the textbox; this means that every time you modify anything in your textbox, your code will fire and make the sum.
Instead, add a button and put your code inside its Click event, or if you want the sum to respond to every keypress while respecting your original value, save your value in a variable and use it to calculate the sum.
'Declare a variable at form level
Dim originalValue as Double
'Code where you put your value in a label, put it also in a variable
lbl_TotalAmount.text = 450
originalValue = 450
'Modify your code to use the original value instead of the label
double charges = Convert.ToDouble(txt_DeliveryCharges.Text);
double sum = originalValue + charges;
lbl_TotalAmount.Text = String.Format(sum.ToString("0.00"));
Your strings need to be converted (parsed) to doubles (since they are representing monetary values), but you need to be sure that you're not trying to parse something that can't be converted. TryParse() evaluates to true (or false if the parse fails), so you can avoid a possible exception.
Additionally, you've stated in comments that you want this to update as the text box is updated, so you'll need a variable that is out of scope to keep the total separated from the calculation. I'm going to work from the assumption that this is a shopping cart, or something like that. In that case, a List<> would be an obvious way to store the values of the items in the cart.
Try this:
using System;
using System.Collections.Generic; //required for List<>
namespace WidowsFormsApplication
{
public class ShoppingCart
{
List<double> shoppingCart = new List<double>();
protected void AddItemToCart()
{
shoppingCart.Add(450);
}
protected void UpdateShoppingCart()
{
double total = 0;
foreach (double item in shoppingCart) //calculate total of shoppingCart
{
total += item;
}
if (Double.TryParse(txt_DeliveryCharges.Text, out double charges))
{
total += charges; //add charges without altering shoppingCart
lbl_TotalAmount.Text = String.Format("{0:0.00}", total);
}
}
}
}
lbl_TotalAmount.Text = lbl_TotalAmount.Text + txt_DeliveryCharges.Text;
lbl_TotalAmount.Text and txt_DeliveryCharges.Text are text fields. You cannot do arithmetic on text fields. You have to convert to number, do the arithmetic then convert back
var charge = Int32.Parse(txt_DeliveryCharges.Text);
var total = Int32.Parse(lbl_TotalAmount.Text);
var newTotal = charge + total;
lbl_TotalAmount.Text = newTotal.ToString();
what you are doing is string concatenation
Not a sum of two numbers, You have to convert your strings to int first
here is how you can do that
int x = 0, y = 0;
if (Int32.TryParse(lbl_TotalAmount.Text out x) && Int32.TryParse(txt_DeliveryCharges.Text, out y))
{
// you know that the parsing attempt
// was successful
lbl_TotalAmount.Text = x + y;
}
yourlabel.Text = ""+ (Int32.Parse(yourlabel.Text)+Int.Parse(your textbox.Text));
or
yourlabel.Text = ""+(Double.Parse(yourlabel.Text)+Double.Parse(yourtextbox.Text));
are you using a double? only need that for decimal numbers that are super precise. whole numbers, use int... if you're using it to do money you want to use Decimal, but it depends on the situation.
I have a problem that I think may not have an answer other then completely rethinking the application, but hopefully you guys can prove me wrong!
I have an application that uses an Exchange Rate to back calculate a Base value from a Currency value to four decimal places. The calculation is pretty simple, and passes all the relevant unit tests giving correct results each time:
public static decimal GetBaseValue(decimal currencyAmount, RateOperator rateOperator,
double exchangeRate)
{
if (exchangeRate <= 0)
{
throw new ArgumentException(ErrorType.
ExchangeRateLessThanOrEqualToZero.ErrorInfo().Description);
}
decimal baseValue = 0;
if (currencyAmount != 0)
{
switch (rateOperator)
{
case RateOperator.Divide:
baseValue = Math.Round(currencyAmount * Convert.ToDecimal(exchangeRate),
4, MidpointRounding.AwayFromZero);
break;
case RateOperator.Multiply:
baseValue = Math.Round(currencyAmount / Convert.ToDecimal(exchangeRate),
4, MidpointRounding.AwayFromZero);
break;
default:
throw new ArgumentOutOfRangeException(nameof(rateOperator));
}
}
return baseValue;
}
I also have the equivalent to calculate currency from base, not shown to avoid confusing the issue and in any case pretty much identical code apart from parameter names and the reversal of the math operators in the switch statement.
My problem comes when I need to apply this process to a transaction which has multiple lines. The rule is that the total of the debits must equal the total of the credits, but some numbers are out by a fraction of a penny. This is because we are totalling the result of two or more individually converted numbers against a single conversion, and I believe that the accumulated rounding causes the error.
Let us assume the following:
Item value of $1.00
VAT value of $0.20 (20%)
Exchange rate of 1.4540 dollars to the pound
Now let us review an example transaction:
Line # Debit Credit
1 $1.20
2 $1.00
3 $0.20
This passes the test, as total credits match total debits.
When we convert (divide each dollar value by 1.454), we see the problem:
Line # Debit Credit
1 £0.8253
2 £0.6878
3 £0.1376
========================
Total £0.8254 £0.8253
========================
This fails and breaks the rule, I believe as a result of the two sets of rounding in the debit column and only one set on the credit column. Yet, I need to be able to calculate backwards and forwards with accuracy, and I need to make sure that the transaction balances in Currency and in Base across all the lines.
So to my question: how best to address this problem? Anyone experienced something similar, and if so do you mind sharing how you resolved it?
EDIT - The Solution I Used
Following the answer from Charles that pointed me absolutely in the right direction I am posting my working method for the benefit of anyone else who might face a similar issue. Whilst understanding that this specific code may not be suitable for a direct copy and paste solution, I hope that the comments and the procedure followed will be of help:
private static void SetBaseValues(Transaction transaction)
{
// Get the initial currency totals
decimal currencyDebitRunningTotal = transaction.CurrencyDebitTotal;
decimal currencyCreditRunningTotal = transaction.CurrencyCreditTotal;
// Only one conversion, but we do one per column
// Note that the values should be the same anyway
// or the transaction would be invalid
decimal baseDebitRunningTotal =
Functions.GetBaseValue(currencyDebitRunningTotal,
transaction.MasterLine.RateOperator,
transaction.MasterLine.ExchangeRate);
decimal baseCreditRunningTotal =
Functions.GetBaseValue(currencyCreditRunningTotal,
transaction.MasterLine.RateOperator,
transaction.MasterLine.ExchangeRate);
// Create a list of transaction lines that belong to this transaction
List<TransactionLineBase> list = new List<TransactionLineBase>
{ transaction.MasterLine };
list.AddRange(transaction.TransactionLines);
// If there is no tax line, don't add a null entry
// as that would cause conversion failure
if (transaction.TaxLine != null)
{
list.Add(transaction.TaxLine);
}
// Sort the list ascending by value
var workingList = list.OrderBy(
x => x.CurrencyCreditAmount ?? 0 + x.CurrencyDebitAmount ?? 0).ToList();
// Iterate the lines excluding any entries where Credit and Debit
// values are both null (this is possible on some rows on
// some transactions types e.g. Reconciliations
foreach (var line in workingList.Where(
line => line.CurrencyCreditAmount != null ||
line.CurrencyDebitAmount != null))
{
if (transaction.CanConvertCurrency)
{
SetBaseValues(line);
}
else
{
var isDebitLine = line.CurrencyCreditAmount == null;
if (isDebitLine)
{
if (line.CurrencyDebitAmount != 0)
{
line.BaseDebitAmount =
line.CurrencyDebitAmount ?? 0 /
currencyDebitRunningTotal * baseDebitRunningTotal;
currencyDebitRunningTotal -=
line.CurrencyDebitAmount ?? 0;
baseDebitRunningTotal -= line.BaseDebitAmount ?? 0;
}
}
else
{
if (line.CurrencyCreditAmount != 0)
{
line.BaseCreditAmount =
line.CurrencyCreditAmount ?? 0/
currencyCreditRunningTotal*baseCreditRunningTotal;
currencyCreditRunningTotal -= line.CurrencyCreditAmount ?? 0;
baseCreditRunningTotal -= line.BaseCreditAmount ?? 0;
}
}
}
}
}
This does rather depend on the situation, but one option for this is to only convert the totals and then allocate this proportionately to each of the parts using a reducing balance method. This ensures that the sum of the parts will always equal the total exactly.
Your debit and credit columns both add up to $1.20, so you convert this at your rate giving you £0.8253.
You then proportionately allocate this to your debit amounts from the smallest up to the largest. The theory behind the sorting is that you're less likely to care about the extra/missing pennies of a larger number.
So you start with the totals of $1.20 and £0.6878 and then calculate the proportion of your converted balance that applies to your smallest dollar amount:
$0.20 / $1.20 * 0.8253 = £0.1376
You then deduct the amounts from your totals (this is the 'reducing balance' part):
$1.20 - $0.20 = $1.00
£0.8253 - £0.1376 = £0.6877
And then calculate the next largest (as you only have 1 more amount in this example, this is trivial):
$1.00 / $1.00 * £0.6877 = £0.6877
So this gives you:
Line # Debit Credit
1 £0.8253
2 £0.6877
3 £0.1376
========================
Total £0.8253 £0.8253
========================
This is typically a business problem not a programming one. The results you're seeing are a result of mathematical rounding. In financial calculations the business will decide when the exchange rate is applied and it has to be applied once. For example, in your case it might be decided to apply it to the item value and then multiply by the VAT to get your total. This is also typically present in your regions accepted accounting/financial accepted standards.
I have code that is taking information from the database to insert into a list. One piece of data that I'm taking will be used for price. (I set its type to decimal in SQL Server), for example: " 1,80".
But when I select it, my decimal variable returns the value 2 !
I want to know if there are a simple way to make decimal not automatically round numbers.
This is the code:
public decimal preco { get; set; }
while (dr.Read())
{
clsCaProd oProd = new clsCaProd();
oProd.cod = dr.GetInt32(0);
oProd.preco = dr.GetDecimal(1); // Here returns "2" instead of "1,80"
oProd.info = dr.GetString(2);
oProd.categ = dr.GetString(3);
oProd.nome = dr.GetString(4);
lProd.Add(oProd);
}
If you declare a column as decimal, the default "scale" is zero. You need to specify the precision and scale.
Try this example:
declare #d1 decimal = 1.80
declare #d2 decimal(19,4) = 1.80
select #d1, #d2;
Results:
2 1.8000
I am trying to make a small pizza order form, but I have a problem with the calculations. After selecting a pizza, the unit price and total calculations are OK but selecting additions introduces a problem. After changing the NumericUpDown value, the calories are not correct (all units have constant prices and calories). The NumericUpDown's name is numberofunit. How can I calculate them?
if (pepper.Checked)
{
string peppereklendi =
Convert.ToString(Convert.ToDouble(unitprice.Text)+ pepperprice);
unitprice.Text = peppereklendi;
total.Text =
Convert.ToString(Convert.ToDecimal(unitprice.Text) * numberofunit.Value);
string pepperkaloriekle =
Convert.ToString(Convert.ToInt16(gizlikalori.Text) + pepperkalori);
gizlikalori.Text = pepperkaloriekle;
amountofcalorie.Text =
Convert.ToString(Convert.ToDecimal(gizlikalori.Text) * numberofunit.Value);
}
else
{
string peppereklendi = unitprice.Text;
unitprice.Text =
Convert.ToString(Convert.ToDouble(peppereklendi) - pepperprice);
total.Text = Convert.ToString(Convert.ToDecimal(unitprice.Text) * numberofunit.Value);
string pepperkaloriekle = gizlikalori.Text;
gizlikalori.Text =
Convert.ToString(Convert.ToDouble(pepperkaloriekle) - pepperkalori);
amountofcalorie.Text =
Convert.ToString(Convert.ToDecimal(gizlikalori.Text) * numberofunit.Value);
}
This Code is pepper's checkbox code.
This is the form of my application.
You should really try to separate the calculation logic from the UI logic (the form). Then the things will become much clearer:
// Get values from the text boxes
decimal up = Convert.ToDecimal(unitprice.Text);
decimal calories = Convert.ToDecimal(gizlikalori.Text);
decimal tot, totCalories;
// Do the calculation
if (pepper.Checked) {
up = up + pepperprice;
calories = calories + pepperkalori;
}
tot = up * numberofunit.Value;
totCalories = calories * numberofunit.Value;
// Assign the results to text boxes
unitprice.Text = up.ToString();
total.Text = tot.ToString();
gizlikalori.Text = calories.ToString();
amountofcalorie.Text = totCalories.ToString();
What you do wrong, is that you subtract the pepper price and pepper calories from the unit price and the unit calories if no pepper is selected. However, the unit price (and calories) are without the pepper already!
I do not see when you are performing this calculation, however if you perform it every time you are increasing the number of units, then you would be adding the pepper price each time! It would probaly be better to have a separate variable for the base unit price that remains unchanged when you check additions. Then always start the calculation from the base unit price.
In addition, you are mixing many different number types. This makes no sense.
The next step to enhance the code even more would be to create a separate class for the calculations. You could also use data binding. This would remove completely the need for doing conversions. See my answer to the following post
: manipulating-textbox-variables-in-calculations