Odoo 8 Allow Negative Invoice Line Item - python

I have a product called "Coupon" with negative amount which is used to offset the product price. However, it seems like Odoo 8 does not allow computation of negative amount to price_subtotal (it becomes 0.00):
Coupon ... ... 1 Each -40.0000 0.0000
When I remove the negative sign, it computes
Coupon ... ... 1 Each 40.0000 40.0000
From an accounting perspective, the total invoice should not be negative. That stays true. However, I do need to allow negative computation of invoice line item(s). Where and what do I need to change? I tried looking into account/account.py but to no avail so far - it's all just "tax" related.
Thanks in advance!
Details of the amount column for the line total
class account_invoice(models.Model)
....
#api.one
#api.depends('invoice_line.price_subtotal', 'tax_line.amount')
def _compute_amount(self):
self.amount_untaxed = sum(line.price_subtotal for line in self.invoice_line)
self.amount_tax = sum(line.amount for line in self.tax_line)
self.amount_total = self.amount_untaxed + self.amount_tax
....
class account_invoice_line(models.Model):
_name = "account.invoice.line"
_description = "Invoice Line"
_order = "invoice_id,sequence,id"
#api.one
#api.depends('price_unit', 'discount', 'invoice_line_tax_id', 'quantity',
'product_id', 'invoice_id.partner_id', 'invoice_id.currency_id')
def _compute_price(self):
price = self.price_unit * (1 - (self.discount or 0.0) / 100.0)
taxes = self.invoice_line_tax_id.compute_all(price, self.quantity, product=self.product_id, partner=self.invoice_id.partner_id)
self.price_subtotal = taxes['total']
if self.invoice_id:
self.price_subtotal = self.invoice_id.currency_id.round(self.price_subtotal)
#api.model
def _default_price_unit(self):
if not self._context.get('check_total'):
return 0
total = self._context['check_total']
for l in self._context.get('invoice_line', []):
if isinstance(l, (list, tuple)) and len(l) >= 3 and l[2]:
vals = l[2]
price = vals.get('price_unit', 0) * (1 - vals.get('discount', 0) / 100.0)
total = total - (price * vals.get('quantity'))
taxes = vals.get('invoice_line_tax_id')
if taxes and len(taxes[0]) >= 3 and taxes[0][2]:
taxes = self.env['account.tax'].browse(taxes[0][2])
tax_res = taxes.compute_all(price, vals.get('quantity'),
product=vals.get('product_id'), partner=self._context.get('partner_id'))
for tax in tax_res['taxes']:
total = total - tax['amount']
return total

Odoo's default behaviour is handling it as expected. The problem is custom code. (For more information read the questions comments)

Related

how to get price range with django?

i have price model
class Product(models.Model):
price = models.IntegerField
membership_discount = models.DecimalField
if i get price parameter, (ex. min_price = 100000, max_price = 500000)
I want to get the products multiplied by the price fields and membership_discount fields.
not this
Product.objects.filter(price__range = (min_price, max_price))
i want
Product.objects.filter(price * (1+membership_discount)__range = (min_price, max_price))
lte = less than or equal to
gte = greater than or equal to
this is documentation: https://docs.djangoproject.com/en/4.0/ref/models/querysets/#gt
max_price = #max price logic here
min_price = #min price logic her
#this will filter all products (price <= max_price and price >= min_price)
Product.objects.filter(price__lte = max_price, price__gte = min_price)
You could use annotations for the QuerySet and apply the filter on the annotation.
Product.objects.annotate(
member_price=F('price') * (1 + F('membership_discount'))
).filter(
member_price__range=(min_price, max_price)
)
If the pricefield and the membership_dicsount do not have the same type, you might need to make usage of the ExpressionWrapper with a specific output_field
Product.objects.annotate(
member_price=ExpressionWrapper(
F('price') * (1 + F('membership_discount')),
output_field=DecimalField()
)
).filter(
member_price__range=(min_price, max_price)
)
Docs:
https://docs.djangoproject.com/en/4.0/ref/models/querysets/#django.db.models.query.QuerySet.annotate
https://docs.djangoproject.com/en/4.0/topics/db/aggregation/
https://docs.djangoproject.com/en/4.0/ref/models/expressions/#using-f-with-annotations

Pyomo energy storage system dispatch optimization

I'm trying to create a model optimization for a energy storage system using pyomo. Using the demand in kWh from an household and the electricity prices, I would like to minimize the cost charging and discharging the battery at the right time. I already have a working model for 1 year of data and the model is able to find an optimal solution (see code below). However, when I try find a model for only three months (let's say from October to December), pyomo returns with a termination condition "unbound" but I can't figure out why.
Model for 1 year of data:
#Battery parameters
battery_capacity = 252
total_energy = 13.1
usable_energy = 12.4
nominal_voltage = 51.8
ratio = total_energy/usable_energy
power = usable_energy / ratio
nominal_current = usable_energy / nominal_voltage * 1000
recharging_hours = battery_capacity/nominal_current
battery_level_threshold = 0
model = pyo.ConcreteModel()
#Set time period
model.T = pyo.Set(initialize=pyo.RangeSet(len(df_2021)),ordered=True)
#PARAMETERS:
model.b_efficiency = pyo.Param(initialize=0.9)
model.b_min_cap = pyo.Param(initialize=0)
model.b_max_cap = pyo.Param(initialize=12.4)
model.b_charge_power = pyo.Param(initialize=power)
model.b_discharge_power = pyo.Param(initialize=power)
model.spot_prices = pyo.Param(model.T,initialize=dict(enumerate(df_2021["Price/kWh"],1)),within=pyo.Any)
model.demand = pyo.Param(model.T, initialize=dict(enumerate(df_2021["Demand"],1)),within=pyo.Any)
#Variables : also the variable has to be indexed with the time T
model.b_soc = pyo.Var(model.T, domain = pyo.NonNegativeReals, bounds = (model.b_min_cap, model.b_max_cap))
model.b_discharge = pyo.Var(model.T, domain = pyo.NonNegativeReals)
model.b_charge = pyo.Var(model.T, domain = pyo.NonNegativeReals)
model.elect_purchased = pyo.Var(model.T, domain = pyo.NonNegativeReals)
#CONSTRAINTS
#Purchase constraint
def purchase_constraint(model,t):
return model.elect_purchased[t] >= model.demand[t] - model.b_discharge[t] + model.b_charge[t]
#State of charge constraint
def soc_constraint(model,t):
if t == model.T.first():
return model.b_soc[t] == model.b_max_cap / 2 #- model.b_discharge[t] + model.b_charge[t]
else:
return model.b_soc[t] == model.b_soc[t-1] - model.b_discharge[t-1] + model.b_charge[t-1]
#Discharge and charge constraints
def discharge_constraint_1(model,t):
""" Maximum discharge rate within a single hour """
return model.b_discharge[t] <= model.b_discharge_power
def discharge_constraint_2(model,t):<br/>
""" Sets the maximum energy available to be discharged as the SOC - minimum SOC """
return model.b_discharge[t] <= model.b_soc[t] - model.b_min_cap
def charge_constraint_1(model,t):
""" Maximum charge rate within a single hour """
return model.b_charge[t] <= model.b_charge_power
def charge_constraint_2(model,t):<br/>
""" Sets the maximum energy available to be cahrge as the SOC max """
return model.b_charge[t] <= model.b_max_cap - model.b_soc[t]
model.purchase_c = pyo.Constraint(model.T, rule = purchase_constraint)
model.soc_c = pyo.Constraint(model.T, rule = soc_constraint)<br/>
model.discharge_c1 = pyo.Constraint(model.T,rule = discharge_constraint_1)
model.discharge_c2 = pyo.Constraint(model.T,rule = discharge_constraint_2)
model.charge_c1 = pyo.Constraint(model.T,rule = charge_constraint_1)
model.charge_c2 = pyo.Constraint(model.T,rule = charge_constraint_2)
#OBJECTIVE
expr = sum(model.elect_purchased[t] * model.spot_prices[t] for t in model.T)
model.objective = pyo.Objective(rule = expr, sense = pyo.minimize)
opt = pyo.SolverFactory('cbc',executable='/usr/bin/cbc')
results = opt.solve(model)
results.write()
Result 1 year data
The optimal solution is found
However, when I change the dataset, using the SAME model structure and constraints, pyomo doesn't find a solution.
Model for 3 months:
#Battery parameters
battery_capacity = 252
total_energy = 13.1
usable_energy = 12.4
nominal_voltage = 51.8
ratio = total_energy/usable_energy
power = usable_energy / ratio
nominal_current = usable_energy / nominal_voltage * 1000
recharging_hours = battery_capacity/nominal_current
battery_level_threshold = 0
model = pyo.ConcreteModel()
#Set time period
model.T = pyo.Set(initialize=pyo.RangeSet(len(df_2021)),ordered=True)
#PARAMETERS:
model.b_efficiency = pyo.Param(initialize=0.9)
model.b_min_cap = pyo.Param(initialize=0)
model.b_max_cap = pyo.Param(initialize=12.4)
model.b_charge_power = pyo.Param(initialize=power)
model.b_discharge_power = pyo.Param(initialize=power)
model.spot_prices = pyo.Param(model.T,initialize=dict(enumerate(df_2021["Price/kWh"],1)),within=pyo.Any)
model.demand = pyo.Param(model.T, initialize=dict(enumerate(df_2021["Demand"],1)),within=pyo.Any)
#Variables : also the variable has to be indexed with the time T
model.b_soc = pyo.Var(model.T, domain = pyo.NonNegativeReals, bounds = (model.b_min_cap, model.b_max_cap))
model.b_discharge = pyo.Var(model.T, domain = pyo.NonNegativeReals)
model.b_charge = pyo.Var(model.T, domain = pyo.NonNegativeReals)
model.elect_purchased = pyo.Var(model.T, domain = pyo.NonNegativeReals)
#CONSTRAINTS
#Purchase constraint
def purchase_constraint(model,t):
return model.elect_purchased[t] >= model.demand[t] - model.b_discharge[t] + model.b_charge[t]
#State of charge constraint
def soc_constraint(model,t):
if t == model.T.first():
return model.b_soc[t] == model.b_max_cap / 2 #- model.b_discharge[t] + model.b_charge[t]
else:
return model.b_soc[t] == model.b_soc[t-1] - model.b_discharge[t-1] + model.b_charge[t-1]
#Discharge and charge constraints
def discharge_constraint_1(model,t):
""" Maximum discharge rate within a single hour """
return model.b_discharge[t] <= model.b_discharge_power
def discharge_constraint_2(model,t):<br/>
""" Sets the maximum energy available to be discharged as the SOC - minimum SOC """
return model.b_discharge[t] <= model.b_soc[t] - model.b_min_cap
def charge_constraint_1(model,t):
""" Maximum charge rate within a single hour """
return model.b_charge[t] <= model.b_charge_power
def charge_constraint_2(model,t):<br/>
""" Sets the maximum energy available to be cahrge as the SOC max """
return model.b_charge[t] <= model.b_max_cap - model.b_soc[t]
model.purchase_c = pyo.Constraint(model.T, rule = purchase_constraint)
model.soc_c = pyo.Constraint(model.T, rule = soc_constraint)<br/>
model.discharge_c1 = pyo.Constraint(model.T,rule = discharge_constraint_1)
model.discharge_c2 = pyo.Constraint(model.T,rule = discharge_constraint_2)
model.charge_c1 = pyo.Constraint(model.T,rule = charge_constraint_1)
model.charge_c2 = pyo.Constraint(model.T,rule = charge_constraint_2)
#OBJECTIVE
expr = sum(model.elect_purchased[t] * model.spot_prices[t] for t in model.T)
model.objective = pyo.Objective(rule = expr, sense = pyo.minimize)
opt = pyo.SolverFactory('cbc',executable='/usr/bin/cbc')
results = opt.solv
Pyomo returns:
WARNING: Loading a SolverResults object with a warning status into
model.name="unknown";
- termination condition: unbounded
- message from solver: <undefined>
# ==========================================================
# = Solver Results =
# ==========================================================
# ----------------------------------------------------------
# Problem Information
# ----------------------------------------------------------
Problem:
- Name: unknown
Lower bound: None
Upper bound: inf
Number of objectives: 1
Number of constraints: 16993
Number of variables: 11329
Number of nonzeros: 2832
Sense: minimize
# ----------------------------------------------------------
# Solver Information
# ----------------------------------------------------------
Solver:
- Status: warning
User time: -1.0
System time: 0.45
Wallclock time: 0.58
Termination condition: unbounded
Termination message: Model was proven to be unbounded.
Statistics:
Branch and bound:
Number of bounded subproblems: 0
Number of created subproblems: 0
Black box:
Number of iterations: 0
Error rc: 0
Time: 0.6151924133300781
Since the only change between the two runs is the length of the dataframe, I don't really know where to look for the error.
Given that the model works on some data, but not on an alternate data source, we can obviously focus a bit on the data set (which isn't shown).
We have a huge clue in the error report that the problem is unbounded. This means that there is nothing to prevent the objective function from running away to infinity, or negative infinity in the case of a minimization problem. So, let's look at your objective function. You are:
sum(demand[t] * price[t] for t in T)
The domain of your demand variable is set to non-negative real numbers (good) and price[t] is a parameter read in from data, and you are minimizing. So the only way this could run away to negative infinity is if there are one (or more) negative prices in your data, which the solver would then act on and demand would be unbounded.
So, comb your data set. if you are using pandas, just use a logical search for rows where price is < 0, you'll likely find at least one. Then you'll have to decide if that is a typo or if it is realistic to have a negative price (which could happen in some systems), and if it is legit, you will have to impose some other constraint to limit the model in that situation.

Delivery price depending on order value with multiple thresholds

Suppose we purchase N products from different vendors. We have many suppliers, but not all products are available from every supplier. The price for each product is known to each retailer, unless the product is not available there. There is also a shipping fee for each seller. The shipping fee is payable only once for all orders from a given supplier. It may also be subject to special restrictions, e.g. if the total order value exceeds 179USD, shipping is free, otherwise shipping is 15.99 USD or it is impossible if it does not meet the minimum order value, e.g. 100 USD. The shipping price for each seller may vary. Based on the products selected and the pricing and shipping information from different vendors, how can we optimize the total cost (product price + shipping) for the entire product cart, assuming we always want to buy all products?
Currently, I can solve this problem for fixed delivery costs for each supplier, but I cannot cope with adding conditional constraints taking into account the value of the order with a given supplier, e.g .:
order> = 179 PLN = free delivery
order <179 PLN AND order> = 100 PLN = delivery 15.99 PLN
minimum order = PLN 100 (below this amount, the order is not possible with the given supplier.
As for the structure of data on delivery costs, the 'key' means the minimum amount from which the 'value', i.e. delivery cost, is met for a given supplier. If the cost of a product under 'product_prices' for a given supplier is 99999999, it means that the product is not available from them. Below is the example data:
'delivery_prices': {
'supplier1': {
100: 14.9,
},
'supplier2': {
0: 19.99,
179: 0,
},
'supplier3': {
100: 15,
200: 10,
250: 0,
},
}
Here is my optimization code as for now:
import pyomo.environ as pe
import pyomo.opt as po
solver = po.SolverFactory('glpk')
def optimization_problem(input_data, solver):
model = pe.ConcreteModel("All products filled")
# sets
model.I = pe.Set(initialize=input_data['supplier_names'])
model.J = pe.Set(initialize=input_data['product_names'])
# parameters
model.s = pe.Param(model.I, initialize=input_data['supplier_stock'])
model.d = pe.Param(model.J, initialize=input_data['demand'])
model.f = pe.Param(model.I, initialize=input_data['delivery_prices'])
model.c = pe.Param(model.I, model.J, initialize=input_data['product_prices'])
# variables
model.x = pe.Var(model.I, model.J, domain=pe.NonNegativeReals)
model.y = pe.Var(model.I, domain=pe.Binary) # product quantity for given suppliersupplier
# constraints
# if sum
def con_satisfaction(model, j):
return sum(model.x[i, j] for i in model.I) >= model.d[j]
model.con_satisfaction = pe.Constraint(model.J, rule=con_satisfaction)
def con_transportation(model, i):
return sum(model.x[i, j] for j in model.J) <= model.s[i] * model.y[i]
model.con_transportation = pe.Constraint(model.I, rule=con_transportation)
# objective
def obj_min_cost(model):
return sum(model.f[i] * model.y[i] for i in model.I)\
+ sum(model.c[i, j] * model.x[i, j] for i in model.I for j in model.J)
model.obj_min_cost = pe.Objective(sense=pe.minimize, rule=obj_min_cost)
solver.solve(model)
products_distribution = { key: value for key, value in model.x.extract_values().items() if value > 0}
products_suppliers = list(set([product[0] for product in products_distribution.keys()]))
suppliers_delivery_prices = {supplier:price for supplier, price in input_data['delivery_prices'].items() if supplier in products_suppliers}
return (model.obj_min_cost(), products_suppliers, suppliers_delivery_prices, products_distribution)
# EXECUTE FUNCTION
optimization_problem(example_dataset, solver)
Does anyone have any guidance how to incorporate conditional delivery prices?
Here is a working example with the introduction of a binary variable, indexed by supplier and tier. Also added are 2 constraints to link the tier to the lower and upper bounds on qty from a supplier (linking constraints).
import pyomo.environ as pe
solver = pe.SolverFactory('glpk')
s1, s2 = 'supplier 1', 'supplier 2'
p1, p2 = 'bike', 'cellphone'
# some fake data
input_data = { 'supplier_names' : [s1, s2],
'product_names' : [p1, p2],
'supplier_stock' : {(s1, p1): 1000, (s1, p2): 500, (s2, p1): 700, (s2, p2): 400},
'demand' : {p1: 50, p2: 80},
'product_prices' : {(s1, p1): 100, (s1, p2): 150, (s2, p1): 90, (s2, p2): 160}
}
max_products = 10000 # some reasonable large "big M" upper bound on the total number of products from a supplier
penalty = 10000 # some reasonably large penalty...
# supplier tier (qty, price)
delivery_prices = { s1: {'below min' : (0, penalty), # <100 N/A, use penalty
'1A' : (100, 19.99),
'1B' : (200, 0.00)},
s2: {'regular' : (0, 25.00),
'free' : (150, 0.00)} }
# make a convenience set of all the tier label possiblities accross all suppliers...
all_tiers = set()
for k in delivery_prices:
all_tiers.update(set(delivery_prices[k].keys()))
model = pe.ConcreteModel("All products filled")
# sets
model.suppliers = pe.Set(initialize=input_data['supplier_names'])
model.products = pe.Set(initialize=input_data['product_names'])
model.tiers = pe.Set(initialize=list(all_tiers))
model.supplier_tiers = pe.Set(within=model.suppliers * model.tiers, initialize={(s,t) for s in delivery_prices
for t in delivery_prices[s]})
# parameters
model.s = pe.Param(model.suppliers, model.products, initialize=input_data['supplier_stock'])
model.d = pe.Param(model.products, initialize=input_data['demand'])
model.c = pe.Param(model.suppliers, model.products, initialize=input_data['product_prices'])
# variables
model.x = pe.Var(model.suppliers, model.products, domain=pe.NonNegativeReals) # product quantity for given suppliersupplier
model.y = pe.Var(model.supplier_tiers, domain=pe.Binary) # buy from supplier s at tier t
# constraints
# link quantity to supplier tier LB
def tier(model, supplier, tier):
return sum(model.x[supplier, p] for p in model.products) >= model.y[supplier, tier]*delivery_prices[supplier][tier][0]
model.tier_min = pe.Constraint(model.supplier_tiers, rule=tier)
# link quantity to supplier UB... this forces selection of some tier to get anything from supplier
def upper(model, supplier):
return sum(model.x[supplier, p] for p in model.products) <= sum(model.y[supplier, tier] for tier in model.tiers
if (supplier, tier) in model.supplier_tiers) * max_products
model.upper_bound = pe.Constraint(model.suppliers, rule=upper)
# meet demand
def demand(model, product):
return sum(model.x[supplier, product] for supplier in model.suppliers) >= model.d[product]
model.demand = pe.Constraint(model.products, rule=demand)
# objective
def obj_min_cost(model):
return sum(model.x[s, p] * model.c[s, p] for s in model.suppliers for p in model.products) + \
sum(model.y[s, t] * delivery_prices[s][t][1] for s,t in model.supplier_tiers)
model.obj_min_cost = pe.Objective(sense=pe.minimize, rule=obj_min_cost)
result = solver.solve(model)
print(result)
model.display()
# products_distribution = { key: value for key, value in model.x.extract_values().items() if value > 0}
# products_suppliers = list(set([product[0] for product in products_distribution.keys()]))
# suppliers_delivery_prices = {supplier:price for supplier, price in input_data['delivery_prices'].items() if supplier in products_suppliers}

Invoice lines sum - Odoo v8

I'm trying to implement this scenario on invoice lines.
Let's say I have 50 products on my invoice line, 40 products are tax affected, and 10 aren't.
So, in the result of my invoice, I have two fields to compute this
exe = fields.Float(string='Monto Exento', digits=dp.get_precision('Account'),
store=True, readonly=True, compute='extras', track_visibility='always')
Should return the total of the 40 tax affected products, and this field
impo = fields.Float(string='Base Imponible', digits=dp.get_precision('Account'),
store=True, readonly=True, compute='extras', track_visibility='always')
Should return the total of the 10 other products, which aren't tax affected.
Now, I call this from a function, and compute the results:
#api.one
#api.depends('invoice_line.price_subtotal', 'tax_line.amount')
def extras(self):
self.exe = self.amount_untaxed + self.amount_tax
self.impo = self.amount_untaxed + self.amount_tax
rec=0
But it isn't working as expected, I don't know what could be wrong here, maybe it is because of how I'm handling this on extras function?
It just sums everything like the total amount of all the lines.
EDIT
Following #phillipstack answer, I've updated my code like this:
#api.one
#api.depends('invoice_line.price_subtotal', 'tax_line.amount')
def extras(self):
self.exe = self.amount_untaxed + self.amount_tax if self.amount_tax and self.amount_tax > 0 else 0
self.impo = self.amount_untaxed + self.amount_tax if not self.amount_tax or self.amount_tax == 0 else 0
But on impo field it just sums it all, taxed or not, I'm trying with one untaxed product, and one taxed.
On exe right it isn't showing any amount.
On exe it should show the total amount of untaxed products, and on impo it should show the total amount of taxed ones.
If You need more info please let me know.
You need to provide an if statement to tell your script to not sum if it meets certain criteria. I dont know enough about your requirements however this might work. Or something like this.
#api.one
#api.depends('invoice_line.price_subtotal', 'tax_line.amount')
def extras(self):
self.exe = self.amount_untaxed + self.amount_tax if self.amount_tax and self.amount_tax > 0 else 0
self.impo = self.amount_untaxed + self.amount_tax if not self.amount_tax or self.amount_tax == 0 else 0

Calculate the future value for only one category using the IRR (Python)

import xlrd
import numpy
fileWorkspace = 'C://Users/jod/Desktop/'
wb1 = xlrd.open_workbook(fileWorkspace + 'assign2.xls')
sh1 = wb1.sheet_by_index(0)
time,amount,category = [],[],[]
for a in range(2,sh1.nrows):
time.append(int(sh1.cell(a,0).value)) # Pulling time from excel (column A)
amount.append(float(sh1.cell(a,1).value)) # Pulling amount from excel (column B)
category.append(str(sh1.cell(a,2).value)) # Pulling category from excel (column C)
#print(time)
#print(amount)
#print(category)
print('\n')
p_p2 = str(sh1.cell(0,1))
p_p1 = p_p2.replace("text:'","")
pp = p_p1.replace("'","")
print(pp) # Printing the type of pay period (Row 1, col B)
c_p2 = str(sh1.cell(1,1))
c_p1 = c_p2.replace("text:'","")
cp = c_p1.replace("'","")
print(cp) # Printing the type of compound period (Row 2, col B)
netflow = 0
outflow = 0
inflow = 0
flow = 0
cat = ["Sales", "Salvage", "Subsidy", "Redeemable", "Utility", "Labor",
"Testing", "Marketing", "Materials", "Logistics"]
if pp == "Years" and cp == "Years": # if pay period and compound period are both in years
IRR = numpy.irr(amount) * 100 # Calculates the internal rate of return (IRR)
print ("IRR:", round(IRR, 2), '%', '\n') # prints (IRR)
for i in time: # for every value in time array
if cat[5] in category: # if "Labor" for cat array is in category array or not
# calculates the present values using all the amount values (col B) instead of
# just using the ones that has "Labor" category label beside them
# Need to make every other value 0, such as beside "Redeemable" and "Salvage"
flow = amount[i] / numpy.power((1 + (IRR/100)), time[i])
if flow>0:
inflow = inflow + flow
if flow<0:
outflow = outflow + flow
print ('Present Value (P) is:', round(flow,0), '\n')
netflow = outflow + inflow
print("In year 0 or current year")
print("-------")
print ('Outflow is: ', round(outflow,0))
print ('Inflow is: ', round(inflow,0))
print ('Netflow is: ', round(netflow,0), '\n')
outflow2 = (round(outflow,0))*(1+(IRR/100))**(9)
inflow2 = (round(inflow,0))*(1+(IRR/100))**(9)
netflow2 = outflow2 + inflow2
print("In year 9")
print("-------")
print ('Outflow is: ', round(outflow2,0))
print ('Inflow is: ', round(inflow2,0))
print ('Netflow is: ', round(netflow2,0), '\n')
I have commented important lines of code for clarification.
Here is the original question:
illustrate the breakdown of major project revenues and expenses by category as a percentage of that project’s future value in year 9. The illustration must also clearly indicate the total future value of the project in year 9 as well as the IRR.
There will be a total of 10 revenue and cost categories that a project may be composed of. The categories are: Sales, salvage, subsidy, redeemable, utility, labor, testing, marketing, materials and logistics. All revenues and expenses will fall in one of these ten categories. The project pay period and compound period will be identified at the top of the Excel sheet. Pay period and compound period may be designated as any of the following: years, quarters, months.
I am getting confused because I am not able to pull the only values from beside the "Labor", "Redeemable", or "Salvage". I just don't know where I am making a mistake, or there is something that is incomplete. Below is the excel file image:
Excel File Image 2
Excel File Image 3
After revising, all cashflows are discounted at the irr. What is done is the following:
i) determineAdjustments takes the pay period (column A) and adjusts if for the year ended (if it is a monthly amount it puts it in the proper year ended) and if its monthly puts in in the month ended (no adjustment necessary). This will divide the pay period by 12 if yearly cash flows are needed (yearly compounding)
ii) IRR is calculated, and the compounding period is used to adjust the monthly IRR for monthly pay periods
iii) all expenses are discounted at the IRR and input into a list for cat_contributions['category_name'] = [discounted period 1, discounted period 2 ... ]
iv) Then the net inflows and outflows are sums of these.
I can't type up data in the spreadsheets from the images as that would take a while, but maybe tinker with this and see if you can get it to work.
from __future__ import division
import xlrd
import numpy
import os
import math
def main(xls = 'xls_name.xlsx', sh = 0):
#save script in same folder as the xls file
os.chdir( os.getcwd() )
wb = xlrd.open_workbook(xls)
sh = wb.sheet_by_index(0)
pay_period = sh.cell_value(0,1)
compounding_period = sh.cell_value(1,1)
compounding_factor, pay_factor = determineAdjustments(
pay_period, compounding_period)
number_of_periods = max( sh.col_values(0, start_rowx = 2) )
flow_per_period = [ 0*i for i in range( int( math.ceil( number_of_periods/pay_factor ) ) + 1 ) ]#list of length number of pay_periods
for r in range(2,sh.nrows):
pay_period = int( math.ceil( sh.cell_value(r,0) / pay_factor ) )
flow_per_period[pay_period] += sh.cell_value(r,1) #unadjusted cash flows
irr = calculateIRR(flow_per_period, compounding_factor)
cat_contributions = sortExpenditures(sh, irr, pay_factor)
total_cat_contributions, netflow, total_outflow, total_inflow = calculateFlows(cat_contributions)
printStats(cat_contributions, irr, compounding_factor, pay_factor,
total_cat_contributions, netflow, total_outflow, total_inflow)
return
def determineAdjustments(pay_period, compounding_period):
if compounding_period == 'years':
compounding_factor = 1
if pay_period == 'months':
pay_factor = 12
if pay_period == 'years':
pay_factor = 1
#assume no days pay periods
if compounding_period == 'months':
compounding_factor = 12
#assume no yearly payouts and that the
#all payments are in months
pay_factor = 1
return compounding_factor, pay_factor
def calculateIRR(cashflow, compounding_factor):
irr = numpy.irr(cashflow)
irr_comp = (1 + irr)**compounding_factor - 1
#seems like in first example it uses rounded irr, can do something like:
#irr_comp = round(irr_comp,4)
return irr_comp
def sortExpenditures(sh, irr, pay_factor):
#percentages and discounting occurs at the IRR caculated in the main
#function
cat = ["Sales", "Salvage", "Subsidy", "Redeemable", "Utility", "Labor",
"Testing", "Marketing", "Materials", "Logistics"]
#python dictionary to sort contributions into categories
cat_contributions = {}
for c in cat:
cat_contributions[c] = []
# create list of contributions of each list item to FV in a dictionary
for r in range(2,sh.nrows):
try:
#discounted cash flow of each expenditure
#using formula FV = expenditure/(1+i)^n
cat_contributions[sh.cell_value(r,2)].append(
sh.cell_value(r,1) / ( (1 + irr) ** (sh.cell_value(r,0)/pay_factor) )
)
except KeyError:
print "No category for type: " + sh.cell_value(r,2) +'\n'
return cat_contributions
def calculateFlows(cat_contributions):
total_outflow = 0
total_inflow = 0
total_cat_contributions = {}
for cat in cat_contributions:
total_cat_contributions[cat] = sum( cat_contributions[cat] )
if total_cat_contributions[cat] < 0:
total_outflow += total_cat_contributions[cat]
else:
total_inflow += total_cat_contributions[cat]
netflow = total_inflow + total_outflow
return total_cat_contributions, netflow, total_outflow, total_inflow
def printStats(cat_contributions, irr, compounding_factor, pay_period,
total_cat_contributions, netflow, total_outflow, total_inflow):
print "IRR: "+str(irr*100) +' %'
if compounding_factor == 1: print "Compounding: Yearly"
if compounding_factor == 12: print "Compounding: Monthly"
if pay_period == 1: "Cashflows: Year Ended"
if pay_period == 12: "Cashflows: Month Ended"
print "Future Value (Net Adjusted Cashflow): " +str(netflow)
print "Adjusted Inflows: " + str(total_inflow)
print "Adjusted Outflows: " + str(total_outflow) +'\n'
for cat in total_cat_contributions:
if total_cat_contributions[cat] != 0:
print '-----------------------------------------------------'
print cat + '\n'
print "Total Contribution to FV " + str( total_cat_contributions[cat] )
if total_cat_contributions[cat] < 0:
print "Contribution to Expenses: " + str ( abs(100 * total_cat_contributions[cat]/total_outflow) )
else:
print "Contribution to Revenues: " + str ( abs(100 * total_cat_contributions[cat]/total_inflow) ) +'\n'
main(xls='Book1.xlsx')

Categories