Good day coding godz,
I am trying to clean up some code and was wondering how to reduce the Cylcomatic Complexity on a method I have created. This method is used during an Import of a CSV file. One of the fields in the CSV file is a license type that is a string (ie "BFI Supervised Lender - Website #2 License") I am converting this to a Integer (1,2,3 or 4) that will be saved to the Database to reference the type of industry based on license type.
Below is my method. Would appreciate some tips for a shade tree VB.NET coder...
Private Function CheckIndustryType(LicName)
Dim VAR_IndType As Integer
Select Case Text_LicName
Case "BFI Level I Check Cashing - Branch License"
VAR_IndType = 3
Case "BFI Level II Check Cashing - Branch Certificate"
VAR_IndType = 3
Case "BFI Supervised Lender - Branch License"
VAR_IndType = 1
Case "BFI Deferred Presentment - Branch License"
VAR_IndType = 3
Case "BFI Supervised Lender - Website #1 License"
VAR_IndType = 1
Case "BFI Supervised Lender - Website #2 License"
VAR_IndType = 1
Case "BFI Supervised Lender - Website #3 License"
VAR_IndType = 1
Case "BFI Supervised Lender - Website #4 License"
VAR_IndType = 1
Case "BFI Supervised Lender - Website #5 License"
VAR_IndType = 1
Case "BFI Supervised Lender - Website #6 License"
VAR_IndType = 1
Case "BFI Level II Check Cashing - Company License"
VAR_IndType = 3
Case "BFI Level I Check Cashing - Company License"
VAR_IndType = 3
Case "fI Branch Mortgage Lender/Servicer"
VAR_IndType = 2
Case "BFI Branch Mortgage Lender/Servicer - Other Trade Name #1"
VAR_IndType = 2
Case "BFI Branch Mortgage Lender/Servicer - Other Trade Name #2"
VAR_IndType = 2
Case "BFI Branch Mortgage Lender/Servicer - Other Trade Name #3"
VAR_IndType = 2
Case "BFI Branch Mortgage Lender/Servicer - Other Trade Name #4"
VAR_IndType = 2
Case "BFI Branch Mortgage Lender/Servicer - Other Trade Name #5"
VAR_IndType = 2
Case "BFI Branch Mortgage Lender/Servicer - Other Trade Name #6"
VAR_IndType = 2
Case "BFI Mortgage Lender / Servicer License"
VAR_IndType = 2
Case "BFI Mortgage Lender/Servicer License - Other Trade Name #1"
VAR_IndType = 2
Case "BFI Mortgage Lender/Servicer License - Other Trade Name #2"
VAR_IndType = 2
Case "BFI Mortgage Lender/Servicer License - Other Trade Name #3"
VAR_IndType = 2
Case "BFI Mortgage Lender/Servicer License - Other Trade Name #4"
VAR_IndType = 2
Case "BFI Mortgage Lender/Servicer License - Other Trade Name #5"
VAR_IndType = 2
Case "BFI Mortgage Lender/Servicer License - Other Trade Name #6"
VAR_IndType = 2
Case Else
VAR_IndType = 4
End Select
Return VAR_IndType
End Function
I would use a dictionary in a situation like this. It reduces the number of independent paths to 2, the second is necessary since you're using a default value of course.
Module Module1
Dim industryTypeMap As New Dictionary(Of String, Int32) From {{"BFI Level I Check Cashing - Branch License", 3},
{"BFI Level II Check Cashing - Branch Certificate", 3},
{"BFI Supervised Lender - Branch License", 1}}
Sub Main()
Console.WriteLine(CheckIndustryType("BFI Supervised Lender - Branch License"))
Console.WriteLine(CheckIndustryType("BFI Level II Check Cashing - Branch Certificate"))
Console.WriteLine(CheckIndustryType("Other"))
End Sub
Private Function CheckIndustryType(LicName As String)
Dim industryInt As Int32
If industryTypeMap.TryGetValue(LicName, industryInt) Then
Return industryInt
Else
Return 4
End If
End Function
End Module
This outputs:
1
3
4
You could also define the dictionary in the function to maintain all of the code together but that would obviously run slower if you're calling the function repeatedly.
As per the comment below - Ideally you would place the actual mapping in an external item that could be updated without recompiling the code (eg a config file or a database).
I shortened the number of cases by using StartsWith and Contains with Select Case True.
Private Function CheckIndustryType(LicName As String) As Integer
Dim VAR_IndType As Integer
Select Case True
Case LicName.Contains("Check Cashing")
VAR_IndType = 3
Case LicName.StartsWith("BFI Supervised Lender")
VAR_IndType = 1
Case LicName.StartsWith("BFI Deferred Presentment")
VAR_IndType = 3
Case LicName.StartsWith("fI")
VAR_IndType = 2
Case LicName.StartsWith("BFI Branch Mortgage Lender")
VAR_IndType = 2
Case Else
VAR_IndType = 4
End Select
Return VAR_IndType
End Function
A long list of long strings is risky, because any change is quite likely to introduce a typo and then the strings don't match. I would probably make an Enum:
Enum LicenseType
BFILevelICheckCashingBranchLicense
BFILevelIICheckCashingBranchCertificate
BFISupervisedLenderBranchLicense
...
End Enum
and then test against that value. To reduce the complexity, I would make an attribute like:
Class VarIndTypeAttribute
Inherits System.Attribute
Public VarIndType As Integer
Sub New(varIndType As Integer
Me.VarIndType = varIndType
End Sub
End Class
and then the enum becomes:
Enum
<VarIndType(3)>
BFILevelICheckCashingBranchLicense
<VarIndType(3)>
BFILevelIICheckCashingBranchCertificate
<VarIndType(1)>
BFISupervisedLenderBranchLicense
...
End Enum
and then you make a method like
<Extension>
Function ToVarIndType(enumValue As [Enum]) As Integer
Dim att = enumValue.GetAttributeOfType(Of VarIndTypeAttribute)()
Return If(att IsNot Nothing, att.VarIndType, 0)
End Function
and then
Dim valueEnum As LicenseType = ...
Dim VarIndType = valueEnum.ToVarIndType
and you don't need the lookup function at all!
You probably want VarIndType to be an Enum as well, so you don't have magic values like 0 and 3 throughout the app.
Related
I'm trying to make it so that if someone wants 3 or less stripes on their shorts it costs 50 cent per stripe on top of the 5.50 base cost for a pair of shorts and then every stripe after the third costs 2 euro each. It works if they chose 3 or less but once I enter any stripe amount above 3 it just displays the base 5.50 cost for the shorts. Not sure what to do any help is appreciated.
I have declared all my variables correctly, I assume the problem is with the code below
'calculate cost of Shorts
If mskShortStripes.Text <= 3 Then
dblTotalShorts += CDbl(mskShorts.Text * 5.5) + (mskShortStripes.Text * 0.5)
ElseIf mskShortStripes.Text > 3 Then
dblTotalShorts += CDbl(mskShorts.Text * 5.5) + (mskShortStripes.Text <= 3 * 0.5) + (mskShortStripes.Text > 3 * 2)
End If
You're asking for trouble working with the .Text property directly as if it were a number. It is not. Fun things happen when the value in your control is not actually a number.
Use Integer.TryParse to convert that string to a number:
Dim numberOfStripes As Integer
If Integer.TryParse(mskShortStripes.Text, numberOfStripes) Then
If numberOfStripes >= 0 Then
' ... now do some math in here with the "numberOfStripes" variable ...
Else
MessageBox.Show("Number of Stripes can't be negative!")
End If
Else
MessageBox.Show("Invalid Number of Stripes!")
End If
I am new to Odoo Accounting. I have been encountering a problem in calculating debit and credit amount of Vendor Bill. I have added two new fields in purchase order line, discount_type and discount_amt. The subtotal value must be (price_unit * quantity) - discount. I could compute the subtotal amount. But when I check the journal items, the debit and credit amount are not changed. I mean the discount amount are not subtracted. But when I saved the form, I got the error which said debit and credit were not balanced. How can I do that?
def compute_price_subtotal(self):
for line in self:
line.discount_type = line.purchase_line_id.discount_type
line.discount_amt = line.purchase_line_id.discount_amt
qty = line.quantity or 0
price_unit = line.price_unit or 0
subtotal = price_unit * qty
discount_type = line.discount_type
discount_amount = line.discount_amt
if discount_type == 'fixed':
discount = discount_amount * qty
line.price_subtotal = subtotal - discount
elif discount_type == 'percentage':
discount = subtotal * (discount_amount / 100)
line.price_subtotal = subtotal - discount
else:
line.price_subtotal = subtotal
if line.move_id.type in line.move_id.get_outbound_types():
sign = 1
elif line.move_id.type in line.move_id.get_inbound_types():
sign = -1
else:
sign = 1
price_subtotal = sign * line.price_subtotal
line.update({
'debit': price_subtotal > 0.0 and price_subtotal or 0.0,
'credit': price_subtotal < 0.0 and -price_subtotal or 0.0,
})
The above method is to calculate the price_subtotal, debit and credit.
In the picture, the untaxed amount is 13800 and tax is 690. So the total amount will be 13800 + 690 = 14490. But in the Journal items, it shows 15000 and the subtotal values are different.
This is because you only modify the line you are interested in.
This tries to modify the debit/credit but it is leaving the account move unbalanced.
As this violate a constrains, the modification of the accounting is prevented.
You need to balance the move before updating anything in it.
This involves updating the tax line and the payable/receivable line too.
Once you have all of those lines, you can update the whole account move.
This means that you have to make something like this:
(assuming the calculation is already done)
lines_to_write = [
(1, line_A_id, line_A_values),
(1, tax_line_id, tax_line_values),
(1, receivable_line_id, receivable_line_values)
]
move.write({'line_ids': lines_to_write})
You can get the list of command here
Btw, to recompute the debit and credit when a business field is changed, you can (should) call the method _get_fields_onchange_subtotal_model to get the new values and then to update the account move line with those new values.
One of the reason is the fact that the accounting and the invoice could be in different currency.
Disclaimer: you should modify taxes only if you are sure of what you are doing.
This can have an impact on the user's tax report.
I'm currently making a blackjack game for my project in school in Visual Basic.
In blackjack, when you have aces (value initially 11) their value turns to 1 when the total value of the cards is > 21. In code, this would just take away 10 for every ace
I'm stuck on this.
This is the code I have (that doesn't work):
Do While PlayerValue > 21 And counter <= noAcesPlayer
counter += 1
PlayerValue -= 10
Loop
In a senario, I have a: 2, 8, A, 8 (=29)
But since there is an Ace, and the total value is > 21, the value should have 10 subtracted from it (=19) - the above code does not do this.
Another scenario would be 10, 8, A, A (=40)
Again, the two Aces should turn into 1's, since the total value > 21, giving 20.
Any help would be greatly appreciated. :)
Here is an approach
Public Enum CardFace
None
Ace
Two
Three
Four
Five
Six
Seven
Eight
Nine
Ten
Jack
Queen
King
End Enum
This code should produce a value of twenty
Dim cards As New List(Of CardFace) From {CardFace.Ten, CardFace.Eight, CardFace.Ace, CardFace.Ace}
Dim total As Integer = 0
Dim numofAces As Integer = 0
For Each c As CardFace In cards
Debug.WriteLine(c.ToString)
If c = CardFace.Ace Then
numofAces += 1
Else
total += c
End If
Next
If numofAces > 0 Then
If total + 11 + (numofAces - 1) > 21 Then
total += numofAces
Else
total += 11 + (numofAces - 1)
End If
End If
Debug.WriteLine(total)
The correct way to build a blackjack hand is the following (in pseudo-code):
Variables: total = 0, soft-flag = false
For each card in hand:
Add card value to total. Faces are 10, aces are 1.
If the card you added was an ace, set soft-flag = true
If total < 12 and soft-flag:
Add 10 to total
Else:
set soft-flag = false
That's it. Only one loop over the cards, no extraneous variables, and you're left with the total value and a flag indicating if the total is soft.
OK in this program i am doing something wrong my code is not comming out like it is suppose to. I am tried to mess with the totalcommission and move it around but I still got the same thing. I just don't know where i messed up at.
Module Module1
Sub Main()
Dim salesPersonName As String
Dim monthlySales, SalesPersonCommission, salesmancode As Integer
Dim totalSales, totalCommission, commission As Integer
Console.WriteLine("Please enter your name: ")
salesPersonName = Console.ReadLine()
Do While Not (salesPersonName.Equals("Eugene"))
Console.WriteLine("Please enter a sales code: ")
salesmancode = CInt(Console.ReadLine())
Select Case salesmancode
Case 1
SalesPersonCommission = CInt(0.01 * monthlySales + 1000)
Case 2
SalesPersonCommission = CInt(0.03 * monthlySales)
Case 3
SalesPersonCommission = CInt(0.04 * monthlySales + 500)
Case Is < 3
SalesPersonCommission = CInt(0.02 * monthlySales + 200)
End Select
totalCommission = totalCommission + commission
Console.WriteLine("Please enter your name= " & salesPersonName)
Console.WriteLine("Monthly Sales= " & monthlySales)
Console.WriteLine("Sales Code= " & salesmancode)
Console.WriteLine("Sales Person Commission= " & SalesPersonCommission)
Console.WriteLine("Please enter your name:")
salesPersonName = Console.ReadLine()
Loop
Console.WriteLine("Total Commission Dollars= " & monthlySales + totalSales)
End Sub
End Module
the code should come out with this:
Name Code MonthlySales Commission
Adam 1 500 1005
Bill 1 2000 1020
Chet 2 3000 90
Doug 3 4000 660
Eve 4 5000 300
Total Monthly Sales: $ 14500
Total CommissionDollars: $ 3075
EDIT:
Instead, this is the output received:
Name Code MonthlySales Commission
Allen 1 0 1000
Take a step back and look at your code, where are you adding monthlySales into totalSale ? Are you trying to print out monthlySales or the Total monthly sales? Do you need to display the totalCommission you are calculating? Do you need to ask for the monthlySales from the user? I am sure you can work through this code.
Should the 4th case be greater than 3 instead of less than 3 (to include all other cases)?
Case Is < 3
changed to:
Case Is > 3
I am using the NPV() function in VB.NET to get NPV for a set of cash flows.
However, the result of NPV() is not consistent with my results performing the calculation manually (nor the Investopedia NPV calc... which matches my manual results)
My correct manual results and the NPV() results are close, within 5%.. but not the same...
Manually, using the NPV formula:
NPV = C0 + C1/(1+r)^1 + C2/(1+r)^2 + C3/(1+r)^3 + .... + Cn/(1+r)^n
The manual result is stored in RunningTotal
With rate r = 0.04
and period n = 10
Here is my relevant code:
EDIT: Do I have OBOB somewhere?
YearCashOutFlow = CDbl(TxtAnnualCashOut.Text)
YearCashInFlow = CDbl(TxtTotalCostSave.Text)
YearCount = 1
PAmount = -1 * (CDbl(TxtPartsCost.Text) + CDbl(TxtInstallCost.Text))
RunningTotal = PAmount
YearNPValue = PAmount
AnnualRateIncrease = CDbl(TxtUtilRateInc.Text)
While AnnualRateIncrease > 1
AnnualRateIncrease = AnnualRateIncrease / 100
End While
AnnualRateIncrease = 1 + AnnualRateIncrease
' ZERO YEAR ENTRIES
ListBoxNPV.Items.Add(Format(PAmount, "currency"))
ListBoxCostSave.Items.Add("$0.00")
ListBoxIRR.Items.Add("-100")
ListBoxNPVCum.Items.Add(Format(PAmount, "currency"))
CashFlows(0) = PAmount
''''
Do While YearCount <= CInt(TxtLifeOfProject.Text)
ReDim Preserve CashFlows(YearCount)
CashFlows(YearCount) = Math.Round(YearCashInFlow - YearCashOutFlow, 2)
If CashFlows(YearCount) > 0 Then OnePos = True
YearNPValue = CashFlows(YearCount) / (1 + DiscountRate) ^ YearCount
RunningTotal = RunningTotal + YearNPValue
ListBoxNPVCum.Items.Add(Format(Math.Round(RunningTotal, 2), "currency"))
ListBoxCostSave.Items.Add(Format(YearCashInFlow, "currency"))
If OnePos Then
ListBoxIRR.Items.Add((IRR(CashFlows, 0.1)).ToString)
ListBoxNPV.Items.Add(Format(NPV(DiscountRate, CashFlows), "currency"))
Else
ListBoxIRR.Items.Add("-100")
ListBoxNPV.Items.Add(Format(RunningTotal, "currency"))
End If
YearCount = YearCount + 1
YearCashInFlow = AnnualRateIncrease * YearCashInFlow
Loop
EDIT: Using the following values:
Discount Rate = 4%
Life of Project = 10 years
Cash Flow 0 = -78110.00
Cash Flow 1 = 28963.23
Cash Flow 2 = 30701.06
Cash Flow 3 = 32543.12
Cash Flow 4 = 34495.71
Cash Flow 5 = 36565.45
Cash Flow 6 = 38759.38
Cash Flow 7 = 41084.94
Cash Flow 8 = 43550.03
Cash Flow 9 = 46163.04
Cash Flow 10 = 48932.82
Using the calculator at http://www.investopedia.com/calculator/NetPresentValue.aspx
And following the manual "textbook" formula I arrive at the same result:
Net Present Value: $225,761.70
I cannot seem to get NPV() to replicate this result... it spits out $217,078.59
I iterate it manually using the example same value... so they must be using a different function than I am...
The MSDN page example clearly states that the initial expense should be included in the cash flows list.
Normally you wouldn't include the first cashflow in the Visual Basic NPV() function (or at least we don't in the leasing world). You would discount all but the first cash flow, then add that first cash flow amount onto your Net Present Value. Here's an example of what I've done before in a calculation engine (minus error checking to simplify the example):
Dim leaseRentalsDiscounted As Double = 0.0
Dim rebatedCashFlows As IEnumerable(Of LeasePayment) = GetLeaseRentalsPaymentStream()
Dim firstFlow As LeasePayment = rebatedCashFlows(0)
Dim doubleStream As Double() = PaymentToDoubleArray(rebatedCashFlows.Skip(1))
If doubleStream.Length > 0 Then
Dim rate As Decimal = New Decimal(Me.Lease.DiscountRate / 100.0 / 12.0)
leaseRentalsDiscounted = NPV(rate, doubleStream)
End If
leaseRentalsDiscounted += firstFlow.Amount
Return leaseRentalsDiscounted
That could account for your 5% -- I know I've run into an issue like this before. To me, in the manual NPV formula you posted, C0 doesn't need to be in the stream that is discounted, so that's why I don't include it in the NPV() function.
The MSDN page notes that if your cash outflow begins at the beginning of the first period (instead of the end) the first value must be added to the NPV value and not included in the cash flows array.
Your manual calculation shows that your cash outflow (C0) occurs at time zero (present value), which indicates you should follow the MSDN page's suggestion.
Cory Larson is right, in part... but the MSDN documentation seems in error to me.
The problem is that the NPV() function is discounting the very first (n=0) element of the array when it should not; it is beginning at n=1
Even though the MSDN documentation specifics that the first element of the array should be the initial expense this is not the case with their function.
In the NPV() function, the first element of the array (as Cory Larson implied) should be the first real cash flow. Then, after the function returns a result, the result should have the initial expense subtracted.
This is because the NPV() function begins with n=1
using the NPV formula: NPV = C0 + C1/(1+r)^1 + C2/(1+r)^2 + C3/(1+r)^3 + .... + Cn/(1+r)^n
In the manual formula, Cn/(1+r)^n, for n=0 you use your initial expense... then the denominator is 1 (because n=0)
In my opinion, the MSDN example at http://msdn.microsoft.com/en-us/library/microsoft.visualbasic.financial.npv.aspx should be amended to the following:
Exclude the initial -70000 value from the array, shift all element down one in index, and decrease the array size by 1.
Then add the initial expense (-70000) to the variable NetPVal to arrive at the actual result.
Somebody should like MS know about their OBOB :D
(But it's actually a feature, right?)
EDIT: And the section which says " The array must contain at least one negative value (a payment) and one positive value (a receipt)."
In not accurate.
As Cory Larson pointed out: a negative value is not required in the array (and, in fact, should be left out!)