You can println your error message, and return false.
Or you can return an Integer, 0 for no error, 1,2,3,… for all the errors which you handle.
These are thoughts which can be dependend on the future use of this function.
For now, choose what’s easiest for you (but type stable).
I see, I didn’t check in Pluto.
So, you can return a tuple of bool and string: (false,“my error”) in case of an error, and (true,“”) or (true,“no error”) in case of no error.
I would define the date field of type Date, Dates as strings are a nightmare (no validation, bad performance, etc.).
When you initialize the DataFrame this way, you create one (probably invalid) entry. It would be better to initialize it empty (but with typed columns):
A bool has just two values: true or false. You can also use integer 0 and 1. Doesn’t matter to much. Important: always returning the same type, e.g. (1,“error”) which is a Tuple{Int,String}.
Yes, you are right.
I want the function ledgertransaction to be agnostic of these details (at least a bit agnostic, of course some logic must be implemented). But dealing with Date-types at this point is quite a step, so I would postpone it for later.
I wanted to have an abbreviation for date, instead of numbers, as I feel this can create confusion. I will look through the documentation for Date.jl, and get this set up properly.
You are thinking too complex here. It’s easy like:
function ledgertransaction!( ledger, date, credit_account, credit_amount, debit_account, debit_amount )
if credit_amount-debit_amount != 0
return (false, "Error -- credit and debit must match.")
else if length(date) == 0
return (false, "Error -- no date entered")
else ...
...
return (true, "no error)
end
ledger=DataFrame(
date=[],
chequing=[],
expenses=[],
income=[]);
function ledgertransaction(
date,
credit_account,
credit_amount,
debit_account,
debit_amount
)
check=credit_amount-debit_amount
if length(date) == 0
return(false,"Error-- no date entered")
else
if !( credit_account in names(ledger) )
return (false, "Error -- credit account not found.")
else
if !( debit_account in names(ledger) )
return (false, "Error--debit account not found")
else
if check != 0
return (false,"Error -- credit and debit must match." )
else
default_row=Dict("chequing"=>0,"expenses"=>0,"income"=>0)
spec = Dict(
"date" => date,
credit_account => credit_amount,
debit_account => debit_amount)
row = merge(default_row, spec)
push!(ledger,row)
journal_entry=DataFrame(
date=["Jan 1, 2000","","",""],
credited_account=["", credit_account,"",""],
debited_account=["","",debit_account,""],
credit=[0,credit_amount,0,0],
debit=[0,0,debit_amount,0],
#balance=[0,
# sum(credit_account),
# sum(add_debit_account),
# 0]
#I need to figure out how to calculate my balance
)
end
end
end
end
end
journal_entry we don’t want yet, first steps first
default_row should be named default_values
using elseif instead of nested else if makes it more readable and less ends to use.
If you want to take a peek:
using DataFrames
ledger=DataFrame(
date=[],
chequing=[],
expenses=[],
income=[]);
function ledgertransaction(
date,
credit_account,
credit_amount,
debit_account,
debit_amount
)
if credit_amount-debit_amount != 0
return (false,"Error -- credit and debit must match.")
elseif length(date) == 0
return (false,"Error -- no date entered")
elseif !( credit_account in names(ledger) )
return (false, "Error -- credit account not found.")
elseif !( debit_account in names(ledger) )
return (false, "Error -- debit account not found")
else
default_values=Dict("chequing"=>0,"expenses"=>0,"income"=>0)
spec = Dict(
"date" => date,
credit_account => credit_amount,
debit_account => debit_amount)
row = merge(default_values, spec)
push!(ledger,row)
return (true, "no error")
end
end
This is, what we want.
But still issue “global variables” is not adressed! I explain what it means:
You are using the DataFrame ledger inside the function. But it is defined outside of the function. This works, because in Pluto (as in the REPL) ledger is implicitly global. This is not the case in modules e.g., this is called soft scope. For details about scope see Scope of Variables · The Julia Language
Example in a new REPL:
julia> function test()
println(i)
end
julia> test()
ERROR: UndefVarError: i not defined
julia> local i =5
5
julia> test()
ERROR: UndefVarError: i not defined
julia> global i = 5 #works without global in REPL
5
julia> test()
5
Our code we develop should work in REPL, Pluto or in a module or script without any changes. It should be as generic as possible. And we want to avoid anything which decreases our performance. The docs say, we should avoid globals. Even we don’t know why, we do it, because it’s easy to avoid, it doesn’t cost anything, it’s just better.
So, what do you suggest to avoid global ledger? Hint: ledger itself in our Pluto scenario is always global (or we put a local before the definition), but we want our function ledgertransaction not to rely on that. We want to have a working ledgertransaction even if ledger is not global!