From e60212239ec9b5c15941550055c06b3eed1dcce0 Mon Sep 17 00:00:00 2001 From: Phuoc CAO Date: Mon, 30 Jan 2023 15:53:59 +0100 Subject: [PATCH] added actif_edit template --- cao_blogr.sqlite | Bin 532480 -> 532480 bytes cao_blogr/forms.py | 21 ++- cao_blogr/services/portfolio.py | 11 +- .../templates/portfolio/actif2_edit.jinja2 | 88 +++++++++++ .../templates/portfolio/actif_edit.jinja2 | 99 ++++++++++++ .../templates/portfolio/portfolio.jinja2 | 4 +- cao_blogr/views/portfolio.py | 149 +++++++++++++++++- 7 files changed, 364 insertions(+), 8 deletions(-) create mode 100644 cao_blogr/templates/portfolio/actif2_edit.jinja2 create mode 100644 cao_blogr/templates/portfolio/actif_edit.jinja2 diff --git a/cao_blogr.sqlite b/cao_blogr.sqlite index 228736f11bc0feec6f4470920e74a36998d68f1a..123e57487b6e01d52037d304c32c71bd0930d485 100644 GIT binary patch delta 415 zcmZoTpwMtYVS+T{wuv&%jN3LQyz>(>Hc&9Iv@$ifGBVXOGc_|aFxi~ozwm-Gdk6zB zTQ>v$6u!TFHN0=wBYC;ly1DDPc5vR|l;uca-@-d zjLOqr=rAg=@;EcF%1)oC%NVnLt2$#J3k&Z=hU44abr`)Eqk%?6%W8@;u?HxMFo?=4 zGchtUa{vJ+2ZJytgJTF3DmW(=XZR=OWTq#AUCI2SqJr_t0oTwVBLgF2T?0d1V5k`y zSeY1F85-&tm{?jG8Ek)}%V=h_T_J(dgrAKQWRl|an0&@#C?f4K^^8Ew1jNih%mT!$ NK+Lv1rk;Jt0suR)cO(D+ delta 155 zcmZoTpwMtYVS+T{@`*CejLSDByz>(>vQ#jzv@$WZGB(gNF*G(XGuxctzwm-00|Nsu z`xXZNDSUtVYIxu9PUPj{aptb$+QE5;QO}h>L7@*J1Qx yoF1vp7_ahcH?I 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 %} + +
+ +
+ + {{ form.classe(class_='form-control') }} +
+ + {% for error in form.symbole.errors %} +
{{ error }}
+ {% endfor %} +
+ + {{form.symbole(class_='form-control')}} +
+ + {% for error in form.libelle.errors %} +
{{ error }}
+ {% endfor %} +
+ + {{form.libelle(class_='form-control')}} +
+ +
+ +
+
{{ item.devise }}
+ {{form.pru(class_='form-control')}} +
+
+ +
+ +
+
{{ item.devise }}
+ {{form.cours(class_='form-control')}} +
+
+ +
+

Dernière modif. : {{ item.modif_le }}

+
+ +
+
+ + Retour + + {% if form.no_id.data %} + + {% endif %} +
+ + +
+ + + + +{% 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 %} + +
+ +
+ + {{ form.classe(class_='form-control') }} +
+ + {% for error in form.symbole.errors %} +
{{ error }}
+ {% endfor %} +
+ + {{form.symbole(class_='form-control')}} +

{{ item.libelle }}

+
+ +
+ + {{form.nombre(class_='form-control')}} +
+ +
+ +
+
{{ item.devise }}
+ {{form.pru(class_='form-control')}} +
+
+ +
+ +
+
%
+ {{form.ter(class_='form-control')}} +
+
+ +
+ +
+
%
+ {{form.pc_rdt(class_='form-control')}} +
+
+ +
+ + {{form.website(class_='form-control')}} +
+ +
+

Dernière modif. : {{ item.modif_le }}

+
+ +
+
+ + Retour + + {% if form.no_id.data %} + + {% endif %} +
+ + +
+ + + + +{% 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, + }