diff --git a/cao_blogr.sqlite b/cao_blogr.sqlite
index 228736f..123e574 100644
Binary files a/cao_blogr.sqlite and b/cao_blogr.sqlite differ
diff --git a/cao_blogr/forms.py b/cao_blogr/forms.py
index 6fcf091..439ce6b 100644
--- a/cao_blogr/forms.py
+++ b/cao_blogr/forms.py
@@ -35,9 +35,28 @@ class HistoForm(Form):
class AllocationForm(Form):
no_cat = IntegerField(widget=HiddenInput())
- classe = StringField('Classe', validators=[InputRequired(), Length(min=1, max=25)], filters=[strip_filter])
+ classe = StringField("Classe d'actif", validators=[InputRequired(), Length(min=1, max=25)], filters=[strip_filter])
pc_cible = IntegerField(validators=[InputRequired()])
type = SelectField('Type', choices=[('ACTION','ACTION'),('AUTRE','AUTRE')])
ordre = IntegerField(validators=[InputRequired()])
bg_color = SelectField('Couleur de fond', choices=[('info','BLEU'),('danger','ROUGE'),('warning','ORANGE'),('success','VERT')])
+class ActifForm(Form):
+ no_id = IntegerField(widget=HiddenInput())
+ classe = SelectField('Classe')
+ symbole = StringField('Symbole', validators=[InputRequired(), Length(min=1, max=15)], filters=[strip_filter])
+ nombre = IntegerField(validators=[InputRequired()])
+ pru = DecimalField('PRU', places=3, validators=[InputRequired()])
+ ter = DecimalField('TER', places=2, validators=[InputRequired()])
+ pc_rdt = DecimalField('% rendement', places=2, validators=[InputRequired()])
+ website = StringField('Web site', validators=[Length(min=1, max=100)], filters=[strip_filter])
+
+class Actif2Form(Form):
+ no_id = IntegerField(widget=HiddenInput())
+ classe = SelectField('Classe')
+ symbole = StringField('Symbole', validators=[InputRequired(), Length(min=1, max=15)], filters=[strip_filter])
+ libelle = StringField('Libellé', validators=[InputRequired(), Length(min=1, max=45)], filters=[strip_filter])
+ pru = DecimalField('Total investi', places=3, validators=[InputRequired()])
+ cours = DecimalField('Total valeur', places=3, validators=[InputRequired()])
+ pc_rdt = DecimalField('% rendement', places=2, validators=[InputRequired()])
+
diff --git a/cao_blogr/services/portfolio.py b/cao_blogr/services/portfolio.py
index 5ead230..7c74554 100644
--- a/cao_blogr/services/portfolio.py
+++ b/cao_blogr/services/portfolio.py
@@ -24,12 +24,20 @@ class PFService(object):
query = request.dbsession.query(Allocation).filter(Allocation.no_cat == no_cat).first()
return query
+ @classmethod
+ def get_allocation_byType(cls, request, type):
+ # lire une allocation par le no_id
+ query = request.dbsession.query(Allocation).filter(Allocation.type == type).all()
+ return query
+
@classmethod
def get_histo(cls, request, no_id):
if no_id == '0':
+ items = request.dbsession.query(Histo).order_by(sa.asc(Histo.date)).all()
+ elif no_id == '-1':
items = request.dbsession.query(Histo).order_by(sa.desc(Histo.date)).all()
else:
- # lire le histo par le no_id
+ # lire le histo par le no_id
items = request.dbsession.query(Histo).filter(Histo.no_id == no_id).first()
return items
@@ -74,7 +82,6 @@ class PFService(object):
def delete_actif(request, no_id):
request.dbsession.query(Actifs).filter(Actifs.no_id == no_id).delete(synchronize_session=False)
- request.dbsession.commit()
return
diff --git a/cao_blogr/templates/portfolio/actif2_edit.jinja2 b/cao_blogr/templates/portfolio/actif2_edit.jinja2
new file mode 100644
index 0000000..fc11149
--- /dev/null
+++ b/cao_blogr/templates/portfolio/actif2_edit.jinja2
@@ -0,0 +1,88 @@
+{% extends "cao_blogr:templates/layout.jinja2" %}
+
+{% block content %}
+
+
+
+
+
+
+
+
+
+
+
Etes-vous certain(e) de vouloir supprimer la ligne {{ form.symbole.data }} ?
+
+
+
+
+
+
+{% endblock %}
diff --git a/cao_blogr/templates/portfolio/actif_edit.jinja2 b/cao_blogr/templates/portfolio/actif_edit.jinja2
new file mode 100644
index 0000000..4ac94e3
--- /dev/null
+++ b/cao_blogr/templates/portfolio/actif_edit.jinja2
@@ -0,0 +1,99 @@
+{% extends "cao_blogr:templates/layout.jinja2" %}
+
+{% block content %}
+
+
+
+
+
+
+
+
+
+
+
Etes-vous certain(e) de vouloir supprimer la ligne {{ form.symbole.data }} ?
+
+
+
+
+
+
+{% endblock %}
diff --git a/cao_blogr/templates/portfolio/portfolio.jinja2 b/cao_blogr/templates/portfolio/portfolio.jinja2
index c90d880..bbc94e0 100644
--- a/cao_blogr/templates/portfolio/portfolio.jinja2
+++ b/cao_blogr/templates/portfolio/portfolio.jinja2
@@ -120,9 +120,9 @@
| {{ ligne.Allocation.classe }} |
{% if ligne.Allocation.type=='ACTION' %}
- {{ ligne.Actifs.libelle }} |
+ {{ ligne.Actifs.libelle }} |
{% else %}
- {{ ligne.Actifs.libelle }} |
+ {{ ligne.Actifs.libelle }} |
{% endif %}
{% if ligne.Actifs.devise=='EUR' %}
{{ '{0:0.2f} €'.format(ligne.Actifs.cours) }} |
diff --git a/cao_blogr/views/portfolio.py b/cao_blogr/views/portfolio.py
index d4b7cd0..86ae3f7 100644
--- a/cao_blogr/views/portfolio.py
+++ b/cao_blogr/views/portfolio.py
@@ -3,12 +3,13 @@ from pyramid.view import (
)
from pyramid.httpexceptions import HTTPFound
from ..services.portfolio import PFService
-from ..forms import AllocationForm, HistoForm
-from ..models.portfolio import Histo, Allocation
+from ..forms import ActifForm, Actif2Form, AllocationForm, HistoForm
+from ..models.portfolio import Actifs, Histo, Allocation
import datetime #<- will be used to set default dates on models
import yfinance as yf
import json
+import html
@view_config(route_name='portfolio', renderer='../templates/portfolio/portfolio.jinja2', permission='view')
def portfolio(request):
@@ -113,7 +114,7 @@ def portfolio(request):
@view_config(route_name='histo_list', renderer='../templates/portfolio/histo_list.jinja2', permission='view')
def histo_list(request):
# lire l historique
- items = PFService.get_histo(request, '0')
+ items = PFService.get_histo(request, '-1')
return {
'page_title': 'Historique des parts',
@@ -213,3 +214,145 @@ def histo_edit(request):
'form': form,
'item': entry,
}
+
+@view_config(route_name='actif_edit', renderer='../templates/portfolio/actif_edit.jinja2', permission='view')
+def actif_edit(request):
+ no_id = request.matchdict['no_id']
+ url = request.route_url('actif_edit', no_id=no_id)
+
+ # get the list of classes
+ classes = PFService.get_allocation_byType(request, 'ACTION')
+
+ if no_id == '0':
+ # create a new allocation
+ entry = Actifs()
+ entry.pru = 0
+ entry.cours = 0
+ entry.pc_rdt = 0
+ entry.ter = 0
+ form = ActifForm(request.POST, entry)
+ form.classe.choices = [(row.classe, row.classe) for row in classes]
+ page_title = "Nouvel actif"
+
+ else:
+ # modify post
+ entry = PFService.get_actifs(request, no_id)
+ if not entry:
+ request.session.flash(u"Actif non trouvé : %s" % no_id, 'warning')
+ return HTTPFound(location=request.route_url('portfolio'))
+ form = ActifForm(request.POST, entry)
+ form.classe.choices = [(row.classe, row.classe) for row in classes]
+ page_title = "Modifier Actif : " + entry.symbole
+
+ if 'form.submitted' in request.params :
+ if no_id == '0':
+ form.populate_obj(entry)
+ # récupérer le cours du symbole de Yahoo finance
+ ticker = yf.Ticker(entry.symbole)
+ entry.cours = ticker.fast_info.last_price
+ entry.devise = ticker.info.get('currency')
+ entry.libelle = html.unescape(ticker.info.get('shortName'))
+ # raccourcir le libelle
+ entry.libelle = entry.libelle.replace('UCITS ','')
+ entry.libelle = entry.libelle.replace('World U','World')
+ entry.pc_allocation = 1.0
+ entry.valeur = float(entry.cours) * entry.parite * entry.nombre;
+ entry.plus_value = entry.valeur - float(entry.pru * entry.nombre);
+ entry.pc_plusvalue = entry.plus_value / entry.valeur * 100;
+ entry.rendement = entry.valeur * float(entry.pc_rdt) / 100;
+ request.dbsession.add(entry)
+ return HTTPFound(location=request.route_url('portfolio'))
+ else:
+ del form.no_id # SECURITY: prevent overwriting of primary key
+ form.populate_obj(entry)
+ # récupérer le cours du symbole de Yahoo finance
+ ticker = yf.Ticker(entry.symbole)
+ entry.cours = ticker.fast_info.last_price
+ entry.devise = ticker.info.get('currency')
+ entry.libelle = html.unescape(ticker.info.get('shortName'))
+ # raccourcir le libelle
+ entry.libelle = entry.libelle.replace('UCITS ','')
+ entry.libelle = entry.libelle.replace('World U','World')
+ entry.valeur = float(entry.cours) * entry.parite * entry.nombre;
+ entry.plus_value = entry.valeur - float(entry.pru * entry.nombre);
+ entry.pc_plusvalue = entry.plus_value / entry.valeur * 100;
+ entry.rendement = entry.valeur * float(entry.pc_rdt) / 100;
+
+ return HTTPFound(location=request.route_url('portfolio'))
+
+ if 'form.deleted' in request.params:
+ PFService.delete_actif(request, entry.no_id)
+ request.session.flash("La fiche a été supprimée avec succès.", 'success')
+ return HTTPFound(location=request.route_url('portfolio'))
+
+ return {
+ 'page_title': page_title,
+ 'url': url,
+ 'form': form,
+ 'item': entry,
+ }
+
+@view_config(route_name='actif2_edit', renderer='../templates/portfolio/actif2_edit.jinja2', permission='view')
+def actif2_edit(request):
+ no_id = request.matchdict['no_id']
+ url = request.route_url('actif2_edit', no_id=no_id)
+
+ # get the list of classes
+ classes = PFService.get_allocation_byType(request, 'AUTRE')
+
+ if no_id == '0':
+ # create a new allocation
+ entry = Actifs()
+ entry.pru = 0
+ entry.cours = 0
+ entry.pc_rdt = 0
+ entry.ter = 0
+ entry.devise = 'EUR'
+ form = Actif2Form(request.POST, entry)
+ form.classe.choices = [(row.classe, row.classe) for row in classes]
+ page_title = "Nouvel actif"
+
+ else:
+ # modify post
+ entry = PFService.get_actifs(request, no_id)
+ if not entry:
+ request.session.flash(u"Actif non trouvé : %s" % no_id, 'warning')
+ return HTTPFound(location=request.route_url('portfolio'))
+ form = Actif2Form(request.POST, entry)
+ form.classe.choices = [(row.classe, row.classe) for row in classes]
+ page_title = "Modifier Actif : " + entry.symbole
+
+ if 'form.submitted' in request.params :
+ if no_id == '0':
+ form.populate_obj(entry)
+ entry.nombre = 1000
+ entry.devise = 'EUR'
+ entry.parite = 1.0
+ entry.pc_allocation = 1.0
+ entry.valeur = float(entry.cours) * entry.parite * entry.nombre;
+ entry.plus_value = entry.valeur - float(entry.pru * entry.nombre);
+ entry.pc_plusvalue = entry.plus_value / entry.valeur * 100;
+ entry.rendement = entry.valeur * float(entry.pc_rdt) / 100;
+ request.dbsession.add(entry)
+ return HTTPFound(location=request.route_url('portfolio'))
+ else:
+ del form.no_id # SECURITY: prevent overwriting of primary key
+ form.populate_obj(entry)
+ entry.valeur = float(entry.cours) * entry.parite * entry.nombre;
+ entry.plus_value = entry.valeur - float(entry.pru * entry.nombre);
+ entry.pc_plusvalue = entry.plus_value / entry.valeur * 100;
+ entry.rendement = entry.valeur * float(entry.pc_rdt) / 100;
+
+ return HTTPFound(location=request.route_url('portfolio'))
+
+ if 'form.deleted' in request.params:
+ PFService.delete_actif(request, entry.no_id)
+ request.session.flash("La fiche a été supprimée avec succès.", 'success')
+ return HTTPFound(location=request.route_url('portfolio'))
+
+ return {
+ 'page_title': page_title,
+ 'url': url,
+ 'form': form,
+ 'item': entry,
+ }