added actif_edit template

This commit is contained in:
2023-01-30 15:53:59 +01:00
parent f8023701a4
commit e60212239e
7 changed files with 364 additions and 8 deletions

Binary file not shown.

View File

@@ -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()])

View File

@@ -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

View File

@@ -0,0 +1,88 @@
{% extends "cao_blogr:templates/layout.jinja2" %}
{% block content %}
<form action="{{ url }}" method="post" class="form">
<div class="form-group">
<label class="required-field">{{ form.classe.label }}</label>
{{ form.classe(class_='form-control') }}
</div>
{% for error in form.symbole.errors %}
<div class="error">{{ error }}</div>
{% endfor %}
<div class="form-group">
<label class="required-field" for="symbole">{{form.symbole.label}}</label>
{{form.symbole(class_='form-control')}}
</div>
{% for error in form.libelle.errors %}
<div class="error">{{ error }}</div>
{% endfor %}
<div class="form-group">
<label class="required-field" for="libelle">{{form.libelle.label}}</label>
{{form.libelle(class_='form-control')}}
</div>
<div class="form-group">
<label class="required-field" for="pru">{{form.pru.label}}</label>
<div class="input-group">
<div class="input-group-addon">{{ item.devise }}</div>
{{form.pru(class_='form-control')}}
</div>
</div>
<div class="form-group">
<label class="required-field" for="cours">{{form.cours.label}}</label>
<div class="input-group">
<div class="input-group-addon">{{ item.devise }}</div>
{{form.cours(class_='form-control')}}
</div>
</div>
<div class="form-group">
<p class="form-control-static"><b>Dernière modif. :</b> {{ item.modif_le }}</p>
</div>
<br>
<div class="form-group">
<a class="btn btn-default" href="{{ request.route_url('portfolio') }}">
<span class="glyphicon glyphicon-chevron-left"></span> Retour</a>
<button class="btn btn-primary" type="submit" name="form.submitted">
<span class="glyphicon glyphicon-ok"></span> Enregistrer</button>
{% if form.no_id.data %}
<button class="btn btn-danger" type="button" data-toggle="modal" data-target="#confirmDelete">
<span class="glyphicon glyphicon-remove"></span> Supprimer</button>
{% endif %}
</div>
</form>
<!-- Modal : Confirmation SUPRESSION -->
<div id="confirmDelete" class="modal" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</button>
<h4 class="modal-title">Supprimer cette allocation</h4>
</div>
<div class="modal-body">
<!-- The form is placed inside the body of modal -->
<p>Etes-vous certain(e) de vouloir supprimer la ligne <b>{{ form.symbole.data }}</b> ?</p>
</div>
<div class="modal-footer">
<div class="form-group">
<div class="text-center">
<form id="confirmForm" method="post">
<button type="submit" class="btn btn-danger" name="form.deleted">Supprimer</button>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,99 @@
{% extends "cao_blogr:templates/layout.jinja2" %}
{% block content %}
<form action="{{ url }}" method="post" class="form">
<div class="form-group">
<label class="required-field">{{ form.classe.label }}</label>
{{ form.classe(class_='form-control') }}
</div>
{% for error in form.symbole.errors %}
<div class="error">{{ error }}</div>
{% endfor %}
<div class="form-group">
<label class="required-field" for="symbole">{{form.symbole.label}}</label>
{{form.symbole(class_='form-control')}}
<p class="form-control-static">{{ item.libelle }}</p>
</div>
<div class="form-group">
<label class="required-field" for="nombre">{{form.nombre.label}}</label>
{{form.nombre(class_='form-control')}}
</div>
<div class="form-group">
<label class="control-label required-field" for="pru">{{form.pru.label}}</label>
<div class="input-group">
<div class="input-group-addon">{{ item.devise }}</div>
{{form.pru(class_='form-control')}}
</div>
</div>
<div class="form-group">
<label class="required-field" for="ter">{{form.ter.label}}</label>
<div class="input-group">
<div class="input-group-addon">%</div>
{{form.ter(class_='form-control')}}
</div>
</div>
<div class="form-group">
<label class="required-field" for="pc_rdt">{{form.pc_rdt.label}}</label>
<div class="input-group">
<div class="input-group-addon">%</div>
{{form.pc_rdt(class_='form-control')}}
</div>
</div>
<div class="form-group">
<label class="required-field" for="website">{{form.website.label}}</label>
{{form.website(class_='form-control')}}
</div>
<div class="form-group">
<p class="form-control-static"><b>Dernière modif. :</b> {{ item.modif_le }}</p>
</div>
<br>
<div class="form-group">
<a class="btn btn-default" href="{{ request.route_url('portfolio') }}">
<span class="glyphicon glyphicon-chevron-left"></span> Retour</a>
<button class="btn btn-primary" type="submit" name="form.submitted">
<span class="glyphicon glyphicon-ok"></span> Enregistrer</button>
{% if form.no_id.data %}
<button class="btn btn-danger" type="button" data-toggle="modal" data-target="#confirmDelete">
<span class="glyphicon glyphicon-remove"></span> Supprimer</button>
{% endif %}
</div>
</form>
<!-- Modal : Confirmation SUPRESSION -->
<div id="confirmDelete" class="modal" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</button>
<h4 class="modal-title">Supprimer cette allocation</h4>
</div>
<div class="modal-body">
<!-- The form is placed inside the body of modal -->
<p>Etes-vous certain(e) de vouloir supprimer la ligne <b>{{ form.symbole.data }}</b> ?</p>
</div>
<div class="modal-footer">
<div class="form-group">
<div class="text-center">
<form id="confirmForm" method="post">
<button type="submit" class="btn btn-danger" name="form.deleted">Supprimer</button>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -120,9 +120,9 @@
<tr>
<td class="{{ ligne.Allocation.bg_color }}">{{ ligne.Allocation.classe }}</td>
{% if ligne.Allocation.type=='ACTION' %}
<td><a href="actif_edit/{{ ligne.no_id }}">{{ ligne.Actifs.libelle }}</a></td>
<td><a href="actif_edit/{{ ligne.Actifs.no_id }}">{{ ligne.Actifs.libelle }}</a></td>
{% else %}
<td><a href="actif2_edit/{{ ligne.no_id }}">{{ ligne.Actifs.libelle }}</a></td>
<td><a href="actif2_edit/{{ ligne.Actifs.no_id }}">{{ ligne.Actifs.libelle }}</a></td>
{% endif %}
{% if ligne.Actifs.devise=='EUR' %}
<td class="text-right">{{ '{0:0.2f} €'.format(ligne.Actifs.cours) }}</td>

View File

@@ -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,
}