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
Related
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 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.
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 task, that I'm unsure on how i should approach.
there's a list of doubles, and i need to group them together to add up to a specific value.
Say i have:
14.6666666666666,
14.6666666666666,
2.37499999999999,
1.04166666666665,
1.20833333333334,
1.20833333333334,
13.9583333333333,
1.20833333333334,
3.41666666666714,
3.41666666666714,
1.20833333333334,
1.20833333333334,
14.5416666666666,
1.20833333333335,
1.04166666666666,
And i would like to group into set values such as 12,14,16
I would like to take the highest value in the list then group it with short ones to equal the closest value above.
example:
take double 14.6666666666666, and group it with 1.20833333333334 to bring me close to 16, and if there are anymore small doubles left in the list, group them with that as well.
Then move on to the next double in the list..
That's literally the "Cutting stock Problem" (Sometimes called the 1 Dimensional Bin Packing Problem). There are a number of well documented solutions.
The only way to get the "Optimal" solution (other than a quantum computer) is to cycle through every combination, and select the best outcome.
A quicker way to get an "OK" solution is called the "First Fit Algorithm". It takes the requested values in the order they come, and removes them from the first piece of material that can fulfill the request.
The "First Fit Algorithm" can be slightly improved by pre-ordering the the values from largest to smallest, and pre-ordering the materials from smallest to largest. You could also uses the material that is closest to being completely consumed by the request, instead of the first piece that can fulfill the request.
A compromise, but one that requires more code is a "Genetic Algorithm". This is an over simplification, but you could use the basic idea of the "First Fit Algorithm", but randomly swap two of the values before each pass. If the efficiency increases, you keep the change, and if it decreases, you go back to the last state. Repeat until a fixed amount of time has passed or until you're happy.
Put the doubles in a list and sort them. Grab the highest value that is less than the target to start. Then loop through from the start of the list adding the values until you reach a point where adding the value will put you over the limit.
var threshold = 16;
List<double> values = new List<double>();
values.Add(14.932034);
etc...
Sort the list:
values = values.OrderBy(p => p).ToList();
Grab the highest value that is less than your threshold:
// Highest value under threshold
var highestValue = values.Where(x => x < threshold).Max();
Now perform your search and calculations until you reach your solution:
currentValue = highestValue
Console.WriteLine("Starting with: " + currentValue);
foreach(var val in values)
{
if(currentValue + val <= theshold)
{
currentValue = currentValue + val;
Console.WriteLine(" + " + val.ToString());
}
else
break;
}
Console.WriteLine("Finished with: " + currentValue.ToString());
Console.ReadLine();
Repeat the process for the next value and so on until you've output all of the solutions you want.
So. I've been working on a tip calculator for a local business and i'm running into some issues.
Its supposed to first take 10% of the total tips and subtract it from the total tip amount (this is a bonus for people closing the store), take the remaining tip amount and divide it giving front workers 65% and back workers 35% then take the number of front workers who are closing, divide the 10% among the front workers who are closing and the back workers who are closing and give them their respective amounts as mentioned before (65% or 35%) ontop of the split 10% then spit the totals out into their respective boxes.
Its not spitting out the amounts. I've written the equations out several times on paper and implemented them but somehow they are quite clicking.
private void calcButton_Click(object sender, EventArgs e)
{
//define objects
double tipAmount;
double tipReduction;
double tipDeduction;
double frontWorkers;
double backWorkers;
double closeFrontWorkers;
double closeBackWorkers;
double frontWorkersUse;
double backWorkersUse;
double closeFrontWorkersUse;
double closeBackWorkersUse;
double frontPer;
double backPer;
double closeFrontPer;
double closeBackPer;
double tipDeductionPer;
double totalCloseWorkers;
double frontWorkersNew;
double backWorkersNew;
tipAmount = double.Parse(tipBox.Text);
frontWorkers = double.Parse(frontWorkBox.Text);
backWorkers = double.Parse(backWorkBox.Text);
closeFrontWorkers = double.Parse(frontCloseWorkBox.Text);
closeBackWorkers = double.Parse(backCloseWorkBox.Text);
frontWorkersNew = frontWorkers + closeFrontWorkers;
backWorkersNew = backWorkers + closeBackWorkers;
tipReduction = tipAmount * .9;
tipDeduction = tipAmount - tipReduction;
totalCloseWorkers = closeFrontWorkers + closeBackWorkers;
tipDeductionPer = tipDeduction / totalCloseWorkers;
frontWorkersUse = tipReduction * .65;
backWorkersUse = tipReduction * .35;
closeFrontWorkersUse = (frontWorkersUse/frontWorkersNew);
closeBackWorkersUse = (backWorkersUse/backWorkersNew);
frontPer = frontWorkersUse / frontWorkersNew;
backPer = backWorkersUse / backWorkersNew;
closeFrontPer = (closeFrontWorkersUse / closeFrontWorkers) + tipDeduction;
closeBackPer = (closeBackWorkersUse / closeBackWorkers) + tipDeduction;
totalFrontWorkBox.Text = String.Format("{0:c}", frontPer);
totalBackWorkBox.Text = String.Format("{0:c}", backPer);
totalCloseWorkBoxFront.Text = String.Format("{0:c}", closeFrontPer);
totalCloseWorkBoxBack.Text = String.Format("{0:c}", closeBackPer);
}
Well, your (main) issue is in the logic.
I copied your code into Linqpad and changed it so that it wasn't relying on text boxes. Then I put some simple values in place of your numbers, and compared to what I'd expect. This showed that your error was with the closing workers' tip calculations, rather than the general tips. And the closing workers' tips were too big!
I then had a look at the relevant bits of your code and wondered why you were assigning the variable tipDeductionPer but never using its value - you're using tipDeduction and giving each of the closing staff the full amount!
What everyone else says in the comments is valid too. Your code could be a lot cleaner and easier to understand, and you'd find it easier to spot bugs. You could post on Code Review once you've fixed your bugs to get some pointers on how to improve it.