fusion devis et facture dans un chantier
This commit is contained in:
@@ -2,57 +2,55 @@ Metadata-Version: 2.1
|
|||||||
Name: mondumas
|
Name: mondumas
|
||||||
Version: 1.0
|
Version: 1.0
|
||||||
Summary: mondumas
|
Summary: mondumas
|
||||||
Home-page: UNKNOWN
|
Home-page:
|
||||||
Author:
|
Author:
|
||||||
Author-email:
|
Author-email:
|
||||||
License: UNKNOWN
|
|
||||||
Description: # README #
|
|
||||||
|
|
||||||
Cette application web permet aux collaborateurs de l'entreprise Dumas :
|
|
||||||
|
|
||||||
- de consulter et de gérer les dossiers des clients depuis leur tablette
|
|
||||||
- de créer un rapport de fuite et le faire signer par le client
|
|
||||||
|
|
||||||
Elle est développée avec les composants open source suivants :
|
|
||||||
|
|
||||||
## Backend
|
|
||||||
|
|
||||||
- [Python](https://www.python.org/downloads/) 3.7
|
|
||||||
- [Pyramid web framework](https://trypyramid.com/) 1.10
|
|
||||||
- [MySQL server](https://mysql.com/) 5.7 sur Windows Server 2008 R2 Standard
|
|
||||||
- [Apache web server](https://apache.org/) 2.4 sur Debian GNU/Linux 9 (stretch)
|
|
||||||
|
|
||||||
## Frontend
|
|
||||||
|
|
||||||
- [Bootstrap framework](https://getbootstrap.com/) for CSS 3.3.7
|
|
||||||
- [Jquery](https://jquery.com/download/) for JavaScript 3.2.1
|
|
||||||
- Chameleon templates
|
|
||||||
- [FormValidation](https://formvalidation.io/) form validator 0.7.0
|
|
||||||
|
|
||||||
## Jquery Plugins
|
|
||||||
|
|
||||||
- [DataTables](https://datatables.net/) 1.10.20
|
|
||||||
- [Fullcalendar](https://fullcalendar.io/) 3.9.0
|
|
||||||
- [fullcalendar Scheduler](https://fullcalendar.io/) 1.9.4
|
|
||||||
- [Jquery-ui et jquery-ui-themes](https://jqueryui.com/) 1.12.1
|
|
||||||
- [jSignature](https://willowsystems.github.io/jSignature)
|
|
||||||
- [less.js](http://lesscss.org/) 3.11.1
|
|
||||||
- [moment.js](https://momentjs.com/) with-locales.min.js
|
|
||||||
|
|
||||||
|
|
||||||
[Learn Markdown](https://bitbucket.org/tutorials/markdowndemo)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
0.0
|
|
||||||
---
|
|
||||||
|
|
||||||
- Initial version
|
|
||||||
|
|
||||||
Keywords: web wsgi bfg pylons pyramid
|
Keywords: web wsgi bfg pylons pyramid
|
||||||
Platform: UNKNOWN
|
|
||||||
Classifier: Programming Language :: Python
|
Classifier: Programming Language :: Python
|
||||||
Classifier: Framework :: Pyramid
|
Classifier: Framework :: Pyramid
|
||||||
Classifier: Topic :: Internet :: WWW/HTTP
|
Classifier: Topic :: Internet :: WWW/HTTP
|
||||||
Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
|
Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
|
||||||
Provides-Extra: testing
|
Provides-Extra: testing
|
||||||
|
|
||||||
|
# README #
|
||||||
|
|
||||||
|
Cette application web permet aux collaborateurs de l'entreprise Dumas :
|
||||||
|
|
||||||
|
- de consulter et de gérer les dossiers des clients depuis leur tablette
|
||||||
|
- de créer un rapport de fuite et le faire signer par le client
|
||||||
|
|
||||||
|
Elle est développée avec les composants open source suivants :
|
||||||
|
|
||||||
|
## Backend
|
||||||
|
|
||||||
|
- [Python](https://www.python.org/downloads/) 3.7
|
||||||
|
- [Pyramid web framework](https://trypyramid.com/) 1.10
|
||||||
|
- [MySQL server](https://mysql.com/) 5.7 sur Windows Server 2008 R2 Standard
|
||||||
|
- [Apache web server](https://apache.org/) 2.4 sur Debian GNU/Linux 9 (stretch)
|
||||||
|
|
||||||
|
## Frontend
|
||||||
|
|
||||||
|
- [Bootstrap framework](https://getbootstrap.com/) for CSS 3.3.7
|
||||||
|
- [Jquery](https://jquery.com/download/) for JavaScript 3.2.1
|
||||||
|
- Chameleon templates
|
||||||
|
- [FormValidation](https://formvalidation.io/) form validator 0.7.0
|
||||||
|
|
||||||
|
## Jquery Plugins
|
||||||
|
|
||||||
|
- [DataTables](https://datatables.net/) 1.10.20
|
||||||
|
- [Fullcalendar](https://fullcalendar.io/) 3.9.0
|
||||||
|
- [fullcalendar Scheduler](https://fullcalendar.io/) 1.9.4
|
||||||
|
- [Jquery-ui et jquery-ui-themes](https://jqueryui.com/) 1.12.1
|
||||||
|
- [jSignature](https://willowsystems.github.io/jSignature)
|
||||||
|
- [less.js](http://lesscss.org/) 3.11.1
|
||||||
|
- [moment.js](https://momentjs.com/) with-locales.min.js
|
||||||
|
|
||||||
|
|
||||||
|
[Learn Markdown](https://bitbucket.org/tutorials/markdowndemo)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
0.0
|
||||||
|
---
|
||||||
|
|
||||||
|
- Initial version
|
||||||
|
|||||||
@@ -1,4 +1,2 @@
|
|||||||
[paste.app_factory]
|
[paste.app_factory]
|
||||||
main = mondumas:main
|
main = mondumas:main
|
||||||
[console_scripts]
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ SQLAlchemy==1.2.19
|
|||||||
transaction
|
transaction
|
||||||
zope.sqlalchemy==1.1
|
zope.sqlalchemy==1.1
|
||||||
waitress
|
waitress
|
||||||
mysqlclient==2.1
|
mysqlclient==1.4
|
||||||
docutils
|
docutils
|
||||||
pdfkit
|
pdfkit
|
||||||
python-dateutil
|
python-dateutil
|
||||||
|
|||||||
@@ -25,19 +25,23 @@ def execute_query(request, query, params):
|
|||||||
mark_changed(request.dbsession)
|
mark_changed(request.dbsession)
|
||||||
transaction.commit()
|
transaction.commit()
|
||||||
|
|
||||||
def get_devis_byName(request, societe, name):
|
|
||||||
numero = to_int(name)
|
|
||||||
|
|
||||||
if numero > 0:
|
def get_dossiers_byName(request, societe, name):
|
||||||
query = """SELECT date,'DE' AS TYPE, LPAD(no_id,6,'0') AS numero, nomcli, CONCAT(c_nom,'; ',c_adr,'; ',c_ville) AS chantier, COALESCE(totalht,0) AS montant, status, nosin, nopol, nochantier, web
|
# lires tous les dossiers d'un chantier
|
||||||
FROM devis WHERE societe=:societe AND no_id >=:name AND web = 'W' LIMIT 300;;""" % (societe, name)
|
|
||||||
elif len(name) == 0:
|
query = """select * from (
|
||||||
query = """SELECT date,'DE' AS TYPE, LPAD(no_id,6,'0') AS numero, nomcli, CONCAT(c_nom,'; ',c_adr,'; ',c_ville) AS chantier, COALESCE(totalht,0) AS montant, status, nosin, nopol, nochantier, web
|
SELECT date,'DD' AS TYPE, LPAD(no_id,6,'0') AS numero, nomcli, CONCAT(c_nom,'; ',c_adr,'; ',c_ville) AS chantier, 0 AS montant, status, nosin, societe , no_id as nochantier
|
||||||
FROM devis WHERE societe=:societe AND web = 'W' ORDER BY no_id DESC LIMIT 300;"""
|
FROM dem_devis WHERE societe=:societe AND c_nom LIKE :name
|
||||||
else:
|
UNION
|
||||||
query = """(SELECT date,'DE' AS TYPE, LPAD(no_id,6,'0') AS numero, nomcli, CONCAT(c_nom,'; ',c_adr,'; ',c_ville) AS chantier, COALESCE(totalht,0) AS montant, status, nosin, nopol , nochantier, web
|
SELECT date,'DE' AS TYPE, LPAD(no_id,6,'0') AS numero, nomcli, CONCAT(c_nom,'; ',c_adr,'; ',c_ville) AS chantier, COALESCE(totalht,0) AS montant, status, nosin, societe , nochantier
|
||||||
FROM devis WHERE societe=:societe AND c_nom LIKE ':name%' AND web = 'W' LIMIT 500)"""
|
FROM devis WHERE societe=:societe AND c_nom LIKE :name
|
||||||
results = request.dbsession.execute(query, {'societe': societe, 'name': name}).fetchall()
|
UNION
|
||||||
|
SELECT date,'FA' AS TYPE, LPAD(no_id,6,'0') AS numero, nomcli, CONCAT(c_nom,'; ',c_adr,'; ',c_ville) AS chantier, COALESCE(totalht,0) AS montant, status, nosin, societe , nochantier
|
||||||
|
FROM facture WHERE societe=:societe AND c_nom LIKE :name
|
||||||
|
) a
|
||||||
|
order by date, TYPE
|
||||||
|
"""
|
||||||
|
results = request.dbsession.execute(query, {'societe': societe, 'name': name+'%'}).fetchall()
|
||||||
return results
|
return results
|
||||||
|
|
||||||
def get_devfac_by_no(request,nodossier):
|
def get_devfac_by_no(request,nodossier):
|
||||||
@@ -235,3 +239,14 @@ def update_devis_cloture(request, nodevis, status, logged_in):
|
|||||||
query = "UPDATE devis SET STATUS = :status, USERMAJ = :logged_in WHERE societe=:societe AND no_id=:nochantier;"
|
query = "UPDATE devis SET STATUS = :status, USERMAJ = :logged_in WHERE societe=:societe AND no_id=:nochantier;"
|
||||||
execute_query(request, query, {'societe': societe, 'nochantier': nochantier, 'status': status, 'logged_in': logged_in})
|
execute_query(request, query, {'societe': societe, 'nochantier': nochantier, 'status': status, 'logged_in': logged_in})
|
||||||
|
|
||||||
|
def update_devis_nochantier(request, societe, no_devis, nochantier):
|
||||||
|
# extraire type de doc et no de doc à mettre à jour
|
||||||
|
type = no_devis[0:2]
|
||||||
|
no_id = no_devis[3:]
|
||||||
|
if type == 'DE':
|
||||||
|
# maj le numero du dossier du devis
|
||||||
|
query = "UPDATE devis SET nochantier = :nochantier WHERE societe=:societe AND no_id=:no_id;"
|
||||||
|
else:
|
||||||
|
# maj le numero du dossier de la facture
|
||||||
|
query = "UPDATE facture SET nochantier = :nochantier WHERE societe=:societe AND no_id=:no_id;"
|
||||||
|
execute_query(request, query, {'societe': societe, 'nochantier': nochantier, 'no_id': no_id})
|
||||||
|
|||||||
@@ -22,8 +22,9 @@ def includeme(config):
|
|||||||
# devis
|
# devis
|
||||||
config.add_route('devis_ligne', '/devis_ligne/{type_ligne}/{nodevis}/{nolig}')
|
config.add_route('devis_ligne', '/devis_ligne/{type_ligne}/{nodevis}/{nolig}')
|
||||||
config.add_route('devis_lig_mv', '/devis_lig_mv/{move}/{nodevis}/{nolig}')
|
config.add_route('devis_lig_mv', '/devis_lig_mv/{move}/{nodevis}/{nolig}')
|
||||||
config.add_route('devis_list', '/devis_list')
|
config.add_route('devis_list', '/devis_list/{societe}/{nodevis}')
|
||||||
config.add_route('devis_create', '/devis_create/{nodossier}')
|
config.add_route('devis_create', '/devis_create/{nodossier}')
|
||||||
|
config.add_route('devis_nochantier', '/devis_nochantier/{societe}/{nodevis}/{nochantier}')
|
||||||
config.add_route('devis_web', '/devis_web/{nodevis}')
|
config.add_route('devis_web', '/devis_web/{nodevis}')
|
||||||
config.add_route('devis_view', '/devis_view/{nodevis}')
|
config.add_route('devis_view', '/devis_view/{nodevis}')
|
||||||
config.add_route('devis_preview', '/devis_preview/{nodevis}')
|
config.add_route('devis_preview', '/devis_preview/{nodevis}')
|
||||||
|
|||||||
@@ -133,11 +133,11 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-4">
|
<div class="col-xs-4">
|
||||||
<a href="${request.application_url}/devis_list" tal:condition="logged_in == 'CAO'">
|
<a href="${request.application_url}/devis_list/PE/0" tal:condition="logged_in == 'CAO'">
|
||||||
<div class="info-box bg-prod">
|
<div class="info-box bg-prod">
|
||||||
<span class="info-box-icon"><i class="glyphicon glyphicon-text-height"></i></span>
|
<span class="info-box-icon"><i class="glyphicon glyphicon-search"></i></span>
|
||||||
<div class="info-box-content">
|
<div class="info-box-content">
|
||||||
<span class="info-box-number">E-DEVIS</span>
|
<span class="info-box-number">RECHERCHE DEVIS</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -27,10 +27,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-sm-4 control-label">Nom ou numéro du chantier</label>
|
<label class="col-sm-4 control-label">Numéro du devis</label>
|
||||||
<div class="col-sm-8">
|
<div class="col-sm-8">
|
||||||
<input type="text" class="form-control" name="name" value="${name}"
|
<input type="text" class="form-control" name="nodevis" value="${nodevis}" >
|
||||||
placeholder="Le nom ou le numéro doit avoir de 2 à 30 caractères de long" >
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -53,19 +52,20 @@
|
|||||||
<th>Client</th>
|
<th>Client</th>
|
||||||
<th>Chantier</th>
|
<th>Chantier</th>
|
||||||
<th class="text-right">Montant</th>
|
<th class="text-right">Montant</th>
|
||||||
<th>Sinistre</th>
|
<th>No chantier</th>
|
||||||
<th class="text-center">Statut</th>
|
<th class="text-center">Action</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr tal:repeat="detail devis">
|
<tr tal:repeat="detail dossiers">
|
||||||
<td>
|
<td>${detail.TYPE}-${detail.numero}</td>
|
||||||
<a href="/devis_web/${societe}-DE${detail.numero}">${societe}-${detail.numero}-W</a>
|
|
||||||
</td>
|
|
||||||
<td>${detail.date.strftime('%d-%m-%Y')}</td>
|
<td>${detail.date.strftime('%d-%m-%Y')}</td>
|
||||||
<td>${detail.nomcli}</td>
|
<td>${detail.nomcli}</td>
|
||||||
<td>${detail.chantier}</td>
|
<td>${detail.chantier}</td>
|
||||||
<td class="text-right">${layout.to_euro(detail.montant)}</td>
|
<td class="text-right">${layout.to_euro(detail.montant)}</td>
|
||||||
<td>${detail.nosin}</td>
|
<td>${detail.nochantier}</td>
|
||||||
<td class="text-center">${detail.status}</td>
|
<td class="text-center">
|
||||||
|
<a tal:condition="detail.nochantier == 0" id="modalButton" href="#confirmCreate"
|
||||||
|
data-toggle="modal" data-societe="${detail.societe}" data-nodevis="${detail.TYPE}-${detail.numero}">Joindre</a>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
</thead>
|
</thead>
|
||||||
@@ -74,26 +74,53 @@
|
|||||||
<br />
|
<br />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<!-- Modal : Confirmation CREATION -->
|
||||||
$(document).ready(function() {
|
<div class="modal fade" id="confirmCreate" role="dialog" aria-labelledby="confirmCreateLabel" aria-hidden="true">
|
||||||
$('#devis-search-form').formValidation({
|
<div class="modal-dialog">
|
||||||
framework: 'bootstrap',
|
<div class="modal-content">
|
||||||
message: 'This value is not valid',
|
<div class="modal-header">
|
||||||
icon: {
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||||
valid: 'glyphicon glyphicon-ok',
|
<h4 class="modal-title">Ajouter le nochantier dans le </h4>
|
||||||
invalid: 'glyphicon glyphicon-remove',
|
</div>
|
||||||
validating: 'glyphicon glyphicon-refresh'
|
<div class="modal-body">
|
||||||
},
|
<!-- The form is placed inside the body of modal -->
|
||||||
});
|
<form id="add_justif-form" class="form-horizontal" action="${url}" method="post">
|
||||||
$('form input').on('keypress', function(e) {
|
<div class="form-group">
|
||||||
var code = e.keyCode || e.which;
|
<label class="control-label col-xs-4">Document No:</label>
|
||||||
if (code === 13) {
|
<div class="col-xs-8">
|
||||||
e.preventDefault();
|
<input type="text" name="md_nodevis" id="md_nodevis" value="" readonly/>
|
||||||
// simuler clic bouton submit
|
</div>
|
||||||
document.getElementById("submitButton").click();
|
</div>
|
||||||
}
|
<div class="form-group">
|
||||||
});
|
<label class="control-label col-xs-4">Chantier No :</label>
|
||||||
|
<div class="col-xs-8">
|
||||||
|
<input type="text" name="md_nochantier" id="md_nochantier" value=""/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-xs-4">Société:</label>
|
||||||
|
<div class="col-xs-8">
|
||||||
|
<input type="text" name="md_societe" id="md_societe" value="" readonly/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal">Annuler</button>
|
||||||
|
<button type="submit" class="btn btn-success" name="form.joined">Ajouter</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
$('#confirmCreate').on('show.bs.modal', function(e) {
|
||||||
|
var societe = $(e.relatedTarget).data('societe');
|
||||||
|
var nodevis = $(e.relatedTarget).data('nodevis');
|
||||||
|
$(e.currentTarget).find('input[name="md_societe"]').val(societe);
|
||||||
|
$(e.currentTarget).find('input[name="md_nodevis"]').val(nodevis);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -21,35 +21,49 @@ from ..models.devis import *
|
|||||||
|
|
||||||
@view_config(route_name='devis_list', renderer='../templates/devis/devis_list.pt', permission='view')
|
@view_config(route_name='devis_list', renderer='../templates/devis/devis_list.pt', permission='view')
|
||||||
def devis_list(request):
|
def devis_list(request):
|
||||||
|
societe = request.matchdict['societe']
|
||||||
|
nodevis = request.matchdict['nodevis']
|
||||||
|
try:
|
||||||
|
int(nodevis)
|
||||||
|
except:
|
||||||
|
message = "Numero de Devis incorrect : %s" % societe + '-' + nodevis
|
||||||
|
|
||||||
url = request.route_url('devis_list')
|
|
||||||
|
|
||||||
logged_in = request.authenticated_userid.upper()
|
|
||||||
message = ''
|
message = ''
|
||||||
member = get_member_by_id(request, logged_in)
|
dossiers=None
|
||||||
societe_defaut = member.societe
|
|
||||||
societe = societe_defaut
|
|
||||||
access_defaut = member.access
|
|
||||||
liste=[]
|
|
||||||
name = ''
|
|
||||||
cb_tous = "non"
|
|
||||||
|
|
||||||
if 'form.submitted' in request.params:
|
if 'form.submitted' in request.params:
|
||||||
name = request.params['name']
|
|
||||||
societe = request.params['societe']
|
societe = request.params['societe']
|
||||||
|
nodevis = request.params['nodevis']
|
||||||
|
|
||||||
|
if 'form.joined' in request.params:
|
||||||
|
md_nochantier = request.params['md_nochantier']
|
||||||
|
md_nodevis = request.params['md_nodevis']
|
||||||
|
md_societe = request.params['md_societe']
|
||||||
|
|
||||||
|
# modifier un devis à partir d'un dossier
|
||||||
|
update_devis_nochantier(request, md_societe, md_nodevis, md_nochantier)
|
||||||
|
|
||||||
|
message = "Le devis %s a été modifié avec succès : " % md_nodevis
|
||||||
|
|
||||||
|
url = request.route_url('devis_list', societe=societe, nodevis=nodevis)
|
||||||
|
if nodevis != '0':
|
||||||
|
# lire le devis
|
||||||
|
devis = get_devis_by_no(request, societe + '-DE' + nodevis)
|
||||||
|
if devis == None:
|
||||||
|
message = "Devis non trouvé : %s" % societe + '-' + nodevis
|
||||||
|
else:
|
||||||
|
# lire tous les dossiers du chantiers
|
||||||
|
dossiers = get_dossiers_byName(request, societe, devis.C_NOM)
|
||||||
|
|
||||||
# lire les devis
|
|
||||||
devis = get_devis_byName(request, societe, name)
|
|
||||||
if len(devis) == 0:
|
|
||||||
message = "Devis non trouvé : %s" % name
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'page_title': "Rechercher un devis",
|
'page_title': "Rechercher un devis",
|
||||||
'url': url,
|
'url': url,
|
||||||
'message': message,
|
'message': message,
|
||||||
'devis': devis,
|
'dossiers': dossiers,
|
||||||
'societe': societe,
|
'societe': societe,
|
||||||
'name': name,
|
'nodevis': nodevis,
|
||||||
}
|
}
|
||||||
|
|
||||||
@view_config(route_name='devis_create', permission='view')
|
@view_config(route_name='devis_create', permission='view')
|
||||||
@@ -64,6 +78,17 @@ def devis_create(request):
|
|||||||
request.session.flash(u"Le devis %s a été créé avec succès" % no_devis.last_insert_id, 'success')
|
request.session.flash(u"Le devis %s a été créé avec succès" % no_devis.last_insert_id, 'success')
|
||||||
return HTTPFound(location=request.route_url("dossier_view", nodossier=nodossier) + '#tab_documents')
|
return HTTPFound(location=request.route_url("dossier_view", nodossier=nodossier) + '#tab_documents')
|
||||||
|
|
||||||
|
@view_config(route_name='devis_nochantier', permission='view')
|
||||||
|
def devis_nochantier(request):
|
||||||
|
societe = request.matchdict['societe']
|
||||||
|
nodevis = request.matchdict['nodevis']
|
||||||
|
nochantier = request.matchdict['nochantier']
|
||||||
|
|
||||||
|
# modifier un devis à partir d'un dossier
|
||||||
|
update_devis_nochantier(request, societe, nodevis, nochantier)
|
||||||
|
|
||||||
|
request.session.flash(u"Le devis %s a été modifié avec succès : " + nodevis, 'success')
|
||||||
|
return HTTPFound(location=request.route_url("devis_list", societe=societe, nodevis=nodevis))
|
||||||
|
|
||||||
|
|
||||||
@view_config(route_name='devis_view', renderer='../templates/devis/devis_view.pt', permission='view')
|
@view_config(route_name='devis_view', renderer='../templates/devis/devis_view.pt', permission='view')
|
||||||
|
|||||||
Reference in New Issue
Block a user